2023-09-12 18:58:10 +00:00
|
|
|
#include "..\script_component.hpp"
|
2016-12-06 09:59:29 +00:00
|
|
|
/*
|
|
|
|
* Author: Glowbal, commy2
|
|
|
|
* Handling of the open wounds & injuries upon the handleDamage eventhandler.
|
|
|
|
*
|
|
|
|
* Arguments:
|
|
|
|
* 0: Unit That Was Hit <OBJECT>
|
2022-02-17 20:03:12 +00:00
|
|
|
* 1: Damage done to each body part <ARRAY>
|
|
|
|
* 2: Type of the damage done <STRING>
|
2016-12-06 09:59:29 +00:00
|
|
|
*
|
|
|
|
* Return Value:
|
|
|
|
* None
|
|
|
|
*
|
2019-03-30 16:07:54 +00:00
|
|
|
* Example:
|
2022-02-20 23:23:56 +00:00
|
|
|
* [player, [[0.5, "Body", 1]], "bullet"] call ace_medical_damage_fnc_woundsHandlerBase
|
2019-03-30 16:07:54 +00:00
|
|
|
*
|
2016-12-06 09:59:29 +00:00
|
|
|
* Public: No
|
|
|
|
*/
|
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
params ["_unit", "_allDamages", "_typeOfDamage"];
|
2022-02-20 23:23:56 +00:00
|
|
|
TRACE_3("woundsHandlerBase",_unit,_allDamages,_typeOfDamage);
|
2016-12-06 09:59:29 +00:00
|
|
|
|
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
if !(_typeOfDamage in GVAR(damageTypeDetails)) then {
|
|
|
|
WARNING_1("damage type not found",_typeOfDamage);
|
2019-03-21 18:33:51 +00:00
|
|
|
_typeOfDamage = "unknown";
|
|
|
|
};
|
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
GVAR(damageTypeDetails) get _typeOfDamage params ["_thresholds", "_selectionSpecific", "", "_damageWoundDetails"];
|
2016-12-06 09:59:29 +00:00
|
|
|
|
|
|
|
// Administration for open wounds and ids
|
2019-06-22 18:36:27 +00:00
|
|
|
private _openWounds = GET_OPEN_WOUNDS(_unit);
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
private _createdWounds = false;
|
2019-05-12 04:13:59 +00:00
|
|
|
private _updateDamageEffects = false;
|
2016-12-06 09:59:29 +00:00
|
|
|
private _painLevel = 0;
|
2022-02-17 20:03:12 +00:00
|
|
|
private _criticalDamage = false;
|
2016-12-08 10:38:43 +00:00
|
|
|
private _bodyPartDamage = _unit getVariable [QEGVAR(medical,bodyPartDamage), [0,0,0,0,0,0]];
|
2017-06-08 18:50:38 +00:00
|
|
|
private _bodyPartVisParams = [_unit, false, false, false, false]; // params array for EFUNC(medical_engine,updateBodyPartVisuals);
|
2019-05-12 04:13:59 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// process wounds separately for each body part hit
|
|
|
|
{ // forEach _allDamages
|
|
|
|
_x params ["_damage", "_bodyPart"];
|
2024-03-07 21:08:13 +00:00
|
|
|
_bodyPart = toLowerANSI _bodyPart;
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// silently ignore structural damage
|
|
|
|
if (_bodyPart == "#structural") then {continue};
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// Convert the selectionName to a number and ensure it is a valid selection.
|
2023-06-24 05:11:56 +00:00
|
|
|
private _bodyPartNToAdd = ALL_BODY_PARTS find _bodyPart;
|
2022-02-17 20:03:12 +00:00
|
|
|
if (_bodyPartNToAdd < 0) then {
|
|
|
|
ERROR_1("invalid body part %1",_bodyPart);
|
|
|
|
continue
|
|
|
|
};
|
2018-05-10 16:44:02 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// determine how many wounds to create
|
|
|
|
private _nWounds = [_damage, _thresholds, true] call FUNC(interpolatePoints);
|
|
|
|
if (_nWounds < 1) then {
|
|
|
|
TRACE_2("Damage created zero wounds",_damage,_typeOfDamage);
|
|
|
|
continue
|
|
|
|
};
|
|
|
|
private _dmgPerWound = _damage/_nWounds;
|
|
|
|
|
|
|
|
// find the available injuries for this damage type and damage amount
|
|
|
|
private _weightedWoundTypes = [];
|
|
|
|
{
|
|
|
|
private _weighting = _x select 1;
|
|
|
|
private _woundWeight = [_dmgPerWound, _weighting] call FUNC(interpolatePoints);
|
|
|
|
_weightedWoundTypes pushBack _x;
|
|
|
|
_weightedWoundTypes pushBack _woundWeight;
|
|
|
|
} forEach _damageWoundDetails;
|
|
|
|
|
|
|
|
if (_weightedWoundTypes isEqualTo []) then {
|
|
|
|
TRACE_2("No valid wounds",_damage,_typeOfDamage);
|
|
|
|
continue
|
|
|
|
};
|
2018-05-10 16:44:02 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
for "_i" from 1 to _nWounds do {
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// Select the injury we are going to add
|
|
|
|
selectRandomWeighted _weightedWoundTypes params ["_woundTypeToAdd", "", "_dmgMultiplier", "_bleedMultiplier", "_sizeMultiplier", "_painMultiplier", "_fractureMultiplier"];
|
|
|
|
if (isNil "_woundTypeToAdd") then {
|
|
|
|
WARNING_4("No valid wound types",_damage,_dmgPerWound,_typeOfDamage,_bodyPart);
|
|
|
|
continue
|
|
|
|
};
|
|
|
|
GVAR(woundDetails) get _woundTypeToAdd params ["","_injuryBleedingRate","_injuryPain","_causeLimping","_causeFracture"];
|
|
|
|
private _woundClassIDToAdd = GVAR(woundClassNames) find _woundTypeToAdd;
|
2023-06-24 05:11:56 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// Add a bit of random variance to wounds
|
|
|
|
private _woundDamage = _dmgPerWound * _dmgMultiplier * random [0.9, 1, 1.1];
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
_bodyPartDamage set [_bodyPartNToAdd, (_bodyPartDamage select _bodyPartNToAdd) + _woundDamage];
|
|
|
|
_bodyPartVisParams set [[1,2,3,3,4,4] select _bodyPartNToAdd, true]; // Mark the body part index needs updating
|
2019-06-10 16:23:21 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// Anything above this value is guaranteed worst wound possible
|
|
|
|
private _worstDamage = 2;
|
2019-06-10 16:23:21 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
#define LARGE_WOUND_THRESHOLD 0.5
|
2017-06-05 16:51:53 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// Config specifies bleeding and pain for worst possible wound
|
|
|
|
// Worse wound correlates to higher damage, damage is not capped at 1
|
|
|
|
private _woundSize = linearConversion [0.1, _worstDamage, _woundDamage * _sizeMultiplier, LARGE_WOUND_THRESHOLD^3, 1, true];
|
2023-06-24 05:11:56 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
private _pain = _woundSize * _painMultiplier * _injuryPain;
|
|
|
|
_painLevel = _painLevel + _pain;
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
private _bleeding = _woundSize * _bleedMultiplier * _injuryBleedingRate;
|
2016-12-14 17:04:56 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// large wounds are > LARGE_WOUND_THRESHOLD
|
|
|
|
// medium is > LARGE_WOUND_THRESHOLD^2
|
|
|
|
// minor is > LARGE_WOUND_THRESHOLD^3
|
|
|
|
private _category = 0 max (2 - floor (ln _woundSize / ln LARGE_WOUND_THRESHOLD)) min 2;
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
private _classComplex = 10 * _woundClassIDToAdd + _category;
|
|
|
|
|
2023-06-24 05:11:56 +00:00
|
|
|
// Create a new injury. Format [0:classComplex, 1:amountOf, 2:bleedingRate, 3:woundDamage]
|
|
|
|
private _injury = [_classComplex, 1, _bleeding, _woundDamage];
|
2022-02-17 20:03:12 +00:00
|
|
|
|
2023-06-24 05:11:56 +00:00
|
|
|
if (_bodyPart isEqualTo "head" || {_bodyPart isEqualTo "body" && {_woundDamage > PENETRATION_THRESHOLD}}) then {
|
2022-02-17 20:03:12 +00:00
|
|
|
_criticalDamage = true;
|
|
|
|
};
|
|
|
|
if ([_unit, _bodyPartNToAdd, _bodyPartDamage, _woundDamage] call FUNC(determineIfFatal)) then {
|
|
|
|
if (!isPlayer _unit || {random 1 < EGVAR(medical,deathChance)}) then {
|
|
|
|
TRACE_1("determineIfFatal returned true",_woundDamage);
|
|
|
|
[QEGVAR(medical,FatalInjury), _unit] call CBA_fnc_localEvent;
|
2019-10-08 15:45:09 +00:00
|
|
|
};
|
2022-02-17 20:03:12 +00:00
|
|
|
};
|
2019-05-12 04:13:59 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
#ifdef DEBUG_MODE_FULL
|
|
|
|
systemChat format["%1, damage: %2, peneration: %3, bleeding: %4, pain: %5", _bodyPart, _woundDamage toFixed 2, _woundDamage > PENETRATION_THRESHOLD, _bleeding toFixed 3, _pain toFixed 3];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (true) do {
|
|
|
|
case (
|
|
|
|
_causeFracture
|
|
|
|
&& {EGVAR(medical,fractures) > 0}
|
|
|
|
&& {_bodyPartNToAdd > 1}
|
|
|
|
&& {_woundDamage > FRACTURE_DAMAGE_THRESHOLD}
|
|
|
|
&& {random 1 < (_fractureMultiplier * EGVAR(medical,fractureChance))}
|
|
|
|
): {
|
|
|
|
private _fractures = GET_FRACTURES(_unit);
|
|
|
|
_fractures set [_bodyPartNToAdd, 1];
|
|
|
|
_unit setVariable [VAR_FRACTURES, _fractures, true];
|
|
|
|
|
|
|
|
[QEGVAR(medical,fracture), [_unit, _bodyPartNToAdd]] call CBA_fnc_localEvent;
|
|
|
|
TRACE_1("Limb fracture",_bodyPartNToAdd);
|
|
|
|
|
|
|
|
_updateDamageEffects = true;
|
2016-12-06 09:59:29 +00:00
|
|
|
};
|
2022-02-17 20:03:12 +00:00
|
|
|
case (
|
|
|
|
_causeLimping
|
|
|
|
&& {EGVAR(medical,limping) > 0}
|
|
|
|
&& {_bodyPartNToAdd > 3}
|
|
|
|
&& {_woundDamage > LIMPING_DAMAGE_THRESHOLD}
|
|
|
|
): {
|
|
|
|
_updateDamageEffects = true;
|
|
|
|
};
|
|
|
|
};
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
// if possible merge into existing wounds
|
|
|
|
private _createNewWound = true;
|
2023-06-24 05:11:56 +00:00
|
|
|
private _existingWounds = _openWounds getOrDefault [_bodyPart, [], true];
|
2022-02-17 20:03:12 +00:00
|
|
|
{
|
2023-06-24 05:11:56 +00:00
|
|
|
_x params ["_classID", "_oldAmountOf", "_oldBleeding", "_oldDamage"];
|
2022-02-17 20:03:12 +00:00
|
|
|
if (
|
|
|
|
(_classComplex == _classID) &&
|
2023-06-24 05:11:56 +00:00
|
|
|
{(_bodyPart isNotEqualTo "body") || {(_woundDamage < PENETRATION_THRESHOLD) isEqualTo (_oldDamage < PENETRATION_THRESHOLD)}} && // penetrating body damage is handled differently
|
2022-02-17 20:03:12 +00:00
|
|
|
{(_bodyPartNToAdd > 3) || {!_causeLimping} || {(_woundDamage <= LIMPING_DAMAGE_THRESHOLD) isEqualTo (_oldDamage <= LIMPING_DAMAGE_THRESHOLD)}} // ensure limping damage is stacked correctly
|
|
|
|
) exitWith {
|
|
|
|
TRACE_2("merging with existing wound",_injury,_x);
|
|
|
|
private _newAmountOf = _oldAmountOf + 1;
|
2023-06-24 05:11:56 +00:00
|
|
|
_x set [1, _newAmountOf];
|
2022-02-17 20:03:12 +00:00
|
|
|
private _newBleeding = (_oldAmountOf * _oldBleeding + _bleeding) / _newAmountOf;
|
2023-06-24 05:11:56 +00:00
|
|
|
_x set [2, _newBleeding];
|
2022-02-17 20:03:12 +00:00
|
|
|
private _newDamage = (_oldAmountOf * _oldDamage + _woundDamage) / _newAmountOf;
|
2023-06-24 05:11:56 +00:00
|
|
|
_x set [3, _newDamage];
|
2022-02-17 20:03:12 +00:00
|
|
|
_createNewWound = false;
|
2016-12-08 11:59:30 +00:00
|
|
|
};
|
2023-06-24 05:11:56 +00:00
|
|
|
} forEach _existingWounds;
|
2022-02-17 20:03:12 +00:00
|
|
|
|
|
|
|
if (_createNewWound) then {
|
|
|
|
TRACE_1("adding new wound",_injury);
|
2023-06-24 05:11:56 +00:00
|
|
|
_existingWounds pushBack _injury;
|
2016-12-06 09:59:29 +00:00
|
|
|
};
|
2022-02-17 20:03:12 +00:00
|
|
|
_createdWounds = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
// selection-specific damage only hits the first part
|
|
|
|
if (_selectionSpecific > 0) then {
|
|
|
|
break;
|
2016-12-06 09:59:29 +00:00
|
|
|
};
|
2022-02-17 20:03:12 +00:00
|
|
|
} forEach _allDamages;
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2019-05-12 04:13:59 +00:00
|
|
|
if (_updateDamageEffects) then {
|
|
|
|
[_unit] call EFUNC(medical_engine,updateDamageEffects);
|
|
|
|
};
|
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
if (_createdWounds) then {
|
|
|
|
_unit setVariable [VAR_OPEN_WOUNDS, _openWounds, true];
|
|
|
|
_unit setVariable [QEGVAR(medical,bodyPartDamage), _bodyPartDamage, true];
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
[_unit] call EFUNC(medical_status,updateWoundBloodLoss);
|
2019-04-27 19:12:11 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
_bodyPartVisParams call EFUNC(medical_engine,updateBodyPartVisuals);
|
2016-12-06 09:59:29 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
[QEGVAR(medical,injured), [_unit, _painLevel]] call CBA_fnc_localEvent;
|
|
|
|
|
|
|
|
if (_criticalDamage || {_painLevel > PAIN_UNCONSCIOUS}) then {
|
|
|
|
[_unit] call FUNC(handleIncapacitation);
|
|
|
|
};
|
2017-06-05 16:51:53 +00:00
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
TRACE_4("exit",_unit,_painLevel,GET_PAIN(_unit),GET_OPEN_WOUNDS(_unit));
|
2016-12-06 09:59:29 +00:00
|
|
|
};
|
|
|
|
|
2022-02-17 20:03:12 +00:00
|
|
|
[] //return, no further damage handling
|