Jump to content
Zenophon

Zenophon's ArmA 3 Co-op Mission Making Framework

Recommended Posts

Thank you, that worked. I’ve been using this amazing framework for a year now and i don’t know why you put a “0 =†prefix when you call/spawn functions.

Share this post


Link to post
Share on other sites

I do not knew why this is not working for me.

I have huge  lag at start, count allgroups decrease, count allUnits stays at (998)

Unplayable cant even test it.

 

please help.


null = [[x,y,z], [_grp1,_grp2,_grp3]] spawn fnc_cache_loop2;// called 22 times with different pos and groups 
fnc_cache_loop2 = {

	#include "..\Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"
	
	params ["_position", "_groups"];

	private ["_cacheDist", "_cacheArray", "_cacheID", "_indexesToRemove", "_playerGroups", "_players", "_playables"];
    // define the distance at which to cache/uncache
	
    _cacheDist = 1000;
    _cacheArray = [];
	
    _cacheID = [_groups] call Zen_Cache;
    _cacheArray pushBack _cacheID;
   
    while {true} do {
	
        if (count _cacheArray == 0) exitWith {};
        sleep 5;
        _indexesToRemove = [];
		
        {
            sleep 1;
            if ([_x] call Zen_IsCached) then {
                // if the unit is cached, only check if they need to be uncached
                if !([WEST, _position , [_cacheDist, _cacheDist], 360, "ellipse"] call Zen_AreNotInArea) then {
                    0 = [_x] call Zen_UnCache;
                };
            } else {
                _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                // if the units have been uncached, they could have been killed
                if ({alive _x} count _units == 0) then {
                    _indexesToRemove pushBack _forEachIndex;
                } else {
                    // if they are alive, check for caching
                    if ([WEST, _position, [_cacheDist, _cacheDist], 360, "ellipse"] call Zen_AreNotInArea) then {
                        0 = [_x] call Zen_Cache;
                    };
                };
            };
        } forEach _cacheArray;
     
        // keep the marker and cache arrays aligned
        ZEN_FMW_Array_RemoveIndexes(_position, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
    };
};

Share this post


Link to post
Share on other sites

Thank you, that worked. I’ve been using this amazing framework for a year now and i don’t know why you put a “0 =†prefix when you call/spawn functions.

The '0 = ' is just a habit I have now that I developed to indicate that the return value was void or unwanted; I'm just used to seeing it when I skim code looking for function calls (mainly spawned threads or calls that have some side effect).

 

I do not knew why this is not working for me.

I have huge lag at start, count allgroups decrease, count allUnits stays at (998)

Unplayable cant even test it.

please help.

null = [[x,y,z], [_grp1,_grp2,_grp3]] spawn fnc_cache_loop2;// called 22 times with different pos and groups
fnc_cache_loop2 = {
    #include "..\Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"

    params ["_position", "_groups"];

    private ["_cacheDist", "_cacheArray", "_cacheID", "_indexesToRemove", "_playerGroups", "_players", "_playables"];
    // define the distance at which to cache/uncache

    _cacheDist = 1000;
    _cacheArray = [];

    _cacheID = [_groups] call Zen_Cache;
    _cacheArray pushBack _cacheID;

    while {true} do {

        if (count _cacheArray == 0) exitWith {};
        sleep 5;
        _indexesToRemove = [];

        {
            sleep 1;
            if ([_x] call Zen_IsCached) then {
                // if the unit is cached, only check if they need to be uncached
                if !([WEST, _position , [_cacheDist, _cacheDist], 360, "ellipse"] call Zen_AreNotInArea) then {
                    0 = [_x] call Zen_UnCache;
                };
            } else {
                _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                // if the units have been uncached, they could have been killed
                if ({alive _x} count _units == 0) then {
                    _indexesToRemove pushBack _forEachIndex;
                } else {
                    // if they are alive, check for caching
                    if ([WEST, _position, [_cacheDist, _cacheDist], 360, "ellipse"] call Zen_AreNotInArea) then {
                        0 = [_x] call Zen_Cache;
                    };
                };
            };
        } forEach _cacheArray;

        // keep the marker and cache arrays aligned
        ZEN_FMW_Array_RemoveIndexes(_position, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
    };
};

I didn't really highlight this before, but this is supposed to run on only one thread. You have spawned 22 threads by dividing the sets of groups into their own thread. The thread should combine every set of units into a single loop. That's the the only way that you can handle 1000 AI with performance issues. You also have to prevent the 1000 AI from all being uncached at the same time.

_OpforPositions = [];
_cacheArray = [];

// In order to avoid the mission grinding to a halt before the 1000 AI can even spawn
// Cache them as they are spawned to save resources
{
    _grp1 = ...
    _grp2 = ...
    _grp3 = ...

    // into this, combine every set as a nested array into a single array of all opfor
    _cacheID = [[_grp1, _grp2, _grp3]] call Zen_Cache;
    _cacheArray pushBack _cacheID;

    // you align with this an array of all the positions at which the sets were spawned
    _OpforPositions pushBack [x, y, z];
} forEach [...];

// now you give all the data to a single thread
0 = [_OpforPositions, _cacheArray] spawn fnc_cache_loop2;

fnc_cache_loop2 = {
    #include "..\Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"

    _OpforPositions = _this select 0;
    _cacheArray = _this select 1;

    // define the distance at which to cache/uncache
    _cacheDist = 1000;

    while {true} do {
        if (count _cacheArray == 0) exitWith {};
        sleep 1;
        _indexesToRemove = [];

        // Optimize by only getting the array of players every 'while' step
        _players = [West] call Zen_ConvertToObjectArray;

        // now this loop acts on every set of units
        // instead of '_position', use the position in the positions array that aligns with the current set
        {
            sleep 1;
            if ([_x] call Zen_IsCached) then {
                // if the unit is cached, only check if they need to be uncached
                if !([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                    0 = [_x] call Zen_UnCache;
                };
            } else {
                _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                // if the units have been uncached, they could have been killed
                if ({alive _x} count _units == 0) then {
                    _indexesToRemove pushBack _forEachIndex;
                } else {
                    // if they are alive, check for caching
                    if ([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                        0 = [_x] call Zen_Cache;
                    };
                };
            };
        } forEach _cacheArray;

        // keep the marker and cache arrays aligned
        ZEN_FMW_Array_RemoveIndexes(_OpforPositions, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
    };
};

Share this post


Link to post
Share on other sites

Something went wrong. Still unplayable maybe it  all stops because lag.

Can you please check this out?

init.sqf

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf"

enableSaving [false, false];
enableTeamswitch false;

[] call compile preprocessFileLineNumbers "functions\global_fnc.sqf";

if (isServer) then {

private ["_arrayoftaliban", "_cacheboxes", "_cities", "_OpforPositions", "_cacheArray", "_talib_groups", "_areapos",
 "_radius", "_spawnpos", "_talib_count", "_toSpawn", "_talib_grp", "_spawnVehpos", "_talib_veh", "_talib_vehgrp", "_cacheID"];
    
    #define TALIB_VEH (["LOP_AM_UAZ_DshKM", "LOP_ISTS_M1025_W_Mk19", "LOP_ISTS_Landrover_M2", "LOP_AM_Offroad_M2"])
    cacheB = [];
    cacheboxes = [];

    [] call compile preprocessFileLineNumbers "functions\server_fnc.sqf";

    _arrayoftaliban = [
        
    "LOP_AM_Infantry_AR",
    "LOP_AM_Infantry_AT",
    "LOP_AM_Infantry_Corpsman",
    "LOP_AM_Infantry_Engineer",
    "LOP_AM_Infantry_GL",
    "LOP_AM_Infantry_Rifleman",
    "LOP_AM_Infantry_Rifleman_2",
    "LOP_AM_Infantry_Rifleman_3",
    "LOP_AM_Infantry_SL",
    "LOP_ISTS_Infantry_AR",
    "LOP_ISTS_Infantry_AT",
    "LOP_ISTS_Infantry_Corpsman",
    "LOP_ISTS_Infantry_GL",
    "LOP_ISTS_Infantry_Rifleman",
    "LOP_ISTS_Infantry_Rifleman_2",
    "LOP_ISTS_Infantry_Rifleman_3",
    "LOP_ISTS_Infantry_TL"
    ];
        
    _cacheboxes = [
    
            "rhs_3Ya40_1_single",
            "rhs_gear_crate",
            "rhs_7ya37_1_single"
    
    ];
        
    _cities = call fnc_urbanAreas;
    missionNamespace setVariable ["op_areas",_cities,false];
    
_OpforPositions = [];
_cacheArray = [];
_talib_groups = [];

    {
        _areapos = _x select 0;//[x,y,z]
        _radius = _x select 1;//number
    
        for "_i" from 0 to 2 do {
        
            _spawnpos = [_areapos, [0, _radius+100], [], 1, [1,_radius+100],360] call Zen_FindGroundPosition;
            _talib_count = floor random [7,13,(count _arrayoftaliban)];
            _toSpawn = [_arrayoftaliban, _talib_count, false] call Zen_ArrayGetRandomSequence;
            _talib_grp = [_spawnpos, _toSpawn] call Zen_SpawnGroup;
            null = [_talib_grp, "Militia"] call  Zen_SetAISkill;
            _talib_groups pushback _talib_grp;

        };
        
        _spawnVehpos = [_areapos, [0, _radius+100], [], 1, [1,_radius+100],360] call Zen_FindGroundPosition;
        _talib_veh = [_spawnVehpos, 0, selectRandom TALIB_VEH, INDEPENDENT] call bis_fnc_spawnvehicle;
        _talib_vehgrp = _talib_veh select 2;
        null = [_talib_vehgrp, "Militia"] call  Zen_SetAISkill;        
        _talib_groups pushback _talib_vehgrp;
        
        _cacheID = [_talib_groups] call Zen_Cache;
        _cacheArray pushBack _cacheID;
        _OpforPositions pushBack _areapos;


    } forEach _cities;
    
null = [_OpforPositions, _cacheArray] spawn fnc_cache_loop;
null = [980,980,100,true] call Zen_SetViewDistance;
};

 

fnc_cache_loop

fnc_cache_loop = {

    #include "..\Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"
 
    _OpforPositions = _this select 0;
    _cacheArray = _this select 1;
 
    // define the distance at which to cache/uncache
    _cacheDist = 1000;
 
    while {true} do {
	
        if (count _cacheArray == 0) exitWith {};
        sleep 1;
        _indexesToRemove = [];
 
        // Optimize by only getting the array of players every 'while' step
        _players = [West] call Zen_ConvertToObjectArray;
 
        // now this loop acts on every set of units
        // instead of '_position', use the position in the positions array that aligns with the current set
        {
            sleep 1;
            if ([_x] call Zen_IsCached) then {
                // if the unit is cached, only check if they need to be uncached
                if !([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                    0 = [_x] call Zen_UnCache;
                };
            } else {
                _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                // if the units have been uncached, they could have been killed
                if ({alive _x} count _units == 0) then {
                    _indexesToRemove pushBack _forEachIndex;
                } else {
                    // if they are alive, check for caching
                    if ([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                        0 = [_x] call Zen_Cache;
                    };
                };
            };
        } forEach _cacheArray;
 
        // keep the marker and cache arrays aligned
        ZEN_FMW_Array_RemoveIndexes(_OpforPositions, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
    };
};

Share this post


Link to post
Share on other sites

I cut the init.sqf to the minimum, and this is what I tested

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf"

if (!isServer) exitWith {};
sleep 1;

fnc_cache_loop = {
    _OpforPositions = _this select 0;
    _cacheArray = _this select 1;

    // define the distance at which to cache/uncache
    _cacheDist = 1000;

    while {true} do {
        if (count _cacheArray == 0) exitWith {};
        sleep 1;
        _indexesToRemove = [];

        _players = [West] call Zen_ConvertToObjectArray;
        {
            sleep 1;
            if ([_x] call Zen_IsCached) then {
                if !([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                    0 = [_x] call Zen_UnCache;
                };
            } else {
                _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                if ({alive _x} count _units == 0) then {
                    _indexesToRemove pushBack _forEachIndex;
                } else {
                    if ([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                        0 = [_x] call Zen_Cache;
                    };
                };
            };
        } forEach _cacheArray;

        ZEN_FMW_Array_RemoveIndexes(_OpforPositions, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
    };
};

_OpforPositions = [];
_cacheArray = [];
_talib_groups = [];
_talib_vehs = [];

for "_i" from 1 to 25 do {
    _areapos = ["mkAll"] call Zen_FindGroundPosition;
    _radius = 300;

    for "_i" from 1 to 3 do {
        _spawnPos = [_areapos, [0, _radius+100], [], 1, [1,_radius+100]] call Zen_FindGroundPosition;
        _talib_grp = [_spawnPos, east, "militia", [7, 16]] call Zen_SpawnInfantry;
        _talib_groups pushBack _talib_grp;
    };

    _spawnVehpos = [_areapos, [0, _radius+100], [], 1, [1,_radius+100]] call Zen_FindGroundPosition;
    _talib_veh = [_spawnVehpos, east] call Zen_SpawnGroundVehicle;
    _talib_vehgrp = group driver _talib_veh;
    0 = [_talib_vehgrp, "Militia"] call  Zen_SetAISkill;

    _talib_groups pushBack _talib_vehgrp;
    _talib_vehs pushBack _talib_veh;

    _cacheID = [_talib_groups + _talib_vehs] call Zen_Cache;
    _cacheArray pushBack _cacheID;
    _OpforPositions pushBack _areapos;
};

0 = [_OpforPositions, _cacheArray] spawn fnc_cache_loop;

At 25 areas, the fps starts around 60 for me, and declines to around 20-30 during the spawning process, with some stutters to below 10; I did not turn down the graphics from what I normally play at. The spawning process took 165 seconds and spawned 938 AI. While the cache loop was running, I got a fairly constant 35 fps walking around and about 25 while driving fast. If I turn down the graphics to the absolute minumum, I get 45 fps when stationary and 30 when driving fast. All of this was done on Altis, with a i5-6600k at 3.5Ghz, a GTX 970, and 8GB RAM.

I didn't run it on a dedicated server, but I imagine a server with a good CPU will be able to manage 35-45 fps with all of the units cached. I urge you to test this init.sqf exactly as I posted it and in an isolated environment (you just need a player unit and an area marker "mkAll" that covers Altis). If the game is unplayable and locked to 1 fps for you, look at your CPU/GPU usage and temperatures.

Also, in the full mission, if you have many players in different places, more AI will be uncached at once; the scripts cannot control the performance impact of uncached AI. Estimate or count via scripts how many AI are really uncached at once, if it's beyond a hundred or two, expect poor performance if they are all being managed by the server. Check out the framework's headless client demonstration; even running the HC as a separate .exe on the same machine as the server will help.

Share this post


Link to post
Share on other sites

WOOW! This is working absolutely beautiful.

Thank you a lot for you time.

Share this post


Link to post
Share on other sites

HI!

After dozen of hours i have finally get this working.

Now i tried to force un-cached units to do some movements by execute  orders functions for un-cached group/vehicle and

i am getting an error after they get cached again.

I assume this happen because the order function  is working in loop and still active after vehicle has been cached.

 

rpt:

.....
15:12:32 "Argument 1 is void"
15:12:32 1881.03
15:12:32 [<NULL-group>,0]
15:12:32 ["Zen_CheckArguments",[<NULL-group>,0],1881.03]
15:12:32 ["Zen_ValuesAreEqual",[<NULL-group>,0],1881.03]
15:12:32 ["Zen_ArrayRemoveValue",[[<NULL-group>],0],1881.03]
15:12:32 ["Zen_OrderVehiclePatrol",[R Alpha 2-2:65,[6150,1150,0],200],783.733]
15:12:42 "-- Zen_CheckArguments Error --"
15:12:42 "Argument 1 is void"
15:12:42 1891.08
15:12:42 [<NULL-group>,0]
15:12:42 ["Zen_CheckArguments",[<NULL-group>,0],1891.08]
15:12:42 ["Zen_ValuesAreEqual",[<NULL-group>,0],1891.08]
15:12:42 ["Zen_ArrayRemoveValue",[[<NULL-group>],0],1891.08]
15:12:42 ["Zen_OrderVehiclePatrol",[R Alpha 2-2:65,[6150,1150,0],200],783.733]
15:12:52 "-- Zen_CheckArguments Error --"
15:12:52 "Argument 1 is void"
15:12:52 1901.08
15:12:52 [<NULL-group>,0]
15:12:52 ["Zen_CheckArguments",[<NULL-group>,0],1901.08]
15:12:52 ["Zen_ValuesAreEqual",[<NULL-group>,0],1901.08]
15:12:52 ["Zen_ArrayRemoveValue",[[<NULL-group>],0],1901.08]
15:12:52 ["Zen_OrderVehiclePatrol",[R Alpha 2-2:65,[6150,1150,0],200],783.733]
....

 

init.sqf

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf"

// Desert Insurgency by DaVidoSS and Zenophon
// 0.1 ALPHA = 2016/11/11
// Tested with ArmA 3 1.64

// This will fade in from black, to hide jarring actions at mission start, this is optional and you can change the value
titleText ["Good Luck", "BLACK FADED", 0.2];
// Some functions may not continue running properly after loading a saved game, do not delete this line
enableSaving [false, false];
// All clients stop executing here, do not delete this line

if (!isServer) exitWith {};
// Execution stops until the mission begins (past briefing), do not delete this line
sleep 1;

// Enter the mission code here

_fnc_cache_loop = {
	
	#include "Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"
	
        _OpforPositions = _this select 0;
        _cacheArray = _this select 1;
     
        // define the distance at which to cache/uncache
        _cacheDist = 1000;
     
        while {true} do {
            if (count _cacheArray == 0) exitWith {};
            sleep 1;
            _indexesToRemove = [];
     
            _players = [West] call Zen_ConvertToObjectArray;
            {
                sleep 1;
                if ([_x] call Zen_IsCached) then {
                    if !([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
					
                        0 = [_x] call Zen_UnCache;
						
						_talibsGrps = [];
						_talibsin = [(_OpforPositions select _forEachIndex), [300,300], 360, "ellipse", [], Independent] call Zen_GetAllInArea;
						{_talibsGrps pushBackUnique (group _x)} forEach _talibsin;
						_talibVehgrps = _talibsGrps select {count ((units _x) select {(vehicle _x) isKindOf "LandVehicle"}) > 0};
						
						{
							0 = [_x, "Militia"] call Zen_SetAISkill;
							
						} forEach _talibsGrps;

						
						if (count _talibVehgrps != 0) then {
						
							_talibsGrps = _talibsGrps - _talibVehgrps;
							_talibVeh = vehicle (((units (_talibVehgrps select 0)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);
							0 = [_talibVeh,(_OpforPositions select _forEachIndex),200] spawn Zen_OrderVehiclePatrol;
						
						};
						
						switch (count _talibsGrps) do {
						
							case 0: {};
							case 1: {
							 0 = [_talibsGrps,(_OpforPositions select _forEachIndex),[0,150]] spawn Zen_OrderInfantryPatrol;
							};
							
							case 2: {
							0 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,150]] spawn Zen_OrderInfantryPatrol;
							0 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',5] spawn Zen_OrderInfantryPatrolBuilding;
							};
							
							case 3: {
							0 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,100]] spawn Zen_OrderInfantryPatrol;
							0 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',5] spawn Zen_OrderInfantryPatrolBuilding;
							0 = [[_talibsGrps select 2],(_OpforPositions select _forEachIndex),[0,200]] spawn Zen_OrderInfantryPatrol;
							};
							
							case 4: {
							0 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,150]] spawn Zen_OrderInfantryPatrol;
							0 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',5] spawn Zen_OrderInfantryPatrolBuilding;
							0 = [[_talibsGrps select 2],(_OpforPositions select _forEachIndex),[0,200]] spawn Zen_OrderInfantryPatrol;
							0 = [[_talibsGrps select 3],(_OpforPositions select _forEachIndex),[0,100]] spawn Zen_OrderInfantryPatrol;
							};
							default {};
						
						};

                    };
                } else {
                    _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                    if ({alive _x} count _units == 0) then {
                        _indexesToRemove pushBack _forEachIndex;
                    } else {
                        if ([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                            0 = [_x] call Zen_Cache;
                        };
                    };
                };
            } forEach _cacheArray;
     
            ZEN_FMW_Array_RemoveIndexes(_OpforPositions, _indexesToRemove)
            ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
        };
};
	

_fnc_urbanAreas = {

	
	// return array of cities center on every A2/A3 map in format [x,y,z]
	// function correct positions to fit exact sector center
	// usage: _cities = call fnc_urbanAreas;
	private ["_i", "_cities", "_locations", "_cityTypes", "_randomLoc", "_cityPos", "_cityType", "_posX", "_posY", "_rstX", "_rstY"];
	
	_i = 0;
	_cities = [];

	_locations = configfile >> "CfgWorlds" >> worldName >> "Names";
	_cityTypes = ["CityCenter"];
	
	for "_x" from 0 to (count _locations - 1) do {
	
		_randomLoc = _locations select _x;
		_cityPos = getArray(_randomLoc >> "position");
		_cityType = getText(_randomLoc >> "type");
		
			if (typeName (_cityPos select 0) == "String") then {
			
				_cityPos = [call compile (_cityPos select 0),call compile (_cityPos select 1)];
			};
		
			if (_cityType in _cityTypes) then {
			
				//position correction
				_posX = floor (_cityPos select 0);
				_posY = floor (_cityPos select 1);
				_rstX = _posX%50;
				_rstY = _posY%50;
				_posX = (50 - _rstX) + _posX;
				if (_posX%100 == 0) then {_posX = _posX + 50};
				_posY = (50 - _rstY) + _posY;
				if (_posY%100 == 0) then {_posY = _posY + 50};
				_cityPos set [0, _posX];
				_cityPos set [1, _posY];
				_cityPos set [2, 0];
					if (_citypos distance (getMarkerPos "basemarker") > 1200) then {
						
						_cities set [_i,_citypos];
						_i = _i + 1;
						
					};
			};
	};
	
	_cities;
 
};

	_OpforPositions = [];
    _cacheArray = [];
    _talib_groups = [];
    _talib_vehs = [];
	
    _arrayoftaliban = [
        
    "LOP_AM_Infantry_AR",
    "LOP_AM_Infantry_AT",
    "LOP_AM_Infantry_Corpsman",
    "LOP_AM_Infantry_Engineer",
    "LOP_AM_Infantry_GL",
    "LOP_AM_Infantry_Rifleman",
    "LOP_AM_Infantry_Rifleman_2",
    "LOP_AM_Infantry_Rifleman_3",
    "LOP_AM_Infantry_SL",
    "LOP_ISTS_Infantry_AR",
    "LOP_ISTS_Infantry_AT",
    "LOP_ISTS_Infantry_Corpsman",
    "LOP_ISTS_Infantry_GL",
    "LOP_ISTS_Infantry_Rifleman",
    "LOP_ISTS_Infantry_Rifleman_2",
    "LOP_ISTS_Infantry_Rifleman_3",
    "LOP_ISTS_Infantry_TL"
    ];
	
	_arrayoftalibanveh = [
	
	"LOP_ISTS_Landrover_M2",
	"LOP_ISTS_M1025_W_M2",
	"LOP_ISTS_M998_D_4DR",
	"LOP_ISTS_Offroad_M2",
	"LOP_AM_Landrover_M2",
	"LOP_AM_Offroad_M2",
	"LOP_ISTS_BTR60",
	"LOP_AM_BTR60",
	"LOP_AM_UAZ_DshKM",
	"LOP_AM_UAZ_SPG"
	];
	
	_cities = call _fnc_urbanAreas;
	
	{
		_name = format ["mk1_%1",_forEachIndex+1];
		_cityMarker = createMarkerLocal [_name, _x];
		_cityMarker setMarkerTypeLocal "Empty";
		_cityMarker setMarkerSizeLocal [200, 200];
		_cityMarker setMarkerShapeLocal "ELLIPSE";
		_cityMarker setMarkerAlphaLocal 0;
	
	} forEach _cities;
	
	_z = 1;
	
    while {_z <= (count _cities)} do {

	
        _areapos = getMarkerPos format["mk1_%1",_z];
        _radius = 200;
     
        for "_i" from 1 to 3 do {
            _spawnPos = [_areapos, [0, _radius], [], 1] call Zen_FindGroundPosition;
            _talib_count = floor random [7,10,(count _arrayoftaliban)];
            _toSpawn = [_arrayoftaliban, _talib_count, false] call Zen_ArrayGetRandomSequence;
            _talib_grp = [_spawnpos, _toSpawn] call Zen_SpawnGroup;
			waitUntil {sleep 1; count ((units _talib_grp) select {!isNull _x}) isEqualTo _talib_count};
            _talib_groups pushBack _talib_grp;
        };
     
        _spawnVehpos = [_areapos, [0, _radius], [], 1, [2,_radius]] call Zen_FindGroundPosition;
		_talib_veharray = [_spawnVehpos, 0, selectRandom TALIB_VEH, Independent] call bis_fnc_spawnvehicle;//used BIS for custom faction
		_talib_vehgrp = (_talib_veharray select 2);
		_talib_veh = (_talib_veharray select 0);
     
        _talib_groups pushBack _talib_vehgrp;
        _talib_vehs pushBack _talib_veh;
		
        _cacheID = [_talib_groups + _talib_vehs] call Zen_Cache;
		
		sleep 1;

        _cacheArray pushBack _cacheID;
        _OpforPositions pushBack _areapos;
		_z = _z + 1;
    };
	
    waitUntil {sleep 1; _z > (count _cities)};
	
    0 = [_OpforPositions, _cacheArray] spawn _fnc_cache_loop;

 

How to create this correctly?

Share this post


Link to post
Share on other sites

Before calling Zen_Cache, all threads that manage those units must be stopped using terminate. Those threads must be stored along with the set identifiers. For brevity, I will implement that in the previous example I gave. The four additional parts are highlighted by comments. Also note that you can use Zen_GetCachedUnits to get the units listed in their groups more efficiently.

#include "Zen_FrameworkFunctions\Zen_InitHeader.sqf"

if (!isServer) exitWith {};
sleep 1;

fnc_cache_loop = {
    _OpforPositions = _this select 0;
    _cacheArray = _this select 1;
    _threadsArray = _this select 2;
    _cacheDist = 1000;

    while {true} do {
        if (count _cacheArray == 0) exitWith {};
        sleep 1;
        _indexesToRemove = [];

        _players = [West] call Zen_ConvertToObjectArray;
        {
            sleep 1;
            if ([_x] call Zen_IsCached) then {
                if !([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                    0 = [_x] call Zen_UnCache;

                    // Store the handle to any patrol function threads
                    _h_thread1 = [...] spawn Zen_OrderInfantryPatrol;
                    _h_thread2 = [...] spawn Zen_OrderInfantryPatrol;

                    _threadsArray set [_forEachIndex, [_h_thread1, _h_thread2]];
                };
            } else {
                _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                if ({alive _x} count _units == 0) then {
                    _indexesToRemove pushBack _forEachIndex;
                } else {
                    if ([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {

                        // terminate each thread running for this set of units
                        {
                            terminate _x;
                        } forEach (_threadsArray select _forEachIndex);

                        0 = [_x] call Zen_Cache;
                    };
                };
            };
        } forEach _cacheArray;

        ZEN_FMW_Array_RemoveIndexes(_OpforPositions, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
        ZEN_FMW_Array_RemoveIndexes(_threadsArray, _indexesToRemove)
    };
};

// Declare the array for the threads
_threadsArray = [];

_cacheArray = [];
_OpforPositions = [];
_talib_groups = [];
_talib_vehs = [];

for "_i" from 1 to 25 do {
    _areapos = ["mkAll"] call Zen_FindGroundPosition;
    _radius = 300;

    for "_i" from 1 to 3 do {
        _spawnPos = [_areapos, [0, _radius+100], [], 1, [1,_radius+100]] call Zen_FindGroundPosition;
        _talib_grp = [_spawnPos, east, "militia", [7, 16]] call Zen_SpawnInfantry;
        _talib_groups pushBack _talib_grp;
    };

    _spawnVehpos = [_areapos, [0, _radius+100], [], 1, [1,_radius+100]] call Zen_FindGroundPosition;
    _talib_veh = [_spawnVehpos, east] call Zen_SpawnGroundVehicle;
    _talib_vehgrp = group driver _talib_veh;
    0 = [_talib_vehgrp, "Militia"] call  Zen_SetAISkill;

    _talib_groups pushBack _talib_vehgrp;
    _talib_vehs pushBack _talib_veh;

    _cacheID = [_talib_groups + _talib_vehs] call Zen_Cache;
    _cacheArray pushBack _cacheID;
    _OpforPositions pushBack _areapos;

    // Initialize each nested array of threads
    _threadsArray pushBack [];
};

0 = [_OpforPositions, _cacheArray, _threadsArray] spawn fnc_cache_loop;

Share this post


Link to post
Share on other sites

Thank you Zen that's do the trick.

One still left behind. The Zen_SetAISkill.

Do i need to recall it every time when groups is uncached?

Share this post


Link to post
Share on other sites

The AI's skills, loadout, etc. will remain the same since the cache system uses enableSimulationGlobal rather than deleting and respawning them.

Share this post


Link to post
Share on other sites

Update and Release #47

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

The endless road towards perfection continues, if a bit slowly recently. This release is a mix of suggestions, bug fixes, and other minor improvements I've found. Most notably, Zen_FindTerrainSlope has been removed, and Zen_FindTerrainGradient modified to handle both its original duties as well as Zen_FindTerrainSlope's. The conversion should be

// the old Zen_FindTerrainSlope is now the same as
(90 - (([_pos] call Zen_FindTerrainGradient) select 2))
This also relates to the standardization of coordinate system among framework functions (such as measuring that second angle from the Z axis). Zen_ExtendPosition has been renamed Zen_ExtendVector; I am aware that this is a common function and renaming it is annoying, but no other change to is arguments is necessary. Zen_ExtendVector has the same parameters, with the addition of an optional parameter sequence using a standardized vector. Also, Zen_FindAveragePosition now averages the Z coordinate, so it can now be used with other vector functions by converting to Cartesian coordinates.

Zen_FindInRange (and indirectly Zen_SpawnInfantry and all other functions that use it) has had its distribution corrected when using rounded numbers. Specifically, Zen_SpawnInfantry can spawn the maximum number of units given; Zen_SpawnInfantry also supports a Gaussian distribution of random units.

Zen_OrderInfantryPatrol should prevent units from chasing a spotted enemy even though they can no longer see them (or had already killed them). This keeps the AI in the patrol area better and reduces aggressive behavior in which the AI kept chasing players after the player had evaded them.

Zen_SetAISkill has a new present for 'player' skill. This sets all skills except accuracy and spot time to 1; accuracy has been tuned such that the AI has comparable accuracy to an experienced ArmA player (obviously player skill varies widely, so this is just an estimate). The AI fires more slowly than a player at long range, but they can reliably hit a standing target at 400-500 meters. The spotting time skill of the AI is much more subjective, as I don't know the details of internal AI code that allows them to spot enemies. Its value is meant to be an estminate of how much situational awareness the average player has, particularly when they are engaged in a firefight.

As for documentation, I have added a new demonstration for the Dialog System. This demonstration includes five dialogs with explanations of the specific properties as well as how the dialog system work conceptually. This should provide good examples to support the (somewhat terse) list of controls and properties within the dialog system documentation file itself. Also, a relatively short section has been added to the AI caching demonstration.

11/20/16

  • Fixed: Zen_FindCenterPosition did not remove itself from the stack trace when exiting on special cases of arguments
  • Fixed: Zen_FindInRange rounded numbers were not equally distributed at the edge
  • Fixed: Zen_MoveAsSet caused an error by including markers in its height calculations
  • Fixed: Zen_SpawnInfantry rarely spawned the maximum number of units
  • Fixed: Zen_SpawnInfantryGarrison argument checking did not allow a nested array for the skill parameter
  • Added: Zen_ExecuteCommand command for enableFatigue
  • Added: Zen_ExtendVector parameter for cylindrical vector
  • Added: Zen_SetAISkill preset for 'player' skill
  • Added: Zen_SpawnInfantry and Zen_SpawnInfantryGarrison parameter for Gaussian distribution of random unit number
  • Added: Zen_TrackGroups, Zen_TrackInfantry, and Zen_TrackVehicles parameter for marker update time interval
  • Removed: Zen_FindTerrainSlope
  • Improved: Zen_CreateTemplate, Zen_FindGroundPosition, Zen_GetAmbientClutterCount, Zen_IsForestArea, Zen_IsUrbanArea, and Zen_OrderInfantryPatrolBuilding optimized
  • Improved: Zen_FindAveragePosition averages the Z coordinate
  • Improved: Zen_FindTerrainGradient finds the 3D gradient
  • Improved: Zen_ExtendPosition renamed Zen_ExtendVector
  • Improved: Zen_OrderInfantryPatrol sets the behavior of groups chasing the enemy to combat
  • Improved: Zen_OrderInfantryPatrol orders the AI to return to their normal patrol pattern when they lose contact with the enemy
  • Tweaked: Zen_SpawnInfantry allows the AI to flee
  • Tweaked: Zen_TrackGroups and Zen_TrackInfantry start from 1 when using number text
  • Documentation: Added Dialog System demonstration
  • Documentation: Removed for Zen_FindTerrainSlope
  • Documentation: Corrected for Zen_RotateAsSet
  • Documentation: Improved for Zen_ExecuteCommand
  • Documentation: Improved AI Caching demonstration
  • Documentation: Updated for Zen_FindAveragePosition, Zen_FindTerrainGradient, Zen_ExtendVector, Zen_SetAISkill, Zen_SpawnInfantry, Zen_SpawnInfantryGarrison, Zen_TrackGroups, Zen_TrackInfantry, and Zen_TrackVehicles
  • Documentation: Updated Notepad++ SQF language and autocompletion file with ArmA 1.64 stable commands

Code Example

In this section, I will present a function or piece of code that shows something not present in the existing documentation. These examples are adapted to be general, accessible, and customizable. They are meant to be useful to mission makers who want to include the code as part of their mission, as well as those who want to learn general coding techniques and specific framework implementations. These are not contrived or coded with any specific skill level in mind; they are taken from full missions I have finished or am working on with minimal changes. Of course, if you have any questions about an example, suggestions for an example, your own example to present, or if find any bugs, please let me know.

There are several issues about friendly AI in ArmA that have always bothered me, and while the AI can be useful in some situations, they end being a hindrance or just becoming cannon fodder in others. I wanted to find a way to being the friendly AI on a more equal footing with players and give players the sense that the AI can be an effective tool.

Setting their skill to a high value helps, but having an aimbot AI do all the work for you isn't fun. The new 'player' Zen_SetAISkill preset is designed to find a balance between spray-and-pray and aimbot AI.

However, teamwork isn't just about who can kill the enemy. The code below intends to correct the issue of AI communication with players. Typically, AI say something like 'Enemy spotted, 300 meters, south.'; which is generally vague and useless. Compare that to a message like "He's in the second story window of the yellow building bearing 137"; this is extremely specific and allows teammates to immediately engage the threat.

My solution allows the mission to simulate the AI calling out enemies like human can by marking the enemies the AI spots on the map. This varies in effectiveness based upon whether the player is marked on the map, if they have a GPS, how much map/situational awareness they have. The information is taken directly from the AI knowledge, and the marker fades away when the AI cannot see the enemy.

To use this code, just copy-paste it into the init.sqf and let the thread run. The main things of interest that you might want to tweak are the 'sleep 5; \' in the marker thread, and the sideChat message about the spotting. The sleep command controls how quickly a marker fades, so having it fade quickly means the players will have to pay closer attention to the map. The sideChat is a very accurate spotting report in the form of 'X distance at Y degrees'; by default it only appears when a new enemy is spotted.

0 = [] spawn {
    Opfor_Assets = [];
    while {true} do {
        Blufor_Assets = [west] call Zen_ConvertToObjectArray;
        _opfor = [east] call Zen_ConvertToObjectArray;

        {
            sleep 0.05;
            _opforUnit = _x;
            _spotTime = 0;
            _spotPos = [];
            _vel = [];
            _spotter = objNull;
            {
                if !(isPlayer _x) then {
                    _knowledge = _x targetKnowledge _opforUnit;
                    if ((_knowledge select 1) && {((_knowledge select 2) > _spotTime)}) then {
                        _spotTime = _knowledge select 2;
                        _spotPos = _knowledge select 6;
                        _vel = velocity _opforUnit;
                        _spotter = _x;
                    };
                };
            } forEach Blufor_Assets;
            if (_spotTime > 0) then {
                _indexes = [Opfor_Assets, _opforUnit, 0] call Zen_ArrayGetNestedIndex;
                if (count _indexes == 0) then {
                    _spotter sideChat ("Enemy Spotted, " + str round ([_spotter, _spotPos] call Zen_Find2dDistance) + " meters at " + str (((round (90 - ([_spotter, _spotPos] call Zen_FindDirection))) + 360) % 360));
                    _marker = [_spotPos, "", "colorOpfor", [1, 1], "mil_dot", 0, 1] call Zen_SpawnMarker;

                    #define MKR_THREAD \
                    _markerThread = [_marker] spawn { \
                        _marker = _this select 0; \
                        for "_i" from 1 to 10 do { \
                            _marker setMarkerAlpha (1/_i); \
                            sleep 4; \
                        }; \
                        _marker setMarkerAlpha 0; \
                    };

                    MKR_THREAD
                    Opfor_Assets pushBack [_opforUnit, _spotTime, _spotPos, _vel, _marker, _markerThread];
                } else {
                    _opforThreat = Opfor_Assets select (_indexes select 0);
                    if (_spotTime > (_opforThreat select 1)) then {
                        // _spotter sideChat ("Enemy Spotted, " + str round ([_spotter, _spotPos] call Zen_Find2dDistance) + " meters at " + str (((round (90 - ([_spotter, _spotPos] call Zen_FindDirection))) + 360) % 360));
                        _opforThreat set [1, _spotTime];
                        _opforThreat set [2, _spotPos];
                        _opforThreat set [3, _vel];
                        terminate (_opforThreat select 5);
                        _marker = (_opforThreat select 4);
                        _marker setMarkerPos _spotPos;
                        _marker setMarkerAlpha 1;
                        MKR_THREAD
                        _opforThreat set [5, _markerThread];
                    };
                };
            };
        } forEach _opfor;
        sleep 1;
    };
};

Share this post


Link to post
Share on other sites

Thanks for the update Zen.

 

I am still on alpha testing the capabilities.

After many investigations i ended with this code:

 

fnc_cache_loop = {
	
	#include "..\Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"
	
	private ["_OpforPositions", "_cacheArray", "_threadsArray", "_cacheDist", "_tbveh", "_indexesToRemove", "_players", "_talibsGrps",
			"_talibsin", "_talibVehgrps", "_talibVeh", "_talibVeh2", "_talibVeh3", "_units", "_h_threadv0", "_h_threadv1", "_h_threadv2",
			"_h_thread1","_h_thread2","_h_thread3","_h_thread4", "_h_thread5", "_h_thread6"];
			
        _OpforPositions = _this select 0;
        _cacheArray = _this select 1;
		_threadsArray = _this select 2;
     
        // define the distance at which to cache/uncache
        _cacheDist = 1000;
		_tbveh = 0;
		
        while {true} do {
		
            if (count _cacheArray == 0) exitWith {};
            sleep 1;
            _indexesToRemove = [];
            _players = [West] call Zen_ConvertToObjectArray;
			
            {
                sleep 1;
                if ([_x] call Zen_IsCached) then {
                    if !([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
						
						_talibsGrps = [];
						_talibsin = [_x] call Zen_GetCachedUnits;
						
                        0 = [_x] call Zen_UnCache;
						
						{_talibsGrps pushBack (group (_x select 0))} forEach _talibsin;
						_talibVehgrps = _talibsGrps select {vehicle leader _x != leader _x};
						
							if (count _talibVehgrps != 0) then {
						
								_talibsGrps = _talibsGrps - _talibVehgrps;
								
								switch (count _talibVehgrps) do {
								
									case 1: {
									
									_talibVeh = vehicle (((units (_talibVehgrps select 0)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);
									_h_threadv0 = [_talibVeh,(_OpforPositions select _forEachIndex),200] spawn Zen_OrderVehiclePatrol;
									_tbveh = 1;
									
									};
									case 2: {
									
									_talibVeh = vehicle (((units (_talibVehgrps select 0)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);
									_talibVeh2 = vehicle (((units (_talibVehgrps select 1)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);								
									_h_threadv0 = [_talibVeh,(_OpforPositions select _forEachIndex),200] spawn Zen_OrderVehiclePatrol;
									_h_threadv1 = [_talibVeh2,(_OpforPositions select _forEachIndex),100] spawn Zen_OrderVehiclePatrol;
									_tbveh = 2;
									
									};
									case 3: {
									
									_talibVeh = vehicle (((units (_talibVehgrps select 0)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);
									_talibVeh2 = vehicle (((units (_talibVehgrps select 1)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);								
									_talibVeh3 = vehicle (((units (_talibVehgrps select 2)) select {(vehicle _x) != _x && _x == driver (vehicle _x)}) select 0);								
									_h_threadv0 = [_talibVeh,(_OpforPositions select _forEachIndex),200] spawn Zen_OrderVehiclePatrol;
									_h_threadv1 = [_talibVeh2,(_OpforPositions select _forEachIndex),150] spawn Zen_OrderVehiclePatrol;
									_h_threadv2 = [_talibVeh3,(_OpforPositions select _forEachIndex),200] spawn Zen_OrderVehiclePatrol;
									_tbveh = 3;
									};
									
									default {};

								};
							};

						switch (count _talibsGrps) do {

							case 1: {
							

								_h_thread1 = [_talibsGrps,(_OpforPositions select _forEachIndex),[0,150]] spawn Zen_OrderInfantryPatrol;
							 
								if (count _talibVehgrps != 0) then {
								
									switch _tbveh do {
									
										case 1: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_thread1]];
										};
										case 2: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_thread1]];								
										};
										case 3: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_threadv2,_h_thread1]];	
										};
										default {};
									};
									
								 
								} else {
								  
									_threadsArray set [_forEachIndex, [_h_thread1]];
								};
							};
							
							case 2: {
							
							_h_thread1 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread2 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',3] spawn Zen_OrderInfantryPatrolBuilding;
								
								if (count _talibVehgrps != 0) then {
								 
									switch _tbveh do {
									
										case 1: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_thread1,_h_thread2]];
										};
										case 2: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_thread1,_h_thread2]];								
										};
										case 3: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_threadv2,_h_thread1,_h_thread2]];	
										};
										default {};
									};
								 
								} else {
								  
									_threadsArray set [_forEachIndex, [_h_thread1,_h_thread2]];
								};
							};
							
							case 3: {
							
							_h_thread1 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread2 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',3] spawn Zen_OrderInfantryPatrolBuilding;
							_h_thread3 = [[_talibsGrps select 2],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
								
								if (count _talibVehgrps != 0) then {
								 
									switch _tbveh do {
									
										case 1: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_thread1,_h_thread2,_h_thread3]];
										};
										case 2: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_thread1,_h_thread2,_h_thread3]];								
										};
										case 3: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_threadv2,_h_thread1,_h_thread2,_h_thread3]];	
										};
										default {};
									};
								 
								} else {
								  
									_threadsArray set [_forEachIndex, [_h_thread1,_h_thread2,_h_thread3]];
								};
							};
							
							case 4: {
							
							_h_thread1 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread2 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',3] spawn Zen_OrderInfantryPatrolBuilding;
							_h_thread3 = [[_talibsGrps select 2],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread4 = [[_talibsGrps select 3],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
		
								if (count _talibVehgrps != 0) then {
								 
									switch _tbveh do {
									
										case 1: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_thread1,_h_thread2,_h_thread3,_h_thread4]];
										};
										case 2: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_thread1,_h_thread2,_h_thread3,_h_thread4]];								
										};
										case 3: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_threadv2,_h_thread1,_h_thread2,_h_thread3,_h_thread4]];	
										};
										default {};
									};
								 
								} else {
								  
									_threadsArray set [_forEachIndex, [_h_thread1,_h_thread2,_h_thread3,_h_thread4]];
								};
							};
							
							case 5: {
							
							_h_thread1 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread2 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',3] spawn Zen_OrderInfantryPatrolBuilding;
							_h_thread3 = [[_talibsGrps select 2],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread4 = [[_talibsGrps select 3],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread5 = [[_talibsGrps select 4],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
								if (count _talibVehgrps != 0) then {
								 
									switch _tbveh do {
									
										case 1: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5]];
										};
										case 2: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5]];								
										};
										case 3: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_threadv2,_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5]];	
										};
										default {};
									};
								 
								} else {
								  
									_threadsArray set [_forEachIndex, [_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5]];
								};
							};
							
							case 6: {
							
							_h_thread1 = [[_talibsGrps select 0],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread2 = [[_talibsGrps select 1],(_OpforPositions select _forEachIndex),true,'aware',3] spawn Zen_OrderInfantryPatrolBuilding;
							_h_thread3 = [[_talibsGrps select 2],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread4 = [[_talibsGrps select 3],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread5 = [[_talibsGrps select 4],(_OpforPositions select _forEachIndex),[0,floor (random [80, 130, 200])]] spawn Zen_OrderInfantryPatrol;
							_h_thread6 = [[_talibsGrps select 5],(_OpforPositions select _forEachIndex),true,'aware',2] spawn Zen_OrderInfantryPatrolBuilding;
								if (count _talibVehgrps != 0) then {
								 
									switch _tbveh do {
									
										case 1: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5,_h_thread6]];
										};
										case 2: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5,_h_thread6]];								
										};
										case 3: {
										_threadsArray set [_forEachIndex, [_h_threadv0,_h_threadv1,_h_threadv2,_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5,_h_thread6]];	
										};
										default {};
									};
								 
								} else {
								  
									_threadsArray set [_forEachIndex, [_h_thread1,_h_thread2,_h_thread3,_h_thread4,_h_thread5,_h_thread6]];
								};
							};
							default {};

						};
						
                    };
                } else {
                    _units = [([_x] call Zen_GetCachedUnits)] call Zen_ConvertToObjectArray;
                    if ({alive _x} count _units == 0) then {
                        _indexesToRemove pushBack _forEachIndex;
						//sector clear
						0 = [(_OpforPositions select _forEachIndex)] spawn fnc_clearDead;
                    } else {
                        if ([_players, _OpforPositions select _forEachIndex, [_cacheDist, _cacheDist], 0, "ellipse"] call Zen_AreNotInArea) then {
                            
							{
							
								terminate _x;
							
							} forEach (_threadsArray select _forEachIndex);
							
												
							0 = [_x] call Zen_Cache;
							0 = [(_OpforPositions select _forEachIndex)] spawn fnc_clearDead;
                        };
                    };
                };
            } forEach _cacheArray;
     
            ZEN_FMW_Array_RemoveIndexes(_OpforPositions, _indexesToRemove)
            ZEN_FMW_Array_RemoveIndexes(_cacheArray, _indexesToRemove)
			ZEN_FMW_Array_RemoveIndexes(_threadsArray, _indexesToRemove)
        };
};

 

