Jump to content
Blitzen88

Trying to add a check to Zenophon's Garrison Script

Recommended Posts

Greetings,

 

I use Zenophon's Garrison Script to garrison buildings with AI.  Its my favorite garrison script for a number of reasons. However, the script runs some sort of check to place an AI solider directly in front of a window - this way the AI unit can have a full field of view.  Although this is ideal for most scenarios, sometimes I like to "fortify" a house by blocking all of the windows. This way, a player has to clear the house of AI as opposed to shooting them through the window.  However, when I do this, Zenophon's script ignores/fails to place units in the building because their view is blocked.

 

I'm trying to add a parameter to turn the view checking on/off but I cant figure it out.  Here is a version of the script:

 

Spoiler

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

                Arma III - AI Garrison - Zenophon

Created by Zenophon

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

* Script puts units into nearby building positions to create a defensive position.

* Multiple buildings can be filled either evenly or to the limit of each sequentially

* Added Minimum Building Positions Requirement to Script

* Parameters:

    1. Array, the building(s) nearest this position is used
        
        - "getPosATL this"
        
        - "getPosATL _proxythis" for Jebus

    2. Array of objects, the units that will garrison the building(s)
        
        - "units group this"
        
        - "units group _proxythis" for Jebus

    3. Scalar, radius in which to fill building(s), -1 for only nearest building, (Default: -1)

    4. Boolean, true to put units on the roof, false for only inside, (Default: false)

    5. Boolean, true to fill all buildings in radius evenly, false for one by one, (Default: false)

    6. Boolean, true to fill from the top of the building down, (Default: false)

    7. Boolean, true to order AI units to move to the position instead of teleporting, (Default: false)

    8. Minimum Number of Building Positions (Default: 5)
    
    9. View Check - True to skip viewcheck, false to implement view check
    
    10. JBOY - True for 

* Call with (Unit Init): [getPosATL This, Units Group This, 50, False, False, False, False, 5, true, true] execVM "Scripts\AI\AI_Garrison_Zen.sqf"

* Call with (Spawn Module): [getPosATL Leader (_This Select 0), Units (_This Select 0), 50, False, False, False, False, 5] execVM "Scripts\AI\AI_Garrison_Zen.sqf"

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

//Define keywords

#define I(X) X = X + 1;

#define EVAL(X) (X call _comparator)

#define EYE_HEIGHT 1.53

#define CHECK_DISTANCE 5

#define FOV_ANGLE 10

#define ROOF_CHECK 4

#define ROOF_EDGE 2

//Make Variables Private

private [

    //Input Variable
    "_center",

    //Input Variable
    "_units",

    //Input Variable
    "_buildingRadius",

    //Input Variable
    "_putOnRoof",

    //Input Variable
    "_fillEvenly",

    "_Zen_ExtendPosition",

    "_buildingsArray",

    "_buildingPosArray",

    "_buildingPositions",

    "_posArray",

    "_unitIndex",

    "_j",

    "_building",

    "_posArray",

    "_randomIndex",

    "_housePos",

    "_startAngle",

    "_i",

    "_checkPos",

    "_hitCount",

    "_isRoof",

    "_edge",

    "_k",

    "_unUsedUnits",

    "_array",

    //Input Variable
    "_sortHeight",

    "_Zen_InsertionSort",

    "_Zen_ArrayShuffle",

    //Input Variable
    "_doMove",
    
    //Input Variable
    "_minPositions",
    
    //Input Variable
    "_ViewCheck",
    
    //Input Variable
    "_JBoyCheck"

];

//Define Input Variables / parameters

_center = _this param [0, [0,0,0], [[]], 3];

_units = _this param [1, [objNull], [[]]];

_buildingRadius = _this param [2, -1, [0]];

_putOnRoof = _this param [3, false, [true]];

_fillEvenly = _this param [4, false, [true]];

_sortHeight = _this param [5, false, [true]];

_doMove = _this param [6, false, [true]];

_minPositions = _this param [7, 5, [0]];

_ViewCheck = _this param [8, True];

_JBoyCheck = _this param [9, True];

//Define additional variables that are used by the script

_Group = group (_units select 0);

_GroupLeader = leader _Group;

//Exit script if wrong position is given

if (_center isEqualTo [0,0,0]) exitWith {
    
    systemChat "Zen Garrison: Invalid Position Given";
    
    ([])
};

//Exit script if no units are available for garrisoning

if ((count _units == 0) || {isNull (_units select 0)}) exitWith {
    
    systemChat "Zen Garrison: No Units Provided";
    
    ([])
};

