Medical - Rework CPR and Bleeding in cardiac arrest (#7060)

* Medical - Rework CPR and Bleeding in cardiac arrest

* remove cprCreatesPulse, add cprSuccessChance

* hide cpr for basic diagnose

* Update addons/medical_statemachine/functions/fnc_enteredStateCardiacArrest.sqf

Co-Authored-By: SilentSpike <silentspike100+Github@gmail.com>
This commit is contained in:
PabstMirror 2019-06-27 19:01:20 -05:00 committed by GitHub
parent 56cb638aff
commit de13ab0f47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 116 additions and 106 deletions

View File

@ -19,12 +19,16 @@
_return pushBack ""; _return pushBack "";
// State: // State:
private _hasStableVitals = [_unit] call EFUNC(medical_status,hasStableVitals);
private _targetState = [_unit, EGVAR(medical,STATE_MACHINE)] call CBA_statemachine_fnc_getCurrentState; private _targetState = [_unit, EGVAR(medical,STATE_MACHINE)] call CBA_statemachine_fnc_getCurrentState;
if (!local _unit) then {_targetState = "NotLocal";}; if (!local _unit) then {_targetState = "NotLocal";};
private _color = switch (_targetState) do {case "Default": {"33FF33"}; case "Injured": {"FF3333"}; case "Unconscious": {"FF8833"}; case "CardiacArrest": {"FF33AA"}; default {"555555"}}; private _color = switch (_targetState) do {case "Default": {"33FF33"}; case "Injured": {"FF3333"}; case "Unconscious": {"FF8833"}; case "CardiacArrest": {"FF33AA"}; default {"555555"}};
private _unconcFlag = if IS_UNCONSCIOUS(_unit) then {"[<t color='#FFFFFF'>U</t>]"} else {""}; _return pushBack format ["<t color='#%1'>State: %2</t>", _color, _targetState];
_return pushBack format ["<t color='#%1'>State: %2</t> [StableVitals: %3] %4", _color, _targetState, _hasStableVitals, _unconcFlag]; private _hasStableVitals = ["N", "Y"] select ([_unit] call EFUNC(medical_status,hasStableVitals));
private _hasStableCondition = ["N", "Y"] select ([_unit] call EFUNC(medical_status,isInStableCondition));
private _unconcFlag = if IS_UNCONSCIOUS(_unit) then {"[<t color='#BBFFBB'>U</t>]"} else {""};
private _timeLeft = _unit getVariable [QEGVAR(medical_statemachine,cardiacArrestTimeLeft), -1];
private _cardiactArrestFlag = if IN_CRDC_ARRST(_unit) then {format ["[<t color='#BBBBFF'>CA</t> %1]", _timeLeft toFixed 1]} else {""};
_return pushBack format ["[StableVitals: %1] [StableCon: %2] %3 %4", _hasStableVitals, _hasStableCondition, _unconcFlag, _cardiactArrestFlag];
// Blood: // Blood:
private _bloodVolume = GET_BLOOD_VOLUME(_unit); private _bloodVolume = GET_BLOOD_VOLUME(_unit);

View File

@ -18,5 +18,6 @@
if (EGVAR(interact_menu,menuBackground) == 1) then {[QGVAR(id), false] call EFUNC(common,blurScreen)}; if (EGVAR(interact_menu,menuBackground) == 1) then {[QGVAR(id), false] call EFUNC(common,blurScreen)};
if (EGVAR(interact_menu,menuBackground) == 2) then {(uiNamespace getVariable [QEGVAR(interact_menu,menuBackground), displayNull]) closeDisplay 0}; if (EGVAR(interact_menu,menuBackground) == 2) then {(uiNamespace getVariable [QEGVAR(interact_menu,menuBackground), displayNull]) closeDisplay 0};
GVAR(pendingReopen) = false;
GVAR(menuPFH) call CBA_fnc_removePerFrameHandler; GVAR(menuPFH) call CBA_fnc_removePerFrameHandler;
GVAR(menuPFH) = -1; GVAR(menuPFH) = -1;

View File

@ -86,6 +86,7 @@ class ACE_Medical_StateMachine {
}; };
}; };
class CardiacArrest { class CardiacArrest {
onState = QFUNC(handleStateCardiacArrest);
onStateEntered = QFUNC(enteredStateCardiacArrest); onStateEntered = QFUNC(enteredStateCardiacArrest);
onStateLeaving = QFUNC(leftStateCardiacArrest); onStateLeaving = QFUNC(leftStateCardiacArrest);
class DeathAI { class DeathAI {

View File

@ -3,6 +3,7 @@ PREP(conditionExecutionDeath);
PREP(enteredStateCardiacArrest); PREP(enteredStateCardiacArrest);
PREP(enteredStateDeath); PREP(enteredStateDeath);
PREP(enteredStateFatalInjury); PREP(enteredStateFatalInjury);
PREP(handleStateCardiacArrest);
PREP(handleStateDefault); PREP(handleStateDefault);
PREP(handleStateInjured); PREP(handleStateInjured);
PREP(handleStateUnconscious); PREP(handleStateUnconscious);

View File

@ -17,7 +17,4 @@
params ["_unit"]; params ["_unit"];
private _startTime = _unit getVariable [QGVAR(cardiacArrestStart), CBA_missionTime]; (_unit getVariable [QGVAR(cardiacArrestTimeLeft), -1]) <= 0
private _lifeTime = _unit getVariable [QGVAR(cardiacArrestTime), GVAR(cardiacArrestTime)];
(CBA_missionTime - _startTime) > _lifeTime

View File

@ -20,10 +20,12 @@ params ["_unit"];
// 10% possible variance in cardiac arrest time // 10% possible variance in cardiac arrest time
private _time = GVAR(cardiacArrestTime); private _time = GVAR(cardiacArrestTime);
_time = _time + random [_time*-0.1, 0, _time*0.1]; _time = _time + _time * random [-0.1, 0, 0.1];
_unit setVariable [QGVAR(cardiacArrestTime), _time]; _unit setVariable [QGVAR(cardiacArrestTimeLeft), _time];
_unit setVariable [QGVAR(cardiacArrestStart), CBA_missionTime]; _unit setVariable [QGVAR(cardiacArrestTimeLastUpdate), CBA_missionTime];
TRACE_3("enteredStateCardiacArrest",_unit,_time,CBA_missionTime);
// Update the unit status to reflect cardiac arrest // Update the unit status to reflect cardiac arrest
[_unit, true] call EFUNC(medical_status,setCardiacArrest); [_unit, true] call EFUNC(medical_status,setCardiacArrest);

View File

@ -0,0 +1,37 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles the unconscious state
*
* Arguments:
* 0: The Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* [player] call ace_medical_statemachine_fnc_handleStateCardiacArrest
*
* Public: No
*/
params ["_unit"];
// If the unit died the loop is finished
if (!alive _unit) exitWith {};
if (!local _unit) exitWith {};
[_unit] call EFUNC(medical_vitals,handleUnitVitals);
private _timeDiff = CBA_missionTime - (_unit getVariable [QGVAR(cardiacArrestTimeLastUpdate), 0]);
if (_timeDiff >= 1) then {
_timeDiff = _timeDiff min 10;
_unit setVariable [QGVAR(cardiacArrestTimeLastUpdate), CBA_missionTime];
private _recieveingCPR = alive (_unit getVariable [QEGVAR(medical,CPR_provider), objNull]);
private _timeLeft = _unit getVariable [QGVAR(cardiacArrestTimeLeft), -1];
TRACE_3("cardiac arrest life tick",_unit,_recieveingCPR,_timeDiff);
if (_recieveingCPR) then { _timeDiff = _timeDiff * 0.5; }; // if being cpr'ed, then time decrease is reduced
_timeLeft = _timeLeft - _timeDiff; // negative values are fine
_unit setVariable [QGVAR(cardiacArrestTimeLeft), _timeLeft];
};

View File

@ -17,11 +17,9 @@
*/ */
params ["_unit"]; params ["_unit"];
TRACE_1("leftStateCardiacArrest",_unit);
_unit setVariable [QGVAR(cardiacArrestTime), nil]; _unit setVariable [QGVAR(cardiacArrestTimeLeft), nil];
_unit setVariable [QEGVAR(medical,cardiacArrestStart), nil]; _unit setVariable [QGVAR(cardiacArrestTimeLastUpdate), nil];
// Temporary fix for vitals loop on cardiac arrest exit
_unit setVariable [QGVAR(lastTimeUpdated), CBA_missionTime];
[_unit, false] call EFUNC(medical_status,setCardiacArrest); [_unit, false] call EFUNC(medical_status,setCardiacArrest);

View File

@ -22,4 +22,5 @@ if (_woundBleeding == 0) exitWith {0};
private _cardiacOutput = [_unit] call FUNC(getCardiacOutput); private _cardiacOutput = [_unit] call FUNC(getCardiacOutput);
(_woundBleeding * _cardiacOutput * EGVAR(medical,bleedingCoefficient)) // even if heart stops blood will still flow slowly (gravity)
(_woundBleeding * (_cardiacOutput max 0.05) * EGVAR(medical,bleedingCoefficient))

View File

@ -7,7 +7,7 @@
* 0: The Unit <OBJECT> * 0: The Unit <OBJECT>
* *
* Return Value: * Return Value:
* Current cardiac output (litre per second) <NUMBER> * Current cardiac output (liter per second) <NUMBER>
* *
* Example: * Example:
* [player] call ace_medical_status_fnc_getCardiacOutput * [player] call ace_medical_status_fnc_getCardiacOutput
@ -16,7 +16,7 @@
*/ */
/* /*
Cardiac output (Q or or CO ) is the volume of blood being pumped by the heart, in particular by a left or right ventricle in the CBA_missionTime interval of one second. CO may be measured in many ways, for example dm3/min (1 dm3 equals 1 litre). Cardiac output (Q or or CO ) is the volume of blood being pumped by the heart, in particular by a left or right ventricle in the CBA_missionTime interval of one second. CO may be measured in many ways, for example dm3/min (1 dm3 equals 1 liter).
Source: http://en.wikipedia.org/wiki/Cardiac_output Source: http://en.wikipedia.org/wiki/Cardiac_output
*/ */

View File

@ -14,6 +14,7 @@
*/ */
params ["_unit", "_active"]; params ["_unit", "_active"];
TRACE_2("setCardiacArrest",_unit,_active);
// No change to make // No change to make
if (_active isEqualTo IN_CRDC_ARRST(_unit)) exitWith {}; if (_active isEqualTo IN_CRDC_ARRST(_unit)) exitWith {};

View File

@ -269,7 +269,7 @@ class GVAR(actions) {
treatmentTime = 15; treatmentTime = 15;
items[] = {}; items[] = {};
condition = QFUNC(canCPR); condition = QFUNC(canCPR);
callbackSuccess = QFUNC(cpr); callbackSuccess = QFUNC(cprSuccess);
callbackFailure = QFUNC(cprFailure); callbackFailure = QFUNC(cprFailure);
callbackProgress = QFUNC(cprProgress); callbackProgress = QFUNC(cprProgress);
callbackStart = QFUNC(cprStart); callbackStart = QFUNC(cprStart);

View File

@ -2,9 +2,6 @@ class ACE_Settings {
class EGVAR(medical,allowLitterCreation) { class EGVAR(medical,allowLitterCreation) {
movedToSqf = 1; movedToSqf = 1;
}; };
class EGVAR(medical,CPRcreatesPulse) {
movedToSqf = 1;
};
class EGVAR(medical,litterCleanUpDelay) { class EGVAR(medical,litterCleanUpDelay) {
movedToSqf = 1; movedToSqf = 1;
}; };

View File

@ -4,7 +4,6 @@ PREP(addToTriageCard);
PREP(bandage); PREP(bandage);
PREP(bandageLocal); PREP(bandageLocal);
PREP(bodyCleanupLoop); PREP(bodyCleanupLoop);
PREP(calculateBlood);
PREP(canBandage); PREP(canBandage);
PREP(canCPR); PREP(canCPR);
PREP(canSplint); PREP(canSplint);
@ -17,7 +16,7 @@ PREP(checkItems);
PREP(checkPulse); PREP(checkPulse);
PREP(checkPulseLocal); PREP(checkPulseLocal);
PREP(checkResponse); PREP(checkResponse);
PREP(cpr); PREP(cprSuccess);
PREP(cprFailure); PREP(cprFailure);
PREP(cprLocal); PREP(cprLocal);
PREP(cprProgress); PREP(cprProgress);

View File

@ -1,29 +0,0 @@
#include "script_component.hpp"
/*
* Author: Zakant
* Calculate the blood lost and blood volume for a unit. Used from CPR to simulate a heart rate while in cardiac arrest.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* None
*
* Public: No
*/
params["_unit"];
// We will just simulate blood flow for now!
private _lastTimeUpdated = _unit getVariable [QEGVAR(medical,lastTimeUpdated), CBA_missionTime];
private _deltaT = CBA_missionTime - _lastTimeUpdated;
private _lastTimeValuesSynced = _unit getVariable [QEGVAR(medical,lastMomentValuesSynced), 0];
private _syncValues = (CBA_missionTime - _lastTimeValuesSynced) >= (10 + floor(random(10)));
_unit setVariable [QEGVAR(medical,lastTimeUpdated), CBA_missionTime];
if (_deltaT != 0) then {
private _change = ([_unit, _deltaT, _syncValues] call EFUNC(medical_status,getBloodVolumeChange));
private _bloodVolume = 0 max (GET_BLOOD_VOLUME(_unit) + _change) min DEFAULT_BLOOD_VOLUME;
_unit setVariable [VAR_BLOOD_VOL, _bloodVolume, _syncValues];
};

View File

@ -18,4 +18,6 @@
params ["", "_patient"]; params ["", "_patient"];
!(_patient call EFUNC(common,isAwake)) && {!(_patient getVariable [QGVAR(isReceivingCPR), false])} !(_patient call EFUNC(common,isAwake))
&& {(GVAR(advancedDiagnose)) || {IN_CRDC_ARRST(_patient)}} // if basic diagnose, then only show action if appropriate (they can't tell difference between uncon/ca)
&& {isNull (_patient getVariable [QEGVAR(medical,CPR_provider), objNull])}

View File

@ -16,13 +16,7 @@
* Public: No * Public: No
*/ */
params ["", "_patient"]; params ["_medic", "_patient"];
TRACE_2("cprFailure",_medic,_patient);
if (!(_patient call EFUNC(common,isAwake)) || {IN_CRDC_ARRST(_patient)}) then { _patient setVariable [QEGVAR(medical,CPR_provider), objNull, true];
_patient setVariable [VAR_HEART_RATE, 0, true];
};
// Patient is no longer receiving CPR
_patient setVariable [QGVAR(isReceivingCPR), false, true];
_patient call FUNC(calculateBlood);

View File

@ -17,9 +17,14 @@
*/ */
params ["_medic", "_patient"]; params ["_medic", "_patient"];
TRACE_2("cprLocal",_medic,_patient);
[_patient, "activity", LSTRING(Activity_CPR), [[_medic, false, true] call EFUNC(common,getName)]] call FUNC(addToLog); [_patient, "activity", LSTRING(Activity_CPR), [[_medic, false, true] call EFUNC(common,getName)]] call FUNC(addToLog);
if (random 1 >= 0.6) then { if ((random 1) < GVAR(cprSuccessChance)) then {
TRACE_1("CPR random success",GVAR(cprSuccessChance));
[QEGVAR(medical,CPRSucceeded), _patient] call CBA_fnc_localEvent; [QEGVAR(medical,CPRSucceeded), _patient] call CBA_fnc_localEvent;
} else {
TRACE_1("CPR random fail",GVAR(cprSuccessChance));
}; };

View File

@ -5,7 +5,7 @@
* *
* Arguments: * Arguments:
* 0: Arguments <ARRAY> * 0: Arguments <ARRAY>
* 0: Medic (not used) <OBJECT> * 0: Medic <OBJECT>
* 1: Patient <OBJECT> * 1: Patient <OBJECT>
* *
* Return Value: * Return Value:
@ -18,12 +18,10 @@
*/ */
params ["_args"]; params ["_args"];
_args params ["", "_patient"]; _args params ["_medic", "_patient"];
// Cancel CPR is patient wakes up // Cancel CPR if patient wakes up
if (_patient getVariable EFUNC(common,isAwake) || {!IN_CRDC_ARRST(_patient)}) exitWith {false};
// Calculate blood volume, if there is no pulse nothing happens !(_patient call EFUNC(common,isAwake))
_patient call FUNC(calculateBlood); && {(GVAR(advancedDiagnose)) || {IN_CRDC_ARRST(_patient)}} // if basic diagnose, then only show action if appropriate (they can't tell difference between uncon/ca)
&& {_medic == (_patient getVariable [QEGVAR(medical,CPR_provider), objNull])}
true

View File

@ -4,7 +4,7 @@
* Handles starting the CPR treatment. * Handles starting the CPR treatment.
* *
* Arguments: * Arguments:
* 0: Medic (not used) <OBJECT> * 0: Medic <OBJECT>
* 1: Patient <OBJECT> * 1: Patient <OBJECT>
* *
* Return Value: * Return Value:
@ -16,14 +16,7 @@
* Public: No * Public: No
*/ */
params ["", "_patient"]; params ["_medic", "_patient"];
TRACE_2("cprStart",_medic,_patient);
// Prevent others from performing CPR _patient setVariable [QEGVAR(medical,CPR_provider), _medic, true];
_patient setVariable [QGVAR(isReceivingCPR), true, true];
// Create a random pulse based on setting
if (GVAR(cprCreatesPulse) && {GET_HEART_RATE(_patient) == 0}) then {
_patient setVariable [VAR_HEART_RATE, round random [25, 30, 35], true];
};
_patient setVariable [QEGVAR(medical,lastTimeUpdated), CBA_missionTime, true];

View File

@ -11,17 +11,19 @@
* None * None
* *
* Example: * Example:
* [player, cursorObject] call ace_medical_treatment_fnc_cpr * [player, cursorObject] call ace_medical_treatment_fnc_cprSuccess
* *
* Public: No * Public: No
*/ */
params ["_medic", "_patient"]; params ["_medic", "_patient"];
TRACE_2("cprSuccess",_medic,_patient);
_patient setVariable [QGVAR(isReceivingCPR), false, true]; _patient setVariable [QEGVAR(medical,CPR_provider), objNull, true];
_patient setVariable [VAR_HEART_RATE, 0, true];
_patient call FUNC(calculateBlood);
if (alive _patient && {IN_CRDC_ARRST(_patient)}) then { if (alive _patient && {IN_CRDC_ARRST(_patient)}) then {
TRACE_1("sending cprLocal event",_patient);
[QGVAR(cprLocal), [_medic, _patient], _patient] call CBA_fnc_targetEvent; [QGVAR(cprLocal), [_medic, _patient], _patient] call CBA_fnc_targetEvent;
} else {
TRACE_1("not alive or in cardiac arrest",_patient);
}; };

View File

@ -38,15 +38,6 @@
true true
] call CBA_settings_fnc_init; ] call CBA_settings_fnc_init;
[
QGVAR(cprCreatesPulse),
"CHECKBOX",
[LSTRING(CPRCreatesPulse_DisplayName), LSTRING(CPRCreatesPulse_Description)],
[ELSTRING(medical,Category), LSTRING(SubCategory_Treatment)],
true,
true
] call CBA_settings_fnc_init;
// todo: should this setting differentiate between medical vehicles and facilities? // todo: should this setting differentiate between medical vehicles and facilities?
[ [
QGVAR(locationsBoostTraining), QGVAR(locationsBoostTraining),
@ -165,6 +156,15 @@
true true
] call CBA_settings_fnc_init; ] call CBA_settings_fnc_init;
[
QGVAR(cprSuccessChance),
"SLIDER",
[LSTRING(cprSuccessChance_DisplayName), LSTRING(cprSuccessChance_Description)],
[ELSTRING(medical,Category), LSTRING(SubCategory_Treatment)],
[0, 1, 0.4, 2],
true
] call CBA_settings_fnc_init;
[ [
QGVAR(allowLitterCreation), QGVAR(allowLitterCreation),
"CHECKBOX", "CHECKBOX",

View File

@ -44,15 +44,6 @@
<English>Enables extended, more in-depth medication handling. Also, enables the use of Adenosine and Atropine.</English> <English>Enables extended, more in-depth medication handling. Also, enables the use of Adenosine and Atropine.</English>
<Russian>Устанавливает расширенное использование лекарств</Russian> <Russian>Устанавливает расширенное использование лекарств</Russian>
</Key> </Key>
<Key ID="STR_ACE_Medical_Treatment_CPRCreatesPulse_DisplayName">
<English>CPR Creates Pulse</English>
<German>HLW erzeugt einen Puls</German>
<Japanese>心肺蘇生による脈</Japanese>
<Russian>СЛР создаст пульс</Russian>
</Key>
<Key ID="STR_ACE_Medical_Treatment_CPRCreatesPulse_Description">
<English>Controls whether performing CPR creates a pulse for the patient.</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_LocationsBoostTraining_DisplayName"> <Key ID="STR_ACE_Medical_Treatment_LocationsBoostTraining_DisplayName">
<English>Locations Boost Training</English> <English>Locations Boost Training</English>
<Czech>Místa pro vylepšení zkušeností</Czech> <Czech>Místa pro vylepšení zkušeností</Czech>
@ -1939,6 +1930,12 @@
<Chinesesimp>进行心肺复苏术中...</Chinesesimp> <Chinesesimp>进行心肺复苏术中...</Chinesesimp>
<Chinese>進行心肺復甦術中...</Chinese> <Chinese>進行心肺復甦術中...</Chinese>
</Key> </Key>
<Key ID="STR_ACE_Medical_Treatment_cprSuccessChance_DisplayName">
<English>CPR Success Chance</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_cprSuccessChance_Description">
<English>Probability that cpr will be successful in restoring heart rhythm.</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_Actions_Blood4_1000"> <Key ID="STR_ACE_Medical_Treatment_Actions_Blood4_1000">
<English>Give Blood IV (1000ml)</English> <English>Give Blood IV (1000ml)</English>
<German>Bluttransfusion IV (1000ml)</German> <German>Bluttransfusion IV (1000ml)</German>

View File

@ -113,6 +113,7 @@ switch (true) do {
TRACE_3("BloodVolume Fatal",_unit,BLOOD_VOLUME_FATAL,_bloodVolume); TRACE_3("BloodVolume Fatal",_unit,BLOOD_VOLUME_FATAL,_bloodVolume);
[QEGVAR(medical,Bleedout), _unit] call CBA_fnc_localEvent; [QEGVAR(medical,Bleedout), _unit] call CBA_fnc_localEvent;
}; };
case (IN_CRDC_ARRST(_unit)): {}; // if in cardiac arrest just break now to avoid throwing unneeded events
case (_hemorrhage == 4): { case (_hemorrhage == 4): {
TRACE_3("Class IV Hemorrhage",_unit,_hemorrhage,_bloodVolume); TRACE_3("Class IV Hemorrhage",_unit,_hemorrhage,_bloodVolume);
[QEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent; [QEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent;

View File

@ -22,7 +22,15 @@ params ["_unit", "_hrTargetAdjustment", "_deltaT", "_syncValue"];
private _heartRate = GET_HEART_RATE(_unit); private _heartRate = GET_HEART_RATE(_unit);
if !IN_CRDC_ARRST(_unit) then { if IN_CRDC_ARRST(_unit) then {
if (alive (_unit getVariable [QEGVAR(medical,CPR_provider), objNull])) then {
if (_heartRate == 0) then { _syncValue = true }; // always sync on large change
_heartRate = random [25, 30, 35];
} else {
if (_heartRate != 0) then { _syncValue = true }; // always sync on large change
_heartRate = 0
};
} else {
private _hrChange = 0; private _hrChange = 0;
private _targetHR = 0; private _targetHR = 0;
private _bloodVolume = GET_BLOOD_VOLUME(_unit); private _bloodVolume = GET_BLOOD_VOLUME(_unit);