From 5b507ed9f534cdf2de309f6777e52151a10f5626 Mon Sep 17 00:00:00 2001 From: Glowbal Date: Fri, 13 Feb 2015 22:55:13 +0100 Subject: [PATCH] Combined medium and advanced into one, with options to turn on other features. Changed wounds for a more diverse variation. Added handleDamage caching for advanced. --- addons/medical/functions/fnc_getBloodLoss.sqf | 35 +++-- addons/medical/functions/fnc_handleDamage.sqf | 14 +- .../functions/fnc_handleDamage_advanced.sqf | 6 +- .../functions/fnc_handleDamage_caching.sqf | 136 ++++++++++++++++++ .../functions/fnc_handleDamage_medium.sqf | 35 ----- .../functions/fnc_handleDamage_wounds.sqf | 103 ++++++++++--- .../functions/fnc_handleUnitVitals.sqf | 2 +- 7 files changed, 253 insertions(+), 78 deletions(-) create mode 100644 addons/medical/functions/fnc_handleDamage_caching.sqf delete mode 100644 addons/medical/functions/fnc_handleDamage_medium.sqf diff --git a/addons/medical/functions/fnc_getBloodLoss.sqf b/addons/medical/functions/fnc_getBloodLoss.sqf index a010f0fdde..3ea3c4f59a 100644 --- a/addons/medical/functions/fnc_getBloodLoss.sqf +++ b/addons/medical/functions/fnc_getBloodLoss.sqf @@ -22,24 +22,33 @@ */ #define DEFAULT_CARDIAC_OUTPUT 5.25 -private ["_totalBloodLoss","_tourniquets","_openWounds", "_value", "_cardiacOutput"]; +private ["_totalBloodLoss","_tourniquets","_openWounds", "_value", "_cardiacOutput", "_internalWounds"]; // TODO Only use this calculation if medium or higher, otherwise use vanilla calculations (for basic medical). _totalBloodLoss = 0; -_tourniquets = _this getvariable [QGVAR(tourniquets), [0,0,0,0,0,0]]; -_openWounds = _this getvariable [QGVAR(openWounds), []]; -//_cardiacOutput = [_this] call FUNC(getCardiacOutput); -{ - if ((_tourniquets select (_x select 2)) < 1) then { +// Advanced medical bloodloss handling +if (GVAR(level) >= 1) then { + _tourniquets = _this getvariable [QGVAR(tourniquets), [0,0,0,0,0,0]]; + _openWounds = _this getvariable [QGVAR(openWounds), []]; + //_cardiacOutput = [_this] call FUNC(getCardiacOutput); - _totalBloodLoss = _totalBloodLoss + ([BLOODLOSS_SMALL_WOUNDS, BLOODLOSS_MEDIUM_WOUNDS, BLOODLOSS_LARGE_WOUNDS] select (_x select 1)); - // (((BLOODLOSS_SMALL_WOUNDS * (_x select 0))) + ((BLOODLOSS_MEDIUM_WOUNDS * (_x select 1))) + ((BLOODLOSS_LARGE_WOUNDS * (_x select 2))) * (_cardiacOutput / DEFAULT_CARDIAC_OUTPUT)); - }; -}foreach _openWounds; + { + if ((_tourniquets select (_x select 2)) < 1) then { + // total bleeding ratio * percentage of injury left + _totalBloodLoss = _totalBloodLoss + ((_x select 4) * (_x select 3)); -// cap the blood loss to be no greater as the current cardiac output -//(_totalBloodLoss min _cardiacOutput); + // (((BLOODLOSS_SMALL_WOUNDS * (_x select 0))) + ((BLOODLOSS_MEDIUM_WOUNDS * (_x select 1))) + ((BLOODLOSS_LARGE_WOUNDS * (_x select 2))) * (_cardiacOutput / DEFAULT_CARDIAC_OUTPUT)); + }; + }foreach _openWounds; -_totalBloodLoss; \ No newline at end of file + _internalWounds = _this getvariable [QGVAR(internalWounds), []]; + { + _totalBloodLoss = _totalBloodLoss + ((_x select 4) * (_x select 3)); + }foreach _internalWounds; + + // cap the blood loss to be no greater as the current cardiac output + //(_totalBloodLoss min _cardiacOutput); +}; +_totalBloodLoss; diff --git a/addons/medical/functions/fnc_handleDamage.sqf b/addons/medical/functions/fnc_handleDamage.sqf index 95f8f851d8..cb96a15529 100644 --- a/addons/medical/functions/fnc_handleDamage.sqf +++ b/addons/medical/functions/fnc_handleDamage.sqf @@ -45,18 +45,18 @@ if (isNil QGVAR(level)) then { }; _damageReturn = (_this select 2); -_typeOfDamage = [_this select 4] call FUNC(getTypeOfDamage); - -if (GVAR(level) >= 0) then { - _damageReturn = (_this + [_damageReturn, _typeOfDamage]) call FUNC(handleDamage_basic); +if (GVAR(level) == 0) then { + _damageReturn = (_this + [_damageReturn]) call FUNC(handleDamage_basic); }; if (_damageReturn < 0.01) exitWith {0}; if (GVAR(level) >= 1) then { - _damageReturn = (_this + [_damageReturn, _typeOfDamage]) call FUNC(handleDamage_medium); - if (GVAR(level) >= 2) then { - _damageReturn = (_this + [_damageReturn, _typeOfDamage]) call FUNC(handleDamage_advanced); + [_unit, _selectionName, _damage, _source, _projectile, _damageReturn] call FUNC(handleDamage_caching); + + // TODO check if this should would have killed the unit.. + if (_damageReturn > 0.95) then { + _damageReturn = 0.95; }; }; diff --git a/addons/medical/functions/fnc_handleDamage_advanced.sqf b/addons/medical/functions/fnc_handleDamage_advanced.sqf index c345c4fbb1..0066829f60 100644 --- a/addons/medical/functions/fnc_handleDamage_advanced.sqf +++ b/addons/medical/functions/fnc_handleDamage_advanced.sqf @@ -25,7 +25,9 @@ _amountOfDamage = _this select 2; _sourceOfDamage = _this select 3; _typeOfProjectile = _this select 4; _returnDamage = _this select 5; -_typeOfDamage = _this select 6; //[_typeOfProjectile] call FUNC(getTypeOfDamage); +_typeOfDamage = [_typeOfProjectile] call FUNC(getTypeOfDamage); + +[_unit, _selectionName, _amountOfDamage, _typeOfProjectile, _typeOfDamage] call FUNC(handleDamage_wounds); if (GVAR(enableAirway)) then { [_unit,_selectionName,_amountOfDamage,_sourceOfDamage, _typeOfDamage] call FUNC(handleDamage_airway); @@ -36,4 +38,4 @@ if (GVAR(enableFractures)) then { if (GVAR(enableInternalBleeding)) then { [_unit,_selectionName,_amountOfDamage,_sourceOfDamage, _typeOfDamage] call FUNC(handleDamage_internalInjuries); }; -_returnDamage; \ No newline at end of file +_returnDamage; diff --git a/addons/medical/functions/fnc_handleDamage_caching.sqf b/addons/medical/functions/fnc_handleDamage_caching.sqf new file mode 100644 index 0000000000..f4b1e56410 --- /dev/null +++ b/addons/medical/functions/fnc_handleDamage_caching.sqf @@ -0,0 +1,136 @@ +/* + * Author: KoffeinFlummi, Glowbal + * Cache a handleDamage call to execute it 3 frames later + * + * Arguments: + * 0: Unit That Was Hit + * 1: Name Of Hit Selection + * 2: Amount Of Damage + * 3: Shooter + * 4: Projectile + * 5: Current damage to be returned + * + * Return Value: + * + * + * Public: No + */ + +#include "script_component.hpp" + +private ["_unit", "_selectionName","_damage", "_source","_projectile","_hitSelections","_hitPoints","_newDamage","_cache_hitpoints","_cache_projectiles","_cache_params","_cache_damages"]; +_unit = _this select 0; +_selectionName = _this select 1; +_damage = _this select 2; +_source = _this select 3; +_projectile = _this select 4; +_returnDamage = _this select 5; + +_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_damageCaching), -3]) + 2) then { + _unit setVariable [QGVAR(frameNo_damageCaching), diag_frameno]; + + // handle the cached damages 3 frames later + [{ + private ["_args","_unit","_frameNo"]; + _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_advanced); + }; + }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 { + private ["_index","_otherDamage"]; + // 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 { + private ["_hitPoint", "_restore"]; + // 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_handleDamage_medium.sqf b/addons/medical/functions/fnc_handleDamage_medium.sqf deleted file mode 100644 index 78e0bca011..0000000000 --- a/addons/medical/functions/fnc_handleDamage_medium.sqf +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Author: Glowbal - * Medium HandleDamage EH function. - * - * Arguments: - * 0: Unit That Was Hit - * 1: Name Of Hit Selection - * 2: Amount Of Damage - * 3: Shooter - * 4: Projectile - * 5: Current damage to be returned - * 6: Type of Damage - * - * Return Value: - * Damage To Be Inflicted - * - * Public: No - */ - -#include "script_component.hpp" - -private ["_unit","_selectionName","_amountOfDamage","_sourceOfDamage","_typeOfProjectile","_typeOfDamage"]; -_unit = _this select 0; -_selectionName = _this select 1; -_amountOfDamage = _this select 2; -_sourceOfDamage = _this select 3; -_typeOfProjectile = _this select 4; -_returnDamage = _this select 5; -_typeOfDamage = _this select 6; //[_typeOfProjectile] call FUNC(getTypeOfDamage); - -if (GVAR(enableAirway)) then { - [_unit,_selectionName,_amountOfDamage,_sourceOfDamage, _typeOfDamage] call FUNC(handleDamage_wounds); -}; - -_returnDamage; diff --git a/addons/medical/functions/fnc_handleDamage_wounds.sqf b/addons/medical/functions/fnc_handleDamage_wounds.sqf index 73b9e47e67..0c14274197 100644 --- a/addons/medical/functions/fnc_handleDamage_wounds.sqf +++ b/addons/medical/functions/fnc_handleDamage_wounds.sqf @@ -16,64 +16,127 @@ */ #include "script_component.hpp" + +#define RANDOM_BODY_PART round(random(6)) +#define RANDOM_OPEN_WOUND (1 + round(random(2))) #define ADD_INJURY(BODYPART,TYPE,AMOUNT) _woundID = 1; \ _amountOf = count _openWounds; \ if (_amountOf > 0) then { _woundID = (_openWounds select (_amountOf - 1) select 0) + 1; }; \ for "_i" from 1 to AMOUNT /* step +1 */ do { \ - _openWounds pushback [_woundID, _woundType, _bodyPartn, 1 /* percentage treated */]; \ + _openWounds pushback [_woundID, _woundType, BODYPART, 1 /* percentage treated */, FIND_BLEEDING_RATE(TYPE)]; \ _woundID = _woundID + 1; \ }; -private ["_unit", "_selectionName", "_amountOfDamage", "_sourceOfDamage", "_typeOfDamage", "_bodyPartn", "_woundType"]; +private ["_unit", "_selectionName", "_damage", "_typeOfProjectile", "_typeOfDamage", "_bodyPartn", "_woundType"]; _unit = _this select 0; _selectionName = _this select 1; -_amountOfDamage = _this select 2; -_sourceOfDamage = _this select 3; +_damage = _this select 2; +_typeOfProjectile = _this select 3; _typeOfDamage = _this select 4; _bodyPartn = [_selectionName] call FUNC(selectionNameToNumber); _woundType = 1; -if (_amountOfDamage > 0.05) then { +if (_damage > 0.05) then { private ["_wounds", "_woundID", "_amountOf"]; _openWounds = _unit getvariable[QGVAR(openWounds), []]; + // TODO specify openWounds based off typeOfInjury details better. - switch (toLower _typeOfInjury) do { + switch (toLower _typeOfInjury) do { case "bullet": { - ADD_INJURY(_bodyPartn, round(random(2)), 1); + if (_damage < 0.1) exitwith { + if (random(1) => 0.5) then { + if (random(1) => 0.5) then { + ADD_INJURY(_bodyPartn, SCRATCH, 1); + } else { + ADD_INJURY(_bodyPartn, BRUISES, 1); // didn't do much damage, so the skin got only bruised. + }; + } else { + ADD_INJURY(_bodyPartn, GRAZE_WOUND, 1); + }; + }; + // TODO base upon caliber of the round and damage done, using _typeOfProjectile and CfgAmmo class. + switch (true) do { + case (_damage >= 1.2): {ADD_INJURY(_bodyPartn, LARGE_OPEN_WOUND, 1);}; + case (_damage >= 0.7): {ADD_INJURY(_bodyPartn, MEDIUM_OPEN_WOUND, 1);}; + default {ADD_INJURY(_bodyPartn, MINOR_OPEN_WOUND, 1);}; + }; }; case "grenade": { - ADD_INJURY(_bodyPartn, round(random(2)), 1); + if (_damage < 0.1) exitwith { + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, BRUISES, 1); + } else { + ADD_INJURY(RANDOM_BODY_PART, GRAZE_WOUND, 1); + }; + }; for "_i" from 0 to round(random(3)) /* step +1 */ do { - ADD_INJURY(round(random(6)), round(random(2)), 1); + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, RANDOM_OPEN_WOUND, 1); + } else { + ADD_INJURY(RANDOM_BODY_PART, SCHRAPNEL_WOUND, 1); + }; }; }; case "explosive": { - ADD_INJURY(_bodyPartn, round(random(2)), 1); + if (_damage < 0.1) exitwith { + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, BRUISES, 1); + } else { + ADD_INJURY(RANDOM_BODY_PART, GRAZE_WOUND, 1); + }; + }; + + ADD_INJURY(RANDOM_BODY_PART, RANDOM_OPEN_WOUND, 1); for "_i" from 0 to round(random(4)) /* step +1 */ do { - ADD_INJURY(round(random(6)), round(random(2)), 1); + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, SCHRAPNEL_WOUND, 1); + }; + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, RANDOM_OPEN_WOUND, 1); + }: }; }; case "shell": { - ADD_INJURY(_bodyPartn, round(random(2)), 1); - for "_i" from 0 to round(random(5)) /* step +1 */ do { - ADD_INJURY(round(random(6)), round(random(2)), 1); + if (_damage < 0.1) exitwith { + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, BRUISES, 1); + } else { + ADD_INJURY(RANDOM_BODY_PART, GRAZE_WOUND, 1); + }; }; + + ADD_INJURY(RANDOM_BODY_PART, RANDOM_OPEN_WOUND, 1); + for "_i" from 0 to round(random(5)) /* step +1 */ do { + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, SCHRAPNEL_WOUND, 1); + }; + if (random(1) => 0.5) then { + ADD_INJURY(RANDOM_BODY_PART, RANDOM_OPEN_WOUND, 1); + }: + }; + }; case "backblast": { - if (random(1)>=0.5) then{ - ADD_INJURY(_bodyPartn, round(random(2)), 1); + if (random(1)>=0.5) then { + ADD_INJURY(RANDOM_BODY_PART, BRUISES, 1); }; }; case "unknown": { - ADD_INJURY(_bodyPartn, round(random(1)), 1); + ADD_INJURY(_bodyPartn, RANDOM_OPEN_WOUND, 1); }; case "vehiclecrash": { - if (random(1)>=0.5) then{ - ADD_INJURY(_bodyPartn, round(random(1)), 1); + if (random(1)>=0.5) then { + ADD_INJURY(_bodyPartn, RANDOM_OPEN_WOUND, 1); + }; + for "_i" from 0 to round(random(5)) /* step +1 */ do { + if (random(_damage) => 0.25) then { + ADD_INJURY(RANDOM_BODY_PART, BRUISES, 1); + }; }; }; default { - ADD_INJURY(_bodyPartn, round(random(1)), 1); + ADD_INJURY(_bodyPartn, RANDOM_OPEN_WOUND, 1); + // TODO allow third party to handle this instead ? }; }; _unit setvariable [GVAR(openWounds), _openWounds, true]; diff --git a/addons/medical/functions/fnc_handleUnitVitals.sqf b/addons/medical/functions/fnc_handleUnitVitals.sqf index 9e5a5400c4..b666e78914 100644 --- a/addons/medical/functions/fnc_handleUnitVitals.sqf +++ b/addons/medical/functions/fnc_handleUnitVitals.sqf @@ -68,7 +68,7 @@ if ([_unit] call EFUNC(common,isAwake)) then { }; // handle advanced medical, with vitals -if ((missionNamespace getvariable[QGVAR(setting_AdvancedLevel), 0]) > 0) exitwith { +if ((missionNamespace getvariable[QGVAR(level), 0]) > 0) exitwith { // Set the vitals _heartRate = (_unit getvariable [QGVAR(heartRate), 0]) + ([_unit] call FUNC(getHeartRateChange));