Reworked fire module

This commit is contained in:
johnb432 2024-01-26 18:38:26 +01:00
parent 4756bb6f8c
commit 13e3ba3099
18 changed files with 474 additions and 417 deletions

View File

@ -107,7 +107,7 @@ if (_smokeDelayEnabled) then {
_vehicle setVariable [QGVAR(nextFlame), _time + (MIN_TIME_BETWEEN_FLAMES max random MAX_TIME_BETWEEN_FLAMES)];
{
[QEGVAR(fire,burn), [_x, _intensity * 1.5, _instigator]] call CBA_fnc_globalEvent;
[QEGVAR(fire,burn), [_x, _intensity * 1.5, _instigator], _x] call CBA_fnc_targetEvent;
} forEach crew _vehicle
};

View File

@ -1,10 +1,10 @@
PREP(burn);
PREP(isBurning);
PREP(isPlant);
PREP(burnEffects);
PREP(burnIndicator);
PREP(burnReaction);
PREP(burnSimulation);
PREP(fireManagerPFH);
PREP(isBurning);
PREP(medical_progress);
PREP(medical_success);
PREP(medical_canPatDown);

View File

@ -1,38 +1,46 @@
#include "script_component.hpp"
[QGVAR(burn), FUNC(burn)] call CBA_fnc_addEventHandler;
[QGVAR(burn), LINKFUNC(burn)] call CBA_fnc_addEventHandler;
[QGVAR(burnEffects), LINKFUNC(burnEffects)] call CBA_fnc_addEventHandler;
[QGVAR(burnSimulation), LINKFUNC(burnSimulation)] call CBA_fnc_addEventHandler;
// Only play sound if enabled in settings
[QGVAR(playScream), {
if (!GVAR(enableScreams)) exitWith {};
params ["_scream", "_source"];
// only play sound if enabled in settings
if (GVAR(enableScreams)) then {
_source say3D _scream;
};
_source say3D _scream;
}] call CBA_fnc_addEventHandler;
["ace_settingsInitialized", {
TRACE_1("settingsInit", GVAR(enabled));
if (!GVAR(enabled)) exitWith {};
if (isServer) then {
["CBA_settingsInitialized", {
TRACE_1("settingsInit", GVAR(enabled));
if (!GVAR(enabled)) exitWith {};
GVAR(fireSources) = createHashMap;
if (isServer) then {
[QGVAR(addFireSource), {
params ["_source", "_radius", "_intensity", "_key", ["_condition", { true }], ["_conditionArgs", []]];
params ["_source", "_radius", "_intensity", "_key", ["_condition", {true}], ["_conditionArgs", []]];
private _fireLogic = createVehicle ["ACE_LogicDummy", [0, 0, 0], [], 0, "NONE"];
if (_source isEqualType objNull) then {
_fireLogic attachTo [_source];
} else {
_fireLogic setPosASL _source;
};
[GVAR(fireSources), _key, [_fireLogic, _radius, _intensity, _condition, _conditionArgs]] call CBA_fnc_hashSet;
GVAR(fireSources) set [_key, [_fireLogic, _radius, _intensity, _condition, _conditionArgs]];
}] call CBA_fnc_addEventHandler;
[QGVAR(removeFireSource), {
params ["_key"];
[GVAR(fireSources), _key] call CBA_fnc_hashRem;
GVAR(fireSources) deleteAt _key;
}] call CBA_fnc_addEventHandler;
[{ _this call FUNC(fireManagerPFH) }, FIRE_MANAGER_PFH_DELAY, []] call CBA_fnc_addPerFrameHandler;
GVAR(fireSources) = [[], nil] call CBA_fnc_hashCreate;
};
}] call CBA_fnc_addEventHandler;
[LINKFUNC(fireManagerPFH), FIRE_MANAGER_PFH_DELAY, []] call CBA_fnc_addPerFrameHandler;
}] call CBA_fnc_addEventHandler;
};

View File

@ -8,6 +8,4 @@ PREP_RECOMPILE_END;
#include "initSettings.inc.sqf"
GVAR(burningPlants) = [];
ADDON = true;

View File

