Jump to content
Blitzen88

Spawning Squads based upon Player Distance and other factors

Recommended Posts

Ladies, Gents, and AC130 Gunships

 

I’m trying to create a script which will spawn an infantry group, which will then exec a building garrison script, when certain conditions are met:


1) Sector A is NOT controlled by the player’s side


2) The player is within X distance from the sector’s trigger

 

By itself, this is not very difficult:


Sector_A getVariable "owner" != Playerside && (Sector_A_Trigger distance player) <= 500

 

However, I also want to delete the squad, and that squad only, if the player’s distance is greater than X.  I also want the script to exec again if the player gets close to the sector’s trigger.  For example:


1) Player gets close to the Sector and the Player’s side does NOT own the Sector A —> Group Spawns it


2) Player travels away from the Sector à The Group, and only that group, is deleted


3) Player again gets close to the Sector and the Player’s side does NOT own the Sector A —> Group Spawns in


4) Etc.

 

Can anyone point me in the direction of a framework that would accomplish this?

Share this post


Link to post
Share on other sites

Seems like you are using a trigger so set the trigger to be repeatable and use the ondeact to execute the removal of the group 

Share this post


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

Seems like you are using a trigger so set the trigger to be repeatable and use the ondeact to execute the removal of the group 

Trying to avoid triggers

Share this post


Link to post
Share on other sites
Spoiler

/*==========================================================================================

Arma III - Spawn AI "Auto Garrison"

Created by Blitzen

===========================================================================================

* Garrisoned AI Units do not work well in AI vs AI Sector Control Missions

* AI Units will not clear buildings and, therefore, units that garrison buildings within a sector capture area will re-capture sectors once the superior force leaves

* Although it is possible to make a sector control mission without garrisoning AI, that takes away the fun of building clearing from the player

* Script tries to fix that by spawning an AI garrison group when certain conditions are met:

* Conditions for Spawning a Garrison Group:

1) The player is within a certain distance of the Sector capture area (sector trigger)

2) The Sector is controlled by the enemy side

3) The player is not within the Sector capture area (sector trigger)

4) A sector garrison group has not already been spawned in

* The Garrison Group will be deleted if the Player travels a certain distance away from the Sector capture area

* Deleting the group will allow AI groups to capture the sector without having to deal with a garrisoned group

* Parameters:

- Enemy Side - The Enemy Side - will determine the side of the garrison group (East / West / Independent)

- Sector - The sector which will be garrisoned.  Obviously, it is not possible to garrison sectors that do not have buildings

- Distance - The distance the player has to be from the sector to trigger spawning a garrison group

- Garrison Loadout - Whether the garrison group should use RHS or Vanilla loadouts (True - RHS, False - Vanilla)

- Delay - How frequently the script loops

* Call with (via Init): Null = [East, "A", 500, True, 20] execVM "Scripts\SpawnAI\SpawnAI_PlayerOnlyGarrison.sqf";

===========================================================================================*/

//Define Input Variables

_EnemySide = _this select 0;

_SelectedSector = _this select 1;

_SpawnDistance = _this select 2;

_ModType = _this select 3;

_Delay = _this select 4;

//Define Variables for Scoping Issues

_Sector = [];

_SectorTrigger = [];

_GarrisonGroup = [];

_AlreadySpawned = False;

//Define Sectors and Sector Triggers based upon Input

