Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions addons/medical/dev/test_hitpointConfigs.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,16 @@ INFO_1("Checking uniforms for correct medical hitpoints [%1 units]",count _units
private _testPass = true;
{
private _typeOf = configName _x;
private _hitpoints = (configProperties [_x >> "HitPoints", "isClass _x", true]) apply {configName _x};

// _typeOf createUnit [position player, group player, "z = this"];
// 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);
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;
};

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;
};
// _typeOf createUnit [position player, group player, "z = this"];
// deleteVehicle z;
} forEach _units;

_testPass
28 changes: 12 additions & 16 deletions addons/medical_engine/XEH_postInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,23 @@
[_new] call FUNC(updateDamageEffects); // Run on new controlled unit to update QGVAR(aimFracture)
}, true] call CBA_fnc_addPlayerEventHandler;


["CAManBase", "init", {
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 (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 (_allHitPoints param [0, ""] != "ACE_HDBracket") then {
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)};
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);
} else {
// Calling this function inside curly brackets allows the usage of
// "exitWith", which would be broken with "HandleDamage" otherwise.
_unit setVariable [
QEGVAR(medical,HandleDamageEHID),
_unit addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}]
];
};

// Calling this function inside curly brackets allows the usage of
// "exitWith", which would be broken with "HandleDamage" otherwise.
_unit setVariable [
QEGVAR(medical,HandleDamageEHID),
_unit addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}]
];
Comment thread
veteran29 marked this conversation as resolved.
}, nil, [IGNORE_BASE_UAVPILOTS], true] call CBA_fnc_addClassEventHandler;

#ifdef DEBUG_MODE_FULL
Expand Down Expand Up @@ -94,7 +90,7 @@
};
}] call CBA_fnc_addEventHandler;

