Jump to content
Sign in to follow this  
Fuzzy Bandit

Naming Multiple Variables inside a forEach Loop

Recommended Posts

Hello!

Looking into how I would name multiple variables inside a forEach loop, or is this just impossible?

Let's say I want to check the value of three variables, and continue by executing the appropriate code. In this example, I want to check the values of the variables named tflag1, tflag2 and tflag3, and create code according to each possible value. Relative markers are named marker1, marker2 and marker3.

Here's the code I tried:

{
switch (tflag_x) do {
	case ("US"): {
		"marker_x" setMarkerTypeLocal "Faction_US";
		"marker_x" setMarkerSizeLocal [1,0.6];
		"marker_x" setMarkerTextLocal "CP_x (US)";
	};
	case ("PLA"): {
		"marker_x" setMarkerTypeLocal "Faction_RU";
		"marker_x" setMarkerSizeLocal [1,0.6];
		"marker_x" setMarkerTextLocal "CP_x (RUS)";
	};
};
} forEach [1, 2, 3];

Now that gives me error of an undefined variable named 'tflag_x'. Obviously it's looking for tflag_x as opposed to tflag1, tflag2 and tflag3.

Is there any way to achieve what I'm trying to achieve? Or do I just have to go the long way round and double-up on code?

Cheers!

Edited by Fuzzy Bandit

Share this post


Link to post
Share on other sites

Isn't flag a string name and _x a number you need to combine the two.

I did something along those lines a long time ago and must admit it wasn't easy and of no help as I can't remember how I did it.

Could use do something like this.

predefine a variable and use that to get the names out the usual way.

tempflag= [flag1,flag2,flag3]

switch (temptflag select _x) do {

ect.

Share this post


Link to post
Share on other sites

Untested:

{
 _flg = call compile format ["tflag%1",_x];
switch _flg do {
	case ("US"): {
		"marker_x" setMarkerTypeLocal "Faction_US";
		"marker_x" setMarkerSizeLocal [1,0.6];
		"marker_x" setMarkerTextLocal "CP_x (US)";
	};
	case ("PLA"): {
		"marker_x" setMarkerTypeLocal "Faction_RU";
		"marker_x" setMarkerSizeLocal [1,0.6];
		"marker_x" setMarkerTextLocal "CP_x (RUS)";
	};
};
} forEach [1, 2, 3];

Share this post


Link to post
Share on other sites

Just tested shk - not working. No errors either - just nothing happens! :D

I was trying something along the lines of:

c = 0;
for [{c=0},{c<3},{c=c+1}] do {
{
	switch (_x) do {
		case ("US"): {
			{
				"_x" setMarkerTypeLocal "Faction_US";
				"_x" setMarkerSizeLocal [1,0.6];
				"_x" setMarkerTextLocal "CP1 (US)";
			} forEach ((flagArray select _x) select 1);
		};
	};
[color="Red"]} forEach ((flagArray select c) select 0);[/color]
};

However, on the line highlighted in red I'm getting an error that says "Type String, Expected Array".

Not sure what's going on there! Any ideas?

Share this post


Link to post
Share on other sites

shk's concept does work, he just missed to apply it to the marker name and marker text as well.

{
 _flg = call compile format ["tflag%1",_x];
 _markertext = format["CP_%1", _x];
 _marker = format["marker_%1", _x];
switch _flg do {
	case ("US"): {
		_marker setMarkerTypeLocal "Faction_US";
		_marker setMarkerSizeLocal [1,0.6];
		_marker setMarkerTextLocal (_markertext + " (US)");
	};
	case ("PLA"): {
		_marker setMarkerTypeLocal "Faction_RU";
		_marker setMarkerSizeLocal [1,0.6];
		_marker setMarkerTextLocal (_markertext + " (RUS)");
	};
};
} forEach [1, 2, 3];

Edited by dengibtsschon
fixed typo in the line for the _markertext

Share this post


Link to post
Share on other sites

Apologies for the late reply!

Works a charm, dengibtsschon!

Thanks for all the help everybody!

One question though. What exactly is 'call compile' doing? It doesn't say all that much about the two statements in the wiki. I understand what format does, and was trying to use it in the past with this piece of script without success.

I understand this script fully. I'm just not totally sure what I'm doing by calling and compiling... :D

Share this post


Link to post
Share on other sites

Fantastic stuff - knowing that compile converts STRING to CODE is priceless information!

I've heard about the 'power' of 'call' and 'compile' used with 'format', but never quite understood why.

Cheers!

Edit:

Been quickly researching the uses of this particular method, and am having some issues with it.

I made a small debug script with the code below, called from a radio trigger using the code:

nul0 = [1, "Neutral"] execVM "debug.sqf";

This is the code in debug.sqf:

_flagname = call compile format ["flag%1", (_this select 0)];
_tflagname = call compile format ["tflag%1", (_this select 0)];
_markername = call compile format ["marker%1", (_this select 0)];
_cpname = call compile format ["CP%1", (_this select 0)];

hint format ["_flagname = %1\n_tflagname = %2\n_markername = %3\n_cpname = %4", _flagname, _tflagname, _markername, _cpname];

Every single variable is returned as 'Any'.

Any idea why? Or am I doing something painfully wrong? :D

Edited by Fuzzy Bandit

Share this post


Link to post
Share on other sites

Did you initialise the variables flag1, tflag1, marker1 and CP1 first?

Share this post


Link to post
Share on other sites

They're supposed to be created dynamically using some code to create a Game Logic. Maybe you could help me with that too! :D

Thread is linked here.

Share this post


Link to post
Share on other sites

Even easier is using missionNameSpace getVariable format ["flag%1", _i], which will have the same effect of call compile format ["flag%1", _i] but supposedly much more efficient and clear.

Share this post


Link to post
Share on other sites

The first one just gets the variable of given name, the second actually compiles the code in the string just to realize that the code tells it to return said variable. End result is the same.

Share this post


Link to post
Share on other sites

Is there any way to create variables using either call compile or getVariable?

Share this post


Link to post
Share on other sites

missionNamespace setVariable [varNameString, varValue];

ex:

missionNamespace setVariable [format ["myGlobalVar%1%2", _i, _j], _i*_j]];

