-
Notifications
You must be signed in to change notification settings - Fork 739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add garrison zeus modules #4555
Changes from 3 commits
fa4d7f8
328c78e
39a50ab
538ae61
56a14cc
9b26c8e
ec0c684
7e9ee8d
27dc72d
921944b
907fd90
32ec046
19a9d16
315e50b
adb2d65
89dc380
15478f8
186bf7f
6555a4b
d8dafa4
10d2321
343319c
f0795e1
b5e1e3c
b23492a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* | ||
* Author: alganthe | ||
* Garrison function used to garrison AI inside buildings. | ||
* | ||
* Arguments: | ||
* 0: The building(s) nearest this position are used <POSITION> | ||
* 1: Limit the building search to those type of building <ARRAY> | ||
* 2: Units that will be garrisoned <ARRAY> | ||
* 3: Radius to fill building(s) <SCALAR> default: 50 | ||
* 4: 0: even filling, 1: building by building, 2: random filling <SCALAR> default: 0 | ||
* 5: True to fill building(s) from top to bottom <BOOL> default: false | ||
|
||
* Return Value: | ||
* Array of units not garrisoned | ||
* | ||
* Public: Yes | ||
* | ||
* Example: | ||
* [position, nil, [unit1, unit2, unit3, unitN], 200, 1, false] call ace_common_fnc_garrison | ||
*/ | ||
#include "script_component.hpp" | ||
|
||
params [["_startingPos",[0,0,0], [[]], 3], ["_buildingTypes", ["Building"], [[]]], ["_unitsArray", [], [[]]], ["_fillingRadius", 50, [0]], ["_fillingType", 0, [0]], ["_topDownFilling", false, [true]]]; | ||
|
||
_unitsArray = _unitsArray select {alive _x && {!isPlayer _x}}; | ||
|
||
if (_startingPos isEqualTo [0,0,0]) exitWith { | ||
[LSTRING(GarrisonInvalidPosition)] call EFUNC(common,displayTextStructured); | ||
}; | ||
|
||
if (count _unitsArray == 0 || {isNull (_unitsArray select 0)}) exitWith { | ||
[LSTRING(GarrisonNoUnits)] call EFUNC(common,displayTextStructured); | ||
}; | ||
|
||
private _buildings = []; | ||
|
||
if (_fillingRadius < 50) then { | ||
_buildings = nearestObjects [_startingPos, _buildingTypes, 50]; | ||
} else { | ||
_buildings = nearestObjects [_startingPos, _buildingTypes, _fillingRadius]; | ||
_buildings = _buildings call BIS_fnc_arrayShuffle; | ||
}; | ||
|
||
if (count _buildings == 0) exitWith { | ||
[LSTRING(GarrisonNoBuilding)] call EFUNC(common,displayTextStructured); | ||
}; | ||
|
||
private _buildingsIndexes = []; | ||
|
||
if (_topDownFilling) then { | ||
{ | ||
private _buildingPos = _x buildingPos -1; | ||
|
||
// Those reverse are necessary, as dumb as it is there's no better way to sort those subarrays in sqf | ||
{ | ||
reverse _x; | ||
} foreach _buildingPos; | ||
|
||
_buildingPos sort false; | ||
|
||
{ | ||
reverse _x; | ||
} foreach _buildingPos; | ||
|
||
_buildingsIndexes pushback _buildingPos; | ||
} foreach _buildings; | ||
} else { | ||
{ | ||
_buildingsIndexes pushback (_x buildingPos -1); | ||
} foreach _buildings; | ||
}; | ||
|
||
// Remove buildings without positions | ||
{ | ||
_buildingsIndexes deleteAt (_buildingsIndexes find _x); | ||
} foreach (_buildingsIndexes select {count _x == 0}); | ||
|
||
// Warn the user that there's not enough positions to place all units | ||
private _count = 0; | ||
{_count = _count + count _x} foreach _buildingsIndexes; | ||
private _leftOverAICount = (count _unitsArray) - _count; | ||
if (_leftOverAICount > 0) then { | ||
[LSTRING(GarrisonNotEnoughPos)] call EFUNC(common,displayTextStructured); | ||
}; | ||
|
||
private _placedUnits = []; | ||
|
||
// Do the placement | ||
switch (_fillingType) do { | ||
case 0: { | ||
while {count _unitsArray > 0} do { | ||
if (count _buildingsIndexes == 0) exitWith {}; | ||
|
||
private _building = _buildingsIndexes select 0; | ||
|
||
if (_building isEqualTo []) then { | ||
_buildingsIndexes deleteAt 0; | ||
} else { | ||
private _pos = _building select 0; | ||
|
||
private _nearestUnits = (_pos nearEntities ["CAManBase", 1]); | ||
if (count _nearestUnits > 0 && {count (_nearestUnits select {getPos _x select 2 == _pos select 2}) > 0}) then { | ||
_buildingsIndexes set [0, _building - [_pos]]; | ||
|
||
} else { | ||
private _unit = _unitsArray select 0; | ||
_unit setPos _pos; | ||
_placedUnits pushBack _unit; | ||
_unitsArray deleteAt (_unitsArray find _unit); | ||
_building deleteAt 0; | ||
_buildingsIndexes deleteAt 0; | ||
_buildingsIndexes pushbackUnique _building; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
case 1: { | ||
while {count _unitsArray > 0} do { | ||
if (count _buildingsIndexes == 0) exitWith {}; | ||
|
||
private _building = _buildingsIndexes select 0; | ||
|
||
if (_building isEqualTo []) then { | ||
_buildingsIndexes deleteAt 0; | ||
} else { | ||
private _pos = _building select 0; | ||
|
||
private _nearestUnits = (_pos nearEntities ["CAManBase", 1]); | ||
if (count _nearestUnits > 0 && {count (_nearestUnits select {getPos _x select 2 == _pos select 2}) > 0}) then { | ||
_buildingsIndexes set [0, _building - [_pos]]; | ||
|
||
} else { | ||
private _unit = _unitsArray select 0; | ||
_unit setPos _pos; | ||
_placedUnits pushBack _unit; | ||
_unitsArray deleteAt (_unitsArray find _unit); | ||
_buildingsIndexes set [0, _building - [_pos]]; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
case 2: { | ||
while {count _unitsArray > 0} do { | ||
if (count _buildingsIndexes == 0) exitWith {}; | ||
|
||
private _building = selectRandom _buildingsIndexes; | ||
|
||
if (_building isEqualTo []) then { | ||
_buildingsIndexes deleteAt (_buildingsIndexes find _building); | ||
} else { | ||
private _pos = selectRandom _building; | ||
|
||
private _nearestUnits = (_pos nearEntities ["CAManBase", 1]); | ||
if (count _nearestUnits > 0 && {count (_nearestUnits select {getPos _x select 2 == _pos select 2}) > 0}) then { | ||
_buildingsIndexes set [(_buildingsIndexes find _building), _building - [_pos]]; | ||
|
||
} else { | ||
private _unit = _unitsArray select 0; | ||
_unit setPos _pos; | ||
_unitsArray deleteAt (_unitsArray find _unit); | ||
_placedUnits pushBack _unit; | ||
_buildingsIndexes set [(_buildingsIndexes find _building), _building - [_pos]]; | ||
}; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
[QGVAR(disableAI), [_placedUnits, "AUTOCOMBAT"], _placedUnits] call CBA_fnc_targetEvent; | ||
[QGVAR(disableAI), [_placedUnits, "PATH"], _placedUnits] call CBA_fnc_targetEvent; | ||
_unitsArray |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Author: alganthe | ||
* Used to un-garrison units garrisoned with ace_common_fnc_garrison | ||
* | ||
* Arguments: | ||
* 0: Array of units to un-garrison <ARRAY> | ||
* | ||
* Return Value: | ||
* Nothing | ||
* | ||
* Public: Yes | ||
* | ||
*/ | ||
#include "script_component.hpp" | ||
|
||
params [["_unitsArray", [], [[]]]]; | ||
|
||
{ | ||
if !(isPlayer _x) then { | ||
if (local _x) then { | ||
_x enableAI "AUTOCOMBAT"; | ||
_x enableAI "PATH"; | ||
} else { | ||
[QGVAR(enableAI), [_placedUnits, "AUTOCOMBAT"], _placedUnits] call CBA_fnc_targetEvent; | ||
[QGVAR(enableAI), [_placedUnits, "PATH"], _placedUnits] call CBA_fnc_targetEvent; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why this is being done inside this loop (also this variable doesn't exist). |
||
}; | ||
|
||
private _leader = leader _x; | ||
if (_leader != _x) then { | ||
doStop _x; | ||
if (local _leader) then { | ||
_x doFollow _leader; | ||
} else { | ||
[QGVAR(doFollow), [_x, _leader], _leader] call CBA_fnc_targetEvent; | ||
}; | ||
|
||
} else { | ||
if (local _x) then { | ||
_x doMove ((nearestBuilding (getPos _x)) buildingExit 0); | ||
} else { | ||
[QGVAR(doMove), [_x, ((nearestBuilding (getPos _x)) buildingExit 0)], _x] call CBA_fnc_targetEvent; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than sending an event for every unit from within this function, you should be running this whole function via an event from the module function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 Also to the note above:
|
||
}; | ||
}; | ||
}; | ||
} foreach _unitsArray; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -874,5 +874,21 @@ | |
<Russian>Нет места для выгрузки</Russian> | ||
<Japanese>降ろすための空間がありません</Japanese> | ||
</Key> | ||
<Key ID="STR_ACE_Common_GarrisonInvalidPosition"> | ||
<English>Invalid position provided.</English> | ||
<French>Position invalide fourni</French> | ||
</Key> | ||
<Key ID="STR_ACE_Common_GarrisonNoUnits"> | ||
<English>No units provided.</English> | ||
<French>Aucune unité fourni</French> | ||
</Key> | ||
<Key ID="STR_ACE_Common_GarrisonNotEnoughPos"> | ||
<English>There isn't enough positions to place all units.</English> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. aren't |
||
<French>Il n'y a pas assez de positions pour placer toutes les unités</French> | ||
</Key> | ||
<Key ID="STR_ACE_Common_GarrisonNoBuilding"> | ||
<English>No building found.</English> | ||
<French>Aucun bâtiment trouvé</French> | ||
</Key> | ||
</Package> | ||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -174,4 +174,16 @@ class CfgVehicles { | |
function = QFUNC(moduleUnconscious); | ||
icon = QPATHTOF(UI\Icon_Module_Zeus_Unconscious_ca.paa); | ||
}; | ||
class GVAR(moduleGarrison): GVAR(moduleBase) { | ||
curatorCanAttach = 1; | ||
displayName = CSTRING(ModuleGarrison_DisplayName); | ||
curatorInfoType = QGVAR(RscGarrison); | ||
icon = QPATHTOF(UI\Icon_Module_Zeus_Garrison_ca.paa); | ||
}; | ||
class GVAR(moduleUnGarrison): GVAR(moduleBase) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if I like having a separate module to undo the effects of another, could combine these into 1 by having a UI option to un-garrison - that way there's a single module that handles all things garrison. Though that's just a thought, very much unsure what works better in terms of UX (if anyone else has thoughts on this please chime in). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should decide with what and stick with it. Currently there is already add/remove Virtual Arsenal which is 2 modules. Can always streamline into 1 later if it would be better. |
||
curatorCanAttach = 1; | ||
displayName = CSTRING(ModuleUnGarrison_DisplayName); | ||
function = QFUNC(moduleUnGarrison); | ||
icon = QPATHTOF(UI\Icon_Module_Zeus_UnGarrison_ca.paa); | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Author: alganthe | ||
* Module calling the garrison function | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing final dot. |
||
* | ||
* Arguments: | ||
* 0: The module logic <OBJECT> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* 1: Position of the module <POSITION> | ||
* 2: Radius of the task <NUMBER> | ||
* 3: Filling mode of the garrison function <NUMBER> | ||
* 4: Enable or not top down filling <BOOLEAN> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* | ||
* Return Value: | ||
* None | ||
*/ | ||
|
||
#include "script_component.hpp" | ||
|
||
params ["_logic", "_pos", "_radius" ,"_mode" , "_topDownMode"]; | ||
|
||
private _unit = (attachedTo _logic); | ||
private _building = nearestBuilding (getPosASL _unit); | ||
|
||
// Handles errors | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validation of the module target should only be taking place in the ui function (it doesn't make sense to show zeus a UI and then tell them they placed the module wrong only after they close it). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only true if the zeus interface updates in real time. I think it's fine to have the check in both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, good point. The unit could be deleted between the UI opening and closing. Can safely ignore my initial comment 👍 |
||
scopeName "Main"; | ||
private _fnc_errorAndClose = { | ||
params ["_msg"]; | ||
deleteVehicle _logic; | ||
[_msg] call EFUNC(common,displayTextStructured); | ||
breakOut "Main"; | ||
}; | ||
|
||
switch (false) do { | ||
case !(isNull _unit): { | ||
[LSTRING(NothingSelected)] call _fnc_errorAndClose; | ||
}; | ||
case (_unit isKindOf "CAManBase"): { | ||
[LSTRING(OnlyInfantry)] call _fnc_errorAndClose; | ||
}; | ||
case (alive _unit): { | ||
[LSTRING(OnlyAlive)] call _fnc_errorAndClose; | ||
}; | ||
case (_unit distance _building < 500): { | ||
[LSTRING(BuildingTooFar)] call _fnc_errorAndClose; | ||
}; | ||
}; | ||
|
||
private _units = units _unit; | ||
// Make sure all units are disembarked. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No final dot. |
||
{ | ||
if (vehicle _x != _x) then { | ||
moveOut _x; | ||
}; | ||
} forEach _units; | ||
|
||
[_pos, ["Building"], _units, _radius, _mode, _topDownMode] call EFUNC(common,garrison); | ||
|
||
deleteVehicle _logic; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsure if these functions belong in common or not 😝
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say ace_ai, despite it not having any sqf atm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't CBA now have garrison functions that could be used instead? If not, then yeah, those should go into
ace_ai
component.