Switch (_SelectedSector) do {

// Sector A
//---------------------------------------
case "A" : {

_Sector = Sector_A;

_SectorTrigger = Sector_A_Inner;

};

// Sector B
//---------------------------------------

case "B" : {

_Sector = Sector_B;

_SectorTrigger = Sector_B_Inner;

};

// Sector C 
//---------------------------------------

case "C" : {

_Sector = Sector_C;

_SectorTrigger = Sector_C_Inner;

};

// Sector D 
//---------------------------------------

case "D" : {

_Sector = Sector_D;

_SectorTrigger = Sector_D_Inner;

};

// Sector E 
//---------------------------------------

case "E" : {

_Sector = Sector_E;

_SectorTrigger = Sector_E_Inner;

};

// Sector F 
//---------------------------------------

case "F" : {

_Sector = Sector_F;

_SectorTrigger = Sector_F_Inner;

};

// Sector G 
//---------------------------------------

case "G" : {

_Sector = Sector_G;

_SectorTrigger = Sector_G_Inner;

};

// Sector H
//---------------------------------------

case "H" : {

_Sector = Sector_H;

_SectorTrigger = Sector_H_Inner;

};

};

//Start Loop

while {true} do {

if ( (_SectorTrigger distance player <= _SpawnDistance) && (_Sector getVariable "owner" == _EnemySide) && !(Player inArea _SectorTrigger) && (_AlreadySpawned = False) ) then {

If (_EnemySide isEqualTo East ) then {

_GarrisonGroup = [getPos _SectorTrigger, EAST, (configFile >> "CfgGroups" >> "East" >> "OPF_F" >> "Infantry" >> "OIA_InfSquad")] call BIS_fnc_spawnGroup;

};

If (_EnemySide isEqualTo West ) then {

_GarrisonGroup = [getPos _SectorTrigger, WEST, (configFile >> "CfgGroups" >> "West" >> "BLU_F" >> "Infantry" >> "BUS_InfSquad")] call BIS_fnc_spawnGroup;

};

If (_EnemySide isEqualTo Independent ) then {

_GarrisonGroup = [getPos _SectorTrigger, Independent, (configFile >> "CfgGroups" >> "INDEP" >> "IND_F" >> "Infantry" >> "HAF_InfSquad")] call BIS_fnc_spawnGroup;

};

[getPosATL _SectorTrigger, units _GarrisonGroup, 50, False, False, False, False, 5] execVM "Scripts\AI\AI_Garrison_Zen.sqf";

if (_ModType) then {

_Conversion = {_x execVM "Scripts\Loadouts\RHS\Loadout_RHS_Default.sqf"} foreach units _GarrisonGroup;

};

_AlreadySpawned = True;

};

if (_SectorTrigger distance player > _SpawnDistance) then {

{deletevehicle _x} foreach _GarrisonGroup;

_AlreadySpawned = False;

};

Sleep _Delay;

//End Loop
};

Heres what I came up with. Havent tested it yet. 
 

Is there any way to trigger spawning via a distance eventhandler?

Share this post


Link to post
Share on other sites
On 8/12/2022 at 2:49 PM, Blitzen88 said:

Trying to avoid triggers

Spoiler

913.gif

 

On 8/12/2022 at 4:25 PM, Blitzen88 said:

Is there any way to trigger spawning via a distance eventhandler?

You mean like an onEnterArea/onExitArea eventhandler? That's one reason of why there are triggers ^^

 

Now we don't have to hand-place every trigger, we can just spawn them as needed.

Assuming your preferred detection area is a circle defined by _SectorTrigger's position and _SpawnDistance this should do the trick.

NOTES: I've checked syntax and only cursory logic based on what you had already written. It's mostly for learning purposes. Let me know if there are issues. You didn't specify if it was for SP or MP environment so I just assumed something that works for both.

Spoiler

// If MP and not host then exit
if(!isServer) exitWith {};

//Define Input Variables
params ["_EnemySide", "_SelectedSector", "_SpawnDistance", "_ModType", "_Delay"];

//Define Sectors and Sector Triggers based upon Input, instead of listing each one we can get their variable references directly from missionNamespace.
private _Sector 	= missionNamespace getVariable [ format ["Sector_%1", _SelectedSector], objNull ];
private _SectorTrigger 	= missionNamespace getVariable [ format ["Sector_%1_Inner", _SelectedSector], objNull ];

