Jump to content
Zenophon

Zenophon's ArmA 3 Co-op Mission Making Framework

Recommended Posts

Thank you for the reply Zeno. I am a basic 'beginner' in scripting you can say. Not 100% noob. I make and play SP missions alone, hence honestly I do not decorate my init.sqf with titles as the above example. Like I usually do with mods, I just copy pasted your call script lines along with relevant sqf files in mission folder. My init.sqf is bland as:

0 = [p1, 1600, 10] call Zen_SpawnAmbientVehicles;

0 = [m1, east, 0.8, 3] call Zen_SpawnInfantry;

I believe the scripts with text "0 =...." or "null = " dont come in init.sqf but rather in unit init or trigger. But like I said, I tried putting these there also. Anyway I will try your approach tonight.

In layman's terms, can you please explain to me where the script lines with "..call so-and-so" and "....execVM so-and-so.sqf" are put? Ive seen execVM's are usually in init.sqf. But call function? and I also observed that in your "call" lines you have not put ".sqf" in the end. Is it a usual practice to leave ".sqf" word in "call" lines?

Thnks man :D

The Init.sqf template is to help scripters with locality, order of execution, and make sure they're using the framework functions as intended. The titleText is optional, but the others are not. Again, I really recommend reading at least the topmost readme.txt file and starting with the tutorials; their goal is to teach how to setup the framework and use its functions (other documentation will help with more advanced scripting using the framework).

