Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix environmental damage sources #6515

Merged
merged 5 commits into from
Aug 15, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 79 additions & 66 deletions addons/medical_engine/functions/fnc_handleDamage.sqf
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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];
Expand All @@ -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"],
Expand All @@ -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"];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should actually raise an additional wounding event in these cases (if you hit the ground at high speed the injury wouldn't fully transfer to your body - you'd still break your legs).

};
_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),
Expand All @@ -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