Jump to content
dragonmalice

createUnit alternative syntax init broken?

Recommended Posts

Posted this on the main Bohemia forums by mistake, so reposting:

 

I routinely spawn units from mixed factions (i.e., NATO with CSAT) as OPFOR via script, and the alternative syntax of createUnit is great for this since they automatically join the side of the group provided.  Since the patch this week, though, the init parameter doesn't seem to be working.

 

Usage:

 

_soldier = "";
_element createUnit [_x, _thisGroup, "this setPosATL _x; _soldier = this;"];

 

Last week, _soldier would be the unit created.  Now, the value doesn't change from ""

 

This is in a forEach loop, spawning units at each of array of locations.  _element, _x, and _thisGroup are all defined.  The script worked fine last week.

 

I changed to the normal createUnit syntax followed by a joinSilent command for now (and it works), but there can be a delay resulting in friendly fire if the server lags.

 

I didn't see anything about changes to the syntax in the patch notes.  Has anyone else had trouble with this?

Share this post


Link to post
Share on other sites

First of all, complete (or coherent) code is always better.

In your two lines, the first one is useless,

the second one is you are running the alternative syntax of createUnit without applying the example 7 of BIKI of the use of this. As you can read, there is no possible use of local variable _soldier outside the init code.

So, the use of such syntax in a loop is not the best one.

 

And yes, the first syntax needs a join (joinSilent) on group, after createUnit, for consolidating the side. That's what I do in my own scripts, with no delay (except for createVehicleCrew just after spawning an UAV).

Usually, server lags come from too many units/objects, and/or many scripts with poor optimization, or even bad codes.
 

 

  • Like 1

Share this post


Link to post
Share on other sites

Helped someone with the same problem. I just changed to a different syntax, but AI began to shoot each other (no matter what i did (joinSilent etc)).
Depending on your use case, you can be lazy and just do like I did.

_soldier = objNull;
_element createUnit [_x, _thisGroup, "this setPosATL _x; tempsoldier = this;"];
_soldier = tempsoldier;

 

  • Like 1

Share this post


Link to post
Share on other sites

@El'Rabito - beats the occasional friendly fire incident!  Thanks!

 

@pierrmgi - It's a lengthy spawn script calling many external functions.  Not sure how many pages of script I'd have to post before it would appear complete!  I'll replace the variables with values next time to simplify.

 

I can only say that I've been using local variables in the alternate context of createUnit for quite a while, and it was working until last week.  While I find it useful for _soldier to exist with a value for troubleshooting if the createUnit does fail, I vaguely remember having to predefine it in the higher scope for it to work - just like you would with an if...then statement.

 

Example 7 in the createUnit syntax is very easy to read as such: Two different variables were used.  The first failed because it was written wrong, the second succeeded because it was written correctly.  In any case, I'll switch to a global variable for now.  Thanks guys!

Share this post


Link to post
Share on other sites
On 10/12/2024 at 11:07 AM, El' Rabito said:

this setPosATL _x;

this should be doing nothing at best or throw an error at worst. Have you enabled -showScriptErrors on your A3 launcher / startup parameters?

Share this post


Link to post
Share on other sites

Okay, not sure what else to do - here are short videos of another script running using the same format in the two different game versions.  This script is a bit easier to demonstrate than the other, but should answer your questions I hope.

 

Version 2.18, notice units all spawn, but the units in the buildings are all ground level as the setPosATL does not work:

https://youtu.be/eZ3nNEcBpng

 

Version 2.16 legacy, notice units are spawning at all levels of the tower as the setPosATL code does work

https://youtu.be/T4h7Fe2k8Fg
 

The only thing that changed between these videos is the game version.  Full spawn script listed below (sorry, I know I have a lot of optimizations I still need to do!).  I can do more stuff showing the other script working if needed.  The more I think about it, the more I realize how big of a loss this is gonna be.  

 

Yes, the script errors is enabled in the launcher - none shown in either version.

Share this post


Link to post
Share on other sites

[strongholdLocation] execVM 'bandits\spawnBanditStronghold.sqf'; called from trigger with a 1k radius.  strongholdLocation is the center of the trigger.  I'm trying to be brief - the videos should show this part working.  Scroll down to the "Spawn Bandits" section for relevant script.

 

