diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 75fa85e3d8..3cf65b6cc0 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -1,7 +1,9 @@ #include "script_component.hpp" /* - * Author: commy2 - * Main HandleDamage EH function for soldiers. + * Author: commy2, SilentSpike + * 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". * * Arguments: * Handle damage EH @@ -13,49 +15,35 @@ */ // for travis #define HIT_STRUCTURAL QGVAR($#structural) -#define HIT_CRASH QGVAR($#crash) -params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator"]; -//diag_log text str _this; +params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint"]; // HD sometimes triggers for remote units - ignore. -if (!local _unit) exitWith {nil}; +if !(local _unit) exitWith {nil}; // Get missing meta info -private ["_hitPoint", "_oldDamage"]; -private _isCrash = false; +private _oldDamage = 0; -// Store -if (_hitPointIndex < 0) then { +if (_hitPoint isEqualTo "") then { _hitPoint = "#structural"; _oldDamage = damage _unit; - - // Handle vehicle crashes - if (_damage == _unit getVariable [HIT_CRASH, -1]) then { - _isCrash = (_ammo == ""); - _unit setVariable [HIT_CRASH, -1]; - } else { - _unit setVariable [HIT_CRASH, _damage]; - }; } else { - _hitPoint = toLower (getAllHitPointsDamage _unit select 0 select _hitPointIndex); _oldDamage = _unit getHitIndex _hitPointIndex; - - // No crash, reset - _unit setVariable [HIT_CRASH, -1]; }; // Damage can be disabled with old variable or via sqf command allowDamage if !(isDamageAllowed _unit && _unit getVariable [QEGVAR(medical,allowDamage), true]) exitWith {_oldDamage}; +// Damages are stored for "ace_hdbracket" event triggered last private _newDamage = _damage - _oldDamage; _unit setVariable [format [QGVAR($%1), _hitPoint], _newDamage]; -// These control blood material visuals. -// If damage is in dummy hitpoints, "hands" and "legs", don't change anything +// 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}; -// Add injury +// 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 { _unit setVariable [QGVAR(lastShooter), _shooter]; _unit setVariable [QGVAR(lastInstigator), _instigator]; @@ -80,10 +68,8 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), 0]; private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), 0]; - // Find hit point that received the maxium damage. - // second param is a priority. should multiple hitpoints receive the same - // amount of damage (e.g. max which is 4), we don't want them to be sorted - // alphabetically (which would mean that RightLeg is always chosen) + // Find hit point that received the maxium damage + // Priority used for sorting if incoming damage is equivalent (e.g. max which is 4) private _allDamages = [ [_damageHead, PRIORITY_HEAD, "Head"], [_damageBody, PRIORITY_BODY, "Body"], @@ -97,57 +83,69 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { _allDamages sort false; (_allDamages select 0) params ["_receivedDamage", "", "_woundedHitPoint"]; + // We know it's structural when no specific hitpoint is damaged if (_receivedDamage == 0) then { _receivedDamage = _damageStructural; _woundedHitPoint = "Body"; }; - TRACE_2("received",_receivedDamage,_woundedHitPoint); - // Check for falling damage. + // Environmental damage sources all have empty ammo string + // No explicit source given, we infer from differences between them if (_ammo isEqualTo "") then { - if (velocity _unit select 2 < -2) then { - if (_receivedDamage < 0.35) then { - // Less than ~ 5 m - _woundedHitPoint = selectRandom ["LeftLeg", "RightLeg"]; + // Any collision with terrain/vehicle/object has a shooter + // Check this first because burning can happen at any velocity + if !(isNull _shooter) then { + _ammo = "#collision"; + + /* + If shooter != unit then they hit unit, otherwise it could be: + - Unit hitting anything at speed + - An empty vehicle hitting unit + - A physX object hitting unit + Assume fall damage for downward velocity because it's most common + */ + if (_shooter == _unit && {(velocity _unit select 2) < -2}) then { + _woundedHitPoint = selectRandom ["RightLeg", "LeftLeg"]; + TRACE_5("Fall",_unit,_shooter,_instigator,_damage,_receivedDamage); } else { - // More than ~ 5 m - _woundedHitPoint = selectRandom ["LeftLeg", "RightLeg", "Body", "Head"]; + _woundedHitPoint = selectRandom ["RightArm", "LeftArm", "RightLeg", "LeftLeg"]; + TRACE_5("Collision",_unit,_shooter,_instigator,_damage,_receivedDamage); + }; + + // Significant damage suggests high relative velocity + // Momentum transfers to body/head for worse wounding + if (_receivedDamage > 0.35) then { + _woundedHitPoint = selectRandom ["Body", "Head"]; }; - _ammo = "#falling"; } else { - if (_receivedDamage > 0.1) then { - // Assume collision damage. - _woundedHitPoint = "Body"; - _ammo = "#vehiclecrash"; + // Anything else is almost guaranteed to be fire damage + _woundedHitPoint = selectRandom ["LeftLeg", "RightLeg", "Body"]; + _ammo = "#unknown"; + + // Fire damage can occur as lots of minor damage events + // Combine these until significant enough to wound + private _combinedDamage = _receivedDamage + (_unit getVariable [QGVAR(trivialDamage), 0]); + if (_combinedDamage > 0.1) then { + _unit setVariable [QGVAR(trivialDamage), 0]; + _receivedDamage = _combinedDamage; + TRACE_5("Burning",_unit,_shooter,_instigator,_damage,_receivedDamage); } else { - // Probably fire damage: - _woundedHitPoint = selectRandom ["LeftLeg", "RightLeg", "Body"]; - _ammo = "#unknown"; - private _combinedDamage = _receivedDamage + (_unit getVariable [QGVAR(trivialDamage), 0]); - if (_combinedDamage > 0.1) then { - // if the new sum is large enough, reset variable and continue with it added in - _unit setVariable [QGVAR(trivialDamage), 0]; - TRACE_2("Using sum of trivialDamage",_receivedDamage,_combinedDamage); - _receivedDamage = _combinedDamage; - } else { - // otherwise just save the new sum into the variable and exit - _unit setVariable [QGVAR(trivialDamage), _combinedDamage]; - _receivedDamage = 0; - }; + _unit setVariable [QGVAR(trivialDamage), _combinedDamage]; + _receivedDamage = 0; }; }; }; - // Don't trigger for minor damage. + // No wounds for minor damage if (_receivedDamage > 1E-3) then { [QEGVAR(medical,woundReceived), [_unit, _woundedHitPoint, _receivedDamage, _shooter, _ammo]] call CBA_fnc_localEvent; + TRACE_2("received",_receivedDamage,_woundedHitPoint); }; - // resetting these single-damage-event tracker vars, if we don't do this then - // subsequent wounds will be piled onto the selection which has accumulated - // the most wounding + // Clear stored damages otherwise they will influence future damage events + // (aka wounds will pile onto the historically most damaged hitpoint) { - _unit setVariable [_x, 0]; + _unit setVariable [_x, nil]; } forEach [ QGVAR($HitFace),QGVAR($HitNeck),QGVAR($HitHead), QGVAR($HitPelvis),QGVAR($HitAbdomen),QGVAR($HitDiaphragm),QGVAR($HitChest),QGVAR($HitBody), @@ -157,19 +155,34 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { 0 }; -// Check for drowning damage. -// Don't change the third expression. Safe method for FLOATs. -if (_hitPoint isEqualTo "#structural" && {getOxygenRemaining _unit <= 0.5} && {_damage isEqualTo (_oldDamage + 0.005)}) exitWith { +// 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" && + {getOxygenRemaining _unit <= 0.5} && + {_damage isEqualTo (_oldDamage + 0.005)} +) exitWith { [QEGVAR(medical,woundReceived), [_unit, "Body", _newDamage, _unit, "#drowning"]] call CBA_fnc_localEvent; + TRACE_5("Drowning",_unit,_shooter,_instigator,_damage,_newDamage); 0 }; -// Handle vehicle crashes -if (_isCrash) exitWith { +// 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 +private _vehicle = vehicle _unit; +if ( + _hitPoint isEqualTo "#structural" && + {_ammo isEqualTo ""} && + {_vehicle != _unit} && + {vectorMagnitude (velocity _vehicle) > 5} + // todo: no way to detect if stationary and another vehicle hits you +) exitWith { [QEGVAR(medical,woundReceived), [_unit, "Body", _newDamage, _unit, "#vehiclecrash"]] call CBA_fnc_localEvent; + TRACE_5("Crash",_unit,_shooter,_instigator,_damage,_newDamage); 0 }; +// We store our own damage values so engine damage is unnecessary 0