ACE3/addons/fire/functions/fnc_burnSimulation.sqf
johnb432 f45dff8a09
Fire - Mini-Rewrite (#9757)
Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>
Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>
2024-06-22 15:07:36 -03:00

168 lines
6.4 KiB
Plaintext

#include "..\script_component.hpp"
/*
* Author: tcvm, johnb43
* Simulates fire intensity over time on burning units.
* Arbitrary values to ignite people. Assumed maximum is "10".
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Instigator <OBJECT>
*
* Return Value:
* None
*
* Example:
* [player, player] call ace_fire_fnc_burnSimulation
*
* Public: No
*/
params ["_unit", "_instigator"];
[{
params ["_args", "_pfhID"];
_args params ["_unit", "_instigator"];
if (isNull _unit) exitWith {
TRACE_1("unit is null",_unit);
_pfhID call CBA_fnc_removePerFrameHandler;
};
// Locality has changed
if (!local _unit) exitWith {
TRACE_1("unit is no longer local",_unit);
_pfhID call CBA_fnc_removePerFrameHandler;
[QGVAR(burnSimulation), [_unit, _instigator], _unit] call CBA_fnc_targetEvent;
};
// If the unit is invulnerable, in water or if the fire has died out, stop burning the unit
if (
!(_unit call FUNC(isBurning)) ||
{!(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]})} ||
{private _eyePos = eyePos _unit; surfaceIsWater _eyePos && {(_eyePos select 2) < 0.1}}
) exitWith {
TRACE_3("unit is no longer burning, invulnerable or in water",_unit,_unit call FUNC(isBurning),isDamageAllowed _unit && {_unit getVariable [ARR_2(QEGVAR(medical,allowDamage),true)]});
// Remove global effects
(_unit getVariable [QGVAR(jipID), ""]) call CBA_fnc_removeGlobalEventJIP;
// Update globally that the unit isn't burning anymore
_unit setVariable [QGVAR(intensity), nil, true];
_pfhID call CBA_fnc_removePerFrameHandler;
if (!isNil {_unit getVariable QGVAR(stopDropRoll)} && {!isPlayer _unit}) then {
_unit setUnitPos "AUTO";
_unit setVariable [QGVAR(stopDropRoll), nil, true];
};
};
if (isGamePaused) exitWith {};
private _intensity = _unit getVariable [QGVAR(intensity), 0];
// Propagate fire to other units (alive or dead) if it's intense
if (_intensity >= BURN_THRESHOLD_INTENSE) then {
TRACE_2("check for other units",_unit,_intensity);
{
private _distancePercent = 1 - ((_unit distance _x) / BURN_PROPAGATE_DISTANCE);
private _adjustedIntensity = _intensity * _distancePercent;
// Don't burn if intensity is too low or already burning with higher intensity
if (BURN_MIN_INTENSITY > _adjustedIntensity || {(_x getVariable [QGVAR(intensity), 0]) > _adjustedIntensity}) then {
continue;
};
[QGVAR(burn), [_x, _adjustedIntensity, _instigator], _x] call CBA_fnc_targetEvent;
TRACE_3("propagate fire",_x,_intensity,_adjustedIntensity);
} forEach nearestObjects [_unit, ["CAManBase"], BURN_PROPAGATE_DISTANCE];
};
// Update intensity/fire reactions
if (CBA_missionTime >= _unit getVariable [QGVAR(intensityUpdate), 0]) then {
TRACE_2("update intensity",_unit,_intensity);
_unit setVariable [QGVAR(intensityUpdate), CBA_missionTime + INTENSITY_UPDATE];
_intensity = _intensity - INTENSITY_LOSS - (rain / 10);
if (_unit call EFUNC(common,isAwake)) then {
if (_unit call EFUNC(common,isPlayer)) then {
// Decrease intensity of burn if rolling around
if ((animationState _unit) in PRONE_ROLLING_ANIMS) then {
_intensity = _intensity * INTENSITY_DECREASE_MULT_ROLLING;
};
} else {
private _sdr = _unit getVariable [QGVAR(stopDropRoll), false];
private _vehicle = objectParent _unit;
if (isNull _vehicle && {_sdr || {0.05 > random 1}}) then {
_unit setVariable [QGVAR(stopDropRoll), true, true];
if (!_sdr) then {
TRACE_1("stop, drop, roll!",_unit);
_unit setUnitPos "DOWN";
doStop _unit;
};
// Queue up a bunch of animations
for "_i" from 0 to 2 do {
[_unit, selectRandom ["amovppnemstpsnonwnondnon_amovppnemevasnonwnondl", "amovppnemstpsnonwnondnon_amovppnemevasnonwnondr"], 0] call EFUNC(common,doAnimation);
};
_intensity = _intensity - (1 / _intensity);
} else {
// Make the unit leave the vehicle
if (_vehicle != _unit) then {
TRACE_1("Ejecting",_unit);
_unit leaveVehicle _vehicle;
unassignVehicle _unit;
_unit action ["Eject", _vehicle];
};
_unit disableAI "TARGET";
_unit disableAI "AUTOTARGET";
// Run away, erraticly
if (leader group _unit != _unit) then {
[_unit] join grpNull;
};
_unit doMove ((getPosATL _unit) getPos [20 + random 35, floor (random 360)]);
_unit setSpeedMode "FULL";
_unit setSuppression 1;
};
};
// Play screams and throw weapon (if enabled)
_unit call FUNC(burnReaction);
};
if (!isNull _instigator) then {
_unit setVariable [QEGVAR(medical,lastDamageSource), _instigator];
_unit setVariable [QEGVAR(medical,lastInstigator), _instigator];
};
// Common burn areas are the hands and face https://www.ncbi.nlm.nih.gov/pubmed/16899341/
private _bodyPart = ["Head", "Body", "LeftArm", "RightArm", "LeftLeg", "RightLeg"] selectRandomWeighted [0.77, 0.5, 0.8, 0.8, 0.3, 0.3];
// Keep pain around unconciousness limit to allow for more fun interactions
private _damageToAdd = [0.15, _intensity / BURN_MAX_INTENSITY] select (!alive _unit || {GET_PAIN_PERCEIVED(_unit) < (PAIN_UNCONSCIOUS + random 0.2)});
// Use event directly, as ace_medical_fnc_addDamageToUnit requires unit to be alive
[QEGVAR(medical,woundReceived), [_unit, [[_damageToAdd, _bodyPart, _damageToAdd]], _instigator, "burn"]] call CBA_fnc_localEvent;
_unit setVariable [QGVAR(intensity), _intensity, true]; // Globally sync intensity across all clients to make sure simulation is deterministic
};
}, BURN_PROPAGATE_UPDATE, [_unit, _instigator]] call CBA_fnc_addPerFrameHandler;