Medical Statemachine - Improve edge case death handling (#8788)

* fix #8772

* add WARNING for dead/null units

* fix macro

* improve warnings

* handle scripted death without warnings

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

Co-authored-by: PabstMirror <pabstmirror@gmail.com>
This commit is contained in:
GhostIsSpooky 2022-03-07 15:28:51 -03:00 committed by GitHub
parent 90ebe08012
commit 9209fedcf7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 50 additions and 13 deletions

View File

@ -43,7 +43,7 @@ class ACE_Medical_StateMachine {
}; };
class Unconscious { class Unconscious {
onState = QFUNC(handleStateUnconscious); onState = QFUNC(handleStateUnconscious);
onStateEntered = QUOTE([ARR_2(_this,true)] call EFUNC(medical_status,setUnconsciousState)); onStateEntered = QFUNC(enteredStateUnconscious);
class DeathAI { class DeathAI {
targetState = "Dead"; targetState = "Dead";
condition = QUOTE(!GVAR(AIUnconsciousness) && {!isPlayer _this}); condition = QUOTE(!GVAR(AIUnconsciousness) && {!isPlayer _this});

View File

@ -4,6 +4,7 @@ PREP(conditionSecondChance);
PREP(enteredStateCardiacArrest); PREP(enteredStateCardiacArrest);
PREP(enteredStateDeath); PREP(enteredStateDeath);
PREP(enteredStateFatalInjury); PREP(enteredStateFatalInjury);
PREP(enteredStateUnconscious);
PREP(handleStateCardiacArrest); PREP(handleStateCardiacArrest);
PREP(handleStateDefault); PREP(handleStateDefault);
PREP(handleStateInjured); PREP(handleStateInjured);

View File

@ -17,6 +17,9 @@
*/ */
params ["_unit"]; params ["_unit"];
if (isNull _unit || {!isNil {_unit getVariable QEGVAR(medical,causeOfDeath)}}) exitWith {
WARNING_1("enteredStateCardiacArrest: State transition on dead or null unit - %1",_unit);
};
// 10% possible variance in cardiac arrest time // 10% possible variance in cardiac arrest time
private _time = GVAR(cardiacArrestTime); private _time = GVAR(cardiacArrestTime);

View File

@ -16,13 +16,15 @@
*/ */
params ["_unit"]; params ["_unit"];
if (isNull _unit) exitWith {}; if (isNull _unit || {!isNil {_unit getVariable QEGVAR(medical,causeOfDeath)}}) exitWith {
if ((_unit getVariable [QEGVAR(medical,causeOfDeath), ""]) == "#scripted") exitWith {};
WARNING_1("enteredStateDeath: State transition on dead or null unit - %1",_unit);
};
//IGNORE_PRIVATE_WARNING ["_thisOrigin", "_thisTransition"]; // vars provided by CBA_statemachine //IGNORE_PRIVATE_WARNING ["_thisOrigin", "_thisTransition"]; // vars provided by CBA_statemachine
TRACE_3("enteredStateDeath",_this,_thisOrigin,_thisTransition); TRACE_4("enteredStateDeath",_this,_thisOrigin,_thisTransition,CBA_missionTime);
private _causeOfDeath = format ["%1:%2", _thisOrigin, _thisTransition]; private _causeOfDeath = format ["%1:%2", _thisOrigin, _thisTransition];
private _instigator = _unit getVariable [QEGVAR(medical,lastInstigator), objNull]; private _instigator = _unit getVariable [QEGVAR(medical,lastInstigator), objNull];
// could delay a frame here to fix the double killed EH, but we lose it being a "native" kill (scoreboard / rating)
[_unit, _causeOfDeath, _instigator] call EFUNC(medical_status,setDead); [_unit, _causeOfDeath, _instigator] call EFUNC(medical_status,setDead);

View File

@ -16,5 +16,8 @@
*/ */
params ["_unit"]; params ["_unit"];
if (isNull _unit || {!isNil {_unit getVariable QEGVAR(medical,causeOfDeath)}}) exitWith {
WARNING_1("enteredStateFatalInjury: State transition on dead or null unit - %1",_unit);
};
[QEGVAR(medical,FatalInjuryInstantTransition), _unit] call CBA_fnc_localEvent; [QEGVAR(medical,FatalInjuryInstantTransition), _unit] call CBA_fnc_localEvent;

View File

@ -0,0 +1,26 @@
#include "script_component.hpp"
/*
* Author: GhostIsSpooky
* Handles a unit reaching the point of unconsciousness (calls for a status update).
*
* Arguments:
* 0: The Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* [player] call ace_medical_statemachine_fnc_enteredStateUnconscious
*
* Public: No
*/
params ["_unit"];
if (isNull _unit || {!isNil {_unit getVariable QEGVAR(medical,causeOfDeath)}}) exitWith {
WARNING_1("enteredStateUnconscious: State transition on dead or null unit - %1",_unit);
};
//IGNORE_PRIVATE_WARNING ["_thisOrigin", "_thisTransition"]; // vars provided by CBA_statemachine
TRACE_4("enteredStateUnconscious",_this,_thisOrigin,_thisTransition,CBA_missionTime);
[_unit, true] call EFUNC(medical_status,setUnconsciousState);

View File

@ -18,8 +18,7 @@
params ["_unit"]; params ["_unit"];
// If the unit died the loop is finished // If the unit died the loop is finished
if (!alive _unit) exitWith {}; if (!alive _unit || {!local _unit}) exitWith {};
if (!local _unit) exitWith {};
[_unit] call EFUNC(medical_vitals,handleUnitVitals); [_unit] call EFUNC(medical_vitals,handleUnitVitals);
@ -34,4 +33,3 @@ if (_timeDiff >= 1) then {
_timeLeft = _timeLeft - _timeDiff; // negative values are fine _timeLeft = _timeLeft - _timeDiff; // negative values are fine
_unit setVariable [QGVAR(cardiacArrestTimeLeft), _timeLeft]; _unit setVariable [QGVAR(cardiacArrestTimeLeft), _timeLeft];
}; };

View File

@ -18,8 +18,7 @@
params ["_unit"]; params ["_unit"];
// If the unit died the loop is finished // If the unit died the loop is finished
if (!alive _unit) exitWith {}; if (!alive _unit || {!local _unit}) exitWith {};
if (!local _unit) exitWith {};
if ([_unit] call EFUNC(medical_vitals,handleUnitVitals)) then { // returns true when update ran if ([_unit] call EFUNC(medical_vitals,handleUnitVitals)) then { // returns true when update ran
private _painLevel = GET_PAIN_PERCEIVED(_unit); private _painLevel = GET_PAIN_PERCEIVED(_unit);

View File

@ -18,8 +18,7 @@
params ["_unit"]; params ["_unit"];
// If the unit died the loop is finished // If the unit died the loop is finished
if (!alive _unit) exitWith {}; if (!alive _unit || {!local _unit}) exitWith {};
if (!local _unit) exitWith {};
if ([_unit] call EFUNC(medical_vitals,handleUnitVitals)) then { // returns true when update ran if ([_unit] call EFUNC(medical_vitals,handleUnitVitals)) then { // returns true when update ran
private _painLevel = GET_PAIN_PERCEIVED(_unit); private _painLevel = GET_PAIN_PERCEIVED(_unit);

View File

@ -26,6 +26,12 @@ _unit setVariable [QEGVAR(medical,causeOfDeath), _reason, true];
// Send a local event before death // Send a local event before death
[QEGVAR(medical,death), [_unit]] call CBA_fnc_localEvent; [QEGVAR(medical,death), [_unit]] call CBA_fnc_localEvent;
// Update the state machine if necessary (forced respawn, scripted death, etc)
private _unitState = [_unit, EGVAR(medical,STATE_MACHINE)] call CBA_statemachine_fnc_getCurrentState;
if (_unitState isNotEqualTo "Dead") then {
[_unit, EGVAR(medical,STATE_MACHINE), _unitState, "Dead"] call CBA_statemachine_fnc_manualTransition;
};
// (#8803) Reenable damage if disabled to prevent having live units in dead state // (#8803) Reenable damage if disabled to prevent having live units in dead state
// Keep this after death event for compatibility with third party hooks // Keep this after death event for compatibility with third party hooks
if !(isDamageAllowed _unit) then { if !(isDamageAllowed _unit) then {

View File

@ -9,7 +9,7 @@
* 1: Set unconscious <BOOL> * 1: Set unconscious <BOOL>
* *
* Return Value: * Return Value:
* Success <BOOL> * None
* *
* Example: * Example:
* [player, true] call ace_medical_status_fnc_setUnconsciousState * [player, true] call ace_medical_status_fnc_setUnconsciousState
@ -21,7 +21,7 @@ params ["_unit", "_active"];
TRACE_2("setUnconsciousState",_unit,_active); TRACE_2("setUnconsciousState",_unit,_active);
// No change to make // No change to make
if (_active isEqualTo IS_UNCONSCIOUS(_unit)) exitWith { TRACE_2("no change",_active,IS_UNCONSCIOUS(_unit)); }; if (_active isEqualTo IS_UNCONSCIOUS(_unit) || {!alive _unit}) exitWith { TRACE_2("no change",_active,IS_UNCONSCIOUS(_unit)); };
_unit setVariable [VAR_UNCON, _active, true]; _unit setVariable [VAR_UNCON, _active, true];