Of course if you already know the name in advance you can just do varName = varValue and the variable will be defined, but if you want to use a string to determine the variable name, use setVariable.

I think that even missionNamespace setVariable [varNameString, varValue, true]; will also automatically publicVariable it.

Share this post


Link to post
Share on other sites

Ok, so say I wanted to formulate an object name using one of the above methods and insert it into an IF statement, how would I do that?

At the moment I'm getting the error 'Type String, Expected Bool' wherever I turn.

for [{_c = 1}, {_c <= 5}, {_c = _c + 1}] do {
missionNameSpace setVariable ["_flagname", format ["flag%1", _c]];
if (player distance _flagname < 10) then {
	hint format ["You're closer than 10m to Flag #%1", _c];
};
};

Share this post


Link to post
Share on other sites

Just for kicks try another par pair pf parentheses (shown in green)

for [{_c = 1}, {_c <= 5}, {_c = _c + 1}] do {
missionNameSpace setVariable ["_flagname", format ["flag%1", _c]];
if ([color="Green"]([/color]player distance _flagname[color="green"])[/color] < 10) then {
	hint format ["You're closer than 10m to Flag #%1", _c];
};
};

I think that even missionNamespace setVariable [varNameString, varValue, true]; will also automatically publicVariable it.

There seems to be some difficulty with this when using missionNameSpace per this Biki entry.

In Arma 2 1.05 the missionnamespace object allows only the two main Arguments by syntax. Publishing the Variable afterwards works fine.

It is safer to just PV it after. See Biki notes for more:

---------- Post added at 07:16 PM ---------- Previous post was at 07:11 PM ----------

Just for kicks try another par pair pf parentheses (shown in green)

for [{_c = 1}, {_c <= 5}, {_c = _c + 1}] do {
missionNameSpace setVariable ["_flagname", format ["flag%1", _c]];
if ([color="Green"]([/color]player distance _flagname[color="green"])[/color] < 10) then {
	hint format ["You're closer than 10m to Flag #%1", _c];
};
};

I think that even missionNamespace setVariable [varNameString, varValue, true]; will also automatically publicVariable it.