spawnBanditStronghold.sqf:

---------------------------------------------------------------------------------------------------------------------

 

//Spawn a bandit stronghold!  Created when player finds intel in a vehicle/container in a bandit camp
//Spawned at predetermined locations from banditStronghold.sqf
//Intended to be early game opportunities to gain some offensive equipment of fairly low quality (T34's, lightly armed helos, vehicle ammo, etc.)
//Like the bandit camp, defense force is infantry only - the technical was out on the road (hence finding the location of the camp and then the stronghold).
//The helos, jets, and armor found here are only partially working and need service

params ["_location"];

if (isServer) then
{
  ["You're close to an active bandit stronghold!"] remoteExec ["hint", 0];
  _safePositions = [];
  _safePos = [0,0,0];
  _vehicleList = [];
  _waypoint = true;
  _bandits = ["I_L_Hunter_F", "I_C_Soldier_Bandit_5_F", "I_L_Criminal_SMG_F", "I_C_Soldier_Bandit_1_F", "CFP_O_ABUSAYYAF_Sniper_01", "I_C_Soldier_Bandit_8_F", "CFP_O_SOREBEL_Autorifleman_2_01", "I_C_Soldier_Bandit_2_F"];
  _crates = ["Box_NATO_AmmoVeh_F", "CargoNet_01_box_F", "Box_NATO_AmmoVeh_F"];
  _fortify = selectRandom ["C_Van_01_box_F", "C_Truck_02_covered_F", "CUP_C_Ural_Civ_02", "CUP_C_Ural_Civ_01", "CUP_C_V3S_Covered_TKC"];
  _services = ["C_Offroad_01_repair_F", "C_Van_01_fuel_F", "C_IDAP_Van_02_medevac_F", _fortify];
  _helos = ["B_Heli_Light_01_dynamicLoadout_F", "vn_b_air_uh1c_07_07"];
  _armors = ["CFP_O_NKARMY_T34_01", "CFP_B_UGARMY_BRDM_2_01", "CFP_B_AFARMY_M113_01"];
  _jets = [];
  //Highly variable group size - bandits might all be here, they may all be out raiding
  _groupSize = 20 + (round random 20) + (round random 20);
  //Some locations aren't favorable for random vehicle spawning.  Set an alternate point for a decent vehicle area if needed.
  _vehiclePark = [];
  switch (_location) do
  {
    case kastroCastle: {_vehiclePark = [3179.59, 12898.8, 0]};
    case thronosCastle: {_vehiclePark = [4966.4, 21847.2, 0]};
    case almyraDesert: {_vehiclePark = [22965, 18852, 0]};
    case kosmasChurch: {_vehiclePark = [8892, 7569, 0]};
    case nwDump: {_vehiclePark = [5842, 20081, 0]};
    case dam: {_vehiclePark = [9340, 13745, 0]};
    case adRefinery: {_vehiclePark = [9539, 15326, 0]};
    case magosRT: {_vehiclePark = [4547.02, 15445, 0]};
  };

  _fnc_clearInventory = 
  {
    params ["_unit"];

    clearItemCargoGlobal _unit;
    clearMagazineCargoGlobal _unit;
    clearWeaponCargoGlobal _unit;
    clearBackpackCargoGlobal _unit;
  }; 

  _fnc_dmgVehicle = 
  {
    params ["_vehicle"];
    _damagePoints = [];
    //Get 3 arrays of damage info for vehicle, first of which is hit locations
    _fullList = getAllHitPointsDamage _vehicle;
    //Move array of hit locations to seperate array
    _damagePoints = _fullList select 0;
    _wheels = ["hitlfwheel","hitlf2wheel","hitrfwheel","hitrf2wheel","hitlbwheel","hitlmwheel","hitrbwheel","hitrmwheel"];
    {
      //Randomly damage each part of vehicle
      _vehicle setHitPointDamage [_x, random .75];
    } forEach _damagePoints;
    {
      //Increase chances of wheel damage
      if ((_x in _damagePoints) and (random 1 > .5)) then {_vehicle setHitPointDamage [_x, 1]};
    } forEach _wheels;
    _vehicle setFuel random .5;

    //Set random amount of ammo.  Using setVehicleAmmo will delete magazines (as opposed to leaving empty ones like firing the weapons does) - the vehicle service pads need the empties
    //Store all of the fully stocked vehicle info, cycle through non-Pylon turrets.  Remove the mag, readd it with less ammo
    _allPylonMags = getPylonMagazines _vehicle;
    _allTurretMags = magazinesAllTurrets _vehicle;
    _allPylons = getAllPylonsInfo _vehicle;
    {  
      _magazine = _x select 0; 
      _turretPath = _x select 1; 
      _ammoCount = _x select 2;
      if !(_magazine in _allPylonMags) then
      {
        _newAmmoCount = round random (_ammoCount / 4);
        if (_ammoCount == 1) then {if (random 100 < 25) then {_newAmmoCount = 1} else {_newAmmoCount = 0}};
        [_vehicle, [_magazine, _turretPath]] remoteExec ["removeMagazineTurret", 0];
        [_vehicle, [_magazine, _turretPath, _newAmmoCount]] remoteExec ["addMagazineTurret", 0]; 
      };
    } forEach _allTurretMags;
    //Cycle through the pylons and set their ammo counts to random amount.
    {
      _pylon = _x select 0;
      _magazine = _x select 3;
      _ammoCount = _x select 4; 
      _newAmmoCount = round (random .25 * _ammoCount);
      _vehicle setAmmoOnPylon [_pylon, _newAmmoCount];
    } forEach _allPylons;
  };

  //Spawn vehicles and crates
  //Spawn ammo or building supply crates
  _crateNumber = 1 + (round random 2);
  _vehicleNumber = 2 + (round random 2);
  _vehiclesSpawned = 0;
  _cratesSpawned = 0;
  for "_i" from 1 to _crateNumber do
  {
    _cratesSpawned = _cratesSpawned + 1;
    _crate = selectRandom _crates;
    _crateLocation = [_location, 0, 50, 3, 0, 0.5, 0] call BIS_fnc_findSafePos;
    _thisCrate = _crate createVehicle _crateLocation;
    _thisCrate allowDamage false;
    _vehicleList pushBack _thisCrate;
    [_thisCrate] call _fnc_clearInventory;
    //If it's an ammo crate, give it a random amount of ammo
    _ammo = round random 600;
    if (_crate == "Box_NATO_AmmoVeh_F") then {[_thisCrate, _ammo] call ace_rearm_fnc_setSupplyCount};
    if (_crate in ["Box_NATO_AmmoVeh_F", "CargoNet_01_box_F"]) then
    {
      [_thisCrate, true, [1,1,1], 0, true] call ace_dragging_fnc_setCarryable;
    };
  };
  waitUntil {_cratesSpawned == _crateNumber};
  //Spawn a piece of armor
  //Remaining spots are service vehicles
  //Armor gets damaged, defueled, dearmed.  Service vehicles are 100%  
  _armor = selectRandom _armors;
  _safePos = _location findEmptyPosition [10, 150, _armor];
  //while {_safePos distance _location > 150} do {_safePos = [_vehiclePark, 0, 150, 15, 0, 0.5, 0] call BIS_fnc_findSafePos};
  _thisArmor = _armor createVehicle _safePos;
  _thisArmor enableSimulation false;
  _thisArmor allowDamage false;
  _vehicleList pushback _thisArmor;
  [_thisArmor] call _fnc_clearInventory;
  _vehiclesSpawned = _vehiclesSpawned + 1;
  sleep .5;
  for "_i" from 2 to _vehicleNumber do
  {
    _vehicle = selectRandom _services;
    _safePos = _location findEmptyPosition [10, 150, _vehicle];
    //while {_safePos distance _location > 150} do {_safePos = [_vehiclePark, 0, 150, 15, 0, 0.5, 0] call BIS_fnc_findSafePos};
    _thisVehicle = _vehicle createVehicle _safePos;
    _thisVehicle enableSimulation false;
    _thisVehicle allowDamage false;
    _vehicleList pushback _thisVehicle;
    [_thisVehicle] call _fnc_clearInventory;
    //If it's a fuel truck, give it a random amount of fuel cargo
    _fuelCap = round random 1000;
    if (_vehicle == "C_Van_01_fuel_F") then {[_thisVehicle, _fuelCap] call ace_refuel_fnc_setFuel};
    //If it's an ambulance, make it an ACE medical facility
    if (_vehicle == "C_IDAP_Van_02_medevac_F") then {_thisVehicle setVariable ["ace_medical_isMedicalFacility", true, true]; _thisVehicle addItemCargoGlobal ["ACE_surgicalKit", 1]; _thisVehicle addItemCargoGlobal ["ACE_personalAidKit", 5];};
    _vehiclesSpawned = _vehiclesSpawned + 1;
    sleep .5;
  };
 
  waitUntil {_vehiclesSpawned == _vehicleNumber};
    
  //Helos get damaged, defueled, dearmed
  _helo = selectRandom _helos;
  _safePos = _location findEmptyPosition [10, 150, _helo];
  //while {_safePos distance _location > 200} do {_safePos = [_location, 0, 200, 10, 0, 0.5, 0] call BIS_fnc_findSafePos};
  _thisHelo = _helo createVehicle _safePos;
  _thisHelo enableSimulation false;
  _thisHelo allowDamage false;
  _vehicleList pushback _thisHelo;
  [_thisHelo] call _fnc_clearInventory;
  sleep 2;
   
/*Create jets if location is airfield.  Create array of locations and refer to them by name like RP list
  //If there are jet locations, fill them.  Jets get damaged, defueled, dearmed.
  if (count _jetLocations > 0) then
  {
    {
      _jet = selectRandom _jets;
      _thisJet = _helo createVehicle _x;
      _thisJet setDir _jetDir;
      [_thisJet] call _fnc_clearInventory;
      [_thisJet] call _fnc_dmgVehicle;
      sleep .5;
    } forEach _jetLocations;
  };
*/


  //Spawn bandits
  _spawnRadius = 150;
  _spawnPoints = [];
  _position = [];
  // Define a list of map buildings to spawn AI in - first map objects, then buildings that are placed manually
  _buildings = nearestTerrainObjects [_location, ["House", "Building", "Bunker", "Chapel", "Church", "Fortress", "FuelStation", "Hospital", "Lighthouse", "Powersolar", "Ruin", "Transmitter", "View-Tower", "Watertower"], _spawnRadius];
  // List of created objects
  _objects = nearestObjects [_location, ["Land_ControlTower_01_F", "Land_Slum_House02_F", "Land_Slum_House01_F", "Land_vn_hootch_02_01", "Land_vn_tent_01_03", "Land_Metal_Shed_F", "Land_MedicalTent_01_NATO_generic_inner_F", "Land_BagBunker_Large_F", "Land_BagBunker_Small_F"], _spawnRadius];
  // Mash em together
  _buildings append _objects;
  //Create array of all positions in all of the buildings
  _allPositions = [];
  _buildings apply {_allPositions append (_x buildingPos -1)};

  // Loop through the number of AI to spawn
  for "_i" from 1 to _groupSize do 
  {
    _position = [];
    if (random 100 <= 50 and count _allPositions > 0) then
    {
      // Select a random building to spawn AI in
      _building = selectRandom _buildings;
      // Get a random position within the building
      _position = selectRandom _allPositions;
      //Remove that position from array so it can't be reused
      _allPositions = _allPositions - [_position];
    }
    else
    {
      //Make sure some guys spawn near the vehicles
      _infantryPoint = _location;
      if (round random 100 < 10) then {_infantryPoint = _vehiclePark};
      _position = [[[_infantryPoint, _spawnRadius]], []] call BIS_fnc_randomPos;
      _position = [_position select 0, _position select 1, 0];
    };
    //If the selected position is too close to player, move it close to the center
    _tooClose = false;
    {if (_position distance _x < 50) then {_tooClose = true}} forEach allPlayers;
    if (_tooClose == true or count _position == 0) then {_position = [[[_location, 10]], []] call BIS_fnc_randomPos; _position = [_position select 0, _position select 1, 0];}; 
    _spawnPoints pushBack _position;
  };

  _groupList = [];
  _thisGroup = createGroup [resistance, true];
  _holdGroup = createGroup [resistance, true];

  {
    //if playerServer spot is hosting, wait to spawn until frame rate is decent (avoid lag spikes)
    if !(isNil "playerServer") then {waitUntil {diag_fps > 50}};
    _element = selectRandom _bandits;
    //There are two groups.  3/4 are movement disabled garrison, the other will have a CBA defend task placed on it
    if (random 100 < 25) then {_element createUnit [_x, _thisGroup]}
    else {_element createUnit [_x, _holdGroup, "this disableAI 'PATH'; this setPosATL _x;"];};
    //3/4 of the bandits are untrained and won't leave their positions unless released by 20m firing event handler on players
    //_element createUnit [_x, _thisGroup, "if (round random 100 < 75) then {this disableAI 'PATH'}; this setPosATL _x;"];
    _groupList pushBack _element; _groupList pushBack _x;    
  } forEach _spawnPoints;

  //Have the mobile bandits defend the area with randomized values for garrison/patrol
  _patrol = random 1;
  _garrison = random 1;
  if (_waypoint == true) then 
  {
    //[_thisGroup, _location, 200, 1, _patrol, _garrison] call CBA_fnc_taskDefend;
    [_thisGroup, _location] call BIS_fnc_taskDefend;
  };

  //Re-enable damage on all vehicles, run damage function on military vehicles
  {
    _x allowDamage true;
    _x enableSimulation true;
    if (typeOf _x in _armors or typeOf _x in _helos) then {[_x] call _fnc_dmgVehicle};
  } forEach _vehicleList;

  waitUntil {sleep 30; ({alive _x} count units _thisGroup) + ({alive _x} count units _holdGroup) <= 5;};
  banditStronghold = false; publicVariable "banditStronghold";
  deleteVehicle bshTrigger;
  deleteMarker "banditStrongholdMarker";
}; //end ifServer

