ACE3/addons/medical_engine/functions/fnc_handleDamage.sqf
Rutger "RedBery" Meijering 15036d7fd8 Fixes to unconsciousness and damageTypes systems (#6589)
* Fixes BleedingCoef having impact on bloodloss KO
Fixes incorrect var in setUnconscious

* Changes method for checking if typeOfDamage is valid
Adds check for if typeOfDamage is valid to extension version of woundsHandler

* Changes collision wound location select to a weighted system to make it more lethal at faster collisions
Adds collision and drowning damage types to config (drowning has no related wounds to prevent wounds being added while drowning).

* Fixes unneeded space

* Fixes minor mistake (body damage chance should go from 1 to 0 without ever becoming negative)
2019-03-21 19:33:51 +01:00

191 lines
8.0 KiB
Plaintext

#include "script_component.hpp"
/*
* 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
*
* Return Value:
* Damage to be inflicted <NUMBER>
*
* Public: No
*/
// for travis
#define HIT_STRUCTURAL QGVAR($#structural)
params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint"];
// HD sometimes triggers for remote units - ignore.
if !(local _unit) exitWith {nil};
// Get missing meta info
private _oldDamage = 0;
if (_hitPoint isEqualTo "") then {
_hitPoint = "#structural";
_oldDamage = damage _unit;
} else {
_oldDamage = _unit getHitIndex _hitPointIndex;
};
// 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];
// 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};
// 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];
private _damageStructural = _unit getVariable [HIT_STRUCTURAL, 0];
// --- Head
private _damageFace = _unit getVariable [QGVAR($HitFace), 0];
private _damageNeck = _unit getVariable [QGVAR($HitNeck), 0];
private _damageHead = (_unit getVariable [QGVAR($HitHead), 0]) max _damageFace max _damageNeck;
// --- Body
private _damagePelvis = _unit getVariable [QGVAR($HitPelvis), 0];
private _damageAbdomen = _unit getVariable [QGVAR($HitAbdomen), 0];
private _damageDiaphragm = _unit getVariable [QGVAR($HitDiaphragm), 0];
private _damageChest = _unit getVariable [QGVAR($HitChest), 0];
private _damageBody = (_unit getVariable [QGVAR($HitBody), 0]) max _damagePelvis max _damageAbdomen max _damageDiaphragm max _damageChest;
// --- Arms and Legs
private _damageLeftArm = _unit getVariable [QGVAR($HitLeftArm), 0];
private _damageRightArm = _unit getVariable [QGVAR($HitRightArm), 0];
private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), 0];
private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), 0];
// 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"],
[_damageLeftArm, PRIORITY_LEFT_ARM, "LeftArm"],
[_damageRightArm, PRIORITY_RIGHT_ARM, "RightArm"],
[_damageLeftLeg, PRIORITY_LEFT_LEG, "LeftLeg"],
[_damageRightLeg, PRIORITY_RIGHT_LEG, "RightLeg"]
];
TRACE_2("incoming",_allDamages,_damageStructural);
_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";
};
// Environmental damage sources all have empty ammo string
// No explicit source given, we infer from differences between them
if (_ammo isEqualTo "") then {
// 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 {
_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
// Higher momentum results in higher chance for head to be hit for more lethality
if (_receivedDamage > 0.35) then {
private _headHitWeight = (_receivedDamage / 2) min 1;
_woundedHitPoint = selectRandomWeighted ["Body", (1 - _headHitWeight), "Head", _headHitWeight];
};
} else {
// 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 {
_unit setVariable [QGVAR(trivialDamage), _combinedDamage];
_receivedDamage = 0;
};
};
};
// 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);
};
// Clear stored damages otherwise they will influence future damage events
// (aka wounds will pile onto the historically most damaged hitpoint)
{
_unit setVariable [_x, nil];
} forEach [
QGVAR($HitFace),QGVAR($HitNeck),QGVAR($HitHead),
QGVAR($HitPelvis),QGVAR($HitAbdomen),QGVAR($HitDiaphragm),QGVAR($HitChest),QGVAR($HitBody),
QGVAR($HitLeftArm),QGVAR($HitRightArm),QGVAR($HitLeftLeg),QGVAR($HitRightLeg)
];
0
};
// 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
};
// 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