Jump to content
Sign in to follow this  
Fuzzy Bandit

Dynamically Creating and Naming a Vehicle

Recommended Posts

Hello!

Looking into how I would dynamically name a vehicle after creating it using code.

My target is to create a series of guns which name themselves according using a For loop.

Here's the unsuccessful code with anything related added in at the top.

//Related Code
opGunCount = 14;
_pos = getMarkerPos "opforSpawn";


//Actual Code
_c = 1;
for [{_c = 1}, {_c <= opGunCount}, {_c = _c + 1}] do {
gun = call compile format ["opgun%1", _c];
gun = "D30_RU" createVehicle _pos;
publicVariable "gun";
};

I've tried a fair few variations of the code and tried using setVehicleVarName too.

Please tell me anything that works for you, or any advice you may have. Been trying to get this damned thing to work for flippin' ages!

Cheers!

Share this post


Link to post
Share on other sites

Here is some code Shk helped me with. It spawns groups and assigns them a name pulled from the array. I'm guessing you could substitute the SpawnGroup for a vehicle or whatever. Obviously you don't need the wp bit but I thought I'd leave it in its entirity.

//nul = [<"GroupName">,<"Spawn">,<"Wp1">,<"Wp2">] execvm "spawn.sqf"

_grp = _this select 0;
_pos = _this select 1;
_wp1 = _this select 2;
_wp2 = _this select 3;

call compile format ["%1 = [getmarkerpos _pos, EAST, %2] call BIS_fnc_spawnGroup",_grp,["RU_Soldier","RU_Soldier2",]];

_grp = call compile format ["%1",_grp];

Sleep 3;

_waypoint0 = _grp addwaypoint [getmarkerpos _wp1,0];
_waypoint0 setWaypointType "move";
_waypoint0 setWaypointBehaviour "alert";
_waypoint0 setwaypointcombatmode "green";
_waypoint0 setWaypointSpeed "full";

_waypoint1 = _grp addwaypoint [getmarkerpos _wp2,0];
_waypoint1 setWaypointType "SAD";
_waypoint1 setWaypointBehaviour "combat";
_waypoint1 setWaypointformation "wedge";
_waypoint1 setwaypointcombatmode "red";
_waypoint1 setWaypointSpeed "normal"; 

Share this post


Link to post
Share on other sites

gun = call compile format ["opgun%1", _c];

gun = "D30_RU" createVehicle _pos;

publicVariable "gun";

Try this (UNTESTED!)

var = format["opgun%1", _c];
obj = "D30_RU" createVehicle _pos;
missionNamespace setVariable [ var, obj ];
obj setVehicleVarName var;
publicVariable var;

Share this post


Link to post
Share on other sites

mcvittees - I've tried code very similar to that. I wrote some quick debug code which gives me the name of the vehicle I'm currently in. With the current two methods (mine and yours), if I create the vehicles, get in them and use the debug code, it shows the name of the vehicle correctly as 'opgun1', 'opgun2' etc.

The issue is that they don't seem to be making themselves available to the server. If I then use the code:

if ((opgun1 emptyPositions "Gunner") < 1) then {
hint format ["opgun1 is occupied by:\n%1", (name opgun1)];
};

I then get the error:

ERROR: No Vehicle.

Trying your method now alef.

Share this post


Link to post
Share on other sites
_c = 1;
for [{_c = 1}, {_c <= opGunCount}, {_c = _c + 1}] do {
gun = call compile format ["opgun%1", _c];
gun = 
publicVariable "gun";
};

Your code says this:

gun = nil;
gun = vehicle;
publicvariable "gun";

You want this:

for "_i" from 1 to opGunCount do {
_v = "D30_RU" createVehicle _pos;
call compile ["opGun%1 = _v;", _i];
publicVariable format["opGun%1",_i];
};

Share this post


Link to post
Share on other sites

alef - adapted, tested and working perfectly!

Can you run through exactly what is happening in the segment of code? I'm not too familiar with missionNameSpace.

Share this post


Link to post
Share on other sites

In a regular piece of code, missionNamespace is the namespace you are operating within, why did he specify it? It is a work around to avoid compiling a string via compile to increase performance over the method I gave you.

Is this script being run more than once? If not, then don't worry about it. ;)

Share this post


Link to post
Share on other sites

