Skip to content

Commit

Permalink
Medical AI - Give blood in Cardiac Arrest before doing CPR (#10154)
Browse files Browse the repository at this point in the history
Give blood in CA if needed before doing CPR
  • Loading branch information
johnb432 authored Jul 29, 2024
1 parent 7e93715 commit 98da279
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 27 deletions.
93 changes: 76 additions & 17 deletions addons/medical_ai/functions/fnc_healingLogic.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ if (_finishTime > 0) exitWith {
};
if ((_treatmentTarget == _target) && {(_treatmentEvent select [0, 1]) != "#"}) then {
[_treatmentEvent, _treatmentArgs, _target] call CBA_fnc_targetEvent;

// Splints are already logged on their own
switch (_treatmentEvent) do {
case QEGVAR(medical_treatment,bandageLocal): {
[_target, "activity", ELSTRING(medical_treatment,Activity_bandagedPatient), [[_healer, false, true] call EFUNC(common,getName)]] call EFUNC(medical_treatment,addToLog);
};
case QEGVAR(medical_treatment,ivBagLocal): {
[_target, _treatmentArgs select 2] call EFUNC(medical_treatment,addToTriageCard);
[_target, "activity", ELSTRING(medical_treatment,Activity_gaveIV), [[_healer, false, true] call EFUNC(common,getName)]] call EFUNC(medical_treatment,addToLog);
};
case QEGVAR(medical_treatment,medicationLocal): {
private _usedItem = ["ACE_epinephrine", "ACE_morphine"] select (_treatmentArgs select 2 == "Morphine");
[_target, _usedItem] call EFUNC(medical_treatment,addToTriageCard);
[_target, "activity", ELSTRING(medical_treatment,Activity_usedItem), [[_healer, false, true] call EFUNC(common,getName), getText (configFile >> "CfgWeapons" >> _usedItem >> "displayName")]] call EFUNC(medical_treatment,addToLog);
};
};

#ifdef DEBUG_MODE_FULL
INFO_4("%1->%2: %3 - %4",_healer,_target,_treatmentEvent,_treatmentArgs);
systemChat format ["Applying [%1->%2]: %3", _healer, _treatmentTarget, _treatmentEvent];
Expand Down Expand Up @@ -75,9 +92,12 @@ private _treatmentEvent = "#none";
private _treatmentArgs = [];
private _treatmentTime = 6;
private _treatmentItem = "";
switch (true) do {
case ((GET_WOUND_BLEEDING(_target) > 0)
&& {([_healer, "@bandage"] call FUNC(itemCheck)) # 0}): {

if (true) then {
if (
(GET_WOUND_BLEEDING(_target) > 0) &&
{([_healer, "@bandage"] call FUNC(itemCheck)) # 0}
) exitWith {
// Select first bleeding wound and bandage it
private _selection = "?";
{
Expand All @@ -94,47 +114,82 @@ switch (true) do {
_treatmentArgs = [_target, _selection, "FieldDressing"];
_treatmentItem = "@bandage";
};
case (IN_CRDC_ARRST(_target) && {EGVAR(medical_treatment,cprSuccessChanceMin) > 0}): {

private _hasIV = ([_healer, "@iv"] call FUNC(itemCheck)) # 0;
private _bloodVolume = GET_BLOOD_VOLUME(_target);

// If in cardiac arrest, first add some blood to injured if necessary, then do CPR (doing CPR when not enough blood is suboptimal if you have IVs)
// If healer has no IVs, allow AI to do CPR to keep injured alive
if (
IN_CRDC_ARRST(_target) &&
{EGVAR(medical_treatment,cprSuccessChanceMin) > 0} &&
{!_hasIV || {_bloodVolume >= BLOOD_VOLUME_CLASS_3_HEMORRHAGE}}
) exitWith {
_treatmentEvent = QEGVAR(medical_treatment,cprLocal);
_treatmentArgs = [_healer, _target];
_treatmentTime = 15;
};
case (_isMedic && {GET_BLOOD_VOLUME(_target) < MINIMUM_BLOOD_FOR_STABLE_VITALS}
&& {([_healer, "@iv"] call FUNC(itemCheck)) # 0}): {

private _needsIv = _bloodVolume < MINIMUM_BLOOD_FOR_STABLE_VITALS;
private _canGiveIv = _isMedic && _hasIV && _needsIv;

if (_canGiveIv) then {
// Check if patient's blood volume + remaining IV volume is enough to allow the patient to wake up
private _totalIvVolume = 0; //in ml
{
_x params ["_volumeRemaining"];
_totalIvVolume = _totalIvVolume + _volumeRemaining;
} forEach (_target getVariable [QEGVAR(medical,ivBags), []]);

if (GET_BLOOD_VOLUME(_target) + (_totalIvVolume / 1000) > MINIMUM_BLOOD_FOR_STABLE_VITALS) exitWith {
_treatmentEvent = "#waitForBlood";
// Check if the medic has to wait, which allows for a little multitasking
if (_bloodVolume + (_totalIvVolume / 1000) >= MINIMUM_BLOOD_FOR_STABLE_VITALS) then {
_treatmentEvent = "#waitForIV";
_canGiveIv = false;
};
};

if (_canGiveIv) exitWith {
_treatmentEvent = QEGVAR(medical_treatment,ivBagLocal);
_treatmentTime = 5;
_treatmentArgs = [_target, call _fnc_findNoTourniquet, "SalineIV"];
_treatmentItem = "@iv";
};
case (((_fractures select 4) == 1)
&& {([_healer, "splint"] call FUNC(itemCheck)) # 0}): {

if (
((_fractures select 4) == 1) &&
{([_healer, "splint"] call FUNC(itemCheck)) # 0}
) exitWith {
_treatmentEvent = QEGVAR(medical_treatment,splintLocal);
_treatmentTime = 6;
_treatmentArgs = [_healer, _target, "leftleg"];
_treatmentItem = "splint";
};
case (((_fractures select 5) == 1)
&& {([_healer, "splint"] call FUNC(itemCheck)) # 0}): {

if (
((_fractures select 5) == 1) &&
{([_healer, "splint"] call FUNC(itemCheck)) # 0}
) exitWith {
_treatmentEvent = QEGVAR(medical_treatment,splintLocal);
_treatmentTime = 6;
_treatmentArgs = [_healer, _target, "rightleg"];
_treatmentItem = "splint";
};
case ((count (_target getVariable [VAR_MEDICATIONS, []])) >= 6): {

// Wait until the injured has enough blood before administering drugs
if (_needsIv) then {
_treatmentEvent = "#waitForIV"
};

if (_treatmentEvent == "#waitForIV") exitWith {};

if ((count (_target getVariable [VAR_MEDICATIONS, []])) >= 6) exitWith {
_treatmentEvent = "#tooManyMeds";
};
case ((IS_UNCONSCIOUS(_target) || {_heartRate <= 50})
&& {([_healer, "epinephrine"] call FUNC(itemCheck)) # 0}): {

if (
((IS_UNCONSCIOUS(_target) && {_heartRate < 160}) || {_heartRate <= 50}) &&
{([_healer, "epinephrine"] call FUNC(itemCheck)) # 0}
) exitWith {
if (CBA_missionTime < (_target getVariable [QGVAR(nextEpinephrine), -1])) exitWith {
_treatmentEvent = "#waitForEpinephrineToTakeEffect";
};
Expand All @@ -147,8 +202,11 @@ switch (true) do {
_treatmentArgs = [_target, call _fnc_findNoTourniquet, "Epinephrine"];
_treatmentItem = "epinephrine";
};
case (((GET_PAIN_PERCEIVED(_target) > 0.25) || {_heartRate >= 180})
&& {([_healer, "morphine"] call FUNC(itemCheck)) # 0}): {

if (
(((GET_PAIN_PERCEIVED(_target) > 0.25) && {_heartRate > 40}) || {_heartRate >= 180}) &&
{([_healer, "morphine"] call FUNC(itemCheck)) # 0}
) exitWith {
if (CBA_missionTime < (_target getVariable [QGVAR(nextMorphine), -1])) exitWith {
_treatmentEvent = "#waitForMorphineToTakeEffect";
};
Expand All @@ -169,6 +227,7 @@ _healer setVariable [QGVAR(currentTreatment), [CBA_missionTime + _treatmentTime,
if ((_treatmentEvent select [0,1]) != "#") then {
private _treatmentClassname = _treatmentArgs select 2;
if (_treatmentEvent == QEGVAR(medical_treatment,splintLocal)) then { _treatmentClassname = "Splint" };
if (_treatmentEvent == QEGVAR(medical_treatment,cprLocal)) then { _treatmentClassname = "CPR" };
[_healer, _treatmentClassname, (_healer == _target)] call FUNC(playTreatmentAnim);
};

Expand Down
2 changes: 1 addition & 1 deletion addons/medical_ai/functions/fnc_playTreatmentAnim.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* None
*
* Example:
* [cursorObject, true, true] call ace_medical_ai_fnc_playTreatmentAnim
* [cursorObject, "Splint", true] call ace_medical_ai_fnc_playTreatmentAnim
*
* Public: No
*/
Expand Down
7 changes: 5 additions & 2 deletions addons/medical_ai/functions/fnc_requestMedic.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@

private _assignedMedic = _this getVariable QGVAR(assignedMedic);
private _healQueue = _assignedMedic getVariable [QGVAR(healQueue), []];
_healQueue pushBack _this;
_assignedMedic setVariable [QGVAR(healQueue), _healQueue];

// Only update if it was actually changed
if (_healQueue pushBackUnique _this != -1) then {
_assignedMedic setVariable [QGVAR(healQueue), _healQueue];
};

#ifdef DEBUG_MODE_FULL
systemChat format ["%1 requested %2 for medical treatment", _this, _assignedMedic];
Expand Down
9 changes: 2 additions & 7 deletions addons/medical_ai/stateMachine.inc.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,8 @@ GVAR(stateMachine) = [{call EFUNC(common,getLocalUnits)}, true] call CBA_statema
#endif
}, {}, {}, "Safe"] call CBA_statemachine_fnc_addState;

[GVAR(stateMachine), LINKFUNC(healSelf), {}, {
_this setVariable [QGVAR(treatmentOverAt), nil];
}, "HealSelf"] call CBA_statemachine_fnc_addState;

[GVAR(stateMachine), LINKFUNC(healUnit), {}, {
_this setVariable [QGVAR(treatmentOverAt), nil];
}, "HealUnit"] call CBA_statemachine_fnc_addState;
[GVAR(stateMachine), LINKFUNC(healSelf), {}, {}, "HealSelf"] call CBA_statemachine_fnc_addState;
[GVAR(stateMachine), LINKFUNC(healUnit), {}, {}, "HealUnit"] call CBA_statemachine_fnc_addState;

// Add Transistions [statemachine, originalState, targetState, condition, onTransition, name]
[GVAR(stateMachine), "Initial", "Injured", LINKFUNC(isInjured), {}, "Injured"] call CBA_statemachine_fnc_addTransition;
Expand Down

0 comments on commit 98da279

Please sign in to comment.