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.
This commit is contained in:
Glowbal 2015-02-13 22:55:13 +01:00
parent a609d00730
commit 5b507ed9f5
7 changed files with 253 additions and 78 deletions

View File

@ -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;
// 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);
{
if ((_tourniquets select (_x select 2)) < 1) then {
// total bleeding ratio * percentage of injury left
_totalBloodLoss = _totalBloodLoss + ((_x select 4) * (_x select 3));
_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;
_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;

View File

@ -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;
};
};

View File

@ -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);

View File

@ -0,0 +1,136 @@
/*
* Author: KoffeinFlummi, Glowbal
* Cache a handleDamage call to execute it 3 frames later
*
* Arguments:
* 0: Unit That Was Hit <OBJECT>
* 1: Name Of Hit Selection <STRING>
* 2: Amount Of Damage <NUMBER>
* 3: Shooter <OBJECT>
* 4: Projectile <STRING>
* 5: Current damage to be returned <NUMBER>
*
* Return Value:
* <nil>
*
* 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];

View File

@ -1,35 +0,0 @@
/*
* Author: Glowbal
* Medium HandleDamage EH function.
*
* Arguments:
* 0: Unit That Was Hit <OBJECT>
* 1: Name Of Hit Selection <STRING>
* 2: Amount Of Damage <NUMBER>
* 3: Shooter <OBJECT>
* 4: Projectile <STRING>
* 5: Current damage to be returned <NUMBER>
* 6: Type of Damage <STRING>
*
* Return Value:
* Damage To Be Inflicted <NUMBER>
*
* 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;

View File

@ -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 {
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);
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);
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];

View File

@ -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));