["CAManBase", "deleted", {
["CAManBase", "Deleted", {
params ["_unit"];
TRACE_3("unit deleted",_unit,objectParent _unit,local _unit);
if ((!isNull objectParent _unit) && {local objectParent _unit}) then {
Expand Down
2 changes: 2 additions & 0 deletions addons/medical_engine/XEH_preInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -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(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 = {
params ["_vehicle"];
private _type = typeOf _vehicle;
Expand Down
58 changes: 28 additions & 30 deletions addons/medical_engine/functions/fnc_handleDamage.sqf
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include "..\script_component.hpp"
/*
* Author: commy2, kymckay
* Author: commy2, kymckay, LinkIsGrim
* HandleDamage EH where wound events are raised based on incoming damage.
* Be aware that for each source of damage, the EH can fire multiple times (once for each hitpoint).
* We store these incoming damages and compare them on our final hitpoint: "ace_hdbracket".
* We store these incoming damages and compare them on last iteration of the event (_context == 2).
*
* Arguments:
* Handle damage EH
Expand All @@ -13,15 +13,16 @@
*
* 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.
if !(local _unit) exitWith {nil};

// Get missing meta info
private _oldDamage = 0;
private _structuralDamage = _context == 0;

if (_hitPoint isEqualTo "") then {
if (_structuralDamage) then {
_hitPoint = "#structural";
_oldDamage = damage _unit;
} else {
Expand All @@ -33,26 +34,29 @@ if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), t

private _newDamage = _damage - _oldDamage;

// Happens occasionally for vehiclehit events (see line 80 onwards)
// Just exit early to save some frametime
if (_newDamage == 0 && {_hitpoint isNotEqualTo "ace_hdbracket"}) exitWith {_oldDamage};
// _newDamage == 0 happens occasionally for vehiclehit events (see line 80 onwards), just exit early to save some frametime
// context 4 is engine "bleeding". For us, it's just a duplicate event for #structural which we can ignore without any issues
if (_context != 2 && {_context == 4 || _newDamage == 0}) exitWith {
TRACE_4("Skipping engine bleeding or zero damage",_ammo,_newDamage,_directHit,_context);
_oldDamage
};

// Get scaled armor value of hitpoint and calculate damage before armor
// We scale using passThrough to handle explosive-resistant armor properly (#9063)
// We need realDamage to determine which limb was hit correctly
[_unit, _hitpoint] call FUNC(getHitpointArmor) params ["_armor", "_armorScaled"];
private _realDamage = _newDamage * _armor;
if (_hitPoint isNotEqualTo "#structural") then {
if (!_structuralDamage) then {
private _armorCoef = _armor/_armorScaled;
private _damageCoef = linearConversion [0, 1, GVAR(damagePassThroughEffect), 1, _armorCoef];
_newDamage = _newDamage * _damageCoef;
};
TRACE_4("Received hit",_hitpoint,_ammo,_newDamage,_realDamage);
TRACE_6("Received hit",_hitpoint,_ammo,_newDamage,_realDamage,_directHit,_context);

// Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
// Damage occurs in consistent increments
if (
_hitPoint isEqualTo "#structural" &&
_structuralDamage &&
{getOxygenRemaining _unit <= 0.5} &&
{_damage isEqualTo (_oldDamage + 0.005)}
) exitWith {
Expand All @@ -64,14 +68,14 @@ if (

// Faster than (vehicle _unit), also handles dead units
private _vehicle = objectParent _unit;
private _inVehicle = !isNull _vehicle;
private _environmentDamage = _ammo == "";

// Crashing a vehicle doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
// It does fire the EH multiple times, but this seems to scale with the intensity of the crash
if (
EGVAR(medical,enableVehicleCrashes) &&
{_hitPoint isEqualTo "#structural"} &&
{_ammo isEqualTo ""} &&
{!isNull _vehicle} &&
{_environmentDamage && _inVehicle && _structuralDamage} &&
{vectorMagnitude (velocity _vehicle) > 5}
// todo: no way to detect if stationary and another vehicle hits you
) exitWith {
Expand All @@ -83,11 +87,8 @@ if (

// Receiving explosive damage inside a vehicle doesn't trigger for each hitpoint
// This is the case for mines, explosives, artillery, and catasthrophic vehicle explosions
// Triggers twice, but that doesn't matter as damage is low
if (
_hitPoint isEqualTo "#structural" &&
{!isNull _vehicle} &&
{_ammo isNotEqualTo ""} &&
(!_environmentDamage && _inVehicle && _structuralDamage) &&
{
private _ammoCfg = configFile >> "CfgAmmo" >> _ammo;
GET_NUMBER(_ammoCfg >> "explosive",0) > 0 ||
Expand All @@ -104,9 +105,13 @@ if (
0
};

// This hitpoint is set to trigger last, evaluate all the stored damage values
// to determine where wounds are applied
if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
// Damages are stored for last iteration of the HandleDamage event (_context == 2)
_unit setVariable [format [QGVAR($%1), _hitPoint], [_realDamage, _newDamage]];

// 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,lastInstigator), _instigator];

Expand Down Expand Up @@ -157,7 +162,7 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {

// Environmental damage sources all have empty ammo string
// No explicit source given, we infer from differences between them
if (_ammo isEqualTo "") then {
if (_environmentDamage) then {
// Any collision with terrain/vehicle/object has a shooter
// Check this first because burning can happen at any velocity
if !(isNull _shooter) then {
Expand Down Expand Up @@ -199,16 +204,9 @@ if (_hitPoint isEqualTo "ace_hdbracket") exitWith {
QGVAR($HitLeftArm),QGVAR($HitRightArm),QGVAR($HitLeftLeg),QGVAR($HitRightLeg),
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
// Handled in fnc_damageBodyPart, persist here
if (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"]) exitWith {_oldDamage};

// We store our own damage values so engine damage is unnecessary
0
// For all other hitpoints, we store our own damage values, so engine damage is unnecessary
[0, _oldDamage] select (_hitPoint in ["hithead", "hitbody", "hithands", "hitlegs"])
11 changes: 0 additions & 11 deletions addons/medical_engine/script_macros_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,4 @@
};\
class HitRightLeg: HitLeftLeg {\
name = "leg_r";\
};\
class ACE_HDBracket {\
armor = 1;\
material = -1;\
name = "head";\
passThrough = 0;\
radius = 1;\
explosionShielding = 1;\
visual = "";\
minimalHit = 0;\
depends = "HitHead";\
}