Is there any possibility to improve this or any other method where i can give the units tasks in cooperate with zen_Cache?

 

When the Un-cache process starts it gives 3-5 min lagging.

I assume it happens because of this huge loop.

scripts running during lagg:

19:07:19 "[""BIS_fnc_moduleCurator: Owner"",""a3\modules_f_curator\curator\functions\fn_moduleCurator.sqf"",true,114]"
19:07:19 "[""<spawn>"",""ca\structures_c\Misc_Powerlines\emmisiveLights.sqf"",true,57]"
19:07:19 "[""TFAR_fnc_ServerInit"",""task_force_radio\functions\fn_ServerInit.sqf"",true,81]"
19:07:19 "[""BIS_fnc_addCommMenuItem_loop"",""A3\functions_f\Misc\fn_addCommMenuItem.sqf"",true,91]"
19:07:19 "[""<spawn>"",""ca\structures_c\Misc_Powerlines\emmisiveLights.sqf"",true,32]"
19:07:19 "[""Soldier Tracker by Quiksilver - Artillery Computer and UAV Terminal support"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\scripts\QS_icons.sqf"",true,1398]"
19:07:19 "[""Soldier Tracker (GPS Icons) by Quiksilver - Waiting for GPS display"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\scripts\QS_icons.sqf"",true,1422]"
19:07:19 "[""Soldier Tracker (Group Icons) by Quiksilver"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\scripts\QS_icons.sqf"",true,1558]"
19:07:19 "[""TFAR_fnc_isForcedCurator"",""task_force_radio\functions\fn_radioReplaceProcess.sqf"",true,108]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\functions\globalfnc.sqf"",true,81]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\functions\serverfnc.sqf"",true,296]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\functions\serverfnc.sqf"",true,610]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\functions\serverfnc.sqf"",true,622]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Engima\Civilians\Server\ServerFunctions.sqf"",true,448]"
19:07:19 "[""BIS_fnc_spawnVehicle"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Engima\Traffic\Server\Functions.sqf"",true,540]"
19:07:19 "[""BIS_fnc_worldArea"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Engima\Traffic\Server\Functions.sqf"",true,85]"
19:07:19 "[""fn_animalBehaviour_mainLoop"",""A3\functions_f\ambient\fn_animalBehaviour.sqf"",true,230]"
19:07:19 "[""fn_animalBehaviour_mainLoop"",""A3\functions_f\ambient\fn_animalBehaviour.sqf"",true,348]"
19:07:19 "[""fn_animalBehaviour_mainLoop"",""A3\functions_f\ambient\fn_animalBehaviour.sqf"",true,348]"
19:07:19 "[""fn_animalBehaviour_mainLoop"",""A3\functions_f\ambient\fn_animalBehaviour.sqf"",true,348]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_OrdersFunctions\Zen_OrderVehiclePatrol.sqf"",true,122]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_OrdersFunctions\Zen_OrderInfantryPatrol.sqf"",true,205]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_OrdersFunctions\Zen_OrderInfantryPatrolBuilding.sqf"",true,66]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_OrdersFunctions\Zen_OrderInfantryPatrol.sqf"",true,205]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_OrdersFunctions\Zen_OrderInfantryPatrol.sqf"",true,205]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_OrdersFunctions\Zen_OrderInfantryPatrol.sqf"",true,205]"
19:07:19 "[""<spawn>"",""C:\Users\DaVidoSS\Documents\Arma 3\missions\desert_insurgency2.Takistan\Zen_FrameworkFunctions\Zen_DataFunctions\Zen_ValueIsInArray.sqf"",true,43]"