//Define ExtendedPosition Function

_Zen_ExtendPosition = {
    
    private ["_center", "_dist", "_phi"];
    
    _center = _this select 0;
   
   _dist = _this select 1;
    
    _phi = _this select 2;

    ([(_center select 0) + (_dist * (cos _phi)),(_center select 1) + (_dist * (sin _phi)), (_this select 3)])
};

//Define InsertionSort Function

_Zen_InsertionSort = {
    
    private ["_i", "_j", "_count", "_array", "_element", "_value", "_comparator"];

    _array = _this select 0;
   
   _comparator = _this select 1;
   
   _count = count _array - 1;

    if (count _array == 0) exitWith {};
   
   for "_i" from 1 to _count step 1 do {
       
       scopeName "forI";
       
       _element = _array select _i;
        
        _value = EVAL(_element);

        for [{_j = _i}, {_j >= 1}, {_j = _j - 1}] do {
          
            if (_value > EVAL(_array select (_j - 1))) then {
                
                breakTo "forI";
            };
            
            _array set [_j, _array select (_j - 1)];
        };

        _array set [_j, _element];
    };
   
   if (true) exitWith {};
};

//Define ArrayShuffle Function

_Zen_ArrayShuffle = {
    
    private ["_array", "_j", "_i", "_temp"];
    
    _array = _this select 0;

    if (count _array > 1) then {
        
        for "_i" from 0 to (count _array - 1) do {
            
                _j = _i + floor random ((count _array) - _i);
                
                _temp = _array select _i;
                
                _array set [_i, (_array select _j)];
                
                _array set [_j, _temp];
        };
    };
    
    if (true) exitWith {};
};

//Define JBOY Up/Down so units can call it later

//Note - AI Trench script defines JBOY Up/Down as well - need to use different fnc name

JBOY_fnc_UpDown_Garrison = {
                                    
    _dude = _this select 0;

    _stances = _this select 1;

    _dude removeAllEventHandlers "FiredNear";

    while {alive _dude} do {

        if ((unitPos _dude) == (_stances select 0)) then {
            
            _dude setUnitPos (_stances select 1);

        } else {
        
            _dude setUnitPos (_stances select 0);
        
        };

    sleep (1 + (random 7));

    };

};

//Get Array of buildings

if (_buildingRadius < 0) then {
    
    _buildingsArray = [nearestBuilding _center];
    
    } else {
    
    _buildingsArray0 = nearestObjects [_center, ["house"], _buildingRadius];
    
    _buildingsArray1 = nearestObjects [_center, ["building"], _buildingRadius];
    
    _buildingsArray = _buildingsArray0 arrayIntersect _buildingsArray1;
};


if (count _buildingsArray == 0) exitWith {
    
    //New - Added Patrol
    
    systemChat "Zen Garrison: No Buildings Found - Starting Patrol";
    
    [_Group, getpos _GroupLeader, 200] call BIS_fnc_taskPatrol;
    
    ([])
};


//Get Array of building positions

_buildingPosArray = [];

0 = [_buildingsArray] call _Zen_ArrayShuffle;

{
    _posArray = [];
    
    for "_i" from 0 to 1000 do {
        
        if ((_x buildingPos _i) isEqualTo [0,0,0]) exitWith {};
        
        _posArray pushBack (_x buildingPos _i);
    };

    _buildingPosArray pushBack _posArray;
    
} forEach _buildingsArray;

// New - collect the index of all buildings with too few positions

_removeBuildingIndexes = [];

{
    if (count _x <= _minPositions) then {
        
        _removeBuildingIndexes pushBack _forEachIndex;
    };

} forEach _buildingPosArray;

// Reverse and remove synchronously

reverse _removeBuildingIndexes;

{
    _buildingsArray deleteAt _x;
    
    _buildingPosArray deleteAt _x;
    
} forEach _removeBuildingIndexes;

// If no buildings passed the check

if (count _buildingPosArray == 0) exitWith {
    
    //New - Added Patrol
    
    systemChat "Zen Garrison: No Buildings w/Min. Positions - Starting Patrol";

    [_Group, getpos _GroupLeader, 200] call BIS_fnc_taskPatrol;
    
    ([])
};

if (_sortHeight) then {
    
    {
        
        0 = [_x, {-1 * (_this select 2)}] call _Zen_InsertionSort;
    
    } forEach _buildingPosArray;

} else {
   
   {
       
       0 = [_x] call _Zen_ArrayShuffle;
    
    } forEach _buildingPosArray;
    
};

//Heart of the Script

_unitIndex = 0;

