Skip to content

Commit ee6ef87

Browse files
committed
ACRE2 compatibility and loadout restore fixes
- Preserve ACRE2 radio state when moving packs\n- Avoid ACRE filterUnitLoadout cargo corruption by filtering only radio IDs\n- Normalize cargo counts from actual container to prevent dropped items\n- Restore cargo reliably by adding ACRE radios last\n- No debug logging
1 parent 741a2ad commit ee6ef87

19 files changed

Lines changed: 695 additions & 48 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/Stringtable.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,5 @@
110110
<Russian>Вынуждать игрока ходить когда рюкзак находится на груди</Russian>
111111
<Turkish>Oyuncu sırt çantası gövdesindeyken yürümeye zorlanır</Turkish>
112112
</Key>
113-
</Package>
114-
</Project>
113+
</Package>
114+
</Project>

addons/main/XEH_PREP.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,21 @@ PREP(chestpackVariables);
2222
PREP(clearAllCargo);
2323
PREP(clearAllItemsFromChestpack);
2424
PREP(clearCargoBackpacks);
25+
PREP(acreIsInitialized);
26+
PREP(acreFilterCargoLoadout);
27+
PREP(normalizeCargoLoadoutCounts);
28+
PREP(acreGetUnitRadioIds);
29+
PREP(acreGetRadioState);
30+
PREP(acreApplyRadioState);
31+
PREP(acreCaptureRadioStatesFromLoadout);
32+
PREP(acreRestoreRadioStates);
2533
PREP(EHAnimDone);
2634
PREP(EHGetIn);
2735
PREP(EHGetOut);
2836
PREP(EHHandleDisconnect);
2937
PREP(EHKilled);
3038
PREP(itemMass);
39+
PREP(chestpackAcreRadios);
3140
PREP(moduleAdd);
3241
PREP(moduleOnChest);
3342
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: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 FUNC(acreGetUnitRadioIds);
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+
private _unitRadiosLc = _unitRadios apply {toLower _x};
42+
{
43+
// Loadout/cargo can contain ID classnames with different casing than ACRE returns (e.g. "ACRE_PRC343_ID_3"
44+
// vs "acre_prc343_id_3"), so match case-insensitively but store the actual carried ID string.
45+
private _idx = _unitRadiosLc find (toLower _x);
46+
if (_idx >= 0) then {_radioIds pushBackUnique (_unitRadios select _idx);};
47+
} forEach _classnames;
48+
49+
private _states = [];
50+
{
51+
private _state = [_x] call FUNC(acreGetRadioState);
52+
if (_state isNotEqualTo []) then {
53+
_states pushBack _state;
54+
};
55+
} forEach _radioIds;
56+
57+
_states
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Filter a cargo loadout array for ACRE2 so radios can be safely restored.
5+
*
6+
* Converts ACRE radio ID item classnames (e.g. "ACRE_PRC152_ID_2") to their base radio classnames
7+
* (e.g. "ACRE_PRC152") while leaving all other cargo unchanged.
8+
*
9+
* Arguments:
10+
* 0: Cargo loadout <ARRAY>
11+
*
12+
* Return Value:
13+
* Filtered cargo loadout <ARRAY>
14+
*
15+
* Example:
16+
* [((getUnitLoadout player) select 5) select 1] call bocr_main_fnc_acreFilterCargoLoadout;
17+
*
18+
* Public: No
19+
*/
20+
21+
params ["_cargoLoadout"];
22+
if !(_cargoLoadout isEqualType []) exitWith {_cargoLoadout};
23+
24+
private _cfgAcreRadios = configFile >> "CfgAcreRadios";
25+
private _hasCfgAcreRadios = isClass _cfgAcreRadios;
26+
27+
private _out = [];
28+
{
29+
private _entry = _x;
30+
if !(_entry isEqualType [] && {count _entry > 0}) then {
31+
_out pushBack _entry;
32+
} else {
33+
private _first = _entry select 0;
34+
if (_first isEqualType "") then {
35+
private _cls = _first;
36+
private _clsUc = toUpper _cls;
37+
38+
private _pos = _clsUc find "_ID_";
39+
if (_pos > 0) then {
40+
private _base = _cls select [0, _pos];
41+
if (!_hasCfgAcreRadios || {isClass (_cfgAcreRadios >> _base)}) then {
42+
private _new = +_entry;
43+
_new set [0, _base];
44+
// ACRE radios are always 1 per entry; ensure numeric count if present.
45+
if (count _new > 1 && {(_new select 1) isEqualType 0} && {(_new select 1) < 1}) then {
46+
_new set [1, 1];
47+
};
48+
_entry = _new;
49+
};
50+
};
51+
};
52+
53+
_out pushBack _entry;
54+
};
55+
} forEach _cargoLoadout;
56+
57+
_out
58+
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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "script_component.hpp"
2+
/*
3+
* Author: BackpackOnChestRedux contributors
4+
* Returns carried ACRE2 radio IDs for a given unit.
5+
*
6+
* Notes:
7+
* - acre_api_fnc_getAllRadios returns base radio classnames from CfgAcreRadios, not carried IDs.
8+
* - This helper enumerates all base radios and queries IDs by type for the given unit.
9+
*
10+
* Arguments:
11+
* 0: Unit <OBJECT>
12+
*
13+
* Return Value:
14+
* Radio IDs <ARRAY>
15+
*
16+
* Example:
17+
* [player] call bocr_main_fnc_acreGetUnitRadioIds;
18+
*
19+
* Public: No
20+
*/
21+
22+
params ["_unit"];
23+
24+
if (!([_unit] call FUNC(acreIsInitialized))) exitWith {[]};
25+
26+
// Fast-path for the local player when available.
27+
if (hasInterface && {!isNull player} && {_unit isEqualTo player} && {!isNil "acre_api_fnc_getCurrentRadioList"}) exitWith {
28+
[] call acre_api_fnc_getCurrentRadioList
29+
};
30+
31+
if (isNil "acre_api_fnc_getAllRadiosByType") exitWith {[]};
32+
33+
private _bases = [] call acre_api_fnc_getAllRadios;
34+
if !(_bases isEqualType []) exitWith {[]};
35+
36+
private _ids = [];
37+
{
38+
if (_x isEqualType "") then {
39+
_ids append ([_x, _unit] call acre_api_fnc_getAllRadiosByType);
40+
};
41+
} forEach _bases;
42+
43+
_ids
44+
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

0 commit comments

Comments
 (0)