Just tested your code as well Rommel - that works a charm as well, though you forgot 'format' in the 3rd line. :P

---------- Post added at 16:41 ---------- Previous post was at 16:40 ----------

The script can and most probably will be run more than once, but never at the same time.

As well as that, before the vehicles are created, the 'old' vehicles with the same name are erased, making way for the new ones.

So which one is faster? Rommel's or alef's? :D

Share this post


Link to post
Share on other sites
Just tested your code as well Rommel - that works a charm as well, though you forgot 'format' in the 3rd line. :P

Ha, I apologize, I'm not at home at the moment :p.

So which one is faster? Rommel's or alef's? :D

This should give you the best of both. :cool:

for "_i" from 1 to opGunCount do {
_s = format["opGun%1", _i];
_v = "D30_RU" createVehicle _pos;
missionNamespace setVariable [_s,_v];
//_v setVehicleVarName _s; //If this is needed, then do it, otherwise disregard
publicVariable _s;
};

Share this post


Link to post
Share on other sites

Once again, adapted, tested and working perfectly.

Thanks you two!

Also thanks for the slight correction with the "for" loop. I never knew you could do it like that - saves a bit of tom-foolery! :P

Share this post


Link to post
Share on other sites

Ok, maybe I'm missing something here, but I have to ask again: what's the point? (I've asked the same question over here)

Is it because multiplayer (JIP, respawn or something?) requires this ugly play with variable variables? If so, fine (I guess). But if not, please stop it. You do not have to give every friggin thing a `name` (global variable). The various create functions return a pointer to your precious objects and there is no need to duplicate them while littering the global namespace.

theArtillery = [];

for "_i" from 0 to (_n - 1) do
{
  theArtillery set [(count theArtillery), ("D30_RU" createVehicle _pos)];
};

You'll end up with only one global variable and you will be able to manage your guns much better. It will be easier to do _anything_ with these guns. Make them all fire at a target, check whether all of them are destroyed, delete them all in one go, etc...

Just look how beautiful code could look like:

{
  // do something with all the guns
  _x doFire someTarget;
} forEach theArtillery;

or this one:

if (({alive _x} count theArtillery) == 0) then
{
  // victory, yay! (or we're screwed, bohoo)
};

Show me your code that does these things with your variable variables. Btw. do you have another variable that keeps track of the number of variable variables too? :rolleyes:

It's a friggin _list_ of guns. Put them in an array and you get all the power of list processing for free. Why do you guys insist on replicating your own pseudo-lists with numbered variables? Why? It's dangerous, it's tedious, .. it's plain stupid.

Please. Unless I'm not missing some crazy engine limitation (such as multiplayer scripting - I have not much clue about that, sorry), stop giving such bad advice. Time and again...

Share this post


Link to post
Share on other sites

To begin with, yes, this script in intended as a 'respawn' script. There's more happening than just the creation of the vehicles and it would seem that without globally defining each with their own separate name, some of those things are possible.

The reason I needed them all to have their own separate names is because there is a separate script (run from the player) which attempts to board 'opgun1'. If 'opgun1' is occupied, the script will try to board 'opgun2'. This is done from within a For loop.

Now, when creating a vehicle I know that it immediately gives that vehicle a name depending on the text before (highlighted in red below).

[color="Red"]vehicle[/color] = "typeOfVehicle" createVehicle position;

The issue is that if I then attempted to call the name of a player inside that vehicle (e.g. priority would be the Gunner when talking about artillery pieces) using the code:

if ((vehicle emptyPositions "Gunner") < 1) then {
    hint format ["vehicle is occupied by:\n%1", name vehicle];
};

The hint would display:

vehicle is occupied by:
Error: No Vehicle

Currently, in certain terms, a 'number' is applied to each gun, labelling it. Depending on the gun's 'number', different commands are issued using a switch statement.

At the moment, and maybe unfortunately on my part, there's a global variable which states how many guns are currently occupied. I use this variable to help determine which gun to board upon using my 1 action assigned to the player. Let's say there are 14 guns lying around in a field. 7 are occupied and 7 are not. The player then uses an action assigned to them labelled 'Board Gun'. This will execute the following script (assuming opGunCount = 14):

boardgun.sqf

_c = 1;
_going = 1;
While {_going == 1} do {
    for "_c" from 1 to opGunCount do {
         _gun = call compile format ["opgun%1", _c];
         if ((_gun emptyPositions "Gunner") > 0) then {
              player moveInGunner _gun;
              _going = 0;
         };
    };
    _going = 0;
};

That, in theory (I do not have access to the actual script at this time), should find the first empty slot within the 14 guns and put the player into it.

If you could tell me how to do that using one multi-dimensional array then I'd be more than happy to change it and cut down on code. The issue is that I'm just not sure if it's possible.

You've brought this up before, and I'll say again - I'm really not afraid of arrays. They're wonderful things and I'd prefer to use one, it's just that I'm not totally sure of how I would incorporate an array into my code and get the same functionality.

Thanks for the help everyone, and thank you for your input ruebe.

Is there any way I would be able to incorporate an array into my code?

Cheers.

Share this post


Link to post
Share on other sites

if (isserver) then {
 opGunCount = 14;
 _pos = getMarkerPos "opforSpawn";

 opGuns = [];
 for "_i" from 0 to opGunCount do {
   opGuns set [count opGuns,("D30_RU" createVehicle _pos)];
 };
 publicvariable "opGuns";
};

{
 if ((_x emptyPositions "Gunner") > 0) exitwith {
   player moveInGunner _x;
 };
} foreach opGuns;

Didn't test, but just for ideas.

Share this post


Link to post
Share on other sites

Looks good to me.

Is there any way I would be able to apply individual commands to each gun upon creation, though? There's no pattern to the commands, so the best I can do at the moment is a For loop and a Switch statement.

Using that method would clear things up, but it doesn't solve the issue of having to apply different commands to each gun, as it would seem that they are immediately added to the array upon creation, and them having no 'name' presents me with quite an issue!

I was going to write some code I thought might've worked with the array, but upon writing it I ended up just defining loads of variables again.

I also see that you've used the command 'exitWith' in that piece of code. Sound incredibly useful to me! I'm guessing it executes the code inside the '{}'s before terminating the script? If so, it'd help me loads! If not, how exactly does it work?

Cheers!

Share this post


Link to post
Share on other sites

Well, you can do whatever to the individual guns:

_v = opGuns select n;

_v dosomething;

Instead of remembering guns name, you have to remember it's position in the array.

exitwith exists the current code block (not script straight away) and runs the given code.

Share this post


Link to post
Share on other sites
To begin with, yes, this script in intended as a 'respawn' script.

I fail to see that this matters in this regard and I highly doubt that there actually is such a necessity for globals in this case.

In fact I guess this bad practice emerged from the ordinary useage of the ingame-editor where you usually put in some name like s1 or arty1 and so on for units (which are global variables again). Sure, this seems to be straight forward, but more often than not you'd be better of with something like null = this call registerArtillery; in the init line of the unit, which would register the unit in a global array theArtillery.

The reason I needed them all to have their own separate names is because there is a separate script (run from the player) which attempts to board 'opgun1'. If 'opgun1' is occupied, the script will try to board 'opgun2'. This is done from within a For loop.

I think you are a bit confused here. Seeking the first unoccupied gun is nothing other than list processing. You search a list for an item. Except you don't really manage a list which makes things needlessly worse.

Now, when creating a vehicle I know that it immediately gives that vehicle a name depending on the text before (highlighted in red below).

No, that's a misconception. You are _binding_ whatever createVehicle returns to the global variable `vehicle`. Stop calling variables a name. That's confusing, because a variable consists of more than just a name. A variable consists of a name itself (resp. what it points to) can be addressed in its namespace and it consists of a value; in this case a pointer to something (typically a memory address).

createVehicle creates an object and put's it somewhere in the memory. What you get (resp. what createVehicle returns) is a pointer to this object (or the allocated memory if you will). Binding that pointer to a global variable then looks like this:

  myGlobalVariable --> pointer (or memory address if you will) returned by createVehicle --> the actual object/memory

...where the arrow may be read as `is pointing to`. The global variable is pointing to a pointer which is pointing to the actual object/memory.

Now you can just put these pointers returned by createVehicle into an array. But please, don't bind them to a global variable first (to which you would say is giving it a name) and then put these global variables into the array.. that's just silly. Anyway, if you put them into an array, as suggested, this then looks something like this:

  myGlobalArray = [
     0: pointer returned by createVehicle --> the actual object/memory
     1: pointer returned by createVehicle --> the actual object/memory
     n: pointer returned by createVehicle --> the actual object/memory
  ];

  or

  (myGlobalArray select 0) --> the actual object/memory

The issue is that if I then attempted to call the name of a player inside that vehicle (e.g. priority would be the Gunner when talking about artillery pieces)...

Again you are confused. In this case, `name` is a totally other concept (see name). It's about identities of units. And if `vehicle` actually exists and is manned too, then I don't see why it shouldn't be able to retrieve the identity of the unit in there (though commander comes first, then driver. The gunner comes last. So you'd need to do something like (name (gunner vehicle)))

