mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Medical Engine - Apply damage to the correct hitpoint (#7415)
* HandleDamage uses armour values to determine which hitpoint was damaged * Tidied up comments * Newlines * Tabs? In MY code?! * Added uniform caching and option to force disable caching * Review suggestions * Review suggestions/code style * Spelling and select * Removed unnecessary validity check * Apply suggestions from code review Co-Authored-By: commy2 <commy-2@gmx.de> * Tweaks and optimisations, removed _noCache Also fixed cache nil vs empty * Different approach with fewer loops Lookup is now done per-hitpoint and default values cached fnc_getItemArmor made a helper function as it's now only a few lines * Tabs & newlines * Moved uniform logic inside helper function * Optimisations * Tweaks & optimisations, improved formatting * Ignore explosionShielding * Moved getArmor back to separate func, add per-unit caching * Formatting * Review suggestions Co-authored-by: commy2 <commy-2@gmx.de>
This commit is contained in:
parent
f069ac4c92
commit
c949a07c83
@ -4,3 +4,5 @@ PREP(updateBodyPartVisuals);
|
||||
PREP(updateDamageEffects);
|
||||
PREP(setStructuralDamage);
|
||||
PREP(setUnconsciousAnim);
|
||||
PREP(getHitpointArmor);
|
||||
PREP(getItemArmor);
|
||||
|
@ -28,6 +28,9 @@ if (isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_K) || isNil QUOTE(FATAL_SUM_DAMAGE_WEIB
|
||||
FATAL_SUM_DAMAGE_WEIBULL_L = _x1 / _b1^(1/FATAL_SUM_DAMAGE_WEIBULL_K);
|
||||
};
|
||||
|
||||
// Cache for armor values of equipped items (vests etc)
|
||||
GVAR(armorCache) = false call CBA_fnc_createNamespace;
|
||||
|
||||
// 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.
|
||||
|
47
addons/medical_engine/functions/fnc_getHitpointArmor.sqf
Normal file
47
addons/medical_engine/functions/fnc_getHitpointArmor.sqf
Normal file
@ -0,0 +1,47 @@
|
||||
#include "script_component.hpp"
|
||||
/*
|
||||
* Author: Pterolatypus
|
||||
* Checks a unit's equipment to calculate the total armor on a hitpoint.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Unit <OBJECT>
|
||||
* 1: Hitpoint <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* Total armor for the given hitpoint <NUMBER>
|
||||
*
|
||||
* Example:
|
||||
* [player, "HitChest"] call ace_medical_engine_fnc_getHitpointArmor
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params ["_unit", "_hitpoint"];
|
||||
|
||||
private _uniform = uniform _unit;
|
||||
// If unit is naked, use its underwear class instead
|
||||
if (_uniform isEqualTo "") then {
|
||||
_uniform = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "nakedUniform");
|
||||
};
|
||||
|
||||
private _gear = [
|
||||
_uniform,
|
||||
vest _unit,
|
||||
headgear _unit
|
||||
];
|
||||
|
||||
private _rags = _gear joinString "$";
|
||||
private _var = format [QGVAR(armorCache$%1), _hitpoint];
|
||||
_unit getVariable [_var, [""]] params ["_prevRags", "_armor"];
|
||||
|
||||
if (_rags != _prevRags) then {
|
||||
_armor = 0;
|
||||
|
||||
{
|
||||
_armor = _armor + ([_x, _hitpoint] call FUNC(getItemArmor));
|
||||
} forEach _gear;
|
||||
|
||||
_unit setVariable [_var, [_rags, _armor]];
|
||||
};
|
||||
|
||||
_armor // return
|
48
addons/medical_engine/functions/fnc_getItemArmor.sqf
Normal file
48
addons/medical_engine/functions/fnc_getItemArmor.sqf
Normal file
@ -0,0 +1,48 @@
|
||||
#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.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Item Class <STRING>
|
||||
* 1: Hitpoint <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* Item armor for the given hitpoint <NUMBER>
|
||||
*
|
||||
* Example:
|
||||
* ["V_PlateCarrier_rgr", "HitChest"] call ace_medical_engine_fnc_getItemArmor
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params ["_item", "_hitpoint"];
|
||||
|
||||
private _key = format ["%1$%2", _item, _hitpoint];
|
||||
private _armor = GVAR(armorCache) getVariable _key;
|
||||
|
||||
if (isNil "_armor") then {
|
||||
TRACE_2("Cache miss",_item,_hitpoint);
|
||||
if ("" in [_item, _hitpoint]) exitWith {
|
||||
_armor = 0;
|
||||
GVAR(armorCache) setVariable [_key, _armor];
|
||||
};
|
||||
|
||||
private _itemInfo = configFile >> "CfgWeapons" >> _item >> "ItemInfo";
|
||||
|
||||
if (getNumber (_itemInfo >> "type") == TYPE_UNIFORM) then {
|
||||
private _unitCfg = configFile >> "CfgVehicles" >> getText (_itemInfo >> "uniformClass");
|
||||
private _entry = _unitCfg >> "HitPoints" >> _hitpoint;
|
||||
|
||||
_armor = getNumber (_unitCfg >> "armor") * getNumber (_entry >> "armor")
|
||||
} else {
|
||||
private _condition = format ["getText (_x >> 'hitpointName') == '%1'", _hitpoint];
|
||||
private _entry = configProperties [_itemInfo >> "HitpointsProtectionInfo", _condition] param [0, configNull];
|
||||
|
||||
_armor = getNumber (_entry >> "armor");
|
||||
};
|
||||
|
||||
GVAR(armorCache) setVariable [_key, _armor];
|
||||
};
|
||||
|
||||
_armor // return
|
@ -34,9 +34,13 @@ if (_hitPoint isEqualTo "") then {
|
||||
// Damage can be disabled with old variable or via sqf command allowDamage
|
||||
if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]}) exitWith {_oldDamage};
|
||||
|
||||
// Damages are stored for "ace_hdbracket" event triggered last
|
||||
private _newDamage = _damage - _oldDamage;
|
||||
_unit setVariable [format [QGVAR($%1), _hitPoint], _newDamage];
|
||||
// Get armor value of hitpoint and calculate damage before armor
|
||||
private _armor = [_unit, _hitpoint] call FUNC(getHitpointArmor);
|
||||
private _realDamage = _newDamage * _armor;
|
||||
// Damages are stored for "ace_hdbracket" event triggered last
|
||||
_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]];
|
||||
TRACE_3("Received hit",_hitpoint,_newDamage,_realDamage);
|
||||
|
||||
// Engine damage to these hitpoints controls blood visuals, limping, weapon sway
|
||||
// Handled in fnc_damageBodyPart, persist here
|
||||
@ -51,52 +55,61 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
|
||||
private _damageStructural = _unit getVariable [HIT_STRUCTURAL, 0];
|
||||
|
||||
// --- Head
|
||||
private _damageFace = _unit getVariable [QGVAR($HitFace), 0];
|
||||
private _damageNeck = _unit getVariable [QGVAR($HitNeck), 0];
|
||||
private _damageHead = (_unit getVariable [QGVAR($HitHead), 0]) max _damageFace max _damageNeck;
|
||||
private _damageHead = [
|
||||
_unit getVariable [QGVAR($HitFace), [0,0]],
|
||||
_unit getVariable [QGVAR($HitNeck), [0,0]],
|
||||
_unit getVariable [QGVAR($HitHead), [0,0]]
|
||||
];
|
||||
_damageHead sort false;
|
||||
_damageHead = _damageHead select 0;
|
||||
|
||||
// --- Body
|
||||
private _damagePelvis = _unit getVariable [QGVAR($HitPelvis), 0];
|
||||
private _damageAbdomen = _unit getVariable [QGVAR($HitAbdomen), 0];
|
||||
private _damageDiaphragm = _unit getVariable [QGVAR($HitDiaphragm), 0];
|
||||
private _damageChest = _unit getVariable [QGVAR($HitChest), 0];
|
||||
private _damageBody = (_unit getVariable [QGVAR($HitBody), 0]) max _damagePelvis max _damageAbdomen max _damageDiaphragm max _damageChest;
|
||||
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]]
|
||||
// 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];
|
||||
private _damageRightArm = _unit getVariable [QGVAR($HitRightArm), 0];
|
||||
private _damageLeftLeg = _unit getVariable [QGVAR($HitLeftLeg), 0];
|
||||
private _damageRightLeg = _unit getVariable [QGVAR($HitRightLeg), 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 maxium damage
|
||||
// Priority used for sorting if incoming damage is equivalent (e.g. max which is 4)
|
||||
private _allDamages = [
|
||||
[_damageHead, PRIORITY_HEAD, "Head"],
|
||||
[_damageBody, PRIORITY_BODY, "Body"],
|
||||
[_damageLeftArm, PRIORITY_LEFT_ARM, "LeftArm"],
|
||||
[_damageRightArm, PRIORITY_RIGHT_ARM, "RightArm"],
|
||||
[_damageLeftLeg, PRIORITY_LEFT_LEG, "LeftLeg"],
|
||||
[_damageRightLeg, PRIORITY_RIGHT_LEG, "RightLeg"]
|
||||
_damageHead + [PRIORITY_HEAD, "Head"],
|
||||
_damageBody + [PRIORITY_BODY, "Body"],
|
||||
_damageLeftArm + [PRIORITY_LEFT_ARM, "LeftArm"],
|
||||
_damageRightArm + [PRIORITY_RIGHT_ARM, "RightArm"],
|
||||
_damageLeftLeg + [PRIORITY_LEFT_LEG, "LeftLeg"],
|
||||
_damageRightLeg + [PRIORITY_RIGHT_LEG, "RightLeg"]
|
||||
];
|
||||
TRACE_2("incoming",_allDamages,_damageStructural);
|
||||
|
||||
// represents all incoming damage for selecting a non-selectionSpecific wound location, (used for selectRandomWeighted [value1,weight1,value2....])
|
||||
private _damageSelectionArray = [
|
||||
HITPOINT_INDEX_HEAD, _damageHead, HITPOINT_INDEX_BODY, _damageBody, HITPOINT_INDEX_LARM, _damageLeftArm,
|
||||
HITPOINT_INDEX_RARM, _damageRightArm, HITPOINT_INDEX_LLEG, _damageLeftLeg, HITPOINT_INDEX_RLEG, _damageRightLeg
|
||||
HITPOINT_INDEX_HEAD, _damageHead select 1, HITPOINT_INDEX_BODY, _damageBody select 1, HITPOINT_INDEX_LARM, _damageLeftArm select 1,
|
||||
HITPOINT_INDEX_RARM, _damageRightArm select 1, HITPOINT_INDEX_LLEG, _damageLeftLeg select 1, HITPOINT_INDEX_RLEG, _damageRightLeg select 1
|
||||
];
|
||||
|
||||
_allDamages sort false;
|
||||
(_allDamages select 0) params ["_receivedDamage", "", "_woundedHitPoint"];
|
||||
if (_damageHead >= HEAD_DAMAGE_THRESHOLD) then {
|
||||
TRACE_3("reporting fatal head damage instead of max",_damageHead,_receivedDamage,_woundedHitPoint);
|
||||
_receivedDamage = _damageHead;
|
||||
(_allDamages select 0) params ["", "_receivedDamage", "", "_woundedHitPoint"];
|
||||
private _receivedDamageHead = _damageHead select 1;
|
||||
if (_receivedDamageHead >= HEAD_DAMAGE_THRESHOLD) then {
|
||||
TRACE_3("reporting fatal head damage instead of max",_receivedDamageHead,_receivedDamage,_woundedHitPoint);
|
||||
_receivedDamage = _receivedDamageHead;
|
||||
_woundedHitPoint = "Head";
|
||||
};
|
||||
|
||||
// We know it's structural when no specific hitpoint is damaged
|
||||
if (_receivedDamage == 0) then {
|
||||
_receivedDamage = _damageStructural;
|
||||
_receivedDamage = _damageStructural select 1;
|
||||
_woundedHitPoint = "Body";
|
||||
_damageSelectionArray = [1, 1]; // sum of weights would be 0
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user