Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions addons/recorder/fnc_eh_fired_client.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ if (isNil "_projectile") exitWith {
false;
};

// Dedup guard — when both FiredMan (unit) and Fired (vehicle) EHs fire for the
// same shot, only the first one to run should process. The second sees
// projectileData already set and exits.
if (!isNil {_projectile getVariable QGVARMAIN(projectileData)}) exitWith {false};

// Zeus remote control fix: FiredMan fires on the controller's body, not the
// controlled unit. bis_fnc_moduleRemoteControl_unit (local to the controller's
// machine) gives us the actual unit doing the firing.
Expand Down
77 changes: 77 additions & 0 deletions addons/recorder/fnc_eh_fired_server.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,83 @@ GVAR(trackedPlacedObjects) = createHashMap;
// allow inheritance, don't exclude anything, and apply retroactively
}, true, [], true] call CBA_fnc_addClassEventHandler;

// Fallback for static weapons whose shots bypass FiredMan on the gunner unit.
// ACE Crew Served Weapons (CSW) fires the vehicle weapon via forceWeaponFire,
// which may not trigger the unit's FiredMan EH. Additionally, for mortars with
// allowFireOnLoad, ACE creates a temporary AI agent as gunner when the seat is
// empty (solo mortar use) — this agent has no OCAP ID.
// The dedup guard in eh_fired_client prevents double-tracking when both fire.
//
// Like FiredMan above, the Fired EH must live on the machine that owns the
// vehicle. We use the same Init + Local + remoteExec pattern: add on init,
// transfer on locality change. Only eh_fired_client is called (not eh_firedMan)
// because eh_firedMan is server-only and the EH may run on a client.
// eh_fired_client already handles weapon attribution (lastFired) via broadcast,
// and ACE CSW handles setShotParents for its own projectiles.
["StaticWeapon", "init", {
params ["_entity"];

if (local _entity) then {
private _id = _entity addEventHandler ["Fired", {
params ["_vehicle", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile"];
if (!isNil {_projectile getVariable QGVARMAIN(projectileData)}) exitWith {};
private _firer = gunner _vehicle;
if (isNull _firer) exitWith {};
if ((_firer getVariable [QGVARMAIN(id), -1]) isEqualTo -1) then {
private _reloader = _vehicle getVariable ["ace_csw_reloader", objNull];
if (!isNull _reloader) then { _firer = _reloader; };
};
[_firer, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile, _vehicle] call FUNC(eh_fired_client);
}];
_entity setVariable [QGVARMAIN(staticFiredEHExists), true];
_entity setVariable [QGVARMAIN(staticFiredEH), _id];
} else {
[_entity, {
private _id = _this addEventHandler ["Fired", {
params ["_vehicle", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile"];
if (!isNil {_projectile getVariable QGVARMAIN(projectileData)}) exitWith {};
private _firer = gunner _vehicle;
if (isNull _firer) exitWith {};
if ((_firer getVariable [QGVARMAIN(id), -1]) isEqualTo -1) then {
private _reloader = _vehicle getVariable ["ace_csw_reloader", objNull];
if (!isNull _reloader) then { _firer = _reloader; };
};
[_firer, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile, _vehicle] call FUNC(eh_fired_client);
}];
_this setVariable [QGVARMAIN(staticFiredEHExists), true];
_this setVariable [QGVARMAIN(staticFiredEH), _id];
}] remoteExec ["call", owner _entity];
};

_entity addEventHandler ["Local", {
params ["_entity", "_isLocal"];
private _staticFiredEHExists = _entity getVariable [QGVARMAIN(staticFiredEHExists), false];

if (!_isLocal && _staticFiredEHExists) then {
_entity removeEventHandler ["Fired", _entity getVariable QGVARMAIN(staticFiredEH)];
_entity setVariable [QGVARMAIN(staticFiredEHExists), false];
_entity setVariable [QGVARMAIN(staticFiredEH), nil];
};

if (_isLocal && !_staticFiredEHExists) then {
private _id = _entity addEventHandler ["Fired", {
params ["_vehicle", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile"];
if (!isNil {_projectile getVariable QGVARMAIN(projectileData)}) exitWith {};
private _firer = gunner _vehicle;
if (isNull _firer) exitWith {};
if ((_firer getVariable [QGVARMAIN(id), -1]) isEqualTo -1) then {
private _reloader = _vehicle getVariable ["ace_csw_reloader", objNull];
if (!isNull _reloader) then { _firer = _reloader; };
};
[_firer, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile, _vehicle] call FUNC(eh_fired_client);
}];
_entity setVariable [QGVARMAIN(staticFiredEHExists), true];
_entity setVariable [QGVARMAIN(staticFiredEH), _id];
};
}];

}, true, [], true] call CBA_fnc_addClassEventHandler;


// Finally, we'll add a CBA Event Handler to take in the pre-processed fired data here on the server and send it to the extension.
[QGVARMAIN(handleFiredManData), {
Expand Down
Loading