The 'call' command is used to run code that already been compiled into a variable of type 'code' (it's an official type). execVM is used to compile a script (.sqf file) then run the function (compiled code). The framework compiles its many functions like so:

Zen_ArrayAppend = compileFinal preprocessFileLineNumbers "Zen_FrameworkFunctions\Zen_DataFunctions\Zen_ArrayAppend.sqf";
So that they are preprocessed and compiled only once, then you can use call or spawn on that variable (hence no .sqf ending) to run them:

0 = [_array, 12345] call Zen_ArrayAppend;
This is simply more efficient than execVM (which recompiles every time) and makes the framework a more professional-looking API. Call also allows the function to run in a lower scope, which is preferable in many cases (I'll stop there because you asked for layman explanation).

0 = ... is technically incorrect (can't assign to literal), but allowed if the function explicitly returns something (even void). It's just to improve code readability and '0' is faster to type than 'null'. None of these will cause an error:

0 = call {(1)};
0 = 1;
0;

Share this post


Link to post
Share on other sites

The Init.sqf template is to help scripters with locality, order of execution, and make sure they're using the framework functions as intended. The titleText is optional, but the others are not. Again, I really recommend reading at least the topmost readme.txt file and starting with the tutorials; their goal is to teach how to setup the framework and use its functions (other documentation will help with more advanced scripting using the framework).

The 'call' command is used to run code that already been compiled into a variable of type 'code' (it's an official type). execVM is used to compile a script (.sqf file) then run the function (compiled code). The framework compiles its many functions like so:

Zen_ArrayAppend = compileFinal preprocessFileLineNumbers "Zen_FrameworkFunctions\Zen_DataFunctions\Zen_ArrayAppend.sqf";
So that they are preprocessed and compiled only once, then you can use call or spawn on that variable (hence no .sqf ending) to run them:

0 = [_array, 12345] call Zen_ArrayAppend;
This is simply more efficient than execVM (which recompiles every time) and makes the framework a more professional-looking API. Call also allows the function to run in a lower scope, which is preferable in many cases (I'll stop there because you asked for layman explanation).

0 = ... is technically incorrect (can't assign to literal), but allowed if the function explicitly returns something (even void). It's just to improve code readability and '0' is faster to type than 'null'. None of these will cause an error:

0 = call {(1)};
0 = 1;
0;

Thank you Zen. That is one amazing advice. Some clouds of "scripting" fog just disappeared from over my head :D

Share this post


Link to post
Share on other sites

Greetings Zenophon,

 

I've started using your framework and I am impressed.  Very well done!  I'm not sure if you've mentioned it but I'd wager you move around 1s and 0s for a living.  If you say "no I'm a firefighter" or something then I'm going to LOL for sure.

 

I have a suggestion for an expansion on the framework.  I see you have Zen_SpawnAmbientVehicles to pop civvie vehicles around towns.   Could you create some more functions to deal with populating civilians?  Something like:

 

Zen_SpawnAmbientCivilians
 
Spawns (3) civilians in and around all towns and cities
within (2) meters from (1).  Towns and cities are printed on the game map.
Usage: Call
Params: 1. Array, group, object, string, the center
        2. Scalar, the distance
        3. Scalar, the number of civilians, or Array:
            1. Scalar, the minimum number of civilians
            2. Scalar, the maximum number of civilians
 (opt.) 4. String, the faction of the vehicle to spawn  (default: Civ_F)  <--- So we could use custom addon civilains
 (opt.) 5. Placement of civilians  0=outside, 1 = mixed, 2 = indoors (default: 0)
Return: Array of objects, the spawned civilians
 
 
And to complete the idea, maybe something to make them move around a little:
 
Zen_OrderCiviliansToWander
 
Orders all (1) to wander within the area (2)
Usage: Call
Params: 1. Array, the civilians to order
      OR
              1. Area marker, the area containing civilians to order.
              2. Area marker, the area for civilians to wander.
Return: Void
 
Would something like that be doable?  Thank you for all the hours you have put into this framework, great stuff!
 
 

Share this post


Link to post
Share on other sites

Also on a different note, would something like this be possible if put in a .sqf that is called from a trigger on activation:

 

Bascially I'm trying to create an endless stream of OPFOR while the players are in a certain area marker.  It randomly chooses a player to see if they are in the area, then if no enemy group exists, it makes one near the player.   On the next loop, if the group is too far away or dead, the group is deleted, allowing another group to be spawned on the next cycle.  The script below might not work as is, I have yet to test it.  (the infantry units declared are custom)

 

Would your functions that I use work outside of init.sqf?

_opforGrp1 = [];

_aliveOpforGrp1 = [];

_patrol;

ParaGameLogic globalChat "Starting random Battle script.";
While {true} do 
{
	_players = units group X11;
	_numPlayers = count _players;
	_randPlayer = floor(random _numPlayers);
	_playerLoc = getpos _randPlayer;
	_isInArea = [_randPlayer,"MkCityCenter"] call Zen_AreInArea;
	
	if (_isInArea) then
	{	
		if (_opforGrp1 == []) then
		{
			_randomPos = [_randPlayer,[200,500]] call Zen_FindGroundPosition;
			sleep 3;
			_opforGrp1 = [_randomPos,["isc_is_squad_leader_o","isc_is_grenadier_o","isc_is_rifleman_o","isc_is_militaman_o","isc_is_irregular_o","isc_is_autorifleman_o","isc_is_machinegunner_o","isc_is_sniper_o","isc_is_at_o"]] call Zen_SpawnGroup;
			0 = [_opforGrp1,_playerLoc] spawn Zen_OrderInfantryMove;
		}
		else
		{
			_aliveOpforGrp1 = ["MkCityCenter",[],_opforGrp1] call Zen_GetAllInArea;
			if (_aliveOpforGrp1 = []) then  // all dead
			{
				_opforGrp1 = [];
			else
				_opforGrp1Dist = [_opforGrp1] call Zen_FindCenterPosition;
				_nearest = objNull;
				_nearestdist = 1000;
				{
					_dist= vehicle _x distance _opforGrp1Dist;
					if (isPlayer _x and _dist < _nearestdist) then
					{
						_nearest= _x;
						_nearestdist = _dist;
					};
				} forEach playableUnits;
				if (!isNull _nearest) then
				{
					_patrol = [_opforGrp1,_playerLoc] spawn Zen_OrderInfantryMove;
				}
				else  // group is over 1k away from any player, so delete them
				{	
					terminate _patrol;
					{
					deleteVehicle _x;
					} forEach units _opforGrp1;
					_opforGrp1 = [];
				};
			};
		};	
	};
	sleep 120;
};

Share this post


Link to post
Share on other sites

Greetings Zenophon,

I've started using your framework and I am impressed. Very well done! I'm not sure if you've mentioned it but I'd wager you move around 1s and 0s for a living. If you say "no I'm a firefighter" or something then I'm going to LOL for sure.

I have a suggestion for an expansion on the framework. I see you have Zen_SpawnAmbientVehicles to pop civvie vehicles around towns. Could you create some more functions to deal with populating civilians? Something like:

Zen_SpawnAmbientCivilians

Spawns (3) civilians in and around all towns and cities

within (2) meters from (1). Towns and cities are printed on the game map.

Usage: Call

Params: 1. Array, group, object, string, the center

2. Scalar, the distance

3. Scalar, the number of civilians, or Array:

1. Scalar, the minimum number of civilians

2. Scalar, the maximum number of civilians

(opt.) 4. String, the faction of the vehicle to spawn (default: Civ_F) <--- So we could use custom addon civilains

(opt.) 5. Placement of civilians 0=outside, 1 = mixed, 2 = indoors (default: 0)

Return: Array of objects, the spawned civilians

And to complete the idea, maybe something to make them move around a little:

Zen_OrderCiviliansToWander

Orders all (1) to wander within the area (2)

Usage: Call

Params: 1. Array, the civilians to order

OR

1. Area marker, the area containing civilians to order.

2. Area marker, the area for civilians to wander.

Return: Void

Would something like that be doable? Thank you for all the hours you have put into this framework, great stuff!

I'm actually a college student (physics major). I do various programming and computer stuff for fun. Zen_SpawnInfantry, Zen_SpawnInfantryGarrison, and Zen_OrderInfantryPatrol work fine with civilians. So spawning them looks a lot like other spawning code:

_civGroups = [];
for "_i" from 1 to 10 do {
    _pos = [_mkTown] call Zen_FindGroundPosition;
    if (random 1 > 0.5) then {
        _group = [_pos, civilian, 0, 1] call Zen_SpawnInfantry;
        _civGroups pushBack _group;
    } else {
        _group = [_pos, civilian, 0, 1] call Zen_SpawnInfantryGarrison;
    };
};

0 = [_civGroups, _mkTown, [], 0, "limited", "careless", false] spawn Zen_OrderInfantryPatrol;
Civilians may freak out and run away when they see combat. If you don't want that, you can spawn them as resistance, make them captive, and give them civilian clothes. Also, the Co-6 Sweep mission from my ZMCM pack has a lot of stuff like this with civilians.

 

Also on a different note, would something like this be possible if put in a .sqf that is called from a trigger on activation:

Bascially I'm trying to create an endless stream of OPFOR while the players are in a certain area marker. It randomly chooses a player to see if they are in the area, then if no enemy group exists, it makes one near the player. On the next loop, if the group is too far away or dead, the group is deleted, allowing another group to be spawned on the next cycle. The script below might not work as is, I have yet to test it. (the infantry units declared are custom)

Would your functions that I use work outside of init.sqf?

_opforGrp1 = [];

_aliveOpforGrp1 = [];

_patrol;

ParaGameLogic globalChat "Starting random Battle script.";
While {true} do 
{
    _players = units group X11;
    _numPlayers = count _players;
    _randPlayer = floor(random _numPlayers);
    _playerLoc = getpos _randPlayer;
    _isInArea = [_randPlayer,"MkCityCenter"] call Zen_AreInArea;
    
    if (_isInArea) then
    {	
        if (_opforGrp1 == []) then
        {
            _randomPos = [_randPlayer,[200,500]] call Zen_FindGroundPosition;
            sleep 3;
            _opforGrp1 = [_randomPos,["isc_is_squad_leader_o","isc_is_grenadier_o","isc_is_rifleman_o","isc_is_militaman_o","isc_is_irregular_o","isc_is_autorifleman_o","isc_is_machinegunner_o","isc_is_sniper_o","isc_is_at_o"]] call Zen_SpawnGroup;
            0 = [_opforGrp1,_playerLoc] spawn Zen_OrderInfantryMove;
        }
        else
        {
            _aliveOpforGrp1 = ["MkCityCenter",[],_opforGrp1] call Zen_GetAllInArea;
            if (_aliveOpforGrp1 = []) then  // all dead
            {
                _opforGrp1 = [];
            else
                _opforGrp1Dist = [_opforGrp1] call Zen_FindCenterPosition;
                _nearest = objNull;
                _nearestdist = 1000;
                {
                    _dist= vehicle _x distance _opforGrp1Dist;
                    if (isPlayer _x and _dist < _nearestdist) then
                    {
                        _nearest= _x;
                        _nearestdist = _dist;
                    };
                } forEach playableUnits;
                if (!isNull _nearest) then
                {
                    _patrol = [_opforGrp1,_playerLoc] spawn Zen_OrderInfantryMove;
                }
                else  // group is over 1k away from any player, so delete them
                {	
                    terminate _patrol;
                    {
                    deleteVehicle _x;
                    } forEach units _opforGrp1;
                    _opforGrp1 = [];
                };
            };
        };	
    };
    sleep 120;
};

All the functions work anywhere after they are compiled at the start of the Init.sqf. Basically anything called from the init.sqf or editor after the mission gets past the briefing (not editor init fields).  There's also the issue with unscheduled execution, see the FAQ.txt answer about that if you're using eventhandlers. For more detail:

https://community.bistudio.com/wiki/Initialization_Order

I discourage the use of editor triggers because they are very hard to maintain/debug. You could use a framework function from an editor trigger activation, but not as it's condition (triggers run before the Init.sqf). What is the equivalent of an editor trigger in SQF is something like this in the Init.sqf (or any script):

0 = [] spawn {
    waitUntil {
        sleep 2;
        (condition)
    };

    activation
};
Why is this better? First, I can set 'sleep 2' to any number I want, so checking every 30 seconds or every frame (remove sleep entirely) is possible. Editor triggers always check per frame (that is rarely necessary).

Second, you can pass arguments to spawn that have been returned by previous function calls; e.g. compute a random position and wait for the player to get there. An editor trigger would need that random position to be a global variable computed before the mission began (some pre-init script).

Third, I can start and stop this SQF code at any time. Just assign it to a function and spawn or terminate it's thread at will. You can create a trigger from SQF code, but then you might as well be typing waitUntil instead of createTrigger.

Sorry for that rant; as for your code, yes, you can put that in a trigger activation (might want to make it a function and call it there) or below a waitUntil loop. The kind of trigger you use will not impact how your code works. Using the spawn/waitUntil method will allow you to pass arguments to the code though; I always find that much clearer to read that using global variables (which you're not using there so...).

Share this post


Link to post
Share on other sites

Hey Zenophon! I have a question and a feature request.

 

The question is, I have a mission where I spawn about 20 - 30 vehicles at the altis airport then I have them patrol around it. Now if I put all the vehicles into an array then call the array for the patrol then everything works fine and the server runs ay 35 FPS at the lowest but if I split the vehicles into two sperate arrays (same amount of vehicles) and call two seperate patrols then the server gets eaten alive running below 7 FPS. I noticed  that this is true for infantry patrols as well although not quite as bad (15 FPS and lower). I have also tried spawning 3 single vehicles and having them patrol the same marker with three seperate patrol calls and it kills the server but to put them all into the same patrol call and it works fine. However spawning a vehicle patrol and infantry patrol at the same time doesn't affect the server. Any thoughts on this? **NOTE. This is all on a dedicated server.

 

Now a feature request related to all your different patrol functions. Is it possible the have an array of seperate markers (or positions with min and max distances etc.) to patrol so that they patrol in consentrated areas but move freely from one area to another. EX. Three seperate marked towns a fair distance apart. The patrol waypoint could be in any of the three marked area's. The vehicles will move from one marked area to another. That way I don't have to use multiple patrols and all the vehicles will move to and patrol all three areas with out the need for a massive patrol area where vehicles will be going to remote and unnessary areas.

 

And as always thanks for your framework. It makes for some awesome missions!

Share this post


Link to post
Share on other sites

Hey Zenophon! I have a question and a feature request.

The question is, I have a mission where I spawn about 20 - 30 vehicles at the altis airport then I have them patrol around it. Now if I put all the vehicles into an array then call the array for the patrol then everything works fine and the server runs ay 35 FPS at the lowest but if I split the vehicles into two sperate arrays (same amount of vehicles) and call two seperate patrols then the server gets eaten alive running below 7 FPS. I noticed that this is true for infantry patrols as well although not quite as bad (15 FPS and lower). I have also tried spawning 3 single vehicles and having them patrol the same marker with three seperate patrol calls and it kills the server but to put them all into the same patrol call and it works fine. However spawning a vehicle patrol and infantry patrol at the same time doesn't affect the server. Any thoughts on this? **NOTE. This is all on a dedicated server.

Now a feature request related to all your different patrol functions. Is it possible the have an array of seperate markers (or positions with min and max distances etc.) to patrol so that they patrol in consentrated areas but move freely from one area to another. EX. Three seperate marked towns a fair distance apart. The patrol waypoint could be in any of the three marked area's. The vehicles will move from one marked area to another. That way I don't have to use multiple patrols and all the vehicles will move to and patrol all three areas with out the need for a massive patrol area where vehicles will be going to remote and unnessary areas.

And as always thanks for your framework. It makes for some awesome missions!

The performance impact should be nowhere near that much. You'll see a decrease at the very start of patrolling, as it has to calculate waypoints for 30 vehicles. After that, it handles whatever vehicles have stopped at 10 second intervals. If the vehicles don't all start moving right at the beginning, it's having troubling finding good positions.

The first thing I found in testing this is that Zen_FindGroundPosition is not handling road positions properly. It allows the road chosen to be outside the area, which is only an issue there are no roads in the area, but some right outside of it. The Airport does have roads around it, so I don't think this is your issue.

I put together this test mission to see what I reproduce; it will also make sure that nothing else in your mission is responsible. All you need is a new mission with a player (object 'X') and a marker ('mkPatrol') anywhere on Altis (except the desert with no roads, I'm working to fix that). I made 'mkPatrol' a 600x600m square, as that nearly covered the airport; the angle and size of the marker should make little difference.

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf";

enableSaving [false, false];

Zen_MP_sideChat = {
    (_this select 0) sideChat str (_this select 1);
};

Zen_Debug_Arguments = false;
if !(isServer) exitWith {};
sleep 1;

_args = [X, "Nothing Spawned"];
ZEN_FMW_MP_REAll("Zen_MP_sideChat", _args, call)

sleep 5;

_args = [X, "30 vehicles Spawning"];
ZEN_FMW_MP_REAll("Zen_MP_sideChat", _args, call)

_vehicleArray = [];
_quantAngles = ["mkPatrol", 0, 360] call Zen_QuantizeAngles;
for "_i" from 1 to 30 do {
    _pos = ["mkPatrol", _quantAngles] call Zen_FindPositionPoly;
    _vehicle = [_pos, "B_MRAP_01_hmg_F"] call Zen_SpawnGroundVehicle;
    _vehicleArray pushBack _vehicle;
};

_args = [X, "30 vehicles Spawned"];
ZEN_FMW_MP_REAll("Zen_MP_sideChat", _args, call)

sleep 5;

_args = [X, "30 vehicles patrolling, 1 thread"];
ZEN_FMW_MP_REAll("Zen_MP_sideChat", _args, call)

_h_Patrol = [_vehicleArray, "mkPatrol"] spawn Zen_OrderVehiclePatrol;

sleep 60;

_args = [X, "30 vehicles patrolling, 2 threads"];
ZEN_FMW_MP_REAll("Zen_MP_sideChat", _args, call)

terminate _h_Patrol;
_h_Patrol1 = [([_vehicleArray, 0, 14] call Zen_ArrayGetIndexedSlice), "mkPatrol"] spawn Zen_OrderVehiclePatrol;
_h_Patrol2 = [([_vehicleArray, 15] call Zen_ArrayGetIndexedSlice), "mkPatrol"] spawn Zen_OrderVehiclePatrol;

On a dedicated server, use '#monitor 1' and watch the FPS; the mission will tell you which stage it's in. When I ran it, I saw the expected FPS drop for spawning the cars and when starting the patrols, but it remained at 49-50 the rest of the time. The vehicles should also respond promptly to the move commands when a thread starts.

As for patrolling multiple areas, I see how having the number of vehicles in each area change dynamically would be good. Currently there's only one documented way to do this using 1 thread, that is blacklist markers. Using a single large marker and then blacklisting areas using many other markers will work. The issue is that it can be tedious to place the blacklist markers so that you get the shape you want; it also decreases performance as the number of blacklist markers grows.

An easy way to do this would be to give the patrol functions the full blacklist, whitelist, whitelist parameter for Zen_FindGroundPosition. It's actually already possible to do this, but isn't allowed by the patrol functions' argument checking. All I have to do is allow [[], ...] formats for the blacklist and change the documentation to tell people this is possible.

You can use a single large marker, and the 'one' whitelist (the point must be in at least one of the markers). This could be inefficient if the markers are very far apart, but then the patrolling units would have a long way to go between them, so it's up to the mission maker to use it in a sensible way. If you've disabled argument checking, you can try this right now.

Share this post


Link to post
Share on other sites

So I was using a different way by using Zen_ArrayAppend rather than pushback (which is simpiler to use).

vehAirbaseArray = [];

[] spawn {
	_vehAirbaseArray = [];
	for "_i" from 1 to (5 + round random 5) do {
		_ObjectivePos1 = ["AIRBASE", 0, ["RaceTrack"]] call Zen_FindGroundPosition;
		_enemyVeh1 = [_ObjectivePos1,["O_MBT_02_cannon_F","I_MBT_03_cannon_F"]] call Zen_SpawnGroundVehicle;
		0 = [_vehAirbaseArray, _enemyVeh1] call Zen_ArrayAppend;
	};
	0 = [vehAirbaseArray, _vehAirbaseArray] call Zen_ArrayAppend;
};

0 = [vehAirbaseArray, "AIRBASE", ["RaceTrack"]] spawn Zen_OrderVehiclePatrol;

I spawn about five of these at the same time to control the amount of each vehicle type is created. each has it's own local array then appended into the main array...vehAirbaseArray

Do you suppose that this method was what was causing the problems as now that I look at it, I think it was creating a nested array for the patrol order?

Share this post


Link to post
Share on other sites

So I was using a different way by using Zen_ArrayAppend rather than pushback (which is simpiler to use).

vehAirbaseArray = [];

[] spawn {
	_vehAirbaseArray = [];
	for "_i" from 1 to (5 + round random 5) do {
		_ObjectivePos1 = ["AIRBASE", 0, ["RaceTrack"]] call Zen_FindGroundPosition;
		_enemyVeh1 = [_ObjectivePos1,["O_MBT_02_cannon_F","I_MBT_03_cannon_F"]] call Zen_SpawnGroundVehicle;
		0 = [_vehAirbaseArray, _enemyVeh1] call Zen_ArrayAppend;
	};
	0 = [vehAirbaseArray, _vehAirbaseArray] call Zen_ArrayAppend;
};

0 = [vehAirbaseArray, "AIRBASE", ["RaceTrack"]] spawn Zen_OrderVehiclePatrol;

I spawn about five of these at the same time to control the amount of each vehicle type is created. each has it's own local array then appended into the main array...vehAirbaseArray

Do you suppose that this method was what was causing the problems as now that I look at it, I think it was creating a nested array for the patrol order?

pushBack vs. Zen_ArrayAppend is nearly equivalent; Zen_ArrayAppend just allows more arguments (like a loop of pushBack commands). 'spawn' and 'spawn' will give Zen_OrderVehiclePatrol vehAirbaseArray before it's been populated. If you wanted spawning in a separate thread:

[] spawn {
    _vehAirbaseArray = [];
    for "_i" from 1 to (5 + round random 5) do {
        _ObjectivePos1 = ["AIRBASE", 0, ["RaceTrack"]] call Zen_FindGroundPosition;
        _enemyVeh1 = [_ObjectivePos1,["O_MBT_02_cannon_F","I_MBT_03_cannon_F"]] call Zen_SpawnGroundVehicle;
        0 = [_vehAirbaseArray, _enemyVeh1] call Zen_ArrayAppend;
    };

    0 = [_vehAirbaseArray, "AIRBASE", ["RaceTrack"]] spawn Zen_OrderVehiclePatrol;
};

Or if you're repeating the spawning loops, which I think you're describing, something like (this is pseudocode now):

Global_Array = [];
_threads = [];
{
    _h_spawn = _x spawn {
        // spawning code
        // append to Global_Array
    };

    _threads pushBack _h_spawn;
} forEach _argsArray;

waitUntil {
    sleep 1;
    (({scriptDone _x} count _threads) == count _threads)
};

0 = [Global_Array, ...] spawn Zen_OrderVehiclePatrol;

as spawning many vehicles at random positions can take several seconds; Zen_OrderVehiclePatrol (or really Zen_ConvertToObjectArray) will copy the given array and not use any updates you make to it.

So long as spawn one patrol thread (for all patrols functions), the performance impact will be the same. As I say, spawning and the initial assignment of waypoints will be a FPS dip, but things should be smooth after that. At 10 sec interval, even 10 Zen_OrderVehiclePatrol threads wouldn't have much performance impact. They're only calculating positions when a vehicle stops (which tends to happen at different times for every vehicle).

Share this post


Link to post
Share on other sites

 

pushBack vs. Zen_ArrayAppend is nearly equivalent; Zen_ArrayAppend just allows more arguments (like a loop of pushBack commands). 'spawn' and 'spawn' will give Zen_OrderVehiclePatrol vehAirbaseArray before it's been populated. If you wanted spawning in a separate thread:

 I actually have a waitUntil check running that the patrol order isn't given until all the spawned - vehicle spawning scripts have completed. I had just pulled the code out to show you how I was populating the array.

 

I'm not sure why if I run more than one patrol order at the same time it has such a negative impact on my server.

 

As for using multiple markers in one patrol order, would it be possible to have the code select a random marker from the array every time it creates a new waypoint for each vehicle if more than one marker (or area to patrol) is specified?

 

Also is there a way to turn off the roads only way points in the patrols in case I want them to patrol area's without roads (say with tanks)?

Share this post


Link to post
Share on other sites

I actually have a waitUntil check running that the patrol order isn't given until all the spawned - vehicle spawning scripts have completed. I had just pulled the code out to show you how I was populating the array.

I'm not sure why if I run more than one patrol order at the same time it has such a negative impact on my server.

As for using multiple markers in one patrol order, would it be possible to have the code select a random marker from the array every time it creates a new waypoint for each vehicle if more than one marker (or area to patrol) is specified?

Also is there a way to turn off the roads only way points in the patrols in case I want them to patrol area's without roads (say with tanks)?

You tried the simple test init I post and you still get a performance drop? That could be a hardware issue on the server.

If it accepts an array of markers, it must accept an array of blacklist arrays, which isn't that bad. However, you get the same functionality with the whitelist for a small lost of efficiency and can use both the blacklist and whitelist together to makes shapes. So I'll just do both, as well as make sure it doesn't break current usages of the function. All patrols functions will get these improvements.

Zen_OrderVehiclePatrol prefers roads and will usually find one. Using setBehaviour 'combat' will make them ignore roads, but the function will set them back to 'aware' for every new waypoint. I can just make roads or no roads a boolean and add a parameter for behaviour for all patrols functions (Zen_OrderInfantryPatrol already has that).

Share this post


Link to post
Share on other sites

Update and Release #32
Introduction

Greetings fellow scripters and Armaholics, in this latest installment, I will continue to discuss the development of the framework and, of course, shamelessly advertise the framework in any way possible.

If this sounds boring, you can download the latest version from the original post. As always, the links to Google Drive for the .7z and .zip versions are already up to date. For those looking for older versions, go to file>revisions. The new version will be on Armaholic soon. Please bring any technical issues or mistakes to my attention, so e.g. people don't download the wrong version etc.

Changelog

Another month, another Wednesday (depending on your time zone), another framework release. This release brings a host of changes to the patrol functions, including multiple patrols areas and the full Zen_FindGroundPosition blacklist/whitelist argument.

All of them, Zen_OrderAircraftPatrol, Zen_OrderBoatPatrol, Zen_OrderInfantryPatrol, and Zen_OrderVehiclePatrol, can be given an array of patrol markers/points, an array of black/white lists, and an array of limited angles. When giving multiple patrols areas, please refer to the documentation (I tried to make it as readable as possible) and remember that when using multiple areas the patrol size/blacklist and limited angles arguments are not optional (use [] and 0 to skip). Also, the black/whitelists and patrol area sizes must align with their respective area in the previous argument. I didn't do a function spotlight with any examples of this, so just post or PM me if the documentation is unclear.

Current usages of these functions are unaffected by the multiple areas change; however, they are affected by the new AI behavior parameter. Check your optional arguments for patrol functions. Zen_OrderInfantryPatrol already had this parameter, but now all of the patrols functions can use it. The former blacklist is the full whitelist/blacklist parameter; this is being passed directly to Zen_FindGroundPosition and works in the exact same way. Zen_OrderVehiclePatrol also has parameter for road positions; false means ignore them entirely.

Zen_FindGroundPosition's handling of road parameter type 1 (prefer roads) has been optimized. The function will check if the nearest road is within the geometric area before selecting it. No other roads will be checked for performance reasons. Larger road search distances will decrease performance; they need only find 1 road. Road parameter types 2 and 3 (require and avoid) are unaffected.

Continuing with the theme of roads, Zen_FindRoadDirection now prints out a full stack trace if there is no road within 50m. You can disable this (along will all argument checking) with Zen_Debug_Arguments = false;.

Zen_OrderVehicleMove now detects helicopter and planes and changes the completion radius accordingly. The goal is the have the function complete execution while those vehicles move over their waypoint. This should help with chaining helicopter waypoints without stopping. Of course, the distances themselves may need to be tweaked.

The framework's header has slightly changed its JIP detection check. This sounds minor but may have unforeseen effects. To be technical, the test of 'player == player' is now 'local player'. The motivation is that establishing locality is done later than defining 'player' and is very important. Please report any issues, bugs, or oddities you find with JIP that may be due to this.

9/2/15

  • Fixed: Zen_FindGroundPosition allowed a selected road to be outside the area, decreasing performance
  • Fixed: Zen_FindRoadDirection prints out a full stack trace when no road is found
  • Fixed: Zen_OrderInfantryPatrol AI pathfinding for avoiding the minimum area
  • Added: Zen_OrderAircraftPatrol, Zen_OrderBoatPatrol, Zen_OrderInfantryPatrol, and Zen_OrderVehiclePatrol parameters for multiple patrol areas
  • Added: Zen_OrderAircraftPatrol, Zen_OrderBoatPatrol, and Zen_OrderVehiclePatrol parameter for AI behavior
  • Added: Zen_OrderVehiclePatrol parameter for road positions
  • Improved: Zen_ArrayGetIndexedSlice now accepts ending indexes greater than the array size
  • Improved: Zen_FindRoadDirection only prints an error if there is no road nearby when Zen_Debug_Arguments is true
  • Improved: Zen_OrderVehicleMove completion condition adapts to the type of vehicle
  • Improved: Framework JIP check
  • Documentation: Improved for Zen_OrderInfantryMove
  • Documentation: Updated for Zen_ArrayGetIndexedSlice, Zen_OrderAircraftPatrol, Zen_OrderBoatPatrol, Zen_OrderInfantryPatrol, Zen_OrderVehicleMove, and Zen_OrderVehiclePatrol
  • Documentation: Updated Notepad++ SQF language and autocompletion file with ArmA 1.50 stable commands


Roadmap

An early version of the new dialog system didn't make it into this update. The issue not a lack of coding, but of coding with the correct design. The style of the API (functions and their arguments) I present is very important and, in my opinion, one of the greatest strengths of the framework. The system must be as intuitive and usable as possible, with few special cases or 'that's complicated' solutions. Then the code behind the design must support it with enough foresight to be generic and extensible so I can add features later.

I'm planning on making a special release in about a week with an early version of the dialogs. The initial features will include buttons, text boxes, and lists. You'll be able to 'hook' a button to a list and get arguments from it. Refreshing and closing of the dialog with be provided for you. It will probably come with it's own demonstration(s) at first, but possibly a sequence of tutorials later. Many features will not be included at first, but almost everything that's possible (and useful) will be added eventually.

Share this post


Link to post
Share on other sites

Hey Zen. Is your framework compatible with ASR AI?

After skimming through the ASR AI source code, here's what I can see. ASR AI is compatible with spawning AI through scripts during the mission. It will automatically detect any spawned soldier that is a subclass of: SoldierWB, SoldierEB, and SoldierGB (use 'isKindOf' on an object if you're not sure); then it applies the skill changes and everything else. ASR AI does not give AI groups waypoints, so it has no conflict with any framework Zen_Order... function.

However, every spawning function that spawns AI, except Zen_SpawnGroup, uses Zen_SetAISkill to set their skills. Whichever function makes the skill changes last will win out, so it just depends upon the timing. To avoid a conflict and use ASR AI's skill settings, you can only use Zen_SpawnGroup to spawn AI. That excludes Zen_SpawnAircraft, Zen_SpawnGroundVehicle, Zen_SpawnHelicopter, Zen_SpawnBoat, Zen_SpawnVehicleCrew, Zen_SpawnConvoy, Zen_SpawnInfantry, Zen_SpawnInfantryGarrison, and the 'officer' and 'pow' objectives of Zen_CreateObjective.

Your best option is to find and comment out every usage of Zen_SetAISkill in the framework. That's not as bad as it sounds, as most of those function don't call Zen_SetAISkill directly. Just put a // at the beginning of the line given for each file:

  • line 39 in Zen_ObjectiveSystem\Zen_SpawnOfficer.sqf
  • line 40 in Zen_ObjectiveSystem\Zen_SpawnPow.sqf
  • line 70 in Zen_SpawningFunctions\Zen_SpawnInfantry.sqf
  • line 150 in Zen_SpawningFunctions\Zen_SpawnVehicleCrew.sqf

That will remove all AI skill changes from the framework. You're free to use this (or any) altered version of the framework as part of any missions you release.

  • Like 1

Share this post


Link to post
Share on other sites

I'm having issues with the framwe work not being able to find the roads. Find ground positions are placing points that are not on roads (vehicles spawning off the roads by large margins (50m or more)) and seems to happen in all the functions that are to locate roads. I also get this error multiple times when the ambient vehicles spawn:

21:36:08 "-- Zen_FindRoadDirection Error --"
21:36:08 "Given point does have not a road within 50 meters."
21:36:08 414.419
21:36:08 [[4141.28,11347,0]]
21:36:08 ["Zen_FindRoadDirection",[[4141.28,11347,0]],414.419]
21:36:08 ["Zen_SpawnAmbientVehicles",["ambveh",4000,[2,5]],135.812]

All this started after the 1.50 patch, my mission started to break. Now I have heli insertions and patrols that spawn but won't move as well.

 

Has anyone else noticed this behaviour?

 

I'm going to verify my game cache to see if I got a bad update, hopefully that's all it is! It could also be one of the mods I'm using I'll also test without them!

Share this post


Link to post
Share on other sites

I'm having issues with the framwe work not being able to find the roads. Find ground positions are placing points that are not on roads (vehicles spawning off the roads by large margins (50m or more)) and seems to happen in all the functions that are to locate roads. I also get this error multiple times when the ambient vehicles spawn:

21:36:08 "-- Zen_FindRoadDirection Error --"
21:36:08 "Given point does have not a road within 50 meters."
21:36:08 414.419
21:36:08 [[4141.28,11347,0]]
21:36:08 ["Zen_FindRoadDirection",[[4141.28,11347,0]],414.419]
21:36:08 ["Zen_SpawnAmbientVehicles",["ambveh",4000,[2,5]],135.812]
All this started after the 1.50 patch, my mission started to break. Now I have heli insertions and patrols that spawn but won't move as well.

Has anyone else noticed this behaviour?

I'm going to verify my game cache to see if I got a bad update, hopefully that's all it is! It could also be one of the mods I'm using I'll also test without them!

This is a failure of Zen_FindGroundPosition in this configuration or similar:

_pos = ["<AreaMarker>", 0, [], 1, [1, 100]] call Zen_FindGroundPosition;
All road points were rejected as falling into the minimum area (0 here). The mistake is this:

([_nearestRoad, getMarkerPos _area, [_min, _min], 0, "ellipse"] call Zen_IsPointInPoly)
is not equal to this:

([_nearestRoad, _area, [_min, _min], 0, "ellipse"] call Zen_IsPointInPoly)
Zen_IsPointInPoly will ignore those extra arguments when _area is an area marker. The maximum geometry is the area marker, the minimum geometry is a circle of the minimum radius. Road selection must obey both constraints properly.

This fix will be in the special update this Wednesday which includes the release of the new Dialog System.

Share this post


Link to post
Share on other sites

I am actually using the getMarkerPos on an area marker which should return a position array.

baseDefendPatrol = [baseDefendVehArray, getMarkerPos "TerminalInside", 400, [0, 360], "normal", "aware", true, false] spawn Zen_OrderVehiclePatrol;

"TerminaInside" is an area marker that I used to just get my center point.

 

On a vehicle patrol spawn I get this error:

23:20:18 "-- Zen_CheckArguments Error --"
23:20:18 "Argument 2 contains a wrong type"
23:20:18 1729.23
23:20:18 [[vehicle_88,vehicle_91,vehicle_89,vehicle_90,vehicle_93,vehicle_92,vehicle_94],[14604.2,16792,0],400,[0,360],"normal","aware",true,false]
23:20:18 ["Zen_CheckArguments",[[vehicle_88,vehicle_91,vehicle_89,vehicle_90,vehicle_93,vehicle_92,vehicle_94],[14604.2,16792,0],400,[0,360],"normal","aware",true,false],1729.23]
23:20:18 ["Zen_OrderVehiclePatrol",[[vehicle_88,vehicle_91,vehicle_89,vehicle_90,vehicle_93,vehicle_92,vehicle_94],[14604.2,16792,0],400,[0,360],"normal","aware",true,false],1729.23]

The error I posted above was for spawning ambient vehicles in which I use just a marker (not area marker) with:

0 = ["ambveh", 4000, [2, 5]] call Zen_SpawnAmbientVehicles;

I hope I explained this well enough and that it's all from the same cause!

Share this post


Link to post
Share on other sites

"Argument 2 contains a wrong type" is an argument checking mistake; a number was not being allowed in the second argument. That will be fixed next release; for now 'Zen_Debug_Arguments = false;' will get you past it.

Zen_SpawnAmbientVehicles is populating town based upon the markers returned from Zen_ConfigGetLocations. That should be fixed indirectly, but I'll test it to make sure.

Share this post


Link to post
Share on other sites

Update and Release #33

Introduction

Greetings fellow scripters and Armaholics, in this latest installment, I will continue to discuss the development of the framework and, of course, shamelessly advertise the framework in any way possible.

If this sounds boring, you can download the latest version from the original post. As always, the links to Google Drive for the .7z and .zip versions are already up to date. For those looking for older versions, go to file>revisions. The new version will be on Armaholic soon. Please bring any technical issues or mistakes to my attention, so e.g. people don't download the wrong version etc.

Changelog

This special release brings an Alpha release of the new Dialog System. Remember, 'Alpha' means that many features are missing; I have many additions and improvements planned, but feel free to suggest more. You may encounter bugs, issues, and limitations; just report them to me and the system will benefit.

The dialog system is a 100% scripted approach to creating dialogs in-game. I used only the UI classes provided in vanilla ArmA and an empty dialog class as a host for dynamically created content. I know almost nothing about writing traditional dialogs in ArmA; I barely managed to create the loadout dialog in the framework using the BI wiki. However, I can now make dozens of different dialogs of any complexity using this system.

I have abstracted everything about dialogs so that users don't have to deal with the countless lines of property setting required for traditional dialogs. Dialogs and Controls are now 'objects' that are assigned 'properties' and trigger 'events'. You can display controls by linking them to a dialog; you can pass arguments ti events by linking controls together.

Finally, I gave all the properties a sensible name and sufficient documentation, so you don't have to wonder what 'period = 1.2' is on a traditional dialog. Instead, you use 'Position', 'Size', etc. in an intuitive way. Traditional dialogs expect you to know what types and styles to use; my dialog system has "Button", "List", and "Text" (more will be added). The point of all this is to make it much faster to create a dialog, and then be able to read and maintain that dialog code.

To expand upon the control type, control property, and function documentation, a new tutorial incorporating and simple but useful dialog into a mission is included. I have also provided a slightly longer example in the Spotlight section of this post. If you have any questions or want further examples, just ask.

 

Planned for next week are generic events from lists, data copy/remove operations on dialogs and controls, and unlinking controls.  I'll also be slowly adding every property and type of control that's possible to script.

9/10/15

  • New Function: Zen_CreateControl
  • New Function: Zen_CreateDialog
  • New Function: Zen_InvokeDialog
  • New Function: Zen_GetControlData
  • New Function: Zen_GetDialogControls
  • New Function: Zen_LinkControl
  • New Function: Zen_UpdateControl
  • Fixed: Zen_FindGroundPosition did not find road positions in an area marker
  • Fixed: Zen_IsReady did not detect some vehicles being stopped
  • Fixed: Zen_OrderAircraftPatrol, Zen_OrderBoatPatrol, Zen_OrderInfantryPatrol, and Zen_OrderVehiclePatrol argument checking did not allow a position for argument 2
  • Documentation: Added tutorial POWDialog
  • Documentation: Added for Zen_CreateDialog, Zen_CreateControl, Zen_InvokeDialog, Zen_GetControlData, Zen_GetDialogControls, Zen_LinkControl, and Zen_UpdateControl

Spotlight

As users of my framework know, there is an enormous number of functions at your disposal. The amount of documentation that has to be sifted through can be extremely daunting. Each release I spotlight a function and talk about its usefulness. If you have found an obscure function (not in tutorials, barely seen in demonstrations or sample missions) that you think is useful, PM me and I can spotlight it.

The functions chosen for this release are: The Dialog System. I have attempted to document this new system as completely as possible; however, I cannot imagine every possible question or concern that may arise. Thus, since the dialog system is a visual effect, I give you an example to experiment with:

F_OKbutton = {
    hint str _this;
};

_dialogID = [] call Zen_CreateDialog;

_controlButton = ["Button", ["Text", "OK"], ["Position", [0, 0]], ["Size", [5,2]], ["Function", "F_OKbutton"]] call Zen_CreateControl;
_controlButtonRefresh = ["Button", ["Text", "Refresh"], ["Position", [0, 2]], ["Size", [5,2]], ["Function", "Zen_RefreshDialog"]] call Zen_CreateControl;
_controlButtonClose = ["Button", ["Text", "Close"], ["Position", [0, 4]], ["Size", [5,2]], ["Function", "Zen_CloseDialog"]] call Zen_CreateControl;
_controlText = ["Text", ["Text", "Hello"], ["Position", [0, 6]], ["Size", [5,2]]] call Zen_CreateControl;
_controlList = ["List", ["List", ["Alpha", "Bravo"]], ["ListData", ["Alpha", "Bravo"]], ["Position", [5, 0]], ["Size", [40,10]]] call Zen_CreateControl;

0 = [_controlButton, ["TextColor", [138,42,132,255]], ["LinksTo", [_controlList]]] call Zen_UpdateControl;

{
    0 = [_dialogID, _x] call Zen_LinkControl;
} forEach [_controlButton, _controlButtonRefresh, _controlButtonClose, _controlText, _controlList];

0 = [_dialogID] spawn Zen_InvokeDialog;

This code contains the three types of controls currently offered and most of the control properties. It shows you the basic steps to create and display a dialog. It doesn't actually do anything, but I'll leave it up to you to do something clever with it.

  • Like 4

Share this post


Link to post
Share on other sites

Hey Zen. I downloaded the new update and I get an error that crashes Arma to desktop that says Zen_LoadoutDialog.hpp is missing!

 

I copied the one from the last update over for now to make it work!

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

×