Here  a clip how it looks like.

Strange repeat lagg

VIDEO

Share this post


Link to post
Share on other sites

Update and Release #48
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 release includes a new function, Zen_SubdivideMarker, which acts exactly as you would expect.  It breaks up large rectangles (it pretends ellipses are rectangles) into smaller rectangles.  You can use this to spread out spawning and patrols more evenly, quickly get more granularity in zone capture style gamemodes, etc..

 

We have reached a total of 90 preprocessor macros with the addition of ZEN_FMW_Code_GetRemoteVar.  This is handy for dealing with cases where every client may have a different value of the same global variable.  Also, in the case where the server is managing data attached to each client, retrieving the client's data on-demand typically saves bandwidth compared to always publicVariable'ing the data from the client when it changes.

 

The dialog system has received several improvements.  Zen_InvokeDialog can now move the origin of the dialog anywhere on (or off) the screen; that argument works with safeZoneX etc. for determining screen edges.  There is a new type of a control, the map itself.  Other than size and position, there's not much customization to do, but it can be a useful alternative to have the real map open behind the dialog.  Zen_InvokeDialog now allows you to enable user input while a dialog is open.  This a bit of a hack and isn't really intended; you enable it at your own risk.

 

1/26/17
 

Spoiler
  1. New Function: Zen_SubdivideMarker
  2. Fixed: Zen_OrderAircraftPatrol, Zen_OrderBoatPatrol, Zen_OrderInfantryPatrol, and Zen_OrderVehiclePatrol now interpret their arguments correctly
  3. Fixed: Zen_InvokeDialog caused an error when attempting to show a list with no elements
  4. Fixed: Zen_OrderInfantryPatrol did not generate waypoints correctly for groups within the minimum radius
  5. Added: Zen_InvokeDialog parameter to allow user input to control their character while the dialog is open
  6. Added: Zen_InvokeDialog parameter to move the center of the dialog
  7. Added: Framework macro ZEN_FMW_Code_GetRemoteVar
  8. Added: Zen_CreateControl type Map
  9. Documentation: Fixed ZEN_FMW_MP_REClient, Zen_GiveLoadoutCustom and Zen_StringGenerateRandom Notepad++ hints
  10. Documentation: Added for ZEN_FMW_Code_GetRemoteVar, Zen_SubdivideMarker
  11. Documentation: Updated for Zen_CreateControl, Zen_InvokeDialog
  12. Documentation: Updated Notepad++ SQF language and autocompletion file with ArmA 1.66 stable commands

 