for [{_j = 0}, {(_unitIndex < count _units) && {(count _buildingPosArray > 0)}}, {I(_j)}] do {
    
    scopeName "for";

    _building = _buildingsArray select (_j % (count _buildingsArray));
   
    _posArray = _buildingPosArray select (_j % (count _buildingPosArray));

    if (count _posArray == 0) then {
       
        _buildingsArray deleteAt (_j % (count _buildingsArray));
        
        _buildingPosArray deleteAt (_j % (count _buildingPosArray));
    };

    while {(count _posArray) > 0} do {
        
        scopeName "while";
        
        if (_unitIndex >= count _units) exitWith {};

        _housePos = _posArray select 0;
        
        _posArray deleteAt 0;
        
        _housePos = [(_housePos select 0), (_housePos select 1), (_housePos select 2) + (getTerrainHeightASL _housePos) + EYE_HEIGHT];

        _startAngle = (round random 10) * (round random 36);
        
        for "_i" from _startAngle to (_startAngle + 350) step 10 do {
            
            _checkPos = [_housePos, CHECK_DISTANCE, (90 - _i), (_housePos select 2)] call _Zen_ExtendPosition;
            //HERE-------------------------------------------------------------------------------------------------------------------------------------------------------------NEW!
            if (!(lineIntersects [_checkPos, [_checkPos select 0, _checkPos select 1, (_checkPos select 2) + 25], objNull, objNull]) || {_ViewCheck}) then {
                //HERE-------------------------------------------------------------------------------------------------------------------------------------------------------------NEW!
                if (!(lineIntersects [_housePos, _checkPos, objNull, objNull]) || {_ViewCheck}) then {
                    
                    _checkPos = [_housePos, CHECK_DISTANCE, (90 - _i), (_housePos select 2) + (CHECK_DISTANCE * tan FOV_ANGLE)] call _Zen_ExtendPosition;
                    //HERE-------------------------------------------------------------------------------------------------------------------------------------------------------------NEW!
                    if (!(lineIntersects [_housePos, _checkPos, objNull, objNull]) || {_ViewCheck}) then {
                    
                        _hitCount = 0;
                        
                        for "_k" from 30 to 360 step 30 do {
                            
                            _checkPos = [_housePos, 20, (90 - _k), (_housePos select 2)] call _Zen_ExtendPosition;
                            
                            if (lineIntersects [_housePos, _checkPos, objNull, objNull]) then {
                                
                                I(_hitCount)
                                
                            };

                            if (_hitCount >= ROOF_CHECK) exitWith {};
                        };

                        _isRoof = (_hitCount < ROOF_CHECK) && {!(lineIntersects [_housePos, [_housePos select 0, _housePos select 1, (_housePos select 2) + 25], objNull, objNull])};
                        
                        if (!(_isRoof) || {((_isRoof) && {(_putOnRoof)})}) then {
                            
                            if (_isRoof) then {
                                
                                _edge = false;
                                
                                for "_k" from 30 to 360 step 30 do {
                                    
                                    _checkPos = [_housePos, ROOF_EDGE, (90 - _k), (_housePos select 2)] call _Zen_ExtendPosition;
                                    
                                    _edge = !(lineIntersects [_checkPos, [(_checkPos select 0), (_checkPos select 1), (_checkPos select 2) - EYE_HEIGHT - 1], objNull, objNull]);
                                    
                                    if (_edge) exitWith {
                                        
                                        _i = _k;
                                    };
                                };
                            };

                            if (!(_isRoof) || {_edge}) then {
                                
                                (_units select _unitIndex) doWatch ([_housePos, CHECK_DISTANCE, (90 - _i), (_housePos select 2) - (getTerrainHeightASL _housePos)] call _Zen_ExtendPosition);

                                (_units select _unitIndex) disableAI "TARGET";
                                
                                //MOVEMENT
                                
                                if (_doMove) then {
                                    
                                    (_units select _unitIndex) doMove ASLToATL ([(_housePos select 0), (_housePos select 1), (_housePos select 2) - EYE_HEIGHT]);
                                    
                                } else {
                                    
                                    (_units select _unitIndex) setPosASL [(_housePos select 0), (_housePos select 1), (_housePos select 2) - EYE_HEIGHT];
                                    
                                    (_units select _unitIndex) setDir _i;

                                    doStop (_units select _unitIndex);
                                    
                                    (_units select _unitIndex) forceSpeed 0;
                                };
                                
                                //New -  JBOY Argument
                                if (_isRoof) then {
                                    
                                   (_units select _unitIndex) setUnitPos "MIDDLE";
                                   
                                   if (_JBoyCheck) then {
                                       
                                        (_units select _unitIndex) addEventHandler ["FiredNear",{[(_this select 0),["DOWN","MIDDLE"]] spawn {JBOY_fnc_UpDown_Garrison};}];
                                   };
                                   
                                } else {
                                    
                                   (_units select _unitIndex) setUnitPos "UP";
                                   
                                   if (_JBoyCheck) then {
                                   
                                        (_units select _unitIndex) addEventHandler ["FiredNear",{[(_this select 0),["UP","MIDDLE"]] spawn {JBOY_fnc_UpDown_Garrison};}];
                                        
                                    };
                                };

                                I(_unitIndex)
                                
                                if (_fillEvenly) then {
                                    
                                    breakTo "for";
                                    
                                } else {
                                    
                                    breakTo "while";
                                    
                                };
                            };
                        };
                    };
                };
            };
        };
    };
};

