From e56cc0d74e4743388d48bc355a1326dfc51e275b Mon Sep 17 00:00:00 2001 From: PabstMirror Date: Sat, 11 May 2019 23:13:59 -0500 Subject: [PATCH] Limping / Splint Treatment (#6947) * Improve adjustment calcs / wound blood loss / medications fix func descriptions Calc wound blood loss on events reorder includes so scritpmacroMed has global effect trivial optimization for getCardiacOutput Fix var Fix wounds not reopening (nil _category) Fix surgical kit inherting canBandage conditional debug hitpoints Update ACE_Medical_Treatment_Actions.hpp Use woundBleeding for IS_BLEEDING macro rework medication vars comments Reset var in init / fullHeal Update addons/medical_treatment/functions/fnc_onMedicationUsage.sqf Co-Authored-By: PabstMirror * Change wound data array Drop unique id and merge classId and category * Splinting and treatment and gui * Add arm fractures and aim effects * localizations and event * fix * cleanup * Apply suggestions from code review Co-Authored-By: PabstMirror * formating, rename bone images * Apply suggestions from code review Co-Authored-By: PabstMirror * disable calls to extension * Update fnc_onMedicationUsage.sqf * Medical - Skip unneeded setVars on initUnit (#6949) * Medical - Transfer state machine state on locality (#6950) * Medical - Transfer state machine state on locality * Fix feedback isUnconscious var * Exclude AI * Make UAV excludes consistant, formating * Update fnc_treatmentFullHealLocal.sqf * reset fractures on respawn --- addons/common/XEH_postInit.sqf | 4 +- .../functions/fnc_arithmeticGetResult.sqf | 20 ++-- addons/medical/XEH_postInit.sqf | 4 +- addons/medical/dev/watchVariable.sqf | 23 +++-- addons/medical/initSettings.sqf | 20 ++++ addons/medical/stringtable.xml | 24 +++++ .../medical_damage/ACE_Medical_Injuries.hpp | 2 + addons/medical_damage/XEH_preInit.sqf | 10 +- .../functions/fnc_handleIncapacitation.sqf | 2 +- .../functions/fnc_parseConfigForInjuries.sqf | 24 +++-- .../functions/fnc_woundsHandler.sqf | 2 + .../functions/fnc_woundsHandlerSQF.sqf | 97 +++++++++--------- addons/medical_engine/XEH_PREP.hpp | 2 +- addons/medical_engine/XEH_postInit.sqf | 7 ++ .../functions/fnc_setLimping.sqf | 30 ------ .../functions/fnc_updateDamageEffects.sqf | 67 ++++++++++++ .../medical_engine/script_macros_medical.hpp | 7 +- addons/medical_feedback/XEH_postInit.sqf | 3 +- .../data/body_image/arm_left_b.paa | Bin 0 -> 26461 bytes .../data/body_image/arm_right_b.paa | Bin 0 -> 26424 bytes .../data/body_image/leg_left_b.paa | Bin 0 -> 26500 bytes .../data/body_image/leg_right_b.paa | Bin 0 -> 26498 bytes .../functions/fnc_modifyAction.sqf | 2 +- .../functions/fnc_updateBodyImage.sqf | 34 ++++-- .../functions/fnc_updateInjuryList.sqf | 18 +++- addons/medical_gui/gui.hpp | 34 ++++++ addons/medical_gui/script_component.hpp | 4 + addons/medical_gui/stringtable.xml | 6 ++ addons/medical_gui/ui/splint.paa | Bin 0 -> 2881 bytes .../medical_statemachine/CfgEventHandlers.hpp | 10 ++ addons/medical_statemachine/XEH_PREP.hpp | 2 + addons/medical_statemachine/XEH_postInit.sqf | 2 + .../functions/fnc_handleStateDefault.sqf | 8 +- .../functions/fnc_handleStateInjured.sqf | 8 +- .../functions/fnc_handleStateUnconscious.sqf | 8 +- .../functions/fnc_localityChangedEH.sqf | 33 ++++++ .../functions/fnc_localityTransfer.sqf | 26 +++++ addons/medical_status/CfgEventHandlers.hpp | 4 +- .../medical_status/functions/fnc_initUnit.sqf | 94 +++++++++-------- .../functions/fnc_updateWoundBloodLoss.sqf | 2 +- .../ACE_Medical_Treatment_Actions.hpp | 13 +++ addons/medical_treatment/CfgWeapons.hpp | 11 ++ addons/medical_treatment/XEH_PREP.hpp | 4 + addons/medical_treatment/XEH_postInit.sqf | 1 + .../functions/fnc_canBandage.sqf | 2 +- .../functions/fnc_findMostEffectiveWound.sqf | 5 +- .../functions/fnc_getBandageTime.sqf | 4 +- .../functions/fnc_handleBandageOpening.sqf | 39 +++---- .../functions/fnc_splint.sqf | 25 +++++ .../functions/fnc_splintCondition.sqf | 25 +++++ .../functions/fnc_splintLocal.sqf | 31 ++++++ .../functions/fnc_treatmentBandageLocal.sqf | 10 +- .../functions/fnc_treatmentFullHealLocal.sqf | 6 +- .../fnc_treatmentSurgicalKit_onProgress.sqf | 11 +- .../functions/fnc_treatment_success.sqf | 2 +- addons/medical_treatment/stringtable.xml | 12 +++ 56 files changed, 615 insertions(+), 229 deletions(-) delete mode 100644 addons/medical_engine/functions/fnc_setLimping.sqf create mode 100644 addons/medical_engine/functions/fnc_updateDamageEffects.sqf create mode 100644 addons/medical_gui/data/body_image/arm_left_b.paa create mode 100644 addons/medical_gui/data/body_image/arm_right_b.paa create mode 100644 addons/medical_gui/data/body_image/leg_left_b.paa create mode 100644 addons/medical_gui/data/body_image/leg_right_b.paa create mode 100644 addons/medical_gui/ui/splint.paa create mode 100644 addons/medical_statemachine/functions/fnc_localityChangedEH.sqf create mode 100644 addons/medical_statemachine/functions/fnc_localityTransfer.sqf create mode 100644 addons/medical_treatment/functions/fnc_splint.sqf create mode 100644 addons/medical_treatment/functions/fnc_splintCondition.sqf create mode 100644 addons/medical_treatment/functions/fnc_splintLocal.sqf diff --git a/addons/common/XEH_postInit.sqf b/addons/common/XEH_postInit.sqf index f1c1249749..6a7f1a61e2 100644 --- a/addons/common/XEH_postInit.sqf +++ b/addons/common/XEH_postInit.sqf @@ -19,8 +19,8 @@ //Status Effect EHs: [QGVAR(setStatusEffect), {_this call FUNC(statusEffect_set)}] call CBA_fnc_addEventHandler; -["forceWalk", false, ["ACE_SwitchUnits", "ACE_Attach", "ACE_dragging", "ACE_Explosives", "ACE_Ladder", "ACE_Sandbag", "ACE_refuel", "ACE_rearm", "ACE_Trenches"]] call FUNC(statusEffect_addType); -["blockSprint", false, []] call FUNC(statusEffect_addType); +["forceWalk", false, ["ace_advanced_fatigue", "ACE_SwitchUnits", "ACE_Attach", "ACE_dragging", "ACE_Explosives", "ACE_Ladder", "ACE_Sandbag", "ACE_refuel", "ACE_rearm", "ACE_Trenches"]] call FUNC(statusEffect_addType); +["blockSprint", false, ["ace_advanced_fatigue", "ace_medical_fracture"]] call FUNC(statusEffect_addType); ["setCaptive", true, [QEGVAR(captives,Handcuffed), QEGVAR(captives,Surrendered)]] call FUNC(statusEffect_addType); ["blockDamage", false, ["fixCollision", "ACE_cargo"]] call FUNC(statusEffect_addType); ["blockEngine", false, ["ACE_Refuel"]] call FUNC(statusEffect_addType); diff --git a/addons/common/functions/fnc_arithmeticGetResult.sqf b/addons/common/functions/fnc_arithmeticGetResult.sqf index 418d510d66..e54f44fd27 100644 --- a/addons/common/functions/fnc_arithmeticGetResult.sqf +++ b/addons/common/functions/fnc_arithmeticGetResult.sqf @@ -6,7 +6,7 @@ * Arguments: * 0: Namespace * 1: Number Set ID - * 2: Operation (sum, product, min, max, avg) + * 2: Operation (sum, product, min, max, avg) (Case Sensitive) * * Return Value: * Value @@ -19,11 +19,19 @@ */ params ["_namespace", "_setID", "_op"]; -TRACE_3("params",_namespace,_setID,_op); +TRACE_3("arithmeticGetResult",_namespace,_setID,_op); private _data = (_namespace getVariable _setID) param [2, []]; switch (_op) do { + case ("max"): { + private _result = -1e99; + { + _result = _result max (call _x); + nil + } count _data; + _result // return + }; case ("sum"): { private _result = 0; { @@ -48,14 +56,6 @@ switch (_op) do { } count _data; _result // return }; - case ("max"): { - private _result = -1e99; - { - _result = _result max (call _x); - nil - } count _data; - _result // return - }; case ("avg"): { private _result = 0; { diff --git a/addons/medical/XEH_postInit.sqf b/addons/medical/XEH_postInit.sqf index 5dd19195e8..c2d202f567 100644 --- a/addons/medical/XEH_postInit.sqf +++ b/addons/medical/XEH_postInit.sqf @@ -6,9 +6,7 @@ if (!hasInterface) exitWith {}; [missionNamespace, "ACE_setCustomAimCoef", QUOTE(ADDON), { - private _pain = GET_PAIN_PERCEIVED(ACE_player); - - linearConversion [0, 1, _pain, 1, 5, true]; + (linearConversion [0, 1, GET_PAIN_PERCEIVED(ACE_player), 1, 5, true]) + (ACE_player getVariable [QEGVAR(medical_engine,aimFracture), 0]) }] call EFUNC(common,arithmeticSetSource); #ifdef DEBUG_MODE_FULL diff --git a/addons/medical/dev/watchVariable.sqf b/addons/medical/dev/watchVariable.sqf index 0e39cae4cd..dc97952e0c 100644 --- a/addons/medical/dev/watchVariable.sqf +++ b/addons/medical/dev/watchVariable.sqf @@ -3,7 +3,8 @@ ["medical", { // Hide when patient display is up because they might overlap - if (!isNull EGVAR(medical_gui,displayPatientInformationTarget)) exitWith {""}; + private _display = uiNamespace getVariable [QEGVAR(medical_gui,RscPatientInfo), displayNull]; + if (!isNull _display) exitWith {"Paused"}; private _unit = cursorTarget; if (!(_unit isKindOf "CAManBase")) then {_unit = cursorObject}; @@ -59,6 +60,10 @@ _return pushBack format ["Hitpoints: [HHed:%1] [HBod: %2]", (_unit getHitPointDamage "HitHead") toFixed 2, (_unit getHitPointDamage "HitBody") toFixed 2]; _return pushBack format ["[HHnd:%1] [HLeg: %2] %3", (_unit getHitPointDamage "HitHands") toFixed 2, (_unit getHitPointDamage "HitLegs") toFixed 2, _limping]; + private _fractures = _unit getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]; + private _canSprint = if (isSprintAllowed _unit) then {""} else {"[Sprint Blocked]"}; + _return pushBack format ["Fractures: %1 %2", _fractures, _canSprint]; + // Tourniquets: _return pushBack "------- Tourniquets: -------"; @@ -82,24 +87,24 @@ _return pushBack "------- Wounds: -------"; private _wounds = _unit getVariable [QEGVAR(medical,openWounds), []]; { - _x params ["", "_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage", "_xCategory"]; - _return pushBack format ["%1: [%2-%3] [x%4] [Bld: %5] [Dmg: %6]", ALL_SELECTIONS select _xBodyPartN, _xClassID, _xCategory, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2]; + _x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage"]; + _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]; } forEach _wounds; // Bandaged Wounds: _return pushBack "------- Bandaged Wounds: -------"; private _wounds = _unit getVariable [QEGVAR(medical,bandagedWounds), []]; { - _x params ["", "_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage", "_xCategory"]; - _return pushBack format ["%1: [%2-%3] [x%4] [Bld: %5] [Dmg: %6]", ALL_SELECTIONS select _xBodyPartN, _xClassID, _xCategory, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2]; + _x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage"]; + _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]; } forEach _wounds; // Stitched Wounds: _return pushBack "------- Stitched Wounds: -------"; private _wounds = _unit getVariable [QEGVAR(medical,stitchedWounds), []]; { - _x params ["", "_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage", "_xCategory"]; - _return pushBack format ["%1: [%2-%3] [x%4] [Bld: %5] [Dmg: %6]", ALL_SELECTIONS select _xBodyPartN, _xClassID, _xCategory, _xAmountOf toFixed 1, _xBleeding toFixed 4, _xDamage toFixed 2]; + _x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "_xBleeding", "_xDamage"]; + _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]; } forEach _wounds; // IVs: @@ -139,6 +144,10 @@ _return pushBack "------- Medications Raw: -------"; _return append _rawMedications; + if (_unit isEqualTo ACE_player) then { + _return pushBack format ["ACE_setCustomAimCoef: %1", [missionNamespace, "ACE_setCustomAimCoef", "max"] call EFUNC(common,arithmeticGetResult)]; + }; + // Footer: _return pushBack ""; diff --git a/addons/medical/initSettings.sqf b/addons/medical/initSettings.sqf index 0ae2c0e9f1..f1eb85bc92 100644 --- a/addons/medical/initSettings.sqf +++ b/addons/medical/initSettings.sqf @@ -12,3 +12,23 @@ private _categoryArray = [LELSTRING(medical,Category_DisplayName), "?"]; {[QGVAR(spontaneousWakeUpChance), _this] call EFUNC(common,cbaSettings_settingChanged)}, true // Needs mission restart ] call CBA_settings_fnc_init; + +[ + QEGVAR(medical,limping), "LIST", + [LSTRING(setting_limping_DisplayName), LSTRING(setting_limping_Description)], + _categoryArray, + [[0,1,2],[LELSTRING(common,disabled), LLSTRING(setting_limping_limpOnOpenWounds), LLSTRING(setting_limping_limpRequiresStitching)], 1], // [values, titles, defaultIndex] + true, // isGlobal + {[QEGVAR(medical,limping), _this] call EFUNC(common,cbaSettings_settingChanged)}, + true // Needs mission restart +] call CBA_settings_fnc_init; + +[ + QEGVAR(medical,fractures), "LIST", + [LSTRING(setting_fractures_DisplayName), LSTRING(setting_fractures_Description)], + _categoryArray, + [[0,1,2],[LELSTRING(common,disabled), LLSTRING(setting_fractures_splintHealsFully), LLSTRING(setting_fractures_splintHasEffects)], 1], // [values, titles, defaultIndex] + true, // isGlobal + {[QEGVAR(medical,fractures), _this] call EFUNC(common,cbaSettings_settingChanged)}, + true // Needs mission restart +] call CBA_settings_fnc_init; diff --git a/addons/medical/stringtable.xml b/addons/medical/stringtable.xml index 081e000d92..722628bf42 100644 --- a/addons/medical/stringtable.xml +++ b/addons/medical/stringtable.xml @@ -481,5 +481,29 @@ 뚜껑 닫기 Zamknij pokrywę + + Limping + + + Limp when unit has leg wounds...(todo) + + + Limp on open wounds + + + Limp on open or bandaged wounds + + + Fractues + + + Limp fractures... (todo) + + + Splints fully heal fractures + + + Splints heal (but cannot sprint) + diff --git a/addons/medical_damage/ACE_Medical_Injuries.hpp b/addons/medical_damage/ACE_Medical_Injuries.hpp index ea15797cbe..9cdeb6d6a7 100644 --- a/addons/medical_damage/ACE_Medical_Injuries.hpp +++ b/addons/medical_damage/ACE_Medical_Injuries.hpp @@ -36,6 +36,7 @@ class ACE_Medical_Injuries { pain = 0.8; minDamage = 0.1; causeLimping = 1; + causeFracture = 1; }; // Slicing wounds made with a sharp instrument, leaving even edges. They may be as minimal as a paper cut or as significant as a surgical incision. class Cut { @@ -59,6 +60,7 @@ class ACE_Medical_Injuries { pain = 0.9; minDamage = 0.35; causeLimping = 1; + causeFracture = 1; }; // Deep, narrow wounds produced by sharp objects such as nails, knives, and broken glass. class PunctureWound { diff --git a/addons/medical_damage/XEH_preInit.sqf b/addons/medical_damage/XEH_preInit.sqf index 203f11109d..081b0cf48b 100644 --- a/addons/medical_damage/XEH_preInit.sqf +++ b/addons/medical_damage/XEH_preInit.sqf @@ -17,11 +17,13 @@ addMissionEventHandler ["Loaded",{ }]; // decide which woundsHandler to use by whether the extension is present or not -if ("ace_medical" callExtension "version" != "") then { - DFUNC(woundsHandlerActive) = LINKFUNC(woundsHandler); -} else { +// if ("ace_medical" callExtension "version" != "") then { + + // DFUNC(woundsHandlerActive) = LINKFUNC(woundsHandler); +// } else { + INFO("Using woundsHandlerSQF"); DFUNC(woundsHandlerActive) = LINKFUNC(woundsHandlerSQF); -}; +// }; [QEGVAR(medical,woundReceived), { params ["_unit", "_woundedHitPoint", "_receivedDamage", "", "_ammo"]; diff --git a/addons/medical_damage/functions/fnc_handleIncapacitation.sqf b/addons/medical_damage/functions/fnc_handleIncapacitation.sqf index f61b2f9c90..1adaf997b8 100644 --- a/addons/medical_damage/functions/fnc_handleIncapacitation.sqf +++ b/addons/medical_damage/functions/fnc_handleIncapacitation.sqf @@ -24,7 +24,7 @@ _bodyPartDamage params ["_headDamage", "_bodyDamage", "_leftArmDamage", "_rightA // Exclude non penetrating body damage { - _x params ["", "", "_bodyPartN", "_amountOf", "", "_damage"]; + _x params ["", "_bodyPartN", "_amountOf", "", "_damage"]; if (_bodyPartN == 1 && {_damage < PENETRATION_THRESHOLD}) then { _bodyDamage = _bodyDamage - (_amountOf * _damage); }; diff --git a/addons/medical_damage/functions/fnc_parseConfigForInjuries.sqf b/addons/medical_damage/functions/fnc_parseConfigForInjuries.sqf index 682b12e141..89da4bca4e 100644 --- a/addons/medical_damage/functions/fnc_parseConfigForInjuries.sqf +++ b/addons/medical_damage/functions/fnc_parseConfigForInjuries.sqf @@ -19,7 +19,8 @@ private _injuriesConfigRoot = configFile >> "ACE_Medical_Injuries"; // --- parse wounds GVAR(woundClassNames) = []; -GVAR(woundsData) = []; // @todo classTypes are strings currently. Convert them to unqiue IDs instead. +GVAR(woundClassNamesComplex) = []; // index = 10 * classID + category; [will contain nils] e.g. ["aMinor", "aMed", "aLarge", nil, nil..."bMinor"] +GVAR(woundsData) = []; private _woundsConfig = _injuriesConfigRoot >> "wounds"; private _classID = 0; @@ -34,17 +35,20 @@ private _classID = 0; private _minDamage = GET_NUMBER(_entry >> "minDamage",0); private _maxDamage = GET_NUMBER(_entry >> "maxDamage",-1); private _causes = GET_ARRAY(_entry >> "causes",[]); - private _causeLimping = GET_NUMBER(_entry >> "causeLimping",0); + private _causeLimping = GET_NUMBER(_entry >> "causeLimping",0) == 1; + private _causeFracture = GET_NUMBER(_entry >> "causeFracture",0) == 1; if !(_causes isEqualTo []) then { GVAR(woundClassNames) pushBack _className; - GVAR(woundsData) pushBack [_classID, _selections, _bleeding, _pain, [_minDamage, _maxDamage], _causes, _className, _causeLimping]; + GVAR(woundsData) pushBack [_classID, _selections, _bleeding, _pain, [_minDamage, _maxDamage], _causes, _className, _causeLimping, _causeFracture]; + { + GVAR(woundClassNamesComplex) set [10 * _classID + _forEachIndex, format ["%1%2", _className, _x]]; + } forEach ["Minor", "Medium", "Large"]; _classID = _classID + 1; }; } forEach configProperties [_woundsConfig, "isClass _x"]; // --- parse damage types -GVAR(allDamageTypes) = []; // @todo, currently unused by handle damage (was GVAR(allAvailableDamageTypes)) GVAR(allDamageTypesData) = [] call CBA_fnc_createNamespace; // minimum lethal damage collection, mapped to damageTypes @@ -57,8 +61,6 @@ private _selectionSpecificDefault = getNumber (_damageTypesConfig >> "selectionS private _entry = _x; private _className = configName _entry; - GVAR(allDamageTypes) pushBack _className; - // Check if this type is in the causes of a wound class, if so, we will store the wound types for this damage type private _woundTypes = []; { @@ -89,8 +91,8 @@ private _selectionSpecificDefault = getNumber (_damageTypesConfig >> "selectionS ]; TRACE_1("",_extensionArgs); - private _extensionRes = "ace_medical" callExtension _extensionArgs; - TRACE_1("",_extensionRes); + // private _extensionRes = "ace_medical" callExtension _extensionArgs; + // TRACE_1("",_extensionRes); } forEach configProperties [_damageTypesConfig, "isClass _x"]; // extension loading @@ -121,8 +123,8 @@ private _selectionSpecificDefault = getNumber (_damageTypesConfig >> "selectionS ]; TRACE_1("",_extensionArgs); - private _extensionRes = "ace_medical" callExtension _extensionArgs; - TRACE_1("",_extensionRes); + // private _extensionRes = "ace_medical" callExtension _extensionArgs; + // TRACE_1("",_extensionRes); } forEach GVAR(woundsData); -"ace_medical" callExtension "ConfigComplete"; +// "ace_medical" callExtension "ConfigComplete"; diff --git a/addons/medical_damage/functions/fnc_woundsHandler.sqf b/addons/medical_damage/functions/fnc_woundsHandler.sqf index 68e43f2c36..41bc97e609 100644 --- a/addons/medical_damage/functions/fnc_woundsHandler.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandler.sqf @@ -18,6 +18,8 @@ * Public: No */ +WARNING("this function needs to be updated for changes to woundsHandlerSQF"); + params ["_unit", "_bodyPart", "_damage", "_typeOfDamage"]; TRACE_4("start",_unit,_bodyPart,_damage,_typeOfDamage); diff --git a/addons/medical_damage/functions/fnc_woundsHandlerSQF.sqf b/addons/medical_damage/functions/fnc_woundsHandlerSQF.sqf index 88c70538a8..87fd6ef42e 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerSQF.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerSQF.sqf @@ -19,17 +19,14 @@ */ params ["_unit", "_bodyPart", "_damage", "_typeOfDamage"]; -TRACE_4("start",_unit,_bodyPart,_damage,_typeOfDamage); +TRACE_4("woundsHandlerSQF",_unit,_bodyPart,_damage,_typeOfDamage); // Convert the selectionName to a number and ensure it is a valid selection. private _bodyPartN = ALL_BODY_PARTS find toLower _bodyPart; -if (_bodyPartN < 0) exitWith {}; +if (_bodyPartN < 0) exitWith { ERROR_1("invalid body part %1",_bodyPart); }; -if (_typeOfDamage isEqualTo "") then { - _typeOfDamage = "unknown"; -}; - -if (isNil {GVAR(allDamageTypesData) getVariable _typeOfDamage} ) then { +if ((_typeOfDamage isEqualTo "") || {isNil {GVAR(allDamageTypesData) getVariable _typeOfDamage}}) then { + WARNING_1("damage type [%1] not found",_typeOfDamage); _typeOfDamage = "unknown"; }; @@ -65,22 +62,22 @@ private _allPossibleInjuries = []; } forEach _woundTypes; // No possible wounds available for this damage type or damage amount. -if (_highestPossibleSpot < 0) exitWith {}; +if (_highestPossibleSpot < 0) exitWith { TRACE_2("no wounds possible",_damage,_highestPossibleSpot); }; // Administration for open wounds and ids private _openWounds = _unit getVariable [QEGVAR(medical,openWounds), []]; -private _woundID = _unit getVariable [QEGVAR(medical,lastUniqueWoundID), 1]; // Unique wound ids are not used anywhere: ToDo Remove from openWounds array +private _updateDamageEffects = false; private _painLevel = 0; private _critialDamage = false; private _bodyPartDamage = _unit getVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0]]; private _bodyPartVisParams = [_unit, false, false, false, false]; // params array for EFUNC(medical_engine,updateBodyPartVisuals); -private _woundsCreated = []; + { _x params ["_thresholdMinDam", "_thresholdWoundCount"]; if (_thresholdMinDam <= _damage) exitWith { private _woundDamage = _damage / (_thresholdWoundCount max 1); // If the damage creates multiple wounds - for "_i" from 0 to (_thresholdWoundCount-1) do { + for "_i" from 1 to _thresholdWoundCount do { // Find the injury we are going to add. Format [ classID, allowdSelections, bleedingRate, injuryPain] private _oldInjury = if (random 1 >= 0.85) then { _woundTypes select _highestPossibleSpot @@ -88,15 +85,13 @@ private _woundsCreated = []; selectRandom _allPossibleInjuries }; - _oldInjury params ["_woundClassIDToAdd", "", "_injuryBleedingRate", "_injuryPain"]; + _oldInjury params ["_woundClassIDToAdd", "", "_injuryBleedingRate", "_injuryPain", "", "", "", "_causeLimping", "_causeFracture"]; private _bodyPartNToAdd = [floor random 6, _bodyPartN] select _isSelectionSpecific; // 6 == count ALL_BODY_PARTS _bodyPartDamage set [_bodyPartNToAdd, (_bodyPartDamage select _bodyPartNToAdd) + _woundDamage]; _bodyPartVisParams set [[1,2,3,3,4,4] select _bodyPartNToAdd, true]; // Mark the body part index needs updating - // Create a new injury. Format [ID, classID, bodypart, percentage treated, bleeding rate] - private _injury = [_woundID, _woundClassIDToAdd, _bodyPartNToAdd, 1, _injuryBleedingRate]; // The higher the nastiness likelihood the higher the change to get a painful and bloody wound private _nastinessLikelihood = linearConversion [0, 20, (_woundDamage / _thresholdWoundCount), 0.5, 30, true]; @@ -110,73 +105,81 @@ private _woundsCreated = []; // wound category (minor [0..0.5], medium[0.5..1.0], large[1.0+]) private _category = floor linearConversion [0, 1, _bleedingModifier, 0, 2, true]; - // wound category (minor, medium, large) - private _category = floor ((0 max _bleeding min 0.1) / 0.05); + private _classComplex = 10 * _woundClassIDToAdd + _category; - _injury set [4, _bleeding]; - _injury set [5, _woundDamage]; - _injury set [6, _category]; + // Create a new injury. Format [0:classComplex, 1:bodypart, 2:amountOf, 3:bleedingRate, 4:woundDamage] + private _injury = [_classComplex, _bodyPartNToAdd, 1, _bleeding, _woundDamage]; if (_bodyPartNToAdd == 0 || {_bodyPartNToAdd == 1 && {_woundDamage > PENETRATION_THRESHOLD}}) then { _critialDamage = true; }; -#ifdef DEBUG_MODE_FULL + + #ifdef DEBUG_MODE_FULL systemChat format["%1, damage: %2, peneration: %3, bleeding: %4, pain: %5", _bodyPart, _woundDamage toFixed 2, _woundDamage > PENETRATION_THRESHOLD, _bleeding toFixed 3, _pain toFixed 3]; -#endif + #endif // Emulate damage to vital organs switch (true) do { // Fatal damage to the head is guaranteed death - case (_bodyPartNToAdd == 0 && {_woundDamage >= HEAD_DAMAGE_THRESHOLD}): { + case (_bodyPartNToAdd == 0 && {_woundDamage >= HEAD_DAMAGE_THRESHOLD}): { TRACE_1("lethal headshot",_woundDamage toFixed 2); [QEGVAR(medical,FatalInjury), _unit] call CBA_fnc_localEvent; }; // Fatal damage to torso has various results based on organ hit - case (_bodyPartNToAdd == 1 && {_woundDamage >= ORGAN_DAMAGE_THRESHOLD}): { + case (_bodyPartNToAdd == 1 && {_woundDamage >= ORGAN_DAMAGE_THRESHOLD}): { // Heart shot is lethal if (random 1 < HEART_HIT_CHANCE) then { TRACE_1("lethal heartshot",_woundDamage toFixed 2); [QEGVAR(medical,FatalInjury), _unit] call CBA_fnc_localEvent; }; }; - }; - - // todo `forceWalk` based on leg damage - private _causeLimping = (GVAR(woundsData) select _woundClassIDToAdd) select 7; - if (_causeLimping == 1 && {_woundDamage > LIMPING_DAMAGE_THRESHOLD} && {_bodyPartNToAdd > 3}) then { - [_unit, true] call EFUNC(medical_engine,setLimping); + case (_causeFracture && {EGVAR(medical,fractures) > 0} && {_bodyPartNToAdd > 1} && {_woundDamage > FRACTURE_DAMAGE_THRESHOLD}): { + TRACE_1("limb fracture",_bodyPartNToAdd); + // todo: play sound? + private _fractures = _unit getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]; + _fractures set [_bodyPartNToAdd, 1]; + _unit setVariable [QEGVAR(medical,fractures), _fractures, true]; + [QEGVAR(medical,fracture), [_unit, _bodyPartNToAdd]] call CBA_fnc_localEvent; // local event for fracture + _updateDamageEffects = true; + }; + case (_causeLimping && {EGVAR(medical,limping) > 0} && {_bodyPartNToAdd > 3} && {_woundDamage > LIMPING_DAMAGE_THRESHOLD}): { + _updateDamageEffects = true; + }; }; // if possible merge into existing wounds private _createNewWound = true; { - _x params ["", "_classID", "_bodyPartN", "_oldAmountOf", "_oldBleeding", "_oldDamage", "_oldCategory"]; - if (_woundClassIDToAdd == _classID && {_bodyPartNToAdd == _bodyPartN && {(_woundDamage < PENETRATION_THRESHOLD) isEqualTo (_oldDamage < PENETRATION_THRESHOLD)}}) then { - if (_oldCategory == _category) exitWith { - private _newAmountOf = _oldAmountOf + 1; - _x set [3, _newAmountOf]; - private _newBleeding = (_oldAmountOf * _oldBleeding + _bleeding) / _newAmountOf; - _x set [4, _newBleeding]; - private _newDamage = (_oldAmountOf * _oldDamage + _woundDamage) / _newAmountOf; - _x set [5, _newDamage]; - _createNewWound = false; - }; + _x params ["_classID", "_bodyPartN", "_oldAmountOf", "_oldBleeding", "_oldDamage"]; + if ( + (_classComplex == _classID) && + {_bodyPartNToAdd == _bodyPartN} && + {(_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 + ) exitWith { + TRACE_2("merging with existing wound",_injury,_x); + private _newAmountOf = _oldAmountOf + 1; + _x set [2, _newAmountOf]; + private _newBleeding = (_oldAmountOf * _oldBleeding + _bleeding) / _newAmountOf; + _x set [3, _newBleeding]; + private _newDamage = (_oldAmountOf * _oldDamage + _woundDamage) / _newAmountOf; + _x set [4, _newDamage]; + _createNewWound = false; }; } forEach _openWounds; if (_createNewWound) then { + TRACE_1("adding new wound",_injury); _openWounds pushBack _injury; }; - - // New injuries will also increase the wound ID - _woundID = _woundID + 1; - - // Store the injury so we can process it later correctly. - _woundsCreated pushBack _injury; }; }; } forEach _thresholds; +if (_updateDamageEffects) then { + [_unit] call EFUNC(medical_engine,updateDamageEffects); +}; + _unit setVariable [QEGVAR(medical,openWounds), _openWounds, true]; _unit setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true]; @@ -190,4 +193,4 @@ if (_critialDamage || {_painLevel > PAIN_UNCONSCIOUS}) then { [_unit] call FUNC(handleIncapacitation); }; -TRACE_5("exit",_unit,_painLevel,GET_PAIN(_unit),_unit getVariable QEGVAR(medical,openWounds),_woundsCreated); +TRACE_4("exit",_unit,_painLevel,GET_PAIN(_unit),_unit getVariable QEGVAR(medical,openWounds)); diff --git a/addons/medical_engine/XEH_PREP.hpp b/addons/medical_engine/XEH_PREP.hpp index 3c7872c515..0bacdd97e7 100644 --- a/addons/medical_engine/XEH_PREP.hpp +++ b/addons/medical_engine/XEH_PREP.hpp @@ -1,6 +1,6 @@ PREP(handleDamage); PREP(damageBodyPart); PREP(updateBodyPartVisuals); -PREP(setLimping); +PREP(updateDamageEffects); PREP(setStructuralDamage); PREP(setUnconsciousAnim); diff --git a/addons/medical_engine/XEH_postInit.sqf b/addons/medical_engine/XEH_postInit.sqf index b7dccbeb56..2a6aa2b717 100644 --- a/addons/medical_engine/XEH_postInit.sqf +++ b/addons/medical_engine/XEH_postInit.sqf @@ -1,5 +1,12 @@ #include "script_component.hpp" +[QGVAR(updateDamageEffects), LINKFUNC(updateDamageEffects)] call CBA_fnc_addEventHandler; +["unit", { + params ["_new"]; + [_new] call FUNC(updateDamageEffects); // Run on new controlled unit to update QGVAR(aimFracture) +}, true] call CBA_fnc_addPlayerEventHandler; + + ["CAManBase", "init", { params ["_unit"]; diff --git a/addons/medical_engine/functions/fnc_setLimping.sqf b/addons/medical_engine/functions/fnc_setLimping.sqf deleted file mode 100644 index 11ead53c54..0000000000 --- a/addons/medical_engine/functions/fnc_setLimping.sqf +++ /dev/null @@ -1,30 +0,0 @@ -#include "script_component.hpp" -/* - * Author: commy2 - * Forces a unit to limp or not. - * - * Arguments: - * 0: Unit - * 1: Limping (optional, default: true) - * - * Return Value: - * None - * - * Example: - * [player, true] call ace_medical_engine_fnc_setLimping - * - * Public: No - */ - -params [["_unit", objNull, [objNull]], ["_isLimping", true, [false]]]; - -if (!local _unit) exitWith { - ERROR("Unit not local or null"); -}; - -_unit setVariable [QEGVAR(medical,isLimping), _isLimping, true]; - -// refresh -private _isDamaged = _unit getHitPointDamage "HitLegs" >= DAMAGED_MIN_THRESHOLD && {_unit getHitPointDamage "HitLegs" != LIMPING_MIN_DAMAGE}; - -[_unit, "Legs", _isDamaged] call FUNC(damageBodyPart); diff --git a/addons/medical_engine/functions/fnc_updateDamageEffects.sqf b/addons/medical_engine/functions/fnc_updateDamageEffects.sqf new file mode 100644 index 0000000000..dffe7f2ebc --- /dev/null +++ b/addons/medical_engine/functions/fnc_updateDamageEffects.sqf @@ -0,0 +1,67 @@ +#include "script_component.hpp" +/* + * Author: commy2, PabstMirror + * Updates damage effects for limping and fractures + * + * Arguments: + * 0: Unit + * 1: Limping (optional, default: true) + * + * Return Value: + * None + * + * Example: + * [player] call ace_medical_engine_fnc_updateDamageEffects + * + * Public: No + */ + +params [["_unit", objNull, [objNull]]]; + +if (!local _unit) exitWith { ERROR("Unit not local or null"); }; + +private _isLimping = false; + +if (EGVAR(medical,fractures) > 0) then { + private _fractures = _unit getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]; + TRACE_1("",_fractures); + if (((_fractures select 4) == 1) || {(_fractures select 5) == 1}) then { + TRACE_1("limping because of fracture",_fractures); + _isLimping = true; + }; + private _aimFracture = 0; + if ((_fractures select 2) == 1) then { _aimFracture = _aimFracture + 4; }; + if ((_fractures select 3) == 1) then { _aimFracture = _aimFracture + 4; }; + + if (EGVAR(medical,fractures) == 2) then { // the limp with a splint will still cause effects + private _isSprintBlocked = ((_fractures select 4) == -1) || {(_fractures select 5) == -1}; // block sprinting if we have a leg splint on + if (_isSprintBlocked || {!isSprintAllowed _unit}) then { // only update status effect if we need to + TRACE_1("updating status effect",_isSprintBlocked); + [_unit, "blockSprint", QEGVAR(medical,fracture), _isSprintBlocked] call EFUNC(common,statusEffect_set); + }; + if ((_fractures select 2) == 1) then { _aimFracture = _aimFracture + 2; }; + if ((_fractures select 3) == 1) then { _aimFracture = _aimFracture + 2; }; + }; + _unit setVariable [QGVAR(aimFracture), _aimFracture, false]; // local only var, used in ace_medical's postInit to set ACE_setCustomAimCoef +}; + +if (!_isLimping && {EGVAR(medical,limping) > 0}) then { + private _woundsToCheck = _unit getVariable [QEGVAR(medical,openWounds), []]; + if (EGVAR(medical,limping) == 2) then { + _woundsToCheck = _woundsToCheck + (_unit getVariable [QEGVAR(medical,bandagedWounds), []]); // do not append + }; + { + _x params ["_xClassID", "_xBodyPartN", "_xAmountOf", "", "_xDamage"]; + if ((_xBodyPartN > 3) && {_xAmountOf > 0} && {_xDamage > LIMPING_DAMAGE_THRESHOLD} && { + (EGVAR(medical_damage,woundsData) select (_xClassID / 10)) select 7}) exitWith { // select _causeLimping from woundsData + TRACE_1("limping because of wound",_x); + _isLimping = true; + }; + } forEach _woundsToCheck; +}; +_unit setVariable [QEGVAR(medical,isLimping), _isLimping, true]; + +// refresh +private _isDamaged = _unit getHitPointDamage "HitLegs" >= DAMAGED_MIN_THRESHOLD && {_unit getHitPointDamage "HitLegs" != LIMPING_MIN_DAMAGE}; + +[_unit, "Legs", _isDamaged] call FUNC(damageBodyPart); diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index 8e38faa455..7f17a19161 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -74,12 +74,15 @@ // Minimum leg damage required for limping #define LIMPING_DAMAGE_THRESHOLD 0.30 +// Minimum limb damage required for fracture +#define FRACTURE_DAMAGE_THRESHOLD 0.50 + // Minimum body part damage required for blood effect on uniform #define VISUAL_BODY_DAMAGE_THRESHOLD 0.35 // Empty wound data, used for some default return values -// [ID, classID, bodypartIndex, amountOf, bloodloss, damage, category] -#define EMPTY_WOUND [-1, -1, -1, 0, 0, 0, 0] +// [classID, bodypartIndex, amountOf, bloodloss, damage] +#define EMPTY_WOUND [-1, -1, 0, 0, 0] // Base time to bandage each wound category #define BANDAGE_TIME_S 4 diff --git a/addons/medical_feedback/XEH_postInit.sqf b/addons/medical_feedback/XEH_postInit.sqf index faa826a534..546f3de95f 100644 --- a/addons/medical_feedback/XEH_postInit.sqf +++ b/addons/medical_feedback/XEH_postInit.sqf @@ -56,8 +56,7 @@ GVAR(heartBeatEffectRunning) = false; // Update effects to match new unit's current status (this also handles respawn) ["unit", { params ["_new"]; - - private _status = _new getVariable ["ace_unconscious", false]; + private _status = IS_UNCONSCIOUS(_new); if (["task_force_radio"] call EFUNC(common,isModLoaded)) then { _new setVariable ["tf_voiceVolume", [1, 0] select _status, true]; diff --git a/addons/medical_gui/data/body_image/arm_left_b.paa b/addons/medical_gui/data/body_image/arm_left_b.paa new file mode 100644 index 0000000000000000000000000000000000000000..9d4cefd89b55a01876e5f95f9c4a5c2f8573afd0 GIT binary patch literal 26461 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 literal 0 HcmV?d00001 diff --git a/addons/medical_gui/data/body_image/leg_right_b.paa b/addons/medical_gui/data/body_image/leg_right_b.paa new file mode 100644 index 0000000000000000000000000000000000000000..3b0dc02605ebdcfce850a5c1e6808a3209cb2f26 GIT binary patch literal 26498 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 literal 0 HcmV?d00001 diff --git a/addons/medical_gui/functions/fnc_modifyAction.sqf b/addons/medical_gui/functions/fnc_modifyAction.sqf index d4754dab06..7c197cc102 100644 --- a/addons/medical_gui/functions/fnc_modifyAction.sqf +++ b/addons/medical_gui/functions/fnc_modifyAction.sqf @@ -25,7 +25,7 @@ private _bloodLossOnBodyPart = 0; // Add all bleeding from wounds on selection { - _x params ["", "", "_bodyPartN", "_amountOf", "_bleeding"]; + _x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; if (_bodyPartN == _partIndex) then { _bloodLossOnBodyPart = _bloodLossOnBodyPart + (_amountOf * _bleeding); diff --git a/addons/medical_gui/functions/fnc_updateBodyImage.sqf b/addons/medical_gui/functions/fnc_updateBodyImage.sqf index 62b2ebd0fb..8093286eea 100644 --- a/addons/medical_gui/functions/fnc_updateBodyImage.sqf +++ b/addons/medical_gui/functions/fnc_updateBodyImage.sqf @@ -20,16 +20,17 @@ params ["_ctrlGroup", "_target"]; // Get tourniquets, damage, and blood loss for target private _tourniquets = GET_TOURNIQUETS(_target); +private _fractures = _target getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]; private _bodyPartDamage = _target getVariable [QEGVAR(medical,bodyPartDamage), [0, 0, 0, 0, 0, 0]]; private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0]; { - _x params ["", "", "_bodyPartN", "_amountOf", "_bleeding"]; + _x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; _bodyPartBloodLoss set [_bodyPartN, (_bodyPartBloodLoss select _bodyPartN) + (_bleeding * _amountOf)]; } forEach (_target getVariable [QEGVAR(medical,openWounds), []]); { - _x params ["_bodyPartIDC", ["_tourniquetIDC", -1]]; + _x params ["_bodyPartIDC", ["_tourniquetIDC", -1], ["_fractureIDC", -1]]; // Show or hide the tourniquet icon if (_tourniquetIDC != -1) then { @@ -37,6 +38,27 @@ private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0]; private _ctrlTourniquet = _ctrlGroup controlsGroupCtrl _tourniquetIDC; _ctrlTourniquet ctrlShow _hasTourniquet; }; + // Show or hide fractrue/bones + if (_fractureIDC != -1) then { + private _ctrlBone = _ctrlGroup controlsGroupCtrl _fractureIDC; + switch (_fractures select _forEachIndex) do { + case (0): { + _ctrlBone ctrlShow false; + }; + case (1): { + _ctrlBone ctrlShow true; + _ctrlBone ctrlSetTextColor [1, 0, 0, 1]; + }; + case (-1): { + if (EGVAR(medical,fractures) == 2) then { + _ctrlBone ctrlShow true; + _ctrlBone ctrlSetTextColor [0, 0, 1, 1]; + } else { + _ctrlBone ctrlShow false; + }; + }; + }; + }; // Update body part color based on blood loss and damage private _bloodLoss = _bodyPartBloodLoss select _forEachIndex; @@ -52,8 +74,8 @@ private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0]; } forEach [ [IDC_BODY_HEAD], [IDC_BODY_TORSO], - [IDC_BODY_ARMLEFT, IDC_BODY_ARMLEFT_T], - [IDC_BODY_ARMRIGHT, IDC_BODY_ARMRIGHT_T], - [IDC_BODY_LEGLEFT, IDC_BODY_LEGLEFT_T], - [IDC_BODY_LEGRIGHT, IDC_BODY_LEGRIGHT_T] + [IDC_BODY_ARMLEFT, IDC_BODY_ARMLEFT_T, IDC_BODY_ARMLEFT_B], + [IDC_BODY_ARMRIGHT, IDC_BODY_ARMRIGHT_T, IDC_BODY_ARMRIGHT_B], + [IDC_BODY_LEGLEFT, IDC_BODY_LEGLEFT_T, IDC_BODY_LEGLEFT_B], + [IDC_BODY_LEGRIGHT, IDC_BODY_LEGRIGHT_T, IDC_BODY_LEGRIGHT_B] ]; diff --git a/addons/medical_gui/functions/fnc_updateInjuryList.sqf b/addons/medical_gui/functions/fnc_updateInjuryList.sqf index 49966fdc4a..39a8120f36 100644 --- a/addons/medical_gui/functions/fnc_updateInjuryList.sqf +++ b/addons/medical_gui/functions/fnc_updateInjuryList.sqf @@ -58,6 +58,14 @@ switch (GET_HEMORRHAGE(_target)) do { if (HAS_TOURNIQUET_APPLIED_ON(_target,_selectionN)) then { _entries pushBack [localize LSTRING(Status_Tourniquet_Applied), [0.77, 0.51, 0.08, 1]]; }; +switch ((_target getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]) select _selectionN) do { + case (1): {_entries pushBack [localize LSTRING(Status_Fractured), [1, 0, 0, 1]];}; + case (-1): { + if (EGVAR(medical,fractures) == 2) then { // Ignore if the split has no effect + _entries pushBack [localize LSTRING(Status_SplintApplied), [1, 1, 1, 1]]; + }; + }; +}; // Indicate the amount of pain the unit is in if ([_target] call EFUNC(common,isAwake)) then { @@ -93,7 +101,9 @@ if (_totalIvVolume >= 1) then { private _woundEntries = []; private _fnc_getWoundDescription = { - private _className = EGVAR(medical_damage,woundsData) select _woundClassID select 6; + private _classIndex = _woundClassID / 10; + private _category = _woundClassID % 10; + private _className = EGVAR(medical_damage,woundsData) select _classIndex select 6; private _suffix = ["Minor", "Medium", "Large"] select _category; private _woundName = localize format [ELSTRING(medical_damage,%1_%2), _className, _suffix]; if (_amountOf >= 1) then { @@ -104,7 +114,7 @@ private _fnc_getWoundDescription = { }; { - _x params ["", "_woundClassID", "_bodyPartN", "_amountOf", "", "", "_category"]; + _x params ["_woundClassID", "_bodyPartN", "_amountOf"]; if (_selectionN == _bodyPartN) then { if (_amountOf > 0) then { _woundEntries pushBack [call _fnc_getWoundDescription, [1, 1, 1, 1]]; @@ -117,14 +127,14 @@ private _fnc_getWoundDescription = { } forEach (_target getVariable [QEGVAR(medical,openWounds), []]); { - _x params ["", "_woundClassID", "_bodyPartN", "_amountOf", "", "", "_category"]; + _x params ["_woundClassID", "_bodyPartN", "_amountOf"]; if (_selectionN == _bodyPartN && {_amountOf > 0}) then { _woundEntries pushBack [format ["[B] %1", call _fnc_getWoundDescription], [0.88, 0.7, 0.65, 1]]; }; } forEach (_target getVariable [QEGVAR(medical,bandagedWounds), []]); { - _x params ["", "_woundClassID", "_bodyPartN", "_amountOf", "", "", "_category"]; + _x params ["_woundClassID", "_bodyPartN", "_amountOf"]; if (_selectionN == _bodyPartN && {_amountOf > 0}) then { _woundEntries pushBack [format ["[S] %1", call _fnc_getWoundDescription], [0.7, 0.7, 0.7, 1]]; }; diff --git a/addons/medical_gui/gui.hpp b/addons/medical_gui/gui.hpp index eceb263a60..06db05ad38 100644 --- a/addons/medical_gui/gui.hpp +++ b/addons/medical_gui/gui.hpp @@ -45,6 +45,24 @@ class GVAR(BodyImage): RscControlsGroupNoScrollbars { idc = IDC_BODY_LEGRIGHT; text = QPATHTOF(data\body_image\leg_right.paa); }; + class ArmLeftB: Background { + idc = IDC_BODY_ARMLEFT_B; + text = QPATHTOF(data\body_image\arm_left_b.paa); + colorText[] = {0, 0, 0.8, 1}; + show = 0; + }; + class ArmRightB: ArmLeftB { + idc = IDC_BODY_ARMRIGHT_B; + text = QPATHTOF(data\body_image\arm_right_b.paa); + }; + class LegLeftB: ArmLeftB { + idc = IDC_BODY_LEGLEFT_B; + text = QPATHTOF(data\body_image\leg_left_b.paa); + }; + class LegRightB: ArmLeftB { + idc = IDC_BODY_LEGRIGHT_B; + text = QPATHTOF(data\body_image\leg_right_b.paa); + }; class ArmLeftT: Background { idc = IDC_BODY_ARMLEFT_T; text = QPATHTOF(data\body_image\arm_left_t.paa); @@ -549,6 +567,22 @@ class RscTitles { w = POS_W(8.5); h = POS_H(8.5); }; + class ArmLeftB: ArmLeftB { + w = POS_W(8.5); + h = POS_H(8.5); + }; + class ArmRightB: ArmRightB { + w = POS_W(8.5); + h = POS_H(8.5); + }; + class LegLeftB: LegLeftB { + w = POS_W(8.5); + h = POS_H(8.5); + }; + class LegRightB: LegRightB { + w = POS_W(8.5); + h = POS_H(8.5); + }; class ArmLeftT: ArmLeftT { w = POS_W(8.5); h = POS_H(8.5); diff --git a/addons/medical_gui/script_component.hpp b/addons/medical_gui/script_component.hpp index a1c8fb6a8d..c4c7aafcd8 100644 --- a/addons/medical_gui/script_component.hpp +++ b/addons/medical_gui/script_component.hpp @@ -67,6 +67,10 @@ #define IDC_BODY_ARMRIGHT_T 6040 #define IDC_BODY_LEGLEFT_T 6045 #define IDC_BODY_LEGRIGHT_T 6050 +#define IDC_BODY_ARMLEFT_B 6055 +#define IDC_BODY_ARMRIGHT_B 6060 +#define IDC_BODY_LEGRIGHT_B 6065 +#define IDC_BODY_LEGLEFT_B 6070 #define IDC_TRIAGE_STATUS 7000 #define IDC_TRIAGE_SELECT 7100 diff --git a/addons/medical_gui/stringtable.xml b/addons/medical_gui/stringtable.xml index 3359f08bd5..98a27cc566 100644 --- a/addons/medical_gui/stringtable.xml +++ b/addons/medical_gui/stringtable.xml @@ -775,6 +775,12 @@ 大量失血 大量失血 + + Fractured + + + Splint Applied + diff --git a/addons/medical_gui/ui/splint.paa b/addons/medical_gui/ui/splint.paa new file mode 100644 index 0000000000000000000000000000000000000000..521809124a6f57c3efa1f6ffb717d9a08cef110b GIT binary patch literal 2881 zcmeHJ&ubGw6n;s%l2{gcaPepkhKhJ}B?k{%l=dVpG?V0z!~O~Jq$k-!y^9d)NqW$W zcaKV-ISCdop$DNYWDB#kDPm2dG|l+E-K-6v$+rFg1!oD5*`4{`_rCYuOcH8gVXkm% zp*S~1MD2DjoR{bB@LXZ=_r>}7yB6kE{O+fT9-b%CrirfMceY`XMU)PJc_TacZ~p}y zRwa>BQOc8UQHX8X+Ioo*Sx+ie4)Wv@0o;ZZLQgUe zG4RK>$L|YudfUT$Aqq|3Uy^3+8_^<5`yzCL=k-) z$1BdyKfJEs147jCiM1Y4vjY+_pCgk^@l(B zlmcGhGq7f0qji6`%~F;KX~F>OKVWff&`NM!h3$i~q3K$@Ka^43a+KPZBFAOB-U;Px z$x_v-b9z%5T^o<%Nyn-qo^6@YYNfD!>BYA`$`a=!ZpBXSRPlPU?6NLMZ_%aKusKlz zFT|0|>&ctSc9ZY}J(A(ETYn&+A9&~~ z@QTY@e^92~fqvai=7hLsXoi9?BRKmDRv}EdC3-dLW>Zc1kMsHWZ`30nG?Sa^ik}?n zvJG`^$EEb;4Rq`B7R$Hf + * 1: isLocal + * + * Return Value: + * None + * + * Example: + * [player, true] call ace_medical_statemachine_fnc_localityChangedEH + * + * Public: No + */ + +params ["_unit", "_isLocal"]; +TRACE_2("localityChangedEH",_unit,_isLocal); + +if (!alive _unit) exitWith {TRACE_1("dead", _this)}; + +if (!_isLocal) then { + // If locality changed, broadcast the last medical state + _unit setVariable [VAR_HEART_RATE, GET_HEART_RATE(_unit), true]; + _unit setVariable [VAR_BLOOD_PRESS, _unit getVariable [VAR_BLOOD_PRESS, [80, 120]], true]; + _unit setVariable [VAR_BLOOD_VOL, GET_BLOOD_VOLUME(_unit), true]; + + private _currentState = [_unit, EGVAR(medical,STATE_MACHINE)] call CBA_statemachine_fnc_getCurrentState; + TRACE_2("sending current state",_unit,_currentState); + [QGVAR(localityTransfer), [_unit, _currentState], _unit] call CBA_fnc_targetEvent; +}; diff --git a/addons/medical_statemachine/functions/fnc_localityTransfer.sqf b/addons/medical_statemachine/functions/fnc_localityTransfer.sqf new file mode 100644 index 0000000000..abf4ddd5a3 --- /dev/null +++ b/addons/medical_statemachine/functions/fnc_localityTransfer.sqf @@ -0,0 +1,26 @@ +#include "script_component.hpp" +/* + * Author: PabstMirror + * Handles locality switch. + * + * Arguments: + * 0: Unit + * 1: State + * + * Return Value: + * None + * + * Example: + * [player, "Injured"] call ace_medical_statemachine_fnc_localityTransfer + * + * Public: No + */ + +params ["_unit", "_currentState"]; +TRACE_2("localityTransfer",_unit,_currentState); + +private _oldState = [_unit, EGVAR(medical,STATE_MACHINE)] call CBA_statemachine_fnc_getCurrentState; +if (_oldState != _currentState) then { + TRACE_2("changing state",_oldState,_currentState); + [_unit, EGVAR(medical,STATE_MACHINE), _oldState, _currentState, {}, "LocalityChange"] call CBA_statemachine_fnc_manualTransition; +}; diff --git a/addons/medical_status/CfgEventHandlers.hpp b/addons/medical_status/CfgEventHandlers.hpp index 93107ab7d3..94a04d1af3 100644 --- a/addons/medical_status/CfgEventHandlers.hpp +++ b/addons/medical_status/CfgEventHandlers.hpp @@ -19,8 +19,8 @@ class Extended_PostInit_EventHandlers { class Extended_Init_EventHandlers { class CAManBase { class ADDON { - onRespawn = 1; - init = QUOTE(call FUNC(initUnit)); + init = QUOTE([ARR_2((_this select 0), false)] call FUNC(initUnit)); + exclude[] = {IGNORE_BASE_UAVPILOTS}; }; }; }; diff --git a/addons/medical_status/functions/fnc_initUnit.sqf b/addons/medical_status/functions/fnc_initUnit.sqf index d53cedbc63..95d587778f 100644 --- a/addons/medical_status/functions/fnc_initUnit.sqf +++ b/addons/medical_status/functions/fnc_initUnit.sqf @@ -5,17 +5,23 @@ * * Arguments: * 0: The Unit + * 1: Is Respawned * * Return Value: * None * * Example: - * [bob] call ace_medical_status_fnc_init + * [bob, false] call ace_medical_status_fnc_initUnit * * Public: No */ -params ["_unit"]; +params ["_unit", ["_isRespawn", true]]; +TRACE_2("initUnit",_unit,_isRespawn); + +if (!_isRespawn) then { // Always add respawn EH (same as CBA's onRespawn=1) + _unit addEventHandler ["Respawn", {[(_this select 0), true] call FUNC(initUnit)}]; +}; if (!local _unit) exitWith {}; @@ -23,58 +29,60 @@ if (damage _unit > 0) then { _unit setDamage 0; }; -// - Blood and heart ---------------------------------------------------------- -_unit setVariable [VAR_BLOOD_VOL, DEFAULT_BLOOD_VOLUME, true]; -_unit setVariable [VAR_HEART_RATE, DEFAULT_HEART_RATE, true]; -_unit setVariable [VAR_BLOOD_PRESS, [80, 120], true]; -_unit setVariable [VAR_PERIPH_RES, DEFAULT_PERIPH_RES, true]; -_unit setVariable [VAR_CRDC_ARRST, false, true]; -_unit setVariable [VAR_HEMORRHAGE, 0, true]; +if (_isRespawn) then { + TRACE_1("reseting all vars on respawn",_isRespawn); // note: state is handled by ace_medical_statemachine_fnc_resetStateDefault -// - Pain --------------------------------------------------------------------- -_unit setVariable [VAR_PAIN, 0, true]; -_unit setVariable [VAR_IN_PAIN, false, true]; -_unit setVariable [VAR_PAIN_SUPP, 0, true]; + // - Blood and heart ---------------------------------------------------------- + _unit setVariable [VAR_BLOOD_VOL, DEFAULT_BLOOD_VOLUME, true]; + _unit setVariable [VAR_HEART_RATE, DEFAULT_HEART_RATE, true]; + _unit setVariable [VAR_BLOOD_PRESS, [80, 120], true]; + _unit setVariable [VAR_PERIPH_RES, DEFAULT_PERIPH_RES, true]; + _unit setVariable [VAR_CRDC_ARRST, false, true]; + _unit setVariable [VAR_HEMORRHAGE, 0, true]; -// - Wounds ------------------------------------------------------------------- -_unit setVariable [QEGVAR(medical,openWounds), [], true]; -_unit setVariable [QEGVAR(medical,bandagedWounds), [], true]; -_unit setVariable [QEGVAR(medical,stitchedWounds), [], true]; -_unit setVariable [QEGVAR(medical,isLimping), false, true]; + // - Pain --------------------------------------------------------------------- + _unit setVariable [VAR_PAIN, 0, true]; + _unit setVariable [VAR_IN_PAIN, false, true]; + _unit setVariable [VAR_PAIN_SUPP, 0, true]; -// - Misc --------------------------------------------------------------------- -_unit setVariable [VAR_UNCON, false, true]; + // - Wounds ------------------------------------------------------------------- + _unit setVariable [QEGVAR(medical,openWounds), [], true]; + _unit setVariable [QEGVAR(medical,bandagedWounds), [], true]; + _unit setVariable [QEGVAR(medical,stitchedWounds), [], true]; + _unit setVariable [QEGVAR(medical,isLimping), false, true]; + _unit setVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0], true]; -// - Treatments --------------------------------------------------------------- -_unit setVariable [VAR_TOURNIQUET, DEFAULT_TOURNIQUET_VALUES, true]; -_unit setVariable [QEGVAR(medical,occludedMedications), nil, true]; //Delayed Medications (from tourniquets) -_unit setVariable [QEGVAR(medical,ivBags), nil, true]; + // - Misc --------------------------------------------------------------------- + _unit setVariable [VAR_UNCON, false, true]; -// - Update wound bleeding -[_unit] call EFUNC(medical_status,updateWoundBloodLoss); + // - Treatments --------------------------------------------------------------- + _unit setVariable [VAR_TOURNIQUET, DEFAULT_TOURNIQUET_VALUES, true]; + _unit setVariable [QEGVAR(medical,occludedMedications), nil, true]; //Delayed Medications (from tourniquets) + _unit setVariable [QEGVAR(medical,ivBags), nil, true]; -// triage card and logs -_unit setVariable [QEGVAR(medical,triageLevel), 0, true]; -_unit setVariable [QEGVAR(medical,triageCard), [], true]; + // - Update wound bleeding + [_unit] call EFUNC(medical_status,updateWoundBloodLoss); -// damage storage -_unit setVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0], true]; -#ifdef DEBUG_TESTRESULTS -_unit setVariable [QEGVAR(medical,bodyPartStatus), [0,0,0,0,0,0], true]; -#endif + // triage card and logs + _unit setVariable [QEGVAR(medical,triageLevel), 0, true]; + _unit setVariable [QEGVAR(medical,triageCard), [], true]; -// medication -_unit setVariable [VAR_MEDICATIONS, [], true]; + // damage storage + _unit setVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0], true]; -// TODO move to treatment -private _logs = _unit getVariable [QEGVAR(medical,allLogs), []]; -{ - _unit setVariable [_x, nil]; -} forEach _logs; -_unit setVariable [QEGVAR(medical,allLogs), [], true]; + // medication + _unit setVariable [VAR_MEDICATIONS, [], true]; + + // TODO move to treatment + private _logs = _unit getVariable [QEGVAR(medical,allLogs), []]; + { + _unit setVariable [_x, nil]; + } forEach _logs; + _unit setVariable [QEGVAR(medical,allLogs), [], true]; +}; [{ params ["_unit"]; - TRACE_2("Unit Init",_unit,local _unit); + TRACE_3("Unit Init",_unit,local _unit,typeOf _unit); [QGVAR(initialized), [_unit]] call CBA_fnc_localEvent; }, [_unit], 0.5] call CBA_fnc_waitAndExecute; diff --git a/addons/medical_status/functions/fnc_updateWoundBloodLoss.sqf b/addons/medical_status/functions/fnc_updateWoundBloodLoss.sqf index 2ead65ad3a..037a098c9d 100644 --- a/addons/medical_status/functions/fnc_updateWoundBloodLoss.sqf +++ b/addons/medical_status/functions/fnc_updateWoundBloodLoss.sqf @@ -20,7 +20,7 @@ params ["_unit"]; private _tourniquets = GET_TOURNIQUETS(_unit); private _bodyPartBleeding = [0,0,0,0,0,0]; { - _x params ["", "", "_bodyPart", "_amountOf", "_bleeeding"]; + _x params ["", "_bodyPart", "_amountOf", "_bleeeding"]; if (_tourniquets select _bodyPart == 0) then { _bodyPartBleeding set [_bodyPart, (_bodyPartBleeding select _bodyPart) + (_amountOf * _bleeeding)]; }; diff --git a/addons/medical_treatment/ACE_Medical_Treatment_Actions.hpp b/addons/medical_treatment/ACE_Medical_Treatment_Actions.hpp index ae1582c040..d3cd6dbc0b 100644 --- a/addons/medical_treatment/ACE_Medical_Treatment_Actions.hpp +++ b/addons/medical_treatment/ACE_Medical_Treatment_Actions.hpp @@ -83,6 +83,19 @@ class GVAR(Actions) { callbackSuccess = QFUNC(treatmentTourniquetRemove); condition = QUOTE([ARR_2(_target,_bodyPart)] call FUNC(hasTourniquetAppliedTo)); }; + // --- splint + class Splint: BasicBandage { + displayName = CSTRING(Apply_Splint); + displayNameProgress = CSTRING(Applying_Splint); + category = "bandage"; + icon = QPATHTOEF(medical_gui,ui\splint.paa); + allowedSelections[] = {"LeftArm", "RightArm", "LeftLeg", "RightLeg"}; + items[] = {"ACE_splint"}; + treatmentTime = 7; + callbackSuccess = QFUNC(splint); + condition = QFUNC(splintCondition); + litter[] = {}; + }; // --- syringes class Morphine: FieldDressing { diff --git a/addons/medical_treatment/CfgWeapons.hpp b/addons/medical_treatment/CfgWeapons.hpp index 17ed52fd4f..c2fdb2b5d0 100644 --- a/addons/medical_treatment/CfgWeapons.hpp +++ b/addons/medical_treatment/CfgWeapons.hpp @@ -70,6 +70,17 @@ class CfgWeapons { mass = 2; }; }; + class ACE_splint: ACE_ItemCore { + scope = 2; + author = ECSTRING(common,ACETeam); + displayName = CSTRING(splint_Display); + picture = QPATHTOF(ui\items\tourniquet_x_ca.paa); + model = QPATHTOF(data\tourniquet.p3d); + descriptionShort = CSTRING(splint_Desc_Short); + class ItemInfo: CBA_MiscItem_ItemInfo { + mass = 2; + }; + }; class ACE_morphine: ACE_ItemCore { scope = 2; author = ECSTRING(common,ACETeam); diff --git a/addons/medical_treatment/XEH_PREP.hpp b/addons/medical_treatment/XEH_PREP.hpp index 77e91fbc2a..5d0e71ae53 100644 --- a/addons/medical_treatment/XEH_PREP.hpp +++ b/addons/medical_treatment/XEH_PREP.hpp @@ -36,6 +36,10 @@ PREP(treatmentTourniquet); PREP(treatmentTourniquetLocal); PREP(treatmentTourniquetRemove); +PREP(splint); +PREP(splintCondition); +PREP(splintLocal); + // misc PREP(addToLog); PREP(addToTriageCard); diff --git a/addons/medical_treatment/XEH_postInit.sqf b/addons/medical_treatment/XEH_postInit.sqf index ecaca5cea6..05d78e97d9 100644 --- a/addons/medical_treatment/XEH_postInit.sqf +++ b/addons/medical_treatment/XEH_postInit.sqf @@ -16,6 +16,7 @@ if (isServer) then { [QGVAR(treatmentIVLocal), FUNC(treatmentIVLocal)] call CBA_fnc_addEventHandler; [QGVAR(treatmentCPRLocal), FUNC(treatmentCPRLocal)] call CBA_fnc_addEventHandler; [QGVAR(treatmentFullHealLocal), FUNC(treatmentFullHealLocal)] call CBA_fnc_addEventHandler; +[QGVAR(treatmentSplintLocal), FUNC(splintLocal)] call CBA_fnc_addEventHandler; // action events [QGVAR(actionCheckPulseLocal), FUNC(actionCheckPulseLocal)] call CBA_fnc_addEventHandler; diff --git a/addons/medical_treatment/functions/fnc_canBandage.sqf b/addons/medical_treatment/functions/fnc_canBandage.sqf index 315862c753..58e1246c3c 100644 --- a/addons/medical_treatment/functions/fnc_canBandage.sqf +++ b/addons/medical_treatment/functions/fnc_canBandage.sqf @@ -28,7 +28,7 @@ private _index = ALL_BODY_PARTS find toLower _bodypart; private _canBandage = false; { - _x params ["", "", "_bodyPartN", "_amountOf", "_bleeding"]; + _x params ["", "_bodyPartN", "_amountOf", "_bleeding"]; // If any single wound on the bodypart is bleeding bandaging can go ahead if (_bodyPartN == _index && {_amountOf * _bleeding > 0}) exitWith { diff --git a/addons/medical_treatment/functions/fnc_findMostEffectiveWound.sqf b/addons/medical_treatment/functions/fnc_findMostEffectiveWound.sqf index a136e1be4a..7622106054 100644 --- a/addons/medical_treatment/functions/fnc_findMostEffectiveWound.sqf +++ b/addons/medical_treatment/functions/fnc_findMostEffectiveWound.sqf @@ -37,15 +37,14 @@ private _woundIndex = -1; private _effectivenessFound = -1; { - _x params ["", "_classID", "_partIndexN", "_amountOf", "_bleeding", "_damage", "_category"]; + _x params ["_classID", "_partIndexN", "_amountOf", "_bleeding", "_damage"]; // Ignore wounds on other bodyparts if (_partIndexN == _partIndex) then { private _woundEffectiveness = _effectiveness; // Select the classname from the wound classname storage - private _suffix = ["Minor", "Medium", "Large"] select _category; - private _className = format ["%1%2", EGVAR(medical_damage,woundClassNames) select _classID, _suffix]; + private _className = EGVAR(medical_damage,woundClassNamesComplex) select _classID; // Get the effectiveness of the bandage on this wound type if (isClass (_config >> _className)) then { diff --git a/addons/medical_treatment/functions/fnc_getBandageTime.sqf b/addons/medical_treatment/functions/fnc_getBandageTime.sqf index 442c7ee0b4..48fd27fdac 100644 --- a/addons/medical_treatment/functions/fnc_getBandageTime.sqf +++ b/addons/medical_treatment/functions/fnc_getBandageTime.sqf @@ -22,11 +22,13 @@ if (_partIndex < 0) exitWith { 0 }; private _targetWound = [_patient, _bandage, _partIndex] call FUNC(findMostEffectiveWound); _targetWound params ["_wound", "_woundIndex", "_effectiveness"]; +TRACE_3("findMostEffectiveWound",_wound,_woundIndex,_effectiveness); // Everything is patched up on this body part already if (_wound isEqualTo EMPTY_WOUND) exitWith { 0 }; -_wound params ["", "", "", "_amountOf", "_bloodloss", "_damage", "_category"]; +_wound params ["_classID", "", "_amountOf", "_bloodloss", "_damage"]; +private _category = (_classID % 10); // Base bandage time is based on wound size and remaining percentage private _bandageTime = ([ diff --git a/addons/medical_treatment/functions/fnc_handleBandageOpening.sqf b/addons/medical_treatment/functions/fnc_handleBandageOpening.sqf index 1953dfce20..2c8b46b76e 100644 --- a/addons/medical_treatment/functions/fnc_handleBandageOpening.sqf +++ b/addons/medical_treatment/functions/fnc_handleBandageOpening.sqf @@ -20,12 +20,9 @@ params ["_target", "_impact", "_part", "_injuryIndex", "_injury", "_bandage"]; TRACE_6("handleBandageOpening",_target,_impact,_part,_injuryIndex,_injury,_bandage); -private _classID = _injury select 1; -private _bodyPartN = _injury select 2; -private _category = _injury select 6; -private _postfix = ["Minor", "Medium", "Large"] select _category; -private _className = format ["%1%2", EGVAR(medical_damage,woundClassNames) select _classID, _postfix]; +_injury params ["_classID", "_bodyPartN"]; +private _className = EGVAR(medical_damage,woundClassNamesComplex) select _classID; private _reopeningChance = DEFAULT_BANDAGE_REOPENING_CHANCE; private _reopeningMinDelay = DEFAULT_BANDAGE_REOPENING_MIN_DELAY; private _reopeningMaxDelay = DEFAULT_BANDAGE_REOPENING_MAX_DELAY; @@ -64,17 +61,18 @@ TRACE_5("configs",_bandage,_className,_reopeningChance,_reopeningMinDelay,_reope private _bandagedWounds = _target getVariable [QEGVAR(medical,bandagedWounds), []]; private _exist = false; { - _x params ["", "_id", "_partN", "_amountOf", "", "", "_oldCategory"]; - if (_id == _classID && {_partN == _bodyPartN && {_oldCategory == _category}}) exitWith { - _x set [3, _amountOf + _impact]; - _bandagedWounds set [_forEachIndex, _x]; + _x params ["_id", "_partN", "_amountOf"]; + if (_id == _classID && {_partN == _bodyPartN}) exitWith { + _x set [2, _amountOf + _impact]; + TRACE_2("adding to existing bandagedWound",_id,_partN); _exist = true; }; } forEach _bandagedWounds; if (!_exist) then { + TRACE_2("adding new bandagedWound",_classID,_bodyPartN); private _bandagedInjury = +_injury; - _bandagedInjury set [3, _impact]; + _bandagedInjury set [2, _impact]; _bandagedWounds pushBack _bandagedInjury; }; @@ -96,29 +94,34 @@ if (random 1 <= _reopeningChance) then { private _openWounds = _target getVariable [QEGVAR(medical,openWounds), []]; if (count _openWounds - 1 < _injuryIndex) exitWith { TRACE_2("index bounds",_injuryIndex,count _openWounds); }; - _injury params ["", "_classID", "_bodyPartN", "", "", "", "_category"]; + _injury params ["_classID", "_bodyPartN"]; private _selectedInjury = _openWounds select _injuryIndex; - if (_selectedInjury select 1 == _classID && {_selectedInjury select 2 == _bodyPartN}) then { // matching the IDs + _selectedInjury params ["_selClassID", "_selBodyPart", "_selAmmount"]; + if ((_selClassID == _classID) && {_selBodyPart == _bodyPartN}) then { // matching the IDs private _bandagedWounds = _target getVariable [QEGVAR(medical,bandagedWounds), []]; private _exist = false; { - _x params ["", "_id", "_partN", "_amountOf", "", "", "_oldCategory"]; - if (_id == _classID && {_partN == _bodyPartN && {_oldCategory == _category}}) exitWith { - _x set [3, 0 max (_amountOf - _impact)]; - _bandagedWounds set [_forEachIndex, _x]; + _x params ["_id", "_partN", "_amountOf"]; + if ((_id == _classID) && {_partN == _bodyPartN}) exitWith { + TRACE_2("bandagedWound exists",_id,_classID); + _x set [2, 0 max (_amountOf - _impact)]; _exist = true; }; } forEach _bandagedWounds; if (_exist) then { TRACE_2("Reopening Wound",_bandagedWounds,_openWounds); - _selectedInjury set [3, (_selectedInjury select 3) + _impact]; - _openWounds set [_injuryIndex, _selectedInjury]; + _selectedInjury set [2, _selAmmount + _impact]; _target setVariable [QEGVAR(medical,bandagedWounds), _bandagedWounds, true]; _target setVariable [QEGVAR(medical,openWounds), _openWounds, true]; [_target] call EFUNC(medical_status,updateWoundBloodLoss); + + // Check if we gained limping from this wound re-opening + if ((EGVAR(medical,limping) == 1) && {_bodyPartN > 3}) then { + [_target] call EFUNC(medical_engine,updateDamageEffects); + }; }; } else { TRACE_3("no match",_selectedInjury,_classID,_bodyPartN); diff --git a/addons/medical_treatment/functions/fnc_splint.sqf b/addons/medical_treatment/functions/fnc_splint.sqf new file mode 100644 index 0000000000..4ced63109c --- /dev/null +++ b/addons/medical_treatment/functions/fnc_splint.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: PabstMirror + * Apply a splint to the patient + * + * Arguments: + * 0: The medic + * 1: The patient + * 2: Body part + * + * Return Value: + * Nothing + * + * Example: + * [player, cursorObject, "LeftLeg"] call ace_medical_treatment_fnc_splint + * + * Public: No + */ + +params ["_caller", "_target", "_bodyPart"]; +TRACE_3("splint",_caller,_target,_bodyPart); + +private _partIndex = ALL_BODY_PARTS find toLower _bodyPart; + +[QGVAR(treatmentSplintLocal), [_caller, _target, _partIndex], _target] call CBA_fnc_targetEvent; diff --git a/addons/medical_treatment/functions/fnc_splintCondition.sqf b/addons/medical_treatment/functions/fnc_splintCondition.sqf new file mode 100644 index 0000000000..be8f9abe9f --- /dev/null +++ b/addons/medical_treatment/functions/fnc_splintCondition.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: PabstMirror + * Can apply a splint to the patient + * + * Arguments: + * 0: The medic + * 1: The patient + * 2: Body part + * + * Return Value: + * Nothing + * + * Example: + * [player, cursorObject, "LeftLeg"] call ace_medical_treatment_fnc_splintCondition + * + * Public: No + */ + +params ["", "_patient", "_bodyPart"]; + +private _partIndex = ALL_BODY_PARTS find toLower _bodyPart; +private _fractures = _patient getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]; + +(_fractures select _partIndex) == 1 diff --git a/addons/medical_treatment/functions/fnc_splintLocal.sqf b/addons/medical_treatment/functions/fnc_splintLocal.sqf new file mode 100644 index 0000000000..1996e8321a --- /dev/null +++ b/addons/medical_treatment/functions/fnc_splintLocal.sqf @@ -0,0 +1,31 @@ +#include "script_component.hpp" +/* + * Author: PabstMirror + * Apply a splint to the patient + * + * Arguments: + * 0: The medic + * 1: The patient + * 2: Body part index + * + * Return Value: + * Nothing + * + * Example: + * [player, cursorObject, 4] call ace_medical_treatment_fnc_splintLocal + * + * Public: No + */ + +params ["_caller", "_target", "_bodyPartNum"]; +TRACE_3("splintLocal",_caller,_target,_bodyPart); + +// Place a tourniquet on the bodypart +private _fractures = _target getVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0]]; +_fractures set [_bodyPartNum, -1]; +_target setVariable [QEGVAR(medical,fractures), _fractures, true]; + +// Check if we fixed limping from this treatment +[_target] call EFUNC(medical_engine,updateDamageEffects); + +// toDo: AddToLog: diff --git a/addons/medical_treatment/functions/fnc_treatmentBandageLocal.sqf b/addons/medical_treatment/functions/fnc_treatmentBandageLocal.sqf index 92bb26e5ca..94ab425b00 100644 --- a/addons/medical_treatment/functions/fnc_treatmentBandageLocal.sqf +++ b/addons/medical_treatment/functions/fnc_treatmentBandageLocal.sqf @@ -31,9 +31,10 @@ _targetWound params ["_wound", "_woundIndex", "_effectiveness"]; if (_effectiveness == -1) exitWith {}; // Find the impact this bandage has and reduce the amount this injury is present -private _amountOf = _wound select 3; +private _amountOf = _wound select 2; private _impact = _effectiveness min _amountOf; -_wound set [3, _amountOf - _impact]; +_amountOf = _amountOf - _impact; +_wound set [2, _amountOf]; _openWounds set [_woundIndex, _wound]; _target setVariable [QEGVAR(medical,openWounds), _openWounds, true]; @@ -45,4 +46,9 @@ if (_impact > 0 && {GVAR(advancedBandages) && {GVAR(woundReopening)}}) then { [_target, _impact, _partIndex, _woundIndex, _wound, _bandage] call FUNC(handleBandageOpening); }; +// Check if we fixed limping from this treatment +if ((EGVAR(medical,limping) == 1) && {_partIndex > 3} && {_amountOf <= 0} && {_target getVariable [QEGVAR(medical,isLimping), false]}) then { + [_target] call EFUNC(medical_engine,updateDamageEffects); +}; + true diff --git a/addons/medical_treatment/functions/fnc_treatmentFullHealLocal.sqf b/addons/medical_treatment/functions/fnc_treatmentFullHealLocal.sqf index dfc712872b..705efc716e 100644 --- a/addons/medical_treatment/functions/fnc_treatmentFullHealLocal.sqf +++ b/addons/medical_treatment/functions/fnc_treatmentFullHealLocal.sqf @@ -39,6 +39,7 @@ _target setVariable [QEGVAR(medical,openWounds), [], true]; _target setVariable [QEGVAR(medical,bandagedWounds), [], true]; _target setVariable [QEGVAR(medical,stitchedWounds), [], true]; _target setVariable [QEGVAR(medical,isLimping), false, true]; +_target setVariable [QEGVAR(medical,fractures), [0,0,0,0,0,0], true]; // - Update wound bleeding [_target] call EFUNC(medical_status,updateWoundBloodLoss); @@ -53,9 +54,6 @@ _target setVariable [QEGVAR(medical,ivBags), nil, true]; // damage storage _target setVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0], true]; -#ifdef DEBUG_TESTRESULTS -_target setVariable [QEGVAR(medical,bodyPartStatus), [0,0,0,0,0,0], true]; -#endif // generic medical admin _target setVariable [VAR_CRDC_ARRST, false, true]; @@ -70,7 +68,7 @@ _target setVariable [VAR_MEDICATIONS, [], true]; // Reset triage card since medication is all reset _target setVariable [QEGVAR(medical,triageCard), [], true]; -[_target, false] call EFUNC(medical_engine,setLimping); +[_target] call EFUNC(medical_engine,updateDamageEffects); // Resetting damage _target setDamage 0; diff --git a/addons/medical_treatment/functions/fnc_treatmentSurgicalKit_onProgress.sqf b/addons/medical_treatment/functions/fnc_treatmentSurgicalKit_onProgress.sqf index 17d5aff0b6..66d2373804 100644 --- a/addons/medical_treatment/functions/fnc_treatmentSurgicalKit_onProgress.sqf +++ b/addons/medical_treatment/functions/fnc_treatmentSurgicalKit_onProgress.sqf @@ -26,7 +26,7 @@ private _bandagedWounds = _target getVariable [QEGVAR(medical,bandagedWounds), [ private _stitchedWounds = _target getVariable [QEGVAR(medical,stitchedWounds), []]; //In case two people stitch up one patient and the last wound has already been closed we can stop already -if (count _bandagedWounds == 0) exitWith { false }; +if (_bandagedWounds isEqualTo []) exitWith { false }; //Has enough time elapsed that we can close another wound? if (_totalTime - _elapsedTime <= (count _bandagedWounds - 1) * 5) then { @@ -35,6 +35,15 @@ if (_totalTime - _elapsedTime <= (count _bandagedWounds - 1) * 5) then { _target setVariable [QEGVAR(medical,bandagedWounds), _bandagedWounds, true]; _target setVariable [QEGVAR(medical,stitchedWounds), _stitchedWounds, true]; TRACE_3("stitched",_treatedWound,count _bandagedWounds,count _stitchedWounds); + + // Check if we fixed limping from this treatment + if ((EGVAR(medical,limping) == 2) && {_target getVariable [QEGVAR(medical,isLimping), false]}) then { + _treatedWound params ["", "_partN"]; + if (_partN > 3) then { // only for LEG wounds + TRACE_3("updating damage effects",_target,_partN,local _target); + [QEGVAR(medical_engine,updateDamageEffects), [_target], _target] call CBA_fnc_targetEvent; + }; + }; }; true diff --git a/addons/medical_treatment/functions/fnc_treatment_success.sqf b/addons/medical_treatment/functions/fnc_treatment_success.sqf index 903e32e677..d3329574cb 100644 --- a/addons/medical_treatment/functions/fnc_treatment_success.sqf +++ b/addons/medical_treatment/functions/fnc_treatment_success.sqf @@ -56,7 +56,7 @@ private _partIndex = (ALL_BODY_PARTS find toLower _bodyPart) max 0; private _openWounds = _target getVariable [QEGVAR(medical,openWounds), []]; { - _x params ["", "", "_bodyPartN", "_amountOf", "_percentageOpen"]; + _x params ["", "_bodyPartN", "_amountOf", "_percentageOpen"]; if (_bodyPartN isEqualTo _partIndex) then { _bloodLossOnBodyPart = _bloodLossOnBodyPart + (_amountOf * _percentageOpen); diff --git a/addons/medical_treatment/stringtable.xml b/addons/medical_treatment/stringtable.xml index 75b9dffc8d..01fd092717 100644 --- a/addons/medical_treatment/stringtable.xml +++ b/addons/medical_treatment/stringtable.xml @@ -765,6 +765,12 @@ 用于压迫静脉与动脉的血液流动, 达到减缓失血速度的目的 用於壓迫靜脈與動脈的血液流動, 達到減緩失血速度的目的 + + Splint + + + Stabilizes a fractured limb + Morphine autoinjector Morphium-Autoinjektor @@ -1960,6 +1966,12 @@ 移除军用止血带 移除軍用止血帶 + + Apply Splint + + + Applying Splint... + Diagnose Diagnose