Anyway, I don't see how this is relevant to the current discussion (only units have identities, vehicles don't) and if it didn't work, then you probably did some other mistake.

At the moment, and maybe unfortunately on my part, there's a global variable which states how many guns are currently occupied.

That's quite dangerous if you ask me. What happens if a gunner of an artillery get's shot, but the artillery itself is intact? Do you keep eventhandlers running for all your gunners? A lot could happen, which would make that variable useless or at least inaccurate.

Instead of keeping track of unoccupied artilleries, you should just have a script that looks for the first unoccupied artillery.

Looking at your `boardgun.sqf`, first thing that comes to my mind is: get rid of that while loop and _going stuff. That's just useless junk. Then you can replace the statement if (...) then { _going = 0; } with if (...) exitWith { player moveInGunner _gun};. There is really no need to put a loop in your loop... unless you were born in Detroit Michigan, make shitty music and are pimpin other peoples rides and stuff...

Second thought: exactly. Use a friggin list/array. :)

If you could tell me how to do that using one multi-dimensional array then I'd be more than happy to change it and cut down on code. The issue is that I'm just not sure if it's possible.

Sure it's possible. And you don't even need a multidimensional array for this. A onedimesional list should be fine.

Maybe the following example will open your eyes:


// initialization
/*
  global variables should be initialized early
  enough. A good place is your init.sqf. 
  Writing globals in caps might help readability, 
  though the engine doesn't distinguish lower-/
  uppercase.
*/
THE_ARTILLERY = [];