@ -1,8 +1,7 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Makes object catch fire. Only call from events. Local effects only.
* Arbitrary values to ignite people. Assumed maximum is "10".
* Author: tcvm, johnb43
* Makes object catch fire. Only call from targeted events, is applied globally.
*
* Arguments:
* 0: Vehicle <OBJECT>
@ -18,322 +17,39 @@
* Public: No
*/
#define INTENSITY_LOSS 0.03
#define INTENSITY_UPDATE 3
#define BURN_PROPOGATE_UPDATE 1
#define BURN_PROPOGATE_DISTANCE 2
#define BURN_PROPOGATE_COUNTER_MAX 5
params ["_unit", "_intensity", ["_instigator", objNull]];
if !(EGVAR(common,settingsInitFinished)) exitWith {
EGVAR(common,runAtSettingsInitialized) pushBack [FUNC(burn), _this];
};
if (!GVAR(enabled)) exitWith {};
private _isBurning = [_unit] call FUNC(isBurning);
if (_isBurning) exitWith {};
params ["_unit", "_intensity", ["_instigator", objNull]];
[{
// looped function
(_this getVariable "params") params ["_unit", "", "_instigator"];
private _unitPos = getPosASL _unit;
// Check if unit is remote (objNull is remote)
if (!local _unit) exitWith {};
_intensity = _unit getVariable [QGVAR(intensity), 0];
// Check if the unit can burn (takes care of spectators and curators)
if (getNumber (configOf _unit >> "isPlayableLogic") == 1) exitWith {};
if (surfaceIsWater _unitPos && {(_unitPos#2) < 1}) then {
_intensity = 0;
};
// If unit is invulnerable, don't burn unit
if !(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]}) exitWith {};
_fireParticle setDropInterval (0.01 max linearConversion [BURN_MAX_INTENSITY, BURN_MIN_INTENSITY, _intensity, 0.03, 0.1, false]);
_fireParticle setParticleParams [
["\A3\data_f\ParticleEffects\Universal\Universal", 16, 10, 32], // sprite sheet values
"", // animation name
"Billboard", // particle type
1, // timer period
0.7, // lifetime
"destructionEffect2", // position
[0, 0, 1], // move velocity
0, // rotation velocity
10, // weight
7.9, // volume
1, // rubbing
[0.3, 0.3], // size
[
[1, 1, 1, -0],
[1, 1, 1, -1],
[1, 1, 1, -1],
[1, 1, 1, -1],
[1, 1, 1, -0]
], // colour
[0.5, 1], // animation speed
1, // random dir period
0, // random dir intensity
"", // on timer script
"", // before destroy script
_unit, // particle source
0,
false,
0,
[[0.8, 0.6, 0.2, 1]] // emissive color
];
_fireParticle setParticleRandom [
0.04 * _intensity, // life time
[0.05, 0.05, 2], // position
[0.05 * _intensity, 0.05 * _intensity, 0.05 * _intensity], // move velocity
0, // rotation velocity
0.06 * _intensity, // size
[0, 0, 0, 0], // color
0, // random direction period
0 // random direction intensity
];
// If unit is already burning, don't burn more
if (_unit call FUNC(isBurning)) exitWith {};
_smokeParticle setParticleCircle [0, [0, 0, 0]];
_smokeParticle setParticleRandom [
0, // life time
[0.25, 0.25, 0], // position
[0.2, 0.2, 0], // move velocity
0, // rotation velocity
0.25, // size
[0, 0, 0, 0.1], // color
0, // random direction period
0 // random direction intensity
];
_smokeParticle setParticleParams [
["\A3\data_f\ParticleEffects\Universal\Universal", 16, 7, 48], // sprite sheet values
"", // animation name
"Billboard", // particle type
1, // timer period
8, // lifetime
[0, 0, 1.1], // position
[0, 0, 1], // move velocity
0, // rotation velocity
10, // weight
7.9, // volume
0.066, // rubbing
[1, 3, 6], // size
[
[0.5, 0.5, 0.5, 0.15],
[0.75, 0.75, 0.75, 0.075],
[1, 1, 1, 0]
], // colour
[0.125], // animation speed
1, // random dir period
0, // random dir intensity
"", // on timer script
"", // before destroy script
_unit // particle source
];
_smokeParticle setDropInterval 0.15;
_unit setVariable [QGVAR(intensity), _intensity, true];
_fireLight setLightBrightness ((_intensity * 3) / 10);
_lightFlare setLightBrightness (_intensity / 30);
// Fire simulation
private _burnSimulationJipID = [QGVAR(burnSimulation), [_unit, _instigator], format [QGVAR(burnSimulation_%1), hashValue _unit]] call CBA_fnc_globalEventJIP;
[_burnSimulationJipID, _unit] call CBA_fnc_removeGlobalEventJIP;
private _distanceToUnit = (_unit distance ace_player);
_fireLight setLightAttenuation [1, 10 max (5 min (10 - _intensity)), 0, 15];
_lightFlare setLightFlareSize (_intensity * (3 / 4)) * FLARE_SIZE_MODIFIER;
// Spawn effects for unit
private _burnEffectsJipID = [QGVAR(burnEffects), _unit, format [QGVAR(burnEffects_%1), hashValue _unit]] call CBA_fnc_globalEventJIP;
[_burnEffectsJipID, _unit] call CBA_fnc_removeGlobalEventJIP;
if (!GVAR(enableFlare)) then {
_lightFlare setLightFlareSize 0;
};
_unit setVariable [QGVAR(jipIDs), [_burnSimulationJipID, _burnEffectsJipID], true];
// always keep flare visible to perceiving unit as long as it isnt the player
if (_unit isNotEqualTo ace_player) then {
private _relativeAttachPoint = [0, 0, 0.3];
if (_distanceToUnit > 1.5) then {
_relativeAttachPoint = (vectorNormalized (_unit worldToModelVisual (getPos ace_player))) vectorMultiply linearConversion [5, 30, _distanceToUnit, 0.5, 1.5];
_relativeAttachPoint set [2, 0.3 + ((_unit selectionPosition "pelvis") select 2)];
};
_lightFlare attachTo [_unit, _relativeAttachPoint];
};
if (!isGamePaused) then {
// If the unit goes to spectator alive _unit == true and they will be on fire and still take damage
// Only workaround I could think of, kinda clunky
if (_isThisUnitAlive) then {
_isThisUnitAlive = (alive _unit) && { getNumber ((configOf _unit) >> "isPlayableLogic") != 1 };
};
// propagate fire
if ((CBA_missionTime - _lastPropogateUpdate) >= BURN_PROPOGATE_UPDATE) then {
_lastPropogateUpdate = CBA_missionTime;
if !([ace_player] call FUNC(isBurning)) then {
if ((vehicle _unit) isEqualTo (vehicle ace_player)) then {
if (0.5 > random 1) then {
[QGVAR(burn), [ace_player, _intensity * (7 / 8), _instigator]] call CBA_fnc_globalEvent;
};
} else {
if ((ace_player isKindOf "Man") && {_unit isNotEqualTo ace_player} && {isDamageAllowed ace_player && {ace_player getVariable [QEGVAR(medical,allowDamage), true]}}) then {
private _burnCounter = _unit getVariable [QGVAR(burnCounter), 0];
if (_distanceToUnit < BURN_PROPOGATE_DISTANCE) then {
if (_burnCounter < BURN_PROPOGATE_COUNTER_MAX) then {
_burnCounter = _burnCounter + 1;
} else {
[QGVAR(burn), [ace_player, _intensity * (3 / 4), _instigator]] call CBA_fnc_globalEvent;
};
} else {
_burnCounter = 0;
};
_unit setVariable [QGVAR(burnCounter), _burnCounter];
};
};
};
};
// update intensity/fire reactions
if ((CBA_missionTime - _lastIntensityUpdate) >= INTENSITY_UPDATE) then {
_lastIntensityUpdate = CBA_missionTime;
_intensity = _intensity - INTENSITY_LOSS - (rain / 10);
if (local _unit) then {
if (_isThisUnitAlive) then {
if !(IS_UNCONSCIOUS(_unit)) then {
if !(isPlayer _unit) then {
private _sdr = _unit getVariable [QGVAR(stopDropRoll), false];
if ((_unit isEqualTo vehicle _unit) && (_sdr || ({ 0.05 > random 1 }))) then {
_unit setVariable [QGVAR(stopDropRoll), 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 {
private _group = (group _unit);
private _vehicle = vehicle _unit;
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
if (leader _group != _unit) then {
[_unit] join grpNull;
};
_unit doMove ((getPosATL _unit) getPos [20 + random 35, floor (random 360)]);
_unit setSpeedMode "FULL";
_unit setSuppression 1;
};
} else {
if ((animationState _unit) in PRONE_ROLLING_ANIMS) then {
// decrease intensity of burn
_intensity = _intensity * INTENSITY_DECREASE_MULT_ROLLING;
};
};
[_unit] call FUNC(burnReaction);
};
// Common burn areas are the hands and face https://www.ncbi.nlm.nih.gov/pubmed/16899341/
private _woundSelection = ["Head", "Body", "LeftArm", "RightArm", "LeftLeg", "RightLeg"] selectRandomWeighted [0.77, 0.5, 0.8, 0.8, 0.3, 0.3];
if (GET_PAIN_PERCEIVED(_unit) < (PAIN_UNCONSCIOUS + random 0.2)) then {
// keep pain around unconciousness limit to allow for more fun interactions
[_unit, _intensity / BURN_MAX_INTENSITY, _woundSelection, "burn", _instigator] call EFUNC(medical,addDamageToUnit);
} else {
[_unit, 0.15, _woundSelection, "burn", _instigator] call EFUNC(medical,addDamageToUnit);
};
};
_unit setVariable [QGVAR(intensity), _intensity, true]; // globally sync intensity across all clients to make sure simulation is deterministic
};
};
private _burnIndicatorPFH = _unit getVariable [QGVAR(burnUIPFH), -1];
if (_unit isEqualTo ace_player && { _isThisUnitAlive } && { _burnIndicatorPFH < 0 }) then {
_burnIndicatorPFH = [FUNC(burnIndicator), 1, _unit] call CBA_fnc_addPerFrameHandler;
_unit setVariable [QGVAR(burnUIPFH), _burnIndicatorPFH];
};
};
}, 0, [_unit, _intensity, _instigator], {
TRACE_1("burn init",GVAR(enableFlare));
// init function
private _params = _this getVariable "params";
_params params ["_unit", "_startingIntensity"];
_intensity = _startingIntensity;
private _unitPos = getPos _unit;
_fireParticle = "#particlesource" createVehicleLocal _unitPos;
_fireParticle attachTo [_unit, [0, 0, 0]];
_fireParticle setDropInterval 0.03;
_smokeParticle = "#particlesource" createVehicleLocal _unitPos;
_fireLight = "#lightpoint" createVehicleLocal _unitPos;
_fireLight setLightIntensity 0;
_fireLight setLightAmbient [0.8, 0.6, 0.2];
_fireLight setLightColor [1, 0.5, 0.4];
_fireLight attachTo [_unit, [0, 0, 0]];
_fireLight setLightDayLight false;
_lightFlare = "#lightpoint" createVehicleLocal _unitPos;
_lightFlare setLightIntensity 0;
_lightFlare setLightColor [1, 0.8, 0.8];
_lightFlare setLightUseFlare true;
_lightFlare setLightFlareMaxDistance 100;
_lightFlare setLightFlareSize 0;
if (_unit isNotEqualTo ace_player) then {
private _relativeAttachPoint = (vectorNormalized (_unit worldToModelVisual (getPos ace_player))) vectorMultiply 1;
_relativeAttachPoint set [2, 0.5];
_lightFlare attachTo [_unit, _relativeAttachPoint];
} else {
_lightFlare attachTo [_unit, [0, 0, 0.3]];
};
if (isServer) then {
_fireSound = createSoundSource ["Sound_Fire", _unitPos, [], 0];
_fireSound attachTo [_unit, [0, 0, 0], "Head"];
};
_unit setVariable [QGVAR(burning), true];
_unit setVariable [QGVAR(intensity), _intensity];
_unit setVariable [QGVAR(burnUIPFH), -1];
if (local _unit) then {
if (_unit isEqualTo ace_player) then {
private _burnIndicatorPFH = [FUNC(burnIndicator), 1, _unit] call CBA_fnc_addPerFrameHandler;
_unit setVariable [QGVAR(burnUIPFH), _burnIndicatorPFH];
};
[_unit, false] call FUNC(burnReaction);
};
_lastIntensityUpdate = 0;
_lastPropogateUpdate = 0;
_isThisUnitAlive = true;
}, {
(_this getVariable "params") params ["_unit"];
// deinit function
deleteVehicle _fireParticle;
deleteVehicle _smokeParticle;
deleteVehicle _fireLight;
deleteVehicle _lightFlare;
deleteVehicle _fireSound;
if (local _unit) then {
if (!isPlayer _unit) then {
_unit setUnitPos "AUTO";
_unit setVariable [QGVAR(stopDropRoll), false];
};
};
_unit setVariable [QGVAR(burning), false];
_unit setVariable [QGVAR(burnCounter), 0];
}, {
// run condition
true
}, {
// exit condition
(_this getVariable "params") params ["_unit"];
private _unitAlive = (alive _unit) && { getNumber ((configOf _unit) >> "isPlayableLogic") != 1 };
private _unitIsUnit = { (_unit != vehicle _unit) && { isNull vehicle _unit } };
!_unitAlive || _unitIsUnit || { _intensity <= BURN_MIN_INTENSITY } || { !([_unit] call FUNC(isBurning)) }
}, ["_intensity", "_fireParticle", "_smokeParticle", "_fireLight", "_fireSound", "_lightFlare", "_lastIntensityUpdate", "_lastPropogateUpdate", "_isThisUnitAlive"]] call CBA_fnc_createPerFrameHandlerObject;
// Play screams and optional weapon dropping
if (_unit call EFUNC(common,isAwake)) then {
[_unit, false] call FUNC(burnReaction);
};

View File

@ -0,0 +1,191 @@
#include "..\script_component.hpp"
/*
* Author: tcvm, johnb43
* Spawns particle effects for a burning unit.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* player call ace_fire_fnc_burnEffects
*
* Public: No
*/
params ["_unit"];
// Spawn particles
private _unitPos = getPos _unit;
private _fireParticle = objNull;
private _smokeParticle = objNull;
private _fireLight = objNull;
private _lightFlare = objNull;
if (hasInterface) then {
_fireParticle = "#particlesource" createVehicleLocal _unitPos;
_fireParticle attachTo [_unit, [0, 0, 0]];
_fireParticle setDropInterval 0.03;
_smokeParticle = "#particlesource" createVehicleLocal _unitPos;
_fireLight = "#lightpoint" createVehicleLocal _unitPos;
_fireLight setLightIntensity 0;
_fireLight setLightAmbient [0.8, 0.6, 0.2];
_fireLight setLightColor [1, 0.5, 0.4];
_fireLight attachTo [_unit, [0, 0, 0]];
_fireLight setLightDayLight false;
_lightFlare = "#lightpoint" createVehicleLocal _unitPos;
_lightFlare setLightIntensity 0;
_lightFlare setLightColor [1, 0.8, 0.8];
_lightFlare setLightUseFlare true;
_lightFlare setLightFlareMaxDistance 100;
_lightFlare setLightFlareSize 0;
if (_unit != ACE_player) then {
private _relativeAttachPoint = vectorNormalized (_unit worldToModelVisual (getPos ACE_player));
_relativeAttachPoint set [2, 0.5];
_lightFlare attachTo [_unit, _relativeAttachPoint];
} else {
_lightFlare attachTo [_unit, [0, 0, 0.3]];
};
};
private _fireSound = objNull;
if (isServer) then {
_fireSound = createSoundSource ["Sound_Fire", _unitPos, [], 0];
_fireSound attachTo [_unit, [0, 0, 0], "Head"];
};
[{
params ["_args", "_pfhID"];
_args params ["_unit", "_fireParticle", "_smokeParticle", "_fireLight", "_lightFlare", "_fireSound"];
if (isNull _unit || {!(_unit call FUNC(isBurning))}) exitWith {
_pfhID call CBA_fnc_removePerFrameHandler;
deleteVehicle _fireParticle;
deleteVehicle _smokeParticle;
deleteVehicle _fireLight;
deleteVehicle _lightFlare;
deleteVehicle _fireSound;
};
// Display burn indicators
if (_unit == ACE_player && {alive _unit} && {isNil {_unit getVariable QGVAR(burnUIPFH)}}) then { // this accounts for player remote controlled a new unit
private _burnIndicatorPFH = [LINKFUNC(burnIndicator), 1, _unit] call CBA_fnc_addPerFrameHandler;
_unit setVariable [QGVAR(burnUIPFH), _burnIndicatorPFH];
};
if (!hasInterface) exitWith {};
private _intensity = _unit getVariable [QGVAR(intensity), 0];
_fireParticle setDropInterval (0.01 max linearConversion [BURN_MAX_INTENSITY, BURN_MIN_INTENSITY, _intensity, 0.03, 0.1, false]);
_fireParticle setParticleParams [
["\A3\data_f\ParticleEffects\Universal\Universal", 16, 10, 32], // sprite sheet values
"", // animation name
"Billboard", // particle type
1, // timer period
0.7, // lifetime
"destructionEffect2", // position
[0, 0, 1], // move velocity
0, // rotation velocity
10, // weight
7.9, // volume
1, // rubbing
[0.3, 0.3], // size
[
[1, 1, 1, -0],
[1, 1, 1, -1],
[1, 1, 1, -1],
[1, 1, 1, -1],
[1, 1, 1, -0]
], // colour
[0.5, 1], // animation speed
1, // random dir period
0, // random dir intensity
"", // on timer script
"", // before destroy script
_unit, // particle source
0,
false,
0,
[[0.8, 0.6, 0.2, 1]] // emissive color
];
_fireParticle setParticleRandom [
0.04 * _intensity, // life time
[0.05, 0.05, 2], // position
[0.05 * _intensity, 0.05 * _intensity, 0.05 * _intensity], // move velocity
0, // rotation velocity
0.06 * _intensity, // size
[0, 0, 0, 0], // color
0, // random direction period
0 // random direction intensity
];
_smokeParticle setParticleCircle [0, [0, 0, 0]];
_smokeParticle setParticleRandom [
0, // life time
[0.25, 0.25, 0], // position
[0.2, 0.2, 0], // move velocity
0, // rotation velocity
0.25, // size
[0, 0, 0, 0.1], // color
0, // random direction period
0 // random direction intensity
];
_smokeParticle setParticleParams [
["\A3\data_f\ParticleEffects\Universal\Universal", 16, 7, 48], // sprite sheet values
"", // animation name
"Billboard", // particle type
1, // timer period
8, // lifetime
[0, 0, 1.1], // position
[0, 0, 1], // move velocity
0, // rotation velocity
10, // weight
7.9, // volume
0.066, // rubbing
[1, 3, 6], // size
[
[0.5, 0.5, 0.5, 0.15],
[0.75, 0.75, 0.75, 0.075],
[1, 1, 1, 0]
], // colour
[0.125], // animation speed
1, // random dir period
0, // random dir intensity
"", // on timer script
"", // before destroy script
_unit // particle source
];
_smokeParticle setDropInterval 0.15;
_fireLight setLightBrightness ((_intensity * 3) / 10);
_lightFlare setLightBrightness (_intensity / 30);
private _distanceToUnit = _unit distance ACE_player;
_fireLight setLightAttenuation [1, 10 max (5 min (10 - _intensity)), 0, 15];
_lightFlare setLightFlareSize (_intensity * (3 / 4)) * FLARE_SIZE_MODIFIER;
if (!GVAR(enableFlare)) then {
_lightFlare setLightFlareSize 0;
};
// Always keep flare visible to perceiving unit as long as it isn't the player
if (_unit != ACE_player) then {
private _relativeAttachPoint = [0, 0, 0.3];
if (_distanceToUnit > 1.5) then {
_relativeAttachPoint = (vectorNormalized (_unit worldToModelVisual (getPos ACE_player))) vectorMultiply linearConversion [5, 30, _distanceToUnit, 0.5, 1.5];
_relativeAttachPoint set [2, 0.3 + ((_unit selectionPosition "pelvis") select 2)];
};
_lightFlare attachTo [_unit, _relativeAttachPoint];
};
}, 0, [_unit, _fireParticle, _smokeParticle, _fireLight, _lightFlare, _fireSound]] call CBA_fnc_addPerFrameHandler;

View File

@ -11,26 +11,29 @@
* None
*
* Example:
* [player, 4] call ace_fire_fnc_burnIndicator
* [player, _pfhID] call ace_fire_fnc_burnIndicator
*
* Public: No
*/
params ["_unit", "_pfhHandle"];
if !(IS_UNCONSCIOUS(_unit)) then {
private _iteration = _unit getVariable [QGVAR(indicatorIteration), 0];
if (_iteration == 0) then {
QGVAR(indicatorLayer) cutRsc [QGVAR(onFire1), "PLAIN"];
_iteration = 1;
} else {
QGVAR(indicatorLayer) cutRsc [QGVAR(onFire2), "PLAIN"];
_iteration = 0;
};
_unit setVariable [QGVAR(indicatorIteration), _iteration];
if (!alive _unit || {!(_unit call FUNC(isBurning))}) exitWith {
_pfhHandle call CBA_fnc_removePerFrameHandler;
_unit setVariable [QGVAR(burnUIPFH), nil];
};
if (!([_unit] call FUNC(isBurning)) || { !alive _unit }) then {
[_pfhHandle] call CBA_fnc_removePerFrameHandler;
_unit setVariable [QGVAR(burnUIPFH), -1];
if !(_unit call EFUNC(common,isAwake)) exitWith {};
private _iteration = _unit getVariable [QGVAR(indicatorIteration), 0];
if (_iteration == 0) then {
QGVAR(indicatorLayer) cutRsc [QGVAR(onFire1), "PLAIN"];
_iteration = 1;
} else {
QGVAR(indicatorLayer) cutRsc [QGVAR(onFire2), "PLAIN"];
_iteration = 0;
};
_unit setVariable [QGVAR(indicatorIteration), _iteration];

View File

@ -5,7 +5,7 @@
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Should unit throw its current weapon <BOOL>
* 1: Should unit throw its current weapon <BOOL> (default: true)
*
* Return Value:
* None
@ -18,14 +18,15 @@ params ["_unit", ["_throwWeapon", true]];
if (
_throwWeapon
&& {GVAR(dropWeapon) > 0}
&& {_unit in _unit && {(currentWeapon _unit) isNotEqualTo ""}}
&& {isNull objectParent _unit && {(currentWeapon _unit) != ""}}
&& {!isPlayer _unit || GVAR(dropWeapon) >= 2}
) then {
[_unit] call EFUNC(common,throwWeapon);
_unit call EFUNC(common,throwWeapon);
};
if (_unit isKindOf "CAManBase") then {
private _soundID = floor (1 + random 15);
private _sound = format [QGVAR(scream_%1), _soundID];
[QGVAR(playScream), [_sound, _unit]] call CBA_fnc_globalEvent;
};

View File

@ -0,0 +1,156 @@
#include "..\script_component.hpp"
/*
* Author: tcvm, johnb43
* Makes object catch fire. Simulates fire intensity over time.
* Arbitrary values to ignite people. Assumed maximum is "10".
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Instigator <OBJECT>
*
* Return Value:
* None
*
* Example:
* player call ace_fire_fnc_burnSimulation
*
* Public: No
*/
params ["_unit", "_instigator"];
// Reset variables
_unit setVariable [QGVAR(stopDropRoll), nil];
_unit setVariable [QGVAR(burnCounter), nil];
[{
params ["_args", "_pfhID"];
_args params ["_unit", "_instigator"];
private _remote = !local _unit;
// If unit is local and the fire has died now, the effects need to be cleaned up -> do not stop PFH here
if (isNull _unit || {_remote && {!(_unit call FUNC(isBurning))}}) exitWith {
_pfhID call CBA_fnc_removePerFrameHandler;
};
if (_remote) exitWith {};
private _unitPos = getPosASL _unit;
// If unit is invulnerable or in water or if the fire has died out, stop burning unit
if (
!(_unit call FUNC(isBurning)) ||
{surfaceIsWater _unitPos && {(_unitPos select 2) < 1}} ||
{!(isDamageAllowed _unit && {_unit getVariable [QEGVAR(medical,allowDamage), true]})}
) exitWith {
// Remove global effects and simulation
{
_x call CBA_fnc_removeGlobalEventJIP;
} forEach (_unit getVariable [QGVAR(jipIDs), []]);
// Update globally that the unit isn't burning anymore
_unit setVariable [QGVAR(intensity), nil, true];
_pfhID call CBA_fnc_removePerFrameHandler;
_unit setVariable [QGVAR(stopDropRoll), nil];
_unit setVariable [QGVAR(burnCounter), nil];
if (!isPlayer _unit) then {
_unit setUnitPos "AUTO";
};
};
if (isGamePaused) exitWith {};
private _intensity = _unit getVariable [QGVAR(intensity), 0];
// Propagate fire to other units if it's intense
if (_intensity >= BURN_THRESHOLD_INTENSE) then {
_unitPos = ASLToAGL _unitPos;
{
if !(_x call FUNC(isBurning)) then {
_distancePercent = 1 - ((_unitPos distance _x) / BURN_PROPAGATE_DISTANCE);
[QGVAR(burn), [_x, _intensity * _distancePercent, _instigator], _x] call CBA_fnc_targetEvent;
};
} forEach (_unitPos nearEntities ["CAManBase", BURN_PROPAGATE_DISTANCE]);
};
// Update intensity/fire reactions
if (CBA_missionTime >= _unit getVariable [QGVAR(intensityUpdate), 0]) then {
_unit setVariable [QGVAR(intensityUpdate), CBA_missionTime + INTENSITY_UPDATE];
_intensity = _intensity - INTENSITY_LOSS - (rain / 10);
if (_unit call EFUNC(common,isAwake)) then {
if (!isPlayer _unit) then {
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];
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;
};
} else {
// Decrease intensity of burn if rolling around
if ((animationState _unit) in PRONE_ROLLING_ANIMS) then {
_intensity = _intensity * INTENSITY_DECREASE_MULT_ROLLING;
};
};
// Play screams and throw weapon (if enabled)
_unit call FUNC(burnReaction);
};
// Common burn areas are the hands and face https://www.ncbi.nlm.nih.gov/pubmed/16899341/
private _woundSelection = ["Head", "Body", "LeftArm", "RightArm", "LeftLeg", "RightLeg"] selectRandomWeighted [0.77, 0.5, 0.8, 0.8, 0.3, 0.3];
if (GET_PAIN_PERCEIVED(_unit) < (PAIN_UNCONSCIOUS + random 0.2)) then {
// Keep pain around unconciousness limit to allow for more fun interactions
[_unit, _intensity / BURN_MAX_INTENSITY, _woundSelection, "burn", _instigator] call EFUNC(medical,addDamageToUnit);
} else {
[_unit, 0.15, _woundSelection, "burn", _instigator] call EFUNC(medical,addDamageToUnit);
};
_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;

View File

@ -5,39 +5,41 @@
* Used to handle external burning objects, not used internally because internal methods are more performant.
*
* Arguments:
* 0: Unit on fire <OBJECT>
* 1: PFH Handle <NUMBER>
* 0: Args (not used) <ARRAY>
* 1: PFH Handle (not used) <NUMBER>
*
* Return Value:
* None
*
* Example:
* [ace_fire_fnc_fireManagerPFH, 0.25, [_unit]] call CBA_fnc_addPerFrameHandler
* [ace_fire_fnc_fireManagerPFH, 0.25] call CBA_fnc_addPerFrameHandler
*
* Public: No
*/
params ["_args", "_handle"];
private _attachedObject = objNull;
private _sourcePos = [];
private _distancePercent = 0;
[GVAR(fireSources), {
_value params ["", "", "", "_condition", "_conditionArgs"];
_conditionArgs call _condition;
}] call CBA_fnc_hashFilter;
{
_y params ["_source", "_radius", "_intensity", "_condition", "_conditionArgs"];
[GVAR(fireSources), {
_value params ["_source", "_radius", "_intensity"];
private _attachedObject = attachedTo _source;
private _sourcePos = getPosATL _source;
if (_attachedObject isNotEqualTo objNull) then {
_sourcePos = getPosATL _attachedObject;
// Remove when condition is no longer valid
if !(_conditionArgs call _condition) then {
GVAR(fireSources) deleteAt _x;
continue;
};
private _nearEntities = _sourcePos nearEntities ["Man", _radius];
_attachedObject = attachedTo _source;
_sourcePos = ASLtoAGL getPosASL ([_source, _attachedObject] select (!isNull _attachedObject));
// Burn units close to the fire
{
private _burning = [_x] call FUNC(isBurning);
if !(_burning) then {
private _distancePercent = 1 - ((_sourcePos distance _x) / _radius);
[QGVAR(burn), [_x, _intensity * _distancePercent]] call CBA_fnc_globalEvent;
if !(_x call FUNC(isBurning)) then {
_distancePercent = 1 - ((_sourcePos distance _x) / _radius);
[QGVAR(burn), [_x, _intensity * _distancePercent], _x] call CBA_fnc_targetEvent;
};
} forEach _nearEntities;
}] call CBA_fnc_hashEachPair;
} forEach (_sourcePos nearEntities ["CAManBase", _radius]);
} forEach GVAR(fireSources);

View File

@ -1,10 +1,10 @@
#include "..\script_component.hpp"
/*
* Author: commy2
* Check if object is burning.
* Check if unit is burning.
*
* Arguments:
* 0: Vehicle <OBJECT>
* 0: Unit <OBJECT>
*
* Return Value:
* None
@ -17,7 +17,4 @@
params [["_unit", objNull, [objNull]]];
_unit getVariable [QGVAR(burning), false] || {
GVAR(burningPlants) = GVAR(burningPlants) select {!isNull _x};
_unit in GVAR(burningPlants)
}
(_unit getVariable [QGVAR(intensity), 0]) > BURN_MIN_INTENSITY

View File

@ -1,20 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: commy2
* Check if object is a map placed bush or tree.
*
* Arguments:
* 0: Object <OBJECT>
*
* Return Value:
* Is bush or tree? <BOOL>
*
* Example:
* cursorObject call ace_fire_fnc_isPlant
*
* Public: No
*/
params [["_object", objNull, [objNull]]];
_object in nearestTerrainObjects [_object, ["TREE", "SMALL TREE", "BUSH"], 0.1]

View File

@ -18,4 +18,4 @@
params ["", "_patient"];
[_patient] call FUNC(isBurning)
_patient call FUNC(isBurning)

View File

@ -5,8 +5,8 @@
*
* Arguments:
* 0: Arguments <ARRAY>
* 0: Medic <OBJECT>
* 1: Patient <OBJECT>
* - 0: Medic (not used) <OBJECT>
* - 1: Patient <OBJECT>
*
* Return Value:
* Continue pat down <BOOL>
@ -18,6 +18,6 @@
*/
params ["_args"];
_args params ["_medic", "_patient"];
_args params ["", "_patient"];
[_patient] call FUNC(isBurning)
_patient call FUNC(isBurning)

View File

@ -6,6 +6,8 @@
* Arguments:
* 0: Medic <OBJECT>
* 1: Patient <OBJECT>
* 2: Body Part <STRING>
* 3: Treatment <STRING>
*
* Return Value:
* None
@ -20,6 +22,7 @@ params ["_medic", "_patient", "_bodyPart", "_classname"];
private _intensity = _patient getVariable [QGVAR(intensity), 0];
_intensity = _intensity * INTENSITY_DECREASE_MULT_PAT_DOWN;
_patient setVariable [QGVAR(intensity), _intensity, true];
if (_intensity > BURN_MIN_INTENSITY) then {

View File

@ -2,20 +2,20 @@
QGVAR(enabled), "CHECKBOX",
[ELSTRING(common,Enabled), LSTRING(Setting_Description)],
LSTRING(Category_DisplayName),
true, // default value
true, // isGlobal
true,
1,
{[QGVAR(fireEnabled), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
true // needs mission restart
] call CBA_fnc_addSetting;
[
QGVAR(enableFlare), "CHECKBOX",
[LSTRING(Setting_FlareEnable), LSTRING(Setting_FlareDescription)],
LSTRING(Category_DisplayName),
false, // default value
true, // isGlobal
false,
1,
{[QGVAR(flareEnabled), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
true
] call CBA_fnc_addSetting;
[
@ -23,18 +23,16 @@
[LSTRING(Setting_DropWeapon), LSTRING(Setting_DropWeapon_Description)],
LSTRING(Category_DisplayName),
[
[0,1,2],
[localize "STR_A3_OPTIONS_DISABLED", ELSTRING(common,aiOnly), ELSTRING(common,playersAndAI)],
[0, 1, 2],
["STR_A3_OPTIONS_DISABLED", ELSTRING(common,aiOnly), ELSTRING(common,playersAndAI)],
1
],
true // isGlobal
1
] call CBA_fnc_addSetting;
[
QGVAR(enableScreams), "CHECKBOX",
[LSTRING(Setting_EnableScreams), LSTRING(Setting_EnableScreams_Description)],
LSTRING(Category_DisplayName),
true,
false // isGlobal
true
] call CBA_fnc_addSetting;

View File

@ -31,9 +31,14 @@
"amovppnemstpsoptwbindnon_amovppnemevasoptwbindr"\
]
#define BURN_MAX_INTENSITY 10
#define BURN_MIN_INTENSITY 1
#define INTENSITY_DECREASE_MULT_PAT_DOWN 0.8
#define INTENSITY_DECREASE_MULT_ROLLING INTENSITY_DECREASE_MULT_PAT_DOWN
#define INTENSITY_LOSS 0.03
#define INTENSITY_UPDATE 3
#define BURN_PROPAGATE_UPDATE 1
#define BURN_PROPAGATE_DISTANCE 2
#define BURN_THRESHOLD_INTENSE 3

View File

@ -36,7 +36,6 @@ switch (false) do {
[LSTRING(RequiresAddon)] call FUNC(showMessage);
};
default {
[QEGVAR(fire,burn), [_unit, 5]] call CBA_fnc_globalEvent;
[QEGVAR(fire,burn), [_unit, 5], _unit] call CBA_fnc_targetEvent;
};
};