2018-07-30 09:22:14 +00:00
|
|
|
#include "script_component.hpp"
|
2016-07-30 12:00:42 +00:00
|
|
|
/*
|
2018-08-15 06:57:11 +00:00
|
|
|
* 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".
|
2016-07-30 12:00:42 +00:00
|
|
|
*
|
|
|
|
* Arguments:
|
|
|
|
* Handle damage EH
|
|
|
|
*
|
|
|
|
* Return Value:
|
|
|
|
* Damage to be inflicted <NUMBER>
|
|
|
|
*
|
|
|
|
* Public: No
|
|
|
|
*/
|
2016-09-26 18:52:08 +00:00
|
|
|
// for travis
|
|
|
|
#define HIT_STRUCTURAL QGVAR($#structural)
|
2016-09-26 18:45:29 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint"];
|
2016-07-30 12:00:42 +00:00
|
|
|
|
|
|
|
// HD sometimes triggers for remote units - ignore.
|
2018-08-15 06:57:11 +00:00
|
|
|
if !(local _unit) exitWith {nil};
|
2016-07-30 12:00:42 +00:00
|
|
|
|
|
|
|
// Get missing meta info
|
2018-08-15 06:57:11 +00:00
|
|
|
private _oldDamage = 0;
|
2016-07-30 12:00:42 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
if (_hitPoint isEqualTo "") then {
|
2016-07-30 12:00:42 +00:00
|
|
|
_hitPoint = "#structural";
|
|
|
|
_oldDamage = damage _unit;
|
|
|
|
} else {
|
|
|
|
_oldDamage = _unit getHitIndex _hitPointIndex;
|
|
|
|
};
|
|
|
|
|
2018-07-18 18:31:29 +00:00
|
|
|
// Damage can be disabled with old variable or via sqf command allowDamage
|
2019-03-17 20:26:15 +00:00
|
|
|
if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]}) exitWith {_oldDamage};
|
2018-07-18 18:31:29 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// Damages are stored for "ace_hdbracket" event triggered last
|
2016-07-30 12:00:42 +00:00
|
|
|
private _newDamage = _damage - _oldDamage;
|
2016-09-26 16:30:39 +00:00
|
|
|
_unit setVariable [format [QGVAR($%1), _hitPoint], _newDamage];
|
2016-07-30 12:00:42 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// Engine damage to these hitpoints controls blood visuals, limping, weapon sway
|
|
|
|
// Handled in fnc_damageBodyPart, persist here
|
2016-09-26 17:59:00 +00:00
|
|
|
if (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"]) exitWith {_oldDamage};
|
2016-07-30 12:00:42 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// This hitpoint is set to trigger last, evaluate all the stored damage values
|
|
|
|
// to determine where wounds are applied
|
2016-07-30 12:00:42 +00:00
|
|
|
if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
|
2019-04-27 19:04:31 +00:00
|
|
|
_unit setVariable [QEGVAR(medical,lastDamageSource), _shooter];
|
|
|
|
_unit setVariable [QEGVAR(medical,lastInstigator), _instigator];
|
2016-10-12 21:20:22 +00:00
|
|
|
|
2016-09-26 18:52:08 +00:00
|
|
|
private _damageStructural = _unit getVariable [HIT_STRUCTURAL, 0];
|
2016-09-26 16:30:39 +00:00
|
|
|
|
2016-07-30 12:00:42 +00:00
|
|
|
// --- Head
|
2016-09-26 16:30:39 +00:00
|
|
|
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;
|
2016-07-30 12:00:42 +00:00
|
|
|
|
|
|
|
// --- Body
|
2016-09-26 16:30:39 +00:00
|
|
|
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;
|
2016-07-30 12:00:42 +00:00
|
|
|
|
|
|
|
// --- Arms and Legs
|
2016-09-26 16:30:39 +00:00
|
|
|
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];
|
2016-07-30 12:00:42 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// Find hit point that received the maxium damage
|
|
|
|
// Priority used for sorting if incoming damage is equivalent (e.g. max which is 4)
|
2016-07-30 12:00:42 +00:00
|
|
|
private _allDamages = [
|
2016-09-26 16:30:39 +00:00
|
|
|
[_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"]
|
2016-07-30 12:00:42 +00:00
|
|
|
];
|
2016-09-26 16:30:39 +00:00
|
|
|
TRACE_2("incoming",_allDamages,_damageStructural);
|
2016-07-30 12:00:42 +00:00
|
|
|
|
2019-10-08 15:44:49 +00:00
|
|
|
// represents all incoming damage for selecting a non-selectionSpecific wound location, (used for selectRandomWeighted [value1,weight1,value2....])
|
|
|
|
private _damageSelectionArray = [
|
|
|
|
HITPOINT_INDEX_HEAD, _damageHead, HITPOINT_INDEX_BODY, _damageBody, HITPOINT_INDEX_LARM, _damageLeftArm,
|
|
|
|
HITPOINT_INDEX_RARM, _damageRightArm, HITPOINT_INDEX_LLEG, _damageLeftLeg, HITPOINT_INDEX_RLEG, _damageRightLeg
|
|
|
|
];
|
|
|
|
|
2016-07-30 12:00:42 +00:00
|
|
|
_allDamages sort false;
|
2016-09-26 16:30:39 +00:00
|
|
|
(_allDamages select 0) params ["_receivedDamage", "", "_woundedHitPoint"];
|
2019-07-16 18:00:28 +00:00
|
|
|
if (_damageHead >= HEAD_DAMAGE_THRESHOLD) then {
|
|
|
|
TRACE_3("reporting fatal head damage instead of max",_damageHead,_receivedDamage,_woundedHitPoint);
|
|
|
|
_receivedDamage = _damageHead;
|
|
|
|
_woundedHitPoint = "Head";
|
|
|
|
};
|
2016-09-26 16:30:39 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// We know it's structural when no specific hitpoint is damaged
|
2016-09-26 16:30:39 +00:00
|
|
|
if (_receivedDamage == 0) then {
|
|
|
|
_receivedDamage = _damageStructural;
|
|
|
|
_woundedHitPoint = "Body";
|
2019-10-08 15:44:49 +00:00
|
|
|
_damageSelectionArray = [1, 1]; // sum of weights would be 0
|
2016-09-26 16:30:39 +00:00
|
|
|
};
|
2016-07-30 12:00:42 +00:00
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// Environmental damage sources all have empty ammo string
|
|
|
|
// No explicit source given, we infer from differences between them
|
2016-07-30 12:00:42 +00:00
|
|
|
if (_ammo isEqualTo "") then {
|
2018-08-15 06:57:11 +00:00
|
|
|
// Any collision with terrain/vehicle/object has a shooter
|
|
|
|
// Check this first because burning can happen at any velocity
|
|
|
|
if !(isNull _shooter) then {
|
2019-10-08 15:44:49 +00:00
|
|
|
_ammo = "#collision"; // non-selectionSpecific so only _damageSelectionArray matters
|
2018-08-15 06:57:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
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 {
|
2019-10-08 15:44:49 +00:00
|
|
|
_ammo = "#falling"; // non-selectionSpecific so only _damageSelectionArray matters
|
2019-10-20 21:48:26 +00:00
|
|
|
_damageSelectionArray = [HITPOINT_INDEX_RLEG, 1, HITPOINT_INDEX_LLEG, 1];
|
2018-08-15 06:57:11 +00:00
|
|
|
TRACE_5("Fall",_unit,_shooter,_instigator,_damage,_receivedDamage);
|
2016-12-05 20:34:20 +00:00
|
|
|
} else {
|
2019-10-20 21:48:26 +00:00
|
|
|
_damageSelectionArray = [HITPOINT_INDEX_RARM, 1, HITPOINT_INDEX_LARM, 1, HITPOINT_INDEX_LLEG, 1, HITPOINT_INDEX_RLEG, 1];
|
2018-08-15 06:57:11 +00:00
|
|
|
TRACE_5("Collision",_unit,_shooter,_instigator,_damage,_receivedDamage);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Significant damage suggests high relative velocity
|
|
|
|
// Momentum transfers to body/head for worse wounding
|
2019-03-21 18:33:51 +00:00
|
|
|
// Higher momentum results in higher chance for head to be hit for more lethality
|
2018-08-15 06:57:11 +00:00
|
|
|
if (_receivedDamage > 0.35) then {
|
2019-03-21 18:33:51 +00:00
|
|
|
private _headHitWeight = (_receivedDamage / 2) min 1;
|
2019-10-08 15:44:49 +00:00
|
|
|
if (_recievedDamage < 0.6) then {
|
|
|
|
_damageSelectionArray append [0, (1 - _headHitWeight), 1, _headHitWeight];
|
|
|
|
} else {
|
|
|
|
_damageSelectionArray = [0, (1 - _headHitWeight), 1, _headHitWeight];
|
|
|
|
}
|
2016-12-05 20:34:20 +00:00
|
|
|
};
|
2016-07-30 12:00:42 +00:00
|
|
|
} else {
|
2018-08-15 06:57:11 +00:00
|
|
|
// Anything else is almost guaranteed to be fire damage
|
2019-10-20 21:48:26 +00:00
|
|
|
_damageSelectionArray = [HITPOINT_INDEX_BODY, 1, HITPOINT_INDEX_LLEG, 1, HITPOINT_INDEX_RLEG, 1];;
|
2019-10-08 15:44:49 +00:00
|
|
|
_ammo = "#unknown"; // non-selectionSpecific so only _damageSelectionArray matters
|
2018-08-15 06:57:11 +00:00
|
|
|
|
|
|
|
// 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);
|
2017-06-05 16:42:00 +00:00
|
|
|
} else {
|
2018-08-15 06:57:11 +00:00
|
|
|
_unit setVariable [QGVAR(trivialDamage), _combinedDamage];
|
|
|
|
_receivedDamage = 0;
|
2017-06-05 16:42:00 +00:00
|
|
|
};
|
2016-07-30 12:00:42 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// No wounds for minor damage
|
2016-07-30 12:00:42 +00:00
|
|
|
if (_receivedDamage > 1E-3) then {
|
2019-10-08 15:44:49 +00:00
|
|
|
TRACE_3("received",_receivedDamage,_woundedHitPoint,_damageSelectionArray);
|
|
|
|
[QEGVAR(medical,woundReceived), [_unit, _woundedHitPoint, _receivedDamage, _shooter, _ammo, _damageSelectionArray]] call CBA_fnc_localEvent;
|
2016-07-30 12:00:42 +00:00
|
|
|
};
|
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// Clear stored damages otherwise they will influence future damage events
|
|
|
|
// (aka wounds will pile onto the historically most damaged hitpoint)
|
2018-07-16 21:49:07 +00:00
|
|
|
{
|
2018-08-15 06:57:11 +00:00
|
|
|
_unit setVariable [_x, nil];
|
2018-07-16 21:49:07 +00:00
|
|
|
} 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)
|
|
|
|
];
|
|
|
|
|
2016-07-30 12:00:42 +00:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// 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 {
|
2019-10-08 15:44:49 +00:00
|
|
|
[QEGVAR(medical,woundReceived), [_unit, "Body", _newDamage, _unit, "#drowning", [HITPOINT_INDEX_BODY, 1]]] call CBA_fnc_localEvent;
|
2018-08-15 06:57:11 +00:00
|
|
|
TRACE_5("Drowning",_unit,_shooter,_instigator,_damage,_newDamage);
|
2016-10-20 13:02:24 +00:00
|
|
|
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// 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 {
|
2019-10-08 15:44:49 +00:00
|
|
|
private _damageSelectionArray = [
|
|
|
|
HITPOINT_INDEX_HEAD, 1, HITPOINT_INDEX_BODY, 1, HITPOINT_INDEX_LARM, 1,
|
|
|
|
HITPOINT_INDEX_RARM, 1, HITPOINT_INDEX_LLEG, 1, HITPOINT_INDEX_RLEG, 1
|
|
|
|
];
|
|
|
|
[QEGVAR(medical,woundReceived), [_unit, "Body", _newDamage, _unit, "#vehiclecrash", _damageSelectionArray]] call CBA_fnc_localEvent;
|
2018-08-15 06:57:11 +00:00
|
|
|
TRACE_5("Crash",_unit,_shooter,_instigator,_damage,_newDamage);
|
2016-10-20 13:02:24 +00:00
|
|
|
|
|
|
|
0
|
2016-07-30 12:00:42 +00:00
|
|
|
};
|
|
|
|
|
2018-08-15 06:57:11 +00:00
|
|
|
// We store our own damage values so engine damage is unnecessary
|
2016-09-26 17:59:00 +00:00
|
|
|
0
|