// mission configuration
/*
  `magic numbers` should never be hardcoded,
  thus we're using global variables for this which
  is fine (sure, associative arrays would be nicer).
  Using a `strong` prefix helps. 
  Though we probably should put this in the init.sqf 
  or in it's own file (configuration.sqf or something).
*/
MISSION_CONF_ARTILLERY = 14;



// the actual scripts which should be put in their
// own files to be called or spawned...
// first we define some private functions, which helps
// a lot keeping your code readable. If generally useable
// you might wanna make global functions out of them...
private ["_calculateArtyPosition", "_firstVehicleWithoutGunner", "_i", "_pos", "_veh"];

// integer => position
_calculateArtyPosition = {
  // whatever...
};

// array => object (or objNull)
_firstVehicleWithoutGunner = {
  private ["_veh"];
  _veh = objNull;

  {
     if ((_x emptyPositions "Gunner") > 0) exitWith
     {
        _veh = _x;
     };
  } forEach _this;

  _veh
};


// first we create the artillery and put the pointers
// to them in the array THE_ARTILLERY
for "_i" from 0 to (MISSION_CONF_ARTILLERY - 1) do
{
  _pos = _i call _calculateArtyPosition;
  THE_ARTILLERY set [
     (count THE_ARTILLERY), 
     ("D30_RU" createVehicle _pos)
  ];
};

// let's try to put the player in the first artillery 
// without a gunner
_veh = THE_ARTILLERY call _firstVehicleWithoutGunner;

if (!isNull(_veh)) then
{
  player moveInGunner _veh;
} else 
{
  // there is no empty gunner seat,
  // what now?
};

Ohhh.. Reloading the page I realize I was much too slow and the discussion went on without me... Well, I'll post what I have written anyway, for I see you're still not that comfortable. Maybe it helps. :)

Is there any way I would be able to apply individual commands to each gun upon creation, though?

Well sure. You're certainly not missing any functionality only because you don't bind the pointers to your objects to global variables. As shk already pointed out you can address them individually by numbers (their order in the list) in the most simple case. But you can do more clever things with a list (granted, you could too with your pseudo-list consisting of a bunch of global variables.. but it would be ugly as heck).