// Define which groups to use
private _sideGroupMap = createHashMapFromArray [
	[EAST, configFile >> "CfgGroups" >> "East" >> "OPF_F" >> "Infantry" >> "OIA_InfSquad"],
	[WEST, configFile >> "CfgGroups" >> "West" >> "BLU_F" >> "Infantry" >> "BUS_InfSquad"],
	[Independent, configFile >> "CfgGroups" >> "INDEP" >> "IND_F" >> "Infantry" >> "HAF_InfSquad"]
];

// Create a new trigger for the area centered on _SectorTrigger
private _DetectionTrigger = createTrigger ["EmptyDetector", getPos _SectorTrigger, false];
// Sets up a circular area of size _SpawnDistance around _SectorTrigger
_DetectionTrigger setTriggerArea [_SpawnDistance, _SpawnDistance, 0, false];
// Make sure activation only happens for players and is repeatable
_DetectionTrigger setTriggerActivation ["ANYPLAYER", "PRESENT", true];
// Make sure our own "eventhandlers" are called on activation and deactivation
_DetectionTrigger setTriggerStatements [
	"this",
	"0 = thisTrigger spawn TAG_fnc_onPlayerEnterSector;",
	"0 = thisTrigger spawn TAG_fnc_onPlayerExitSector;"
];
// Set interval between checks
_DetectionTrigger setTriggerInterval _Delay;

// Initialize variables holding our data for the trigger code to use
_DetectionTrigger setVariable ["garrisonUnits", []];
_DetectionTrigger setVariable ["sectorParams", [_Sector, _EnemySide, _sideGroupMap, _ModType]];

// Define onEnter and onExit "eventhandler" functions (these could also be defined outside the script using for instance CfgFunctions)
// All scripted functions are essentially just variables pointing to some code data in missionNamespace, this includes "BIS_fnc_*" functions.
if(isNil "TAG_fnc_onPlayerEnterSector") then {
	TAG_fnc_onPlayerEnterSector = {
		private _trigger = _this;
		private _parameters = _trigger getVariable ["sectorParams", []];
		_parameters params ["_sector", "_EnemySide", "_groupMap", "_useMod"];

		// Check if owner of sector is enemy
		if(_sector getVariable "owner" == _EnemySide) then {
			// Check if units are already spawned, if so do nothing
			private _spawnedUnits = _trigger getVariable "garrisonUnits";			
			if(count _spawnedUnits == 0) then {		
				// Spawn the group	
				private _group = [getPos _trigger, _EnemySide, _groupMap get _EnemySide] call BIS_fnc_spawnGroup;
				// Make sure group is deleted when all units are dead and gone
				_group deleteGroupWhenEmpty true;

				// Garrison units
				private _units = units _group;
				[getPosATL _trigger, _units, 50, False, False, False, False, 5] execVM "Scripts\AI\AI_Garrison_Zen.sqf";

				// Apply mod loadout
				if(_useMod) then {
					{ _x execVM "Scripts\Loadouts\RHS\Loadout_RHS_Default.sqf" } foreach _units;
				};

				// Store spawned units, since _spawnedUnits is an array it's passed by reference and append modifies the existing array we don't need to update the variable "garrisonUnits" variable.
				_spawnedUnits append _units;
			};
		};
	};
};

if(isNil "TAG_fnc_onPlayerExitSector") then {
	TAG_fnc_onPlayerExitSector = {
		private _trigger = _this;
		private _spawnedUnits = _trigger getVariable ["garrisonUnits", []];
		
		// Delete
		{ deleteVehicle _x; } forEach _spawnedUnits;
		
		// Reset the stored units
		_trigger setVariable ["garrisonUnits", []];
	};
};

 

 

  • Like 1

Share this post


Link to post
Share on other sites
6 hours ago, mrcurry said:
  Reveal hidden contents

913.gif

 

You mean like an onEnterArea/onExitArea eventhandler? That's one reason of why there are triggers ^^

 

