Skip to content

Commit

Permalink
Medical AI - AI will remove tourniquets (#10166)
Browse files Browse the repository at this point in the history
* Medical AI - AI will remove tourniquets

* Medical AI - Improve tourniquet removal (for #10166) (#10178)

* Fixes & tweaks

- Have AI remove tourniquets ASAP
- Fixed bug where AI would not remove tourniquet, because it didn't have any bandages
- Allowed for more multitasking

* Allow healer to administer morphine if out of bandages

* Remove TODO comment

* Allow AI to remove tourniquets from limbs with no open wounds

* Update addons/medical_ai/functions/fnc_healingLogic.sqf

---------

Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>
  • Loading branch information
PabstMirror and johnb432 authored Aug 11, 2024
1 parent 96f81f1 commit 0b4029b
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 45 deletions.
125 changes: 80 additions & 45 deletions addons/medical_ai/functions/fnc_healingLogic.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
* Public: No
*/

// TODO: Add AI tourniquet behaviour
// For now, AI handle player or otherwise scripted tourniquets only

params ["_healer", "_target"];
(_healer getVariable [QGVAR(currentTreatment), [-1]]) params ["_finishTime", "_treatmentTarget", "_treatmentEvent", "_treatmentArgs", "_treatmentItem"];

Expand Down Expand Up @@ -87,47 +84,68 @@ if (_finishTime > 0) exitWith {
};
};

// Find a suitable limb (no tourniquets) for injecting and giving IVs
private _fnc_findNoTourniquet = {
private _bodyPart = "";
// Bandage a limb up, then remove the tourniquet on it
private _fnc_removeTourniquet = {
params [["_removeAllTourniquets", false]];

// If all limbs have tourniquets, find the least damaged limb and try to bandage it
if ((_tourniquets select [2]) find 0 == -1) then {
// If no bandages available, wait
if !(([_healer, "@bandage"] call FUNC(itemCheck)) # 0) exitWith {
_treatmentEvent = "#waitForNonTourniquetedLimb"; // TODO: Medic can move onto another patient/should be flagged as out of supplies
// Ignore head & torso if not removing all tourniquets (= administering drugs/IVs)
private _offset = [2, 0] select _removeAllTourniquets;

// Bandage the least bleeding body part
private _bodyPartBleeding = [];
_bodyPartBleeding resize [[4, 6] select _removeAllTourniquets, -1];

{
// Ignore head and torso, if only looking for place to administer drugs/IVs
private _partIndex = (ALL_BODY_PARTS find _x) - _offset;

if (_partIndex >= 0 && {_tourniquets select _partIndex != 0}) then {
{
_x params ["", "_amountOf", "_bleeding"];

// max 0, to set the baseline to 0, as body parts with no wounds are marked with -1
_bodyPartBleeding set [_partIndex, ((_bodyPartBleeding select _partIndex) max 0) + (_amountOf * _bleeding)];
} forEach _y;
};
} forEach GET_OPEN_WOUNDS(_target);

// Bandage the least bleeding body part
private _bodyPartBleeding = [0, 0, 0, 0];
// If there are no open wounds, check if there are tourniquets on limbs with no open wounds (stitched or fully healed),
// as we know there have to be tourniquets at this point
if (_bodyPartBleeding findIf {_x != -1} == -1) then {
_bodyPartBleeding set [_tourniquets findIf {_x != 0}, 0];
};

{
// Ignore head and torso
private _partIndex = (ALL_BODY_PARTS find _x) - 2;
// Ignore body parts that don't have open wounds (-1)
private _minBodyPartBleeding = selectMin (_bodyPartBleeding select {_x != -1});
private _selection = ALL_BODY_PARTS select ((_bodyPartBleeding find _minBodyPartBleeding) + _offset);

if (_partIndex >= 0) then {
{
_x params ["", "_amountOf", "_bleeding"];
_bodyPartBleeding set [_partIndex, (_bodyPartBleeding select _partIndex) + (_amountOf * _bleeding)];
} forEach _y;
};
} forEach GET_OPEN_WOUNDS(_target);
// If not bleeding anymore, remove the tourniquet
if (_minBodyPartBleeding == 0) exitWith {
_treatmentEvent = QGVAR(tourniquetRemove);
_treatmentTime = 7;
_treatmentArgs = [_healer, _target, _selection];
};

// If no bandages available, wait
// If check is done at the start of the scope, it will miss the edge case where the unit ran out of bandages just as they finished bandaging tourniqueted body part
if !(([_healer, "@bandage"] call FUNC(itemCheck)) # 0) exitWith {
_treatmentEvent = "#waitForBandages"; // TODO: Medic can move onto another patient/should be flagged as out of supplies
};

private _minBodyPartBleeding = selectMin _bodyPartBleeding;
private _selection = ALL_BODY_PARTS select ((_bodyPartBleeding find _minBodyPartBleeding) + 2);
// Otherwise keep bandaging
_treatmentEvent = QEGVAR(medical_treatment,bandageLocal);
_treatmentTime = 5;
_treatmentArgs = [_target, _selection, "FieldDressing"];
_treatmentItem = "@bandage";
};

// If not bleeding anymore, remove the tourniquet
if (_minBodyPartBleeding == 0) exitWith {
_treatmentEvent = QGVAR(tourniquetRemove);
_treatmentTime = 7;
_treatmentArgs = [_healer, _target, _selection];
};
// Find a suitable limb (no tourniquets) for adminstering drugs/IVs
private _fnc_findNoTourniquet = {
private _bodyPart = "";

// Otherwise keep bandaging
_treatmentEvent = QEGVAR(medical_treatment,bandageLocal);
_treatmentTime = 5;
_treatmentArgs = [_target, _selection, "FieldDressing"];
_treatmentItem = "@bandage";
// If all limbs have tourniquets, find the least damaged limb and try to bandage it
if ((_tourniquets select [2]) find 0 == -1) then {
call _fnc_removeTourniquet;
} else {
// Select a random non-tourniqueted limb otherwise
private _bodyParts = ["leftarm", "rightarm", "leftleg", "rightleg"];
Expand Down Expand Up @@ -270,7 +288,7 @@ if (true) then {

// Wait until the injured has enough blood before administering drugs
// (_needsIV && !_canGiveIV), but _canGiveIV is false here, otherwise IV would be given
if (_needsIV || {_treatmentEvent == "#waitForIV"}) exitWith {
if (_needsIV || {_doCPR && {_treatmentEvent == "#waitForIV"}}) exitWith {
// If injured is in cardiac arrest and the healer is doing nothing else, start CPR
if (_doCPR) exitWith {
_treatmentEvent = QEGVAR(medical_treatment,cprLocal); // TODO: Medic remains in this loop until injured is given enough IVs or dies
Expand All @@ -284,23 +302,30 @@ if (true) then {
};
};

if ((count (_target getVariable [VAR_MEDICATIONS, []])) >= 6) exitWith {
// These checks are not exitWith, so that the medic can try to bandage up tourniqueted body parts
if ((count (_target getVariable [VAR_MEDICATIONS, []])) >= 6) then {
_treatmentEvent = "#tooManyMeds"; // TODO: Medic can move onto another patient
};

private _heartRate = GET_HEART_RATE(_target);
private _canGiveEpinephrine = !(_treatmentEvent in ["#tooManyMeds", "#waitForIV"]) &&
{IS_UNCONSCIOUS(_target) || {_heartRate <= 50}} &&
{([_healer, "epinephrine"] call FUNC(itemCheck)) # 0};

if (
(IS_UNCONSCIOUS(_target) || {_heartRate <= 50}) &&
{([_healer, "epinephrine"] call FUNC(itemCheck)) # 0}
) exitWith {
if (CBA_missionTime < (_target getVariable [QGVAR(nextEpinephrine), -1])) exitWith {
// This allows for some multitasking
if (_canGiveEpinephrine) then {
if (CBA_missionTime < (_target getVariable [QGVAR(nextEpinephrine), -1])) then {
_treatmentEvent = "#waitForEpinephrineToTakeEffect";
_canGiveEpinephrine = false;
};
if (_heartRate > 180) exitWith {

if (_heartRate > 180) then {
_treatmentEvent = "#waitForSlowerHeart"; // TODO: Medic can move onto another patient, after X amount of time of high HR
_canGiveEpinephrine = false;
};
};

if (_canGiveEpinephrine) exitWith {
// If all limbs are tourniqueted, bandage the one with the least amount of wounds, so that the tourniquet can be removed
_bodyPart = call _fnc_findNoTourniquet;

Expand All @@ -313,8 +338,18 @@ if (true) then {
_treatmentItem = "epinephrine";
};

// Remove all remaining tourniquets by bandaging all body parts
if (_tourniquets isNotEqualTo DEFAULT_TOURNIQUET_VALUES) then {
true call _fnc_removeTourniquet;
};

// If the healer can bandage or remove tourniquets, do that
if (_treatmentEvent in [QEGVAR(medical_treatment,bandageLocal), QGVAR(tourniquetRemove)]) exitWith {};

// Otherwise, if the healer is either done or out of bandages, continue
if (
((GET_PAIN_PERCEIVED(_target) > 0.25) || {_heartRate >= 180}) &&
!(_treatmentEvent in ["#tooManyMeds", "#waitForIV"]) &&
{(GET_PAIN_PERCEIVED(_target) > 0.25) || {_heartRate >= 180}} &&
{([_healer, "morphine"] call FUNC(itemCheck)) # 0}
) exitWith {
if (CBA_missionTime < (_target getVariable [QGVAR(nextMorphine), -1])) exitWith {
Expand Down
1 change: 1 addition & 0 deletions addons/medical_ai/functions/fnc_isInjured.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ if !(alive _this) exitWith {false};
private _fractures = GET_FRACTURES(_this);
((_fractures select 4) == 1) || {(_fractures select 5) == 1}
}
|| { GET_TOURNIQUETS(_this) isNotEqualTo DEFAULT_TOURNIQUET_VALUES }

0 comments on commit 0b4029b

Please sign in to comment.