Medical - Change medical to use hashmaps for wound storage (#8926)

* Refactor medical to use hashmaps for wound storage

- We most frequently want to access wounds by body part, so this makes
  that a constant time lookup.
- The body part index is no longer stored in every wound since it's
  inherent in the wound storage.
- Using body part names as the keys of the hashmap to improve code
  clarity (no more magic numbers).

closes #6468

* Add deserilization migration from old wound arrays

Will migrate from old form array wound storage to the new hashmap
strucutre during deserlization. This is relevant for communities piping
medical state out to a database or similar between sessions.

* fix issue with suture stitching

* change version number in comment

---------

Co-authored-by: Salluci <salluci.lovi@gmail.com>
This commit is contained in:
Kyle Mckay 2023-06-24 06:11:56 +01:00 committed by GitHub
parent 6181616cc3
commit 02365609b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 269 additions and 195 deletions

View File

@ -95,24 +95,33 @@ GVAR(dev_watchVariableRunning) = true;
_return pushBack "------- Open Wounds: -------"; _return pushBack "------- Open Wounds: -------";
private _wounds = GET_OPEN_WOUNDS(_unit); private _wounds = GET_OPEN_WOUNDS(_unit);
{ {
_x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage"]; private _bodyPart = _x;
_return pushBack format ["%1: [%2] [x%3] [Bld: %4] [Dmg: %5]", ALL_SELECTIONS select _xBodyPartN, _xClassID, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2]; {
_x params ["_xClassID", "_xAmountOf", "_xBleeding", "_xDamage"];
_return pushBack format ["%1: [%2] [x%3] [Bld: %4] [Dmg: %5]", _bodyPart, _xClassID, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2];
} forEach _y;
} forEach _wounds; } forEach _wounds;
// Bandaged Wounds: // Bandaged Wounds:
_return pushBack "------- Bandaged Wounds: -------"; _return pushBack "------- Bandaged Wounds: -------";
private _wounds = GET_BANDAGED_WOUNDS(_unit); private _wounds = GET_BANDAGED_WOUNDS(_unit);
{ {
_x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage"]; private _bodyPart = _x;
_return pushBack format ["%1: [%2] [x%3] [Bld: %4] [Dmg: %5]", ALL_SELECTIONS select _xBodyPartN, _xClassID, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2]; {
_x params ["_xClassID", "_xAmountOf", "_xBleeding", "_xDamage"];
_return pushBack format ["%1: [%2] [x%3] [Bld: %4] [Dmg: %5]", _bodyPart, _xClassID, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2];
} forEach _y;
} forEach _wounds; } forEach _wounds;
// Stitched Wounds: // Stitched Wounds:
_return pushBack "------- Stitched Wounds: -------"; _return pushBack "------- Stitched Wounds: -------";
private _wounds = GET_STITCHED_WOUNDS(_unit); private _wounds = GET_STITCHED_WOUNDS(_unit);
{ {
_x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage"]; private _bodyPart = _x;
_return pushBack format ["%1: [%2] [x%3] [Bld: %4] [Dmg: %5]", ALL_SELECTIONS select _xBodyPartN, _xClassID, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2]; {
_x params ["_xClassID", "_xAmountOf", "_xBleeding", "_xDamage"];
_return pushBack format ["%1: [%2] [x%3] [Bld: %4] [Dmg: %5]", _bodyPart, _xClassID, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2];
} forEach _y;
} forEach _wounds; } forEach _wounds;
// IVs: // IVs:

View File

@ -35,6 +35,22 @@ if !(_unit getVariable [QGVAR(initialized), false]) exitWith {
private _state = [_json] call CBA_fnc_parseJSON; private _state = [_json] call CBA_fnc_parseJSON;
// Migration from old array wounding storage serialized in old versions (<= 3.16.0)
{
if ((_state getVariable [_x, createHashMap]) isEqualType []) then {
private _migratedWounds = createHashMap;
{
_x params ["_class", "_bodyPartIndex", "_amountOf", "_bleeding", "_damage"];
private _partWounds = _migratedWounds getOrDefault [ALL_BODY_PARTS select _bodyPartIndex, [], true];
_partWounds pushBack [_class, _amountOf, _bleeding, _damage];
} forEach (_state getVariable _x);
_state setVariable [_x, _migratedWounds];
};
} forEach [VAR_OPEN_WOUNDS, VAR_BANDAGED_WOUNDS, VAR_STITCHED_WOUNDS];
// Set medical variables // Set medical variables
{ {
_x params ["_var", "_default"]; _x params ["_var", "_default"];
@ -57,9 +73,9 @@ private _state = [_json] call CBA_fnc_parseJSON;
[VAR_PAIN, 0], [VAR_PAIN, 0],
[VAR_IN_PAIN, false], [VAR_IN_PAIN, false],
[VAR_PAIN_SUPP, 0], [VAR_PAIN_SUPP, 0],
[VAR_OPEN_WOUNDS, []], [VAR_OPEN_WOUNDS, createHashMap],
[VAR_BANDAGED_WOUNDS, []], [VAR_BANDAGED_WOUNDS, createHashMap],
[VAR_STITCHED_WOUNDS, []], [VAR_STITCHED_WOUNDS, createHashMap],
[VAR_FRACTURES, DEFAULT_FRACTURE_VALUES], [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES],
// State transition should handle this // State transition should handle this
// [VAR_UNCON, false], // [VAR_UNCON, false],

View File

@ -33,9 +33,9 @@ private _state = [] call CBA_fnc_createNamespace;
[VAR_PAIN, 0], [VAR_PAIN, 0],
[VAR_IN_PAIN, false], [VAR_IN_PAIN, false],
[VAR_PAIN_SUPP, 0], [VAR_PAIN_SUPP, 0],
[VAR_OPEN_WOUNDS, []], [VAR_OPEN_WOUNDS, createHashMap],
[VAR_BANDAGED_WOUNDS, []], [VAR_BANDAGED_WOUNDS, createHashMap],
[VAR_STITCHED_WOUNDS, []], [VAR_STITCHED_WOUNDS, createHashMap],
[VAR_FRACTURES, DEFAULT_FRACTURE_VALUES], [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES],
// State transition should handle this // State transition should handle this
// [VAR_UNCON, false], // [VAR_UNCON, false],

View File

@ -44,12 +44,14 @@ private _treatmentTime = 6;
switch (true) do { switch (true) do {
case (GET_WOUND_BLEEDING(_target) > 0): { case (GET_WOUND_BLEEDING(_target) > 0): {
// Select first bleeding wound and bandage it // Select first bleeding wound and bandage it
private _openWounds = GET_OPEN_WOUNDS(_target);
private _selection = "?"; private _selection = "?";
{ {
_x params ["", "_index", "_amount", "_percentage"]; private _foundBleeding = _y findIf {
if ((_amount * _percentage) > 0) exitWith { _selection = ALL_BODY_PARTS select _index; }; _x params ["", "_amount", "_percentage"];
} forEach _openWounds; (_amount * _percentage) > 0
};
if (_foundBleeding != -1) exitWith { _selection = _x; };
} forEach GET_OPEN_WOUNDS(_target);
_treatmentEvent = QEGVAR(medical_treatment,bandageLocal); _treatmentEvent = QEGVAR(medical_treatment,bandageLocal);
_treatmentTime = 5; _treatmentTime = 5;
_treatmentArgs = [_target, _selection, "FieldDressing"]; _treatmentArgs = [_target, _selection, "FieldDressing"];

View File

@ -24,11 +24,11 @@ _bodyPartDamage params ["_headDamage", "_bodyDamage", "_leftArmDamage", "_rightA
// Exclude non penetrating body damage // Exclude non penetrating body damage
{ {
_x params ["", "_bodyPartN", "_amountOf", "", "_damage"]; _x params ["", "_amountOf", "", "_damage"];
if (_bodyPartN == 1 && {_damage < PENETRATION_THRESHOLD}) then { if (_damage < PENETRATION_THRESHOLD) then {
_bodyDamage = _bodyDamage - (_amountOf * _damage); _bodyDamage = _bodyDamage - (_amountOf * _damage);
}; };
} forEach GET_OPEN_WOUNDS(_unit); } forEach (GET_OPEN_WOUNDS(_unit) getOrDefault ["body", []]);
private _damageThreshold = GET_DAMAGE_THRESHOLD(_unit); private _damageThreshold = GET_DAMAGE_THRESHOLD(_unit);

View File

@ -41,12 +41,13 @@ private _bodyPartVisParams = [_unit, false, false, false, false]; // params arra
// process wounds separately for each body part hit // process wounds separately for each body part hit
{ // forEach _allDamages { // forEach _allDamages
_x params ["_damage", "_bodyPart"]; _x params ["_damage", "_bodyPart"];
_bodyPart = toLower _bodyPart;
// silently ignore structural damage // silently ignore structural damage
if (_bodyPart == "#structural") then {continue}; if (_bodyPart == "#structural") then {continue};
// Convert the selectionName to a number and ensure it is a valid selection. // Convert the selectionName to a number and ensure it is a valid selection.
private _bodyPartNToAdd = ALL_BODY_PARTS find toLower _bodyPart; private _bodyPartNToAdd = ALL_BODY_PARTS find _bodyPart;
if (_bodyPartNToAdd < 0) then { if (_bodyPartNToAdd < 0) then {
ERROR_1("invalid body part %1",_bodyPart); ERROR_1("invalid body part %1",_bodyPart);
continue continue
@ -112,10 +113,10 @@ private _bodyPartVisParams = [_unit, false, false, false, false]; // params arra
private _classComplex = 10 * _woundClassIDToAdd + _category; private _classComplex = 10 * _woundClassIDToAdd + _category;
// Create a new injury. Format [0:classComplex, 1:bodypart, 2:amountOf, 3:bleedingRate, 4:woundDamage] // Create a new injury. Format [0:classComplex, 1:amountOf, 2:bleedingRate, 3:woundDamage]
private _injury = [_classComplex, _bodyPartNToAdd, 1, _bleeding, _woundDamage]; private _injury = [_classComplex, 1, _bleeding, _woundDamage];
if (_bodyPartNToAdd == 0 || {_bodyPartNToAdd == 1 && {_woundDamage > PENETRATION_THRESHOLD}}) then { if (_bodyPart isEqualTo "head" || {_bodyPart isEqualTo "body" && {_woundDamage > PENETRATION_THRESHOLD}}) then {
_criticalDamage = true; _criticalDamage = true;
}; };
if ([_unit, _bodyPartNToAdd, _bodyPartDamage, _woundDamage] call FUNC(determineIfFatal)) then { if ([_unit, _bodyPartNToAdd, _bodyPartDamage, _woundDamage] call FUNC(determineIfFatal)) then {
@ -158,28 +159,28 @@ private _bodyPartVisParams = [_unit, false, false, false, false]; // params arra
// if possible merge into existing wounds // if possible merge into existing wounds
private _createNewWound = true; private _createNewWound = true;
private _existingWounds = _openWounds getOrDefault [_bodyPart, [], true];
{ {
_x params ["_classID", "_bodyPartN", "_oldAmountOf", "_oldBleeding", "_oldDamage"]; _x params ["_classID", "_oldAmountOf", "_oldBleeding", "_oldDamage"];
if ( if (
(_classComplex == _classID) && (_classComplex == _classID) &&
{_bodyPartNToAdd == _bodyPartN} && {(_bodyPart isNotEqualTo "body") || {(_woundDamage < PENETRATION_THRESHOLD) isEqualTo (_oldDamage < PENETRATION_THRESHOLD)}} && // penetrating body damage is handled differently
{(_bodyPartNToAdd != 1) || {(_woundDamage < PENETRATION_THRESHOLD) isEqualTo (_oldDamage < PENETRATION_THRESHOLD)}} && // penetrating body damage is handled differently
{(_bodyPartNToAdd > 3) || {!_causeLimping} || {(_woundDamage <= LIMPING_DAMAGE_THRESHOLD) isEqualTo (_oldDamage <= LIMPING_DAMAGE_THRESHOLD)}} // ensure limping damage is stacked correctly {(_bodyPartNToAdd > 3) || {!_causeLimping} || {(_woundDamage <= LIMPING_DAMAGE_THRESHOLD) isEqualTo (_oldDamage <= LIMPING_DAMAGE_THRESHOLD)}} // ensure limping damage is stacked correctly
) exitWith { ) exitWith {
TRACE_2("merging with existing wound",_injury,_x); TRACE_2("merging with existing wound",_injury,_x);
private _newAmountOf = _oldAmountOf + 1; private _newAmountOf = _oldAmountOf + 1;
_x set [2, _newAmountOf]; _x set [1, _newAmountOf];
private _newBleeding = (_oldAmountOf * _oldBleeding + _bleeding) / _newAmountOf; private _newBleeding = (_oldAmountOf * _oldBleeding + _bleeding) / _newAmountOf;
_x set [3, _newBleeding]; _x set [2, _newBleeding];
private _newDamage = (_oldAmountOf * _oldDamage + _woundDamage) / _newAmountOf; private _newDamage = (_oldAmountOf * _oldDamage + _woundDamage) / _newAmountOf;
_x set [4, _newDamage]; _x set [3, _newDamage];
_createNewWound = false; _createNewWound = false;
}; };
} forEach _openWounds; } forEach _existingWounds;
if (_createNewWound) then { if (_createNewWound) then {
TRACE_1("adding new wound",_injury); TRACE_1("adding new wound",_injury);
_openWounds pushBack _injury; _existingWounds pushBack _injury;
}; };
_createdWounds = true; _createdWounds = true;
}; };

View File

@ -48,18 +48,31 @@ if (EGVAR(medical,fractures) > 0) then {
}; };
if (!_isLimping && {EGVAR(medical,limping) > 0}) then { if (!_isLimping && {EGVAR(medical,limping) > 0}) then {
private _woundsToCheck = GET_OPEN_WOUNDS(_unit); private _openWounds = GET_OPEN_WOUNDS(_unit);
// Want a copy of combined arrays to prevent wound mixing
private _legWounds = (_openWounds getOrDefault ["leftleg", []])
+ (_openWounds getOrDefault ["rightleg", []]);
if (EGVAR(medical,limping) == 2) then { if (EGVAR(medical,limping) == 2) then {
_woundsToCheck = _woundsToCheck + GET_BANDAGED_WOUNDS(_unit); // do not append private _bandagedWounds = GET_BANDAGED_WOUNDS(_unit);
_legWounds = _legWounds
+ (_bandagedWounds getOrDefault ["leftleg", []])
+ (_bandagedWounds getOrDefault ["rightleg", []]);
}; };
{ {
_x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "", "_xDamage"]; _x params ["_xClassID", "_xAmountOf", "", "_xDamage"];
if ((_xBodyPartN > 3) && {_xAmountOf > 0} && {_xDamage > LIMPING_DAMAGE_THRESHOLD} && { if (
(EGVAR(medical_damage,woundDetails) get (_xClassID / 10)) select 3}) exitWith { // select _causeLimping from woundDetails (_xAmountOf > 0)
&& {_xDamage > LIMPING_DAMAGE_THRESHOLD}
// select _causeLimping from woundDetails
&& {(EGVAR(medical_damage,woundDetails) get (_xClassID / 10)) select 3}
) exitWith {
TRACE_1("limping because of wound",_x); TRACE_1("limping because of wound",_x);
_isLimping = true; _isLimping = true;
}; };
} forEach _woundsToCheck; } forEach _legWounds;
}; };
_unit setVariable [QEGVAR(medical,isLimping), _isLimping, true]; _unit setVariable [QEGVAR(medical,isLimping), _isLimping, true];

View File

@ -99,8 +99,8 @@
#define VISUAL_BODY_DAMAGE_THRESHOLD 0.35 #define VISUAL_BODY_DAMAGE_THRESHOLD 0.35
// Empty wound data, used for some default return values // Empty wound data, used for some default return values
// [classID, bodypartIndex, amountOf, bloodloss, damage] // [classID, amountOf, bloodloss, damage]
#define EMPTY_WOUND [-1, -1, 0, 0, 0] #define EMPTY_WOUND [-1, 0, 0, 0]
// Base time to bandage each wound category // Base time to bandage each wound category
#define BANDAGE_TIME_S 4 #define BANDAGE_TIME_S 4
@ -173,9 +173,9 @@
#define IS_BLEEDING(unit) (GET_WOUND_BLEEDING(unit) > 0) #define IS_BLEEDING(unit) (GET_WOUND_BLEEDING(unit) > 0)
#define IS_IN_PAIN(unit) (unit getVariable [VAR_IN_PAIN, false]) #define IS_IN_PAIN(unit) (unit getVariable [VAR_IN_PAIN, false])
#define IS_UNCONSCIOUS(unit) (unit getVariable [VAR_UNCON, false]) #define IS_UNCONSCIOUS(unit) (unit getVariable [VAR_UNCON, false])
#define GET_OPEN_WOUNDS(unit) (unit getVariable [VAR_OPEN_WOUNDS, []]) #define GET_OPEN_WOUNDS(unit) (unit getVariable [VAR_OPEN_WOUNDS, createHashMap])
#define GET_BANDAGED_WOUNDS(unit) (unit getVariable [VAR_BANDAGED_WOUNDS, []]) #define GET_BANDAGED_WOUNDS(unit) (unit getVariable [VAR_BANDAGED_WOUNDS, createHashMap])
#define GET_STITCHED_WOUNDS(unit) (unit getVariable [VAR_STITCHED_WOUNDS, []]) #define GET_STITCHED_WOUNDS(unit) (unit getVariable [VAR_STITCHED_WOUNDS, createHashMap])
#define GET_DAMAGE_THRESHOLD(unit) (unit getVariable [QEGVAR(medical,damageThreshold), [EGVAR(medical,AIDamageThreshold),EGVAR(medical,playerDamageThreshold)] select (isPlayer unit)]) #define GET_DAMAGE_THRESHOLD(unit) (unit getVariable [QEGVAR(medical,damageThreshold), [EGVAR(medical,AIDamageThreshold),EGVAR(medical,playerDamageThreshold)] select (isPlayer unit)])
// The following function calls are defined here just for consistency // The following function calls are defined here just for consistency

View File

@ -5,7 +5,7 @@ class ACE_Head {
exceptions[] = {"isNotInside", "isNotSitting"}; exceptions[] = {"isNotInside", "isNotSitting"};
ACTION_CONDITION ACTION_CONDITION
statement = QUOTE([ARR_2(_target,0)] call FUNC(displayPatientInformation)); statement = QUOTE([ARR_2(_target,0)] call FUNC(displayPatientInformation));
modifierFunction = QUOTE([ARR_3(_target,0,_this select 3)] call FUNC(modifyAction)); modifierFunction = QUOTE([ARR_3(_target,""head"",_this select 3)] call FUNC(modifyAction));
runOnHover = 1; runOnHover = 1;
}; };
class ACE_Torso { class ACE_Torso {
@ -15,7 +15,7 @@ class ACE_Torso {
exceptions[] = {"isNotInside", "isNotSitting"}; exceptions[] = {"isNotInside", "isNotSitting"};
ACTION_CONDITION ACTION_CONDITION
statement = QUOTE([ARR_2(_target,1)] call FUNC(displayPatientInformation)); statement = QUOTE([ARR_2(_target,1)] call FUNC(displayPatientInformation));
modifierFunction = QUOTE([ARR_3(_target,1,_this select 3)] call FUNC(modifyAction)); modifierFunction = QUOTE([ARR_3(_target,""body"",_this select 3)] call FUNC(modifyAction));
runOnHover = 1; runOnHover = 1;
class TriageCard { class TriageCard {
displayName = CSTRING(Actions_TriageCard); displayName = CSTRING(Actions_TriageCard);
@ -32,7 +32,7 @@ class ACE_ArmLeft {
exceptions[] = {"isNotInside", "isNotSitting"}; exceptions[] = {"isNotInside", "isNotSitting"};
ACTION_CONDITION ACTION_CONDITION
statement = QUOTE([ARR_2(_target,2)] call FUNC(displayPatientInformation)); statement = QUOTE([ARR_2(_target,2)] call FUNC(displayPatientInformation));
modifierFunction = QUOTE([ARR_3(_target,2,_this select 3)] call FUNC(modifyAction)); modifierFunction = QUOTE([ARR_3(_target,""leftarm"",_this select 3)] call FUNC(modifyAction));
runOnHover = 1; runOnHover = 1;
}; };
class ACE_ArmRight { class ACE_ArmRight {
@ -42,7 +42,7 @@ class ACE_ArmRight {
exceptions[] = {"isNotInside", "isNotSitting"}; exceptions[] = {"isNotInside", "isNotSitting"};
ACTION_CONDITION ACTION_CONDITION
statement = QUOTE([ARR_2(_target,3)] call FUNC(displayPatientInformation)); statement = QUOTE([ARR_2(_target,3)] call FUNC(displayPatientInformation));
modifierFunction = QUOTE([ARR_3(_target,3,_this select 3)] call FUNC(modifyAction)); modifierFunction = QUOTE([ARR_3(_target,""rightarm"",_this select 3)] call FUNC(modifyAction));
runOnHover = 1; runOnHover = 1;
}; };
class ACE_LegLeft { class ACE_LegLeft {
@ -52,7 +52,7 @@ class ACE_LegLeft {
exceptions[] = {"isNotInside", "isNotSitting"}; exceptions[] = {"isNotInside", "isNotSitting"};
ACTION_CONDITION ACTION_CONDITION
statement = QUOTE([ARR_2(_target,4)] call FUNC(displayPatientInformation)); statement = QUOTE([ARR_2(_target,4)] call FUNC(displayPatientInformation));
modifierFunction = QUOTE([ARR_3(_target,4,_this select 3)] call FUNC(modifyAction)); modifierFunction = QUOTE([ARR_3(_target,""leftleg"",_this select 3)] call FUNC(modifyAction));
runOnHover = 1; runOnHover = 1;
}; };
class ACE_LegRight { class ACE_LegRight {
@ -62,6 +62,6 @@ class ACE_LegRight {
exceptions[] = {"isNotInside", "isNotSitting"}; exceptions[] = {"isNotInside", "isNotSitting"};
ACTION_CONDITION ACTION_CONDITION
statement = QUOTE([ARR_2(_target,5)] call FUNC(displayPatientInformation)); statement = QUOTE([ARR_2(_target,5)] call FUNC(displayPatientInformation));
modifierFunction = QUOTE([ARR_3(_target,5,_this select 3)] call FUNC(modifyAction)); modifierFunction = QUOTE([ARR_3(_target,""rightleg"",_this select 3)] call FUNC(modifyAction));
runOnHover = 1; runOnHover = 1;
}; };

View File

@ -5,32 +5,30 @@
* *
* Arguments: * Arguments:
* 0: Unit <OBJECT> * 0: Unit <OBJECT>
* 1: Body part index <NUMBER> * 1: Body part <STRING>
* 2: Action data <ARRAY> * 2: Action data <ARRAY>
* *
* Return Value: * Return Value:
* None * None
* *
* Example: * Example:
* [_target, 0, _actionData] call ace_medical_gui_fnc_modifyAction * [_target, "head", _actionData] call ace_medical_gui_fnc_modifyAction
* *
* Public: No * Public: No
*/ */
#define COLOR_SCALE ["#ffffff", "#fff1a1", "#ffe075", "#ffcb55", "#ffb73c", "#ffa127", "#ff8815", "#ff6d05", "#ff4b00", "#ff0000"] #define COLOR_SCALE ["#ffffff", "#fff1a1", "#ffe075", "#ffcb55", "#ffb73c", "#ffa127", "#ff8815", "#ff6d05", "#ff4b00", "#ff0000"]
params ["_target", "_partIndex", "_actionData"]; params ["_target", "_bodyPart", "_actionData"];
private _partIndex = ALL_BODY_PARTS find _bodyPart;
private _bloodLossOnBodyPart = 0; private _bloodLossOnBodyPart = 0;
// Add all bleeding from wounds on selection // Add all bleeding from wounds on selection
{ {
_x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; _x params ["", "_amountOf", "_bleeding"];
if (_bodyPartN == _partIndex) then {
_bloodLossOnBodyPart = _bloodLossOnBodyPart + (_amountOf * _bleeding); _bloodLossOnBodyPart = _bloodLossOnBodyPart + (_amountOf * _bleeding);
}; } forEach (GET_OPEN_WOUNDS(_target) getOrDefault [_bodyPart, []]);
} forEach GET_OPEN_WOUNDS(_target);
private _frBL = 0 max (_bloodLossOnBodyPart / BLOOD_LOSS_RED_THRESHOLD) min 1; private _frBL = 0 max (_bloodLossOnBodyPart / BLOOD_LOSS_RED_THRESHOLD) min 1;
private _colorInt = ceil (_frBL * (BLOOD_LOSS_TOTAL_COLORS - 1)); // ceil because any bleeding more than zero shouldn't be white private _colorInt = ceil (_frBL * (BLOOD_LOSS_TOTAL_COLORS - 1)); // ceil because any bleeding more than zero shouldn't be white

View File

@ -25,8 +25,11 @@ private _bodyPartDamage = _target getVariable [QEGVAR(medical,bodyPartDamage), [
private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0]; private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0];
{ {
_x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; private _partIndex = ALL_BODY_PARTS find _x;
_bodyPartBloodLoss set [_bodyPartN, (_bodyPartBloodLoss select _bodyPartN) + (_bleeding * _amountOf)]; {
_x params ["", "_amountOf", "_bleeding"];
_bodyPartBloodLoss set [_partIndex, (_bodyPartBloodLoss select _partIndex) + (_bleeding * _amountOf)];
} forEach _y;
} forEach GET_OPEN_WOUNDS(_target); } forEach GET_OPEN_WOUNDS(_target);
{ {

View File

@ -110,9 +110,9 @@ private _fnc_processWounds = {
params ["_wounds", "_format", "_color"]; params ["_wounds", "_format", "_color"];
{ {
_x params ["_woundClassID", "_bodyPartN", "_amountOf"]; _x params ["_woundClassID", "_amountOf"];
if (_selectionN == _bodyPartN && {_amountOf > 0}) then { if (_amountOf > 0) then {
private _classIndex = _woundClassID / 10; private _classIndex = _woundClassID / 10;
private _category = _woundClassID % 10; private _category = _woundClassID % 10;
@ -128,7 +128,7 @@ private _fnc_processWounds = {
_woundEntries pushBack [format [_format, _woundDescription], _color]; _woundEntries pushBack [format [_format, _woundDescription], _color];
}; };
} forEach _wounds; } forEach (_wounds getOrDefault [ALL_BODY_PARTS select _selectionN, []]);
}; };
[GET_OPEN_WOUNDS(_target), "%1", [1, 1, 1, 1]] call _fnc_processWounds; [GET_OPEN_WOUNDS(_target), "%1", [1, 1, 1, 1]] call _fnc_processWounds;

View File

@ -46,9 +46,9 @@ if (_isRespawn) then {
_unit setVariable [VAR_PAIN_SUPP, 0, true]; _unit setVariable [VAR_PAIN_SUPP, 0, true];
// - Wounds ------------------------------------------------------------------- // - Wounds -------------------------------------------------------------------
_unit setVariable [VAR_OPEN_WOUNDS, [], true]; _unit setVariable [VAR_OPEN_WOUNDS, createHashMap, true];
_unit setVariable [VAR_BANDAGED_WOUNDS, [], true]; _unit setVariable [VAR_BANDAGED_WOUNDS, createHashMap, true];
_unit setVariable [VAR_STITCHED_WOUNDS, [], true]; _unit setVariable [VAR_STITCHED_WOUNDS, createHashMap, true];
_unit setVariable [QEGVAR(medical,isLimping), false, true]; _unit setVariable [QEGVAR(medical,isLimping), false, true];
_unit setVariable [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES, true]; _unit setVariable [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES, true];

View File

@ -21,9 +21,12 @@ params ["_unit"];
private _tourniquets = GET_TOURNIQUETS(_unit); private _tourniquets = GET_TOURNIQUETS(_unit);
private _bodyPartBleeding = [0,0,0,0,0,0]; private _bodyPartBleeding = [0,0,0,0,0,0];
{ {
_x params ["", "_bodyPart", "_amountOf", "_bleeeding"]; private _partIndex = ALL_BODY_PARTS find _x;
if (_tourniquets select _bodyPart == 0) then { if (_tourniquets select _partIndex == 0) then {
_bodyPartBleeding set [_bodyPart, (_bodyPartBleeding select _bodyPart) + (_amountOf * _bleeeding)]; {
_x params ["", "_amountOf", "_bleeeding"];
_bodyPartBleeding set [_partIndex, (_bodyPartBleeding select _partIndex) + (_amountOf * _bleeeding)];
} forEach _y;
}; };
} forEach GET_OPEN_WOUNDS(_unit); } forEach GET_OPEN_WOUNDS(_unit);

View File

@ -19,27 +19,26 @@
params ["_patient", "_bodyPart", "_bandage"]; params ["_patient", "_bodyPart", "_bandage"];
TRACE_3("bandageLocal",_patient,_bodyPart,_bandage); TRACE_3("bandageLocal",_patient,_bodyPart,_bandage);
_bodyPart = toLower _bodyPart;
private _partIndex = ALL_BODY_PARTS find toLower _bodyPart;
if (_partIndex < 0) exitWith {};
private _openWounds = GET_OPEN_WOUNDS(_patient); private _openWounds = GET_OPEN_WOUNDS(_patient);
if (_openWounds isEqualTo []) exitWith {}; private _woundsOnPart = _openWounds getOrDefault [_bodyPart, []];
if (_woundsOnPart isEqualTo []) exitWith {};
// Figure out which injury for this bodypart is the best choice to bandage // Figure out which injury for this bodypart is the best choice to bandage
// TODO also use up the remainder on left over injuries // TODO also use up the remainder on left over injuries
private _targetWound = [_patient, _bandage, _partIndex] call FUNC(findMostEffectiveWound); private _targetWound = [_patient, _bandage, _bodyPart] call FUNC(findMostEffectiveWound);
_targetWound params ["_wound", "_woundIndex", "_effectiveness"]; _targetWound params ["_wound", "_woundIndex", "_effectiveness"];
// Everything is patched up on this body part already // Everything is patched up on this body part already
if (_effectiveness == -1) exitWith {}; if (_effectiveness == -1) exitWith {};
// Find the impact this bandage has and reduce the amount this injury is present // Find the impact this bandage has and reduce the amount this injury is present
private _amountOf = _wound select 2; private _amountOf = _wound select 1;
private _impact = _effectiveness min _amountOf; private _impact = _effectiveness min _amountOf;
_amountOf = _amountOf - _impact; _amountOf = _amountOf - _impact;
_wound set [2, _amountOf]; _wound set [1, _amountOf];
_openWounds set [_woundIndex, _wound]; _woundsOnPart set [_woundIndex, _wound];
_patient setVariable [VAR_OPEN_WOUNDS, _openWounds, true]; _patient setVariable [VAR_OPEN_WOUNDS, _openWounds, true];
@ -47,20 +46,28 @@ _patient setVariable [VAR_OPEN_WOUNDS, _openWounds, true];
// Handle the reopening of bandaged wounds // Handle the reopening of bandaged wounds
if (_impact > 0 && {GVAR(advancedBandages) == 2}) then { if (_impact > 0 && {GVAR(advancedBandages) == 2}) then {
[_patient, _impact, _partIndex, _woundIndex, _wound, _bandage] call FUNC(handleBandageOpening); [_patient, _impact, _bodyPart, _woundIndex, _wound, _bandage] call FUNC(handleBandageOpening);
}; };
// Check if we fixed limping from this treatment // Check if we fixed limping from this treatment
if (EGVAR(medical,limping) == 1 && {_partIndex > 3} && {_amountOf <= 0} && {_patient getVariable [QEGVAR(medical,isLimping), false]}) then { if (
EGVAR(medical,limping) == 1
&& {_bodyPart isEqualTo "leftleg" || _bodyPart isEqualTo "rightleg"}
&& {_amountOf <= 0}
&& {_patient getVariable [QEGVAR(medical,isLimping), false]}
) then {
[_patient] call EFUNC(medical_engine,updateDamageEffects); [_patient] call EFUNC(medical_engine,updateDamageEffects);
}; };
if (GVAR(clearTrauma) == 2) then { if (GVAR(clearTrauma) == 2) then {
TRACE_2("clearTrauma - clearing trauma after bandage",_partIndex,_openWounds); TRACE_2("clearTrauma - clearing trauma after bandage",_bodyPart,_openWounds);
private _treatedDamageOf = (_wound select 4) * _impact; private _partIndex = ALL_BODY_PARTS find _bodyPart;
private _treatedDamageOf = (_wound select 3) * _impact;
private _bodyPartDamage = _patient getVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0]]; private _bodyPartDamage = _patient getVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0]];
private _newDam = (_bodyPartDamage select _partIndex) - _treatedDamageOf; private _newDam = (_bodyPartDamage select _partIndex) - _treatedDamageOf;
if (_newDam < 0.05) then { // Prevent obscenely small damage from lack of floating precision
// Prevent obscenely small damage from lack of floating precision
if (_newDam < 0.05) then {
_bodyPartDamage set [_partIndex, 0]; _bodyPartDamage set [_partIndex, 0];
} else { } else {
_bodyPartDamage set [_partIndex, _newDam]; _bodyPartDamage set [_partIndex, _newDam];
@ -68,16 +75,17 @@ if (GVAR(clearTrauma) == 2) then {
_patient setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true]; _patient setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true];
TRACE_2("clearTrauma - healed damage",_partIndex,_treatedDamageOf); TRACE_2("clearTrauma - healed damage",_partIndex,_treatedDamageOf);
switch (_partIndex) do { switch (_bodyPart) do {
case 0: { [_patient, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "head": { [_patient, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
case 1: { [_patient, false, true, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "body": { [_patient, false, true, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
case 2; case "leftarm";
case 3: { [_patient, false, false, true, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "rightarm": { [_patient, false, false, true, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
default { [_patient, false, false, false, true] call EFUNC(medical_engine,updateBodyPartVisuals); }; default { [_patient, false, false, false, true] call EFUNC(medical_engine,updateBodyPartVisuals); };
}; };
}; };
if (_amountOf <= 0) then { // Reset treatment condition cache for nearby players if we stopped all bleeding // Reset treatment condition cache for nearby players if we stopped all bleeding
if (_amountOf <= 0) then {
private _nearPlayers = (_patient nearEntities ["CAManBase", 6]) select {_x call EFUNC(common,isPlayer)}; private _nearPlayers = (_patient nearEntities ["CAManBase", 6]) select {_x call EFUNC(common,isPlayer)};
TRACE_1("clearConditionCaches: bandage",_nearPlayers); TRACE_1("clearConditionCaches: bandage",_nearPlayers);
[QEGVAR(interact_menu,clearConditionCaches), [], _nearPlayers] call CBA_fnc_targetEvent; [QEGVAR(interact_menu,clearConditionCaches), [], _nearPlayers] call CBA_fnc_targetEvent;

View File

@ -1,6 +1,6 @@
#include "script_component.hpp" #include "script_component.hpp"
/* /*
* Author: SilentSpike * Author: kymckay
* Prevents bandage actions from showing if selected body part isn't bleeding. * Prevents bandage actions from showing if selected body part isn't bleeding.
* Toggles between showing all or only basic bandage action for advanced setting. * Toggles between showing all or only basic bandage action for advanced setting.
* *
@ -20,6 +20,7 @@
*/ */
params ["_medic", "_patient", "_bodyPart", "_bandage"]; params ["_medic", "_patient", "_bodyPart", "_bandage"];
_bodyPart = toLower _bodyPart;
// If patient is swimming, don't allow bandage actions. // If patient is swimming, don't allow bandage actions.
if (_patient call EFUNC(common,isSwimming)) exitWith {false}; if (_patient call EFUNC(common,isSwimming)) exitWith {false};
@ -27,16 +28,15 @@ if (_patient call EFUNC(common,isSwimming)) exitWith {false};
// Bandage type and bandage setting XNOR to show only active actions // Bandage type and bandage setting XNOR to show only active actions
if ((_bandage == "BasicBandage") isEqualTo (GVAR(advancedBandages) != 0)) exitWith {false}; if ((_bandage == "BasicBandage") isEqualTo (GVAR(advancedBandages) != 0)) exitWith {false};
private _index = ALL_BODY_PARTS find toLower _bodyPart;
private _canBandage = false; private _canBandage = false;
{ {
_x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; _x params ["", "_amountOf", "_bleeding"];
// If any single wound on the bodypart is bleeding bandaging can go ahead // If any single wound on the bodypart is bleeding bandaging can go ahead
if (_bodyPartN == _index && {_amountOf * _bleeding > 0}) exitWith { if (_amountOf * _bleeding > 0) exitWith {
_canBandage = true; _canBandage = true;
}; };
} forEach GET_OPEN_WOUNDS(_patient); } forEach ((GET_OPEN_WOUNDS(_patient)) getOrDefault [_bodyPart, []]);
_canBandage _canBandage

View File

@ -20,4 +20,4 @@ params ["_medic", "_patient"];
if ((GVAR(consumeSurgicalKit) == 2) && {!([_medic, _patient, ["ACE_suture"]] call FUNC(hasItem))}) exitWith {false}; if ((GVAR(consumeSurgicalKit) == 2) && {!([_medic, _patient, ["ACE_suture"]] call FUNC(hasItem))}) exitWith {false};
(_patient call FUNC(getStitchableWounds) isNotEqualTo []) count (_patient call FUNC(getStitchableWounds)) > 0

View File

@ -28,10 +28,9 @@ if (vehicle _medic != _medic || {vehicle _patient != _patient}) exitWith {};
// Determine if treated body part is bleeding // Determine if treated body part is bleeding
private _index = ALL_BODY_PARTS find toLower _bodyPart; private _index = ALL_BODY_PARTS find toLower _bodyPart;
private _isBleeding = GET_OPEN_WOUNDS(_patient) findIf { private _isBleeding = (GET_OPEN_WOUNDS(_patient) get _bodyPart) findIf {
_x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; _x params ["", "_amountOf", "_bleeding"];
_amountOf * _bleeding > 0
_bodyPartN == _index && {_amountOf * _bleeding > 0}
} != -1; } != -1;
// Get litter config for the treatment // Get litter config for the treatment

View File

@ -1,12 +1,12 @@
#include "script_component.hpp" #include "script_component.hpp"
/* /*
* Author: SilentSpike * Author: kymckay
* Finds the wound most effective to bandage on the given bodypart of the patient for the given bandage type. * Finds the wound most effective to bandage on the given bodypart of the patient for the given bandage type.
* *
* Arguments: * Arguments:
* 0: Patient <OBJECT> * 0: Patient <OBJECT>
* 1: Treatment classname <STRING> * 1: Treatment classname <STRING>
* 2: Body part index <NUMBER> * 2: Body part <STRING>
* *
* Return Value: * Return Value:
* [Wound, Index, Effectiveness] <ARRAY, NUMBER, NUMBER> * [Wound, Index, Effectiveness] <ARRAY, NUMBER, NUMBER>
@ -14,7 +14,7 @@
* Public: No * Public: No
*/ */
params ["_patient", "_bandage", "_partIndex"]; params ["_patient", "_bandage", "_bodyPart"];
// Get the default effectiveness for the used bandage // Get the default effectiveness for the used bandage
private _config = configFile >> QUOTE(ADDON) >> "Bandaging"; private _config = configFile >> QUOTE(ADDON) >> "Bandaging";
@ -29,7 +29,7 @@ if (isClass (_config >> _bandage)) then {
}; };
// Iterate over open wounds to find the most effective target // Iterate over open wounds to find the most effective target
private _openWounds = GET_OPEN_WOUNDS(_patient); private _openWounds = GET_OPEN_WOUNDS(_patient) getOrDefault [_bodyPart, []];
if (_openWounds isEqualTo []) exitWith { [EMPTY_WOUND, -1, -1] }; if (_openWounds isEqualTo []) exitWith { [EMPTY_WOUND, -1, -1] };
private _wound = EMPTY_WOUND; private _wound = EMPTY_WOUND;
@ -37,10 +37,8 @@ private _woundIndex = -1;
private _effectivenessFound = -1; private _effectivenessFound = -1;
{ {
_x params ["_classID", "_partIndexN", "_amountOf", "_bleeding", "_damage"]; _x params ["_classID", "_amountOf", "_bleeding", "_damage"];
// Ignore wounds on other bodyparts
if (_partIndexN == _partIndex) then {
private _woundEffectiveness = _effectiveness; private _woundEffectiveness = _effectiveness;
// Select the classname from the wound classname storage // Select the classname from the wound classname storage
@ -61,12 +59,11 @@ private _effectivenessFound = -1;
}; };
// Track most effective found so far // Track most effective found so far
if (_woundEffectiveness * _amountOf * _bleeding > _effectivenessFound * (_wound select 2) * (_wound select 3)) then { if (_woundEffectiveness * _amountOf * _bleeding > _effectivenessFound * (_wound select 1) * (_wound select 2)) then {
_effectivenessFound = _woundEffectiveness; _effectivenessFound = _woundEffectiveness;
_woundIndex = _forEachIndex; _woundIndex = _forEachIndex;
_wound = _x; _wound = _x;
}; };
};
} forEach _openWounds; } forEach _openWounds;
[_wound, _woundIndex, _effectivenessFound] [_wound, _woundIndex, _effectivenessFound]

View File

@ -50,9 +50,9 @@ _patient setVariable [VAR_TOURNIQUET, DEFAULT_TOURNIQUET_VALUES, true];
_patient setVariable [QGVAR(occludedMedications), nil, true]; _patient setVariable [QGVAR(occludedMedications), nil, true];
// Wounds and Injuries // Wounds and Injuries
_patient setVariable [VAR_OPEN_WOUNDS, [], true]; _patient setVariable [VAR_OPEN_WOUNDS, createHashMap, true];
_patient setVariable [VAR_BANDAGED_WOUNDS, [], true]; _patient setVariable [VAR_BANDAGED_WOUNDS, createHashMap, true];
_patient setVariable [VAR_STITCHED_WOUNDS, [], true]; _patient setVariable [VAR_STITCHED_WOUNDS, createHashMap, true];
_patient setVariable [QEGVAR(medical,isLimping), false, true]; _patient setVariable [QEGVAR(medical,isLimping), false, true];
_patient setVariable [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES, true]; _patient setVariable [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES, true];

View File

@ -1,6 +1,6 @@
#include "script_component.hpp" #include "script_component.hpp"
/* /*
* Author: SilentSpike * Author: kymckay
* Calculates the time to bandage a wound based on it's size, the patient and the medic. * Calculates the time to bandage a wound based on it's size, the patient and the medic.
* *
* Arguments: * Arguments:
@ -18,19 +18,19 @@
* Public: No * Public: No
*/ */
params ["_medic", "_patient", "_bodypart", "_bandage"]; params ["_medic", "_patient", "_bodyPart", "_bandage"];
private _partIndex = ALL_BODY_PARTS find toLower _bodyPart; private _partIndex = ALL_BODY_PARTS find toLower _bodyPart;
if (_partIndex < 0) exitWith { ERROR_1("invalid partIndex - %1",_this); 0 }; if (_partIndex < 0) exitWith { ERROR_1("invalid partIndex - %1",_this); 0 };
private _targetWound = [_patient, _bandage, _partIndex] call FUNC(findMostEffectiveWound); private _targetWound = [_patient, _bandage, _bodyPart] call FUNC(findMostEffectiveWound);
_targetWound params ["_wound", "_woundIndex", "_effectiveness"]; _targetWound params ["_wound", "_woundIndex", "_effectiveness"];
TRACE_3("findMostEffectiveWound",_wound,_woundIndex,_effectiveness); TRACE_3("findMostEffectiveWound",_wound,_woundIndex,_effectiveness);
// Everything is patched up on this body part already // Everything is patched up on this body part already
if (_wound isEqualTo EMPTY_WOUND) exitWith {0}; if (_wound isEqualTo EMPTY_WOUND) exitWith {0};
_wound params ["_classID", "", "_amountOf", "_bloodloss", "_damage"]; _wound params ["_classID", "_amountOf", "_bloodloss", "_damage"];
private _category = (_classID % 10); private _category = (_classID % 10);
// Base bandage time is based on wound size and remaining percentage // Base bandage time is based on wound size and remaining percentage

View File

@ -1,6 +1,6 @@
#include "script_component.hpp" #include "script_component.hpp"
/* /*
* Author: mharis001 * Author: kymckay
* Calculates the Surgical Kit treatment time based on the amount of stitchable wounds. * Calculates the Surgical Kit treatment time based on the amount of stitchable wounds.
* *
* Arguments: * Arguments:
@ -18,4 +18,11 @@
params ["", "_patient"]; params ["", "_patient"];
count (_patient call FUNC(getStitchableWounds)) * GVAR(woundStitchTime)
private _stitchableTotal = 0;
{
_stitchableTotal = _stitchableTotal + count _y;
} forEach (_patient call FUNC(getStitchableWounds));
_stitchableTotal * GVAR(woundStitchTime)

View File

@ -1,14 +1,14 @@
#include "script_component.hpp" #include "script_component.hpp"
/* /*
* Author: mharis001 * Author: kymckay
* Returns a list of all the stitchable wounds that the given unit has. * Returns a hashmap of the stitchable wounds that the given unit has on each body part.
* A stitchable wound is a bandaged wound on a body part that does not have any bleeding wounds. * A stitchable wound is a bandaged wound on a body part that does not have any bleeding wounds.
* *
* Arguments: * Arguments:
* 0: Unit <OBJECT> * 0: Unit <OBJECT>
* *
* Return Value: * Return Value:
* Stitchable Wounds <ARRAY> * Stitchable Wounds <HASHMAP>
* *
* Example: * Example:
* [player] call ace_medical_treatment_fnc_getStitchableWounds * [player] call ace_medical_treatment_fnc_getStitchableWounds
@ -18,16 +18,25 @@
params ["_unit"]; params ["_unit"];
private _bleedingBodyParts = GET_OPEN_WOUNDS(_unit) select { // First determine which body parts have a bleeding wound
_x params ["", "", "_amountOf", "_bleedingRate"]; private _bleedingBodyParts = createHashMap;
{
private _isBleeding = _y findIf {
_x params ["", "_amountOf", "_bleedingRate"];
_amountOf > 0 && {_bleedingRate > 0} _amountOf > 0 && {_bleedingRate > 0}
} apply { } != -1;
_x select 1
};
GET_BANDAGED_WOUNDS(_unit) select { if (_isBleeding) then {
_x params ["", "_bodyPartN"]; _bleedingBodyParts set [_x, true];
};
} forEach GET_OPEN_WOUNDS(_unit);
!(_bodyPartN in _bleedingBodyParts) // Any bandaged wound on a body part not bleeding is stitchable
} private _stitchableWounds = createHashMap;
{
if (!(_x in _bleedingBodyParts) && {_y isNotEqualTo []}) then {
_stitchableWounds set [_x, _y];
};
} forEach GET_BANDAGED_WOUNDS(_unit);
_stitchableWounds

View File

@ -6,7 +6,7 @@
* Arguments: * Arguments:
* 0: The target <OBJECT> * 0: The target <OBJECT>
* 1: The impact <NUMBER> * 1: The impact <NUMBER>
* 2: Selection part number <NUMBER> * 2: Body part <STRING>
* 3: Injury index <NUMBER> * 3: Injury index <NUMBER>
* 4: Injury <ARRAY> * 4: Injury <ARRAY>
* 5: Used Bandage type <STRING> * 5: Used Bandage type <STRING>
@ -20,7 +20,7 @@
params ["_target", "_impact", "_part", "_injuryIndex", "_injury", "_bandage"]; params ["_target", "_impact", "_part", "_injuryIndex", "_injury", "_bandage"];
TRACE_6("handleBandageOpening",_target,_impact,_part,_injuryIndex,_injury,_bandage); TRACE_6("handleBandageOpening",_target,_impact,_part,_injuryIndex,_injury,_bandage);
_injury params ["_classID", "_bodyPartN"]; _injury params ["_classID"];
private _className = EGVAR(medical_damage,woundClassNamesComplex) select _classID; private _className = EGVAR(medical_damage,woundClassNamesComplex) select _classID;
private _reopeningChance = DEFAULT_BANDAGE_REOPENING_CHANCE; private _reopeningChance = DEFAULT_BANDAGE_REOPENING_CHANCE;
@ -61,19 +61,19 @@ TRACE_5("configs",_bandage,_className,_reopeningChance,_reopeningMinDelay,_reope
private _bandagedWounds = GET_BANDAGED_WOUNDS(_target); private _bandagedWounds = GET_BANDAGED_WOUNDS(_target);
private _exist = false; private _exist = false;
{ {
_x params ["_id", "_partN", "_amountOf"]; _x params ["_id", "_amountOf"];
if (_id == _classID && {_partN == _bodyPartN}) exitWith { if (_id == _classID) exitWith {
_x set [2, _amountOf + _impact]; _x set [1, _amountOf + _impact];
TRACE_2("adding to existing bandagedWound",_id,_partN); TRACE_2("adding to existing bandagedWound",_id,_part);
_exist = true; _exist = true;
}; };
} forEach _bandagedWounds; } forEach (_bandagedWounds getOrDefault [_part, []]);
if (!_exist) then { if (!_exist) then {
TRACE_2("adding new bandagedWound",_classID,_bodyPartN); TRACE_2("adding new bandagedWound",_classID,_part);
private _bandagedInjury = +_injury; private _bandagedInjury = +_injury;
_bandagedInjury set [2, _impact]; _bandagedInjury set [1, _impact];
_bandagedWounds pushBack _bandagedInjury; (_bandagedWounds getOrDefault [_part, [], true]) pushBack _bandagedInjury;
}; };
_target setVariable [VAR_BANDAGED_WOUNDS, _bandagedWounds, true]; _target setVariable [VAR_BANDAGED_WOUNDS, _bandagedWounds, true];
@ -92,42 +92,45 @@ if (random 1 <= _reopeningChance * GVAR(woundReopenChance)) then {
TRACE_5("reopen delay finished",_target,_impact,_part,_injuryIndex,_injury); TRACE_5("reopen delay finished",_target,_impact,_part,_injuryIndex,_injury);
private _openWounds = GET_OPEN_WOUNDS(_target); private _openWounds = GET_OPEN_WOUNDS(_target);
if (count _openWounds - 1 < _injuryIndex) exitWith { TRACE_2("index bounds",_injuryIndex,count _openWounds); }; private _woundsOnPart = _openWounds getOrDefault [_part, []];
if (count _woundsOnPart - 1 < _injuryIndex) exitWith { TRACE_2("index bounds",_injuryIndex,count _woundsOnPart); };
_injury params ["_classID", "_bodyPartN"]; _injury params ["_classID"];
private _selectedInjury = _openWounds select _injuryIndex; private _selectedInjury = _woundsOnPart select _injuryIndex;
_selectedInjury params ["_selClassID", "_selBodyPart", "_selAmmount"]; _selectedInjury params ["_selClassID", "_selAmmount"];
if ((_selClassID == _classID) && {_selBodyPart == _bodyPartN}) then { // matching the IDs if (_selClassID == _classID) then { // matching the IDs
private _bandagedWounds = GET_BANDAGED_WOUNDS(_target); private _bandagedWounds = GET_BANDAGED_WOUNDS(_target);
private _exist = false; private _exist = false;
{ {
_x params ["_id", "_partN", "_amountOf"]; _x params ["_id", "_amountOf"];
if ((_id == _classID) && {_partN == _bodyPartN}) exitWith { if (_id == _classID) exitWith {
TRACE_2("bandagedWound exists",_id,_classID); TRACE_2("bandagedWound exists",_id,_classID);
_x set [2, 0 max (_amountOf - _impact)]; _x set [1, 0 max (_amountOf - _impact)];
_exist = true; _exist = true;
}; };
} forEach _bandagedWounds; } forEach (_bandagedWounds getOrDefault [_part, []]);
if (_exist) then { if (_exist) then {
TRACE_2("Reopening Wound",_bandagedWounds,_openWounds); TRACE_2("Reopening Wound",_bandagedWounds,_openWounds);
_selectedInjury set [2, _selAmmount + _impact]; _selectedInjury set [1, _selAmmount + _impact];
_target setVariable [VAR_BANDAGED_WOUNDS, _bandagedWounds, true]; _target setVariable [VAR_BANDAGED_WOUNDS, _bandagedWounds, true];
_target setVariable [VAR_OPEN_WOUNDS, _openWounds, true]; _target setVariable [VAR_OPEN_WOUNDS, _openWounds, true];
[_target] call EFUNC(medical_status,updateWoundBloodLoss); [_target] call EFUNC(medical_status,updateWoundBloodLoss);
private _partIndex = ALL_BODY_PARTS find _part;
// Re-add trauma and damage visuals // Re-add trauma and damage visuals
if (GVAR(clearTrauma) == 2) then { if (GVAR(clearTrauma) == 2) then {
private _injuryDamage = (_selectedInjury select 4) * _impact; private _injuryDamage = (_selectedInjury select 4) * _impact;
private _bodyPartDamage = _target getVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0]]; private _bodyPartDamage = _target getVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0]];
private _newDam = (_bodyPartDamage select _selBodyPart) + _injuryDamage; private _newDam = (_bodyPartDamage select _partIndex) + _injuryDamage;
_bodyPartDamage set [_selBodyPart, _newDam]; _bodyPartDamage set [_partIndex, _newDam];
_target setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true]; _target setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true];
switch (_selBodyPart) do { switch (_partIndex) do {
case 0: { [_target, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case 0: { [_target, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
case 1: { [_target, false, true, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case 1: { [_target, false, true, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
case 2; case 2;
@ -137,12 +140,12 @@ if (random 1 <= _reopeningChance * GVAR(woundReopenChance)) then {
}; };
// Check if we gained limping from this wound re-opening // Check if we gained limping from this wound re-opening
if ((EGVAR(medical,limping) == 1) && {_bodyPartN > 3}) then { if ((EGVAR(medical,limping) == 1) && {_partIndex > 3}) then {
[_target] call EFUNC(medical_engine,updateDamageEffects); [_target] call EFUNC(medical_engine,updateDamageEffects);
}; };
}; };
} else { } else {
TRACE_3("no match",_selectedInjury,_classID,_bodyPartN); TRACE_3("no match",_selectedInjury,_classID,_part);
}; };
}, [_target, _impact, _part, _injuryIndex, +_injury], _delay] call CBA_fnc_waitAndExecute; }, [_target, _impact, _part, _injuryIndex, +_injury], _delay] call CBA_fnc_waitAndExecute;
}; };

View File

@ -25,44 +25,46 @@ _args params ["_medic", "_patient"];
private _stitchableWounds = _patient call FUNC(getStitchableWounds); private _stitchableWounds = _patient call FUNC(getStitchableWounds);
// Stop treatment if there are no wounds that can be stitched remaining // Stop treatment if there are no wounds that can be stitched remaining
if (_stitchableWounds isEqualTo []) exitWith {false}; if (_stitchableWounds isEqualTo createHashMap) exitWith {false};
// Not enough time has elapsed to stitch a wound // Not enough time has elapsed to stitch a wound
if (_totalTime - _elapsedTime > (count _stitchableWounds - 1) * GVAR(woundStitchTime)) exitWith {true}; if (_totalTime - _elapsedTime > ([_patient, _patient] call FUNC(getStitchTime)) - GVAR(woundStitchTime)) exitWith {true};
private _bandagedWounds = GET_BANDAGED_WOUNDS(_patient); private _bandagedWounds = GET_BANDAGED_WOUNDS(_patient);
private _stitchedWounds = GET_STITCHED_WOUNDS(_patient); private _stitchedWounds = GET_STITCHED_WOUNDS(_patient);
// Remove the first stitchable wound from the bandaged wounds // Remove the first stitchable wound from the bandaged wounds
private _treatedWound = _bandagedWounds deleteAt (_bandagedWounds find (_stitchableWounds select 0)); private _bodyPart = (keys _stitchableWounds) select 0;
_treatedWound params ["_treatedID", "_treatedBodyPartN", "_treatedAmountOf", "", "_treatedDamageOf"]; private _bandagedWoundsOnPart = _bandagedWounds get _bodyPart;
private _treatedWound = _bandagedWoundsOnPart deleteAt (count _bandagedWoundsOnPart - 1);
_treatedWound params ["_treatedID", "_treatedAmountOf", "", "_treatedDamageOf"];
// Check if we need to add a new stitched wound or increase the amount of an existing one // Check if we need to add a new stitched wound or increase the amount of an existing one
private _woundIndex = _stitchedWounds findIf { private _woundIndex = (_stitchedWounds getOrDefault [_bodyPart, []]) findIf {
_x params ["_classID", "_bodyPartN"]; _x params ["_classID"];
_classID == _treatedID
_classID == _treatedID && {_bodyPartN == _treatedBodyPartN}
}; };
if (_woundIndex == -1) then { if (_woundIndex == -1) then {
_stitchedWounds pushBack _treatedWound; (_stitchedWounds getOrDefault [_bodyPart, [], true]) pushBack _treatedWound;
} else { } else {
private _wound = _stitchedWounds select _woundIndex; private _wound = (_stitchedWounds get _bodyPart) select _woundIndex;
_wound set [2, (_wound select 2) + _treatedAmountOf]; _wound set [1, (_wound select 1) + _treatedAmountOf];
}; };
if (GVAR(clearTrauma) == 1) then { if (GVAR(clearTrauma) == 1) then {
TRACE_2("clearTrauma - clearing trauma after stitching",_partIndex,_treatedWound); private _partIndex = ALL_BODY_PARTS find _bodyPart;
TRACE_2("clearTrauma - clearing trauma after stitching",_bodyPart,_treatedWound);
private _bodyPartDamage = _patient getVariable [QEGVAR(medical,bodyPartDamage), []]; private _bodyPartDamage = _patient getVariable [QEGVAR(medical,bodyPartDamage), []];
_bodyPartDamage set [_treatedBodyPartN, (_bodyPartDamage select _treatedBodyPartN) - _treatedDamageOf]; _bodyPartDamage set [_partIndex, (_bodyPartDamage select _partIndex) - _treatedDamageOf];
_patient setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true]; _patient setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true];
TRACE_2("clearTrauma - healed damage",_partIndex,_treatedDamageOf); TRACE_2("clearTrauma - healed damage",_bodyPart,_treatedDamageOf);
switch (_treatedBodyPartN) do { switch (_bodyPart) do {
case 0: { [_patient, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "head": { [_patient, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
case 1: { [_patient, false, true, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "body": { [_patient, false, true, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
case 2; case "leftarm";
case 3: { [_patient, false, false, true, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "rightarm": { [_patient, false, false, true, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
default { [_patient, false, false, false, true] call EFUNC(medical_engine,updateBodyPartVisuals); }; default { [_patient, false, false, false, true] call EFUNC(medical_engine,updateBodyPartVisuals); };
}; };
}; };
@ -71,15 +73,19 @@ _patient setVariable [VAR_BANDAGED_WOUNDS, _bandagedWounds, true];
_patient setVariable [VAR_STITCHED_WOUNDS, _stitchedWounds, true]; _patient setVariable [VAR_STITCHED_WOUNDS, _stitchedWounds, true];
// Check if we fixed limping by stitching this wound (only for leg wounds) // Check if we fixed limping by stitching this wound (only for leg wounds)
if (EGVAR(medical,limping) == 2 && {_patient getVariable [QEGVAR(medical,isLimping), false]} && {_treatedBodyPartN > 3}) then { if (
TRACE_3("Updating damage effects",_patient,_treatedBodyPartN,local _patient); EGVAR(medical,limping) == 2
&& {_patient getVariable [QEGVAR(medical,isLimping), false]}
&& {_bodyPart isEqualTo "leftleg" || _bodyPart isEqualTo "rightleg"}
) then {
TRACE_3("Updating damage effects",_patient,_bodyPart,local _patient);
[QEGVAR(medical_engine,updateDamageEffects), _patient, _patient] call CBA_fnc_targetEvent; [QEGVAR(medical_engine,updateDamageEffects), _patient, _patient] call CBA_fnc_targetEvent;
}; };
// Consume a suture for the next wound if one exists, stop stitching if none are left // Consume a suture for the next wound if one exists, stop stitching if none are left
if (GVAR(consumeSurgicalKit) == 2) then { if (GVAR(consumeSurgicalKit) == 2) then {
// Don't consume a suture if there are no more wounds to stitch // Don't consume a suture if there are no more wounds to stitch
if (count _stitchableWounds == 1) exitWith {false}; if (count (values _stitchableWounds) isEqualTo 1) exitWith {false};
([_medic, _patient, ["ACE_suture"]] call FUNC(useItem)) params ["_user"]; ([_medic, _patient, ["ACE_suture"]] call FUNC(useItem)) params ["_user"];
!isNull _user !isNull _user
} else { } else {