Now we don't have to hand-place every trigger, we can just spawn them as needed.

Assuming your preferred detection area is a circle defined by _SectorTrigger's position and _SpawnDistance this should do the trick.

NOTES: I've checked syntax and only cursory logic based on what you had already written. It's mostly for learning purposes. Let me know if there are issues. You didn't specify if it was for SP or MP environment so I just assumed something that works for both.

  Reveal hidden contents


// If MP and not host then exit
if(!isServer) exitWith {};

//Define Input Variables
params ["_EnemySide", "_SelectedSector", "_SpawnDistance", "_ModType", "_Delay"];

//Define Sectors and Sector Triggers based upon Input, instead of listing each one we can get their variable references directly from missionNamespace.
private _Sector 	= missionNamespace getVariable [ format ["Sector_%1", _SelectedSector], objNull ];
private _SectorTrigger 	= missionNamespace getVariable [ format ["Sector_%1_Inner", _SelectedSector], objNull ];

// Define which groups to use
private _sideGroupMap = createHashMapFromArray [
	[EAST, configFile >> "CfgGroups" >> "East" >> "OPF_F" >> "Infantry" >> "OIA_InfSquad"],
	[WEST, configFile >> "CfgGroups" >> "West" >> "BLU_F" >> "Infantry" >> "BUS_InfSquad"],
	[Independent, configFile >> "CfgGroups" >> "INDEP" >> "IND_F" >> "Infantry" >> "HAF_InfSquad"]
];

// Create a new trigger for the area centered on _SectorTrigger
private _DetectionTrigger = createTrigger ["EmptyDetector", getPos _SectorTrigger, false];
// Sets up a circular area of size _SpawnDistance around _SectorTrigger
_DetectionTrigger setTriggerArea [_SpawnDistance, _SpawnDistance, 0, false];
// Make sure activation only happens for players and is repeatable
_DetectionTrigger setTriggerActivation ["ANYPLAYER", "PRESENT", true];
// Make sure our own "eventhandlers" are called on activation and deactivation
_DetectionTrigger setTriggerStatements [
	"this",
	"0 = thisTrigger spawn TAG_fnc_onPlayerEnterSector;",
	"0 = thisTrigger spawn TAG_fnc_onPlayerExitSector;"
];
// Set interval between checks
_DetectionTrigger setTriggerInterval _Delay;

// Initialize variables holding our data for the trigger code to use
_DetectionTrigger setVariable ["garrisonUnits", []];
_DetectionTrigger setVariable ["sectorParams", [_Sector, _EnemySide, _sideGroupMap, _ModType]];

// Define onEnter and onExit "eventhandler" functions (these could also be defined outside the script using for instance CfgFunctions)
// All scripted functions are essentially just variables pointing to some code data in missionNamespace, this includes "BIS_fnc_*" functions.
if(isNil "TAG_fnc_onPlayerEnterSector") then {
	TAG_fnc_onPlayerEnterSector = {
		private _trigger = _this;
		private _parameters = _trigger getVariable ["sectorParams", []];
		_parameters params ["_sector", "_EnemySide", "_groupMap", "_useMod"];

		// Check if owner of sector is enemy
		if(_sector getVariable "owner" == _EnemySide) then {
			// Check if units are already spawned, if so do nothing
			private _spawnedUnits = _trigger getVariable "garrisonUnits";			
			if(count _spawnedUnits == 0) then {		
				// Spawn the group	
				private _group = [getPos _trigger, _EnemySide, _groupMap get _EnemySide] call BIS_fnc_spawnGroup;
				// Make sure group is deleted when all units are dead and gone
				_group deleteGroupWhenEmpty true;

				// Garrison units
				private _units = units _group;
				[getPosATL _trigger, _units, 50, False, False, False, False, 5] execVM "Scripts\AI\AI_Garrison_Zen.sqf";

				// Apply mod loadout
				if(_useMod) then {
					{ _x execVM "Scripts\Loadouts\RHS\Loadout_RHS_Default.sqf" } foreach _units;
				};

				// Store spawned units, since _spawnedUnits is an array it's passed by reference and append modifies the existing array we don't need to update the variable "garrisonUnits" variable.
				_spawnedUnits append _units;
			};
		};
	};
};

