From 75f1cec1d92077b14b5e5c9e74019e76bccf5e4a Mon Sep 17 00:00:00 2001 From: Salluci Date: Tue, 13 Jun 2023 20:24:11 -0300 Subject: [PATCH 01/70] scale armor damage using passthrough --- .../functions/fnc_getItemArmor.sqf | 21 +++++++++++++------ .../functions/fnc_handleDamage.sqf | 11 ++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 26e1bd693aa..b05e2c5b054 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,14 +1,14 @@ #include "script_component.hpp" /* - * Author: Pterolatypus - * Returns the armor value the given item provides to a particular hitpoint, either from a cache or by reading the item config. + * Author: Pterolatypus, Salluci + * Returns the scaled armor value the given item provides to a particular hitpoint, either from a cache or by reading the item config. * * Arguments: * 0: Item Class * 1: Hitpoint * * Return Value: - * Item armor for the given hitpoint + * Scaled item armor for the given hitpoint * * Example: * ["V_PlateCarrier_rgr", "HitChest"] call ace_medical_engine_fnc_getItemArmor @@ -22,9 +22,10 @@ private _key = format ["%1$%2", _item, _hitpoint]; private _armor = GVAR(armorCache) get _key; if (isNil "_armor") then { + _armor = 0; + private _passThrough = 1; TRACE_2("Cache miss",_item,_hitpoint); if ("" in [_item, _hitpoint]) exitWith { - _armor = 0; GVAR(armorCache) set [_key, _armor]; }; @@ -38,14 +39,22 @@ if (isNil "_armor") then { } else { private _entry = _unitCfg >> "HitPoints" >> _hitpoint; _armor = getNumber (_unitCfg >> "armor") * (1 max getNumber (_entry >> "armor")); + _passThrough = (0.01 max getNumber (_entry >> "passThrough")); // prevent dividing by 0 }; } else { private _condition = format ["getText (_x >> 'hitpointName') == '%1'", _hitpoint]; private _entry = configProperties [_itemInfo >> "HitpointsProtectionInfo", _condition] param [0, configNull]; - - _armor = getNumber (_entry >> "armor"); + if (!isNull _entry) then { + _armor = getNumber (_entry >> "armor"); + _passThrough = (0.01 max getNumber (_entry >> "passThrough")); + }; }; + // Scale armor using passthrough to fix explosive resistant armor (#9063) + // Skip scaling for items that don't cover the hitpoint to prevent infinite armor + if (_armor != 0) then { + _armor = (log (_armor / _passThrough)) * 10; + }; GVAR(armorCache) set [_key, _armor]; }; diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 1b3bb238a11..be6e5104485 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -32,7 +32,8 @@ if (_hitPoint isEqualTo "") then { if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]}) exitWith {_oldDamage}; private _newDamage = _damage - _oldDamage; -// Get armor value of hitpoint and calculate damage before armor +// Get scaled armor value of hitpoint and calculate damage before armor +// We scale using passThrough to handle explosive-resistant armor properly, fixing #9063 private _armor = [_unit, _hitpoint] call FUNC(getHitpointArmor); private _realDamage = _newDamage * _armor; TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); @@ -101,7 +102,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), [0,0]]; private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), [0,0]]; - // Find hit point that received the maxium damage + // Find hit point that received the maximum damage // Priority used for sorting if incoming damage is equal private _allDamages = [ [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, "Head"], @@ -115,8 +116,10 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { TRACE_2("incoming",_allDamages,_damageStructural); _allDamages sort false; - _allDamages = _allDamages apply {[_x select 2, _x select 3, _x select 0]}; - + + // We only need real damage at this point, divide by 10 to use engine range of 0-1 (or higher for really high damage) + _allDamages = _allDamages apply {[(_x select 0) / 10, _x select 3]}; + // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them if (_ammo isEqualTo "") then { From 386eda61e5d9488bc35d0fa8e30901651dd95345 Mon Sep 17 00:00:00 2001 From: GhostIsSpooky <69561145+Salluci@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:52:19 -0300 Subject: [PATCH 02/70] add - to comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jouni Järvinen --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index b05e2c5b054..d62b80f7ccd 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -50,7 +50,7 @@ if (isNil "_armor") then { }; }; - // Scale armor using passthrough to fix explosive resistant armor (#9063) + // Scale armor using passthrough to fix explosive-resistant armor (#9063) // Skip scaling for items that don't cover the hitpoint to prevent infinite armor if (_armor != 0) then { _armor = (log (_armor / _passThrough)) * 10; From 510e6aa90ca91bc675a7b0af88a9c898b0fd0cb6 Mon Sep 17 00:00:00 2001 From: GhostIsSpooky <69561145+Salluci@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:52:32 -0300 Subject: [PATCH 03/70] improve condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jouni Järvinen --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index d62b80f7ccd..e3e8e5fc8ec 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -52,7 +52,7 @@ if (isNil "_armor") then { // Scale armor using passthrough to fix explosive-resistant armor (#9063) // Skip scaling for items that don't cover the hitpoint to prevent infinite armor - if (_armor != 0) then { + if (_armor isNotEqualTo 0) then { _armor = (log (_armor / _passThrough)) * 10; }; GVAR(armorCache) set [_key, _armor]; From f290e28f894e6a44eb8b3ba22eacb88a246fa3ee Mon Sep 17 00:00:00 2001 From: GhostIsSpooky <69561145+Salluci@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:52:43 -0300 Subject: [PATCH 04/70] remove extra brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jouni Järvinen --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index e3e8e5fc8ec..27b431a1f27 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -46,7 +46,7 @@ if (isNil "_armor") then { private _entry = configProperties [_itemInfo >> "HitpointsProtectionInfo", _condition] param [0, configNull]; if (!isNull _entry) then { _armor = getNumber (_entry >> "armor"); - _passThrough = (0.01 max getNumber (_entry >> "passThrough")); + _passThrough = 0.01 max getNumber (_entry >> "passThrough"); }; }; From 9ad380dd172d342b5cfafbe1e7a201d8b7f588ff Mon Sep 17 00:00:00 2001 From: Salluci Date: Tue, 13 Jun 2023 22:53:42 -0300 Subject: [PATCH 05/70] remove extra brackets --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 27b431a1f27..eb85849f916 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -39,7 +39,7 @@ if (isNil "_armor") then { } else { private _entry = _unitCfg >> "HitPoints" >> _hitpoint; _armor = getNumber (_unitCfg >> "armor") * (1 max getNumber (_entry >> "armor")); - _passThrough = (0.01 max getNumber (_entry >> "passThrough")); // prevent dividing by 0 + _passThrough = 0.01 max getNumber (_entry >> "passThrough"); // prevent dividing by 0 }; } else { private _condition = format ["getText (_x >> 'hitpointName') == '%1'", _hitpoint]; From 0c82347d5bdf13c2dadf8863f2cfe531824df5a7 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 01:22:12 -0300 Subject: [PATCH 06/70] fix damage sorting --- .../functions/fnc_getHitpointArmor.sqf | 14 +++--- .../functions/fnc_getItemArmor.sqf | 16 +++--- .../functions/fnc_handleDamage.sqf | 50 ++++++++++--------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index 62954a1b568..3e4c57e680d 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -8,7 +8,7 @@ * 1: Hitpoint * * Return Value: - * Total armor for the given hitpoint + * Total armor and scaled armor for the given hitpoint * * Example: * [player, "HitChest"] call ace_medical_engine_fnc_getHitpointArmor @@ -32,16 +32,18 @@ private _gear = [ private _rags = _gear joinString "$"; private _var = format [QGVAR(armorCache$%1), _hitpoint]; -_unit getVariable [_var, [""]] params ["_prevRags", "_armor"]; +_unit getVariable [_var, ["", 0, 0]] params ["_prevRags", "_armor", "_armorScaled"]; if (_rags != _prevRags) then { _armor = 0; - + _armorScaled = 0; { - _armor = _armor + ([_x, _hitpoint] call FUNC(getItemArmor)); + ([_x, _hitpoint] call FUNC(getItemArmor)) params ["_itemArmor", "_itemArmorScaled"]; + _armor = _armor + _itemArmor; + _armorScaled = _armorScaled + _itemArmorScaled; } forEach _gear; - _unit setVariable [_var, [_rags, _armor]]; + _unit setVariable [_var, [_rags, _armor, _armorScaled]]; }; -_armor // return +[_armor, _armorScaled] // return diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index eb85849f916..6094fbde1d9 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -19,14 +19,15 @@ params ["_item", "_hitpoint"]; private _key = format ["%1$%2", _item, _hitpoint]; -private _armor = GVAR(armorCache) get _key; +private _return = GVAR(armorCache) get _key; -if (isNil "_armor") then { - _armor = 0; +if (isNil "_return") then { + private _armor = 0; + private _armorScaled = 0; private _passThrough = 1; TRACE_2("Cache miss",_item,_hitpoint); if ("" in [_item, _hitpoint]) exitWith { - GVAR(armorCache) set [_key, _armor]; + GVAR(armorCache) set [_key, [_armor, _armorScaled]]; }; private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; @@ -53,9 +54,10 @@ if (isNil "_armor") then { // Scale armor using passthrough to fix explosive-resistant armor (#9063) // Skip scaling for items that don't cover the hitpoint to prevent infinite armor if (_armor isNotEqualTo 0) then { - _armor = (log (_armor / _passThrough)) * 10; + _armorScaled = (log (_armor / _passThrough)) * 10; }; - GVAR(armorCache) set [_key, _armor]; + _return = [_armor, _armorScaled]; + GVAR(armorCache) set [_key, _return]; }; -_armor // return +_return // return diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index be6e5104485..52febc996f9 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -34,9 +34,11 @@ if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), t 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, fixing #9063 -private _armor = [_unit, _hitpoint] call FUNC(getHitpointArmor); +// We need both realDamage and realDamageScaled so sorting works properly +[_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; -TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); +private _realDamageScaled = _newDamage * _armorScaled; +TRACE_5("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_realDamageScaled); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs // Damage occurs in consistent increments @@ -74,51 +76,53 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { _unit setVariable [QEGVAR(medical,lastDamageSource), _shooter]; _unit setVariable [QEGVAR(medical,lastInstigator), _instigator]; - private _damageStructural = _unit getVariable [QGVAR($#structural), [0,0]]; + private _damageStructural = _unit getVariable [QGVAR($#structural), [0,0,0]]; // --- Head private _damageHead = [ - _unit getVariable [QGVAR($HitFace), [0,0]], - _unit getVariable [QGVAR($HitNeck), [0,0]], - _unit getVariable [QGVAR($HitHead), [0,0]] + _unit getVariable [QGVAR($HitFace), [0,0,0]], + _unit getVariable [QGVAR($HitNeck), [0,0,0]], + _unit getVariable [QGVAR($HitHead), [0,0,0]] ]; _damageHead sort false; _damageHead = _damageHead select 0; // --- Body private _damageBody = [ - _unit getVariable [QGVAR($HitPelvis), [0,0]], - _unit getVariable [QGVAR($HitAbdomen), [0,0]], - _unit getVariable [QGVAR($HitDiaphragm), [0,0]], - _unit getVariable [QGVAR($HitChest), [0,0]] + _unit getVariable [QGVAR($HitPelvis), [0,0,0]], + _unit getVariable [QGVAR($HitAbdomen), [0,0,0]], + _unit getVariable [QGVAR($HitDiaphragm), [0,0,0]], + _unit getVariable [QGVAR($HitChest), [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 [QGVAR($HitLeftArm), [0,0]]; - private _damageRightArm = _unit getVariable [QGVAR($HitRightArm), [0,0]]; - private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), [0,0]]; - private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), [0,0]]; + private _damageLeftArm = _unit getVariable [QGVAR($HitLeftArm), [0,0,0]]; + private _damageRightArm = _unit getVariable [QGVAR($HitRightArm), [0,0,0]]; + private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), [0,0,0]]; + private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), [0,0,0]]; // Find hit point that received the maximum damage // Priority used for sorting if incoming damage is equal + // _realDamage, priority, _newDamage, body part name private _allDamages = [ - [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, "Head"], - [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, "Body"], - [_damageLeftArm select 0, PRIORITY_LEFT_ARM, _damageLeftArm select 1, "LeftArm"], - [_damageRightArm select 0, PRIORITY_RIGHT_ARM, _damageRightArm select 1, "RightArm"], - [_damageLeftLeg select 0, PRIORITY_LEFT_LEG, _damageLeftLeg select 1, "LeftLeg"], - [_damageRightLeg select 0, PRIORITY_RIGHT_LEG, _damageRightLeg select 1, "RightLeg"], - [_damageStructural select 0, PRIORITY_STRUCTURAL, _damageStructural select 1, "#structural"] + [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, _damageHead select 2, "Head"], + [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, _damageBody select 2, "Body"], + [_damageLeftArm select 0, PRIORITY_LEFT_ARM, _damageLeftArm select 1, _damageLeftArm select 2, "LeftArm"], + [_damageRightArm select 0, PRIORITY_RIGHT_ARM, _damageRightArm select 1, _damageRightArm select 2, "RightArm"], + [_damageLeftLeg select 0, PRIORITY_LEFT_LEG, _damageLeftLeg select 1, _damageLeftLeg select 2, "LeftLeg"], + [_damageRightLeg select 0, PRIORITY_RIGHT_LEG, _damageRightLeg select 1, _damageRightLeg select 2, "RightLeg"], + [_damageStructural select 0, PRIORITY_STRUCTURAL, _damageStructural select 1, _damageStructural select 2, "#structural"] ]; TRACE_2("incoming",_allDamages,_damageStructural); _allDamages sort false; // We only need real damage at this point, divide by 10 to use engine range of 0-1 (or higher for really high damage) - _allDamages = _allDamages apply {[(_x select 0) / 10, _x select 3]}; + // _newDamage is maintained for compatibility + _allDamages = _allDamages apply {[(_x select 3) / 10, _x select 4, _x select 0]}; // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them @@ -169,7 +173,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { }; // Damages are stored for "ace_hdbracket" event triggered last -_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]]; +_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage, _realDamageScaled]]; // Engine damage to these hitpoints controls blood visuals, limping, weapon sway // Handled in fnc_damageBodyPart, persist here From 27ef26f061c9ca53eb90501ab8672bd3c7680eae Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 01:22:59 -0300 Subject: [PATCH 07/70] whitespace --- addons/medical_engine/functions/fnc_getHitpointArmor.sqf | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index 3e4c57e680d..6d9035f00ef 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -37,6 +37,7 @@ _unit getVariable [_var, ["", 0, 0]] params ["_prevRags", "_armor", "_armorScale if (_rags != _prevRags) then { _armor = 0; _armorScaled = 0; + { ([_x, _hitpoint] call FUNC(getItemArmor)) params ["_itemArmor", "_itemArmorScaled"]; _armor = _armor + _itemArmor; From 0098ecb5ac1404421aefdda09b8c4687483cca8c Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 01:24:14 -0300 Subject: [PATCH 08/70] comment --- addons/medical_engine/functions/fnc_handleDamage.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 52febc996f9..4559eba31ee 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -106,7 +106,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { // Find hit point that received the maximum damage // Priority used for sorting if incoming damage is equal - // _realDamage, priority, _newDamage, body part name + // _realDamage, priority, _newDamage, _realDamageScaled, body part name private _allDamages = [ [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, _damageHead select 2, "Head"], [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, _damageBody select 2, "Body"], From 816c5e7716250057a0097c93cdf3b210e572a8de Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 03:11:57 -0300 Subject: [PATCH 09/70] rework armor penetration --- addons/medical_engine/XEH_PREP.hpp | 2 + addons/medical_engine/XEH_preInit.sqf | 1 + .../functions/fnc_calculateDamage.sqf | 54 +++++++++++++++++++ .../functions/fnc_getAmmoData.sqf | 30 +++++++++++ .../functions/fnc_handleDamage.sqf | 12 +++-- addons/medical_engine/initSettings.sqf | 9 ++++ .../medical_engine/script_macros_medical.hpp | 2 +- addons/medical_engine/stringtable.xml | 6 +++ 8 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 addons/medical_engine/functions/fnc_calculateDamage.sqf create mode 100644 addons/medical_engine/functions/fnc_getAmmoData.sqf diff --git a/addons/medical_engine/XEH_PREP.hpp b/addons/medical_engine/XEH_PREP.hpp index 63faaf7bbec..ee55dcee7b3 100644 --- a/addons/medical_engine/XEH_PREP.hpp +++ b/addons/medical_engine/XEH_PREP.hpp @@ -1,6 +1,8 @@ PREP(applyAnimAfterRagdoll); +PREP(calculateDamage); PREP(damageBodyPart); PREP(disableThirdParty); +PREP(getAmmoData); PREP(getHitpointArmor); PREP(getItemArmor); PREP(handleDamage); diff --git a/addons/medical_engine/XEH_preInit.sqf b/addons/medical_engine/XEH_preInit.sqf index 8157baa4ec5..383e9b7b33b 100644 --- a/addons/medical_engine/XEH_preInit.sqf +++ b/addons/medical_engine/XEH_preInit.sqf @@ -32,6 +32,7 @@ if (isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_K) || isNil QUOTE(FATAL_SUM_DAMAGE_WEIB // Cache for armor values of equipped items (vests etc) GVAR(armorCache) = createHashMap; +GVAR(ammoCache) = createHashMap; // Hack for #3168 (units in static weapons do not take any damage): // Doing a manual pre-load with a small distance seems to fix the LOD problems diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf new file mode 100644 index 00000000000..d8d6f909cf9 --- /dev/null +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -0,0 +1,54 @@ +#include "script_component.hpp" +/* + * Author: Salluci + * Calculates final damage inflicted by a round using scaled hitpoint armor and engine damage, taking penetration into account + * + * Arguments: + * 0: Engine damage + * 1: Impact ammo + * 2: Scaled hitpoint Armor + * + * Return Value: + * Final damage for the given hitpoint + * + * Example: + * [0.5, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage + * + * Public: No + */ + +// B_556x45_Ball +#define BASE_SCALING_DAMAGE 9 +#define BASE_SCALING_CALIBER 0.869565 +// Magic numbers, tweak until expected results +#define DAMAGE_SCALING_FACTOR 1.4 +#define ARMOR_NEGATION_FACTOR 9 +// Extra damage from overpenetration +#define OVERKILL_FACTOR 2 + + +params ["_damage", "_ammo", "_armor"]; + +// Skip environmental damage +if (_ammo isEqualTo "") exitWith { + _damage // return +}; + +// Get ammo data to calculate penetration +([_ammo] call FUNC(getAmmoData)) params ["", "_caliber"]; + +private _penFactor = (_caliber / BASE_SCALING_CALIBER); + +// Penetration factor negates armor, we use the vanilla V_PlateCarrier1_blk and B_556x45_Ball as a scaling reference, with increase in ammo "caliber" being exponential for armor negation +// This is gameified and balanced around vanilla armor behavior, since we can't get fireGeometry data for models and their materials can't be relied upon +// A more elegant solution would be using the "HitPart" eventhandler for damage instead, but we'd have issues with reliability due to locality and performance +// Ideally, we want B_556x45_Ball hitting V_PlateCarrier1_blk at point blank to deal ~0.35 damage and scale up to B_127x108 dealing ~2.5 damage on overpenetration +// This should be compatible with weapon and armor mods balanced around vanilla or Spartan0536's values (https://forums.bohemia.net/forums/topic/163694-arma-iii-ballistics-overhaul/) + +private _armorNegated = ARMOR_NEGATION_FACTOR * _penFactor; +_damage = (sqrt ((_damage / BASE_SCALING_DAMAGE) / (1 max (_armor - _armorNegated)))) * DAMAGE_SCALING_FACTOR; +if ( _armorNegated > (_armor * 2)) then { + _damage = _damage * OVERKILL_FACTOR; +}; +TRACE_5("finalDamage", _damage,_ammo,_penFactor,_armor,_armorNegated); +_damage // return diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf new file mode 100644 index 00000000000..a411719dad8 --- /dev/null +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -0,0 +1,30 @@ +#include "script_component.hpp" +/* + * Author: Salluci + * Returns base damage value of a given round, either from a cache or by reading the ammo config. + * + * Arguments: + * 0: Ammo + * + * Return Value: + * Base damage value and penetration factor of a given round + * + * Example: + * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit + * + * Public: No + */ + +params ["_ammo"]; + +private _return = GVAR(ammoCache) get _ammo; +if (isNil "_return") then { + TRACE_1("Cache miss",_ammo); + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + _hit = getNumber (_ammoConfig >> "hit"); + _caliber = getNumber (_ammoConfig >> "caliber"); + _return = [_hit, _caliber]; + GVAR(ammoCache) set [_ammo, _return]; +}; + +_return // return diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 4559eba31ee..28cce88dc6c 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -38,6 +38,13 @@ private _newDamage = _damage - _oldDamage; [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; private _realDamageScaled = _newDamage * _armorScaled; + +// Setting for those who prefer the regular balance or have custom ammo configs +if (EGVAR(medical,alternateArmorPenetration)) then { + // Calculate damage based on ammo "caliber": ammo material penetration + _realDamageScaled = [_realDamage, _ammo, _armorScaled] call FUNC(calculateDamage); +}; + TRACE_5("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_realDamageScaled); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs @@ -120,9 +127,8 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { _allDamages sort false; - // We only need real damage at this point, divide by 10 to use engine range of 0-1 (or higher for really high damage) - // _newDamage is maintained for compatibility - _allDamages = _allDamages apply {[(_x select 3) / 10, _x select 4, _x select 0]}; + // We only need real damage at this point, _newDamage is maintained for compatibility + _allDamages = _allDamages apply {[_x select 3, _x select 4, _x select 0]}; // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them diff --git a/addons/medical_engine/initSettings.sqf b/addons/medical_engine/initSettings.sqf index 9fe80afcb00..032c7ffd9e7 100644 --- a/addons/medical_engine/initSettings.sqf +++ b/addons/medical_engine/initSettings.sqf @@ -6,3 +6,12 @@ true, true ] call CBA_fnc_addSetting; + +[ + QEGVAR(medical,alternateArmorPenetration), + "CHECKBOX", + [LSTRING(AlternateArmorPenetration_DisplayName), LSTRING(AlternateArmorPenetration_Description)], + ELSTRING(medical,Category), + true, + true +] call CBA_fnc_addSetting; diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index 618810414a6..d591a4408ed 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -17,7 +17,7 @@ #define HEAD_DAMAGE_THRESHOLD EGVAR(medical,const_headDamageThreshold) #define HEAD_DAMAGE_THRESHOLD_DEFAULT 1 #define ORGAN_DAMAGE_THRESHOLD EGVAR(medical,const_organDamageThreshold) -#define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.6 +#define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.35 // Consts for determineIfFatal: sum of damage (values are calcualted at runtime in preInit) #define FATAL_SUM_DAMAGE_WEIBULL_K EGVAR(medical,const_fatalSumDamageWeibull_K) #define FATAL_SUM_DAMAGE_WEIBULL_L EGVAR(medical,const_fatalSumDamageWeibull_L) diff --git a/addons/medical_engine/stringtable.xml b/addons/medical_engine/stringtable.xml index 81e2ae8450f..268ee938ed0 100644 --- a/addons/medical_engine/stringtable.xml +++ b/addons/medical_engine/stringtable.xml @@ -23,5 +23,11 @@ 控制乘员是否受到车辆碰撞的伤害。 차량 충돌로 인해 탑승인원들이 피해를 받을 지 결정합니다. + + Use Alternate Armor Penetration + + + Controls whether ammo material penetration ("caliber") is taken into account for damage handling. + From c232f7f9d6d0153910671784c36416d4bfaa4525 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 03:20:19 -0300 Subject: [PATCH 10/70] fix function header --- addons/medical_engine/functions/fnc_calculateDamage.sqf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index d8d6f909cf9..749d3a54f1f 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -1,18 +1,18 @@ #include "script_component.hpp" /* * Author: Salluci - * Calculates final damage inflicted by a round using scaled hitpoint armor and engine damage, taking penetration into account + * Calculates final damage inflicted by a round using scaled hitpoint armor and real damage, taking penetration into account * * Arguments: - * 0: Engine damage + * 0: Real damage * 1: Impact ammo - * 2: Scaled hitpoint Armor + * 2: Scaled hitpoint armor * * Return Value: * Final damage for the given hitpoint * * Example: - * [0.5, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage + * [9, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage * * Public: No */ From 92d636dec0eaf89f7e22279c99a67f900b1fc99a Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 03:33:34 -0300 Subject: [PATCH 11/70] fix function header --- addons/medical_engine/functions/fnc_getHitpointArmor.sqf | 2 +- addons/medical_engine/functions/fnc_getItemArmor.sqf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index 6d9035f00ef..9ddc2cfb0b0 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -1,6 +1,6 @@ #include "script_component.hpp" /* - * Author: Pterolatypus + * Author: Pterolatypus, Salluci * Checks a unit's equipment to calculate the total armor on a hitpoint. * * Arguments: diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 6094fbde1d9..4049b8ff2b2 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,14 +1,14 @@ #include "script_component.hpp" /* * Author: Pterolatypus, Salluci - * Returns the scaled armor value the given item provides to a particular hitpoint, either from a cache or by reading the item config. + * Returns the regular and scaled armor values the given item provides to a particular hitpoint, either from a cache or by reading the item config. * * Arguments: * 0: Item Class * 1: Hitpoint * * Return Value: - * Scaled item armor for the given hitpoint + * Regular and scaled item armor for the given hitpoint * * Example: * ["V_PlateCarrier_rgr", "HitChest"] call ace_medical_engine_fnc_getItemArmor From 33c3e9e9b6e9739cfe48d47b0a69b3735b91ec34 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 03:11:57 -0300 Subject: [PATCH 12/70] rework armor penetration --- addons/medical_engine/XEH_PREP.hpp | 2 + addons/medical_engine/XEH_preInit.sqf | 1 + .../functions/fnc_calculateDamage.sqf | 54 +++++++++++++++++++ .../functions/fnc_getAmmoData.sqf | 30 +++++++++++ .../functions/fnc_handleDamage.sqf | 12 +++-- addons/medical_engine/initSettings.sqf | 9 ++++ .../medical_engine/script_macros_medical.hpp | 2 +- addons/medical_engine/stringtable.xml | 6 +++ 8 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 addons/medical_engine/functions/fnc_calculateDamage.sqf create mode 100644 addons/medical_engine/functions/fnc_getAmmoData.sqf diff --git a/addons/medical_engine/XEH_PREP.hpp b/addons/medical_engine/XEH_PREP.hpp index 63faaf7bbec..ee55dcee7b3 100644 --- a/addons/medical_engine/XEH_PREP.hpp +++ b/addons/medical_engine/XEH_PREP.hpp @@ -1,6 +1,8 @@ PREP(applyAnimAfterRagdoll); +PREP(calculateDamage); PREP(damageBodyPart); PREP(disableThirdParty); +PREP(getAmmoData); PREP(getHitpointArmor); PREP(getItemArmor); PREP(handleDamage); diff --git a/addons/medical_engine/XEH_preInit.sqf b/addons/medical_engine/XEH_preInit.sqf index 8157baa4ec5..383e9b7b33b 100644 --- a/addons/medical_engine/XEH_preInit.sqf +++ b/addons/medical_engine/XEH_preInit.sqf @@ -32,6 +32,7 @@ if (isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_K) || isNil QUOTE(FATAL_SUM_DAMAGE_WEIB // Cache for armor values of equipped items (vests etc) GVAR(armorCache) = createHashMap; +GVAR(ammoCache) = createHashMap; // Hack for #3168 (units in static weapons do not take any damage): // Doing a manual pre-load with a small distance seems to fix the LOD problems diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf new file mode 100644 index 00000000000..d8d6f909cf9 --- /dev/null +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -0,0 +1,54 @@ +#include "script_component.hpp" +/* + * Author: Salluci + * Calculates final damage inflicted by a round using scaled hitpoint armor and engine damage, taking penetration into account + * + * Arguments: + * 0: Engine damage + * 1: Impact ammo + * 2: Scaled hitpoint Armor + * + * Return Value: + * Final damage for the given hitpoint + * + * Example: + * [0.5, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage + * + * Public: No + */ + +// B_556x45_Ball +#define BASE_SCALING_DAMAGE 9 +#define BASE_SCALING_CALIBER 0.869565 +// Magic numbers, tweak until expected results +#define DAMAGE_SCALING_FACTOR 1.4 +#define ARMOR_NEGATION_FACTOR 9 +// Extra damage from overpenetration +#define OVERKILL_FACTOR 2 + + +params ["_damage", "_ammo", "_armor"]; + +// Skip environmental damage +if (_ammo isEqualTo "") exitWith { + _damage // return +}; + +// Get ammo data to calculate penetration +([_ammo] call FUNC(getAmmoData)) params ["", "_caliber"]; + +private _penFactor = (_caliber / BASE_SCALING_CALIBER); + +// Penetration factor negates armor, we use the vanilla V_PlateCarrier1_blk and B_556x45_Ball as a scaling reference, with increase in ammo "caliber" being exponential for armor negation +// This is gameified and balanced around vanilla armor behavior, since we can't get fireGeometry data for models and their materials can't be relied upon +// A more elegant solution would be using the "HitPart" eventhandler for damage instead, but we'd have issues with reliability due to locality and performance +// Ideally, we want B_556x45_Ball hitting V_PlateCarrier1_blk at point blank to deal ~0.35 damage and scale up to B_127x108 dealing ~2.5 damage on overpenetration +// This should be compatible with weapon and armor mods balanced around vanilla or Spartan0536's values (https://forums.bohemia.net/forums/topic/163694-arma-iii-ballistics-overhaul/) + +private _armorNegated = ARMOR_NEGATION_FACTOR * _penFactor; +_damage = (sqrt ((_damage / BASE_SCALING_DAMAGE) / (1 max (_armor - _armorNegated)))) * DAMAGE_SCALING_FACTOR; +if ( _armorNegated > (_armor * 2)) then { + _damage = _damage * OVERKILL_FACTOR; +}; +TRACE_5("finalDamage", _damage,_ammo,_penFactor,_armor,_armorNegated); +_damage // return diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf new file mode 100644 index 00000000000..a411719dad8 --- /dev/null +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -0,0 +1,30 @@ +#include "script_component.hpp" +/* + * Author: Salluci + * Returns base damage value of a given round, either from a cache or by reading the ammo config. + * + * Arguments: + * 0: Ammo + * + * Return Value: + * Base damage value and penetration factor of a given round + * + * Example: + * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit + * + * Public: No + */ + +params ["_ammo"]; + +private _return = GVAR(ammoCache) get _ammo; +if (isNil "_return") then { + TRACE_1("Cache miss",_ammo); + private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + _hit = getNumber (_ammoConfig >> "hit"); + _caliber = getNumber (_ammoConfig >> "caliber"); + _return = [_hit, _caliber]; + GVAR(ammoCache) set [_ammo, _return]; +}; + +_return // return diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 4559eba31ee..28cce88dc6c 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -38,6 +38,13 @@ private _newDamage = _damage - _oldDamage; [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; private _realDamageScaled = _newDamage * _armorScaled; + +// Setting for those who prefer the regular balance or have custom ammo configs +if (EGVAR(medical,alternateArmorPenetration)) then { + // Calculate damage based on ammo "caliber": ammo material penetration + _realDamageScaled = [_realDamage, _ammo, _armorScaled] call FUNC(calculateDamage); +}; + TRACE_5("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_realDamageScaled); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs @@ -120,9 +127,8 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { _allDamages sort false; - // We only need real damage at this point, divide by 10 to use engine range of 0-1 (or higher for really high damage) - // _newDamage is maintained for compatibility - _allDamages = _allDamages apply {[(_x select 3) / 10, _x select 4, _x select 0]}; + // We only need real damage at this point, _newDamage is maintained for compatibility + _allDamages = _allDamages apply {[_x select 3, _x select 4, _x select 0]}; // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them diff --git a/addons/medical_engine/initSettings.sqf b/addons/medical_engine/initSettings.sqf index 9fe80afcb00..032c7ffd9e7 100644 --- a/addons/medical_engine/initSettings.sqf +++ b/addons/medical_engine/initSettings.sqf @@ -6,3 +6,12 @@ true, true ] call CBA_fnc_addSetting; + +[ + QEGVAR(medical,alternateArmorPenetration), + "CHECKBOX", + [LSTRING(AlternateArmorPenetration_DisplayName), LSTRING(AlternateArmorPenetration_Description)], + ELSTRING(medical,Category), + true, + true +] call CBA_fnc_addSetting; diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index 618810414a6..d591a4408ed 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -17,7 +17,7 @@ #define HEAD_DAMAGE_THRESHOLD EGVAR(medical,const_headDamageThreshold) #define HEAD_DAMAGE_THRESHOLD_DEFAULT 1 #define ORGAN_DAMAGE_THRESHOLD EGVAR(medical,const_organDamageThreshold) -#define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.6 +#define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.35 // Consts for determineIfFatal: sum of damage (values are calcualted at runtime in preInit) #define FATAL_SUM_DAMAGE_WEIBULL_K EGVAR(medical,const_fatalSumDamageWeibull_K) #define FATAL_SUM_DAMAGE_WEIBULL_L EGVAR(medical,const_fatalSumDamageWeibull_L) diff --git a/addons/medical_engine/stringtable.xml b/addons/medical_engine/stringtable.xml index 81e2ae8450f..268ee938ed0 100644 --- a/addons/medical_engine/stringtable.xml +++ b/addons/medical_engine/stringtable.xml @@ -23,5 +23,11 @@ 控制乘员是否受到车辆碰撞的伤害。 차량 충돌로 인해 탑승인원들이 피해를 받을 지 결정합니다. + + Use Alternate Armor Penetration + + + Controls whether ammo material penetration ("caliber") is taken into account for damage handling. + From 628c0c81126acd145a6e45d600f6c4b910ea9f1c Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 03:20:19 -0300 Subject: [PATCH 13/70] fix function header --- addons/medical_engine/functions/fnc_calculateDamage.sqf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index d8d6f909cf9..749d3a54f1f 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -1,18 +1,18 @@ #include "script_component.hpp" /* * Author: Salluci - * Calculates final damage inflicted by a round using scaled hitpoint armor and engine damage, taking penetration into account + * Calculates final damage inflicted by a round using scaled hitpoint armor and real damage, taking penetration into account * * Arguments: - * 0: Engine damage + * 0: Real damage * 1: Impact ammo - * 2: Scaled hitpoint Armor + * 2: Scaled hitpoint armor * * Return Value: * Final damage for the given hitpoint * * Example: - * [0.5, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage + * [9, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage * * Public: No */ From 6991e8cb2085d41f774e8b52cff4133bec5e6dc4 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 03:36:50 -0300 Subject: [PATCH 14/70] fix another function header --- addons/medical_engine/functions/fnc_getAmmoData.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf index a411719dad8..237238160c1 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -1,13 +1,13 @@ #include "script_component.hpp" /* * Author: Salluci - * Returns base damage value of a given round, either from a cache or by reading the ammo config. + * Returns base damage value and penetration factor of a given round, either from a cache or by reading the ammo config. * * Arguments: * 0: Ammo * * Return Value: - * Base damage value and penetration factor of a given round + * Base damage value and penetration factor * * Example: * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit From 2d36e669b416cba4a40a0ee3f2cfd5a2553499fb Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 17:24:10 -0300 Subject: [PATCH 15/70] fix infinite armor when no item equipped --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 4049b8ff2b2..27854c2eec6 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -27,7 +27,9 @@ if (isNil "_return") then { private _passThrough = 1; TRACE_2("Cache miss",_item,_hitpoint); if ("" in [_item, _hitpoint]) exitWith { - GVAR(armorCache) set [_key, [_armor, _armorScaled]]; + _return = [_armor, _armorScaled]; + GVAR(armorCache) set [_key, _return]; + _return }; private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; From 56ef3a334505530c7c2a26cb54b471de546aab57 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 17:26:45 -0300 Subject: [PATCH 16/70] remove extra return --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 27854c2eec6..9eade2f0871 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -29,7 +29,6 @@ if (isNil "_return") then { if ("" in [_item, _hitpoint]) exitWith { _return = [_armor, _armorScaled]; GVAR(armorCache) set [_key, _return]; - _return }; private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; From cdf6520f922146afca7cfefd03ebe7b57a75df82 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 17:27:52 -0300 Subject: [PATCH 17/70] fix infinite armor when no item equipped --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 4049b8ff2b2..9eade2f0871 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -27,7 +27,8 @@ if (isNil "_return") then { private _passThrough = 1; TRACE_2("Cache miss",_item,_hitpoint); if ("" in [_item, _hitpoint]) exitWith { - GVAR(armorCache) set [_key, [_armor, _armorScaled]]; + _return = [_armor, _armorScaled]; + GVAR(armorCache) set [_key, _return]; }; private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; From 1d1edbbc88f64ff9b6a37c33eb632c483545dbdc Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 14 Jun 2023 19:19:11 -0300 Subject: [PATCH 18/70] add private --- addons/medical_engine/functions/fnc_getAmmoData.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf index 237238160c1..747e596f5a0 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -21,8 +21,8 @@ private _return = GVAR(ammoCache) get _ammo; if (isNil "_return") then { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; - _hit = getNumber (_ammoConfig >> "hit"); - _caliber = getNumber (_ammoConfig >> "caliber"); + private _hit = getNumber (_ammoConfig >> "hit"); + private _caliber = getNumber (_ammoConfig >> "caliber"); _return = [_hit, _caliber]; GVAR(ammoCache) set [_ammo, _return]; }; From 73119fd0e23674bcb0b6cb16683be9b65f33a153 Mon Sep 17 00:00:00 2001 From: Salluci Date: Thu, 15 Jun 2023 09:20:55 -0300 Subject: [PATCH 19/70] use math --- addons/medical_engine/XEH_preInit.sqf | 2 + .../functions/fnc_calculateDamage.sqf | 72 ++++++++++++------- .../functions/fnc_getAmmoData.sqf | 14 ++-- .../functions/fnc_getItemArmor.sqf | 3 +- .../functions/fnc_handleDamage.sqf | 3 +- .../medical_engine/script_macros_medical.hpp | 3 + 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/addons/medical_engine/XEH_preInit.sqf b/addons/medical_engine/XEH_preInit.sqf index 383e9b7b33b..2c2f3ebeada 100644 --- a/addons/medical_engine/XEH_preInit.sqf +++ b/addons/medical_engine/XEH_preInit.sqf @@ -32,6 +32,8 @@ if (isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_K) || isNil QUOTE(FATAL_SUM_DAMAGE_WEIB // Cache for armor values of equipped items (vests etc) GVAR(armorCache) = createHashMap; + +// Used for armor penetration calculation GVAR(ammoCache) = createHashMap; // Hack for #3168 (units in static weapons do not take any damage): diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index 749d3a54f1f..68575468f88 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -16,39 +16,61 @@ * * Public: No */ - -// B_556x45_Ball -#define BASE_SCALING_DAMAGE 9 -#define BASE_SCALING_CALIBER 0.869565 -// Magic numbers, tweak until expected results -#define DAMAGE_SCALING_FACTOR 1.4 -#define ARMOR_NEGATION_FACTOR 9 -// Extra damage from overpenetration -#define OVERKILL_FACTOR 2 - - +// armor 12 with passthrough 0.5 + a standard uniform, armor 2 with passthrough 0.8 +#define SCALED_SOFT_ARMOR_MAX_VALUE 18 +// armor 2 with passthrough 0.8 +#define SCALED_UNPROTECTED_VALUE 4 params ["_damage", "_ammo", "_armor"]; -// Skip environmental damage -if (_ammo isEqualTo "") exitWith { +// Skip environmental damage and shrapnel +if (_ammo isEqualTo "" || {_ammo isKindOf "ace_frag_base"} || {_ammo isEqualTo "rhs_he_fragments"}) exitWith { + TRACE_1("skipping",_ammo); _damage // return }; // Get ammo data to calculate penetration -([_ammo] call FUNC(getAmmoData)) params ["", "_caliber"]; +// _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) +// While composite armor is actually harder to penetrate than RHA, this also includes the unit's body, soft armor inserts, uniform, and other factors, so it's a good enough approximation +// See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), +([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed", "_blacklisted"]; -private _penFactor = (_caliber / BASE_SCALING_CALIBER); +// Skip everything that isn't a bullet +if (_blacklisted) exitWith { + TRACE_1("skipping",_ammo); + _damage // return +}; -// Penetration factor negates armor, we use the vanilla V_PlateCarrier1_blk and B_556x45_Ball as a scaling reference, with increase in ammo "caliber" being exponential for armor negation -// This is gameified and balanced around vanilla armor behavior, since we can't get fireGeometry data for models and their materials can't be relied upon -// A more elegant solution would be using the "HitPart" eventhandler for damage instead, but we'd have issues with reliability due to locality and performance -// Ideally, we want B_556x45_Ball hitting V_PlateCarrier1_blk at point blank to deal ~0.35 damage and scale up to B_127x108 dealing ~2.5 damage on overpenetration -// This should be compatible with weapon and armor mods balanced around vanilla or Spartan0536's values (https://forums.bohemia.net/forums/topic/163694-arma-iii-ballistics-overhaul/) +// Between 5 and 35% of the projectile's energy is transferred to the unit +// This adds some variety to damage and allows for both lucky hits and survival, rewarding concentrated automatic fire +private _energyTransferred = random [0.05, 0.2, 0.35]; +private _impactSpeed = (_damage/_hit) * _typicalSpeed; // _damage is _hit at _typicalSpeed, see https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed -private _armorNegated = ARMOR_NEGATION_FACTOR * _penFactor; -_damage = (sqrt ((_damage / BASE_SCALING_DAMAGE) / (1 max (_armor - _armorNegated)))) * DAMAGE_SCALING_FACTOR; -if ( _armorNegated > (_armor * 2)) then { - _damage = _damage * OVERKILL_FACTOR; +// We'll use this value as baseline for unprotected body parts, this allows pistols to remain relevant +// There's no need to calculate penetration if there is no armor to begin with +private _penDepth = 2; +if (_armor >= SCALED_UNPROTECTED_VALUE) then { + _penDepth = _penFactor * _impactSpeed * (1 - _energyTransferred); + + // We also want pistols to be relevant against soft armor (armor levels 8-12) but fall off beyond that + // Increasing penetration directly is fine in this case with a compensation for what should be hollow points + // The idea is soft armor should catch most pistol rounds, but not all + if (_armor <= SCALED_SOFT_ARMOR_MAX_VALUE) then { + _penDepth = _penDepth + (1/(_penFactor * _armor)) + random [0, 0.2, 1]; + TRACE_2("hit soft armor",_penDepth,_penFactor); + }; +} else { + // Add some bonus damage to hitting unprotected body parts to account for more of the energy being transferred directly + // This also lets unarmored units potentially survive small hits to torso, rarely. + _hit = _hit * (0.8 + _energyTransferred); }; -TRACE_5("finalDamage", _damage,_ammo,_penFactor,_armor,_armorNegated); +TRACE_4("impact",_impactSpeed,_penDepth,_energyTransferred,_armor); + +// We want to base damage on the weight of the round, its velocity, and how much energy was spent penetrating armor, so we'll use the config value to get damage +// Because impactSpeed comes from the engine impact damage, this already handles damage loss from hits at oblique angles +_damage = _hit * sqrt((_impactSpeed * _energyTransferred) / _typicalSpeed); + +// Now we reduce the armor, capping at 2 because there's only so much damage a single projectile can do +_damage = _damage / (2 max (_armor - _penDepth)); + +TRACE_4("finalDamage",_damage,_ammo,_armor,_penDepth); _damage // return diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf index 747e596f5a0..59d6b7e8b0a 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -1,13 +1,13 @@ #include "script_component.hpp" /* * Author: Salluci - * Returns base damage value and penetration factor of a given round, either from a cache or by reading the ammo config. + * Returns base damage value, penetration factor of, and expected muzzle velocity of a given round, either from a cache or by reading the ammo config. * * Arguments: * 0: Ammo * * Return Value: - * Base damage value and penetration factor + * Base damage value, penetration factor, muzzle velocity * * Example: * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit @@ -21,9 +21,15 @@ private _return = GVAR(ammoCache) get _ammo; if (isNil "_return") then { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; + private _blacklisted = (getText (_ammoConfig >> "simulation")) isNotEqualTo "shotBullet"; + if (_blacklisted) exitWith { + _return = [0, 0, 0, _blacklisted]; + GVAR(ammoCache) set [_ammo, _return]; + }; private _hit = getNumber (_ammoConfig >> "hit"); - private _caliber = getNumber (_ammoConfig >> "caliber"); - _return = [_hit, _caliber]; + private _penFactor = (getNumber (_ammoConfig >> "caliber")) * RHA_PENETRABILITY; + private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); + _return = [_hit, _penFactor, _typicalSpeed, _blacklisted]; GVAR(ammoCache) set [_ammo, _return]; }; diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 9eade2f0871..fc13742dd74 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,3 +1,4 @@ +#define DEBUG_MODE_FULL #include "script_component.hpp" /* * Author: Pterolatypus, Salluci @@ -60,5 +61,5 @@ if (isNil "_return") then { _return = [_armor, _armorScaled]; GVAR(armorCache) set [_key, _return]; }; - +TRACE_3("",_item,_hitpoint,_return); _return // return diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 28cce88dc6c..36cabd68b24 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -39,14 +39,13 @@ private _newDamage = _damage - _oldDamage; private _realDamage = _newDamage * _armor; private _realDamageScaled = _newDamage * _armorScaled; +TRACE_5("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_realDamageScaled); // Setting for those who prefer the regular balance or have custom ammo configs if (EGVAR(medical,alternateArmorPenetration)) then { // Calculate damage based on ammo "caliber": ammo material penetration _realDamageScaled = [_realDamage, _ammo, _armorScaled] call FUNC(calculateDamage); }; -TRACE_5("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_realDamageScaled); - // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs // Damage occurs in consistent increments if ( diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index d591a4408ed..5aef95db904 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -62,6 +62,9 @@ #define PENETRATION_THRESHOLD EGVAR(medical,const_penetrationThreshold) #define PENETRATION_THRESHOLD_DEFAULT 0.35 +// RHA penetrability used for armor penetration calculation, see (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber) +#define RHA_PENETRABILITY 0.015 + // To be replaced by a proper blood pressure calculation #define BLOOD_LOSS_KNOCK_OUT_THRESHOLD EGVAR(medical,const_bloodLossKnockOutThreshold) #define BLOOD_LOSS_KNOCK_OUT_THRESHOLD_DEFAULT 0.5 // 50% of cardiac output From 8ef0af1457b79a5a0c51d890c1597c84b6c6a08d Mon Sep 17 00:00:00 2001 From: Salluci Date: Thu, 15 Jun 2023 09:36:35 -0300 Subject: [PATCH 20/70] improve condition check --- .../medical_engine/functions/fnc_calculateDamage.sqf | 12 +++--------- addons/medical_engine/functions/fnc_getAmmoData.sqf | 5 ----- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index 68575468f88..8b56863c7ea 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -22,8 +22,8 @@ #define SCALED_UNPROTECTED_VALUE 4 params ["_damage", "_ammo", "_armor"]; -// Skip environmental damage and shrapnel -if (_ammo isEqualTo "" || {_ammo isKindOf "ace_frag_base"} || {_ammo isEqualTo "rhs_he_fragments"}) exitWith { +// Skip environmental damage, everything that isn't a bullet, and shrapnel +if !(_ammo isNotEqualTo "" && {!(_ammo isKindOF "BulletBase")} && {!(_ammo isKindOf "ace_frag_base")} && {!(_ammo isEqualTo "rhs_he_fragments")}) exitWith { TRACE_1("skipping",_ammo); _damage // return }; @@ -32,13 +32,7 @@ if (_ammo isEqualTo "" || {_ammo isKindOf "ace_frag_base"} || {_ammo isEqualTo " // _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) // While composite armor is actually harder to penetrate than RHA, this also includes the unit's body, soft armor inserts, uniform, and other factors, so it's a good enough approximation // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), -([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed", "_blacklisted"]; - -// Skip everything that isn't a bullet -if (_blacklisted) exitWith { - TRACE_1("skipping",_ammo); - _damage // return -}; +([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; // Between 5 and 35% of the projectile's energy is transferred to the unit // This adds some variety to damage and allows for both lucky hits and survival, rewarding concentrated automatic fire diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf index 59d6b7e8b0a..c4ff893717e 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -21,11 +21,6 @@ private _return = GVAR(ammoCache) get _ammo; if (isNil "_return") then { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; - private _blacklisted = (getText (_ammoConfig >> "simulation")) isNotEqualTo "shotBullet"; - if (_blacklisted) exitWith { - _return = [0, 0, 0, _blacklisted]; - GVAR(ammoCache) set [_ammo, _return]; - }; private _hit = getNumber (_ammoConfig >> "hit"); private _penFactor = (getNumber (_ammoConfig >> "caliber")) * RHA_PENETRABILITY; private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); From 603508886f1e9458cc1772dfd65cf168e266daf0 Mon Sep 17 00:00:00 2001 From: Salluci Date: Thu, 15 Jun 2023 09:37:30 -0300 Subject: [PATCH 21/70] do it properly --- addons/medical_engine/functions/fnc_calculateDamage.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index 8b56863c7ea..e7130894a64 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -23,7 +23,7 @@ params ["_damage", "_ammo", "_armor"]; // Skip environmental damage, everything that isn't a bullet, and shrapnel -if !(_ammo isNotEqualTo "" && {!(_ammo isKindOF "BulletBase")} && {!(_ammo isKindOf "ace_frag_base")} && {!(_ammo isEqualTo "rhs_he_fragments")}) exitWith { +if !(_ammo isNotEqualTo "" && {!(_ammo isKindOF "BulletBase")} && {!(_ammo isKindOf "ace_frag_base")} && {_ammo isNotEqualTo "rhs_he_fragments"}) exitWith { TRACE_1("skipping",_ammo); _damage // return }; From 434d5d55c329325e33d7f97e70191f07f48cf9fd Mon Sep 17 00:00:00 2001 From: Salluci Date: Thu, 15 Jun 2023 21:00:57 -0300 Subject: [PATCH 22/70] improve macro, condition, remove unarmored bonus --- .../functions/fnc_calculateDamage.sqf | 22 +++++++++++-------- .../functions/fnc_getAmmoData.sqf | 2 +- .../medical_engine/script_macros_medical.hpp | 6 ++--- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index e7130894a64..f8f2433801a 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -16,6 +16,9 @@ * * Public: No */ + +// Average of values in https://pubmed.ncbi.nlm.nih.gov/7304523/ +#define MINIMUM_VELOCITY 50 // armor 12 with passthrough 0.5 + a standard uniform, armor 2 with passthrough 0.8 #define SCALED_SOFT_ARMOR_MAX_VALUE 18 // armor 2 with passthrough 0.8 @@ -23,21 +26,26 @@ params ["_damage", "_ammo", "_armor"]; // Skip environmental damage, everything that isn't a bullet, and shrapnel -if !(_ammo isNotEqualTo "" && {!(_ammo isKindOF "BulletBase")} && {!(_ammo isKindOf "ace_frag_base")} && {_ammo isNotEqualTo "rhs_he_fragments"}) exitWith { - TRACE_1("skipping",_ammo); +if !(_ammo isNotEqualTo "" && {_ammo isKindOf "BulletBase"} && {!(_ammo isKindOf "ace_frag_base")} && {_ammo isNotEqualTo "rhs_he_fragments"}) exitWith { + TRACE_1("skipping non-bullet damage",_ammo); _damage // return }; // Get ammo data to calculate penetration -// _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) +// _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 0.667mm of RHA (penetrability 0.015) // While composite armor is actually harder to penetrate than RHA, this also includes the unit's body, soft armor inserts, uniform, and other factors, so it's a good enough approximation // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), ([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; -// Between 5 and 35% of the projectile's energy is transferred to the unit +private _impactSpeed = (_damage/_hit) * _typicalSpeed; // _damage is _hit at _typicalSpeed, see https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed +if (_impactSpeed < MINIMUM_VELOCITY) exitWith { + TRACE_2("projectile under minimum damage velocity",_ammo,_impactSpeed); + 0 // return +}; + +// Between 5 and 35% of the projectile's energy is always transferred to the unit // This adds some variety to damage and allows for both lucky hits and survival, rewarding concentrated automatic fire private _energyTransferred = random [0.05, 0.2, 0.35]; -private _impactSpeed = (_damage/_hit) * _typicalSpeed; // _damage is _hit at _typicalSpeed, see https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed // We'll use this value as baseline for unprotected body parts, this allows pistols to remain relevant // There's no need to calculate penetration if there is no armor to begin with @@ -52,10 +60,6 @@ if (_armor >= SCALED_UNPROTECTED_VALUE) then { _penDepth = _penDepth + (1/(_penFactor * _armor)) + random [0, 0.2, 1]; TRACE_2("hit soft armor",_penDepth,_penFactor); }; -} else { - // Add some bonus damage to hitting unprotected body parts to account for more of the energy being transferred directly - // This also lets unarmored units potentially survive small hits to torso, rarely. - _hit = _hit * (0.8 + _energyTransferred); }; TRACE_4("impact",_impactSpeed,_penDepth,_energyTransferred,_armor); diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf index c4ff893717e..c1718a333c4 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -22,7 +22,7 @@ if (isNil "_return") then { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; private _hit = getNumber (_ammoConfig >> "hit"); - private _penFactor = (getNumber (_ammoConfig >> "caliber")) * RHA_PENETRABILITY; + private _penFactor = (getNumber (_ammoConfig >> "caliber")) * ARMOR_PENETRABILITY; private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); _return = [_hit, _penFactor, _typicalSpeed, _blacklisted]; GVAR(ammoCache) set [_ammo, _return]; diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index 5aef95db904..08c4ae2b003 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -15,7 +15,7 @@ // Damage threshold above which fatal organ damage can occur #define HEAD_DAMAGE_THRESHOLD EGVAR(medical,const_headDamageThreshold) -#define HEAD_DAMAGE_THRESHOLD_DEFAULT 1 +#define HEAD_DAMAGE_THRESHOLD_DEFAULT 0.6 #define ORGAN_DAMAGE_THRESHOLD EGVAR(medical,const_organDamageThreshold) #define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.35 // Consts for determineIfFatal: sum of damage (values are calcualted at runtime in preInit) @@ -62,8 +62,8 @@ #define PENETRATION_THRESHOLD EGVAR(medical,const_penetrationThreshold) #define PENETRATION_THRESHOLD_DEFAULT 0.35 -// RHA penetrability used for armor penetration calculation, see (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber) -#define RHA_PENETRABILITY 0.015 +// Baseline penetrability used for armor penetration calculation, see (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber) +#define ARMOR_PENETRABILITY 0.015 // To be replaced by a proper blood pressure calculation #define BLOOD_LOSS_KNOCK_OUT_THRESHOLD EGVAR(medical,const_bloodLossKnockOutThreshold) From dc39995cbef9e5283cca03b1504d0b3b6276d870 Mon Sep 17 00:00:00 2001 From: Salluci Date: Sat, 17 Jun 2023 00:06:02 -0300 Subject: [PATCH 23/70] rework math again --- .../functions/fnc_calculateDamage.sqf | 33 +++++++------------ .../functions/fnc_getAmmoData.sqf | 9 ++--- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf index f8f2433801a..188d547b772 100644 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ b/addons/medical_engine/functions/fnc_calculateDamage.sqf @@ -32,10 +32,10 @@ if !(_ammo isNotEqualTo "" && {_ammo isKindOf "BulletBase"} && {!(_ammo isKindOf }; // Get ammo data to calculate penetration -// _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 0.667mm of RHA (penetrability 0.015) +// _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) // While composite armor is actually harder to penetrate than RHA, this also includes the unit's body, soft armor inserts, uniform, and other factors, so it's a good enough approximation // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), -([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; +([_ammo] call FUNC(getAmmoData)) params ["_hit", "_crossSection", "_penFactor", "_typicalSpeed"]; private _impactSpeed = (_damage/_hit) * _typicalSpeed; // _damage is _hit at _typicalSpeed, see https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed if (_impactSpeed < MINIMUM_VELOCITY) exitWith { @@ -43,32 +43,21 @@ if (_impactSpeed < MINIMUM_VELOCITY) exitWith { 0 // return }; -// Between 5 and 35% of the projectile's energy is always transferred to the unit -// This adds some variety to damage and allows for both lucky hits and survival, rewarding concentrated automatic fire -private _energyTransferred = random [0.05, 0.2, 0.35]; - // We'll use this value as baseline for unprotected body parts, this allows pistols to remain relevant // There's no need to calculate penetration if there is no armor to begin with -private _penDepth = 2; +private _penDepth = SCALED_UNPROTECTED_VALUE; if (_armor >= SCALED_UNPROTECTED_VALUE) then { - _penDepth = _penFactor * _impactSpeed * (1 - _energyTransferred); - - // We also want pistols to be relevant against soft armor (armor levels 8-12) but fall off beyond that - // Increasing penetration directly is fine in this case with a compensation for what should be hollow points - // The idea is soft armor should catch most pistol rounds, but not all - if (_armor <= SCALED_SOFT_ARMOR_MAX_VALUE) then { - _penDepth = _penDepth + (1/(_penFactor * _armor)) + random [0, 0.2, 1]; - TRACE_2("hit soft armor",_penDepth,_penFactor); - }; + _penDepth = (_penFactor * _impactSpeed) * random [0.75, 1, 1.25]; // penetration can vary a bit for more damage variety }; -TRACE_4("impact",_impactSpeed,_penDepth,_energyTransferred,_armor); +TRACE_3("impact",_impactSpeed,_penDepth,_armor); -// We want to base damage on the weight of the round, its velocity, and how much energy was spent penetrating armor, so we'll use the config value to get damage +// We want to base damage on the round's weight, cross-section, velocity, and energy spent penetrating armor, so we'll use the config value to get damage // Because impactSpeed comes from the engine impact damage, this already handles damage loss from hits at oblique angles -_damage = _hit * sqrt((_impactSpeed * _energyTransferred) / _typicalSpeed); - -// Now we reduce the armor, capping at 2 because there's only so much damage a single projectile can do -_damage = _damage / (2 max (_armor - _penDepth)); +// There's only so much damage a round can do, limited by the base resistance of an unprotected limb and its energy/cross-section +_damage = ((_hit * _crossSection) * ((_penDepth/_armor) min 1)^2) / SCALED_UNPROTECTED_VALUE; +#ifdef DEBUG_MODE_FULL +systemChat format ["dam: %1, armor: %2, penDepth: %3, hit: %4, crossSection: %5", _damage, _armor, _penDepth, _hit, _crossSection]; +#endif TRACE_4("finalDamage",_damage,_ammo,_armor,_penDepth); _damage // return diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_engine/functions/fnc_getAmmoData.sqf index c1718a333c4..1b0e0eadeac 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_engine/functions/fnc_getAmmoData.sqf @@ -1,13 +1,13 @@ #include "script_component.hpp" /* * Author: Salluci - * Returns base damage value, penetration factor of, and expected muzzle velocity of a given round, either from a cache or by reading the ammo config. + * Returns base damage value, cross-section, penetration factor, and expected muzzle velocity of a given round, either from a cache or by reading the ammo config. * * Arguments: * 0: Ammo * * Return Value: - * Base damage value, penetration factor, muzzle velocity + * Base damage value, cross-section, penetration factor, muzzle velocity * * Example: * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit @@ -22,9 +22,10 @@ if (isNil "_return") then { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; private _hit = getNumber (_ammoConfig >> "hit"); - private _penFactor = (getNumber (_ammoConfig >> "caliber")) * ARMOR_PENETRABILITY; + private _caliber = (getNumber (_ammoConfig >> "caliber")); + private _penFactor = _caliber * ARMOR_PENETRABILITY; private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); - _return = [_hit, _penFactor, _typicalSpeed, _blacklisted]; + _return = [_hit, _caliber^(1/3), _penFactor, _typicalSpeed]; GVAR(ammoCache) set [_ammo, _return]; }; From 34a4b24232d6b33e822ab092ea9fbce7b618abf4 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 28 Jun 2023 17:08:48 +0300 Subject: [PATCH 24/70] cleanup --- .../functions/fnc_handleDamage.sqf | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 4559eba31ee..92deb1f0f27 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -33,12 +33,12 @@ if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), t 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, fixing #9063 -// We need both realDamage and realDamageScaled so sorting works properly +// We scale using passThrough to handle explosive-resistant armor properly (#9063) +// We need realDamage to determine which limb was hit correctly [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; -private _realDamageScaled = _newDamage * _armorScaled; -TRACE_5("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_realDamageScaled); +_newDamage = _newDamage * (_armor/_armorScaled); +TRACE_6("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 @@ -76,11 +76,11 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { _unit setVariable [QEGVAR(medical,lastDamageSource), _shooter]; _unit setVariable [QEGVAR(medical,lastInstigator), _instigator]; - private _damageStructural = _unit getVariable [QGVAR($#structural), [0,0,0]]; + private _damageStructural = _unit getVariable [QGVAR($#structural), [0,0]]; // --- Head private _damageHead = [ - _unit getVariable [QGVAR($HitFace), [0,0,0]], + _unit getVariable [QGVAR($HitFace), [0,0]], _unit getVariable [QGVAR($HitNeck), [0,0,0]], _unit getVariable [QGVAR($HitHead), [0,0,0]] ]; @@ -89,40 +89,38 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { // --- Body private _damageBody = [ - _unit getVariable [QGVAR($HitPelvis), [0,0,0]], - _unit getVariable [QGVAR($HitAbdomen), [0,0,0]], - _unit getVariable [QGVAR($HitDiaphragm), [0,0,0]], - _unit getVariable [QGVAR($HitChest), [0,0,0]] + _unit getVariable [QGVAR($HitPelvis), [0,0]], + _unit getVariable [QGVAR($HitAbdomen), [0,0]], + _unit getVariable [QGVAR($HitDiaphragm), [0,0]], + _unit getVariable [QGVAR($HitChest), [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 [QGVAR($HitLeftArm), [0,0,0]]; - private _damageRightArm = _unit getVariable [QGVAR($HitRightArm), [0,0,0]]; - private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), [0,0,0]]; - private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), [0,0,0]]; + private _damageLeftArm = _unit getVariable [QGVAR($HitLeftArm), [0,0]]; + private _damageRightArm = _unit getVariable [QGVAR($HitRightArm), [0,0]]; + private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), [0,0]]; + private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), [0,0]]; // Find hit point that received the maximum damage // Priority used for sorting if incoming damage is equal - // _realDamage, priority, _newDamage, _realDamageScaled, body part name + // _realDamage, priority, _newDamage, body part name private _allDamages = [ - [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, _damageHead select 2, "Head"], - [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, _damageBody select 2, "Body"], - [_damageLeftArm select 0, PRIORITY_LEFT_ARM, _damageLeftArm select 1, _damageLeftArm select 2, "LeftArm"], - [_damageRightArm select 0, PRIORITY_RIGHT_ARM, _damageRightArm select 1, _damageRightArm select 2, "RightArm"], - [_damageLeftLeg select 0, PRIORITY_LEFT_LEG, _damageLeftLeg select 1, _damageLeftLeg select 2, "LeftLeg"], - [_damageRightLeg select 0, PRIORITY_RIGHT_LEG, _damageRightLeg select 1, _damageRightLeg select 2, "RightLeg"], - [_damageStructural select 0, PRIORITY_STRUCTURAL, _damageStructural select 1, _damageStructural select 2, "#structural"] + [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, "Head"], + [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, "Body"], + [_damageLeftArm select 0, PRIORITY_LEFT_ARM, _damageLeftArm select 1, "LeftArm"], + [_damageRightArm select 0, PRIORITY_RIGHT_ARM, _damageRightArm select 1, "RightArm"], + [_damageLeftLeg select 0, PRIORITY_LEFT_LEG, _damageLeftLeg select 1, "LeftLeg"], + [_damageRightLeg select 0, PRIORITY_RIGHT_LEG, _damageRightLeg select 1, "RightLeg"], + [_damageStructural select 0, PRIORITY_STRUCTURAL, _damageStructural select 1, "#structural"] ]; TRACE_2("incoming",_allDamages,_damageStructural); _allDamages sort false; - // We only need real damage at this point, divide by 10 to use engine range of 0-1 (or higher for really high damage) - // _newDamage is maintained for compatibility - _allDamages = _allDamages apply {[(_x select 3) / 10, _x select 4, _x select 0]}; + _allDamages = _allDamages apply {[_x select 2, _x select 3, _x select 0]}; // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them @@ -173,7 +171,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { }; // Damages are stored for "ace_hdbracket" event triggered last -_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage, _realDamageScaled]]; +_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]]; // Engine damage to these hitpoints controls blood visuals, limping, weapon sway // Handled in fnc_damageBodyPart, persist here From 2b3287068678f50cd32168affd0ff454bca6c627 Mon Sep 17 00:00:00 2001 From: Salluci Date: Wed, 28 Jun 2023 17:14:52 +0300 Subject: [PATCH 25/70] more cleanup --- addons/medical_engine/functions/fnc_handleDamage.sqf | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 92deb1f0f27..649c06cd710 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -38,7 +38,7 @@ private _newDamage = _damage - _oldDamage; [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; _newDamage = _newDamage * (_armor/_armorScaled); -TRACE_6("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); +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 @@ -81,8 +81,8 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { // --- Head private _damageHead = [ _unit getVariable [QGVAR($HitFace), [0,0]], - _unit getVariable [QGVAR($HitNeck), [0,0,0]], - _unit getVariable [QGVAR($HitHead), [0,0,0]] + _unit getVariable [QGVAR($HitNeck), [0,0]], + _unit getVariable [QGVAR($HitHead), [0,0]] ]; _damageHead sort false; _damageHead = _damageHead select 0; @@ -119,7 +119,6 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { TRACE_2("incoming",_allDamages,_damageStructural); _allDamages sort false; - _allDamages = _allDamages apply {[_x select 2, _x select 3, _x select 0]}; // Environmental damage sources all have empty ammo string From aafef614ddc8b7843ee7cb134868e9cedc8f64b6 Mon Sep 17 00:00:00 2001 From: Salluci Date: Thu, 6 Jul 2023 06:53:47 +0300 Subject: [PATCH 26/70] name --- addons/medical_engine/functions/fnc_getHitpointArmor.sqf | 2 +- addons/medical_engine/functions/fnc_getItemArmor.sqf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index 9ddc2cfb0b0..6b9f873b3dd 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -1,6 +1,6 @@ #include "script_component.hpp" /* - * Author: Pterolatypus, Salluci + * Author: Pterolatypus, LinkIsGrim * Checks a unit's equipment to calculate the total armor on a hitpoint. * * Arguments: diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 9eade2f0871..134bd4bfe81 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,6 +1,6 @@ #include "script_component.hpp" /* - * Author: Pterolatypus, Salluci + * Author: Pterolatypus, LinkIsGrim * Returns the regular and scaled armor values the given item provides to a particular hitpoint, either from a cache or by reading the item config. * * Arguments: From 260e1c315e82081cc67d0cb5cdfae240d91ced5f Mon Sep 17 00:00:00 2001 From: Salluci Date: Sun, 9 Jul 2023 19:13:38 +0300 Subject: [PATCH 27/70] don't scale structural damage --- addons/medical_engine/functions/fnc_handleDamage.sqf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 649c06cd710..20c5ad05966 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -37,7 +37,9 @@ private _newDamage = _damage - _oldDamage; // We need realDamage to determine which limb was hit correctly [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; private _realDamage = _newDamage * _armor; -_newDamage = _newDamage * (_armor/_armorScaled); +if (_hitPoint isNotEqualTo "#structural") then { + _newDamage = _newDamage * (_armor/_armorScaled); +}; TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs From 7b437b269dfc6a4f80e6f99564e163ffc171d362 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 11:56:29 +0300 Subject: [PATCH 28/70] move to medical_damage component --- .../medical_damage/ACE_Medical_Injuries.hpp | 3 + addons/medical_damage/XEH_PREP.hpp | 2 + addons/medical_damage/XEH_preInit.sqf | 3 + .../functions/fnc_getAmmoData.sqf | 10 +-- .../functions/fnc_woundReceived.sqf | 6 +- .../fnc_woundsHandlerArmorPenetration.sqf | 74 +++++++++++++++++++ addons/medical_damage/script_component.hpp | 4 +- addons/medical_engine/XEH_PREP.hpp | 2 - addons/medical_engine/XEH_preInit.sqf | 3 - .../functions/fnc_calculateDamage.sqf | 63 ---------------- .../functions/fnc_handleDamage.sqf | 6 -- 11 files changed, 92 insertions(+), 84 deletions(-) rename addons/{medical_engine => medical_damage}/functions/fnc_getAmmoData.sqf (61%) create mode 100644 addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf delete mode 100644 addons/medical_engine/functions/fnc_calculateDamage.sqf diff --git a/addons/medical_damage/ACE_Medical_Injuries.hpp b/addons/medical_damage/ACE_Medical_Injuries.hpp index f09008880ba..d8078c8db27 100644 --- a/addons/medical_damage/ACE_Medical_Injuries.hpp +++ b/addons/medical_damage/ACE_Medical_Injuries.hpp @@ -80,6 +80,9 @@ class ACE_Medical_Injuries { // bullets only create multiple wounds when the damage is very high thresholds[] = {{20, 10}, {4.5, 2}, {3, 1}, {0, 1}}; selectionSpecific = 1; + class woundHandlers { + ADDON = QFUNC(woundsHandlerArmorPenetration); + }; class Avulsion { // at damage, weight. between points, weight is interpolated then wound is chosen by weighted random. diff --git a/addons/medical_damage/XEH_PREP.hpp b/addons/medical_damage/XEH_PREP.hpp index 6df53fb3091..4c77818b34c 100644 --- a/addons/medical_damage/XEH_PREP.hpp +++ b/addons/medical_damage/XEH_PREP.hpp @@ -1,11 +1,13 @@ PREP(debug_explosiveTest); PREP(determineIfFatal); +PREP(getAmmoData); PREP(getTypeOfDamage); PREP(handleIncapacitation); PREP(interpolatePoints); PREP(parseConfigForInjuries); PREP(parseWoundHandlersCfg); PREP(woundReceived); +PREP(woundsHandlerArmorPenetration); PREP(woundsHandlerBase); PREP(woundsHandlerBurning); PREP(woundsHandlerVehiclecrash); diff --git a/addons/medical_damage/XEH_preInit.sqf b/addons/medical_damage/XEH_preInit.sqf index 26445ad61a2..5559089be79 100644 --- a/addons/medical_damage/XEH_preInit.sqf +++ b/addons/medical_damage/XEH_preInit.sqf @@ -10,6 +10,9 @@ PREP_RECOMPILE_END; call FUNC(parseConfigForInjuries); +// Used for armor penetration calculation +GVAR(ammoCache) = createHashMap; + addMissionEventHandler ["Loaded",{ INFO("Mission Loaded - Reloading medical configs for extension"); // Reload configs into extension (handle full game restart) diff --git a/addons/medical_engine/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf similarity index 61% rename from addons/medical_engine/functions/fnc_getAmmoData.sqf rename to addons/medical_damage/functions/fnc_getAmmoData.sqf index 1b0e0eadeac..e6633a7777e 100644 --- a/addons/medical_engine/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -1,13 +1,13 @@ #include "script_component.hpp" /* - * Author: Salluci - * Returns base damage value, cross-section, penetration factor, and expected muzzle velocity of a given round, either from a cache or by reading the ammo config. + * Author: LinkIsGrim + * Returns base damage value, penetration factor, and expected muzzle velocity of a given round, either from a cache or by reading the ammo config. * * Arguments: * 0: Ammo * * Return Value: - * Base damage value, cross-section, penetration factor, muzzle velocity + * Base damage value, penetration factor, muzzle velocity * * Example: * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit @@ -23,9 +23,9 @@ if (isNil "_return") then { private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; private _hit = getNumber (_ammoConfig >> "hit"); private _caliber = (getNumber (_ammoConfig >> "caliber")); - private _penFactor = _caliber * ARMOR_PENETRABILITY; private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); - _return = [_hit, _caliber^(1/3), _penFactor, _typicalSpeed]; + private _penFactor = _caliber * ARMOR_PENETRABILITY * _typicalSpeed; + _return = [_hit, _penFactor, _typicalSpeed]; GVAR(ammoCache) set [_ammo, _return]; }; diff --git a/addons/medical_damage/functions/fnc_woundReceived.sqf b/addons/medical_damage/functions/fnc_woundReceived.sqf index 4a16042b317..95590ce8fef 100644 --- a/addons/medical_damage/functions/fnc_woundReceived.sqf +++ b/addons/medical_damage/functions/fnc_woundReceived.sqf @@ -22,8 +22,8 @@ params ["_unit", "_allDamages", "_shooter", "_ammo"]; private _typeOfDamage = _ammo call FUNC(getTypeOfDamage); if (_typeOfDamage in GVAR(damageTypeDetails)) then { (GVAR(damageTypeDetails) get _typeOfDamage) params ["", "", "_woundHandlers"]; - - private _damageData = [_unit, _allDamages, _typeOfDamage]; + + private _damageData = [_unit, _allDamages, _typeOfDamage, _ammo]; { _damageData = _damageData call _x; TRACE_1("Wound handler returned", _damageData); @@ -31,5 +31,5 @@ if (_typeOfDamage in GVAR(damageTypeDetails)) then { TRACE_1("Return invalid, terminating wound handling", _damageData); }; } forEach _woundHandlers; - + }; diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf new file mode 100644 index 00000000000..2cc817064de --- /dev/null +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -0,0 +1,74 @@ +#define DEBUG_MODE_FULL +#include "script_component.hpp" +/* + * Author: LinkIsGrim + * Custom wounds handler for armor penetration. Calculates damage based on round material penetration and unit armor + * + * Arguments: + * 0: Unit That Was Hit + * 1: Damage done to each body part + * 2: Type of the damage done + * 3: Projectile classname + * + * Return Value: + * None + * + * Example: + * [player, [[0.5, "Body", 1]], "bullet"] call ace_medical_damage_fnc_woundsHandlerArmorPenetration + * + * Public: No + */ + +// Average of values in https://pubmed.ncbi.nlm.nih.gov/7304523/ +#define MINIMUM_VELOCITY 50 +// armor 2 with passthrough 0.8 +#define SCALED_UNPROTECTED_VALUE 4 + +if (!EGVAR(medical,alternateArmorPenetration)) exitWith {_this}; + +params ["_unit", "_allDamages", "_typeOfDamage", "_ammo"]; +TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); + +private _damageData = (_allDamages select 0); // selection specific +_damageData params ["_engineDamage", "", "_realDamage"]; + +private _armor = _realDamage/_engineDamage; + +// See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), +// _penFactor is ammo "caliber" * penetrability * typicalSpeed, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) +// Above is not actually the case but a good enough approximation +([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; + +// Impact damage is hit * (impactSpeed / typicalSpeed): https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed +// Impact damage already takes into account hits at oblique angles, engine handles that +private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; + +// Velocity too low to penetrate even unprotected human skin +if (_impactSpeed < MINIMUM_VELOCITY) exitWith { + TRACE_2("projectile under minimum damage velocity",_ammo,_impactSpeed); + _damageData set [0, 0]; + [_unit, _allDamages, _typeOfDamage] // return +}; + +// We'll use this value as baseline for unprotected body parts, this allows pistols to remain relevant +// There's no need to calculate penetration if there is no armor to begin with +_armor = _armor - SCALED_UNPROTECTED_VALUE; +if (_armor <= 0) exitWith { + _damageData set [0, _realDamage / 5 ]; + [_unit, _allDamages, _typeOfDamage] // return +}; + +// Energy transferred to target can be randomized a bit ("stopping power") +_hit = _hit * random [0.75, 1, 1.25]; + +private _penDepth = _penFactor * (_impactSpeed/_typicalSpeed)^2; + +// We want to base damage on the round's energy and armor penetration exclusively, so we'll use the config value to get damage +// There's only so much damage a round can do, limited by its energy +// Divide by 5 to scale to 0-1 +private _finalDamage = (_hit * ((_penDepth/_armor) min 1)) / 5; +_damageData set [0, _finalDamage]; + +TRACE_3("Armor penetration handled, passing damage", _finalDamage, _damageData, _allDamages); + +[_unit, _allDamages, _typeOfDamage] // return diff --git a/addons/medical_damage/script_component.hpp b/addons/medical_damage/script_component.hpp index 272dc7d565b..c0a448543cd 100644 --- a/addons/medical_damage/script_component.hpp +++ b/addons/medical_damage/script_component.hpp @@ -2,8 +2,8 @@ #define COMPONENT_BEAUTIFIED Medical Damage #include "\z\ace\addons\main\script_mod.hpp" -// #define DEBUG_MODE_FULL -// #define DISABLE_COMPILE_CACHE +#define DEBUG_MODE_FULL +#define DISABLE_COMPILE_CACHE // #define ENABLE_PERFORMANCE_COUNTERS #ifdef DEBUG_ENABLED_MEDICAL_DAMAGE diff --git a/addons/medical_engine/XEH_PREP.hpp b/addons/medical_engine/XEH_PREP.hpp index 4c293f03e78..ffc3543745d 100644 --- a/addons/medical_engine/XEH_PREP.hpp +++ b/addons/medical_engine/XEH_PREP.hpp @@ -1,8 +1,6 @@ PREP(applyAnimAfterRagdoll); -PREP(calculateDamage); PREP(damageBodyPart); PREP(disableThirdParty); -PREP(getAmmoData); PREP(getHitpointArmor); PREP(getItemArmor); PREP(handleDamage); diff --git a/addons/medical_engine/XEH_preInit.sqf b/addons/medical_engine/XEH_preInit.sqf index 2c2f3ebeada..8157baa4ec5 100644 --- a/addons/medical_engine/XEH_preInit.sqf +++ b/addons/medical_engine/XEH_preInit.sqf @@ -33,9 +33,6 @@ if (isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_K) || isNil QUOTE(FATAL_SUM_DAMAGE_WEIB // Cache for armor values of equipped items (vests etc) GVAR(armorCache) = createHashMap; -// Used for armor penetration calculation -GVAR(ammoCache) = createHashMap; - // Hack for #3168 (units in static weapons do not take any damage): // Doing a manual pre-load with a small distance seems to fix the LOD problems // with handle damage not returning full results. diff --git a/addons/medical_engine/functions/fnc_calculateDamage.sqf b/addons/medical_engine/functions/fnc_calculateDamage.sqf deleted file mode 100644 index 188d547b772..00000000000 --- a/addons/medical_engine/functions/fnc_calculateDamage.sqf +++ /dev/null @@ -1,63 +0,0 @@ -#include "script_component.hpp" -/* - * Author: Salluci - * Calculates final damage inflicted by a round using scaled hitpoint armor and real damage, taking penetration into account - * - * Arguments: - * 0: Real damage - * 1: Impact ammo - * 2: Scaled hitpoint armor - * - * Return Value: - * Final damage for the given hitpoint - * - * Example: - * [9, "B_556x45_Ball", 32] call ace_medical_engine_fnc_calculateDamage - * - * Public: No - */ - -// Average of values in https://pubmed.ncbi.nlm.nih.gov/7304523/ -#define MINIMUM_VELOCITY 50 -// armor 12 with passthrough 0.5 + a standard uniform, armor 2 with passthrough 0.8 -#define SCALED_SOFT_ARMOR_MAX_VALUE 18 -// armor 2 with passthrough 0.8 -#define SCALED_UNPROTECTED_VALUE 4 -params ["_damage", "_ammo", "_armor"]; - -// Skip environmental damage, everything that isn't a bullet, and shrapnel -if !(_ammo isNotEqualTo "" && {_ammo isKindOf "BulletBase"} && {!(_ammo isKindOf "ace_frag_base")} && {_ammo isNotEqualTo "rhs_he_fragments"}) exitWith { - TRACE_1("skipping non-bullet damage",_ammo); - _damage // return -}; - -// Get ammo data to calculate penetration -// _penFactor is ammo "caliber" * penetrability, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) -// While composite armor is actually harder to penetrate than RHA, this also includes the unit's body, soft armor inserts, uniform, and other factors, so it's a good enough approximation -// See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), -([_ammo] call FUNC(getAmmoData)) params ["_hit", "_crossSection", "_penFactor", "_typicalSpeed"]; - -private _impactSpeed = (_damage/_hit) * _typicalSpeed; // _damage is _hit at _typicalSpeed, see https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed -if (_impactSpeed < MINIMUM_VELOCITY) exitWith { - TRACE_2("projectile under minimum damage velocity",_ammo,_impactSpeed); - 0 // return -}; - -// We'll use this value as baseline for unprotected body parts, this allows pistols to remain relevant -// There's no need to calculate penetration if there is no armor to begin with -private _penDepth = SCALED_UNPROTECTED_VALUE; -if (_armor >= SCALED_UNPROTECTED_VALUE) then { - _penDepth = (_penFactor * _impactSpeed) * random [0.75, 1, 1.25]; // penetration can vary a bit for more damage variety -}; -TRACE_3("impact",_impactSpeed,_penDepth,_armor); - -// We want to base damage on the round's weight, cross-section, velocity, and energy spent penetrating armor, so we'll use the config value to get damage -// Because impactSpeed comes from the engine impact damage, this already handles damage loss from hits at oblique angles -// There's only so much damage a round can do, limited by the base resistance of an unprotected limb and its energy/cross-section -_damage = ((_hit * _crossSection) * ((_penDepth/_armor) min 1)^2) / SCALED_UNPROTECTED_VALUE; - -#ifdef DEBUG_MODE_FULL -systemChat format ["dam: %1, armor: %2, penDepth: %3, hit: %4, crossSection: %5", _damage, _armor, _penDepth, _hit, _crossSection]; -#endif -TRACE_4("finalDamage",_damage,_ammo,_armor,_penDepth); -_damage // return diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 0955134e4cb..20c5ad05966 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -40,12 +40,6 @@ private _realDamage = _newDamage * _armor; if (_hitPoint isNotEqualTo "#structural") then { _newDamage = _newDamage * (_armor/_armorScaled); }; - -// Calculate damage based on ammo "caliber": ammo material penetration -// Setting for those who prefer the regular balance or have custom ammo configs -if (EGVAR(medical,alternateArmorPenetration)) then { - _newDamage = [_realDamage, _ammo, _armorScaled] call FUNC(calculateDamage); -}; TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs From 82f1e41b659707b73500c70be5d37867374bc5cb Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 11:57:52 +0300 Subject: [PATCH 29/70] revert changes to thresholds --- addons/medical_engine/script_macros_medical.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index ac73bd8bbac..c1cea696575 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -15,9 +15,9 @@ // Damage threshold above which fatal organ damage can occur #define HEAD_DAMAGE_THRESHOLD EGVAR(medical,const_headDamageThreshold) -#define HEAD_DAMAGE_THRESHOLD_DEFAULT 0.6 +#define HEAD_DAMAGE_THRESHOLD_DEFAULT 1 #define ORGAN_DAMAGE_THRESHOLD EGVAR(medical,const_organDamageThreshold) -#define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.35 +#define ORGAN_DAMAGE_THRESHOLD_DEFAULT 0.6 // Consts for determineIfFatal: sum of damage (values are calcualted at runtime in preInit) #define FATAL_SUM_DAMAGE_WEIBULL_K EGVAR(medical,const_fatalSumDamageWeibull_K) #define FATAL_SUM_DAMAGE_WEIBULL_L EGVAR(medical,const_fatalSumDamageWeibull_L) From 60f0d77f40bda41b24b88c2e14d6ac8dfc6e4e91 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:03:39 +0300 Subject: [PATCH 30/70] fix stupid --- addons/medical_damage/ACE_Medical_Injuries.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/ACE_Medical_Injuries.hpp b/addons/medical_damage/ACE_Medical_Injuries.hpp index d8078c8db27..74750c287c7 100644 --- a/addons/medical_damage/ACE_Medical_Injuries.hpp +++ b/addons/medical_damage/ACE_Medical_Injuries.hpp @@ -81,7 +81,7 @@ class ACE_Medical_Injuries { thresholds[] = {{20, 10}, {4.5, 2}, {3, 1}, {0, 1}}; selectionSpecific = 1; class woundHandlers { - ADDON = QFUNC(woundsHandlerArmorPenetration); + GVAR(armorPenetration) = QFUNC(woundsHandlerArmorPenetration); }; class Avulsion { From 6547fa0e0c2564749dbdf0a37028c8717866a68c Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:06:15 +0300 Subject: [PATCH 31/70] finish moving to damage --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- addons/medical_damage/initSettings.sqf | 9 +++++++++ addons/medical_damage/stringtable.xml | 6 ++++++ addons/medical_engine/initSettings.sqf | 9 --------- addons/medical_engine/stringtable.xml | 6 ------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 2cc817064de..aa12601b115 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -8,7 +8,7 @@ * 0: Unit That Was Hit * 1: Damage done to each body part * 2: Type of the damage done - * 3: Projectile classname + * 3: Projectile classname * * Return Value: * None diff --git a/addons/medical_damage/initSettings.sqf b/addons/medical_damage/initSettings.sqf index 14dac50f1a5..fb9cb454aa8 100644 --- a/addons/medical_damage/initSettings.sqf +++ b/addons/medical_damage/initSettings.sqf @@ -1,3 +1,12 @@ +[ + QEGVAR(medical,alternateArmorPenetration), + "CHECKBOX", + [LSTRING(AlternateArmorPenetration_DisplayName), LSTRING(AlternateArmorPenetration_Description)], + ELSTRING(medical,Category), + true, + true +] call CBA_fnc_addSetting; + [ QEGVAR(medical,fatalDamageSource), "LIST", diff --git a/addons/medical_damage/stringtable.xml b/addons/medical_damage/stringtable.xml index 20465959e0c..8b6fa30a5d3 100644 --- a/addons/medical_damage/stringtable.xml +++ b/addons/medical_damage/stringtable.xml @@ -799,5 +799,11 @@ 死于致命伤的概率。 치명상으로 인해 사망할 확률을 정합니다. + + Use Alternate Armor Penetration + + + Controls whether ammo material penetration ("caliber") is taken into account for damage handling. + diff --git a/addons/medical_engine/initSettings.sqf b/addons/medical_engine/initSettings.sqf index 032c7ffd9e7..9fe80afcb00 100644 --- a/addons/medical_engine/initSettings.sqf +++ b/addons/medical_engine/initSettings.sqf @@ -6,12 +6,3 @@ true, true ] call CBA_fnc_addSetting; - -[ - QEGVAR(medical,alternateArmorPenetration), - "CHECKBOX", - [LSTRING(AlternateArmorPenetration_DisplayName), LSTRING(AlternateArmorPenetration_Description)], - ELSTRING(medical,Category), - true, - true -] call CBA_fnc_addSetting; diff --git a/addons/medical_engine/stringtable.xml b/addons/medical_engine/stringtable.xml index 268ee938ed0..81e2ae8450f 100644 --- a/addons/medical_engine/stringtable.xml +++ b/addons/medical_engine/stringtable.xml @@ -23,11 +23,5 @@ 控制乘员是否受到车辆碰撞的伤害。 차량 충돌로 인해 탑승인원들이 피해를 받을 지 결정합니다. - - Use Alternate Armor Penetration - - - Controls whether ammo material penetration ("caliber") is taken into account for damage handling. - From 05673676bd35944f9164b7cf72348520248384c4 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:11:33 +0300 Subject: [PATCH 32/70] fix double stupid --- addons/medical_damage/ACE_Medical_Injuries.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/ACE_Medical_Injuries.hpp b/addons/medical_damage/ACE_Medical_Injuries.hpp index 74750c287c7..6a9000646c9 100644 --- a/addons/medical_damage/ACE_Medical_Injuries.hpp +++ b/addons/medical_damage/ACE_Medical_Injuries.hpp @@ -80,7 +80,7 @@ class ACE_Medical_Injuries { // bullets only create multiple wounds when the damage is very high thresholds[] = {{20, 10}, {4.5, 2}, {3, 1}, {0, 1}}; selectionSpecific = 1; - class woundHandlers { + class woundHandlers: woundHandlers { GVAR(armorPenetration) = QFUNC(woundsHandlerArmorPenetration); }; From b77eca676d11d976740ccb39732f895774bd2fc0 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:13:39 +0300 Subject: [PATCH 33/70] magic number --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index aa12601b115..e96c46c51e9 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -23,6 +23,7 @@ #define MINIMUM_VELOCITY 50 // armor 2 with passthrough 0.8 #define SCALED_UNPROTECTED_VALUE 4 +#define ENGINE_DAMAGE_INDEX 0 if (!EGVAR(medical,alternateArmorPenetration)) exitWith {_this}; @@ -46,7 +47,7 @@ private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; // Velocity too low to penetrate even unprotected human skin if (_impactSpeed < MINIMUM_VELOCITY) exitWith { TRACE_2("projectile under minimum damage velocity",_ammo,_impactSpeed); - _damageData set [0, 0]; + _damageData set [ENGINE_DAMAGE_INDEX, 0]; [_unit, _allDamages, _typeOfDamage] // return }; @@ -54,7 +55,7 @@ if (_impactSpeed < MINIMUM_VELOCITY) exitWith { // There's no need to calculate penetration if there is no armor to begin with _armor = _armor - SCALED_UNPROTECTED_VALUE; if (_armor <= 0) exitWith { - _damageData set [0, _realDamage / 5 ]; + _damageData set [ENGINE_DAMAGE_INDEX, _realDamage / 5 ]; [_unit, _allDamages, _typeOfDamage] // return }; @@ -67,7 +68,7 @@ private _penDepth = _penFactor * (_impactSpeed/_typicalSpeed)^2; // There's only so much damage a round can do, limited by its energy // Divide by 5 to scale to 0-1 private _finalDamage = (_hit * ((_penDepth/_armor) min 1)) / 5; -_damageData set [0, _finalDamage]; +_damageData set [ENGINE_DAMAGE_INDEX, _finalDamage]; TRACE_3("Armor penetration handled, passing damage", _finalDamage, _damageData, _allDamages); From 255ec5df65eb00e5ead8591b11816082326460ba Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:19:21 +0300 Subject: [PATCH 34/70] remove debug --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 857e54e1140..1084ca2577b 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,4 +1,3 @@ -#define DEBUG_MODE_FULL #include "script_component.hpp" /* * Author: Pterolatypus, LinkIsGrim From 6f706fe7a897882ef9e215208364288d6a6fe49f Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:23:06 +0300 Subject: [PATCH 35/70] remove debug pt2 --- addons/medical_damage/script_component.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_damage/script_component.hpp b/addons/medical_damage/script_component.hpp index c0a448543cd..272dc7d565b 100644 --- a/addons/medical_damage/script_component.hpp +++ b/addons/medical_damage/script_component.hpp @@ -2,8 +2,8 @@ #define COMPONENT_BEAUTIFIED Medical Damage #include "\z\ace\addons\main\script_mod.hpp" -#define DEBUG_MODE_FULL -#define DISABLE_COMPILE_CACHE +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE // #define ENABLE_PERFORMANCE_COUNTERS #ifdef DEBUG_ENABLED_MEDICAL_DAMAGE From e4e934599dc768f6b5d9cf088fb8197d7d7b6390 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:51:59 +0300 Subject: [PATCH 36/70] final cleanup --- .../fnc_woundsHandlerArmorPenetration.sqf | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index e96c46c51e9..f30ebd254cd 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -19,8 +19,6 @@ * Public: No */ -// Average of values in https://pubmed.ncbi.nlm.nih.gov/7304523/ -#define MINIMUM_VELOCITY 50 // armor 2 with passthrough 0.8 #define SCALED_UNPROTECTED_VALUE 4 #define ENGINE_DAMAGE_INDEX 0 @@ -34,6 +32,10 @@ private _damageData = (_allDamages select 0); // selection specific _damageData params ["_engineDamage", "", "_realDamage"]; private _armor = _realDamage/_engineDamage; +// There's no need to calculate penetration if there is no armor to begin with, vanilla damage handling is good enough in this case +if (_armor <= SCALED_UNPROTECTED_VALUE) exitWith { + _this // return +}; // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), // _penFactor is ammo "caliber" * penetrability * typicalSpeed, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) @@ -44,32 +46,19 @@ private _armor = _realDamage/_engineDamage; // Impact damage already takes into account hits at oblique angles, engine handles that private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; -// Velocity too low to penetrate even unprotected human skin -if (_impactSpeed < MINIMUM_VELOCITY) exitWith { - TRACE_2("projectile under minimum damage velocity",_ammo,_impactSpeed); - _damageData set [ENGINE_DAMAGE_INDEX, 0]; - [_unit, _allDamages, _typeOfDamage] // return -}; -// We'll use this value as baseline for unprotected body parts, this allows pistols to remain relevant -// There's no need to calculate penetration if there is no armor to begin with -_armor = _armor - SCALED_UNPROTECTED_VALUE; -if (_armor <= 0) exitWith { - _damageData set [ENGINE_DAMAGE_INDEX, _realDamage / 5 ]; - [_unit, _allDamages, _typeOfDamage] // return -}; -// Energy transferred to target can be randomized a bit ("stopping power") -_hit = _hit * random [0.75, 1, 1.25]; +// Energy transferred to target can be randomized a bit, final stage ballistics isn't a science +_hit = _hit * random [0.75, 1, 1.1]; private _penDepth = _penFactor * (_impactSpeed/_typicalSpeed)^2; // We want to base damage on the round's energy and armor penetration exclusively, so we'll use the config value to get damage // There's only so much damage a round can do, limited by its energy -// Divide by 5 to scale to 0-1 -private _finalDamage = (_hit * ((_penDepth/_armor) min 1)) / 5; +// Divide by 10 to scale to 0-1 +private _finalDamage = (_hit * ((_penDepth/_armor) min 1)^2) / 10; _damageData set [ENGINE_DAMAGE_INDEX, _finalDamage]; TRACE_3("Armor penetration handled, passing damage", _finalDamage, _damageData, _allDamages); -[_unit, _allDamages, _typeOfDamage] // return +_this // return From ed9d6d6e297b7a6871ae4c746c38b77f14a07933 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 12:52:19 +0300 Subject: [PATCH 37/70] did i say final? i meant pre-final. --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index f30ebd254cd..4344bef4de7 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -46,8 +46,6 @@ if (_armor <= SCALED_UNPROTECTED_VALUE) exitWith { // Impact damage already takes into account hits at oblique angles, engine handles that private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; - - // Energy transferred to target can be randomized a bit, final stage ballistics isn't a science _hit = _hit * random [0.75, 1, 1.1]; From 46e21e98c27598a91e3d0994636931f4063cf0b1 Mon Sep 17 00:00:00 2001 From: Salluci Date: Mon, 24 Jul 2023 13:22:36 +0300 Subject: [PATCH 38/70] remove 9216 --- .../functions/fnc_getHitpointArmor.sqf | 15 ++++---- .../functions/fnc_getItemArmor.sqf | 36 +++++++------------ .../functions/fnc_handleDamage.sqf | 12 ++----- 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index 6b9f873b3dd..62954a1b568 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -1,6 +1,6 @@ #include "script_component.hpp" /* - * Author: Pterolatypus, LinkIsGrim + * Author: Pterolatypus * Checks a unit's equipment to calculate the total armor on a hitpoint. * * Arguments: @@ -8,7 +8,7 @@ * 1: Hitpoint * * Return Value: - * Total armor and scaled armor for the given hitpoint + * Total armor for the given hitpoint * * Example: * [player, "HitChest"] call ace_medical_engine_fnc_getHitpointArmor @@ -32,19 +32,16 @@ private _gear = [ private _rags = _gear joinString "$"; private _var = format [QGVAR(armorCache$%1), _hitpoint]; -_unit getVariable [_var, ["", 0, 0]] params ["_prevRags", "_armor", "_armorScaled"]; +_unit getVariable [_var, [""]] params ["_prevRags", "_armor"]; if (_rags != _prevRags) then { _armor = 0; - _armorScaled = 0; { - ([_x, _hitpoint] call FUNC(getItemArmor)) params ["_itemArmor", "_itemArmorScaled"]; - _armor = _armor + _itemArmor; - _armorScaled = _armorScaled + _itemArmorScaled; + _armor = _armor + ([_x, _hitpoint] call FUNC(getItemArmor)); } forEach _gear; - _unit setVariable [_var, [_rags, _armor, _armorScaled]]; + _unit setVariable [_var, [_rags, _armor]]; }; -[_armor, _armorScaled] // return +_armor // return diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 1084ca2577b..26e1bd693aa 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,14 +1,14 @@ #include "script_component.hpp" /* - * Author: Pterolatypus, LinkIsGrim - * Returns the regular and scaled armor values the given item provides to a particular hitpoint, either from a cache or by reading the item config. + * Author: Pterolatypus + * Returns the armor value the given item provides to a particular hitpoint, either from a cache or by reading the item config. * * Arguments: * 0: Item Class * 1: Hitpoint * * Return Value: - * Regular and scaled item armor for the given hitpoint + * Item armor for the given hitpoint * * Example: * ["V_PlateCarrier_rgr", "HitChest"] call ace_medical_engine_fnc_getItemArmor @@ -19,16 +19,13 @@ params ["_item", "_hitpoint"]; private _key = format ["%1$%2", _item, _hitpoint]; -private _return = GVAR(armorCache) get _key; +private _armor = GVAR(armorCache) get _key; -if (isNil "_return") then { - private _armor = 0; - private _armorScaled = 0; - private _passThrough = 1; +if (isNil "_armor") then { TRACE_2("Cache miss",_item,_hitpoint); if ("" in [_item, _hitpoint]) exitWith { - _return = [_armor, _armorScaled]; - GVAR(armorCache) set [_key, _return]; + _armor = 0; + GVAR(armorCache) set [_key, _armor]; }; private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; @@ -41,24 +38,15 @@ if (isNil "_return") then { } else { private _entry = _unitCfg >> "HitPoints" >> _hitpoint; _armor = getNumber (_unitCfg >> "armor") * (1 max getNumber (_entry >> "armor")); - _passThrough = 0.01 max getNumber (_entry >> "passThrough"); // prevent dividing by 0 }; } else { private _condition = format ["getText (_x >> 'hitpointName') == '%1'", _hitpoint]; private _entry = configProperties [_itemInfo >> "HitpointsProtectionInfo", _condition] param [0, configNull]; - if (!isNull _entry) then { - _armor = getNumber (_entry >> "armor"); - _passThrough = 0.01 max getNumber (_entry >> "passThrough"); - }; - }; - // Scale armor using passthrough to fix explosive-resistant armor (#9063) - // Skip scaling for items that don't cover the hitpoint to prevent infinite armor - if (_armor isNotEqualTo 0) then { - _armorScaled = (log (_armor / _passThrough)) * 10; + _armor = getNumber (_entry >> "armor"); }; - _return = [_armor, _armorScaled]; - GVAR(armorCache) set [_key, _return]; + + GVAR(armorCache) set [_key, _armor]; }; -TRACE_3("",_item,_hitpoint,_return); -_return // return + +_armor // return diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 20c5ad05966..3bfd60b8b07 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -32,14 +32,9 @@ if (_hitPoint isEqualTo "") then { if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]}) 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 FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; +// Get armor value of hitpoint and calculate damage before armor +private _armor = [_unit, _hitpoint] call FUNC(getHitpointArmor); private _realDamage = _newDamage * _armor; -if (_hitPoint isNotEqualTo "#structural") then { - _newDamage = _newDamage * (_armor/_armorScaled); -}; TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs @@ -106,9 +101,8 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), [0,0]]; private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), [0,0]]; - // Find hit point that received the maximum damage + // Find hit point that received the maxium damage // Priority used for sorting if incoming damage is equal - // _realDamage, priority, _newDamage, body part name private _allDamages = [ [_damageHead select 0, PRIORITY_HEAD, _damageHead select 1, "Head"], [_damageBody select 0, PRIORITY_BODY, _damageBody select 1, "Body"], From bb53055a04028add390202953b2009dd8b74ab32 Mon Sep 17 00:00:00 2001 From: Salluci Date: Tue, 25 Jul 2023 06:10:06 +0300 Subject: [PATCH 39/70] condition --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 4344bef4de7..190056d7d00 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -23,11 +23,12 @@ #define SCALED_UNPROTECTED_VALUE 4 #define ENGINE_DAMAGE_INDEX 0 -if (!EGVAR(medical,alternateArmorPenetration)) exitWith {_this}; params ["_unit", "_allDamages", "_typeOfDamage", "_ammo"]; TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); +if !(EGVAR(medical,alternateArmorPenetration) && {_ammo isNotEqualTo ""}) exitWith {_this}; + private _damageData = (_allDamages select 0); // selection specific _damageData params ["_engineDamage", "", "_realDamage"]; From d27f6f7012d6150c883c8e6fba71f26a3c02d98b Mon Sep 17 00:00:00 2001 From: LinkIsGrim Date: Fri, 28 Jul 2023 06:35:55 +0300 Subject: [PATCH 40/70] add default to ammo --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 190056d7d00..32cc0fcda4e 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -8,7 +8,7 @@ * 0: Unit That Was Hit * 1: Damage done to each body part * 2: Type of the damage done - * 3: Projectile classname + * 3: Projectile classname (default: "") * * Return Value: * None @@ -24,7 +24,7 @@ #define ENGINE_DAMAGE_INDEX 0 -params ["_unit", "_allDamages", "_typeOfDamage", "_ammo"]; +params ["_unit", "_allDamages", "_typeOfDamage", ["_ammo", ""]]; TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); if !(EGVAR(medical,alternateArmorPenetration) && {_ammo isNotEqualTo ""}) exitWith {_this}; From 97cdaf9208a2fb2aefac1977bdeadcccf37293f7 Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Fri, 28 Jul 2023 06:39:07 +0300 Subject: [PATCH 41/70] cleanup --- addons/medical_damage/functions/fnc_getAmmoData.sqf | 2 +- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 1 - addons/medical_engine/functions/fnc_handleDamage.sqf | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index e6633a7777e..2847cdf92bc 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -10,7 +10,7 @@ * Base damage value, penetration factor, muzzle velocity * * Example: - * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoHit + * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoData * * Public: No */ diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 190056d7d00..564af8a99ab 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -23,7 +23,6 @@ #define SCALED_UNPROTECTED_VALUE 4 #define ENGINE_DAMAGE_INDEX 0 - params ["_unit", "_allDamages", "_typeOfDamage", "_ammo"]; TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index 3bfd60b8b07..1b3bb238a11 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -116,7 +116,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith { _allDamages sort false; _allDamages = _allDamages apply {[_x select 2, _x select 3, _x select 0]}; - + // Environmental damage sources all have empty ammo string // No explicit source given, we infer from differences between them if (_ammo isEqualTo "") then { From f1d5b80b039b7c42e0f94333c8140b79b44c89e7 Mon Sep 17 00:00:00 2001 From: LinkIsGrim Date: Fri, 28 Jul 2023 06:46:20 +0300 Subject: [PATCH 42/70] remove debug --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 30cd67dc5a9..2e73f71da4b 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -1,4 +1,3 @@ -#define DEBUG_MODE_FULL #include "script_component.hpp" /* * Author: LinkIsGrim From 300d5feac8dd1276a6ff32f120675c32f726cbeb Mon Sep 17 00:00:00 2001 From: LinkIsGrim Date: Mon, 7 Aug 2023 19:58:05 +0300 Subject: [PATCH 43/70] rework again (this time using armor class) --- .../fnc_woundsHandlerArmorPenetration.sqf | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 2e73f71da4b..1de444a7bf3 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -17,10 +17,20 @@ * * Public: No */ + // armor 2 with passthrough 0.8 #define SCALED_UNPROTECTED_VALUE 4 #define ENGINE_DAMAGE_INDEX 0 +// This gets close to vanilla values on FMJ ammo +#define DAMAGE_SCALING_FACTOR 7 + +// Based off #9216 armor values for vanilla vests +#define ARMOR_LEVEL_1_CAP 12 +#define ARMOR_LEVEL_2_CAP 14 +#define ARMOR_LEVEL_3_CAP 16 +#define ARMOR_LEVEL_4_CAP 20 + params ["_unit", "_allDamages", "_typeOfDamage", ["_ammo", ""]]; TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); @@ -29,30 +39,43 @@ if !(EGVAR(medical,alternateArmorPenetration) && {_ammo isNotEqualTo ""}) exitWi private _damageData = (_allDamages select 0); // selection specific _damageData params ["_engineDamage", "", "_realDamage"]; -private _armor = _realDamage/_engineDamage; +private _armor = (_realDamage/_engineDamage) - SCALED_UNPROTECTED_VALUE; // There's no need to calculate penetration if there is no armor to begin with, vanilla damage handling is good enough in this case -if (_armor <= SCALED_UNPROTECTED_VALUE) exitWith { +if (_armor <= 0) exitWith { _this // return }; +// Armor RHA equivalent, non-linear, ref \a3\Data_F\Penetration\armour_plate/thin/medium/heavy.bisurf +// Excedent armor over the cap gets added as a small bonus to thickness +private _armorThickness = 4; +switch (true) do { + case (_armor >= ARMOR_LEVEL_4_CAP): { + _armorThickness = 110 - (ARMOR_LEVEL_4_CAP - _armor); + }; + case (_armor >= ARMOR_LEVEL_3_CAP): { + _armorThickness = 80 - (ARMOR_LEVEL_3_CAP - _armor); + }; + case (_armor >= ARMOR_LEVEL_2_CAP): { + _armorThickness = 30 - (ARMOR_LEVEL_2_CAP - _armor); + }; + case (_armor >= ARMOR_LEVEL_1_CAP): { + _armorThickness = 12 - (ARMOR_LEVEL_1_CAP - _armor); + }; +}; + // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), -// _penFactor is ammo "caliber" * penetrability * typicalSpeed, for simplification we consider an armor value of 1 to be equivalent to 1mm of RHA (penetrability 0.015) -// Above is not actually the case but a good enough approximation +// _penFactor is ammo "caliber" * RHA penetrability * typicalSpeed, armor plates according to BI are just made of RHA with different thickness ([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; // Impact damage is hit * (impactSpeed / typicalSpeed): https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed -// Impact damage already takes into account hits at oblique angles, engine handles that +// Impact damage is already lowered by engine based on hit angle, so speed and therefore penetration are also naturally lowered private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; -// Energy transferred to target can be randomized a bit, final stage ballistics isn't a science -_hit = _hit * random [0.75, 1, 1.1]; - -private _penDepth = _penFactor * (_impactSpeed/_typicalSpeed)^2; +private _penDepth = _penFactor * (_impactSpeed/_typicalSpeed); // We want to base damage on the round's energy and armor penetration exclusively, so we'll use the config value to get damage // There's only so much damage a round can do, limited by its energy -// Divide by 10 to scale to 0-1 -private _finalDamage = (_hit * ((_penDepth/_armor) min 1)^2) / 10; +private _finalDamage = (_hit * (_penDepth/_armorThickness) min 1) / DAMAGE_SCALING_FACTOR; _damageData set [ENGINE_DAMAGE_INDEX, _finalDamage]; TRACE_3("Armor penetration handled, passing damage", _finalDamage, _damageData, _allDamages); From d227414cf17d965222e57056ddb8e315f57514b5 Mon Sep 17 00:00:00 2001 From: LinkIsGrim Date: Mon, 7 Aug 2023 20:09:17 +0300 Subject: [PATCH 44/70] change default thickness --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 1de444a7bf3..1ccfa9b93ad 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -47,7 +47,7 @@ if (_armor <= 0) exitWith { // Armor RHA equivalent, non-linear, ref \a3\Data_F\Penetration\armour_plate/thin/medium/heavy.bisurf // Excedent armor over the cap gets added as a small bonus to thickness -private _armorThickness = 4; +private _armorThickness = _armor; switch (true) do { case (_armor >= ARMOR_LEVEL_4_CAP): { _armorThickness = 110 - (ARMOR_LEVEL_4_CAP - _armor); From 7e1821a06eecec2fcc9e003d50734fa4dde2be49 Mon Sep 17 00:00:00 2001 From: LinkIsGrim Date: Mon, 7 Aug 2023 20:27:39 +0300 Subject: [PATCH 45/70] minor optimization, divide thickness by 2 --- .../functions/fnc_getAmmoData.sqf | 2 +- .../fnc_woundsHandlerArmorPenetration.sqf | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index 548a18b8cc2..ce2bdca00bc 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -26,7 +26,7 @@ if (isNil "_return") then { private _hit = getNumber (_ammoConfig >> "hit"); private _caliber = (getNumber (_ammoConfig >> "caliber")); private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); - private _penFactor = _caliber * ARMOR_PENETRABILITY * _typicalSpeed; + private _penFactor = _caliber * ARMOR_PENETRABILITY; _return = [_hit, _penFactor, _typicalSpeed]; GVAR(ammoCache) set [_ammo, _return]; }; diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 1ccfa9b93ad..6830dc67fa5 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -23,7 +23,7 @@ #define ENGINE_DAMAGE_INDEX 0 // This gets close to vanilla values on FMJ ammo -#define DAMAGE_SCALING_FACTOR 7 +#define DAMAGE_SCALING_FACTOR 10 // Based off #9216 armor values for vanilla vests #define ARMOR_LEVEL_1_CAP 12 @@ -46,32 +46,33 @@ if (_armor <= 0) exitWith { }; // Armor RHA equivalent, non-linear, ref \a3\Data_F\Penetration\armour_plate/thin/medium/heavy.bisurf +// Divided by 2 to keep inline with vanilla caliber values // Excedent armor over the cap gets added as a small bonus to thickness -private _armorThickness = _armor; +private _armorThickness = _armor/2; switch (true) do { case (_armor >= ARMOR_LEVEL_4_CAP): { - _armorThickness = 110 - (ARMOR_LEVEL_4_CAP - _armor); + _armorThickness = 55 - (ARMOR_LEVEL_4_CAP - _armor); }; case (_armor >= ARMOR_LEVEL_3_CAP): { - _armorThickness = 80 - (ARMOR_LEVEL_3_CAP - _armor); + _armorThickness = 40 - (ARMOR_LEVEL_3_CAP - _armor); }; case (_armor >= ARMOR_LEVEL_2_CAP): { - _armorThickness = 30 - (ARMOR_LEVEL_2_CAP - _armor); + _armorThickness = 15 - (ARMOR_LEVEL_2_CAP - _armor); }; case (_armor >= ARMOR_LEVEL_1_CAP): { - _armorThickness = 12 - (ARMOR_LEVEL_1_CAP - _armor); + _armorThickness = 6 - (ARMOR_LEVEL_1_CAP - _armor); }; }; // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), -// _penFactor is ammo "caliber" * RHA penetrability * typicalSpeed, armor plates according to BI are just made of RHA with different thickness +// _penFactor is ammo "caliber" * RHA penetrability, armor plates according to BI are just made of RHA with different thickness ([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; // Impact damage is hit * (impactSpeed / typicalSpeed): https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed // Impact damage is already lowered by engine based on hit angle, so speed and therefore penetration are also naturally lowered private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; -private _penDepth = _penFactor * (_impactSpeed/_typicalSpeed); +private _penDepth = _penFactor * _impactSpeed; // We want to base damage on the round's energy and armor penetration exclusively, so we'll use the config value to get damage // There's only so much damage a round can do, limited by its energy From 540e479f97930d3fdea31e7bf31aaddaa6a2a39f Mon Sep 17 00:00:00 2001 From: LinkIsGrim Date: Mon, 7 Aug 2023 20:50:07 +0300 Subject: [PATCH 46/70] fix missing bracket --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 6830dc67fa5..c873cbf46bc 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -76,7 +76,7 @@ private _penDepth = _penFactor * _impactSpeed; // We want to base damage on the round's energy and armor penetration exclusively, so we'll use the config value to get damage // There's only so much damage a round can do, limited by its energy -private _finalDamage = (_hit * (_penDepth/_armorThickness) min 1) / DAMAGE_SCALING_FACTOR; +private _finalDamage = (_hit * ((_penDepth/_armorThickness) min 1)) / DAMAGE_SCALING_FACTOR; _damageData set [ENGINE_DAMAGE_INDEX, _finalDamage]; TRACE_3("Armor penetration handled, passing damage", _finalDamage, _damageData, _allDamages); From 6a449ca36f7e0e47c020809c451650af2e344a8b Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Fri, 15 Nov 2024 21:29:35 -0300 Subject: [PATCH 47/70] fix script_component.hpp --- addons/medical_damage/functions/fnc_getAmmoData.sqf | 2 +- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index ce2bdca00bc..6863805af23 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -1,4 +1,4 @@ -#include "script_component.hpp" +#include "..\script_component.hpp" /* * Author: LinkIsGrim * Returns base damage value, penetration factor, and expected muzzle velocity of a given round, either from a cache or by reading the ammo config. diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index c873cbf46bc..8483302f2c4 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -1,4 +1,4 @@ -#include "script_component.hpp" +#include "..\script_component.hpp" /* * Author: LinkIsGrim * Custom wounds handler for armor penetration. Calculates damage based on round material penetration and unit armor From 5c099163f41cdf4d181f4e75f23550350000ad36 Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:16:16 +0100 Subject: [PATCH 48/70] Fix macro --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 8483302f2c4..9e64fe874fa 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -79,6 +79,6 @@ private _penDepth = _penFactor * _impactSpeed; private _finalDamage = (_hit * ((_penDepth/_armorThickness) min 1)) / DAMAGE_SCALING_FACTOR; _damageData set [ENGINE_DAMAGE_INDEX, _finalDamage]; -TRACE_3("Armor penetration handled, passing damage", _finalDamage, _damageData, _allDamages); +TRACE_3("Armor penetration handled, passing damage",_finalDamage,_damageData,_allDamages); _this // return From 5eba4c976e731ecacc1ff7bb5c4e38f49a20e870 Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:16:45 +0100 Subject: [PATCH 49/70] Use getOrDefaultCall --- addons/medical_damage/functions/fnc_getAmmoData.sqf | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index 6863805af23..07b927c3653 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -19,16 +19,12 @@ params ["_ammo"]; -private _return = GVAR(ammoCache) get _ammo; -if (isNil "_return") then { +GVAR(ammoCache) getOrDefaultCall [_ammo, { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; private _hit = getNumber (_ammoConfig >> "hit"); private _caliber = (getNumber (_ammoConfig >> "caliber")); private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); private _penFactor = _caliber * ARMOR_PENETRABILITY; - _return = [_hit, _penFactor, _typicalSpeed]; - GVAR(ammoCache) set [_ammo, _return]; -}; - -_return // return + [_hit, _penFactor, _typicalSpeed] // return +}, true] // return From d259ad1e34f133dc65a970d7553a6b335496ea8c Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:26:36 +0100 Subject: [PATCH 50/70] Update docs --- docs/wiki/framework/medical-framework.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/wiki/framework/medical-framework.md b/docs/wiki/framework/medical-framework.md index c0e94b5f9ee..e29f66781cb 100644 --- a/docs/wiki/framework/medical-framework.md +++ b/docs/wiki/framework/medical-framework.md @@ -224,6 +224,7 @@ Custom wound handlers should follow the same spec as the built-in handler: | 0 | Unit that was hit | Object | Required | | 1 | Array of damage dealt to each body part | Array | Required | | 2 | Type of damage | String | Required | +| 3 | Ammo | String | Optional | | **R** | Parameters to be passed to the next handler in the list, e.g. `_this` or a modified copy of it. Return `[]` to prevent further handling. | Array | Required | The damage elements are sorted in descending order according to how much damage was dealt to each body part _before armor was taken into account_, but the actual damage values are _after armor_. From d6cc11b12e2dbf06205b173ba4ba2c412c67a63a Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Sun, 17 Nov 2024 15:29:56 -0300 Subject: [PATCH 51/70] Update addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com> --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 9e64fe874fa..8b96507a07b 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -4,7 +4,7 @@ * Custom wounds handler for armor penetration. Calculates damage based on round material penetration and unit armor * * Arguments: - * 0: Unit That Was Hit + * 0: Unit that was hit * 1: Damage done to each body part * 2: Type of the damage done * 3: Projectile classname (default: "") From a7eca55a025b46ea0874d8709e7f585598ed39da Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Sun, 17 Nov 2024 15:30:02 -0300 Subject: [PATCH 52/70] Update addons/medical_damage/functions/fnc_getAmmoData.sqf --- addons/medical_damage/functions/fnc_getAmmoData.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index 07b927c3653..4d5af9b83cd 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -27,4 +27,4 @@ GVAR(ammoCache) getOrDefaultCall [_ammo, { private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); private _penFactor = _caliber * ARMOR_PENETRABILITY; [_hit, _penFactor, _typicalSpeed] // return -}, true] // return +}, true] From e29afd9572a0802b687bbe5ffce4a116d07efb62 Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Sun, 17 Nov 2024 15:39:13 -0300 Subject: [PATCH 53/70] readd ammo if it was dropped --- addons/medical_damage/functions/fnc_woundReceived.sqf | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/medical_damage/functions/fnc_woundReceived.sqf b/addons/medical_damage/functions/fnc_woundReceived.sqf index 97bc0d75973..c5d3b368f0f 100644 --- a/addons/medical_damage/functions/fnc_woundReceived.sqf +++ b/addons/medical_damage/functions/fnc_woundReceived.sqf @@ -28,6 +28,7 @@ if (_typeOfDamage in GVAR(damageTypeDetails)) then { private _damageData = [_unit, _allDamages, _typeOfDamage, _ammo]; { _damageData = _damageData call _x; + _damageData pushBackUnique _ammo; // readd ammo if it was dropped from return, BWC for not breaking #9217. pushBackUnique is faster than checking and appending manually TRACE_1("Wound handler returned",_damageData); // If invalid return, exit From 26b375963e32371d349d60491a70e15274967090 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:21:36 -0300 Subject: [PATCH 54/70] use scaling for stupid armor and nothing else, drop setting --- .../functions/fnc_getHitpointArmor.sqf | 12 ++-- .../functions/fnc_getItemArmor.sqf | 68 ++++++++----------- .../functions/fnc_handleDamage.sqf | 9 +-- addons/medical_engine/initSettings.inc.sqf | 9 --- addons/medical_engine/stringtable.xml | 24 ------- 5 files changed, 34 insertions(+), 88 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index d4fdd00fd36..d9ddf305fa8 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -32,23 +32,19 @@ private _gear = [ private _rags = _gear joinString "$"; private _var = format [QGVAR(armorCache$%1), _hitpoint]; -_unit getVariable [_var, ["", 0, 0]] params ["_prevRags", "_armor", "_armorScaled"]; +_unit getVariable [_var, ["", 0]] params ["_prevRags", "_armor"]; if (_rags != _prevRags) then { _armor = 0; - _armorScaled = 0; { - ([_x, _hitpoint] call FUNC(getItemArmor)) params ["_itemArmor", "_itemArmorScaled"]; - _armor = _armor + _itemArmor; - _armorScaled = _armorScaled + _itemArmorScaled; + _armor = _armor + [_x, _hitpoint] call FUNC(getItemArmor); } forEach _gear; // Armor should be at least 1 to prevent dividing by 0 _armor = _armor max 1; - _armorScaled = _armorScaled max 1; - _unit setVariable [_var, [_rags, _armor, _armorScaled]]; + _unit setVariable [_var, [_rags, _armor]]; }; -[_armor, _armorScaled] // return +_armor // return diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 01e6719a0fc..183486abbe9 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -18,53 +18,41 @@ params ["_item", "_hitpoint"]; -private _key = format ["%1$%2", _item, _hitpoint]; -private _return = GVAR(armorCache) get _key; - -if (isNil "_return") then { +GVAR(armorCache) getOrDefaultCall [_this joinString "$", { + TRACE_2("Cache miss",_item,_hitpoint); private _armor = 0; - private _armorScaled = 0; private _passThrough = 1; - TRACE_2("Cache miss",_item,_hitpoint); - if ("" in [_item, _hitpoint]) exitWith { - _return = [_armor, _armorScaled]; - GVAR(armorCache) set [_key, _return]; - }; - private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; - private _itemType = getNumber (_itemInfo >> "type"); - private _passThroughEffect = [1, 0.6] select (_itemType == TYPE_VEST); + if !("" in [_item, _hitpoint]) then { + private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo"; + private _itemType = getNumber (_itemInfo >> "type"); - if (_itemType == TYPE_UNIFORM) then { - private _unitCfg = configFile >> "CfgVehicles" >> getText (_itemInfo >> "uniformClass"); - if (_hitpoint == "#structural") then { - // TODO: I'm not sure if this should be multiplied by the base armor value or not - _armor = getNumber (_unitCfg >> "armorStructural"); + if (_itemType == TYPE_UNIFORM) then { + private _unitCfg = configFile >> "CfgVehicles" >> getText (_itemInfo >> "uniformClass"); + if (_hitpoint == "#structural") then { + // TODO: I'm not sure if this should be multiplied by the base armor value or not + _armor = getNumber (_unitCfg >> "armorStructural"); + } else { + private _entry = _unitCfg >> "HitPoints" >> _hitpoint; + _armor = getNumber (_unitCfg >> "armor") * (1 max getNumber (_entry >> "armor")); + _passThrough = 0.1 max getNumber (_entry >> "passThrough") min 1; // prevent dividing by 0 + }; } else { - private _entry = _unitCfg >> "HitPoints" >> _hitpoint; - _armor = getNumber (_unitCfg >> "armor") * (1 max getNumber (_entry >> "armor")); - _passThrough = 0.1 max getNumber (_entry >> "passThrough") min 1; // prevent dividing by 0 + private _condition = format ["getText (_x >> 'hitpointName') == '%1'", _hitpoint]; + private _entry = configProperties [_itemInfo >> "HitpointsProtectionInfo", _condition] param [0, configNull]; + if (!isNull _entry) then { + _armor = getNumber (_entry >> "armor"); + _passThrough = 0.1 max getNumber (_entry >> "passThrough") min 1; + }; }; - } else { - private _condition = format ["getText (_x >> 'hitpointName') == '%1'", _hitpoint]; - private _entry = configProperties [_itemInfo >> "HitpointsProtectionInfo", _condition] param [0, configNull]; - if (!isNull _entry) then { - _armor = getNumber (_entry >> "armor"); - _passThrough = 0.1 max getNumber (_entry >> "passThrough") min 1; - }; - }; - // Scale armor using passthrough to fix explosive-resistant armor (#9063) - // Skip scaling for uniforms and items that don't cover the hitpoint to prevent infinite armor - if (_armor > 0) then { - if (_itemType == TYPE_UNIFORM) then { - _armorScaled = _armor; - } else { - _armorScaled = (log (_armor / (_passThrough ^ _passThroughEffect))) * 10; + // Scale armor using passthrough to fix explosive-resistant & stupid armor (#9063) + // Skip scaling for uniforms and items that don't cover the hitpoint to prevent infinite armor + if (_itemType != TYPE_UNIFORM && (_armor > _armorLevelStep * 6)) then { + private _passThroughEffect = [1, 0.6] select (_itemType == TYPE_VEST); + _armor = (log (_armor / (_passThrough ^ _passThroughEffect))) * 10; }; }; - _return = [_armor, _armorScaled]; - GVAR(armorCache) set [_key, _return]; -}; -_return // return + _armor // return +}, true] diff --git a/addons/medical_engine/functions/fnc_handleDamage.sqf b/addons/medical_engine/functions/fnc_handleDamage.sqf index cf891f5e532..bbd081fce86 100644 --- a/addons/medical_engine/functions/fnc_handleDamage.sqf +++ b/addons/medical_engine/functions/fnc_handleDamage.sqf @@ -45,16 +45,11 @@ if (_context != 2 && {_context == 4 || _newDamage == 0}) exitWith { _oldDamage }; -// Get scaled armor value of hitpoint and calculate damage before armor +// Get 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 FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; +private _armor = [_unit, _hitpoint] call FUNC(getHitpointArmor); private _realDamage = _newDamage * _armor; -if (!_structuralDamage) then { - private _armorCoef = _armor/_armorScaled; - private _damageCoef = linearConversion [0, 1, GVAR(damagePassThroughEffect), 1, _armorCoef]; - _newDamage = _newDamage * _damageCoef; -}; TRACE_6("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_directHit,_context); // Drowning doesn't fire the EH for each hitpoint and never triggers _context=2 (LastHitPoint) diff --git a/addons/medical_engine/initSettings.inc.sqf b/addons/medical_engine/initSettings.inc.sqf index 062e2a08220..9fe80afcb00 100644 --- a/addons/medical_engine/initSettings.inc.sqf +++ b/addons/medical_engine/initSettings.inc.sqf @@ -6,12 +6,3 @@ true, true ] call CBA_fnc_addSetting; - -[ - QGVAR(damagePassThroughEffect), - "SLIDER", - [LSTRING(damagePassThroughEffect_displayName), LSTRING(damagePassThroughEffect_description)], - ELSTRING(medical,Category), - [0, 1, 1, 2, true], - true -] call CBA_fnc_addSetting; diff --git a/addons/medical_engine/stringtable.xml b/addons/medical_engine/stringtable.xml index 19856781967..bcf0e0b0057 100644 --- a/addons/medical_engine/stringtable.xml +++ b/addons/medical_engine/stringtable.xml @@ -27,29 +27,5 @@ 車両衝突ダメージを有効化 启用车辆碰撞损坏 - - Controls effect of armor 'passThrough' on final damage. Makes high armor values, like ones used in GL rigs, less effective.\nUse 0% for pre 3.16.0 armor behavior.\nOnly touch this if you know what you're doing! - Contrôle l'effet de la "pénétration" de l'armure sur les dégâts finaux. Rend les valeurs d'armures élevées, comme celles utilisées dans les gilets GL, moins efficaces.\nUtilisez 0% pour le comportement des armures des versions antérieures à 3.16.0.\nNe modifiez la valeur que si vous savez ce que vous faîtes ! - Controla el efecto de 'passThrough' de armadura en el daño final. Hace que los valores altos de armadura, como los usados en los chalecos GL, sean menos efectivos.\nUsar 0% para comportamiento de armadura en versiones anteriores a 3.16.0.\nSólo modifica esto si sabes lo que estás haciendo! - Determina l'effetto di danni sul corpo che 'trapassano' l'armatura. Rende alti valori di protezione, come quelli su corpetti GL, meno efficaci.\nUtilizza 0% per il comportamento prima di v3.16.0.\nModifica questo valore solo se sai cosa stai facendo! - Kontroluje wpływ "penetracji" pancerza na ostateczne obrażenia. Sprawia, że wysokie wartości pancerza, takie jak te używane w kamizelkach GL, są mniej skuteczne.\nUżyj 0% dla zachowania pancerza sprzed wersji 3.16.0.\nZmień wartość tylko jeśli wiesz co robisz! - Controla o efeito de penetração (passThrough) da blindagem no dano final. Torna valores de blindagem altos, como os usados em coletes GL, menos eficazes.\nUse 0% para o comportamento de blindagem anterior à versão 3.16.0.\nSó mexa nisso se souber o que está fazendo! - Контролирует эффект `passThrough` при нанесении конечного урона. Делает высокие значения брони, подобные тем, которые используются в GL rigs, менее эффективными.\nИспользуйте 0% для поведения брони до версии 3.16.0.n\Прикасайтесь к этому, только если знаете, что делаете! - Steuert den Effekt des „Durchschlagens“ von Panzerung auf den Gesamtschaden. Macht hohe Panzerungswerte, wie sie in GL-Westen verwendet werden, weniger effektiv.\nVerwende 0% für das Panzerungsverhalten vor 3.16.0.\nÄndere den Wert nur, wenn du weißt, was du tust! - 최종 데미지에 대한 방어구의 'PassThrough' 효과를 조정합니다. GL 리그에 사용되는 것과 같은 높은 방호값을 덜 효과적으로 만듭니다\n3.16.0 이전의 방어구 동작에는 0%를 사용하십시오.\n당신이 뭘 하고 있는지 알고 있는 경우에만 이걸 설정하세요! - ボディアーマーの'passThrough'値が最終的な身体ダメージに与える影響を調整します。擲弾兵リグで使用されるような高い装甲値では効果が低くなります。\n3.16.0以前の挙動にするには0%にしてください。\nこれが何かわからない場合は変更しないことをお勧めします。 - - - Armor PassThrough Effect - Effet de pénétration d'armure - Efecto de Atravesar Armadura - Fattore di Trapasso Armatura - Efekt penetracji pancerza - Efeito de Penetração de Blindagem - Эффект сквозного прохождения брони - Effekt des Panzerungsdurchschlags - 방어구 PassThrough 효과 - 装甲貫通効果 - From 54c9cb9da4bc0040de776c95dbb6fcb953c14961 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:23:28 -0300 Subject: [PATCH 55/70] simplify armor thickness --- .../fnc_woundsHandlerArmorPenetration.sqf | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 8b96507a07b..81ed12cec29 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -6,6 +6,9 @@ * Arguments: * 0: Unit that was hit * 1: Damage done to each body part + * 0: Engine damage + * 1: Body part + * 2: Real damage * 2: Type of the damage done * 3: Projectile classname (default: "") * @@ -18,51 +21,38 @@ * Public: No */ -// armor 2 with passthrough 0.8 -#define SCALED_UNPROTECTED_VALUE 4 -#define ENGINE_DAMAGE_INDEX 0 - // This gets close to vanilla values on FMJ ammo #define DAMAGE_SCALING_FACTOR 10 -// Based off #9216 armor values for vanilla vests -#define ARMOR_LEVEL_1_CAP 12 -#define ARMOR_LEVEL_2_CAP 14 -#define ARMOR_LEVEL_3_CAP 16 -#define ARMOR_LEVEL_4_CAP 20 - params ["_unit", "_allDamages", "_typeOfDamage", ["_ammo", ""]]; TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); -if !(EGVAR(medical,alternateArmorPenetration) && {_ammo isNotEqualTo ""}) exitWith {_this}; +if (!EGVAR(medical,alternateArmorPenetration) || _ammo isEqualTo "") exitWith {_this}; private _damageData = (_allDamages select 0); // selection specific -_damageData params ["_engineDamage", "", "_realDamage"]; +_damageData params ["_engineDamage", "_bodyPart", "_realDamage"]; + +private _armorLevelStep = [4, 2] select (_bodyPart == "head"); +private _armor = (_realDamage/_engineDamage) - _armorLevelStep; -private _armor = (_realDamage/_engineDamage) - SCALED_UNPROTECTED_VALUE; -// There's no need to calculate penetration if there is no armor to begin with, vanilla damage handling is good enough in this case +// There's no need to calculate penetration if there is no armor to begin with, base damage handling is good enough in this case if (_armor <= 0) exitWith { _this // return }; +// Cap at Armor Level V +private _armorLevel = 0 max (round ((_armor - _armorLevelStep) / _armorLevelStep)) min 5; + // Armor RHA equivalent, non-linear, ref \a3\Data_F\Penetration\armour_plate/thin/medium/heavy.bisurf // Divided by 2 to keep inline with vanilla caliber values -// Excedent armor over the cap gets added as a small bonus to thickness -private _armorThickness = _armor/2; -switch (true) do { - case (_armor >= ARMOR_LEVEL_4_CAP): { - _armorThickness = 55 - (ARMOR_LEVEL_4_CAP - _armor); - }; - case (_armor >= ARMOR_LEVEL_3_CAP): { - _armorThickness = 40 - (ARMOR_LEVEL_3_CAP - _armor); - }; - case (_armor >= ARMOR_LEVEL_2_CAP): { - _armorThickness = 15 - (ARMOR_LEVEL_2_CAP - _armor); - }; - case (_armor >= ARMOR_LEVEL_1_CAP): { - _armorThickness = 6 - (ARMOR_LEVEL_1_CAP - _armor); - }; -}; +// Excedent armor over the cap gets added as a small bonus to thickness, up to the armor level V cap +private _armorThickness = (_armor - (_armorLevel + 1) * _armorLevelStep) + ([ + 0, + 6, + 15, + 40, + 55 +] select _armorLevel); // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), // _penFactor is ammo "caliber" * RHA penetrability, armor plates according to BI are just made of RHA with different thickness @@ -74,10 +64,9 @@ private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; private _penDepth = _penFactor * _impactSpeed; -// We want to base damage on the round's energy and armor penetration exclusively, so we'll use the config value to get damage -// There's only so much damage a round can do, limited by its energy +// Max damage is the config value, go down from there based on armor penetration private _finalDamage = (_hit * ((_penDepth/_armorThickness) min 1)) / DAMAGE_SCALING_FACTOR; -_damageData set [ENGINE_DAMAGE_INDEX, _finalDamage]; +_damageData set [0, _finalDamage]; TRACE_3("Armor penetration handled, passing damage",_finalDamage,_damageData,_allDamages); From 18dc75d8ffc299ef8efd7c1eff1691737b6a9928 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:24:09 -0300 Subject: [PATCH 56/70] no fun if it works the first time --- addons/medical_engine/functions/fnc_getHitpointArmor.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index d9ddf305fa8..db9370f4270 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -38,7 +38,7 @@ if (_rags != _prevRags) then { _armor = 0; { - _armor = _armor + [_x, _hitpoint] call FUNC(getItemArmor); + _armor = _armor + ([_x, _hitpoint] call FUNC(getItemArmor)); } forEach _gear; // Armor should be at least 1 to prevent dividing by 0 From 20094319fa1d2955920f4b9456aaa9cfd19312fa Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:50:25 -0300 Subject: [PATCH 57/70] fix undefined variable --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 183486abbe9..5125e2b0692 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -48,6 +48,7 @@ GVAR(armorCache) getOrDefaultCall [_this joinString "$", { // Scale armor using passthrough to fix explosive-resistant & stupid armor (#9063) // Skip scaling for uniforms and items that don't cover the hitpoint to prevent infinite armor + private _armorLevelStep = [4, 2] select (_itemType == TYPE_HEADGEAR); if (_itemType != TYPE_UNIFORM && (_armor > _armorLevelStep * 6)) then { private _passThroughEffect = [1, 0.6] select (_itemType == TYPE_VEST); _armor = (log (_armor / (_passThrough ^ _passThroughEffect))) * 10; From ef0cdf78ab16e87d8cbd2d6fafff9b88a28807d6 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Wed, 18 Dec 2024 23:29:20 -0300 Subject: [PATCH 58/70] fix logic --- .../fnc_woundsHandlerArmorPenetration.sqf | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 81ed12cec29..4e49b21ab16 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -32,27 +32,30 @@ if (!EGVAR(medical,alternateArmorPenetration) || _ammo isEqualTo "") exitWith {_ private _damageData = (_allDamages select 0); // selection specific _damageData params ["_engineDamage", "_bodyPart", "_realDamage"]; -private _armorLevelStep = [4, 2] select (_bodyPart == "head"); -private _armor = (_realDamage/_engineDamage) - _armorLevelStep; +private _armorLevelStep = [2, 4] select (_bodyPart == "body"); +private _armor = (_realDamage/_engineDamage) - 2; // remove base armor // There's no need to calculate penetration if there is no armor to begin with, base damage handling is good enough in this case -if (_armor <= 0) exitWith { +if (_armor <= _armorLevelStep) exitWith { + TRACE_3("skipping no armor",_armor,_bodyPart,_armorLevelStep); _this // return }; // Cap at Armor Level V -private _armorLevel = 0 max (round ((_armor - _armorLevelStep) / _armorLevelStep)) min 5; +// Jumping from no armor to armor level 1 is 2 steps +private _armorLevel = 0 max (floor ((_armor - (_armorLevelStep * 2)) / _armorLevelStep)) min 4; +TRACE_3("gotArmorLevel",_armorLevel,_armor,_armorLevelStep); // Armor RHA equivalent, non-linear, ref \a3\Data_F\Penetration\armour_plate/thin/medium/heavy.bisurf // Divided by 2 to keep inline with vanilla caliber values -// Excedent armor over the cap gets added as a small bonus to thickness, up to the armor level V cap -private _armorThickness = (_armor - (_armorLevel + 1) * _armorLevelStep) + ([ - 0, +private _armorThickness = [ 6, 15, + 21, 40, 55 -] select _armorLevel); +] select _armorLevel; +TRACE_1("gotArmorThickness",_armorThickness); // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), // _penFactor is ammo "caliber" * RHA penetrability, armor plates according to BI are just made of RHA with different thickness From 3fe5924049522bcb51079d4f09fe447006374d1f Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:21:17 -0300 Subject: [PATCH 59/70] defer woundReceived changes to another PR --- addons/medical_damage/functions/fnc_woundReceived.sqf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/medical_damage/functions/fnc_woundReceived.sqf b/addons/medical_damage/functions/fnc_woundReceived.sqf index c5d3b368f0f..74ee8321d0c 100644 --- a/addons/medical_damage/functions/fnc_woundReceived.sqf +++ b/addons/medical_damage/functions/fnc_woundReceived.sqf @@ -25,10 +25,9 @@ private _typeOfDamage = _ammo call FUNC(getTypeOfDamage); if (_typeOfDamage in GVAR(damageTypeDetails)) then { (GVAR(damageTypeDetails) get _typeOfDamage) params ["", "", "_woundHandlers"]; - private _damageData = [_unit, _allDamages, _typeOfDamage, _ammo]; + private _damageData = [_unit, _allDamages, _typeOfDamage]; { _damageData = _damageData call _x; - _damageData pushBackUnique _ammo; // readd ammo if it was dropped from return, BWC for not breaking #9217. pushBackUnique is faster than checking and appending manually TRACE_1("Wound handler returned",_damageData); // If invalid return, exit From d5662380778f235a8de7f2e6ba329882ab2c85d4 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:23:18 -0300 Subject: [PATCH 60/70] setting name/description --- addons/medical_damage/stringtable.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_damage/stringtable.xml b/addons/medical_damage/stringtable.xml index b613bece619..da2a5a4bba0 100644 --- a/addons/medical_damage/stringtable.xml +++ b/addons/medical_damage/stringtable.xml @@ -872,10 +872,10 @@ Utiliser les dommages aux membres - Use Alternate Armor Penetration + Use Custom Armor Penetration - Controls whether ammo material penetration ("caliber") is taken into account for damage handling. + Controls whether ammo material penetration is taken into account for damage handling. From 261c0a64b7017ca52a5faa0ecb417805859133c5 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:25:18 -0300 Subject: [PATCH 61/70] ammo fixes --- .../functions/fnc_getAmmoData.sqf | 20 ++++++++++------ .../fnc_woundsHandlerArmorPenetration.sqf | 24 ++++++++++++------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index 4d5af9b83cd..8a245cc3136 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -10,21 +10,27 @@ * Base damage value, penetration factor, muzzle velocity * * Example: - * ["B_556x45_Ball"] call ace_medical_engine_fnc_getAmmoData + * "B_556x45_Ball" call ace_medical_engine_fnc_getAmmoData * * Public: No */ + // Baseline penetrability used for armor penetration calculation, see (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber) #define ARMOR_PENETRABILITY 0.015 params ["_ammo"]; -GVAR(ammoCache) getOrDefaultCall [_ammo, { +GVAR(ammoCache) getOrDefaultCall [toLowerANSI _ammo, { TRACE_1("Cache miss",_ammo); private _ammoConfig = configFile >> "CfgAmmo" >> _ammo; - private _hit = getNumber (_ammoConfig >> "hit"); - private _caliber = (getNumber (_ammoConfig >> "caliber")); - private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); - private _penFactor = _caliber * ARMOR_PENETRABILITY; - [_hit, _penFactor, _typicalSpeed] // return + + if (isNull _ammoConfig) then { + [0, 0, 0] // return + } else { + private _hit = getNumber (_ammoConfig >> "hit"); + private _caliber = (getNumber (_ammoConfig >> "caliber")); + private _typicalSpeed = getNumber (_ammoConfig >> "typicalSpeed"); + private _penFactor = _caliber * ARMOR_PENETRABILITY; + [_hit, _penFactor, _typicalSpeed] // return + }; }, true] diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 4e49b21ab16..5ff22b05c0b 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -10,7 +10,7 @@ * 1: Body part * 2: Real damage * 2: Type of the damage done - * 3: Projectile classname (default: "") + * 3: Ammo * * Return Value: * None @@ -24,14 +24,23 @@ // This gets close to vanilla values on FMJ ammo #define DAMAGE_SCALING_FACTOR 10 -params ["_unit", "_allDamages", "_typeOfDamage", ["_ammo", ""]]; -TRACE_3("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage); +params ["_unit", "_allDamages", "_typeOfDamage", "_ammo"]; +TRACE_4("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage,_ammo); -if (!EGVAR(medical,alternateArmorPenetration) || _ammo isEqualTo "") exitWith {_this}; +if (!EGVAR(medical,alternateArmorPenetration)) exitWith {_this}; private _damageData = (_allDamages select 0); // selection specific _damageData params ["_engineDamage", "_bodyPart", "_realDamage"]; +private _ammoData = _ammo call FUNC(getAmmoData); + +// See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), +// _penFactor is ammo "caliber" * RHA penetrability, armor plates according to BI are just made of RHAe material +_ammoData params ["_hit", "_penFactor", "_typicalSpeed"]; + +// Skip bad ammo +if (_hit <= 0) exitWith {_this}; + private _armorLevelStep = [2, 4] select (_bodyPart == "body"); private _armor = (_realDamage/_engineDamage) - 2; // remove base armor @@ -57,13 +66,10 @@ private _armorThickness = [ ] select _armorLevel; TRACE_1("gotArmorThickness",_armorThickness); -// See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), -// _penFactor is ammo "caliber" * RHA penetrability, armor plates according to BI are just made of RHA with different thickness -([_ammo] call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; - // Impact damage is hit * (impactSpeed / typicalSpeed): https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#typicalSpeed // Impact damage is already lowered by engine based on hit angle, so speed and therefore penetration are also naturally lowered -private _impactSpeed = (_realDamage/_hit) * _typicalSpeed; +// Assume typicalSpeed < 1 means no damage dropoff +private _impactSpeed = (_realDamage/_hit) * (_typicalSpeed max 1); private _penDepth = _penFactor * _impactSpeed; From 7317266b91274d73936b31312d7ca54ad09dbf8b Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:25:51 -0300 Subject: [PATCH 62/70] drop doc change --- docs/wiki/framework/medical-framework.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/wiki/framework/medical-framework.md b/docs/wiki/framework/medical-framework.md index e29f66781cb..c0e94b5f9ee 100644 --- a/docs/wiki/framework/medical-framework.md +++ b/docs/wiki/framework/medical-framework.md @@ -224,7 +224,6 @@ Custom wound handlers should follow the same spec as the built-in handler: | 0 | Unit that was hit | Object | Required | | 1 | Array of damage dealt to each body part | Array | Required | | 2 | Type of damage | String | Required | -| 3 | Ammo | String | Optional | | **R** | Parameters to be passed to the next handler in the list, e.g. `_this` or a modified copy of it. Return `[]` to prevent further handling. | Array | Required | The damage elements are sorted in descending order according to how much damage was dealt to each body part _before armor was taken into account_, but the actual damage values are _after armor_. From a503f54a50d956e6d3ec1e88d217893bb446a1cc Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:26:26 -0300 Subject: [PATCH 63/70] whitespace --- addons/medical_damage/functions/fnc_woundReceived.sqf | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/medical_damage/functions/fnc_woundReceived.sqf b/addons/medical_damage/functions/fnc_woundReceived.sqf index 74ee8321d0c..c31cf5b3789 100644 --- a/addons/medical_damage/functions/fnc_woundReceived.sqf +++ b/addons/medical_damage/functions/fnc_woundReceived.sqf @@ -26,6 +26,7 @@ if (_typeOfDamage in GVAR(damageTypeDetails)) then { (GVAR(damageTypeDetails) get _typeOfDamage) params ["", "", "_woundHandlers"]; private _damageData = [_unit, _allDamages, _typeOfDamage]; + { _damageData = _damageData call _x; TRACE_1("Wound handler returned",_damageData); From 833578e64bc7a82f7be09fda070de1901d43db64 Mon Sep 17 00:00:00 2001 From: LinkIsGrim <69561145+LinkIsGrim@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:09:15 -0300 Subject: [PATCH 64/70] round instead of floor to prevent floating point miscalc --- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 5ff22b05c0b..9f0c9da07a8 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -52,7 +52,7 @@ if (_armor <= _armorLevelStep) exitWith { // Cap at Armor Level V // Jumping from no armor to armor level 1 is 2 steps -private _armorLevel = 0 max (floor ((_armor - (_armorLevelStep * 2)) / _armorLevelStep)) min 4; +private _armorLevel = 0 max (round ((_armor - (_armorLevelStep * 2)) / _armorLevelStep)) min 4; TRACE_3("gotArmorLevel",_armorLevel,_armor,_armorLevelStep); // Armor RHA equivalent, non-linear, ref \a3\Data_F\Penetration\armour_plate/thin/medium/heavy.bisurf From 298237b91fb567a1baf0528cdd45792c244c70fe Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:31:18 +0100 Subject: [PATCH 65/70] Update stringtable.xml --- addons/medical_engine/stringtable.xml | 52 +++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/addons/medical_engine/stringtable.xml b/addons/medical_engine/stringtable.xml index 7062e6cd4b2..0a459ba5ead 100644 --- a/addons/medical_engine/stringtable.xml +++ b/addons/medical_engine/stringtable.xml @@ -1,32 +1,6 @@ - - Controls effect of armor 'passThrough' on final damage. Makes high armor values, like ones used in GL rigs, less effective.\nUse 0% for pre 3.16.0 armor behavior.\nOnly touch this if you know what you're doing! - Contrôle l'effet de la "pénétration" de l'armure sur les dégâts finaux. Rend les valeurs d'armures élevées, comme celles utilisées dans les gilets GL, moins efficaces.\nUtilisez 0% pour le comportement des armures des versions antérieures à 3.16.0.\nNe modifiez la valeur que si vous savez ce que vous faîtes ! - Controla el efecto de 'passThrough' de armadura en el daño final. Hace que los valores altos de armadura, como los usados en los chalecos GL, sean menos efectivos.\nUsar 0% para comportamiento de armadura en versiones anteriores a 3.16.0.\nSólo modifica esto si sabes lo que estás haciendo! - Determina l'effetto di danni sul corpo che 'trapassano' l'armatura. Rende alti valori di protezione, come quelli su corpetti GL, meno efficaci.\nUtilizza 0% per il comportamento prima di v3.16.0.\nModifica questo valore solo se sai cosa stai facendo! - Kontroluje wpływ "penetracji" pancerza na ostateczne obrażenia. Sprawia, że wysokie wartości pancerza, takie jak te używane w kamizelkach GL, są mniej skuteczne.\nUżyj 0% dla zachowania pancerza sprzed wersji 3.16.0.\nZmień wartość tylko jeśli wiesz co robisz! - Controla o efeito de penetração (passThrough) da blindagem no dano final. Torna valores de blindagem altos, como os usados em coletes GL, menos eficazes.\nUse 0% para o comportamento de blindagem anterior à versão 3.16.0.\nSó mexa nisso se souber o que está fazendo! - Контролирует эффект `passThrough` при нанесении конечного урона. Делает высокие значения брони, подобные тем, которые используются в GL rigs, менее эффективными.\nИспользуйте 0% для поведения брони до версии 3.16.0.n\Прикасайтесь к этому, только если знаете, что делаете! - Steuert den Effekt des „Durchschlagens“ von Panzerung auf den Gesamtschaden. Macht hohe Panzerungswerte, wie sie in GL-Westen verwendet werden, weniger effektiv.\nVerwende 0% für das Panzerungsverhalten vor 3.16.0.\nÄndere den Wert nur, wenn du weißt, was du tust! - 최종 데미지에 대한 방어구의 'PassThrough' 효과를 조정합니다. GL 리그에 사용되는 것과 같은 높은 방호값을 덜 효과적으로 만듭니다\n3.16.0 이전의 방어구 동작에는 0%를 사용하십시오.\n당신이 뭘 하고 있는지 알고 있는 경우에만 이걸 설정하세요! - ボディアーマーの'passThrough'値が最終的な身体ダメージに与える影響を調整します。擲弾兵リグで使用されるような高い装甲値では効果が低くなります。\n3.16.0以前の挙動にするには0%にしてください。\nこれが何かわからない場合は変更しないことをお勧めします。 - 控制Config中PassThrough对最终伤害产生的影响. 使高装甲值护甲(像是原版榴弹胸挂)减伤效果降低\n使用0%来获得3.16.0之前的护甲效果\n除非你知道在干什么, 否则不要更改这个选项! - - - Armor PassThrough Effect - Effet de pénétration d'armure - Efecto de Atravesar Armadura - Fattore di Trapasso Armatura - Efekt penetracji pancerza - Efeito de Penetração de Blindagem - Эффект сквозного прохождения брони - Effekt des Panzerungsdurchschlags - 방어구 PassThrough 효과 - 装甲貫通効果 - 护甲PassThrough效果更改 - Controls whether crew receives damage from vehicle collisions. Définit si les passagers à bord des véhicules peuvent être blessés en cas d'accident. @@ -53,5 +27,31 @@ 車両衝突ダメージを有効化 启用车辆碰撞损坏 + + Controls effect of armor 'passThrough' on final damage. Makes high armor values, like ones used in GL rigs, less effective.\nUse 0% for pre 3.16.0 armor behavior.\nOnly touch this if you know what you're doing! + Contrôle l'effet de la "pénétration" de l'armure sur les dégâts finaux. Rend les valeurs d'armures élevées, comme celles utilisées dans les gilets GL, moins efficaces.\nUtilisez 0% pour le comportement des armures des versions antérieures à 3.16.0.\nNe modifiez la valeur que si vous savez ce que vous faîtes ! + Controla el efecto de 'passThrough' de armadura en el daño final. Hace que los valores altos de armadura, como los usados en los chalecos GL, sean menos efectivos.\nUsar 0% para comportamiento de armadura en versiones anteriores a 3.16.0.\nSólo modifica esto si sabes lo que estás haciendo! + Determina l'effetto di danni sul corpo che 'trapassano' l'armatura. Rende alti valori di protezione, come quelli su corpetti GL, meno efficaci.\nUtilizza 0% per il comportamento prima di v3.16.0.\nModifica questo valore solo se sai cosa stai facendo! + Kontroluje wpływ "penetracji" pancerza na ostateczne obrażenia. Sprawia, że wysokie wartości pancerza, takie jak te używane w kamizelkach GL, są mniej skuteczne.\nUżyj 0% dla zachowania pancerza sprzed wersji 3.16.0.\nZmień wartość tylko jeśli wiesz co robisz! + Controla o efeito de penetração (passThrough) da blindagem no dano final. Torna valores de blindagem altos, como os usados em coletes GL, menos eficazes.\nUse 0% para o comportamento de blindagem anterior à versão 3.16.0.\nSó mexa nisso se souber o que está fazendo! + Контролирует эффект `passThrough` при нанесении конечного урона. Делает высокие значения брони, подобные тем, которые используются в GL rigs, менее эффективными.\nИспользуйте 0% для поведения брони до версии 3.16.0.n\Прикасайтесь к этому, только если знаете, что делаете! + Steuert den Effekt des „Durchschlagens“ von Panzerung auf den Gesamtschaden. Macht hohe Panzerungswerte, wie sie in GL-Westen verwendet werden, weniger effektiv.\nVerwende 0% für das Panzerungsverhalten vor 3.16.0.\nÄndere den Wert nur, wenn du weißt, was du tust! + 최종 데미지에 대한 방어구의 'PassThrough' 효과를 조정합니다. GL 리그에 사용되는 것과 같은 높은 방호값을 덜 효과적으로 만듭니다\n3.16.0 이전의 방어구 동작에는 0%를 사용하십시오.\n당신이 뭘 하고 있는지 알고 있는 경우에만 이걸 설정하세요! + ボディアーマーの'passThrough'値が最終的な身体ダメージに与える影響を調整します。擲弾兵リグで使用されるような高い装甲値では効果が低くなります。\n3.16.0以前の挙動にするには0%にしてください。\nこれが何かわからない場合は変更しないことをお勧めします。 + 控制Config中PassThrough对最终伤害产生的影响. 使高装甲值护甲(像是原版榴弹胸挂)减伤效果降低\n使用0%来获得3.16.0之前的护甲效果\n除非你知道在干什么, 否则不要更改这个选项! + + + Armor PassThrough Effect + Effet de pénétration d'armure + Efecto de Atravesar Armadura + Fattore di Trapasso Armatura + Efekt penetracji pancerza + Efeito de Penetração de Blindagem + Эффект сквозного прохождения брони + Effekt des Panzerungsdurchschlags + 방어구 PassThrough 효과 + 装甲貫通効果 + 护甲PassThrough效果更改 + From 2f45079feac61c0cd90b4bf6c4c9f79324287a26 Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:32:02 +0100 Subject: [PATCH 66/70] Update stringtable.xml --- addons/medical_engine/stringtable.xml | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/addons/medical_engine/stringtable.xml b/addons/medical_engine/stringtable.xml index 0a459ba5ead..bcf0e0b0057 100644 --- a/addons/medical_engine/stringtable.xml +++ b/addons/medical_engine/stringtable.xml @@ -27,31 +27,5 @@ 車両衝突ダメージを有効化 启用车辆碰撞损坏 - - Controls effect of armor 'passThrough' on final damage. Makes high armor values, like ones used in GL rigs, less effective.\nUse 0% for pre 3.16.0 armor behavior.\nOnly touch this if you know what you're doing! - Contrôle l'effet de la "pénétration" de l'armure sur les dégâts finaux. Rend les valeurs d'armures élevées, comme celles utilisées dans les gilets GL, moins efficaces.\nUtilisez 0% pour le comportement des armures des versions antérieures à 3.16.0.\nNe modifiez la valeur que si vous savez ce que vous faîtes ! - Controla el efecto de 'passThrough' de armadura en el daño final. Hace que los valores altos de armadura, como los usados en los chalecos GL, sean menos efectivos.\nUsar 0% para comportamiento de armadura en versiones anteriores a 3.16.0.\nSólo modifica esto si sabes lo que estás haciendo! - Determina l'effetto di danni sul corpo che 'trapassano' l'armatura. Rende alti valori di protezione, come quelli su corpetti GL, meno efficaci.\nUtilizza 0% per il comportamento prima di v3.16.0.\nModifica questo valore solo se sai cosa stai facendo! - Kontroluje wpływ "penetracji" pancerza na ostateczne obrażenia. Sprawia, że wysokie wartości pancerza, takie jak te używane w kamizelkach GL, są mniej skuteczne.\nUżyj 0% dla zachowania pancerza sprzed wersji 3.16.0.\nZmień wartość tylko jeśli wiesz co robisz! - Controla o efeito de penetração (passThrough) da blindagem no dano final. Torna valores de blindagem altos, como os usados em coletes GL, menos eficazes.\nUse 0% para o comportamento de blindagem anterior à versão 3.16.0.\nSó mexa nisso se souber o que está fazendo! - Контролирует эффект `passThrough` при нанесении конечного урона. Делает высокие значения брони, подобные тем, которые используются в GL rigs, менее эффективными.\nИспользуйте 0% для поведения брони до версии 3.16.0.n\Прикасайтесь к этому, только если знаете, что делаете! - Steuert den Effekt des „Durchschlagens“ von Panzerung auf den Gesamtschaden. Macht hohe Panzerungswerte, wie sie in GL-Westen verwendet werden, weniger effektiv.\nVerwende 0% für das Panzerungsverhalten vor 3.16.0.\nÄndere den Wert nur, wenn du weißt, was du tust! - 최종 데미지에 대한 방어구의 'PassThrough' 효과를 조정합니다. GL 리그에 사용되는 것과 같은 높은 방호값을 덜 효과적으로 만듭니다\n3.16.0 이전의 방어구 동작에는 0%를 사용하십시오.\n당신이 뭘 하고 있는지 알고 있는 경우에만 이걸 설정하세요! - ボディアーマーの'passThrough'値が最終的な身体ダメージに与える影響を調整します。擲弾兵リグで使用されるような高い装甲値では効果が低くなります。\n3.16.0以前の挙動にするには0%にしてください。\nこれが何かわからない場合は変更しないことをお勧めします。 - 控制Config中PassThrough对最终伤害产生的影响. 使高装甲值护甲(像是原版榴弹胸挂)减伤效果降低\n使用0%来获得3.16.0之前的护甲效果\n除非你知道在干什么, 否则不要更改这个选项! - - - Armor PassThrough Effect - Effet de pénétration d'armure - Efecto de Atravesar Armadura - Fattore di Trapasso Armatura - Efekt penetracji pancerza - Efeito de Penetração de Blindagem - Эффект сквозного прохождения брони - Effekt des Panzerungsdurchschlags - 방어구 PassThrough 효과 - 装甲貫通効果 - 护甲PassThrough效果更改 - From a8338653ba0b1412bcdd833bc8e419da9e96bcb7 Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:54:39 -0300 Subject: [PATCH 67/70] Update addons/medical_damage/initSettings.inc.sqf Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com> --- addons/medical_damage/initSettings.inc.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_damage/initSettings.inc.sqf b/addons/medical_damage/initSettings.inc.sqf index 8e0f02a651d..5d5e3293cb0 100644 --- a/addons/medical_damage/initSettings.inc.sqf +++ b/addons/medical_damage/initSettings.inc.sqf @@ -4,7 +4,7 @@ [LSTRING(AlternateArmorPenetration_DisplayName), LSTRING(AlternateArmorPenetration_Description)], ELSTRING(medical,Category), true, - true + 1 ] call CBA_fnc_addSetting; [ From 505f524d875030c868373dbcc94973c3e16d257b Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:02:27 -0300 Subject: [PATCH 68/70] Update fnc_getItemArmor.sqf --- addons/medical_engine/functions/fnc_getItemArmor.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/medical_engine/functions/fnc_getItemArmor.sqf b/addons/medical_engine/functions/fnc_getItemArmor.sqf index 5125e2b0692..f0128e2bdf8 100644 --- a/addons/medical_engine/functions/fnc_getItemArmor.sqf +++ b/addons/medical_engine/functions/fnc_getItemArmor.sqf @@ -1,14 +1,14 @@ #include "..\script_component.hpp" /* * Author: Pterolatypus, LinkIsGrim - * Returns the regular and scaled armor values the given item provides to a particular hitpoint, either from a cache or by reading the item config. + * Returns the armor value provided by an item to a hitpoint. Armor may be scaled back if over the cap. * * Arguments: * 0: Item Class * 1: Hitpoint * * Return Value: - * Regular and scaled item armor for the given hitpoint + * Item armor for the given hitpoint, may be scaled. * * Example: * ["V_PlateCarrier_rgr", "HitChest"] call ace_medical_engine_fnc_getItemArmor From b510e95d10028482d396a708c1bd57328d403377 Mon Sep 17 00:00:00 2001 From: Grim <69561145+LinkIsGrim@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:05:36 -0300 Subject: [PATCH 69/70] Update fnc_getHitpointArmor.sqf --- addons/medical_engine/functions/fnc_getHitpointArmor.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf index db9370f4270..6fffe94ae8e 100644 --- a/addons/medical_engine/functions/fnc_getHitpointArmor.sqf +++ b/addons/medical_engine/functions/fnc_getHitpointArmor.sqf @@ -8,7 +8,7 @@ * 1: Hitpoint * * Return Value: - * Total armor and scaled armor for the given hitpoint + * Armor for the given hitpoint, may be scaled. * * Example: * [player, "HitChest"] call ace_medical_engine_fnc_getHitpointArmor From e6f5abae30e03d66f6d598d6f0b0fc651a4a07d7 Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:15:41 +0100 Subject: [PATCH 70/70] Minor cleanup --- addons/medical_damage/functions/fnc_getAmmoData.sqf | 2 +- .../functions/fnc_woundsHandlerArmorPenetration.sqf | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/addons/medical_damage/functions/fnc_getAmmoData.sqf b/addons/medical_damage/functions/fnc_getAmmoData.sqf index 8a245cc3136..53b12e152f2 100644 --- a/addons/medical_damage/functions/fnc_getAmmoData.sqf +++ b/addons/medical_damage/functions/fnc_getAmmoData.sqf @@ -10,7 +10,7 @@ * Base damage value, penetration factor, muzzle velocity * * Example: - * "B_556x45_Ball" call ace_medical_engine_fnc_getAmmoData + * "B_556x45_Ball" call ace_medical_damage_fnc_getAmmoData * * Public: No */ diff --git a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf index 9f0c9da07a8..5ca70c9761a 100644 --- a/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf +++ b/addons/medical_damage/functions/fnc_woundsHandlerArmorPenetration.sqf @@ -29,18 +29,16 @@ TRACE_4("woundsHandlerArmorPenetration",_unit,_allDamages,_typeOfDamage,_ammo); if (!EGVAR(medical,alternateArmorPenetration)) exitWith {_this}; -private _damageData = (_allDamages select 0); // selection specific -_damageData params ["_engineDamage", "_bodyPart", "_realDamage"]; - -private _ammoData = _ammo call FUNC(getAmmoData); - // See (https://community.bistudio.com/wiki/CfgAmmo_Config_Reference#caliber), // _penFactor is ammo "caliber" * RHA penetrability, armor plates according to BI are just made of RHAe material -_ammoData params ["_hit", "_penFactor", "_typicalSpeed"]; +(_ammo call FUNC(getAmmoData)) params ["_hit", "_penFactor", "_typicalSpeed"]; // Skip bad ammo if (_hit <= 0) exitWith {_this}; +private _damageData = _allDamages select 0; // selection specific +_damageData params ["_engineDamage", "_bodyPart", "_realDamage"]; + private _armorLevelStep = [2, 4] select (_bodyPart == "body"); private _armor = (_realDamage/_engineDamage) - 2; // remove base armor