Medical Engine - Remove ACE_HDBracket hitpoint (#9732)

* Medical Engine - Remove ACE_HDBracket hitpoint

* skip context 4

* move checks to vars

* skip uav/logic entities

* add check for arm/leg hitpoints

* Update addons/medical_engine/functions/fnc_handleDamage.sqf

Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>

* don't skip context 2

* clearer custom hitpoint array name

* reenable compile cache

* remove debug mode

* lazy eval

* whitespace

* update comment

* Update fnc_handleDamage.sqf header

* Update addons/medical_engine/functions/fnc_handleDamage.sqf

Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>

* Update addons/medical_engine/XEH_postInit.sqf

Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>

* update hitpoint test

* missing "

* Update addons/medical/dev/test_hitpointConfigs.sqf

Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>

---------

Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>
Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>
This commit is contained in:
Grim 2024-06-11 11:11:28 -03:00 committed by GitHub
parent c862c47617
commit a85074a797
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 71 deletions

View File

@ -21,23 +21,16 @@ INFO_1("Checking uniforms for correct medical hitpoints [%1 units]",count _units
private _testPass = true; private _testPass = true;
{ {
private _typeOf = configName _x; private _typeOf = configName _x;
private _hitpoints = (configProperties [_x >> "HitPoints", "isClass _x", true]) apply {configName _x}; private _hitpoints = (configProperties [_x >> "HitPoints", "isClass _x", true]) apply {toLowerANSI configName _x};
private _expectedHitPoints = ["hitleftarm","hitrightarm","hitleftleg","hitrightleg","hithead","hitbody"];
private _missingHitPoints = _expectedHitPoints select {!(_x in _hitpoints)};
if (_missingHitPoints isNotEqualTo []) then {
WARNING_3("%1 missing ace hitpoints: %2 - class hitpoints: %3",_typeOf,_missingHitPoints,_hitpoints);
_testPass = false;
};
// _typeOf createUnit [position player, group player, "z = this"]; // _typeOf createUnit [position player, group player, "z = this"];
// deleteVehicle z; // deleteVehicle z;
private _lastHitpoint = (_hitpoints param [(count _hitpoints) - 1, "#array"]);
if (_lastHitpoint != "ACE_HDBracket") then {
WARNING_2("%1 has bad last hitpoint: %2",_typeOf,_hitpoints);
_testPass = false;
};
if (((_hitpoints findIf {_x == "HitLeftArm"}) == -1) || {(_hitpoints findIf {_x == "HitRightArm"}) == -1}
|| {(_hitpoints findIf {_x == "HitLeftLeg"}) == -1} || {(_hitpoints findIf {_x == "HitRightLeg"}) == -1}
|| {(_hitpoints findIf {_x == "HitHead"}) == -1} || {(_hitpoints findIf {_x == "HitBody"}) == -1}) then {
WARNING_2("%1 missing ace hitpoints: %2",_typeOf,_hitpoints);
_testPass = false;
};
} forEach _units; } forEach _units;
_testPass _testPass

View File

@ -6,27 +6,23 @@
[_new] call FUNC(updateDamageEffects); // Run on new controlled unit to update QGVAR(aimFracture) [_new] call FUNC(updateDamageEffects); // Run on new controlled unit to update QGVAR(aimFracture)
}, true] call CBA_fnc_addPlayerEventHandler; }, true] call CBA_fnc_addPlayerEventHandler;
["CAManBase", "init", { ["CAManBase", "init", {
params ["_unit"]; params ["_unit"];
// Check if last hit point is our dummy.
private _allHitPoints = getAllHitPointsDamage _unit param [0, []];
reverse _allHitPoints;
while {(_allHitPoints param [0, ""]) select [0,1] == "#"} do { WARNING_1("Ignoring Reflector hitpoint %1",_allHitPoints deleteAt 0); };
if (_allHitPoints param [0, ""] != "ACE_HDBracket") then {
if (unitIsUAV _unit) exitWith {TRACE_1("ignore UAV AI",typeOf _unit);}; if (unitIsUAV _unit) exitWith {TRACE_1("ignore UAV AI",typeOf _unit);};
if (getNumber ((configOf _unit) >> "isPlayableLogic") == 1) exitWith {TRACE_1("ignore logic unit",typeOf _unit)}; if (getNumber (configOf _unit >> "isPlayableLogic") == 1) exitWith {TRACE_1("ignore logic unit",typeOf _unit);};
private _allHitPoints = getAllHitPointsDamage _unit param [0, []];
if ((GVAR(customHitpoints) arrayIntersect _allHitPoints) isNotEqualTo GVAR(customHitpoints)) exitWith {
ERROR_1("Bad hitpoints for unit type ""%1""",typeOf _unit); ERROR_1("Bad hitpoints for unit type ""%1""",typeOf _unit);
} else { };
// Calling this function inside curly brackets allows the usage of // Calling this function inside curly brackets allows the usage of
// "exitWith", which would be broken with "HandleDamage" otherwise. // "exitWith", which would be broken with "HandleDamage" otherwise.
_unit setVariable [ _unit setVariable [
QEGVAR(medical,HandleDamageEHID), QEGVAR(medical,HandleDamageEHID),
_unit addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}] _unit addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}]
]; ];
};
}, nil, [IGNORE_BASE_UAVPILOTS], true] call CBA_fnc_addClassEventHandler; }, nil, [IGNORE_BASE_UAVPILOTS], true] call CBA_fnc_addClassEventHandler;
#ifdef DEBUG_MODE_FULL #ifdef DEBUG_MODE_FULL
@ -88,7 +84,7 @@
}; };
}] call CBA_fnc_addEventHandler; }] call CBA_fnc_addEventHandler;
["CAManBase", "deleted", { ["CAManBase", "Deleted", {
params ["_unit"]; params ["_unit"];
TRACE_3("unit deleted",_unit,objectParent _unit,local _unit); TRACE_3("unit deleted",_unit,objectParent _unit,local _unit);
if ((!isNull objectParent _unit) && {local objectParent _unit}) then { if ((!isNull objectParent _unit) && {local objectParent _unit}) then {

View File

@ -46,6 +46,8 @@ GVAR(animations) setVariable [QUNCON_ANIM(faceDown), [QUNCON_ANIM(1),QUNCON_ANIM
GVAR(animations) setVariable [QUNCON_ANIM(faceLeft), [QUNCON_ANIM(7),QUNCON_ANIM(8),QUNCON_ANIM(1_1),QUNCON_ANIM(7_1),QUNCON_ANIM(8_1)]]; GVAR(animations) setVariable [QUNCON_ANIM(faceLeft), [QUNCON_ANIM(7),QUNCON_ANIM(8),QUNCON_ANIM(1_1),QUNCON_ANIM(7_1),QUNCON_ANIM(8_1)]];
GVAR(animations) setVariable [QUNCON_ANIM(faceRight), [QUNCON_ANIM(5),QUNCON_ANIM(6),QUNCON_ANIM(10),QUNCON_ANIM(5_1),QUNCON_ANIM(6_1)]]; GVAR(animations) setVariable [QUNCON_ANIM(faceRight), [QUNCON_ANIM(5),QUNCON_ANIM(6),QUNCON_ANIM(10),QUNCON_ANIM(5_1),QUNCON_ANIM(6_1)]];
GVAR(customHitpoints) = ["hitleftarm", "hitrightarm", "hitleftleg", "hitrightleg"];
private _fnc_fixStatic = { private _fnc_fixStatic = {
params ["_vehicle"]; params ["_vehicle"];
private _type = typeOf _vehicle; private _type = typeOf _vehicle;

View File

@ -1,9 +1,9 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: commy2, kymckay * Author: commy2, kymckay, LinkIsGrim
* HandleDamage EH where wound events are raised based on incoming damage. * HandleDamage EH where wound events are raised based on incoming damage.
* Be aware that for each source of damage, the EH can fire multiple times (once for each hitpoint). * Be aware that for each source of damage, the EH can fire multiple times (once for each hitpoint).
* We store these incoming damages and compare them on our final hitpoint: "ace_hdbracket". * We store these incoming damages and compare them on last iteration of the event (_context == 2).
* *
* Arguments: * Arguments:
* Handle damage EH * Handle damage EH
@ -13,15 +13,16 @@
* *
* Public: No * Public: No
*/ */
params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint"]; params ["_unit", "_selection", "_damage", "_shooter", "_ammo", "_hitPointIndex", "_instigator", "_hitpoint", "_directHit", "_context"];
// HD sometimes triggers for remote units - ignore. // HD sometimes triggers for remote units - ignore.
if !(local _unit) exitWith {nil}; if !(local _unit) exitWith {nil};
// Get missing meta info // Get missing meta info
private _oldDamage = 0; private _oldDamage = 0;
private _structuralDamage = _context == 0;
if (_hitPoint isEqualTo "") then { if (_structuralDamage) then {
_hitPoint = "#structural"; _hitPoint = "#structural";
_oldDamage = damage _unit; _oldDamage = damage _unit;
} else { } else {
@ -33,26 +34,29 @@ if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), t
private _newDamage = _damage - _oldDamage; private _newDamage = _damage - _oldDamage;
// Happens occasionally for vehiclehit events (see line 80 onwards) // _newDamage == 0 happens occasionally for vehiclehit events (see line 80 onwards), just exit early to save some frametime
// Just exit early to save some frametime // context 4 is engine "bleeding". For us, it's just a duplicate event for #structural which we can ignore without any issues
if (_newDamage == 0 && {_hitpoint isNotEqualTo "ace_hdbracket"}) exitWith {_oldDamage}; if (_context != 2 && {_context == 4 || _newDamage == 0}) exitWith {
TRACE_4("Skipping engine bleeding or zero damage",_ammo,_newDamage,_directHit,_context);
_oldDamage
};
// Get scaled armor value of hitpoint and calculate damage before armor // Get scaled armor value of hitpoint and calculate damage before armor
// We scale using passThrough to handle explosive-resistant armor properly (#9063) // We scale using passThrough to handle explosive-resistant armor properly (#9063)
// We need realDamage to determine which limb was hit correctly // We need realDamage to determine which limb was hit correctly
[_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"]; [_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"];
private _realDamage = _newDamage * _armor; private _realDamage = _newDamage * _armor;
if (_hitPoint isNotEqualTo "#structural") then { if (!_structuralDamage) then {
private _armorCoef = _armor/_armorScaled; private _armorCoef = _armor/_armorScaled;
private _damageCoef = linearConversion [0, 1, GVAR(damagePassThroughEffect), 1, _armorCoef]; private _damageCoef = linearConversion [0, 1, GVAR(damagePassThroughEffect), 1, _armorCoef];
_newDamage = _newDamage * _damageCoef; _newDamage = _newDamage * _damageCoef;
}; };
TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage); TRACE_6("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_directHit,_context);
// Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs // Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
// Damage occurs in consistent increments // Damage occurs in consistent increments
if ( if (
_hitPoint isEqualTo "#structural" && _structuralDamage &&
{getOxygenRemaining _unit <= 0.5} && {getOxygenRemaining _unit <= 0.5} &&
{_damage isEqualTo (_oldDamage + 0.005)} {_damage isEqualTo (_oldDamage + 0.005)}
) exitWith { ) exitWith {
@ -64,14 +68,14 @@ if (
// Faster than (vehicle _unit), also handles dead units // Faster than (vehicle _unit), also handles dead units
private _vehicle = objectParent _unit; private _vehicle = objectParent _unit;
private _inVehicle = !isNull _vehicle;
private _environmentDamage = _ammo == "";
// Crashing a vehicle doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs // Crashing a vehicle doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
// It does fire the EH multiple times, but this seems to scale with the intensity of the crash // It does fire the EH multiple times, but this seems to scale with the intensity of the crash
if ( if (
EGVAR(medical,enableVehicleCrashes) && EGVAR(medical,enableVehicleCrashes) &&
{_hitPoint isEqualTo "#structural"} && {_environmentDamage && _inVehicle && _structuralDamage} &&
{_ammo isEqualTo ""} &&
{!isNull _vehicle} &&
{vectorMagnitude (velocity _vehicle) > 5} {vectorMagnitude (velocity _vehicle) > 5}
// todo: no way to detect if stationary and another vehicle hits you // todo: no way to detect if stationary and another vehicle hits you
) exitWith { ) exitWith {
@ -83,11 +87,8 @@ if (
// Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint // Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint
// This is the case for mines, explosives, artillery, and catasthrophic vehicle explosions // This is the case for mines, explosives, artillery, and catasthrophic vehicle explosions
// Triggers twice, but that doesn't matter as damage is low
if ( if (
_hitPoint isEqualTo "#structural" && (!_environmentDamage && _inVehicle && _structuralDamage) &&
{!isNull _vehicle} &&
{_ammo isNotEqualTo ""} &&
{ {
private _ammoCfg = configFile >> "CfgAmmo" >> _ammo; private _ammoCfg = configFile >> "CfgAmmo" >> _ammo;
GET_NUMBER(_ammoCfg >> "explosive",0) > 0 || GET_NUMBER(_ammoCfg >> "explosive",0) > 0 ||
@ -104,9 +105,13 @@ if (
0 0
}; };
// This hitpoint is set to trigger last, evaluate all the stored damage values // Damages are stored for last iteration of the HandleDamage event (_context == 2)
// to determine where wounds are applied _unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]];
if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
// Ref https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#HandleDamage
// Context 2 means this is the last iteration of HandleDamage, so figure out which hitpoint took the most real damage and send wound event
// Don't exit, as the last iteration can be one of the hitpoints that we need to keep _oldDamage for
if (_context == 2) then {
_unit setVariable [QEGVAR(medical,lastDamageSource), _shooter]; _unit setVariable [QEGVAR(medical,lastDamageSource), _shooter];
_unit setVariable [QEGVAR(medical,lastInstigator), _instigator]; _unit setVariable [QEGVAR(medical,lastInstigator), _instigator];
@ -157,7 +162,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
// Environmental damage sources all have empty ammo string // Environmental damage sources all have empty ammo string
// No explicit source given, we infer from differences between them // No explicit source given, we infer from differences between them
if (_ammo isEqualTo "") then { if (_environmentDamage) then {
// Any collision with terrain/vehicle/object has a shooter // Any collision with terrain/vehicle/object has a shooter
// Check this first because burning can happen at any velocity // Check this first because burning can happen at any velocity
if !(isNull _shooter) then { if !(isNull _shooter) then {
@ -199,16 +204,9 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
QGVAR($HitLeftArm),QGVAR($HitRightArm),QGVAR($HitLeftLeg),QGVAR($HitRightLeg), QGVAR($HitLeftArm),QGVAR($HitRightArm),QGVAR($HitLeftLeg),QGVAR($HitRightLeg),
QGVAR($#structural) QGVAR($#structural)
]; ];
0
}; };
// Damages are stored for "ace_hdbracket" event triggered last
_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]];
// Engine damage to these hitpoints controls blood visuals, limping, weapon sway // Engine damage to these hitpoints controls blood visuals, limping, weapon sway
// Handled in fnc_damageBodyPart, persist here // Handled in fnc_damageBodyPart, persist here
if (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"]) exitWith {_oldDamage}; // For all other hitpoints, we store our own damage values, so engine damage is unnecessary
[0, _oldDamage] select (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"])
// We store our own damage values so engine damage is unnecessary
0

View File

@ -47,15 +47,4 @@
};\ };\
class HitRightLeg: HitLeftLeg {\ class HitRightLeg: HitLeftLeg {\
name = "leg_r";\ name = "leg_r";\
};\
class ACE_HDBracket {\
armor = 1;\
material = -1;\
name = "head";\
passThrough = 0;\
radius = 1;\
explosionShielding = 1;\
visual = "";\
minimalHit = 0;\
depends = "HitHead";\
} }