if(isNil "TAG_fnc_onPlayerExitSector") then {
	TAG_fnc_onPlayerExitSector = {
		private _trigger = _this;
		private _spawnedUnits = _trigger getVariable ["garrisonUnits", []];
		
		// Delete
		{ deleteVehicle _x; } forEach _spawnedUnits;
		
		// Reset the stored units
		_trigger setVariable ["garrisonUnits", []];
	};
};

 

 

Thank you for looking at this!

 

Your solution is a little bit over my head - I can get the overall jist of it but Im going to have to learn about the rest. 
 

I thought about triggers but didn't know how to pass the group for deletion without using global variables. 

Share this post


Link to post
Share on other sites
11 minutes ago, Blitzen88 said:

Your solution is a little bit over my head - I can get the overall jist of it but Im going to have to learn about the rest. 

That's cool, Rome wasn't built in a day. If you got any questions just ask.

 

11 minutes ago, Blitzen88 said:

I thought about triggers but didn't know how to pass the group for deletion without using global variables. 

Yeah, they really became part of my toolbox way back when I realized that triggers had their own variable space just like other objects.

 

Don't get me wrong, a scripted loop-solution is just as valid a method for solving this problem. At face value using triggers seem like a good idea 'cause a repeatable trigger inherently has many of the properties that we want.

Like you noticed it does come with scope issues that needs considering and once you start thinking in MP terms there's also locality issues to consider.

 

As far as your original script there are some minor syntax changes needed but the logic is there and should do the essentially the same, though would only work as advertised in a SP environment.

  • At beginning of the loop you check for (_AlreadySpawned = False), this is problematic because = is the assignment operator and returns Nothing which can't be checked by the preceeding && operator.
    To compare booleans use == or just enter (_AlreadySpawned) since the value is a boolean.
  • When deleting the units you need to iterate over forEach units _GarrisonGroup instead of just foreach _GarrisonGroup.

Share this post


Link to post
Share on other sites
1 hour ago, mrcurry said:

As far as your original script there are some minor syntax changes needed but the logic is there and should do the essentially the same, though would only work as advertised in a SP environment.

  • At beginning of the loop you check for (_AlreadySpawned = False), this is problematic because = is the assignment operator and returns Nothing which can't be checked by the preceeding && operator.
    To compare booleans use == or just enter (_AlreadySpawned) since the value is a boolean.
  • When deleting the units you need to iterate over forEach units _GarrisonGroup instead of just foreach _GarrisonGroup.

 

Thanks for pointing these out - I fixed all of these after some initial testing.

 

1 hour ago, mrcurry said:

Don't get me wrong, a scripted loop-solution is just as valid a method for solving this problem. At face value using triggers seem like a good idea 'cause a repeatable trigger inherently has many of the properties that we want.

Like you noticed it does come with scope issues that needs considering and once you start thinking in MP terms there's also locality issues to consider.

 

 

All of my scripts are for single player.  Do you think there is any sort of performance difference between using a loop script vs. creating triggers?  Im really more concerned about the possible performance impact

Share this post


Link to post
Share on other sites

 

On 8/14/2022 at 7:43 PM, Blitzen88 said:

Do you think there is any sort of performance difference between using a loop script vs. creating triggers?  Im really more concerned about the possible performance impact

 

Everything has a performance impact. The question is if it's noticeable.

 

There are differences between the two methods for sure but to quantify them you'd have to run tests.

 

At the number of sectors you are likely to see in a single mission though? 

Let's just say your time is probably better spent elsewhere. 😉

 

First make it work, then make it fast.

 

  • Like 3

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

×