From 056c35bec250fd202875d0a566a52560b32a83e0 Mon Sep 17 00:00:00 2001
From: johnb432 <58661205+johnb432@users.noreply.github.com>
Date: Tue, 9 Jul 2024 20:54:34 +0200
Subject: [PATCH 1/2] Getting ready for ACE 3.18.0
---
README.md | 2 +
README_steam.md | 2 +
addons/main/CfgVersioning.hpp | 12 ++
addons/main/config.cpp | 1 +
addons/main/functions/fnc_handleDamage.sqf | 157 +++++++++++----------
5 files changed, 96 insertions(+), 78 deletions(-)
create mode 100644 addons/main/CfgVersioning.hpp
diff --git a/README.md b/README.md
index 05bbf79..a5cb0d5 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@ _unit setVariable ["armor_modifier_ace_main_hitPointMultiplier_chest", [armorMul
_unit setVariable ["armor_modifier_ace_main_hitPointMultiplier_limb", [armorMultiplier, minimumArmor, maximumArmor], true];
```
+2 is usually the minimum hitpoint armor a unit has without any equipment on.
+
Examples
* If **Player hitpoint damage reduction - chest** is set to `[10, 0, 0]`, it means that you take a 10th of the damage to the chest.
diff --git a/README_steam.md b/README_steam.md
index 24d0cf2..4c43664 100644
--- a/README_steam.md
+++ b/README_steam.md
@@ -29,6 +29,8 @@ _unit setVariable ["armor_modifier_ace_main_hitPointMultiplier_chest", [armorMul
_unit setVariable ["armor_modifier_ace_main_hitPointMultiplier_limb", [armorMultiplier, minimumArmor, maximumArmor], true];
[/code]
+2 is usually the minimum hitpoint armor a unit has without any equipment on.
+
[h2]Examples[/h2]
[list]
[*] If [b]Player hitpoint damage reduction - chest[/b] is set to '[10, 0, 0]', it means that you take a 10th of the damage to the chest.
diff --git a/addons/main/CfgVersioning.hpp b/addons/main/CfgVersioning.hpp
new file mode 100644
index 0000000..b8af963
--- /dev/null
+++ b/addons/main/CfgVersioning.hpp
@@ -0,0 +1,12 @@
+class CfgSettings {
+ class CBA {
+ class Versioning {
+ class PREFIX {
+ class Dependencies {
+ ACE[] = {"ace_main", {3, 18, 0}, "true"};
+ CBA[] = {"cba_main", {3, 16, 0}, "true"};
+ };
+ };
+ };
+ };
+};
diff --git a/addons/main/config.cpp b/addons/main/config.cpp
index 4a17b1a..5a887c2 100644
--- a/addons/main/config.cpp
+++ b/addons/main/config.cpp
@@ -20,3 +20,4 @@ class CfgPatches {
};
#include "CfgEventHandlers.hpp"
+#include "CfgVersioning.hpp"
diff --git a/addons/main/functions/fnc_handleDamage.sqf b/addons/main/functions/fnc_handleDamage.sqf
index 737488e..5cb80bb 100644
--- a/addons/main/functions/fnc_handleDamage.sqf
+++ b/addons/main/functions/fnc_handleDamage.sqf
@@ -19,13 +19,14 @@
*/
params ["_args", ["_ignoreAllowDamageACE", false]];
-_args params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint", "_directHit"];
+_args params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint", "_directHit", "_context"];
// HD sometimes triggers for remote units - ignore.
if !(local _unit) exitWith {nil};
// Get missing meta info
private _oldDamage = 0;
+private _structuralDamage = _context == 0;
if (_hitPoint isEqualTo "") then {
_hitPoint = "#structural";
@@ -38,26 +39,30 @@ if (_hitPoint isEqualTo "") then {
if !(isDamageAllowed _unit && {_unit getVariable ["ace_medical_allowDamage", true] || _ignoreAllowDamageACE}) exitWith {_oldDamage};
private _newDamage = _damage - _oldDamage;
+
+// _newDamage == 0 happens occasionally for vehiclehit events (see line 80 onwards), just exit early to save some frametime
+// context 4 is engine "bleeding". For us, it's just a duplicate event for #structural which we can ignore without any issues
+if (_context != 2 && {_context == 4 || _newDamage == 0}) exitWith {
+ TRACE_4("Skipping engine bleeding or zero damage",_ammo,_newDamage,_directHit,_context);
+ _oldDamage
+};
+
// Get scaled armor value of hitpoint and calculate damage before armor
// We scale using passThrough to handle explosive-resistant armor properly (#9063)
// We need realDamage to determine which limb was hit correctly
[_unit, _hitpoint] call ace_medical_engine_fnc_getHitpointArmor params ["_armor", "_armorScaled"];
-
private _realDamage = _newDamage * _armor;
-
-// ACE <3.16.0 does not return "_armorScaled"
-if (_hitPoint isNotEqualTo "#structural" && {!isNil "_armorScaled"}) then {
- private _armorCoef = _armor / _armorScaled;
+if (!_structuralDamage) then {
+ private _armorCoef = _armor/_armorScaled;
private _damageCoef = linearConversion [0, 1, ace_medical_engine_damagePassThroughEffect, 1, _armorCoef];
_newDamage = _newDamage * _damageCoef;
};
-
-TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage);
+TRACE_6("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_directHit,_context);
// 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" &&
+ _structuralDamage &&
{getOxygenRemaining _unit <= 0.5} &&
{_damage isEqualTo (_oldDamage + 0.005)}
) exitWith {
@@ -67,14 +72,16 @@ if (
0
};
+// Faster than (vehicle _unit), also handles dead units
+private _vehicle = objectParent _unit;
+private _inVehicle = !isNull _vehicle;
+private _environmentDamage = _ammo == "";
+
// 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 (
ace_medical_enableVehicleCrashes &&
- {_hitPoint isEqualTo "#structural"} &&
- {_ammo isEqualTo ""} &&
- {!isNull _vehicle} &&
+ {_environmentDamage && _inVehicle && _structuralDamage} &&
{vectorMagnitude (velocity _vehicle) > 5}
// todo: no way to detect if stationary and another vehicle hits you
) exitWith {
@@ -85,19 +92,16 @@ if (
};
// Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint
-// This is the case for mines, explosives, artillery, and catastrophic vehicle explosions
-// Triggers twice, but that doesn't matter as damage is low
+// This is the case for mines, explosives, artillery, and catasthrophic vehicle explosions
if (
- _hitPoint isEqualTo "#structural" &&
- {!isNull _vehicle} &&
- {_ammo isNotEqualTo ""} &&
+ (!_environmentDamage && _inVehicle && _structuralDamage) &&
{
private _ammoCfg = configFile >> "CfgAmmo" >> _ammo;
GET_NUMBER(_ammoCfg >> "explosive",0) > 0 ||
{GET_NUMBER(_ammoCfg >> "indirectHit",0) > 0}
}
) exitwith {
- TRACE_6("Vehicle hit",_unit,_shooter,_instigator,_damage,_newDamage,_damages);
+ TRACE_5("Vehicle hit",_unit,_shooter,_instigator,_damage,_newDamage);
_unit setVariable ["ace_medical_lastDamageSource", _shooter];
_unit setVariable ["ace_medical_lastInstigator", _instigator];
@@ -107,9 +111,59 @@ if (
0
};
-// 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 {
+// Get setting for particular unit
+private _multiplierArray = switch (true) do {
+ case (_hitPoint in ["hitface", "hitneck", "hithead"]): {
+ _unit getVariable [QGVAR(hitPointMultiplier_head), [GVAR(hitPointMultiplier_ai_head), GVAR(hitPointMultiplier_player_head)] select (isPlayer _unit)]
+ };
+ case (_hitPoint in ["hitpelvis" ,"hitabdomen", "hitdiaphragm", "hitchest"]): {
+ _unit getVariable [QGVAR(hitPointMultiplier_chest), [GVAR(hitPointMultiplier_ai_chest), GVAR(hitPointMultiplier_player_chest)] select (isPlayer _unit)]
+ };
+ case (_hitPoint in ["hitleftarm", "hitrightarm", "hitleftleg", "hitrightleg"]): {
+ _unit getVariable [QGVAR(hitPointMultiplier_limb), [GVAR(hitPointMultiplier_ai_limb), GVAR(hitPointMultiplier_player_limb)] select (isPlayer _unit)]
+ };
+ default {
+ DEFAULT_SETTINGS
+ };
+};
+
+private _modifiedNewDamage = _newDamage;
+private _modifiedRealDamage = _realDamage;
+
+// If default settings, we don't need to change anything, so skip calculcations and let ace handle damage
+if (_multiplierArray isNotEqualTo DEFAULT_SETTINGS) then {
+ _multiplierArray params ["_hitPointMultiplier", "_armorMin", "_armorMax"];
+
+ switch (true) do {
+ case (_armorMin >= 1 && {_armor < _armorMin}): {
+ // This will decrease damage
+ _modifiedNewDamage = _newDamage * _armor / _armorMin;
+ _modifiedRealDamage = _realDamage * _armor / _armorMin;
+
+ TRACE_6("Under min armor",_armor,_armorMin,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
+ };
+ case (_armorMax >= 1 && {_armor > _armorMax}): {
+ // This will increase damage
+ _modifiedNewDamage = _newDamage * _armor / _armorMax;
+ _modifiedRealDamage = _realDamage * _armor / _armorMax;
+
+ TRACE_6("Over max armor",_armor,_armorMax,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
+ };
+ };
+
+ _modifiedNewDamage = _modifiedNewDamage / _hitPointMultiplier;
+ _modifiedRealDamage = _modifiedRealDamage / _hitPointMultiplier;
+
+ TRACE_5("Hitpoint damage multiplied",_armor,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
+};
+
+// Damages are stored for last iteration of the HandleDamage event (_context == 2)
+_unit setVariable [format ["ace_medical_engine_$%1", _hitPoint], [_realDamage, _newDamage, _modifiedRealDamage, _modifiedNewDamage]];
+
+// Ref https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#HandleDamage
+// Context 2 means this is the last iteration of HandleDamage, so figure out which hitpoint took the most real damage and send wound event
+// Don't exit, as the last iteration can be one of the hitpoints that we need to keep _oldDamage for
+if (_context == 2) then {
_unit setVariable ["ace_medical_lastDamageSource", _shooter];
_unit setVariable ["ace_medical_lastInstigator", _instigator];
@@ -161,7 +215,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
// Environmental damage sources all have empty ammo string
// No explicit source given, we infer from differences between them
- if (_ammo isEqualTo "") then {
+ if (_environmentDamage) then {
// Any collision with terrain/vehicle/object has a shooter
// Check this first because burning can happen at any velocity
if !(isNull _shooter) then {
@@ -203,62 +257,9 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
"ace_medical_engine_$HitLeftArm","ace_medical_engine_$HitRightArm","ace_medical_engine_$HitLeftLeg","ace_medical_engine_$HitRightLeg",
"ace_medical_engine_$#structural"
];
-
- 0
};
-// Get setting for particular unit
-private _multiplierArray = switch (true) do {
- case (_hitPoint in ["hitface", "hitneck", "hithead"]): {
- _unit getVariable [QGVAR(hitPointMultiplier_head), [GVAR(hitPointMultiplier_ai_head), GVAR(hitPointMultiplier_player_head)] select (isPlayer _unit)]
- };
- case (_hitPoint in ["hitpelvis" ,"hitabdomen", "hitdiaphragm", "hitchest"]): {
- _unit getVariable [QGVAR(hitPointMultiplier_chest), [GVAR(hitPointMultiplier_ai_chest), GVAR(hitPointMultiplier_player_chest)] select (isPlayer _unit)]
- };
- case (_hitPoint in ["hitleftarm", "hitrightarm", "hitleftleg", "hitrightleg"]): {
- _unit getVariable [QGVAR(hitPointMultiplier_limb), [GVAR(hitPointMultiplier_ai_limb), GVAR(hitPointMultiplier_player_limb)] select (isPlayer _unit)]
- };
- default {
- DEFAULT_SETTINGS
- };
-};
-
-private _modifiedNewDamage = _newDamage;
-private _modifiedRealDamage = _realDamage;
-
-// If default settings, we don't need to change anything, so skip calculcations and let ace handle damage
-if (_multiplierArray isNotEqualTo DEFAULT_SETTINGS) then {
- _multiplierArray params ["_hitPointMultiplier", "_armorMin", "_armorMax"];
-
- switch (true) do {
- case (_armorMin >= 1 && {_armor < _armorMin}): {
- // This will decrease damage
- _modifiedNewDamage = _newDamage * _armor / _armorMin;
- _modifiedRealDamage = _realDamage * _armor / _armorMin;
-
- TRACE_6("Under min armor",_armor,_armorMin,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
- };
- case (_armorMax >= 1 && {_armor > _armorMax}): {
- // This will increase damage
- _modifiedNewDamage = _newDamage * _armor / _armorMax;
- _modifiedRealDamage = _realDamage * _armor / _armorMax;
-
- TRACE_6("Over max armor",_armor,_armorMax,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
- };
- };
-
- _modifiedNewDamage = _modifiedNewDamage / _hitPointMultiplier;
- _modifiedRealDamage = _modifiedRealDamage / _hitPointMultiplier;
-
- TRACE_5("Hitpoint damage multiplied",_armor,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
-};
-
-// Damages are stored for "ace_hdbracket" event triggered last
-_unit setVariable [format ["ace_medical_engine_$%1", _hitPoint], [_realDamage, _newDamage, _modifiedRealDamage, _modifiedNewDamage]];
-
// 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};
-
-// We store our own damage values so engine damage is unnecessary
-0
+// For all other hitpoints, we store our own damage values, so engine damage is unnecessary
+[0, _oldDamage] select (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"])
From 4e332ec58759684d9d72dec2222a41fddfbcb254 Mon Sep 17 00:00:00 2001
From: johnb432 <58661205+johnb432@users.noreply.github.com>
Date: Sat, 28 Sep 2024 10:06:30 +0200
Subject: [PATCH 2/2] 1.0.3.0
---
CHANGELOG.md | 5 +
addons/main/XEH_PREP.hpp | 6 +-
addons/main/addon.toml | 6 -
addons/main/config.cpp | 4 +-
addons/main/functions/fnc_handleDamage.sqf | 5 +-
addons/main/functions/fnc_handleDamageOld.sqf | 263 ++++++++++++++++++
addons/main/script_macros.hpp | 8 +-
addons/main/script_mod.hpp | 2 +-
addons/main/script_version.hpp | 2 +-
9 files changed, 282 insertions(+), 19 deletions(-)
create mode 100644 addons/main/functions/fnc_handleDamageOld.sqf
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47f9cbc..ecbf7a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# Changelog for Armor Modifier - ACE 28.9.2024
+
+1.0.3.0
+- Updated to work with ACE 3.18.0. Remains backwards compatible with ACE 3.17.1.
+
# Changelog for Armor Modifier - ACE 26.3.2024
1.0.2.0
diff --git a/addons/main/XEH_PREP.hpp b/addons/main/XEH_PREP.hpp
index 3b2bf28..b8c1b8b 100644
--- a/addons/main/XEH_PREP.hpp
+++ b/addons/main/XEH_PREP.hpp
@@ -1 +1,5 @@
-PREP(handleDamage);
+if (getNumber (configFile >> "CfgPatches" >> "ace_main" >> "version") >= 3.18) then {
+ PREP(handleDamage,handleDamage);
+} else {
+ PREP(handleDamage,handleDamageOld);
+};
diff --git a/addons/main/addon.toml b/addons/main/addon.toml
index 3a886f6..5422b8b 100644
--- a/addons/main/addon.toml
+++ b/addons/main/addon.toml
@@ -1,8 +1,2 @@
[rapify]
enabled = true
-
-[binarize]
-enabled = true
-
-[asc]
-enabled = true
diff --git a/addons/main/config.cpp b/addons/main/config.cpp
index 5a887c2..351d419 100644
--- a/addons/main/config.cpp
+++ b/addons/main/config.cpp
@@ -20,4 +20,6 @@ class CfgPatches {
};
#include "CfgEventHandlers.hpp"
-#include "CfgVersioning.hpp"
+
+// Don't force ACE version until 3.18.0 has been out for a while
+// #include "CfgVersioning.hpp"
diff --git a/addons/main/functions/fnc_handleDamage.sqf b/addons/main/functions/fnc_handleDamage.sqf
index 5cb80bb..ab1ebfd 100644
--- a/addons/main/functions/fnc_handleDamage.sqf
+++ b/addons/main/functions/fnc_handleDamage.sqf
@@ -1,5 +1,4 @@
#include "..\script_component.hpp"
-
/*
* Author: commy2, kymckay, modified by johnb43
* Original:
@@ -7,7 +6,7 @@
* 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".
* Added:
- * Handling of damage to allow armor modifcation.
+ * Handling of damage to allow armor modifcation. For ACE 3.18.0 and later.
*
* Arguments:
* Handle damage EH
@@ -100,7 +99,7 @@ if (
GET_NUMBER(_ammoCfg >> "explosive",0) > 0 ||
{GET_NUMBER(_ammoCfg >> "indirectHit",0) > 0}
}
-) exitwith {
+) exitWith {
TRACE_5("Vehicle hit",_unit,_shooter,_instigator,_damage,_newDamage);
_unit setVariable ["ace_medical_lastDamageSource", _shooter];
diff --git a/addons/main/functions/fnc_handleDamageOld.sqf b/addons/main/functions/fnc_handleDamageOld.sqf
new file mode 100644
index 0000000..e8ad79a
--- /dev/null
+++ b/addons/main/functions/fnc_handleDamageOld.sqf
@@ -0,0 +1,263 @@
+#include "..\script_component.hpp"
+/*
+ * Author: commy2, kymckay, modified by johnb43
+ * Original:
+ * 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".
+ * Added:
+ * Handling of damage to allow armor modifcation. For ACE 3.17.1 and prior.
+ *
+ * Arguments:
+ * Handle damage EH
+ *
+ * Return Value:
+ * Damage to be inflicted
+ *
+ * Public: No
+ */
+
+params ["_args", ["_ignoreAllowDamageACE", false]];
+_args params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint", "_directHit"];
+
+// 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 ["ace_medical_allowDamage", true] || _ignoreAllowDamageACE}) exitWith {_oldDamage};
+
+private _newDamage = _damage - _oldDamage;
+// Get scaled armor value of hitpoint and calculate damage before armor
+// We scale using passThrough to handle explosive-resistant armor properly (#9063)
+// We need realDamage to determine which limb was hit correctly
+[_unit, _hitpoint] call ace_medical_engine_fnc_getHitpointArmor params ["_armor", "_armorScaled"];
+
+private _realDamage = _newDamage * _armor;
+
+// ACE <3.16.0 does not return "_armorScaled"
+if (_hitPoint isNotEqualTo "#structural" && {!isNil "_armorScaled"}) then {
+ private _armorCoef = _armor / _armorScaled;
+ private _damageCoef = linearConversion [0, 1, ace_medical_engine_damagePassThroughEffect, 1, _armorCoef];
+ _newDamage = _newDamage * _damageCoef;
+};
+
+TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage);
+
+// 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 {
+ TRACE_5("Drowning",_unit,_shooter,_instigator,_damage,_newDamage);
+ ["ace_medical_woundReceived", [_unit, [[_newDamage, "Body", _newDamage]], _unit, "drowning"]] call CBA_fnc_localEvent;
+
+ 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 (
+ ace_medical_enableVehicleCrashes &&
+ {_hitPoint isEqualTo "#structural"} &&
+ {_ammo isEqualTo ""} &&
+ {!isNull _vehicle} &&
+ {vectorMagnitude (velocity _vehicle) > 5}
+ // todo: no way to detect if stationary and another vehicle hits you
+) exitWith {
+ TRACE_5("Crash",_unit,_shooter,_instigator,_damage,_newDamage);
+ ["ace_medical_woundReceived", [_unit, [[_newDamage, _hitPoint, _newDamage]], _unit, "vehiclecrash"]] call CBA_fnc_localEvent;
+
+ 0
+};
+
+// Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint
+// This is the case for mines, explosives, artillery, and catastrophic vehicle explosions
+// Triggers twice, but that doesn't matter as damage is low
+if (
+ _hitPoint isEqualTo "#structural" &&
+ {!isNull _vehicle} &&
+ {_ammo isNotEqualTo ""} &&
+ {
+ private _ammoCfg = configFile >> "CfgAmmo" >> _ammo;
+ GET_NUMBER(_ammoCfg >> "explosive",0) > 0 ||
+ {GET_NUMBER(_ammoCfg >> "indirectHit",0) > 0}
+ }
+) exitWith {
+ TRACE_6("Vehicle hit",_unit,_shooter,_instigator,_damage,_newDamage,_damages);
+
+ _unit setVariable ["ace_medical_lastDamageSource", _shooter];
+ _unit setVariable ["ace_medical_lastInstigator", _instigator];
+
+ ["ace_medical_woundReceived", [_unit, [[_newDamage, _hitPoint, _newDamage]], _shooter, "vehiclehit"]] call CBA_fnc_localEvent;
+
+ 0
+};
+
+// 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 ["ace_medical_lastDamageSource", _shooter];
+ _unit setVariable ["ace_medical_lastInstigator", _instigator];
+
+ private _damageStructural = _unit getVariable ["ace_medical_engine_$#structural", [0,0,0,0]];
+
+ // --- Head
+ private _damageHead = [
+ _unit getVariable ["ace_medical_engine_$HitFace", [0,0,0,0]],
+ _unit getVariable ["ace_medical_engine_$HitNeck", [0,0,0,0]],
+ _unit getVariable ["ace_medical_engine_$HitHead", [0,0,0,0]]
+ ];
+ _damageHead sort false;
+ _damageHead = _damageHead select 0;
+
+ // --- Body
+ private _damageBody = [
+ _unit getVariable ["ace_medical_engine_$HitPelvis", [0,0,0,0]],
+ _unit getVariable ["ace_medical_engine_$HitAbdomen", [0,0,0,0]],
+ _unit getVariable ["ace_medical_engine_$HitDiaphragm", [0,0,0,0]],
+ _unit getVariable ["ace_medical_engine_$HitChest", [0,0,0,0]]
+ // HitBody removed as it's a placeholder hitpoint and the high armor value (1000) throws the calculations off
+ ];
+ _damageBody sort false;
+ _damageBody = _damageBody select 0;
+
+ // --- Arms and Legs
+ private _damageLeftArm = _unit getVariable ["ace_medical_engine_$HitLeftArm", [0,0,0,0]];
+ private _damageRightArm = _unit getVariable ["ace_medical_engine_$HitRightArm", [0,0,0,0]];
+ private _damageLeftLeg = _unit getVariable ["ace_medical_engine_$HitLeftLeg", [0,0,0,0]];
+ private _damageRightLeg = _unit getVariable ["ace_medical_engine_$HitRightLeg", [0,0,0,0]];
+
+ // Find hit point that received the maxium damage
+ // Priority used for sorting if incoming damage is equal
+ private _allDamages = [
+ // Real damage (ignoring armor), Actual damage (with armor), Real damage modified (ignoring armor), Modified damage (with armor)
+ [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, "Head", _damageHead param [2, _damageHead select 0], _damageHead param [3, _damageHead select 1]],
+ [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, "Body", _damageBody param [2, _damageBody select 0], _damageBody param [3, _damageBody select 1]],
+ [_damageLeftArm select 0, PRIORITY_LEFT_ARM, _damageLeftArm select 1, "LeftArm", _damageLeftArm param [2, _damageLeftArm select 0], _damageLeftArm param [3, _damageLeftArm select 1]],
+ [_damageRightArm select 0, PRIORITY_RIGHT_ARM, _damageRightArm select 1, "RightArm", _damageRightArm param [2, _damageRightArm select 0], _damageRightArm param [3, _damageRightArm select 1]],
+ [_damageLeftLeg select 0, PRIORITY_LEFT_LEG, _damageLeftLeg select 1, "LeftLeg", _damageLeftLeg param [2, _damageLeftLeg select 0], _damageLeftLeg param [3, _damageLeftLeg select 1]],
+ [_damageRightLeg select 0, PRIORITY_RIGHT_LEG, _damageRightLeg select 1, "RightLeg", _damageRightLeg param [2, _damageRightLeg select 0], _damageRightLeg param [3, _damageRightLeg select 1]],
+ [_damageStructural select 0, PRIORITY_STRUCTURAL, _damageStructural select 1, "#structural", _damageStructural param [2, _damageStructural select 0], _damageStructural param [3, _damageStructural select 1]]
+ ];
+ TRACE_2("incoming",_allDamages,_damageStructural);
+
+ _allDamages sort false;
+ // Use modified damages instead of initial ones
+ _allDamages = _allDamages apply {[_x select 5, _x select 3, _x select 4]};
+
+ // 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 {
+ /*
+ 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 {
+ _ammo = "falling";
+ TRACE_5("Fall",_unit,_shooter,_instigator,_damage,_allDamages);
+ } else {
+ _ammo = "collision";
+ TRACE_5("Collision",_unit,_shooter,_instigator,_damage,_allDamages);
+ };
+ } else {
+ // Anything else is almost guaranteed to be fire damage
+ _ammo = "fire";
+ TRACE_5("Fire Damage",_unit,_shooter,_instigator,_damage,_allDamages);
+ };
+ };
+
+ // No wounds for minor damage
+ // TODO check if this needs to be changed for burning damage (occurs as lots of small events that we add together)
+ if ((_allDamages select 0 select 0) > 1E-3) then {
+ TRACE_1("received",_allDamages);
+ ["ace_medical_woundReceived", [_unit, _allDamages, _shooter, _ammo]] call CBA_fnc_localEvent;
+ };
+
+ // 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 [
+ "ace_medical_engine_$HitFace","ace_medical_engine_$HitNeck","ace_medical_engine_$HitHead",
+ "ace_medical_engine_$HitPelvis","ace_medical_engine_$HitAbdomen","ace_medical_engine_$HitDiaphragm","ace_medical_engine_$HitChest","ace_medical_engine_$HitBody",
+ "ace_medical_engine_$HitLeftArm","ace_medical_engine_$HitRightArm","ace_medical_engine_$HitLeftLeg","ace_medical_engine_$HitRightLeg",
+ "ace_medical_engine_$#structural"
+ ];
+
+ 0
+};
+
+// Get setting for particular unit
+private _multiplierArray = switch (true) do {
+ case (_hitPoint in ["hitface", "hitneck", "hithead"]): {
+ _unit getVariable [QGVAR(hitPointMultiplier_head), [GVAR(hitPointMultiplier_ai_head), GVAR(hitPointMultiplier_player_head)] select (isPlayer _unit)]
+ };
+ case (_hitPoint in ["hitpelvis" ,"hitabdomen", "hitdiaphragm", "hitchest"]): {
+ _unit getVariable [QGVAR(hitPointMultiplier_chest), [GVAR(hitPointMultiplier_ai_chest), GVAR(hitPointMultiplier_player_chest)] select (isPlayer _unit)]
+ };
+ case (_hitPoint in ["hitleftarm", "hitrightarm", "hitleftleg", "hitrightleg"]): {
+ _unit getVariable [QGVAR(hitPointMultiplier_limb), [GVAR(hitPointMultiplier_ai_limb), GVAR(hitPointMultiplier_player_limb)] select (isPlayer _unit)]
+ };
+ default {
+ DEFAULT_SETTINGS
+ };
+};
+
+private _modifiedNewDamage = _newDamage;
+private _modifiedRealDamage = _realDamage;
+
+// If default settings, we don't need to change anything, so skip calculcations and let ace handle damage
+if (_multiplierArray isNotEqualTo DEFAULT_SETTINGS) then {
+ _multiplierArray params ["_hitPointMultiplier", "_armorMin", "_armorMax"];
+
+ switch (true) do {
+ case (_armorMin >= 1 && {_armor < _armorMin}): {
+ // This will decrease damage
+ _modifiedNewDamage = _newDamage * _armor / _armorMin;
+ _modifiedRealDamage = _realDamage * _armor / _armorMin;
+
+ TRACE_6("Under min armor",_armor,_armorMin,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
+ };
+ case (_armorMax >= 1 && {_armor > _armorMax}): {
+ // This will increase damage
+ _modifiedNewDamage = _newDamage * _armor / _armorMax;
+ _modifiedRealDamage = _realDamage * _armor / _armorMax;
+
+ TRACE_6("Over max armor",_armor,_armorMax,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
+ };
+ };
+
+ _modifiedNewDamage = _modifiedNewDamage / _hitPointMultiplier;
+ _modifiedRealDamage = _modifiedRealDamage / _hitPointMultiplier;
+
+ TRACE_5("Hitpoint damage multiplied",_armor,_newDamage,_modifiedNewDamage,_realDamage,_modifiedRealDamage);
+};
+
+// Damages are stored for "ace_hdbracket" event triggered last
+_unit setVariable [format ["ace_medical_engine_$%1", _hitPoint], [_realDamage, _newDamage, _modifiedRealDamage, _modifiedNewDamage]];
+
+// 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};
+
+// We store our own damage values so engine damage is unnecessary
+0
diff --git a/addons/main/script_macros.hpp b/addons/main/script_macros.hpp
index 91fd239..102bf43 100644
--- a/addons/main/script_macros.hpp
+++ b/addons/main/script_macros.hpp
@@ -1,9 +1,5 @@
#include "\x\cba\addons\main\script_macros_common.hpp"
-// This part includes parts of the CBA and ACE3 macro libraries
-#define GETMVAR(var1,var2) (missionNamespace getVariable [ARR_2(var1,var2)])
-#define SETMVAR(var1,var2,var3) (missionNamespace setVariable [ARR_3(var1,var2,var3)])
-
#define DEFAULT_SETTINGS [ARR_3(1,0,0)]
#define MINIMUM_SETTINGS [ARR_3(0.001,0,0)]
@@ -11,10 +7,10 @@
#ifdef DISABLE_COMPILE_CACHE
#undef PREP
- #define PREP(fncName) DFUNC(fncName) = compile preprocessFileLineNumbers QPATHTOF(functions\DOUBLES(fnc,fncName).sqf)
+ #define PREP(fncName,filename) DFUNC(fncName) = compile preprocessFileLineNumbers QPATHTOF(functions\DOUBLES(fnc,filename).sqf)
#else
#undef PREP
- #define PREP(fncName) [QPATHTOF(functions\DOUBLES(fnc,fncName).sqf), QFUNC(fncName)] call CBA_fnc_compileFunction
+ #define PREP(fncName,filename) [QPATHTOF(functions\DOUBLES(fnc,filename).sqf), QFUNC(fncName)] call CBA_fnc_compileFunction
#endif
// #include "\z\ace\addons\medical_engine\script_component.hpp"
diff --git a/addons/main/script_mod.hpp b/addons/main/script_mod.hpp
index a850aa8..719db23 100644
--- a/addons/main/script_mod.hpp
+++ b/addons/main/script_mod.hpp
@@ -8,6 +8,6 @@
#define VERSION_AR MAJOR,MINOR,PATCHLVL,BUILD
// MINIMAL required version for the Mod. Components can specify others..
-#define REQUIRED_VERSION 2.14
+#define REQUIRED_VERSION 2.16
#define COMPONENT_NAME QUOTE(Armor Modifier - ACE)
diff --git a/addons/main/script_version.hpp b/addons/main/script_version.hpp
index 62b1e56..9636dec 100644
--- a/addons/main/script_version.hpp
+++ b/addons/main/script_version.hpp
@@ -1,4 +1,4 @@
#define MAJOR 1
#define MINOR 0
-#define PATCHLVL 2
+#define PATCHLVL 3
#define BUILD 0