There seems to be some difficulty with doing this via setVariable when working with missionNameSpace. Biki notes for setVariable:

In Arma 2 1.05 the missionnamespace object allows only the two main Arguments by syntax. Publishing the Variable afterwards works fine.

It is safe to just PV it after.

Edited by Loyalguard

Share this post


Link to post
Share on other sites

I don't get you guys and your fetish for variable variables. What's the point? So you have an arbitrary amount of flags (or anything else) and you do wanna do stuff with all of them at some point. Right? So why the hell do you feel like creating n global variables for these, littering the global namespace for no reason other than making your life a little bit more miserable?

Or in other words: what's wrong with arrays? (and mind you, not n, only one global array for all) They won't bite you... really, call them `a list` if it makes you feel any better. ;)

// global flags
myFlags = [];

// create flags later...
for "_i" from 0 to _randomNumber do
{
  myFlags set [(count myFlags), format["flag%1", _i]];
};

// do something with the flags
{
  _x setMarkerColor ...;
} forEach myFlags;

Since markers are accessed by a unique string instead of a pointer, this example maybe isn't the best there is. But in general just put your newly created objects, groups or units into an array if you need to keep track of them:

theObjectives = [];

for "_i" from 0 to _randomNumber do
{
  theObjectives set [_i, (createVehicle ["someStrategicBuilding", _randomPosition, [], 0, "NONE"])];
};

// ... and then...
{
  // something, something...
} forEach theObjectives;

There is no need to assign a unique variable to all of your objects. Just gather the pointers (returned by the creation functions) and put them in a list. Then access/modify the list.

Also don't be afraid of multidimensional arrays. If multiple variables belong together, you probably shouldn't have defined them in the first place. Instead, head for a datastructure that keeps these things together and write a little `contract`:

// array of arrays: 0=flag/obj, 1=markerId, 2=label
myFlags = [];

// a new flag
myFlags set [(count myFlags), [(createVehicle ["flag", ...]), format["flag%1",_i], "Camp"]];

Then you can iterate over myFlags to modify all of them, you may search a specific flag, delete another, whatever... But everytime you access such a flag datastructure, you have it all together. It's not only about ease of use, but also about data integrity...

So please, stop writing code where you end up with variables like "flag1", "tflag1", "marker1", "CP1".... Stop it. It isn't easier. It isn't any faster. It has no benefit at all. It's just a mess.

Make friends with arrays and array manipulation. :yay:

  • Like 1

Share this post


Link to post
Share on other sites

I'm purely guessing, but maybe missionNameSpace does not like local variables (_flagname).

what's wrong with arrays?

Because it's more convenient to directly access the variable you want instead of running if loops or making iteration functions.

Share this post


Link to post
Share on other sites

Good answer Ruebe, and thank you for the help everybody!

The reason I'm currently using a lot of variables is because I'm attempting to evolve a system of creating and managing flags that I created a while before I even knew about loops! I'm not scared of Arrays at all, I even tried a system using an Array to manage all the flags and variables in it using nested arrays e.g.:

myFlags = [[flag1, tflag1, marker1, CP1], [flag2, tflag2, marker2, CP2]];

The issue was that I found it hard to write code whereby I could select everything I wanted. I know if I wanted to select tflag2 I could write:

((_this select 1) select 1);

But it was creating completely dynamic code that would run a variety of functions on each variable depending on how many flags there were that was the trouble.

The code you posted above - have some small queries about it:

  1. The code snippet below - is that counting how many expressions are inside the Array 'myFlags', and then making nested arrays out of those entries? The issue I'd have, if this is the case, would be that I'm sure about how to add singular entries to an Array. Ideally, a 'Flag Start' script would be executed on each flag with calling code of something along the lines of:
    nul0 = [1, Neutral] execVM "flagstart.sqf";
    for the first flag, or:
    nul0 = [2, Neutral] execVM "flagstart.sqf";
    for the second flag.
    If this is the case, how would I achieve adding the required variables to my Array (e.g. flag1, tflag1)?
    myFlags = [];
    myFlags set [(count myFlags), format["flag%1", _i]];


  2. Are you suggesting dropping the whole variables idea and re-write the code by dynamically looking at the global Array instead of for singular variables? If so, how would I create some code that would add to the global Array 'myFlags', without over-writing any current information.
  3. Am I being an idiot? :D

