Skip to content

Commit 6c86754

Browse files
committed
Add optional ACRE2 radio state preservation
1 parent 741a2ad commit 6c86754

13 files changed

Lines changed: 347 additions & 13 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ An official rewrite and continuation of the original [BackpackOnChest mod by Der
3131
### Improvements from the original
3232
- Optimizations.
3333
- Support for variables associated with the backpack (for items such as the ACE Gunbag or TFAR backpack radios).
34+
- Preserves ACRE2 radio settings (channel/volume/ear, etc.) when moving a backpack to/from the chest.
3435
- Transition to an easier development platform with the support of CBA and ACE macros.
3536
- Various other improvements.
3637

addons/main/XEH_PREP.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@ PREP(chestpackVariables);
2222
PREP(clearAllCargo);
2323
PREP(clearAllItemsFromChestpack);
2424
PREP(clearCargoBackpacks);
25+
PREP(acreIsInitialized);
26+
PREP(acreGetRadioState);
27+
PREP(acreApplyRadioState);
28+
PREP(acreCaptureRadioStatesFromLoadout);
29+
PREP(acreRestoreRadioStates);
2530
PREP(EHAnimDone);
2631
PREP(EHGetIn);
2732
PREP(EHGetOut);
2833
PREP(EHHandleDisconnect);
2934
PREP(EHKilled);
3035
PREP(itemMass);
36+
PREP(chestpackAcreRadios);
3137
PREP(moduleAdd);
3238
PREP(moduleOnChest);
3339
PREP(removeChestpack);

addons/main/XEH_postInit.sqf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ if (isServer) then {
4444
};
4545

4646
GVAR(isACEAXLoaded) = isClass (configFile >> "CfgPatches" >> "aceax_gearinfo");
47+
GVAR(isACRELoaded) = isClass (configFile >> "CfgPatches" >> "acre_main")
48+
|| {isClass (configFile >> "CfgPatches" >> "acre_api")};
4749

4850
// Backpack classnames which will be made invisible instead of being made a chestpack. Useful for items like the vanilla legstrap.
4951
GVAR(exceptions) = [
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Apply a previously exported radio state to a (new) ACRE2 radio ID.
5+
*
6+
* Arguments:
7+
* 0: Radio ID <STRING>
8+
* 1: Radio state <ARRAY> (from fnc_acreGetRadioState)
9+
*
10+
* Return Value:
11+
* None
12+
*
13+
* Example:
14+
* [_newRadioId, _state] call bocr_main_fnc_acreApplyRadioState;
15+
*
16+
* Public: No
17+
*/
18+
19+
params ["_radioId", "_state"];
20+
if !(missionNamespace getVariable [QGVAR(isACRELoaded), false]) exitWith {};
21+
if (_state isEqualTo []) exitWith {};
22+
23+
_state params ["_oldId", "_base", "_channel", "_volume", "_spatial", "_audioSource", "_onOff"];
24+
25+
// These API calls are client-side.
26+
[_radioId, _channel] call acre_api_fnc_setRadioChannel;
27+
[_radioId, _volume] call acre_api_fnc_setRadioVolume;
28+
[_radioId, _spatial] call acre_api_fnc_setRadioSpatial;
29+
[_radioId, _audioSource] call acre_api_fnc_setRadioAudioSource;
30+
[_radioId, _onOff] call acre_api_fnc_setRadioOnOffState;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Capture ACRE2 radio states for radios referenced by a loadout cargo array
5+
* (typically: getUnitLoadout _unit select 5 select 1 for backpack contents).
6+
*
7+
* Arguments:
8+
* 0: Unit <OBJECT>
9+
* 1: Loadout cargo array <ARRAY>
10+
*
11+
* Return Value:
12+
* Array of radio state arrays <ARRAY>
13+
*
14+
* Example:
15+
* [player, ((getUnitLoadout player) select 5) select 1] call bocr_main_fnc_acreCaptureRadioStatesFromLoadout;
16+
*
17+
* Public: No
18+
*/
19+
20+
params ["_unit", "_cargoLoadout"];
21+
22+
if (!([_unit] call FUNC(acreIsInitialized))) exitWith {[]};
23+
if !(_cargoLoadout isEqualType []) exitWith {[]};
24+
25+
private _unitRadios = [_unit] call acre_api_fnc_getAllRadios;
26+
if (_unitRadios isEqualTo []) exitWith {[]};
27+
28+
// Extract classnames referenced by the cargo loadout.
29+
private _classnames = [];
30+
{
31+
if (_x isEqualType [] && {count _x > 0}) then {
32+
private _cargoClass = _x select 0;
33+
if (_cargoClass isEqualType "") then {
34+
_classnames pushBack _cargoClass;
35+
};
36+
};
37+
} forEach _cargoLoadout;
38+
39+
// Radios are represented by their ID classnames; capture only those present in this cargo loadout.
40+
private _radioIds = [];
41+
{
42+
if (_x in _unitRadios) then {
43+
_radioIds pushBackUnique _x;
44+
};
45+
} forEach _classnames;
46+
47+
private _states = [];
48+
{
49+
private _state = [_x] call FUNC(acreGetRadioState);
50+
if !(_state isEqualTo []) then {
51+
_states pushBack _state;
52+
};
53+
} forEach _radioIds;
54+
55+
_states
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Export ACRE2 state for a single radio ID so it can be restored later.
5+
*
6+
* Arguments:
7+
* 0: Radio ID <STRING>
8+
*
9+
* Return Value:
10+
* Radio state <ARRAY>:
11+
* 0: Old radio ID <STRING>
12+
* 1: Base radio classname <STRING>
13+
* 2: Channel <NUMBER>
14+
* 3: Volume <NUMBER>
15+
* 4: Spatial (LEFT/RIGHT/CENTER) <STRING>
16+
* 5: Audio source <STRING>
17+
* 6: On/Off <BOOL>
18+
*
19+
* Example:
20+
* ["ACRE_PRC152_ID_1"] call bocr_main_fnc_acreGetRadioState;
21+
*
22+
* Public: No
23+
*/
24+
25+
params ["_radioId"];
26+
27+
if !(missionNamespace getVariable [QGVAR(isACRELoaded), false]) exitWith {[]};
28+
29+
[
30+
_radioId,
31+
[_radioId] call acre_api_fnc_getBaseRadio,
32+
[_radioId] call acre_api_fnc_getRadioChannel,
33+
[_radioId] call acre_api_fnc_getRadioVolume,
34+
[_radioId] call acre_api_fnc_getRadioSpatial,
35+
[_radioId] call acre_api_fnc_getRadioAudioSource,
36+
[_radioId] call acre_api_fnc_getRadioOnOffState
37+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Returns true if ACRE2 is present and initialized on this client.
5+
*
6+
* Arguments:
7+
* 0: Unit <OBJECT> (optional, for locality checks)
8+
*
9+
* Return Value:
10+
* ACRE initialized <BOOL>
11+
*
12+
* Example:
13+
* [player] call bocr_main_fnc_acreIsInitialized;
14+
*
15+
* Public: No
16+
*/
17+
18+
params [["_unit", objNull]];
19+
20+
if !(missionNamespace getVariable [QGVAR(isACRELoaded), false]) exitWith {false};
21+
if (isNil "acre_api_fnc_isInitialized") exitWith {false};
22+
23+
// Most ACRE API calls are intended to run where the unit is local.
24+
if (!isNull _unit && {!local _unit}) exitWith {false};
25+
26+
call acre_api_fnc_isInitialized
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Restore previously captured ACRE2 radio states after radios have been re-added.
5+
*
6+
* Arguments:
7+
* 0: Unit <OBJECT>
8+
* 1: Radio IDs present before restore <ARRAY>
9+
* 2: Saved radio states <ARRAY>
10+
*
11+
* Return Value:
12+
* None
13+
*
14+
* Example:
15+
* [_unit, _preRadios, _states] call bocr_main_fnc_acreRestoreRadioStates;
16+
*
17+
* Public: No
18+
*/
19+
20+
params ["_unit", "_preRadios", "_states"];
21+
22+
if (!([_unit] call FUNC(acreIsInitialized))) exitWith {};
23+
if !(_preRadios isEqualType []) exitWith {};
24+
if (!(_states isEqualType []) || {_states isEqualTo []}) exitWith {};
25+
26+
// Wait a moment for ACRE to register the new radios (inventory changes are not always immediate).
27+
[
28+
{
29+
params ["_unit", "_preRadios", "_states"];
30+
private _postRadios = [_unit] call acre_api_fnc_getAllRadios;
31+
private _added = _postRadios - _preRadios;
32+
private _expected = count _states;
33+
(count _added) >= _expected
34+
},
35+
{
36+
params ["_unit", "_preRadios", "_states"];
37+
38+
private _postRadios = [_unit] call acre_api_fnc_getAllRadios;
39+
private _added = _postRadios - _preRadios;
40+
if (_added isEqualTo []) exitWith {};
41+
42+
private _remaining = +_added;
43+
{
44+
private _state = _x;
45+
_state params ["_oldId", "_base"];
46+
private _idx = -1;
47+
{
48+
private _radioId = _x;
49+
if (([_radioId] call acre_api_fnc_getBaseRadio) isEqualTo _base) exitWith {
50+
_idx = _forEachIndex;
51+
};
52+
} forEach _remaining;
53+
if (_idx >= 0) then {
54+
private _newId = _remaining deleteAt _idx;
55+
[_newId, _state] call FUNC(acreApplyRadioState);
56+
};
57+
} forEach _states;
58+
},
59+
[_unit, _preRadios, _states],
60+
2,
61+
{
62+
// Timed out - best effort apply using whatever radios exist now.
63+
params ["_unit", "_preRadios", "_states"];
64+
private _postRadios = [_unit] call acre_api_fnc_getAllRadios;
65+
private _added = _postRadios - _preRadios;
66+
if (_added isEqualTo []) exitWith {};
67+
68+
private _remaining = +_added;
69+
{
70+
private _state = _x;
71+
_state params ["_oldId", "_base"];
72+
private _idx = -1;
73+
{
74+
private _radioId = _x;
75+
if (([_radioId] call acre_api_fnc_getBaseRadio) isEqualTo _base) exitWith {
76+
_idx = _forEachIndex;
77+
};
78+
} forEach _remaining;
79+
if (_idx >= 0) then {
80+
private _newId = _remaining deleteAt _idx;
81+
[_newId, _state] call FUNC(acreApplyRadioState);
82+
};
83+
} forEach _states;
84+
}
85+
] call CBA_fnc_waitUntilAndExecute;

addons/main/functions/fnc_actionOnBack.sqf

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,55 @@ params ["_unit"];
1919
private _chestpack = [_unit] call FUNC(chestpack);
2020
private _chestpackLoadout = [_unit] call FUNC(chestpackLoadout);
2121
private _chestpackVariables = [_unit] call FUNC(chestpackVariables);
22+
private _chestpackAcreRadios = [_unit] call FUNC(chestpackAcreRadios);
2223

2324
private _shouldSwitchNVGs = currentVisionMode _unit != 0;
2425

2526
//make sure the player has a chestpack and no backpack
2627
if ((_chestpack isEqualTo "") or (backpack _unit isNotEqualTo "")) exitWith {};
2728

28-
//add items
29-
private _loadout = getUnitLoadout _unit;
30-
_loadout set [5, [_chestpack, _chestpackLoadout]];
31-
_unit setUnitLoadout _loadout;
29+
private _acreEnabled = [_unit] call FUNC(acreIsInitialized);
30+
private _preRadios = [];
31+
if (_acreEnabled && {!(_chestpackAcreRadios isEqualTo [])}) then {
32+
_preRadios = [_unit] call acre_api_fnc_getAllRadios;
33+
};
34+
35+
if (_acreEnabled) then {
36+
// Avoid setUnitLoadout when ACRE is running to prevent full radio re-init.
37+
_unit addBackpackGlobal _chestpack;
38+
private _backpack = backpackContainer _unit;
39+
[_backpack, _chestpackLoadout] call FUNC(setBackpackLoadout);
40+
} else {
41+
//add items
42+
private _loadout = getUnitLoadout _unit;
43+
_loadout set [5, [_chestpack, _chestpackLoadout]];
44+
_unit setUnitLoadout _loadout;
45+
};
3246

3347
if (GVAR(isACEAXLoaded)) then {
3448
[_unit, [_unit] call aceax_gearinfo_fnc_getTextureOptions] call aceax_gearinfo_fnc_setTextureOptions;
3549
};
3650

37-
if (_shouldSwitchNVGs) then {
51+
if (!_acreEnabled && {_shouldSwitchNVGs}) then {
3852
_unit action ["NVGoggles", _unit];
3953
};
4054
//prefilled versions of backpacks (ammo bearer, engineer, explosives, medic, repair, etc)
4155
//can be emptied and placed in unit's backpack
4256
//they must each be emptied when added
4357
//safe since game inventory won't let players add non-empty backpacks into their backpack
44-
[QGVAR(clearCargoBackpacks), [backpackContainer _unit]] call CBA_fnc_globalEvent;
58+
if (!_acreEnabled) then {
59+
[QGVAR(clearCargoBackpacks), [backpackContainer _unit]] call CBA_fnc_globalEvent;
60+
};
4561

4662
//add variables
4763
private _backpack = backpackContainer _unit;
4864
{
4965
_backpack setVariable [(_x select 0), (_x select 1), true];
5066
} forEach _chestpackVariables;
5167

68+
// Restore ACRE radio settings for radios that were inside the pack when it was moved to chest.
69+
if (_acreEnabled && {!(_chestpackAcreRadios isEqualTo [])}) then {
70+
[_unit, _preRadios, _chestpackAcreRadios] call FUNC(acreRestoreRadioStates);
71+
};
72+
5273
[_unit] call FUNC(removeChestpack);

addons/main/functions/fnc_actionOnChest.sqf

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,20 @@ params["_unit"];
1919

2020
private _backpack = backpack _unit;
2121
private _backpackLoad = loadBackpack _unit;
22-
private _backpackLoadout = ((getUnitLoadout _unit) select 5) select 1;
22+
private _unitLoadout = getUnitLoadout _unit;
23+
private _backpackLoadoutRaw = (_unitLoadout select 5) select 1;
24+
private _acreRadioStates = [];
25+
26+
// If ACRE is running, capture per-radio state before the backpack (and its contents) are removed.
27+
// Also filter ACRE radio ID classnames out of the stored loadout to avoid re-adding IDed radios later.
28+
if ([_unit] call FUNC(acreIsInitialized)) then {
29+
_acreRadioStates = [_unit, _backpackLoadoutRaw] call FUNC(acreCaptureRadioStatesFromLoadout);
30+
if !(isNil "acre_api_fnc_filterUnitLoadout") then {
31+
_unitLoadout = [_unitLoadout] call acre_api_fnc_filterUnitLoadout;
32+
};
33+
};
34+
35+
private _backpackLoadout = (_unitLoadout select 5) select 1;
2336
private _backpackVariables = [];
2437

2538
//Variable Handling
@@ -29,5 +42,6 @@ private _backpackVariables = [];
2942
} forEach ((allVariables (backpackContainer _unit) - GVAR(VarBlacklist)));
3043

3144
[_unit, _backpack, _backpackLoadout, _backpackVariables, _backpackLoad] call FUNC(addChestpack);
45+
_unit setVariable [QGVAR(acreChestpackRadios), _acreRadioStates];
3246

3347
removeBackpackGlobal _unit;

0 commit comments

Comments
 (0)