From aadb2899627bc0597dc9e716dec38fa636e861b0 Mon Sep 17 00:00:00 2001 From: Glowbal Date: Sat, 7 Feb 2015 23:55:48 +0100 Subject: [PATCH] Added initial rewrite of CMS vitals script for medium and advanced medical. --- addons/medical/functions/fnc_getBloodLoss.sqf | 42 ++++++ .../functions/fnc_getBloodPressure.sqf | 31 +++++ .../functions/fnc_getBloodVolumeChange.sqf | 54 ++++++++ .../functions/fnc_getCardiacOutput.sqf | 25 ++++ .../functions/fnc_getHeartRateChange.sqf | 82 +++++++++++ .../functions/fnc_handleUnitVitals.sqf | 127 ++++++++++++++++++ 6 files changed, 361 insertions(+) create mode 100644 addons/medical/functions/fnc_getBloodLoss.sqf create mode 100644 addons/medical/functions/fnc_getBloodPressure.sqf create mode 100644 addons/medical/functions/fnc_getBloodVolumeChange.sqf create mode 100644 addons/medical/functions/fnc_getCardiacOutput.sqf create mode 100644 addons/medical/functions/fnc_getHeartRateChange.sqf create mode 100644 addons/medical/functions/fnc_handleUnitVitals.sqf diff --git a/addons/medical/functions/fnc_getBloodLoss.sqf b/addons/medical/functions/fnc_getBloodLoss.sqf new file mode 100644 index 0000000000..9af27ea16d --- /dev/null +++ b/addons/medical/functions/fnc_getBloodLoss.sqf @@ -0,0 +1,42 @@ +/** + * fn_getBloodLoss.sqf + * @Descr: Calculate the total blood loss of a unit. + * @Author: Glowbal + * + * @Arguments: [unit OBJECT] + * @Return: NUMBER Total blood loss of unit + * @PublicAPI: true + */ + +#include "script_component.hpp" + +#define BLOODLOSS_SMALL_WOUNDS 0.025 +#define BLOODLOSS_MEDIUM_WOUNDS 0.05 +#define BLOODLOSS_LARGE_WOUNDS 0.1 + +/** +* The default cardiac output when all stats are set to normal is 5.25. +*/ +#define DEFAULT_CARDIAC_OUTPUT 5.25 + +private ["_totalBloodLoss","_tourniquets","_openWounds", "_value", "_cardiacOutput"]; + + +// 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 { + + _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; + +// cap the blood loss to be no greater as the current cardiac output +//(_totalBloodLoss min _cardiacOutput); + +_totalBloodLoss; \ No newline at end of file diff --git a/addons/medical/functions/fnc_getBloodPressure.sqf b/addons/medical/functions/fnc_getBloodPressure.sqf new file mode 100644 index 0000000000..980d95918d --- /dev/null +++ b/addons/medical/functions/fnc_getBloodPressure.sqf @@ -0,0 +1,31 @@ +/** + * fn_getBloodPressure.sqf + * @Descr: Calculate the current blood pressure of a unit. + * @Author: Glowbal + * + * @Arguments: [unit OBJECT (The unit to get the blood pressure from.)] + * @Return: ARRAY Blood Pressure. Format [low NUMBER, high NUMBER] + * @PublicAPI: true + */ + +#include "script_component.hpp" + +/* + Value is taken because with cardic output and resistance at default values, it will put blood pressure High at 120. +*/ +#define MODIFIER_BP_HIGH 0.229 + +/* + Value is taken because with cardic output and resistance at default values, it will put blood pressure Low at 80. +*/ +#define MODIFIER_BP_LOW 0.1524 + +private ["_unit", "_bloodPressureLow", "_bloodPressureHigh", "_cardiacOutput", "_resistance"]; +_unit = _this select 0; +_cardiacOutput = [_unit] call FUNC(getCardiacOutput); +_resistance = _unit getvariable [QGVAR(peripheralResistance), 100]; + +_bloodPressureHigh = (_cardiacOutput * MODIFIER_BP_HIGH) * _resistance; +_bloodPressureLow = (_cardiacOutput * MODIFIER_BP_LOW) * _resistance; + +[_bloodPressureLow, _bloodPressureHigh]; diff --git a/addons/medical/functions/fnc_getBloodVolumeChange.sqf b/addons/medical/functions/fnc_getBloodVolumeChange.sqf new file mode 100644 index 0000000000..31bda33cce --- /dev/null +++ b/addons/medical/functions/fnc_getBloodVolumeChange.sqf @@ -0,0 +1,54 @@ +/** + * fn_getBloodVolumeChange.sqf + * @Descr: Calculates the blood volume change and decreases the IVs given to the unit. + * @Author: Glowbal + * + * @Arguments: [] + * @Return: NUMBER Bloodvolume change + * @PublicAPI: false + */ + +#include "script_component.hpp" + +/* + IV Change per second calculation: + 250ml should take 60 seconds to fill. 250/60 = 4.166. +*/ +#define IV_CHANGE_PER_SECOND -4.166 + +/* + Blood Change per second calculation for IVs: + 250ml should take 60 seconds to fill in. Total blood volume is 7000ml = 100%. + 7000/100 = 70 = 1% + 250 / 70 = 3.571428571% + 3.571428571 / 60 = 0.0595% per second. +*/ +#define BLOOD_CHANGE_PER_SECOND 0.0595 + + + +private ["_unit","_bloodVolume","_bloodVolumeChange", "_ivVolume"]; +_unit = _this select 0; + +_bloodVolume = _unit getvariable [QGVAR(bloodVolume), 100]; +_bloodVolumeChange = -(_unit call FUNC(getBloodLoss)); + +if (_bloodVolume < 100.0) then { + if ((_unit getvariable [QGVAR(salineIVVolume), 0]) > 0) then { + _bloodVolumeChange = _bloodVolumeChange + BLOOD_CHANGE_PER_SECOND; + _ivVolume = (_unit getvariable [QGVAR(salineIVVolume), 0]) + IV_CHANGE_PER_SECOND; + _unit setvariable [QGVAR(salineIVVolume),_ivVolume]; + }; + if ((_unit getvariable [QGVAR(plasmaIVVolume), 0]) > 0) then { + _bloodVolumeChange = _bloodVolumeChange + BLOOD_CHANGE_PER_SECOND; + _ivVolume = (_unit getvariable [QGVAR(plasmaIVVolume), 0]) + IV_CHANGE_PER_SECOND; + _unit setvariable [QGVAR(plasmaIVVolume),_ivVolume]; + }; + if ((_unit getvariable [QGVAR(bloodIVVolume), 0]) > 0) then { + _bloodVolumeChange = _bloodVolumeChange + BLOOD_CHANGE_PER_SECOND; + _ivVolume = (_unit getvariable [QGVAR(bloodIVVolume), 0]) + IV_CHANGE_PER_SECOND; + _unit setvariable [QGVAR(bloodIVVolume),_ivVolume]; + }; +}; + +_bloodVolumeChange; diff --git a/addons/medical/functions/fnc_getCardiacOutput.sqf b/addons/medical/functions/fnc_getCardiacOutput.sqf new file mode 100644 index 0000000000..e45cd0ab61 --- /dev/null +++ b/addons/medical/functions/fnc_getCardiacOutput.sqf @@ -0,0 +1,25 @@ +/** + * fn_getCardiacOutput.sqf + * @Descr: Get the cardiac output from the Heart, based on current Heart Rate and Blood Volume. + * @Author: Glowbal + * + * @Arguments: [unit OBJECT] + * @Return: NUMBER Current cardiac output. + * @PublicAPI: true + */ + +#include "script_component.hpp" + +/* + Cardiac output (Q or or CO ) is the volume of blood being pumped by the heart, in particular by a left or right ventricle in the time interval of one minute. CO may be measured in many ways, for example dm3/min (1 dm3 equals 1 litre). + + Source: http://en.wikipedia.org/wiki/Cardiac_output +*/ + +// to limit the amount of complex calculations necessary, we take a set modifier to calculate Stroke Volume. +#define MODIFIER_CARDIAC_OUTPUT 19.04761 + +private "_unit"; +_unit = _this select 0; + +((_unit getvariable [QGVAR(bloodVolume), 100])/MODIFIER_CARDIAC_OUTPUT) + ((_unit getvariable [QGVAR(heartRate), 80])/80-1); diff --git a/addons/medical/functions/fnc_getHeartRateChange.sqf b/addons/medical/functions/fnc_getHeartRateChange.sqf new file mode 100644 index 0000000000..a3c0c239cd --- /dev/null +++ b/addons/medical/functions/fnc_getHeartRateChange.sqf @@ -0,0 +1,82 @@ +/** + * fn_getHeartRateChange.sqf + * @Descr: N/A + * @Author: Glowbal + * + * @Arguments: [] + * @Return: void + * @PublicAPI: false + */ + +#include "script_component.hpp" + +#define HEART_RATE_MODIFIER 0.02 + +private ["_unit", "_heartRate", "_hrIncrease", "_bloodLoss", "_time", "_values", "_adjustment", "_adjustments", "_additionalIncrease", "_change", "_callBack", "_bloodVolume"]; +_unit = _this select 0; +_hrIncrease = 0; +if (!(_unit getvariable [QGVAR(inCardiacArrest),false])) then { + _heartRate = _unit getvariable [QGVAR(heartRate), 80]; + _bloodLoss = _unit call FUNC(getBloodLoss); + + _adjustment = _unit getvariable [QGVAR(heartRateAdjustments), []]; + { + _values = (_x select 0); + if (abs _values > 0) then { + _time = (_x select 1); + _callBack = _x select 2; + if (_time <= 0) then { + _time = 1; + }; + _change = (_values / _time); + _hrIncrease = _hrIncrease + _change; + + if ( (_time - 1) < 0) then { + _time = 0; + _adjustment set [_foreachIndex, ObjNull]; + [_unit] call _callBack; + } else { + _time = _time - 1; + _adjustment set [_foreachIndex, [_values - _change, _time]]; + }; + } else { + _adjustment set [_foreachIndex, ObjNull]; + }; + + }foreach _adjustment; + _adjustment = _adjustment - [ObjNull]; + _unit setvariable [QGVAR(heartRateAdjustments), _adjustment]; + + _bloodVolume = _unit getvariable [QGVAR(bloodVolume), 100]; + if (_bloodVolume > 75) then { + if (_bloodLoss >0.0) then { + if (_bloodLoss <0.5) then { + if (_heartRate < 126) then { + _hrIncrease = _hrIncrease + 0.05; + }; + } else { + if (_bloodLoss < 1) then { + if (_heartRate < 161) then { + _hrIncrease = _hrIncrease + 0.1; + }; + } else { + if (_heartRate < 220) then { + _hrIncrease = _hrIncrease + 0.15; + }; + }; + }; + } else { + // Stabalize it + if (_heartRate < (60 + round(random(10)))) then { + _hrIncrease = _hrIncrease + HEART_RATE_MODIFIER; + } else { + if (_heartRate > (77 + round(random(10)))) then { + _hrIncrease = _hrIncrease - HEART_RATE_MODIFIER; + }; + }; + }; + } else { + _hrIncrease = _hrIncrease - HEART_RATE_MODIFIER; + }; +}; +_hrIncrease \ No newline at end of file diff --git a/addons/medical/functions/fnc_handleUnitVitals.sqf b/addons/medical/functions/fnc_handleUnitVitals.sqf new file mode 100644 index 0000000000..745d4bb28d --- /dev/null +++ b/addons/medical/functions/fnc_handleUnitVitals.sqf @@ -0,0 +1,127 @@ +/** + * fn_handleUnitVitals.sqf + * @Descr: Updates the vitals. Is expected to be called every second. + * @Author: Glowbal + * + * @Arguments: [unit OBJECT] + * @Return: void + * @PublicAPI: false + */ + +#include "script_component.hpp" + +private ["_unit", "_heartRate","_bloodPressure","_bloodVolume","_painStatus"]; +_unit = _this select 0; + +_bloodVolume = (_unit getvariable [QGVAR(bloodVolume), 0]) + ([_unit] call FUNC(getBloodVolumeChange)); +if (_bloodVolume <= 0) then { + _bloodVolume = 0; +}; +_unit setvariable [QGVAR(bloodVolume), _bloodVolume]; + +// Set variables for synchronizing information across the net +if (_bloodVolume < 90) then { + if !(_unit getvariable [QGVAR(hasLostBlood), false]) then { + _unit setvariable [QGVAR(hasLostBlood), true, true]; + }; +} else { + if (_unit getvariable [QGVAR(hasLostBlood),false]) then { + _unit setvariable [QGVAR(hasLostBlood), false, true]; + }; +}; + +if ((_unit call FUNC(getBloodLoss)) > 0) then { + if !(_unit getvariable [QGVAR(isBleeding), false]) then { + _unit setvariable [QGVAR(isBleeding), true, true]; + }; +} else { + if (_unit getvariable [QGVAR(isBleeding), false]) then { + _unit setvariable [QGVAR(isBleeding), false, true]; + }; +}; + +_painStatus = _unit getvariable [QGVAR(amountOfPain), 0]; +if (_painStatus > 0) then { + if !(_unit getvariable [QGVAR(hasPain), false]) then { + _unit setvariable [QGVAR(hasPain), true, true]; + }; +} else { + if (_unit getvariable [QGVAR(hasPain), false]) then { + _unit setvariable [QGVAR(hasPain), false, true]; + }; +}; + + +if (_bloodVolume < 30) exitwith { + // [_unit] call FUNC(setDead); +}; + +if ([_unit] call EFUNC(common,isAwake)) then { + if (_bloodVolume < 60) then { + if (random(1) > 0.9) then { + //[_unit] call FUNC(setUnconsciousState); + }; + }; +}; + +// handle advanced medical, with vitals +if ((missionNamespace getvariable[QGVAR(setting_AdvancedLevel), 0]) > 0) exitwith { + + // Set the vitals + _heartRate = (_unit getvariable [QGVAR(heartRate), 0]) + ([_unit] call FUNC(getHeartRateChange)); + _unit setvariable [QGVAR(heartRate), _heartRate]; + + _bloodPressure = [_unit] call FUNC(getBloodPressure); + _unit setvariable [QGVAR(bloodPressure), _bloodPressure]; + + // Handle airway + if (GVAR(setting_allowAirwayInjuries)) then { + _airwayStatus = _unit getvariable [QGVAR(airwayStatus), 100]; + if (((_unit getvariable [QGVAR(airwayOccluded), false]) || (_unit getvariable [QGVAR(airwayCollapsed), false])) && !((_unit getvariable [QGVAR(airwaySecured), false]))) then { + if (_airwayStatus >= 0.5) then { + _unit setvariable [QGVAR(airwayStatus), _airwayStatus - 0.5]; + }; + } else { + if !((_unit getvariable [QGVAR(airwayOccluded), false]) || (_unit getvariable [QGVAR(airwayCollapsed), false])) then { + if (_airwayStatus <= 98.5) then { + _unit setvariable [QGVAR(airwayStatus), _airwayStatus + 1.5]; + }; + }; + }; + }; + + // Check vitals for medical status + // TODO check for in revive state instead of variable + // TODO Implement cardiac arrest. +/* _bloodPressureL = _bloodPressure select 0; + _bloodPressureH = _bloodPressure select 1; + + if (!(_unit getvariable [QGVAR(inCardiacArrest),false])) then { + if (_heartRate < 10 || _bloodPressureH < 30 || _bloodVolume < 20) then { + [_unit] call FUNC(setUnconsciousState); // safety check to ensure unconsciousness for units if they are not dead already. + }; + + if (_bloodPressureH > 260) then { + if (random(1) > 0.7) then { + [_unit] call FUNC(setCardiacArrest); + }; + }; + if (_bloodPressureL < 40 && _heartRate > 190) then { + if (random(1) > 0.7) then { + [_unit] call FUNC(setCardiacArrest); + }; + }; + if (_bloodPressureH > 145 && _heartRate > 150) then { + if (random(1) > 0.7) then { + [_unit] call FUNC(setCardiacArrest); + }; + }; + if (_heartRate > 200) then { + [_unit] call FUNC(setCardiacArrest); + }; + + if (_heartRate < 20) then { + [_unit] call FUNC(setCardiacArrest); + }; + };*/ +};