Thank you for all the help everybody! I'll get past this learning curve soon enough! I'm slowly entering into the scripting mind of figuring out what to do with the resources you had. Currently I have a working game mode using singular named variables and a fair few for, forEach and While loops. What I'm trying to create, however, is a dynamic version which will adapt to what it's given. At the moment I'm having to define all sorts of variables and whatnot which are more 'hard-coded' than I'd like. As I said, I'm attemtping to create a version that will create everything it needs, as opposed to the Mission Editor having to know what to call all of the 10-billion objects placed on the map.

It's more of a personal want than an actual need. I'm trying to create it because I just want to understand how I would do such a thing. It's taught me a lot so far, and I'm gradually understanding more and more about Arrays and Variables and Loops. I'll probably end up in an IT job when I 'grow up', and I really enjoy the coding side of things. While the syntax may not be the same, I've heard that the ideas and methods used in ARMA II scripting is much the same to a lot of languages.

Anyways as I said, thank you for all the help. It is hugely appreciated!

Share this post


Link to post
Share on other sites

Agreed with Ruebe. Galzohar; you can iterate, but also select, or make it a hash. Hash keys can almost be any dataType, so look up per string, integer, group object, unit/vehicle object etc etc :)

Hash functions:

http://dev-heaven.net/docs/cba/files/hashes/fnc_hashCreate-sqf.html

http://dev-heaven.net/docs/cba/files/hashes/fnc_hashGet-sqf.html

http://dev-heaven.net/docs/cba/files/hashes/fnc_hashSet-sqf.html

http://dev-heaven.net/docs/cba/files/hashes/fnc_hashEachPair-sqf.html

etc.

And yea, missionNamespace does only give access to globalVariables, not local variables.

To add to an array: array set [count array, _value];

This is much quicker than array = array + [_value];

My motivation for using array/hashes for instance, is that you can shrink them.

Especially in MP, 100's of dynamically created variables when using publicvariable is a waste, especially if a lot of variables are useless later on ingame (objNull etc);

each publicvariable's current value and all setVehicleInits (regardless if the object they were executed on still exists) since the start of the mission, is synchronized to JIP players.

Edited by Sickboy

Share this post


Link to post
Share on other sites

If you place 20 flags in the editor, and name them flag1...20 then it's still much easier to use variable variable names even if just to place them into the array. So instead of _flagArray = [flag1, flag2.... flag20] you would make a loop that creates that array. Once the array actually exists I agree it's much nicer to use than constantly using variable variable names.

By the way, note that I think you might have wanted to use _flagname and not "_flagname" ?

Share this post


Link to post
Share on other sites

or you'd add in their init:

if (isNil "myAr") then { myAr = [] }; myAr set [count myAr, this];

could also be a call to a function/script etc.

In any case, I don't have a real motivation for keeping variable amount low since BI fixed gvar crashing in a1 1.08 or so,

other then when the variables are also PV'ed :) or when you will need to do a lot of dynamic scripting (call compile format or missionNamespace etc).

Edited by Sickboy

Share this post


Link to post
Share on other sites

Ok, I think I'm taking this all in! :D

As an interrogative, in regards to Arrays and the forEach loop, how exactly would it work?

For example, if I typed the following code:

myFlags = [[flag1, tflag1], [flag2, tflag2], [flag3, tflag3]];

{
_x setVehicleInit "this allowDamage false";
} forEach myFlags;

Would that set the Vehicle Init for the objects called 'flag1', 'flag2' and 'flag3'? Or would it try and run it on every single... is expression the word? e.g. 'flag1', 'tflag1', 'flag2', 'tflag2', 'flag3' and 'tflag3'.

Share this post


Link to post
Share on other sites

_x in your case would be an array (subarray of myFlags).

myFlags = [[flag1, tflag1], [flag2, tflag2], [flag3, tflag3]];

{
 _flag = _x select 0;
 _tflag = _x select 1;
} forEach myFlags;

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×