if (_doMove) then {
    
    0 = [_units, _unitIndex] spawn {
        
        _units = _this select 0;
        
        _unitIndex = _this select 1;

        _usedUnits = [];
       
       for "_i" from 0 to (_unitIndex - 1) do {
            
            _usedUnits pushBack (_units select _i);
        
        };

        while {count _usedUnits > 0} do {
            
            sleep 1;
            
            _toRemove =  [];
            
            {
                if (unitReady _x) then {
                    
                    doStop _x;
                    
                    _x forceSpeed 0;
                    
                    _toRemove pushBack _forEachIndex;
                    
                };
                
            } forEach _usedUnits;

            {
                
            _usedUnits deleteAt (_x - _forEachIndex);
            
            } forEach _toRemove;
        };
        
        if (true) exitWith {};
    };
};


_unUsedUnits = [];

for "_i" from _unitIndex to (count _units - 1) step 1 do {
    
    _unUsedUnits pushBack (_units select _i);

};

(_unUsedUnits)
 

 

Many years ago I reached out to Zenophon regarding this issue and he tried to help me fix this issue.  Here is what he said:

Spoiler

Assuming you want to retain the roof check, you only need to remove the checks that determine if a position is near a window/opening:

 


_startAngle = (round random 10) * (round random 36);
        for "_i" from _startAngle to (_startAngle + 350) step 10 do {
            // _checkPos = [_housePos, CHECK_DISTANCE, (90 - _i), (_housePos select 2)] call _Zen_ExtendPosition;
            // check that there is no collision on with the upwards cylinder of radius CHECK_DISTANCE , centered on _housePos
            // i.e. this center is near an exterior wall of the building
            if (true) then {
            // if !(lineIntersects [_checkPos, [_checkPos select 0, _checkPos select 1, (_checkPos select 2) + 25], objNull, objNull]) then {
                // check there is no collision between the center and this point on the circle
                // i.e. we are facing a window (or any hole in the building)
                // if !(lineIntersects [_housePos, _checkPos, objNull, objNull]) then {
                if (true) then {
                    // check that the unit's line of sight is not obstructed by at least FOV_ANGLE (the window is big enough)
                    // _checkPos = [_housePos, CHECK_DISTANCE, (90 - _i), (_housePos select 2) + (CHECK_DISTANCE * tan FOV_ANGLE)] call _Zen_ExtendPosition;
                    // if !(lineIntersects [_housePos, _checkPos, objNull, objNull]) then {
                    if (true) then {
                        _hitCount = 0;
                        // count collisions with walls to determine if we are on the roof (and not an open balcony)
                        for "_k" from 30 to 360 step 30 do {
                            _checkPos = [_housePos, 20, (90 - _k), (_housePos select 2)] call _Zen_ExtendPosition;
                            if (lineIntersects [_housePos, _checkPos, objNull, objNull]) then {
                                I(_hitCount)
                            };

                            if (_hitCount >= ROOF_CHECK) exitWith {};
                        };

                        // check if center is on the roof, proceed if not or roof is allowed
                        _isRoof = (_hitCount < ROOF_CHECK) && {!(lineIntersects [_housePos, [_housePos select 0, _housePos select 1, (_housePos select 2) + 25], objNull, objNull])};
                        ...

For each one I put a comment for what it is checking, then replaced it with (true) to skip the check.  You could also make the window check an argument to the function and structure the if checks like:

 


if ({orignal check} || {don't check for window arg}) then {
    ...

 

However, I cant figure out how to implement his suggestions - when I add a true/false check with the original conditions the script doesnt seem to work. I would reach out to him again but he seems inactive now.

 

Any help would be appreciated

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

×