diff --git a/addons/cookoff/functions/fnc_cookOff.sqf b/addons/cookoff/functions/fnc_cookOff.sqf index 57cde71682..8efa58bdf2 100644 --- a/addons/cookoff/functions/fnc_cookOff.sqf +++ b/addons/cookoff/functions/fnc_cookOff.sqf @@ -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 }; diff --git a/addons/fire/XEH_PREP.hpp b/addons/fire/XEH_PREP.hpp index d9eacfdee0..efc48a1f23 100644 --- a/addons/fire/XEH_PREP.hpp +++ b/addons/fire/XEH_PREP.hpp @@ -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); diff --git a/addons/fire/XEH_postInit.sqf b/addons/fire/XEH_postInit.sqf index 125cb59140..6da3b4b6e5 100644 --- a/addons/fire/XEH_postInit.sqf +++ b/addons/fire/XEH_postInit.sqf @@ -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; +}; diff --git a/addons/fire/XEH_preInit.sqf b/addons/fire/XEH_preInit.sqf index 2fc794454d..894773534a 100644 --- a/addons/fire/XEH_preInit.sqf +++ b/addons/fire/XEH_preInit.sqf @@ -8,6 +8,4 @@ PREP_RECOMPILE_END; #include "initSettings.inc.sqf" -GVAR(burningPlants) = []; - ADDON = true; diff --git a/addons/fire/functions/fnc_burn.sqf b/addons/fire/functions/fnc_burn.sqf index 08d52bd05c..ea5c0a7f4d 100644 --- a/addons/fire/functions/fnc_burn.sqf +++ b/addons/fire/functions/fnc_burn.sqf @@ -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 @@ -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); +}; diff --git a/addons/fire/functions/fnc_burnEffects.sqf b/addons/fire/functions/fnc_burnEffects.sqf new file mode 100644 index 0000000000..eabf29c053 --- /dev/null +++ b/addons/fire/functions/fnc_burnEffects.sqf @@ -0,0 +1,191 @@ +#include "..\script_component.hpp" +/* + * Author: tcvm, johnb43 + * Spawns particle effects for a burning unit. + * + * Arguments: + * 0: Unit + * + * 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; diff --git a/addons/fire/functions/fnc_burnIndicator.sqf b/addons/fire/functions/fnc_burnIndicator.sqf index 13db8862c9..2874b69d11 100644 --- a/addons/fire/functions/fnc_burnIndicator.sqf +++ b/addons/fire/functions/fnc_burnIndicator.sqf @@ -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]; diff --git a/addons/fire/functions/fnc_burnReaction.sqf b/addons/fire/functions/fnc_burnReaction.sqf index 748fbbd60e..42bd64b43c 100644 --- a/addons/fire/functions/fnc_burnReaction.sqf +++ b/addons/fire/functions/fnc_burnReaction.sqf @@ -5,7 +5,7 @@ * * Arguments: * 0: Unit - * 1: Should unit throw its current weapon + * 1: Should unit throw its current weapon (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; }; diff --git a/addons/fire/functions/fnc_burnSimulation.sqf b/addons/fire/functions/fnc_burnSimulation.sqf new file mode 100644 index 0000000000..ed5a338ea5 --- /dev/null +++ b/addons/fire/functions/fnc_burnSimulation.sqf @@ -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 + * 1: Instigator + * + * 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; diff --git a/addons/fire/functions/fnc_fireManagerPFH.sqf b/addons/fire/functions/fnc_fireManagerPFH.sqf index fa8e7fb789..0fce278fe6 100644 --- a/addons/fire/functions/fnc_fireManagerPFH.sqf +++ b/addons/fire/functions/fnc_fireManagerPFH.sqf @@ -5,39 +5,41 @@ * Used to handle external burning objects, not used internally because internal methods are more performant. * * Arguments: - * 0: Unit on fire - * 1: PFH Handle + * 0: Args (not used) + * 1: PFH Handle (not used) * * 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); diff --git a/addons/fire/functions/fnc_isBurning.sqf b/addons/fire/functions/fnc_isBurning.sqf index 3bdbe560be..ba0dd9e13f 100644 --- a/addons/fire/functions/fnc_isBurning.sqf +++ b/addons/fire/functions/fnc_isBurning.sqf @@ -1,10 +1,10 @@ #include "..\script_component.hpp" /* * Author: commy2 - * Check if object is burning. + * Check if unit is burning. * * Arguments: - * 0: Vehicle + * 0: Unit * * 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 diff --git a/addons/fire/functions/fnc_isPlant.sqf b/addons/fire/functions/fnc_isPlant.sqf deleted file mode 100644 index f132fc72be..0000000000 --- a/addons/fire/functions/fnc_isPlant.sqf +++ /dev/null @@ -1,20 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: commy2 - * Check if object is a map placed bush or tree. - * - * Arguments: - * 0: Object - * - * Return Value: - * Is bush or tree? - * - * Example: - * cursorObject call ace_fire_fnc_isPlant - * - * Public: No - */ - -params [["_object", objNull, [objNull]]]; - -_object in nearestTerrainObjects [_object, ["TREE", "SMALL TREE", "BUSH"], 0.1] diff --git a/addons/fire/functions/fnc_medical_canPatDown.sqf b/addons/fire/functions/fnc_medical_canPatDown.sqf index b7efc262b2..758b83b922 100644 --- a/addons/fire/functions/fnc_medical_canPatDown.sqf +++ b/addons/fire/functions/fnc_medical_canPatDown.sqf @@ -18,4 +18,4 @@ params ["", "_patient"]; -[_patient] call FUNC(isBurning) +_patient call FUNC(isBurning) diff --git a/addons/fire/functions/fnc_medical_progress.sqf b/addons/fire/functions/fnc_medical_progress.sqf index 07d99958d6..fd64b5c27d 100644 --- a/addons/fire/functions/fnc_medical_progress.sqf +++ b/addons/fire/functions/fnc_medical_progress.sqf @@ -5,8 +5,8 @@ * * Arguments: * 0: Arguments - * 0: Medic - * 1: Patient + * - 0: Medic (not used) + * - 1: Patient * * Return Value: * Continue pat down @@ -18,6 +18,6 @@ */ params ["_args"]; -_args params ["_medic", "_patient"]; +_args params ["", "_patient"]; -[_patient] call FUNC(isBurning) +_patient call FUNC(isBurning) diff --git a/addons/fire/functions/fnc_medical_success.sqf b/addons/fire/functions/fnc_medical_success.sqf index 78e119a8fa..5538d7d4db 100644 --- a/addons/fire/functions/fnc_medical_success.sqf +++ b/addons/fire/functions/fnc_medical_success.sqf @@ -6,6 +6,8 @@ * Arguments: * 0: Medic * 1: Patient + * 2: Body Part + * 3: Treatment * * 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 { diff --git a/addons/fire/initSettings.inc.sqf b/addons/fire/initSettings.inc.sqf index 97963f3e32..ceacc410ff 100644 --- a/addons/fire/initSettings.inc.sqf +++ b/addons/fire/initSettings.inc.sqf @@ -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; - diff --git a/addons/fire/script_component.hpp b/addons/fire/script_component.hpp index 86ef159aae..cf7b42a2b4 100644 --- a/addons/fire/script_component.hpp +++ b/addons/fire/script_component.hpp @@ -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 diff --git a/addons/zeus/functions/fnc_moduleBurn.sqf b/addons/zeus/functions/fnc_moduleBurn.sqf index 4cf2017b67..e3e78a5f7b 100644 --- a/addons/zeus/functions/fnc_moduleBurn.sqf +++ b/addons/zeus/functions/fnc_moduleBurn.sqf @@ -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; }; }; -