mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Fix environmental damage sources (#6515)
* Tidy handle damage code * Fix environmental damage conditions - Vehicle damage would never occur because it doesn't fire all the hitpoints - Missing sanity check on fall damage to check that unit isn't in a vehicle - Getting hit by a vehicle while on foot can be detected by checking for a shooter * Improve collision handling - All collisions have a `_shooter` so we simply first check for that because burning doesn't and it could happen at any velocity - We don't actually care what caused the collision because they should all cause the same type of wounding - The exception is fall damage because we want to prioritise wounding the legs, we assume if the `_unit` had downward velocity and `_shooter == _unit` that it was fall damage (not necessarily always true, but almost always is and there doesn't look to be a more specific way to check).
This commit is contained in:
parent
5fbaf426a8
commit
11547d7ff8
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user