Code Example

 

In this section, I will present a function or piece of code that shows something not present in the existing documentation. These examples are adapted to be general, accessible, and customizable. They are meant to be useful to mission makers who want to include the code as part of their mission, as well as those who want to learn general coding techniques and specific framework implementations. These are not contrived or coded with any specific skill level in mind; they are taken from full missions I have finished or am working on with minimal changes. Of course, if you have any questions about an example, suggestions for an example, your own example to present, or if find any bugs, please let me know.

 

One issue that arises in creating co-op missions with a large mission area and/or a high number of players is having AI patrol in separate areas so that a reasonable portion of the map is populated.  While this is of course possible, it results in having dozens or more threads managing each separate area's patrols.

 

The solution I've arrived at is Zen_OrderMultiInfantryPatrol; it will not included as a framework function (due to how it accepts arguments).  Rather it is an external script (that still requires the framework) that generalizes Zen_OrderInfantryPatrol so that all patrols can be handled by a single thread.  This single thread can scale its execution to maintain good performance regardless of how many patrols it is given.


The code is basically a copy-paste from Zen_OrderInfantryPatrol, but below it I will show the unique way it accepts arguments.
 

Spoiler



#include "Zen_FrameworkFunctions\Zen_StandardLibrary.sqf"
#include "Zen_FrameworkFunctions\Zen_FrameworkLibrary.sqf"

