Medical Treatment - Add setting to rollover bandage effectiveness (#8426)

* use leftover bandage effectiveness on other wounds in same limb

variable naming

* Comments

Co-authored-by: Kyle Mckay <5459452+kymckay@users.noreply.github.com>

* variable naming, fix part index check in findMostEffectiveWound

trace 4

* fix function header

* Account for multiple wounds in time calculation

* fix trace, remove systemChat

* update for hashmaps

* compile cache

* Update addons/medical_treatment/functions/fnc_findMostEffectiveWounds.sqf

Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>

* add bandage effectiveness setting

* stupid

* remove limit on bandageRemaining

* Update addons/medical_treatment/functions/fnc_bandageLocal.sqf

Co-authored-by: BrettMayson <brett@mayson.io>

* Update fnc_getBandageTime.sqf

* Update fnc_bandageLocal.sqf

* fix header / debug

---------

Co-authored-by: Salluci <69561145+Salluci@users.noreply.github.com>
Co-authored-by: Kyle Mckay <5459452+kymckay@users.noreply.github.com>
Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>
Co-authored-by: BrettMayson <brett@mayson.io>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>
This commit is contained in:
Grim 2023-09-10 02:06:15 -04:00 committed by GitHub
parent f7e247c87e
commit de8940af9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 45 deletions

View File

@ -25,7 +25,7 @@ PREP(cprStart);
PREP(createLitter); PREP(createLitter);
PREP(createLitterServer); PREP(createLitterServer);
PREP(diagnose); PREP(diagnose);
PREP(findMostEffectiveWound); PREP(findMostEffectiveWounds);
PREP(fullHeal); PREP(fullHeal);
PREP(fullHealLocal); PREP(fullHealLocal);
PREP(getBandageTime); PREP(getBandageTime);

View File

@ -25,35 +25,46 @@ private _openWounds = GET_OPEN_WOUNDS(_patient);
private _woundsOnPart = _openWounds getOrDefault [_bodyPart, []]; private _woundsOnPart = _openWounds getOrDefault [_bodyPart, []];
if (_woundsOnPart isEqualTo []) exitWith {}; if (_woundsOnPart isEqualTo []) exitWith {};
// Figure out which injury for this bodypart is the best choice to bandage // Figure out which injuries for this bodypart are the best choice to bandage
// TODO also use up the remainder on left over injuries private _targetWounds = [_patient, _bandage, _bodyPart, GVAR(bandageEffectiveness)] call FUNC(findMostEffectiveWounds);
private _targetWound = [_patient, _bandage, _bodyPart] call FUNC(findMostEffectiveWound);
_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 (count _targetWounds == 0) exitWith {};
// Find the impact this bandage has and reduce the amount this injury is present private _treatedDamage = 0;
private _amountOf = _wound select 1; private _clearConditionCache = false;
private _impact = _effectiveness min _amountOf;
_amountOf = _amountOf - _impact; {
_wound set [1, _amountOf]; private _wound = _x;
_woundsOnPart set [_woundIndex, _wound]; _wound params ["_classID", "_amountOf", "_bleeding", "_damage"];
_y params ["_effectiveness", "_woundIndex", "_impact"];
// clear condition cache if we stopped all bleeding for this injury
if (!_clearConditionCache) then {
_clearConditionCache = (_effectiveness >= _amountOf);
};
// Reduce the amount this injury is present
(_woundsOnPart select _woundIndex) set [1, _amountOf - _impact];
// Store treated damage for clearing trauma
_treatedDamage = _treatedDamage + (_impact * _damage);
// Handle reopening bandaged wounds
if (_impact > 0 && {GVAR(advancedBandages) == 2}) then {
[_patient, _impact, _bodyPart, _woundIndex, _wound, _bandage] call FUNC(handleBandageOpening);
};
} forEach _targetWounds;
_patient setVariable [VAR_OPEN_WOUNDS, _openWounds, true]; _patient setVariable [VAR_OPEN_WOUNDS, _openWounds, true];
[_patient] call EFUNC(medical_status,updateWoundBloodLoss); [_patient] call EFUNC(medical_status,updateWoundBloodLoss);
// Handle the reopening of bandaged wounds
if (_impact > 0 && {GVAR(advancedBandages) == 2}) then {
[_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 ( if (
EGVAR(medical,limping) == 1 EGVAR(medical,limping) == 1
&& {_bodyPart isEqualTo "leftleg" || _bodyPart isEqualTo "rightleg"} && {_clearConditionCache}
&& {_amountOf <= 0} && {_bodyPart in ["leftleg", "rightleg"]}
&& {_patient getVariable [QEGVAR(medical,isLimping), false]} && {_patient getVariable [QEGVAR(medical,isLimping), false]}
) then { ) then {
[_patient] call EFUNC(medical_engine,updateDamageEffects); [_patient] call EFUNC(medical_engine,updateDamageEffects);
@ -62,9 +73,8 @@ if (
if (GVAR(clearTrauma) == 2) then { if (GVAR(clearTrauma) == 2) then {
TRACE_2("clearTrauma - clearing trauma after bandage",_bodyPart,_openWounds); TRACE_2("clearTrauma - clearing trauma after bandage",_bodyPart,_openWounds);
private _partIndex = ALL_BODY_PARTS find _bodyPart; 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) - _treatedDamage;
// Prevent obscenely small damage from lack of floating precision // Prevent obscenely small damage from lack of floating precision
if (_newDam < 0.05) then { if (_newDam < 0.05) then {
@ -73,7 +83,7 @@ if (GVAR(clearTrauma) == 2) then {
_bodyPartDamage set [_partIndex, _newDam]; _bodyPartDamage set [_partIndex, _newDam];
}; };
_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,_treatedDamage);
switch (_bodyPart) do { switch (_bodyPart) do {
case "head": { [_patient, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); }; case "head": { [_patient, true, false, false, false] call EFUNC(medical_engine,updateBodyPartVisuals); };
@ -85,7 +95,7 @@ if (GVAR(clearTrauma) == 2) 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 { if (_clearConditionCache) 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,20 +1,26 @@
#include "script_component.hpp" #include "script_component.hpp"
/* /*
* Author: kymckay * Author: kymckay, LinkIsGrim
* Finds the wound most effective to bandage on the given bodypart of the patient for the given bandage type. * Finds the most effective wounds to bandage on the given bodypart of the patient for the given bandage type and remaining overall bandage effectiveness.
* *
* Arguments: * Arguments:
* 0: Patient <OBJECT> * 0: Patient <OBJECT>
* 1: Treatment classname <STRING> * 1: Treatment classname <STRING>
* 2: Body part <STRING> * 2: Body part <STRING>
* 3: Amount of bandage remaining <NUMBER> (default: 1)
* 4: Wounds found from prior runs of the function <HASHMAP> (default: Empty Hashmap)
* *
* Return Value: * Return Value:
* [Wound, Index, Effectiveness] <ARRAY, NUMBER, NUMBER> * [Wound, [Effectiveness, Index, Impact]] <HASHMAP> of <ARRAY>:<NUMBER, NUMBER, NUMBER>
*
* Example:
* [cursorObject, "FieldDressing", "rightleg"] call ace_medical_treatment_fnc_findMostEffectiveWounds
* *
* Public: No * Public: No
*/ */
params ["_patient", "_bandage", "_bodyPart"]; params ["_patient", "_bandage", "_bodyPart", ["_bandageRemaining", 1], ["_foundWounds", createHashMap]];
TRACE_1("findMostEffectiveWounds",count _foundWounds);
// 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";
@ -30,13 +36,16 @@ 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) getOrDefault [_bodyPart, []]; private _openWounds = GET_OPEN_WOUNDS(_patient) getOrDefault [_bodyPart, []];
if (_openWounds isEqualTo []) exitWith { [EMPTY_WOUND, -1, -1] }; if (_openWounds isEqualTo []) exitWith {_foundWounds};
private _wound = EMPTY_WOUND; private _wound = EMPTY_WOUND;
private _woundIndex = -1; private _woundIndex = -1;
private _effectivenessFound = -1; private _effectivenessFound = -1;
private _impactFound = -1;
{ {
// Ignore iterated wounds
if (_x in _foundWounds) then {continue};
_x params ["_classID", "_amountOf", "_bleeding", "_damage"]; _x params ["_classID", "_amountOf", "_bleeding", "_damage"];
private _woundEffectiveness = _effectiveness; private _woundEffectiveness = _effectiveness;
@ -52,18 +61,32 @@ private _effectivenessFound = -1;
_woundEffectiveness = getNumber (_woundTreatmentConfig >> "effectiveness"); _woundEffectiveness = getNumber (_woundTreatmentConfig >> "effectiveness");
}; };
} else { } else {
// Basic medical bandage just has a base level config (same effectivenes for all wound types) // Basic medical bandage just has a base level config (same effectiveness for all wound types)
if (_bandage != "BasicBandage") then { if (_bandage != "BasicBandage") then {
WARNING_2("No config for wound type [%1] config base [%2]",_className,_config); WARNING_2("No config for wound type [%1] config base [%2]",_className,_config);
}; };
}; };
// Multiply by bandageRemaining in case this is a rollover
_woundEffectiveness = _woundEffectiveness * _bandageRemaining;
// Track most effective found so far // Track most effective found so far
if (_woundEffectiveness * _amountOf * _bleeding > _effectivenessFound * (_wound select 1) * (_wound select 2)) then { if ((_woundEffectiveness * _amountOf * _bleeding) > (_effectivenessFound * (_wound select 1) * (_wound select 2))) then {
_effectivenessFound = _woundEffectiveness; _effectivenessFound = _woundEffectiveness;
_impactFound = _amountOf min _effectivenessFound;
_woundIndex = _forEachIndex; _woundIndex = _forEachIndex;
_wound = _x; _wound = _x;
}; };
} forEach _openWounds; } forEach _openWounds;
[_wound, _woundIndex, _effectivenessFound] if (_woundIndex isEqualTo -1) exitWith {_foundWounds};
_bandageRemaining = _bandageRemaining - (_impactFound/_effectivenessFound);
_foundWounds set [_wound, [_effectivenessFound, _woundIndex, _impactFound]];
// recursion, run function again to find next most effective wound
if (GVAR(bandageRollover) && {_bandageRemaining > 0}) then {
[_patient, _bandage, _bodyPart, _bandageRemaining, _foundWounds] call FUNC(findMostEffectiveWounds);
};
_foundWounds // return

View File

@ -13,7 +13,7 @@
* Treatment Time <NUMBER> * Treatment Time <NUMBER>
* *
* Example: * Example:
* [player, cursorTarget, "Head", "FieldDressing"] call ace_medical_treatment_fnc_getBandageTime * [player, cursorTarget, "head", "FieldDressing"] call ace_medical_treatment_fnc_getBandageTime
* *
* Public: No * Public: No
*/ */
@ -23,24 +23,35 @@ 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, _bodyPart] call FUNC(findMostEffectiveWound); private _targetWounds = [_patient, _bandage, _bodyPart] call FUNC(findMostEffectiveWounds);
_targetWound params ["_wound", "_woundIndex", "_effectiveness"]; TRACE_1("findMostEffectiveWounds",_targetWounds);
TRACE_3("findMostEffectiveWound",_wound,_woundIndex,_effectiveness);
private _woundCount = count _targetWounds;
// 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 (_woundCount == 0) exitWith {0};
_wound params ["_classID", "_amountOf", "_bloodloss", "_damage"];
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
private _bandageTime = [BANDAGE_TIME_S, BANDAGE_TIME_M, BANDAGE_TIME_L] select _category; private _bandageTimesArray = [BANDAGE_TIME_S, BANDAGE_TIME_M, BANDAGE_TIME_L];
private _bandageTime = 0;
// Scale bandage time based on amount left and effectiveness (less time if only a little wound left) {
// Basic bandage treatment will have a very high effectiveness and can be ignored private _wound = _x;
if (GVAR(advancedBandages) != 0) then { _wound params ["_classID", "", "_amountOf"];
_bandageTime = _bandageTime * linearConversion [0, _effectiveness, _amountOf, 0.666, 1, true]; _y params ["_effectiveness", "", "_impact"];
}; private _category = (_classID % 10);
// Base bandage time is based on wound size and remaining percentage
private _woundTime = _bandageTimesArray select _category;
// Scale bandage time based on amount left and effectiveness (less time if only a little wound left)
// Basic bandage treatment will have a very high effectiveness and can be ignored
if (GVAR(advancedBandages != 0)) then {
_woundTime = _woundTime * linearConversion [0, _effectiveness, _impact, 0.666, 1, true];
};
_bandageTime = _bandageTime + _woundTime;
} forEach _targetWounds;
// Medics are more practised at applying bandages // Medics are more practised at applying bandages
if ([_medic] call FUNC(isMedic)) then { if ([_medic] call FUNC(isMedic)) then {
@ -52,6 +63,11 @@ if (_medic == _patient) then {
_bandageTime = _bandageTime + BANDAGE_TIME_MOD_SELF; _bandageTime = _bandageTime + BANDAGE_TIME_MOD_SELF;
}; };
// Bandaging multiple injuries doesn't require opening a new bandage each time
if (_woundCount > 1) then {
_bandageTime = _bandageTime - (2 * _woundCount);
};
TRACE_1("",_bandageTime); TRACE_1("",_bandageTime);
// Nobody can bandage instantly // Nobody can bandage instantly
_bandageTime max 2.25 _bandageTime max 2.25

View File

@ -25,6 +25,24 @@
true true
] call CBA_fnc_addSetting; ] call CBA_fnc_addSetting;
[
QGVAR(bandageRollover),
"CHECKBOX",
[LSTRING(bandageRollover_DisplayName), LSTRING(bandageRollover_Description)],
[ELSTRING(medical,Category), LSTRING(SubCategory_Treatment)],
true,
false // server can force if necessary, otherwise client decides
] call CBA_fnc_addSetting;
[
QGVAR(bandageEffectiveness),
"SLIDER",
[LSTRING(bandageEffectiveness_DisplayName), LSTRING(bandageEffectiveness_Description)],
[ELSTRING(medical,Category), LSTRING(SubCategory_Treatment)],
[0, 5, 1, 2],
true
] call CBA_fnc_addSetting;
[ [
QGVAR(woundReopenChance), QGVAR(woundReopenChance),
"SLIDER", "SLIDER",

View File

@ -4686,5 +4686,17 @@
<Chinesesimp>身体抽搐了一下,可能还没死!</Chinesesimp> <Chinesesimp>身体抽搐了一下,可能还没死!</Chinesesimp>
<Korean>꿈틀대는걸 보니 죽은 것 같지는 않습니다!</Korean> <Korean>꿈틀대는걸 보니 죽은 것 같지는 않습니다!</Korean>
</Key> </Key>
<Key ID="STR_ACE_Medical_Treatment_bandageRollover_DisplayName">
<English>Bandage Rollover</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_bandageRollover_Description">
<English>If enabled, bandages can close different types of wounds on the same body part.\nBandaging multiple injuries will scale bandaging time accordingly.</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_bandageEffectiveness_DisplayName">
<English>Bandage Effectiveness Coefficient</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_bandageEffectiveness_Description">
<English>Determines how effective bandages are at closing wounds.</English>
</Key>
</Package> </Package>
</Project> </Project>