Headless - Improve group transfer and add API (#9874)

This commit is contained in:
johnb432 2024-05-29 20:49:59 +02:00 committed by GitHub
parent 440b9d5721
commit 120589512e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 227 additions and 61 deletions

View File

@ -1,3 +1,4 @@
ACEX_PREP(blacklist);
ACEX_PREP(endMissionNoPlayers);
ACEX_PREP(handleConnectHC);
ACEX_PREP(handleDisconnect);

View File

@ -10,6 +10,21 @@
};
// Add disconnect EH
addMissionEventHandler ["HandleDisconnect", {call FUNC(handleDisconnect)}];
[QGVAR(transferGroupsRebalance), {
params ["_groups", "_owner", "_rebalance"];
if (_groups isNotEqualTo [] && {_owner > 1}) then {
{
_x setGroupOwner _owner;
} forEach _groups;
};
// Rebalance units
if (_rebalance in [REBALANCE, FORCED_REBALANCE]) then {
(_rebalance == FORCED_REBALANCE) call FUNC(rebalance);
};
}] call CBA_fnc_addEventHandler;
} else {
// Register HC (this part happens on HC only)
[QXGVAR(headlessClientJoined), [player]] call CBA_fnc_globalEvent; // Global event for API purposes

View File

@ -0,0 +1,51 @@
#include "..\script_component.hpp"
/*
* Author: johnb43
* Modifies which units are blacklisted from being transferred to HCs.
*
* Arguments:
* 0: Units <OBJECT, GROUP, ARRAY>
* 1: Add (true) or remove (false) from blacklist <BOOL> (default: true)
* 2: Owner to transfer units to <NUMBER> (default: -1)
* 3: Rebalance <NUMBER> (default: 0)
*
* Return Value:
* None
*
* Example:
* [cursorObject, true] call ace_headless_fnc_blacklist
*
* Public: Yes
*/
params [["_units", objNull, [objNull, grpNull, []]], ["_blacklist", true, [false]], ["_owner", -1, [false]], ["_rebalance", NO_REBALANCE, [0]]];
if !(_units isEqualType []) then {
_units = [_units];
};
// Make sure passed arguments are objects or groups
_units = _units select {_x isEqualType objNull || {_x isEqualType grpNull}};
_units = _units select {!isNull _x};
if (_units isEqualTo []) exitWith {};
private _transfer = _blacklist && {_owner > 1};
private _groups = [];
{
_x setVariable [QXGVAR(blacklist), _blacklist, true];
if (_transfer) then {
if (_x isEqualType objNull) then {
_groups pushBack group _x;
} else {
_groups pushBack _x;
};
};
} forEach _units;
// Try to move AI to new owner; Also takes care of rebalancing groups
if (_transfer || {_rebalance in [REBALANCE, FORCED_REBALANCE]}) then {
[QGVAR(transferGroupsRebalance), [_groups arrayIntersect _groups, _owner, _rebalance]] call CBA_fnc_serverEvent;
};

View File

@ -17,6 +17,9 @@
params ["_force"];
// Filter out any invalid entries
GVAR(headlessClients) = GVAR(headlessClients) select {!isNull _x};
GVAR(headlessClients) params [
["_HC1", objNull, [objNull]],
["_HC2", objNull, [objNull]],
@ -36,12 +39,13 @@ private _idHC2 = -1;
private _idHC3 = -1;
private _currentHC = 0;
if (!local _HC1) then {
// objNull is never local
if (!local _HC1 && !isNull _HC1) then {
_idHC1 = owner _HC1;
_currentHC = 1;
};
if (!local _HC2) then {
if (!local _HC2 && !isNull _HC2) then {
_idHC2 = owner _HC2;
if (_currentHC == 0) then {
@ -49,7 +53,7 @@ if (!local _HC2) then {
};
};
if (!local _HC3) then {
if (!local _HC3 && !isNull _HC3) then {
_idHC3 = owner _HC3;
if (_currentHC == 0) then {
@ -57,84 +61,150 @@ if (!local _HC3) then {
};
};
if (_currentHC == 0) exitWith {
TRACE_1("No Valid HC to transfer to",_currentHC);
if (XGVAR(log)) then {
INFO("No Valid HC to transfer to");
};
};
// Prepare statistics
private _numTransferredHC1 = 0;
private _numTransferredHC2 = 0;
private _numTransferredHC3 = 0;
private _units = [];
private _transfer = false;
private _previousOwner = -1;
// Transfer AI groups
{
// No transfer if empty group
private _transfer = ((units _x) isNotEqualTo []) && {!(_x getVariable [QXGVAR(blacklist), false])};
if (_transfer) then {
// No transfer if waypoints with synchronized triggers exist for the group
private _allWaypointsWithTriggers = (waypoints _x) select {(synchronizedTriggers _x) isNotEqualTo []};
if (_allWaypointsWithTriggers isNotEqualTo []) exitWith {
_units = units _x;
// No transfer if empty group or if group is blacklisted
if (_units isEqualTo [] || {_x getVariable [QXGVAR(blacklist), false]}) then {
continue;
};
// No transfer if waypoints with synchronized triggers exist for the group
if (((waypoints _x) select {(synchronizedTriggers _x) isNotEqualTo []}) isNotEqualTo []) then {
continue;
};
{
// No transfer if already transferred
if (!_force && {(owner _x) in [_idHC1, _idHC2, _idHC3]}) exitWith {
_transfer = false;
};
{
// No transfer if already transferred
if (!_force && {(owner _x) in [_idHC1, _idHC2, _idHC3]}) exitWith {
_transfer = false;
};
// No transfer if any unit in group is blacklisted
if (_x getVariable [QXGVAR(blacklist), false]) exitWith {
_transfer = false;
};
// No transfer if player or UAV in this group
if (isPlayer _x || {unitIsUAV _x}) exitWith {
_transfer = false;
};
// No transfer if player or UAV in this group
if (isPlayer _x || {unitIsUAV _x}) exitWith {
_transfer = false;
};
// No transfer if any unit in group is blacklisted
if (_x getVariable [QXGVAR(blacklist), false]) exitWith {
_transfer = false;
};
private _vehicle = objectParent _x;
private _vehicle = objectParent _x;
// No transfer if the vehicle the unit is in or if the crew in that vehicle is blacklisted
if ((_vehicle getVariable [QXGVAR(blacklist), false]) || {unitIsUAV _vehicle}) exitWith {
_transfer = false;
};
// No transfer if the vehicle the unit is in or if the crew in that vehicle is blacklisted
if ((_vehicle getVariable [QXGVAR(blacklist), false]) || {unitIsUAV _vehicle}) exitWith {
_transfer = false;
};
// Save gear if unit about to be transferred with current loadout (naked unit work-around)
if (XGVAR(transferLoadout) == 1) then {
_x setVariable [QGVAR(loadout), _x call CBA_fnc_getLoadout, true];
};
} forEach _units;
// Save gear if unit about to be transferred with current loadout (naked unit work-around)
if (XGVAR(transferLoadout) == 1) then {
_x setVariable [QGVAR(loadout), _x call CBA_fnc_getLoadout, true];
};
} forEach (units _x);
if (!_transfer) then {
continue;
};
// Round robin between HCs if load balance enabled, else pass all to one HC
if (_transfer) then {
switch (_currentHC) do {
case 1: {
private _transferred = _x setGroupOwner _idHC1;
if (_loadBalance) then {
_currentHC = [3, 2] select (!local _HC2);
};
if (_transferred) then {
_numTransferredHC1 = _numTransferredHC1 + 1;
_previousOwner = groupOwner _x;
switch (_currentHC) do {
case 1: {
if (_loadBalance) then {
// Find the next valid HC
// If none are valid, _currentHC will remain the same
if (_idHC2 != -1) then {
_currentHC = 2;
} else {
if (_idHC3 != -1) then {
_currentHC = 3;
};
};
};
case 2: {
private _transferred = _x setGroupOwner _idHC2;
if (_loadBalance) then {
_currentHC = [1, 3] select (!local _HC3);
};
if (_transferred) then {
_numTransferredHC2 = _numTransferredHC2 + 1;
// Don't transfer if it's already local to HC1
if (_previousOwner == _idHC1) exitWith {};
[QGVAR(groupTransferPre), [_x, _HC1, _previousOwner, _idHC1], [_previousOwner, _idHC1]] call CBA_fnc_targetEvent; // API
private _transferred = _x setGroupOwner _idHC1;
[QGVAR(groupTransferPost), [_x, _HC1, _previousOwner, _idHC1, _transferred], [_previousOwner, _idHC1]] call CBA_fnc_targetEvent; // API
if (_transferred) then {
_numTransferredHC1 = _numTransferredHC1 + 1;
};
};
case 2: {
if (_loadBalance) then {
// Find the next valid HC
// If none are valid, _currentHC will remain the same
if (_idHC3 != -1) then {
_currentHC = 3;
} else {
if (_idHC1 != -1) then {
_currentHC = 1;
};
};
};
case 3: {
private _transferred = _x setGroupOwner _idHC3;
if (_loadBalance) then {
_currentHC = [2, 1] select (!local _HC1);
};
if (_transferred) then {
_numTransferredHC3 = _numTransferredHC3 + 1;
// Don't transfer if it's already local to HC2
if (_previousOwner == _idHC2) exitWith {};
[QGVAR(groupTransferPre), [_x, _HC2, _previousOwner, _idHC2], [_previousOwner, _idHC2]] call CBA_fnc_targetEvent; // API
private _transferred = _x setGroupOwner _idHC2;
[QGVAR(groupTransferPost), [_x, _HC2, _previousOwner, _idHC2, _transferred], [_previousOwner, _idHC2]] call CBA_fnc_targetEvent; // API
if (_transferred) then {
_numTransferredHC2 = _numTransferredHC2 + 1;
};
};
case 3: {
if (_loadBalance) then {
// Find the next valid HC
// If none are valid, _currentHC will remain the same
if (_idHC1 != -1) then {
_currentHC = 1;
} else {
if (_idHC2 != -1) then {
_currentHC = 2;
};
};
};
default {
TRACE_1("No Valid HC to transfer to",_currentHC);
// Don't transfer if it's already local to HC3
if (_previousOwner == _idHC3) exitWith {};
[QGVAR(groupTransferPre), [_x, _HC3, _previousOwner, _idHC3], [_previousOwner, _idHC3]] call CBA_fnc_targetEvent; // API
private _transferred = _x setGroupOwner _idHC2;
[QGVAR(groupTransferPost), [_x, _HC3, _previousOwner, _idHC3, _transferred], [_previousOwner, _idHC3]] call CBA_fnc_targetEvent; // API
if (_transferred) then {
_numTransferredHC3 = _numTransferredHC3 + 1;
};
};
};

View File

@ -17,3 +17,7 @@
#include "\z\ace\addons\main\script_macros.hpp"
#define DELAY_DEFAULT 15
#define NO_REBALANCE 0
#define REBALANCE 1
#define FORCED_REBALANCE 2

View File

@ -156,9 +156,15 @@ MenuType: 0 = Interaction, 1 = Self Interaction
| Event Key | Parameters | Locality | Type | Description |
|---------- |------------|----------|------|-------------|
|---------- |------------|----------|------|-------------|
| `ace_interaction_doorOpeningStarted` | [_house, _door, _animations] | Local | Listen | Called when local unit starts interacting with doors
| `ace_interaction_doorOpeningStopped` | [_house, _door, _animations] | Local | Listen | Called when local unit stopps interacting with doors
| `ace_interaction_doorOpeningStopped` | [_house, _door, _animations] | Local | Listen | Called when local unit stops interacting with doors
### 2.17 Headless (`ace_headless`)
| Event Key | Parameters | Locality | Type | Description |
|---------- |------------|----------|------|-------------|
| `ace_headless_groupTransferPre` | [_group, _HC (OBJECT), _previousOwner, _idHC] | Target | Listen | Called just before a group is transferred from any machine to a HC. Called where group currently is local and on the HC, where group is going to be local.
| `ace_headless_groupTransferPost` | [_group, _HC (OBJECT), _previousOwner, _idHC, _transferredSuccessfully] | Target | Listen | Called just after a group is transferred from a machine to a HC. Called where group was local and on the HC, where group is now local. `_transferredSuccessfully` is passed so mods can actually check if the locality was properly transferred, as ownership transfer is not guaranteed.
## 3. Usage
Also Reference [CBA Events System](https://github.com/CBATeam/CBA_A3/wiki/Custom-Events-System){:target="_blank"} documentation.

View File

@ -30,14 +30,29 @@ As of ACEX v3.2.0 _(before merge into ACE3)_ this feature can also be enabled wi
## 2. Scripting
### 2.1 Disable Transferring for a Group
### 2.1 Manipulating HC Transfers of Groups via function
To prevent a group from transferring to a Headless Client use the following line on a group leader (or every unit in a group in case group leader may not spawn):
`ace_headless_fnc_blacklist`
| Arguments | Type | Optional (default value)
---| --------- | ---- | ------------------------
0 | Units | Object, Group or Array of both | Required
1 | Add (true) or remove (false) from blacklist | Bool | Optional (default: `true`)
2 | Owner to transfer units to | Number | Optional (default: `-1`)
3 | Rebalance (0 = no rebalance, 1 = rebalance, 2 = force rebalance) | Number | (default: `0`)
**R** | None | None | Return value
`Force rebalance` means that all units, including the ones that are on the HCs, are rebalanced amongst the HCs, whereas `rebalance` means that newly spawned units are going to be evenly distributed amongst HCs. Therefore, `rebalance` does not guarantee that the HCs will have an equal amount of groups, whereas `force rebalance` does.
### 2.2 Disable Transferring for a Group via variable
To prevent a group from transferring to a Headless Client use the following line on a unit within a group:
```sqf
this setVariable ["acex_headless_blacklist", true];
```
This variable can also be set on vehicles, disabling transferal of any groups having units in said vehicles.
## 3. Limitations
@ -48,3 +63,7 @@ Some Arma 3 features are incompatible, this is up to BI to add support. Disable
Additionally, groups will not be transferred due to lack of support if they:
- Have waypoints with synchronized triggers (waypoint would not change status based on trigger condition) (added in ACEX v3.2.0 - _before merge into ACE3_)
Groups will not be transferred to avoid issues:
- If a player is within the group.
- If they contain UAVs.