From a85074a797a067646c273f4beb2bcaad3c06e5c2 Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Tue, 11 Jun 2024 11:11:28 -0300 Subject: [PATCH] Medical Engine - Remove ACE_HDBracket hitpoint (#9732) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Medical Engine - Remove ACE_HDBracket hitpoint * skip context 4 * move checks to vars * skip uav/logic entities * add check for arm/leg hitpoints * Update addons/medical_engine/functions/fnc_handleDamage.sqf Co-authored-by: Jouni Järvinen * don't skip context 2 * clearer custom hitpoint array name * reenable compile cache * remove debug mode * lazy eval * whitespace * update comment * Update fnc_handleDamage.sqf header * Update addons/medical_engine/functions/fnc_handleDamage.sqf Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com> * Update addons/medical_engine/XEH_postInit.sqf Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com> * update hitpoint test * missing " * Update addons/medical/dev/test_hitpointConfigs.sqf Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com> --------- Co-authored-by: Jouni Järvinen Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com> --- addons/medical/dev/test_hitpointConfigs.sqf | 21 +++---- addons/medical_engine/XEH_postInit.sqf | 28 ++++----- addons/medical_engine/XEH_preInit.sqf | 2 + .../functions/fnc_handleDamage.sqf | 58 +++++++++---------- .../medical_engine/script_macros_config.hpp | 11 ---- 5 files changed, 49 insertions(+), 71 deletions(-) diff --git a/addons/medical/dev/test_hitpointConfigs.sqf b/addons/medical/dev/test_hitpointConfigs.sqf index 9de5c5e686..7bdeb189c2 100644 --- a/addons/medical/dev/test_hitpointConfigs.sqf +++ b/addons/medical/dev/test_hitpointConfigs.sqf @@ -21,23 +21,16 @@ INFO_1("Checking uniforms for correct medical hitpoints [%1 units]",count _units private _testPass = true; { private _typeOf = configName _x; - private _hitpoints = (configProperties [_x >> "HitPoints", "isClass _x", true]) apply {configName _x}; + private _hitpoints = (configProperties [_x >> "HitPoints", "isClass _x", true]) apply {toLowerANSI configName _x}; + private _expectedHitPoints = ["hitleftarm","hitrightarm","hitleftleg","hitrightleg","hithead","hitbody"]; + private _missingHitPoints = _expectedHitPoints select {!(_x in _hitpoints)}; + if (_missingHitPoints isNotEqualTo []) then { + WARNING_3("%1 missing ace hitpoints: %2 - class hitpoints: %3",_typeOf,_missingHitPoints,_hitpoints); + _testPass = false; + }; // _typeOf createUnit [position player, group player, "z = this"]; // deleteVehicle z; - - private _lastHitpoint = (_hitpoints param [(count _hitpoints) - 1, "#array"]); - if (_lastHitpoint != "ACE_HDBracket") then { - WARNING_2("%1 has bad last hitpoint: %2",_typeOf,_hitpoints); - _testPass = false; - }; - - if (((_hitpoints findIf {_x == "HitLeftArm"}) == -1) || {(_hitpoints findIf {_x == "HitRightArm"}) == -1} - || {(_hitpoints findIf {_x == "HitLeftLeg"}) == -1} || {(_hitpoints findIf {_x == "HitRightLeg"}) == -1} - || {(_hitpoints findIf {_x == "HitHead"}) == -1} || {(_hitpoints findIf {_x == "HitBody"}) == -1}) then { - WARNING_2("%1 missing ace hitpoints: %2",_typeOf,_hitpoints); - _testPass = false; - }; } forEach _units; _testPass diff --git a/addons/medical_engine/XEH_postInit.sqf b/addons/medical_engine/XEH_postInit.sqf index 2514c62254..ed66091498 100644 --- a/addons/medical_engine/XEH_postInit.sqf +++ b/addons/medical_engine/XEH_postInit.sqf @@ -6,27 +6,23 @@ [_new] call FUNC(updateDamageEffects); // Run on new controlled unit to update QGVAR(aimFracture) }, true] call CBA_fnc_addPlayerEventHandler; - ["CAManBase", "init", { params ["_unit"]; - // Check if last hit point is our dummy. - private _allHitPoints = getAllHitPointsDamage _unit param [0, []]; - reverse _allHitPoints; - while {(_allHitPoints param [0, ""]) select [0,1] == "#"} do { WARNING_1("Ignoring Reflector hitpoint %1",_allHitPoints deleteAt 0); }; + if (unitIsUAV _unit) exitWith {TRACE_1("ignore UAV AI",typeOf _unit);}; + if (getNumber (configOf _unit >> "isPlayableLogic") == 1) exitWith {TRACE_1("ignore logic unit",typeOf _unit);}; - if (_allHitPoints param [0, ""] != "ACE_HDBracket") then { - if (unitIsUAV _unit) exitWith {TRACE_1("ignore UAV AI",typeOf _unit);}; - if (getNumber ((configOf _unit) >> "isPlayableLogic") == 1) exitWith {TRACE_1("ignore logic unit",typeOf _unit)}; + private _allHitPoints = getAllHitPointsDamage _unit param [0, []]; + if ((GVAR(customHitpoints) arrayIntersect _allHitPoints) isNotEqualTo GVAR(customHitpoints)) exitWith { ERROR_1("Bad hitpoints for unit type ""%1""",typeOf _unit); - } else { - // Calling this function inside curly brackets allows the usage of - // "exitWith", which would be broken with "HandleDamage" otherwise. - _unit setVariable [ - QEGVAR(medical,HandleDamageEHID), - _unit addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}] - ]; }; + + // Calling this function inside curly brackets allows the usage of + // "exitWith", which would be broken with "HandleDamage" otherwise. + _unit setVariable [ + QEGVAR(medical,HandleDamageEHID), + _unit addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}] + ]; }, nil, [IGNORE_BASE_UAVPILOTS], true] call CBA_fnc_addClassEventHandler; #ifdef DEBUG_MODE_FULL @@ -88,7 +84,7 @@ }; }] call CBA_fnc_addEventHandler; -["CAManBase", "deleted", { +["CAManBase", "Deleted", { params ["_unit"]; TRACE_3("unit deleted",_unit,objectParent _unit,local _unit); if ((!isNull objectParent _unit) && {local objectParent _unit}) then { diff --git a/addons/medical_engine/XEH_preInit.sqf b/addons/medical_engine/XEH_preInit.sqf index b0304f167f..02e431103a 100644 --- a/addons/medical_engine/XEH_preInit.sqf +++ b/addons/medical_engine/XEH_preInit.sqf @@ -46,6 +46,8 @@ GVAR(animations) setVariable [QUNCON_ANIM(faceDown), [QUNCON_ANIM(1),QUNCON_ANIM GVAR(animations) setVariable [QUNCON_ANIM(faceLeft), [QUNCON_ANIM(7),QUNCON_ANIM(8),QUNCON_ANIM(1_1),QUNCON_ANIM(7_1),QUNCON_ANIM(8_1)]]; GVAR(animations) setVariable [QUNCON_ANIM(faceRight), [QUNCON_ANIM(5),QUNCON_ANIM(6),QUNCON_ANIM(10),QUNCON_ANIM(5_1),QUNCON_ANIM(6_1)]]; +GVAR(customHitpoints) = ["hitleftarm", "hitrightarm", "hitleftleg", "hitrightleg"]; + private _fnc_fixStatic = { params ["_vehicle"]; private _type = typeOf _vehicle; diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index a60816222f..61de4997b9 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -1,9 +1,9 @@ #include "..\script_component.hpp" /* - * Author: commy2, kymckay + * Author: commy2, kymckay, LinkIsGrim * HandleDamage EH where wound events are raised based on incoming damage. * Be aware that for each source of damage, the EH can fire multiple times (once for each hitpoint). - * We store these incoming damages and compare them on our final hitpoint: "ace_hdbracket". + * We store these incoming damages and compare them on last iteration of the event (_context == 2). * * Arguments: * Handle damage EH @@ -13,15 +13,16 @@ * * Public: No */ -params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint"]; +params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint", "_directHit", "_context"]; // HD sometimes triggers for remote units - ignore. if !(local _unit) exitWith {nil}; // Get missing meta info private _oldDamage = 0; +private _structuralDamage = _context == 0; -if (_hitPoint isEqualTo "") then { +if (_structuralDamage) then { _hitPoint = "#structural"; _oldDamage = damage _unit; } else { @@ -33,26 +34,29 @@ if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), t private _newDamage = _damage - _oldDamage; -// Happens occasionally for vehiclehit events (see line 80 onwards) -// Just exit early to save some frametime -if (_newDamage == 0 && {_hitpoint isNotEqualTo "ace_hdbracket"}) exitWith {_oldDamage}; +// _newDamage == 0 happens occasionally for vehiclehit events (see line 80 onwards), just exit early to save some frametime +// context 4 is engine "bleeding". For us, it's just a duplicate event for #structural which we can ignore without any issues +if (_context != 2 && {_context == 4 || _newDamage == 0}) exitWith { + TRACE_4("Skipping engine bleeding or zero damage",_ammo,_newDamage,_directHit,_context); + _oldDamage +}; // Get scaled armor value of hitpoint and calculate damage before armor // We scale using passThrough to handle explosive-resistant armor properly (#9063) // We need realDamage to determine which limb was hit correctly [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; -if (_hitPoint isNotEqualTo "#structural") then { +if (!_structuralDamage) then { private _armorCoef = _armor/_armorScaled; private _damageCoef = linearConversion [0, 1, GVAR(damagePassThroughEffect), 1, _armorCoef]; _newDamage = _newDamage * _damageCoef; }; -TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); +TRACE_6("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_directHit,_context); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs // Damage occurs in consistent increments if ( - _hitPoint isEqualTo "#structural" && + _structuralDamage && {getOxygenRemaining _unit <= 0.5} && {_damage isEqualTo (_oldDamage + 0.005)} ) exitWith { @@ -64,14 +68,14 @@ if ( // Faster than (vehicle _unit), also handles dead units private _vehicle = objectParent _unit; +private _inVehicle = !isNull _vehicle; +private _environmentDamage = _ammo == ""; // Crashing a vehicle doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs // It does fire the EH multiple times, but this seems to scale with the intensity of the crash if ( EGVAR(medical,enableVehicleCrashes) && - {_hitPoint isEqualTo "#structural"} && - {_ammo isEqualTo ""} && - {!isNull _vehicle} && + {_environmentDamage && _inVehicle && _structuralDamage} && {vectorMagnitude (velocity _vehicle) > 5} // todo: no way to detect if stationary and another vehicle hits you ) exitWith { @@ -83,11 +87,8 @@ if ( // Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint // This is the case for mines, explosives, artillery, and catasthrophic vehicle explosions -// Triggers twice, but that doesn't matter as damage is low if ( - _hitPoint isEqualTo "#structural" && - {!isNull _vehicle} && - {_ammo isNotEqualTo ""} && + (!_environmentDamage && _inVehicle && _structuralDamage) && { private _ammoCfg = configFile >> "CfgAmmo" >> _ammo; GET_NUMBER(_ammoCfg >> "explosive",0) > 0 || @@ -104,9 +105,13 @@ if ( 0 }; -// This hitpoint is set to trigger last, evaluate all the stored damage values -// to determine where wounds are applied -if (_hitPoint isEqualTo "ace_hdbracket") exitWith { +// Damages are stored for last iteration of the HandleDamage event (_context == 2) +_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]]; + +// Ref https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#HandleDamage +// Context 2 means this is the last iteration of HandleDamage, so figure out which hitpoint took the most real damage and send wound event +// Don't exit, as the last iteration can be one of the hitpoints that we need to keep _oldDamage for +if (_context == 2) then { _unit setVariable [QEGVAR(medical,lastDamageSource), _shooter]; _unit setVariable [QEGVAR(medical,lastInstigator), _instigator]; @@ -157,7 +162,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them - if (_ammo isEqualTo "") then { + if (_environmentDamage) then { // Any collision with terrain/vehicle/object has a shooter // Check this first because burning can happen at any velocity if !(isNull _shooter) then { @@ -199,16 +204,9 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { QGVAR($HitLeftArm),QGVAR($HitRightArm),QGVAR($HitLeftLeg),QGVAR($HitRightLeg), QGVAR($#structural) ]; - - 0 }; -// Damages are stored for "ace_hdbracket" event triggered last -_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]]; - // Engine damage to these hitpoints controls blood visuals, limping, weapon sway // Handled in fnc_damageBodyPart, persist here -if (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"]) exitWith {_oldDamage}; - -// We store our own damage values so engine damage is unnecessary -0 +// For all other hitpoints, we store our own damage values, so engine damage is unnecessary +[0, _oldDamage] select (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"]) diff --git a/addons/medical_engine/script_macros_config.hpp b/addons/medical_engine/script_macros_config.hpp index 611a8ad356..7f65a4b912 100644 --- a/addons/medical_engine/script_macros_config.hpp +++ b/addons/medical_engine/script_macros_config.hpp @@ -47,15 +47,4 @@ };\ class HitRightLeg: HitLeftLeg {\ name = "leg_r";\ - };\ - class ACE_HDBracket {\ - armor = 1;\ - material = -1;\ - name = "head";\ - passThrough = 0;\ - radius = 1;\ - explosionShielding = 1;\ - visual = "";\ - minimalHit = 0;\ - depends = "HitHead";\ }