One common and easy way to manage a list partially is by using the modulo operator. This way, you can easily do something for every second, every third (and so on) object, which is often desired. For example you may put AI in every second artillery:

// occupying every second artillery with AI
private ["_i", "_gun", "_pos", "_commander", "_gunner"];

for "_i" from 0 to ((count THE_ARTILLERY) - 1) do
{
  // if the rest of _i divided by 2 is zero (or if the 
  // number is even), then...
  if ((_i % 2) == 0) then
  {
     _gun = THE_ARTILLERY select _i;
     _pos = position _gun;

     // create AI on the fly, assuming we have already 
     // created the group
     _commander = THE_ARTILLERY_GROUP createUnit ["SoldierWB", (position _pos), [], 0, "NONE"]; 
     _commander moveInCommander _x;

     _gunner = THE_ARTILLERY_GROUP createUnit ["SoldierWB", (position _pos), [], 0, "NONE"];
     _gunner moveInGunner _x;
  };
};

You may do many other tricks with lists like picking a random one or you may sort the list by a certain criteria (like distance to some other object, or by a factor that measures estimated accuracy to reach a certain spot, ... or sorting them by the amount of ammo left...) and then pick the first three of them... lists are very powerful. Or tell me, how would you sort your pseudo-list consisting of a bunch of global variables? hahaha ;)

I was going to write some code I thought might've worked with the array, but upon writing it I ended up just defining loads of variables again.

I hope that what I've written above helps you in this regard. Make yourself familiar with variables, pointers, namespace, assignments and you should be set to go.

...exitWith... If so, it'd help me loads! If not, how exactly does it work?

Learn to read the manual, resp. the wiki. http://community.bistudio.com/wiki/exitWith

Edited by ruebe

Share this post


Link to post
Share on other sites

Gutta say ruebe, for all my worries and woes about how I would manage this, that was THE most impressive, in-depth, helpful post I have ever read.

I'll be testing a huge variety of things on the morrow.

One thing that did confuse me, however, was your use of 'resp.'.

Had a little look, and turns out it's an abbreviation for 'Respectively' in Swedish. Everything makes sense now! :D

Once again, cheers. Here's hoping you won't have to make an angry reply about arrays to another one of my threads. ;)

Share this post


Link to post
Share on other sites
One thing that did confuse me, however, was your use of 'resp.'.

Had a little look, and turns out it's an abbreviation for 'Respectively' in Swedish. Everything makes sense now! :D

Yeah, resp. should indeed stand for respectively. I've assumed this was a common abbreviation. Well... obviously not. But I'm glad you managed to find out what it should stand for.

Once again, cheers. Here's hoping you won't have to make an angry reply about arrays to another one of my threads. ;)

I hope so too. Though it's completely fine if you do so if you don't know any better. Everyone has to start somewhere. What drives me mad is that this whole variable variable stuff is getting approved again and again. And I have the impression that it has gotten even worse since some of thy tribal elders* mentioned that variable variables are even more fun with missionNamespace setVariable [_x, _y];, which seems to be the new schtick around here. :rolleyes:

* In their defence: I'm pretty sure they were mistaken/missunderstood by their disciples. :D

Share this post


Link to post
Share on other sites
Yeah, resp. should indeed stand for respectively. I've assumed this was a common abbreviation. Well... obviously not. But I'm glad you managed to find out what it should stand for.

I hope so too. Though it's completely fine if you do so if you don't know any better. Everyone has to start somewhere. What drives me mad is that this whole variable variable stuff is getting approved again and again. And I have the impression that it has gotten even worse since some of thy tribal elders* mentioned that variable variables are even more fun with missionNamespace setVariable [_x, _y];, which seems to be the new schtick around here. :rolleyes:

* In their defence: I'm pretty sure they were mistaken/missunderstood by their disciples. :D

Having laid out the principles in the BIKI posts and FAQ's, I'm just helping answer the question. You can't re-design everyone's system. Hopefully they'll figure it out on their own. I for one never use global variables in any of my systems unless it must be done; which is quite rare (assuming you leave out functions).

Share this post


Link to post
Share on other sites

Apologies to bump a sort of 'Solved' thread, but just to let you know that I'm now using arrays for this current project and a previous project too.

Took a bit of re-designing, but even the code looks a hell of a lot clearer now.

Cheers for the help! :)

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  

×