Share this post


Link to post
Share on other sites
9 hours ago, mrcurry said:

this should be doing nothing at best or throw an error at worst. Have you enabled -showScriptErrors on your A3 launcher / startup parameters?

This was just the code snippets used from the guy that create the thread. He uses a forEach loop, so should be fine.

Share this post


Link to post
Share on other sites
17 hours ago, El' Rabito said:

This was just the code snippets used from the guy that create the thread. He uses a forEach loop, so should be fine.

 

BIKI can sometimes miss some detailed explanation , but it's barely a source of error!  So, for createUnit, 2 syntaxes:

group createUnit [type, position, markers, placement, special]  // returning element

or

type createUnit [position, group, init, skill, rank] // returning nothing

 

For some reason, @dragonmalice choose to work with the 2nd one (avoidable). That means, the only link between your whole code and the created element as object (not class), is this specific variable THIS, intentionally working inside the init stringed code. So... what you write for this init code is working exactly as same as an init code of an edited unit! No question, no mean about _x or any other undefined local variable.

 

On the other hand, _x is a specific variable working inside the scope of a forEach loop, but you can't pass it inside the stringed init code (see KK's explanation at the bottom of createUnit).

More precisely, when you write:

_element createUnit [_x, _holdGroup, "this disableAI 'PATH'; this setPosATL _x;"]; that seems to be the 2nd syntax. In any way, you can mix the syntaxes. So:

