11#include " ..\script_component.hpp"
2-
32/*
43 * Author: commy2, kymckay, modified by johnb43
54 * Original:
65 * HandleDamage EH where wound events are raised based on incoming damage.
76 * Be aware that for each source of damage, the EH can fire multiple times (once for each hitpoint).
87 * We store these incoming damages and compare them on our final hitpoint: "ace_hdbracket".
98 * Added:
10- * Handling of damage to allow armor modifcation.
9+ * Handling of damage to allow armor modifcation. For ACE 3.18.0 and later.
1110 *
1211 * Arguments:
1312 * Handle damage EH
1918 */
2019
2120params [" _args" , [" _ignoreAllowDamageACE" , false ]];
22- _args params [" _unit" , " _selection" , " _damage" , " _shooter" , " _ammo" , " _hitPointIndex" , " _instigator" , " _hitpoint" , " _directHit" ];
21+ _args params [" _unit" , " _selection" , " _damage" , " _shooter" , " _ammo" , " _hitPointIndex" , " _instigator" , " _hitpoint" , " _directHit" , " _context " ];
2322
2423// HD sometimes triggers for remote units - ignore.
2524if ! (local _unit ) exitWith {nil };
2625
2726// Get missing meta info
2827private _oldDamage = 0 ;
28+ private _structuralDamage = _context == 0 ;
2929
3030if (_hitPoint isEqualTo " " ) then {
3131 _hitPoint = " #structural" ;
@@ -38,26 +38,30 @@ if (_hitPoint isEqualTo "") then {
3838if ! (isDamageAllowed _unit && {_unit getVariable [" ace_medical_allowDamage" , true ] || _ignoreAllowDamageACE }) exitWith {_oldDamage };
3939
4040private _newDamage = _damage - _oldDamage ;
41+
42+ // _newDamage == 0 happens occasionally for vehiclehit events (see line 80 onwards), just exit early to save some frametime
43+ // context 4 is engine "bleeding". For us, it's just a duplicate event for #structural which we can ignore without any issues
44+ if (_context ! = 2 && {_context == 4 || _newDamage == 0 }) exitWith {
45+ TRACE_4(" Skipping engine bleeding or zero damage" ,_ammo ,_newDamage ,_directHit ,_context );
46+ _oldDamage
47+ };
48+
4149// Get scaled armor value of hitpoint and calculate damage before armor
4250// We scale using passThrough to handle explosive-resistant armor properly (#9063)
4351// We need realDamage to determine which limb was hit correctly
4452[_unit , _hitpoint ] call ace_medical_engine_fnc_getHitpointArmor params [" _armor" , " _armorScaled" ];
45-
4653private _realDamage = _newDamage * _armor ;
47-
48- // ACE <3.16.0 does not return "_armorScaled"
49- if (_hitPoint isNotEqualTo " #structural" && {! isNil " _armorScaled" }) then {
50- private _armorCoef = _armor / _armorScaled ;
54+ if (! _structuralDamage ) then {
55+ private _armorCoef = _armor / _armorScaled ;
5156 private _damageCoef = linearConversion [0 , 1 , ace_medical_engine_damagePassThroughEffect, 1 , _armorCoef ];
5257 _newDamage = _newDamage * _damageCoef ;
5358};
54-
55- TRACE_4(" Received hit" ,_hitpoint ,_ammo ,_newDamage ,_realDamage );
59+ TRACE_6(" Received hit" ,_hitpoint ,_ammo ,_newDamage ,_realDamage ,_directHit ,_context );
5660
5761// Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
5862// Damage occurs in consistent increments
5963if (
60- _hitPoint isEqualTo " #structural " &&
64+ _structuralDamage &&
6165 {getOxygenRemaining _unit <= 0.5 } &&
6266 {_damage isEqualTo (_oldDamage + 0.005 )}
6367) exitWith {
6771 0
6872};
6973
74+ // Faster than (vehicle _unit), also handles dead units
75+ private _vehicle = objectParent _unit ;
76+ private _inVehicle = ! isNull _vehicle ;
77+ private _environmentDamage = _ammo == " " ;
78+
7079// Crashing a vehicle doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
7180// It does fire the EH multiple times, but this seems to scale with the intensity of the crash
72- private _vehicle = vehicle _unit ;
7381if (
7482 ace_medical_enableVehicleCrashes &&
75- {_hitPoint isEqualTo " #structural" } &&
76- {_ammo isEqualTo " " } &&
77- {! isNull _vehicle } &&
83+ {_environmentDamage && _inVehicle && _structuralDamage } &&
7884 {vectorMagnitude (velocity _vehicle ) > 5 }
7985 // todo: no way to detect if stationary and another vehicle hits you
8086) exitWith {
8591};
8692
8793// Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint
88- // This is the case for mines, explosives, artillery, and catastrophic vehicle explosions
89- // Triggers twice, but that doesn't matter as damage is low
94+ // This is the case for mines, explosives, artillery, and catasthrophic vehicle explosions
9095if (
91- _hitPoint isEqualTo " #structural" &&
92- {! isNull _vehicle } &&
93- {_ammo isNotEqualTo " " } &&
96+ (! _environmentDamage && _inVehicle && _structuralDamage ) &&
9497 {
9598 private _ammoCfg = configFile >> " CfgAmmo" >> _ammo ;
9699 GET_NUMBER(_ammoCfg >> " explosive" ,0 ) > 0 ||
97100 {GET_NUMBER(_ammoCfg >> " indirectHit" ,0 ) > 0 }
98101 }
99- ) exitwith {
100- TRACE_6 (" Vehicle hit" ,_unit ,_shooter ,_instigator ,_damage ,_newDamage , _damages );
102+ ) exitWith {
103+ TRACE_5 (" Vehicle hit" ,_unit ,_shooter ,_instigator ,_damage ,_newDamage );
101104
102105 _unit setVariable [" ace_medical_lastDamageSource" , _shooter ];
103106 _unit setVariable [" ace_medical_lastInstigator" , _instigator ];
@@ -107,9 +110,59 @@ if (
107110 0
108111};
109112
110- // This hitpoint is set to trigger last, evaluate all the stored damage values
111- // to determine where wounds are applied
112- if (_hitPoint isEqualTo " ace_hdbracket" ) exitWith {
113+ // Get setting for particular unit
114+ private _multiplierArray = switch (true ) do {
115+ case (_hitPoint in [" hitface" , " hitneck" , " hithead" ]): {
116+ _unit getVariable [QGVAR(hitPointMultiplier_head), [GVAR(hitPointMultiplier_ai_head), GVAR(hitPointMultiplier_player_head)] select (isPlayer _unit )]
117+ };
118+ case (_hitPoint in [" hitpelvis" ," hitabdomen" , " hitdiaphragm" , " hitchest" ]): {
119+ _unit getVariable [QGVAR(hitPointMultiplier_chest), [GVAR(hitPointMultiplier_ai_chest), GVAR(hitPointMultiplier_player_chest)] select (isPlayer _unit )]
120+ };
121+ case (_hitPoint in [" hitleftarm" , " hitrightarm" , " hitleftleg" , " hitrightleg" ]): {
122+ _unit getVariable [QGVAR(hitPointMultiplier_limb), [GVAR(hitPointMultiplier_ai_limb), GVAR(hitPointMultiplier_player_limb)] select (isPlayer _unit )]
123+ };
124+ default {
125+ DEFAULT_SETTINGS
126+ };
127+ };
128+
129+ private _modifiedNewDamage = _newDamage ;
130+ private _modifiedRealDamage = _realDamage ;
131+
132+ // If default settings, we don't need to change anything, so skip calculcations and let ace handle damage
133+ if (_multiplierArray isNotEqualTo DEFAULT_SETTINGS) then {
134+ _multiplierArray params [" _hitPointMultiplier" , " _armorMin" , " _armorMax" ];
135+
136+ switch (true ) do {
137+ case (_armorMin >= 1 && {_armor < _armorMin }): {
138+ // This will decrease damage
139+ _modifiedNewDamage = _newDamage * _armor / _armorMin ;
140+ _modifiedRealDamage = _realDamage * _armor / _armorMin ;
141+
142+ TRACE_6(" Under min armor" ,_armor ,_armorMin ,_newDamage ,_modifiedNewDamage ,_realDamage ,_modifiedRealDamage );
143+ };
144+ case (_armorMax >= 1 && {_armor > _armorMax }): {
145+ // This will increase damage
146+ _modifiedNewDamage = _newDamage * _armor / _armorMax ;
147+ _modifiedRealDamage = _realDamage * _armor / _armorMax ;
148+
149+ TRACE_6(" Over max armor" ,_armor ,_armorMax ,_newDamage ,_modifiedNewDamage ,_realDamage ,_modifiedRealDamage );
150+ };
151+ };
152+
153+ _modifiedNewDamage = _modifiedNewDamage / _hitPointMultiplier ;
154+ _modifiedRealDamage = _modifiedRealDamage / _hitPointMultiplier ;
155+
156+ TRACE_5(" Hitpoint damage multiplied" ,_armor ,_newDamage ,_modifiedNewDamage ,_realDamage ,_modifiedRealDamage );
157+ };
158+
159+ // Damages are stored for last iteration of the HandleDamage event (_context == 2)
160+ _unit setVariable [format [" ace_medical_engine_$%1" , _hitPoint ], [_realDamage , _newDamage , _modifiedRealDamage , _modifiedNewDamage ]];
161+
162+ // Ref https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#HandleDamage
163+ // Context 2 means this is the last iteration of HandleDamage, so figure out which hitpoint took the most real damage and send wound event
164+ // Don't exit, as the last iteration can be one of the hitpoints that we need to keep _oldDamage for
165+ if (_context == 2 ) then {
113166 _unit setVariable [" ace_medical_lastDamageSource" , _shooter ];
114167 _unit setVariable [" ace_medical_lastInstigator" , _instigator ];
115168
@@ -161,7 +214,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
161214
162215 // Environmental damage sources all have empty ammo string
163216 // No explicit source given, we infer from differences between them
164- if (_ammo isEqualTo " " ) then {
217+ if (_environmentDamage ) then {
165218 // Any collision with terrain/vehicle/object has a shooter
166219 // Check this first because burning can happen at any velocity
167220 if ! (isNull _shooter ) then {
@@ -203,62 +256,9 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
203256 " ace_medical_engine_$HitLeftArm" ," ace_medical_engine_$HitRightArm" ," ace_medical_engine_$HitLeftLeg" ," ace_medical_engine_$HitRightLeg" ,
204257 " ace_medical_engine_$#structural"
205258 ];
206-
207- 0
208- };
209-
210- // Get setting for particular unit
211- private _multiplierArray = switch (true ) do {
212- case (_hitPoint in [" hitface" , " hitneck" , " hithead" ]): {
213- _unit getVariable [QGVAR(hitPointMultiplier_head), [GVAR(hitPointMultiplier_ai_head), GVAR(hitPointMultiplier_player_head)] select (isPlayer _unit )]
214- };
215- case (_hitPoint in [" hitpelvis" ," hitabdomen" , " hitdiaphragm" , " hitchest" ]): {
216- _unit getVariable [QGVAR(hitPointMultiplier_chest), [GVAR(hitPointMultiplier_ai_chest), GVAR(hitPointMultiplier_player_chest)] select (isPlayer _unit )]
217- };
218- case (_hitPoint in [" hitleftarm" , " hitrightarm" , " hitleftleg" , " hitrightleg" ]): {
219- _unit getVariable [QGVAR(hitPointMultiplier_limb), [GVAR(hitPointMultiplier_ai_limb), GVAR(hitPointMultiplier_player_limb)] select (isPlayer _unit )]
220- };
221- default {
222- DEFAULT_SETTINGS
223- };
224- };
225-
226- private _modifiedNewDamage = _newDamage ;
227- private _modifiedRealDamage = _realDamage ;
228-
229- // If default settings, we don't need to change anything, so skip calculcations and let ace handle damage
230- if (_multiplierArray isNotEqualTo DEFAULT_SETTINGS) then {
231- _multiplierArray params [" _hitPointMultiplier" , " _armorMin" , " _armorMax" ];
232-
233- switch (true ) do {
234- case (_armorMin >= 1 && {_armor < _armorMin }): {
235- // This will decrease damage
236- _modifiedNewDamage = _newDamage * _armor / _armorMin ;
237- _modifiedRealDamage = _realDamage * _armor / _armorMin ;
238-
239- TRACE_6(" Under min armor" ,_armor ,_armorMin ,_newDamage ,_modifiedNewDamage ,_realDamage ,_modifiedRealDamage );
240- };
241- case (_armorMax >= 1 && {_armor > _armorMax }): {
242- // This will increase damage
243- _modifiedNewDamage = _newDamage * _armor / _armorMax ;
244- _modifiedRealDamage = _realDamage * _armor / _armorMax ;
245-
246- TRACE_6(" Over max armor" ,_armor ,_armorMax ,_newDamage ,_modifiedNewDamage ,_realDamage ,_modifiedRealDamage );
247- };
248- };
249-
250- _modifiedNewDamage = _modifiedNewDamage / _hitPointMultiplier ;
251- _modifiedRealDamage = _modifiedRealDamage / _hitPointMultiplier ;
252-
253- TRACE_5(" Hitpoint damage multiplied" ,_armor ,_newDamage ,_modifiedNewDamage ,_realDamage ,_modifiedRealDamage );
254259};
255260
256- // Damages are stored for "ace_hdbracket" event triggered last
257- _unit setVariable [format [" ace_medical_engine_$%1" , _hitPoint ], [_realDamage , _newDamage , _modifiedRealDamage , _modifiedNewDamage ]];
258-
259261// Engine damage to these hitpoints controls blood visuals, limping, weapon sway
260262// Handled in fnc_damageBodyPart, persist here
261- if (_hitPoint in [" hithead" , " hitbody" , " hithands" , " hitlegs" ]) exitWith {_oldDamage };
262-
263- // We store our own damage values so engine damage is unnecessary
264- 0
263+ // For all other hitpoints, we store our own damage values, so engine damage is unnecessary
264+ [0 , _oldDamage ] select (_hitPoint in [" hithead" , " hitbody" , " hithands" , " hitlegs" ])
0 commit comments