dragonmalice 0 Posted October 12 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
pierremgi 4879 Posted October 12 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. 1 Share this post Link to post Share on other sites
El' Rabito 164 Posted October 12 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; 1 Share this post Link to post Share on other sites
dragonmalice 0 Posted October 13 @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
mrcurry 497 Posted October 15 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
dragonmalice 0 Posted October 15 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
dragonmalice 0 Posted October 15 [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
El' Rabito 164 Posted October 15 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
pierremgi 4879 Posted October 16 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. 1 Share this post Link to post Share on other sites
dragonmalice 0 Posted October 16 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
El' Rabito 164 Posted October 16 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
dragonmalice 0 Posted October 17 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