From 68ab7b5143de016822f6536b4786db922c79d3e0 Mon Sep 17 00:00:00 2001 From: Thomas Kooi Date: Thu, 22 Jan 2015 22:00:04 +0100 Subject: [PATCH] Added AGM handleDamage caching --- addons/medical/XEH_preInit.sqf | 5 + .../functions/fnc_cacheHandleDamageCall.sqf | 126 ++++++++++++++++++ addons/medical/functions/fnc_checkDamage.sqf | 55 ++++++++ addons/medical/functions/fnc_onDamage.sqf | 83 ++++++++++++ .../medical/functions/fnc_onInitForUnit.sqf | 2 +- 5 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 addons/medical/functions/fnc_cacheHandleDamageCall.sqf create mode 100644 addons/medical/functions/fnc_checkDamage.sqf create mode 100644 addons/medical/functions/fnc_onDamage.sqf diff --git a/addons/medical/XEH_preInit.sqf b/addons/medical/XEH_preInit.sqf index 15150a57c5..89687a25b0 100644 --- a/addons/medical/XEH_preInit.sqf +++ b/addons/medical/XEH_preInit.sqf @@ -110,6 +110,7 @@ PREP(onTreatmentCompleted); PREP(onUnconscious); PREP(onStartMovingUnit); PREP(onCarryObjectDropped); +PREP(onDamage); PREP(openMenu); PREP(playInjuredSound); @@ -127,6 +128,10 @@ PREP(updateUIInfo); PREP(useEquipment); + +PREP(cacheHandledamageCall); +PREP(checkDamage); + // initalize all module parameters. GVAR(setting_allowInstantDead) = true; GVAR(setting_AdvancedLevel) = 0; diff --git a/addons/medical/functions/fnc_cacheHandleDamageCall.sqf b/addons/medical/functions/fnc_cacheHandleDamageCall.sqf new file mode 100644 index 0000000000..09469ec275 --- /dev/null +++ b/addons/medical/functions/fnc_cacheHandleDamageCall.sqf @@ -0,0 +1,126 @@ +/** + * fnc_cacheHandleDamageCall.sqf + * @Descr: Cache a handleDamage call to execute it 3 frames later + * @Author: KoffeinFlummi + * + * @Arguments: [unit OBJECT, selection STRING, damagE NUMBER, source OBJECT, projectile STRING (Could be an OBJECT on occasion)] + * @Return: + * @PublicAPI: false + */ + + +#include "script_component.hpp" + +private ["_unit", "_selectionName","_damage"]; +_unit = _this select 0; +_selectionName = _this select 1; +_damage = _this select 2; +_source = _this select 3; +_projectile = _this select 4; + +_hitSelections = ["head", "body", "hand_l", "hand_r", "leg_l", "leg_r"]; +_hitPoints = ["HitHead", "HitBody", "HitLeftArm", "HitRightArm", "HitLeftLeg", "HitRightLeg"]; + +// Calculate change in damage. +_newDamage = _damage - (damage _unit); +if (_selectionName in _hitSelections) then { + _newDamage = _damage - (_unit getHitPointDamage (_hitPoints select (_hitSelections find _selectionName))); +}; + +// we want to move damage to another selection; have to do it ourselves. +// this is only the case for limbs, so this will not impact the killed EH. +if (_selectionName != (_this select 1)) then { + _unit setHitPointDamage [_hitPoints select (_hitSelections find _selectionName), _damage + _newDamage]; + _newDamage = 0; +}; +_damage = _damage + _newDamage; + +// From AGM medical: +// Exclude falling damage to everything other than legs; reduce structural damage. +if (((velocity _unit) select 2 < -5) && (vehicle _unit == _unit)) then { + _unit setVariable [QGVAR(isFalling), True]; +}; +if (_unit getVariable [QGVAR(isFalling), false] && !(_selectionName in ["", "leg_l", "leg_r"])) exitWith {}; +if (_unit getVariable [QGVAR(isFalling), false]) then { + _newDamage = _newDamage * 0.7; +}; + +// Finished with the current frame, reset variables +// Note: sometimes handleDamage spans over 2 or even 3 frames. +if (diag_frameno > (_unit getVariable [QGVAR(frameNo), -3]) + 2) then { + _unit setVariable [QGVAR(frameNo), diag_frameno]; + + // handle the cached damages 3 frames later + [{ + _args = _this select 0; + _unit = _args select 0; + _frameNo = _args select 1; + + if (diag_frameno > _frameNo + 2) then { + _cache_projectiles = _unit getVariable [QGVAR(cachedProjectiles), []]; + _cache_hitpoints = _unit getVariable [QGVAR(cachedHitPoints), []]; + _cache_damages = _unit getVariable [QGVAR(cachedDamages), []]; + _cache_params = _unit getVariable [QGVAR(cachedHandleDamageParams), []]; + { + if (typeName _x == typeName "") then { + (_cache_params select _foreachIndex) call FUNC(handleDamage); + }; + }foreach _cache_projectiles; + [(_this select 1)] call cba_fnc_removePerFrameHandler; + }; + }, 0, [_unit, diag_frameno] ] call CBA_fnc_addPerFrameHandler; + + _unit setVariable [QGVAR(cachedProjectiles), []]; + _unit setVariable [QGVAR(cachedHitPoints), []]; + _unit setVariable [QGVAR(cachedDamages), []]; + _unit setVariable [QGVAR(cachedHandleDamageParams), []]; +}; + +// Make sure there's only one damaged selection per projectile per frame. +_cache_projectiles = _unit getVariable QGVAR(cachedProjectiles); +_cache_hitpoints = _unit getVariable QGVAR(cachedHitPoints); +_cache_damages = _unit getVariable QGVAR(cachedDamages); +_cache_params = _unit getVariable QGVAR(cachedHandleDamageParams); + +// Caching of the damage events +if (_selectionName != "") then { + + // Check if the current projectile has already been handled once + if (_projectile in _cache_projectiles) then { + + // if it has been handled, find the index in the cache + _index = _cache_projectiles find _projectile; + + // Find the previous damage this projectile has done + _otherDamage = (_cache_damages select _index); + + // Take the highest damage of the two + if (_otherDamage > _newDamage) then { + _newDamage = 0; + } else { + // Restore the damage before the previous damage was processed + _hitPoint = _cache_hitpoints select _index; + _restore = ((_unit getHitPointDamage _hitPoint) - _otherDamage) max 0; + _unit setHitPointDamage [_hitPoint, _restore]; + + // Make entry unfindable and add the new damage cache + _cache_projectiles set [_index, objNull]; + _cache_projectiles pushBack _projectile; + _cache_hitpoints pushBack (_hitPoints select (_hitSelections find _selectionName)); + _cache_damages pushBack _newDamage; + _cache_params pushBack [_unit, _selectionName, _damage, _source, _projectile]; + }; + } else { + // This is an unhandled projectile + _cache_projectiles pushBack _projectile; + _cache_hitpoints pushBack (_hitPoints select (_hitSelections find _selectionName)); + _cache_damages pushBack _newDamage; + _cache_params pushBack [_unit, _selectionName, _damage, _source, _projectile]; + }; +}; + +// Store the new cached values +_unit setVariable [QGVAR(cachedProjectiles), _cache_projectiles]; +_unit setVariable [QGVAR(cachedHitPoints), _cache_hitpoints]; +_unit setVariable [QGVAR(cachedDamages), _cache_damages]; +_unit setVariable [QGVAR(cachedHandleDamageParams), _cache_params]; diff --git a/addons/medical/functions/fnc_checkDamage.sqf b/addons/medical/functions/fnc_checkDamage.sqf new file mode 100644 index 0000000000..67495c25de --- /dev/null +++ b/addons/medical/functions/fnc_checkDamage.sqf @@ -0,0 +1,55 @@ +/* + * Author: KoffeinFlummi + * + * Checks the unit for leg and arm damage, and removes orphan structural damage. + * + * Arguments: + + 0: Unit + * 1: Leg Damage + * 2: Arm Damage + * 3: Remove orphan damage? (Bool; optional, default No) + * + * Return Value: + * None + */ + +#define LEGDAMAGETRESHOLD1 1 +#define LEGDAMAGETRESHOLD2 1.7 +#define ARMDAMAGETRESHOLD1 1 +#define ARMDAMAGETRESHOLD2 1.7 + +private ["_unit", "_legdamage", "_armdamage", "_damagesum"]; + +_unit = _this select 0; +_legdamage = _this select 1; +_armdamage = _this select 2; + +// Leg Damage +// lightly wounded, only limit walking speed (forceWalk is for suckers) +if (_legdamage >= LEGDAMAGETRESHOLD1) then { + if (_unit getHitPointDamage "HitLegs" != 1) then {_unit setHitPointDamage ["HitLegs", 1]}; +} else { + if (_unit getHitPointDamage "HitLegs" != 0) then {_unit setHitPointDamage ["HitLegs", 0]}; +}; +// @ŧodo: force prone for completely fucked up legs. + +// Arm Damage +// fx only +if (_armdamage >= ARMDAMAGETRESHOLD1) then { + if (_unit getHitPointDamage "HitHands" != 1) then {_unit setHitPointDamage ["HitHands", 1]}; +} else { + if (_unit getHitPointDamage "HitHands" != 0) then {_unit setHitPointDamage ["HitHands", 0]}; +}; + +// remove leftover structural damage if unit is already fully healed +if (count _this > 3 and _this select 3) then { + _damagesum = (_unit getHitPointDamage "HitHead") + + (_unit getHitPointDamage "HitBody") + + (_unit getHitPointDamage "HitLeftArm") + + (_unit getHitPointDamage "HitRightArm") + + (_unit getHitPointDamage "HitLeftLeg") + + (_unit getHitPointDamage "HitRightLeg"); + if (_damagesum <= 0.06) then { + _unit setDamage 0; + }; +}; diff --git a/addons/medical/functions/fnc_onDamage.sqf b/addons/medical/functions/fnc_onDamage.sqf new file mode 100644 index 0000000000..31bee43595 --- /dev/null +++ b/addons/medical/functions/fnc_onDamage.sqf @@ -0,0 +1,83 @@ +/** + * fnc_onDamage.sqf + * @Descr: Called when some dude gets shot. Or stabbed. Or blown up. Or pushed off a cliff. Or hit by a car. Or burnt. Or poisoned. Or gassed. Or cut. You get the idea. + * @Author: KoffeinFlummi + * + * @Arguments: [unit OBJECT, selection STRING, damagE NUMBER, source OBJECT, projectile STRING (Could be an OBJECT on occasion)] + * @Return: damage value to be inflicted + * @PublicAPI: false + */ + +#include "script_component.hpp" + +#define ARMOURCOEF 2 + +private ["_unit", "_selectionName", "_damage", "_source", "_source", "_projectile", "_hitSelections", "_hitPoints", "_newDamage", "_found", "_cache_projectiles", "_cache_hitpoints", "_cache_damages"]; + +_unit = _this select 0; +_selectionName = _this select 1; +_damage = _this select 2; +_source = _this select 3; +_projectile = _this select 4; + +if (!([_unit] call FUNC(hasMedicalEnabled))) exitwith { + call FUNC(handleDamage); // let it run through the damage threshold script +}; +// Otherwise we carry on with collecting the necessary information + +if (typeName _projectile == "OBJECT") then { + _projectile = typeOf _projectile; +}; + +// This seems to only show up in MP too, but since it doesn't +// collide with anything, I'll check it in SP as well. +if (_selectionName == "r_femur_hit") then { + _selectionName = "leg_r"; +}; + +// If the damage is being weird, we just tell it to fuck off. +if !(_selectionName in ["head", "body", "hand_l", "hand_r", "leg_l", "leg_r"]) exitWith {0.01}; + +// Cache the handleDamage call +[_unit, _selectionName, _damage, _source, _projectile] call FUNC(cacheHandledamageCall); + +// Checking if we should return full damage or not +if (_damage > 0.95) then { + _damage = 0.95; +}; + +// Check if a unit would die from this hit +if (alive _unit && (vehicle _unit == _unit) && {alive (vehicle _unit)}) then { + _bodyPartn = [_selectionName] call FUNC(getBodyPartNumber); + + // Find the correct Damage threshold for unit. + _damageThreshold = [1,1,1]; + if (isPlayer _unit) then { + _damageThreshold =_unit getvariable[QGVAR(unitDamageThreshold), [GVAR(damageThreshold_Players), GVAR(damageThreshold_Players), GVAR(damageThreshold_Players)]]; + } else { + _damageThreshold =_unit getvariable[QGVAR(unitDamageThreshold), [GVAR(damageThreshold_AI), GVAR(damageThreshold_AI), GVAR(damageThreshold_AI)]]; + }; + _damageBodyPart = ([_unit,QGVAR(bodyPartStatus),[0,0,0,0,0,0]] call EFUNC(common,getDefinedVariable)) select _bodyPartn; + + // Check if damage to body part is higher as damage head + if (_bodyPartn == 0) exitwith { + if (_damageBodyPart >= ((_damageThreshold select 0) + _newDamage) && {(random(1) > 0.2)}) then { + _damage = 1; + }; + }; + + // Check if damage to body part is higher as damage torso + if (_bodyPartn == 1) exitwith { + if (_damageBodyPart >= ((_damageThreshold select 1) + _newDamage) && {(random(1) > 0.2)}) then { + _damage = 1; + }; + }; + // Check if damage to body part is higher as damage limbs + if (_damageBodyPart >= ((_damageThreshold select 2) + _newDamage) && {(random(1) > 0.95)}) then { + _damage = 1; + }; +} else { + _damage = 1; +}; + +_damage diff --git a/addons/medical/functions/fnc_onInitForUnit.sqf b/addons/medical/functions/fnc_onInitForUnit.sqf index 0a2cac9b6c..a0590b1d44 100644 --- a/addons/medical/functions/fnc_onInitForUnit.sqf +++ b/addons/medical/functions/fnc_onInitForUnit.sqf @@ -21,7 +21,7 @@ if !(_unit isKindOf "CAManBase") exitwith{}; _unit addEventhandler["handleDamage", { if ((missionNamespace getvariable[QGVAR(setting_AdvancedLevel), 0]) >= 0) then { - call FUNC(handleDamage); + call FUNC(onDamage); // cache damage and pass it to handleDamage function }; }];