_element must be a class of unit;

_x must be a position, with multiple possibilities :Object, Group or Array format Position or Position2D (but you could check for valid one by diag_log in rpt file); MAKE SURE these positions are already workable.

_holdGroup, a group;

init a stringed code referring to THIS, and nothing else, and not _x because we are not in the same scope;

skill, rank are optional (ok).

https://community.bistudio.com/wiki/Variables#Scopes

http://killzonekid.com/arma-scripting-tutorials-variables-part-1/ and follow links for part2 & 3

 

As mrcurry wrote, this setPosATL _x, as whole code,  in an init code no matter which one, definitely can't work, what ever arma's version is.

 

That said, it seems createUnit worked in ATL position in your 2.16 video, then AGLS in 2.18 (or equivalent). There is no information about that in last SPOTREP... So, perhaps, you should open a ticket.

 

Little workaround:

- position array must be a global variable (not a local one):

  something like : spawnPoints  instead of _spawnPoints

- then, your stringed init code can be:

"this setpos (spawnPoints deleteAt (floor random count spawnPoints))"

for something like:

{ {_element createUnit [[0,0,0], _holdGroup, "this setpos (spawnPoints deleteAt (floor random count spawnPoints))"  } forEach spawnPoints;

(where _element and _holdGroup are previously defined of course)

As you can see, you can spawn at [0,0,0] as you teleport the created unit to one of the positions, never the same (deleteAt). This position array is emptied.

The fact is you can't choose which unit is teleported on which position.

 

 

  • Like 1

Share this post


Link to post
Share on other sites

First of all, thank you - I originally thought I had lost the init functionality completely.  At least as of 2.18, everything you say is true and I will take all of your advice to heart to adapt.  I want to apologize for not being more coherent in my first post 🙂

 

I have a sinking feeling that it's working properly now and I was benefiting from a bug before, but ya, I'll try a ticket and see how it goes.  Here's the example I should have led with!

 

_position = getPosATL player;
_thisGroup = createGroup [east, true];
_element = "B_Soldier_F";
_soldier = "";
_element createUnit [_position, _thisGroup, "this setPosATL _position; this disableAI 'ALL'; _soldier = this;"];
hint format ["%1", _soldier];

 

Or just watch the video!

 

Share this post


Link to post
Share on other sites
7 hours ago, dragonmalice said:

First of all, thank you - I originally thought I had lost the init functionality completely.  At least as of 2.18, everything you say is true and I will take all of your advice to heart to adapt.  I want to apologize for not being more coherent in my first post 🙂

 

I have a sinking feeling that it's working properly now and I was benefiting from a bug before, but ya, I'll try a ticket and see how it goes.  Here's the example I should have led with!

 

_position = getPosATL player;
_thisGroup = createGroup [east, true];
_element = "B_Soldier_F";
_soldier = "";
_element createUnit [_position, _thisGroup, "this setPosATL _position; this disableAI 'ALL'; _soldier = this;"];
hint format ["%1", _soldier];

 

Or just watch the video!

 



It was a bug, i asked on Arma discord when i helped someone with the same problem.
This was never supposed to work
 

_soldier = "";
_element createUnit [_position, _thisGroup, "this setPosATL _position; this disableAI 'ALL'; _soldier = this;"];

The _soldier var inside the createUnit is a local variable and not inside the same scope as the parent script.
So it will never change _soldier = ""

Share this post


Link to post
Share on other sites

All right, barring anyone wanting to discuss further, this is the last post!  I need to spend some time experimenting to find the best combination of solutions, and the best performance.  That said, I can say that this does work in 2.18 - I don't know how efficient it would be as yet.  I know it seems like I'm just being stubborn, but multiple instances of my spawn script can be running at once, so using global variables presents its own problems.  

 

I previously needed to do the same thing with script based triggers (get local variables into the init), and realized that there is one thing that exists in all scopes - the trigger itself.  If you attach a variable to the trigger, you can access it from either scope.  While the unit isn't defined yet in the higher scope here, the GROUP is!   

 

_position = getPosATL player; 
_thisGroup = createGroup [east, true]; 
_thisGroup setVariable ["MPG_test", _position];
_element = "B_Soldier_F"; 
_element createUnit [_position, _thisGroup, 
"
  _group = group this; 
  _position = _group getVariable 'MPG_test'; 
  this setPosATL _position; 
  this disableAI 'ALL'; 
  _soldier = this;
  _group setVariable ['MPG_soldier', _soldier];
"];
_soldier = _thisGroup getVariable "MPG_soldier";
hint format ["%1", _soldier];

 

Again, tested real quick in 2.18 and it does work.  Thanks again for putting up with me 🙂

 

 

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

×