_Zen_stack_Trace = ["Zen_OrderMultiInfantryPatrol", _this] call Zen_StackAdd;

while {true} do {
    waitUntil {
        sleep 2;
        (count Zen_Multi_Infantry_Patrol_Array > 0)
    };

    _indexesToRemove = [];
    {
        sleep 1;
        _moveData = _x;
        _groups = [_moveData select 0] call Zen_ConvertToGroupArray;
        _groups = [_groups] call Zen_ArrayRemoveDead;

        if (count _groups == 0) then {
            _indexesToRemove pushBack _forEachIndex;
        };

        _center = _moveData select 1;
        _moveDist = _moveData select 2;
        _speedMode = _moveData select 3;
        _behaviorMode = _moveData select 4;
        {
            private "_group";
            _group = _x;

            _man = leader _group;

            if ((unitReady _man) && {(alive _man)}) then {
                _mpos = [0,0,0];

                if (([_man, _center] call Zen_Find2dDistance) < (_moveDist select 0)) then {
                    _mpos = [_center, _moveDist, [], 1, [1,50],0] call Zen_FindGroundPosition;
                } else {
                    while {true} do {
                        _mpos = [_center, _moveDist, [], 1, [1,50]] call Zen_FindGroundPosition;
                        if !([_man, [_man, _mpos] call Zen_Find2dDistance, ([_man, _mpos] call Zen_FindDirection), _center, [(_moveDist select 0), (_moveDist select 0)], 0, "ellipse"] call Zen_IsRayInPoly) exitWith {};
                    };
                };

                if !(isPlayer _man) then {
                    _group setCurrentWaypoint (_group addWaypoint [_mpos, -1]);
                    _group move _mpos;
                    _group setSpeedMode _speedMode;
                    _group setBehaviour _behaviorMode;
                    _group setCombatMode "Red";
                };
            } else {
                _target = _man findNearestEnemy _man;
                if (!(isNull _target) && {(((([_man, _target] call Zen_Find2dDistance) < ((_moveDist select 1) - ([_man, _center] call Zen_Find2dDistance))) || ([_target, _center, [_moveDist select 1, _moveDist select 1], 0, "ellipse"] call Zen_IsPointInPoly)) && (_target isKindOf "Man"))}) then {
                    _mpos = [_target, (random (150 / ((_man knowsAbout _target) + 0.1))), (random 360)] call Zen_ExtendVector;

                    if !(isPlayer _man) then {
                        _group setCurrentWaypoint (_group addWaypoint [_mpos, -1]);
                        _group move _mpos;
                        _group setSpeedMode _speedMode;
                        _group setCombatMode "Red";

                        if (side _group == civilian) then {
                            _group setBehaviour "careless";
                        } else {
                            _group setBehaviour "combat";
                        };
                    };
                };
            };
        } forEach _groups;
    } forEach Zen_Multi_Infantry_Patrol_Array;

    ZEN_FMW_Array_RemoveIndexes(Zen_Multi_Infantry_Patrol_Array, _indexesToRemove)
};

call Zen_StackRemove;
if (true) exitWith {};


 

The function itself has no arguments; it should be spawned as a thread on the server.  You can compile it as a separate file (hence the #include's at the top) or make it an in-line function in the init.  It relies upon a global variable, Zen_Multi_Infantry_Patrol_Array to receive its arguments.
 

Spoiler



// The first step is the initialize the patrol queue
Zen_Multi_Infantry_Patrol_Array = [];

// Then run this thread to start the processing of patrols
0 = [] spawn Zen_OrderMultiInfantryPatrol;

// At any time, and from any thread, in the mission, you can append a patrol to the queue
// Assume we've spawned _group somehow
// Zen_OrderMultiInfantryPatrol will see this patrol's information and begin giving them waypoints
Zen_Multi_Infantry_Patrol_Array pushBack [_group, player, [50, 200], "limited", "aware"];


 

For simplicity, Zen_Multi_Infantry_Patrol_Array doesn't support defining the patrol area with a marker.  I'll probably add that in the future; if anyone really wants it now, just ask and I'll show you what to add.

  • Like 2

Share this post


Link to post
Share on other sites

Sweet thanks zeno! Experimenting with different opts for using zen_cache and patrols with multi markers recently. Ordermultipatrol and subdividemarker will help alot :smile_o: Glad I came to check for a new version. Thanks for preview on multi patrols too, hope it makes it into the framework

Share this post


Link to post
Share on other sites

Hey Zenophon! I tested the new patrol functions and it looks like the aircraft patrol function is broken. I have a test mission that spawns helo's from all 3 sides and are all ordered to patrol the same area (designated by an area marker) around the main airport on Altis but after the last update the helo's all head down to [0,0] and stay there! This mission was built long ago and was tested and working(even on your last update). I have an identical mission for tanks and it is working correctly. I don't have a mission for boats or infantry so I haven't tested those but it seems to be limited to aircraft!

Edited by cdn_biggdogg
spelling

Share this post


Link to post
Share on other sites

Zen_OrderAircraftPatrol does have a bug; for some reason ArmA didn't print an error for an undefined variable.  Line 45 should have '_center' replaced with '_movecenter'; I've checked other patrol scripts and they don't have this bug.

 

if (typeName _movecenter == "STRING") then { \

 

However, this is not the cause of the [0,0,0] bug; it was only preventing Zen_OrderAircraftPatrol from negating that bug.  After a quick test, that bug is not caused by the framework; it is a result of spawning an aircraft using standard SQF script commands.  I tested this code in an empty mission with only the player (I didn't even save the mission):

 

plane = createVehicle ["c_plane_civil_01_f", (getPosATL player) vectorAdd [0,0, 100], [], 0, "FLY"];
createVehicleCrew plane;

 

The aircraft always flies towards [0,0,0].  I have no idea what is causing this; however, it does not affect aircraft placed in the editor.  My guess is that BIS have introduced a bug with either createVehicle or something in the AI's flying code; it is possible that the editor-placed object is running some internal code when it spawns that fixes this bug.  I don't know what SQF commands to use to replicate this fix, if any can.

 

Aircraft spawned with createVehicle will follow move commands and waypoints, but when their final waypoint is complete, they will always immediately move to [0,0,0].  One solution is to issue them new waypoints forever (through Zen_OrderAircraftPatrol or a similar script).

 

Share this post


Link to post
Share on other sites
On 1/31/2017 at 3:13 PM, Zenophon said:

Zen_OrderAircraftPatrol does have a bug; for some reason ArmA didn't print an error for an undefined variable.  Line 45 should have '_center' replaced with '_movecenter'; I've checked other patrol scripts and they don't have this bug.

 


if (typeName _movecenter == "STRING") then { \

 

However, this is not the cause of the [0,0,0] bug; it was only preventing Zen_OrderAircraftPatrol from negating that bug.  After a quick test, that bug is not caused by the framework; it is a result of spawning an aircraft using standard SQF script commands.  I tested this code in an empty mission with only the player (I didn't even save the mission):

 


plane = createVehicle ["c_plane_civil_01_f", (getPosATL player) vectorAdd [0,0, 100], [], 0, "FLY"];
createVehicleCrew plane;

 

The aircraft always flies towards [0,0,0].  I have no idea what is causing this; however, it does not affect aircraft placed in the editor.  My guess is that BIS have introduced a bug with either createVehicle or something in the AI's flying code; it is possible that the editor-placed object is running some internal code when it spawns that fixes this bug.  I don't know what SQF commands to use to replicate this fix, if any can.

 

Aircraft spawned with createVehicle will follow move commands and waypoints, but when their final waypoint is complete, they will always immediately move to [0,0,0].  One solution is to issue them new waypoints forever (through Zen_OrderAircraftPatrol or a similar script).

 

 

Changing the variable to _movecenter fixed the issue. The [0,0,0] issue wasn't caused by the fact that on line 44 of the Zen_OrderAircraftPatrol is defined as:

_mpos = [0,0,0]; \

???

 

What about moveTo orders? Are aircraft ,created using createVehicle, going to move to the location of the moveTo order then go down to [0,0,0]??

Share this post


Link to post
Share on other sites

The issue occurs in the absence of Zen_OrderAircraftPatrol, just with the simple spawning code I tested.

 

Zen_OrderAircraftPatrol is using 'move' and 'addWaypoint'; the aircraft always goes to [0,0,0] after a move order is complete.  Since Zen_OrderAircraftPatrol gives a new waypoint after that, it means that Zen_IsReady returned true for the aircraft.  I don't think anything is ordering the AI to move [0,0,0]; it seems like an intrinsic bug in their AI.  Instead of circling their last waypoint (or their spawn point), they forget it and go to [0,0,0].

 

Share this post


Link to post
Share on other sites

Is it possible to use this in a SP mission? 

nvm. but still, i'd like to know, what could happen if i allow players to save in SP?

Share this post


Link to post
Share on other sites

Saving in SP works for most things; all running scripts are restored and the mission will proceed as intended.  There have been issues reported in the past about UI scripts stopping, addons not loading properly, etc. (which I've never seen myself), so at the time I released my framework I made a blanket statement against saving to prevent such issues in SP.  I was also mostly thinking about missions written for co-op being played in SP, where saving would not be part of the mission's design (and thus not necessary in SP).

 

If a save/load cycle works in your mission, then of course you can enable it.  There's nothing built into the framework against saving.

Share this post


Link to post
Share on other sites

Hi,

 

im doing a COOP mission for unsung vietnam, but i cant get the vietnamese enemy AI to spawn using Zen_SpawnInfantry can you please help me spawn specific unit.

 

 

I have tried this

 

_PatrolPosition = ["guard"] call
Zen_FindGroundPosition;
_group = ["guard", "uns_men_VC_regional_AS1"] call Zen_SpawnGroup;
0 = [_group, "guard"] spawn
Zen_OrderInfantryPatrol;

 

but it only spawn 1 enemy, i have added after the comma the same unit but I still get 1.

 

Thanks

 

Share this post


Link to post
Share on other sites
17 hours ago, Mr. Lt. said:

Hi,

 

im doing a COOP mission for unsung vietnam, but i cant get the vietnamese enemy AI to spawn using Zen_SpawnInfantry can you please help me spawn specific unit.

 

 

I have tried this

 

_PatrolPosition = ["guard"] call
Zen_FindGroundPosition;
_group = ["guard", "uns_men_VC_regional_AS1"] call Zen_SpawnGroup;
0 = [_group, "guard"] spawn
Zen_OrderInfantryPatrol;

 

but it only spawn 1 enemy, i have added after the comma the same unit but I still get 1.

 

Thanks

 

 

As stated in the documentation the function that spawns a group:


 

Zen_SpawnGroup

Spawns each of (2) at (1).  Makes the group the side of the first classname given.
Do not use a non-human class name, only soldiers or civilians.
Usage : Call
Params: 1. Array, group, object, string, the spawn position
        2. Array of strings or String, classname(s) of the units to spawn in order
Return: Group

 

It only spawns one because you only told it to spawn one!

 

_group = ["guard", ["uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1"]] call Zen_SpawnGroup;

 

This will spawn 4 soldiers!

 

Also this

_group = ["guard", "uns_men_VC_regional_AS1"] call Zen_SpawnGroup;

I believe will spawn them at the center of the marker named "guard". I believe you want to use:

_group = [_PatrolPosition, "uns_men_VC_regional_AS1"] call Zen_SpawnGroup;

or

_group = [_PatrolPosition, ["uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1"]] call Zen_SpawnGroup;

You may also want to look at the Zen_SpawnInfantry

 

Zen_SpawnInfantry

Spawns (4) units of side (2) as a group with skill (3) at (1).  (5,6) are based upon
config file organization and are subject to change by BIS.
Usage : Call
Params: 1. Array, group, object, string, the spawn point
        2. Side, of the units to spawn
        3. Skill, see Zen_SetAISkill documentation (2) (Object Functions)
    AND
        4. Scalar, how many units to spawn
    OR
        4. Array:
            1. Scalar, the minimum number of units to spawn
            2. Scalar, the maximum number of units to spawn
    OR
        4. Array:
            1. Scalar, minimum value of a Gaussian distribution
            2. Scalar, center (average) value of a Gaussian distribution
            3. Scalar, maximum value of a Gaussian distribution
    AND
 (opt.) 5. String, the type of soldiers to spawn, (default: 'Men'), 'MenDiver' 'MenRecon' 'MenSniper'
 (opt.) 6. String, the faction of soldiers to spawn, (default: 'All'), 'BLU_F', 'IND_F', 'OPF_F', 'BLU_G_F'
 (opt.) 7. Array of strings, classnames to blacklist from spawning, (default: [])
 (opt.) 8. Array or string, DLC type(s), 'All' for all DLC, (default: '')
Return: Group

 

Share this post


Link to post
Share on other sites
4 hours ago, cdn_biggdogg said:

 

As stated in the documentation the function that spawns a group:


 


Zen_SpawnGroup

Spawns each of (2) at (1).  Makes the group the side of the first classname given.
Do not use a non-human class name, only soldiers or civilians.
Usage : Call
Params: 1. Array, group, object, string, the spawn position
        2. Array of strings or String, classname(s) of the units to spawn in order
Return: Group

 

It only spawns one because you only told it to spawn one!

 


_group = ["guard", ["uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1"]] call Zen_SpawnGroup;

 

This will spawn 4 soldiers!

 

Also this


_group = ["guard", "uns_men_VC_regional_AS1"] call Zen_SpawnGroup;

I believe will spawn them at the center of the marker named "guard". I believe you want to use:


_group = [_PatrolPosition, "uns_men_VC_regional_AS1"] call Zen_SpawnGroup;

or


_group = [_PatrolPosition, ["uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1","uns_men_VC_regional_AS1"]] call Zen_SpawnGroup;

You may also want to look at the Zen_SpawnInfantry

 


Zen_SpawnInfantry

Spawns (4) units of side (2) as a group with skill (3) at (1).  (5,6) are based upon
config file organization and are subject to change by BIS.
Usage : Call
Params: 1. Array, group, object, string, the spawn point
        2. Side, of the units to spawn
        3. Skill, see Zen_SetAISkill documentation (2) (Object Functions)
    AND
        4. Scalar, how many units to spawn
    OR
        4. Array:
            1. Scalar, the minimum number of units to spawn
            2. Scalar, the maximum number of units to spawn
    OR
        4. Array:
            1. Scalar, minimum value of a Gaussian distribution
            2. Scalar, center (average) value of a Gaussian distribution
            3. Scalar, maximum value of a Gaussian distribution
    AND
 (opt.) 5. String, the type of soldiers to spawn, (default: 'Men'), 'MenDiver' 'MenRecon' 'MenSniper'
 (opt.) 6. String, the faction of soldiers to spawn, (default: 'All'), 'BLU_F', 'IND_F', 'OPF_F', 'BLU_G_F'
 (opt.) 7. Array of strings, classnames to blacklist from spawning, (default: [])
 (opt.) 8. Array or string, DLC type(s), 'All' for all DLC, (default: '')
Return: Group

 

 

Thanks it worked. I did try putting 4 men in there it just didnt work the 1st time.

 

the zen_spawninfantry is easier as it can put number of units with just the the scalar value but I cant get it to work with the addon units. I have ready somewhere here i need to look at the cfg file of the addon? then put the faction name of that unit and its corresponding classname?

 

 

 

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

×