diff --git a/addons/cookoff/ACE_Settings.hpp b/addons/cookoff/ACE_Settings.hpp index df96613ee04..d77a828fade 100644 --- a/addons/cookoff/ACE_Settings.hpp +++ b/addons/cookoff/ACE_Settings.hpp @@ -6,4 +6,16 @@ class ACE_Settings { value = 1; typeName = "BOOL"; }; + class GVAR(enableAmmobox) { + displayName = CSTRING(enableBoxCookoff_name); + description = CSTRING(enableBoxCookoff_tooltip); + value = 1; + typeName = "BOOL"; + }; + class GVAR(enableAmmoCookoff) { + displayName = CSTRING(enableAmmoCookoff_name); + description = CSTRING(enableAmmoCookoff_tooltip); + value = 1; + typeName = "BOOL"; + }; }; diff --git a/addons/cookoff/CfgAmmo.hpp b/addons/cookoff/CfgAmmo.hpp new file mode 100644 index 00000000000..952ec223220 --- /dev/null +++ b/addons/cookoff/CfgAmmo.hpp @@ -0,0 +1,33 @@ +class CfgAmmo { + class ShellBase; + class ace_ammoExplosion: ShellBase { + hit = 10; + indirectHit = 0.5; + indirectHitRange = 1; + soundHit[] = {"", 1, 1, 0}; + typicalSpeed = 100; + explosive = 0; + cost = 300; + model = "\A3\Weapons_F\empty.p3d"; + airFriction = 0; + timeToLive = 1; + explosionTime = 0.001; + soundFly[] = {"",1,1}; + soundEngine[] = {"",1,4}; + explosionEffects = "ExploAmmoExplosion"; + }; + + class SmallSecondary; + class ACE_ammoExplosionLarge: SmallSecondary { + soundHit[] = {"", 1, 1, 0}; + model = "\A3\Weapons_F\empty.p3d"; + soundFly[] = {"",1,1}; + soundEngine[] = {"",1,4}; + soundHit1[] = {"",1,1,1}; + soundHit2[] = {"",1,1,1}; + soundHit3[] = {"",1,1,1}; + supersonicCrackFar[] = {"",1,1,1}; + supersonicCrackNear[] = {"",1,1,1}; + craterEffects = "ImpactEffectsMedium"; + }; +}; diff --git a/addons/cookoff/CfgCloudlets.hpp b/addons/cookoff/CfgCloudlets.hpp index 9b262c75c1c..c1671777054 100644 --- a/addons/cookoff/CfgCloudlets.hpp +++ b/addons/cookoff/CfgCloudlets.hpp @@ -52,3 +52,30 @@ class CfgCloudlets { destroyOnWaterSurfaceOffset = 0; }; }; + +class GVAR(ExploAmmoExplosion) { + class ExploAmmoFlash { + position[] = {0,0,0}; + simulation = "particles"; + type = "ExploAmmoFlash"; + intensity = 1; + interval = 1; + lifeTime = 1; + }; + class LightExplosion { + simulation = "light"; + type = "SparksLight"; + position[] = {0,0,0}; + intensity = 1; + interval = 1; + lifeTime = 0.15; + }; + class ExploAmmoSmoke { + position[] = {0,0,0}; + simulation = "particles"; + type = "AutoCannonFired"; + intensity = 1.5; + interval = 1.5; + lifeTime = 1.5; + }; +}; diff --git a/addons/cookoff/XEH_PREP.hpp b/addons/cookoff/XEH_PREP.hpp index 5b744f92215..897fb10153b 100644 --- a/addons/cookoff/XEH_PREP.hpp +++ b/addons/cookoff/XEH_PREP.hpp @@ -2,5 +2,7 @@ PREP(handleDamage); PREP(engineFire); PREP(cookOff); +PREP(cookOffBox); PREP(blowOffTurret); PREP(secondaryExplosions); +PREP(detonateAmmunition); diff --git a/addons/cookoff/XEH_postInit.sqf b/addons/cookoff/XEH_postInit.sqf index ab4d89b9342..1e17e20893f 100644 --- a/addons/cookoff/XEH_postInit.sqf +++ b/addons/cookoff/XEH_postInit.sqf @@ -2,6 +2,7 @@ [QGVAR(engineFire), FUNC(engineFire)] call CBA_fnc_addEventHandler; [QGVAR(cookOff), FUNC(cookOff)] call CBA_fnc_addEventHandler; +[QGVAR(cookOffBox), FUNC(cookOffBox)] call CBA_fnc_addEventHandler; GVAR(cacheTankDuplicates) = call CBA_fnc_createNamespace; @@ -52,21 +53,31 @@ GVAR(cacheTankDuplicates) = call CBA_fnc_createNamespace; }]; }, nil, ["Wheeled_APC_F"], true] call CBA_fnc_addClassEventHandler; +["ReammoBox_F", "init", { + (_this select 0) addEventHandler ["HandleDamage", { + if (GVAR(enableAmmobox)) then { + ["box", _this] call FUNC(handleDamage); + }; + }]; +}, nil, nil, true] call CBA_fnc_addClassEventHandler; + // secondary explosions ["AllVehicles", "killed", { params ["_vehicle"]; - - if (_vehicle getVariable [QGVAR(enable), GVAR(enable)]) then { + if (_vehicle getVariable [QGVAR(enable),GVAR(enable)]) then { _vehicle call FUNC(secondaryExplosions); + if (_vehicle getVariable [QGVAR(enableAmmoCookoff), GVAR(enableAmmoCookoff)]) then { + [_vehicle, magazinesAmmo _vehicle] call FUNC(detonateAmmunition); + }; }; -}, nil, ["Man"]] call CBA_fnc_addClassEventHandler; +}, nil, ["Man","StaticWeapon"]] call CBA_fnc_addClassEventHandler; // blow off turret effect ["Tank", "killed", { - params ["_vehicle"]; - - if (_vehicle getVariable [QGVAR(enable), GVAR(enable)]) then { - _vehicle call FUNC(blowOffTurret); + if ((_this select 0) getVariable [QGVAR(enable),GVAR(enable)]) then { + if (random 1 < 0.15) then { + (_this select 0) call FUNC(blowOffTurret); + }; }; }] call CBA_fnc_addClassEventHandler; diff --git a/addons/cookoff/config.cpp b/addons/cookoff/config.cpp index cc89a2d9170..0673efaffea 100644 --- a/addons/cookoff/config.cpp +++ b/addons/cookoff/config.cpp @@ -8,7 +8,7 @@ class CfgPatches { requiredVersion = REQUIRED_VERSION; requiredAddons[] = {"ace_common"}; author = ECSTRING(common,ACETeam); - authors[] = {"commy2"}; + authors[] = {"commy2", "Glowbal"}; url = ECSTRING(main,URL); VERSION_CONFIG; }; @@ -18,6 +18,7 @@ class CfgPatches { #include "CfgEden.hpp" #include "CfgEventHandlers.hpp" +#include "CfgAmmo.hpp" #include "CfgCloudlets.hpp" #include "CfgSFX.hpp" #include "CfgVehicles.hpp" diff --git a/addons/cookoff/functions/fnc_cookOff.sqf b/addons/cookoff/functions/fnc_cookOff.sqf index 1f8c929bdf5..890bbff557a 100644 --- a/addons/cookoff/functions/fnc_cookOff.sqf +++ b/addons/cookoff/functions/fnc_cookOff.sqf @@ -129,6 +129,6 @@ if (local _vehicle) then { if (local _vehicle) then { _vehicle setDamage 1; }; - }, [_vehicle, _effects], 4 + random 1] call CBA_fnc_waitAndExecute; - }, [_vehicle, _effects, _positions], 3 + random 2] call CBA_fnc_waitAndExecute; -}, _vehicle, 0.5 + random 0.3] call CBA_fnc_waitAndExecute; + }, [_vehicle, _effects], 4 + random 20] call CBA_fnc_waitAndExecute; + }, [_vehicle, _effects, _positions], 3 + random 15] call CBA_fnc_waitAndExecute; +}, _vehicle, 0.5 + random 5] call CBA_fnc_waitAndExecute; diff --git a/addons/cookoff/functions/fnc_cookOffBox.sqf b/addons/cookoff/functions/fnc_cookOffBox.sqf new file mode 100644 index 00000000000..3c040b5f720 --- /dev/null +++ b/addons/cookoff/functions/fnc_cookOffBox.sqf @@ -0,0 +1,75 @@ +/* + * Author: KoffeinFlummi, commy2, SilentSpike + * Start a cook-off in the given ammo box. + * + * Arguments: + * 0: Ammo box + * + * Return Value: + * None + * + * Example: + * [_box] call ace_cookoff_fnc_cookOffBox + * + * Public: No + */ +#include "script_component.hpp" + +params ["_box"]; + +if (_box getVariable [QGVAR(isCookingOff), false]) exitWith {}; +_box setVariable [QGVAR(isCookingOff), true]; + +if (local _vehicle) then { + [QGVAR(cookOffBox), _box] call CBA_fnc_remoteEvent; +}; + +[{ + params ["_box"]; + + // Box will start smoking + private _smoke = "#particlesource" createVehicleLocal [0,0,0]; + _smoke setParticleClass "AmmoSmokeParticles2"; + _smoke attachTo [_box, [0,0,0]]; + + private _effects = [_smoke]; + + if (isServer) then { + private _sound = createSoundSource ["Sound_Fire", position _box, [], 0]; + _effects pushBack _sound; + }; + + [{ + params ["_box", "_effects"]; + + // These functions are smart and do all the cooking off work + if (local _box) then { + _box call FUNC(secondaryExplosions); + if (_box getVariable [QGVAR(enableAmmoCookoff), GVAR(enableAmmoCookoff)]) then { + [_box, magazinesAmmo _box] call FUNC(detonateAmmunition); + }; + + // This shit is busy being on fire, magazines aren't accessible/usable + clearMagazineCargoGlobal _box; + }; + + // Light the fire (also handles lighting) + private _fire = "#particlesource" createVehicleLocal [0,0,0]; + _fire setParticleClass "AmmoBulletCore"; + _fire attachTo [_box, [0,0,0]]; + + _effects pushBack _fire; + + [{ + params ["_box", "_effects"]; + + { + deleteVehicle _x; + } forEach _effects; + + if (local _box) then { + _box setDamage 1; + }; + }, [_box, _effects], 45 + random 75] call CBA_fnc_waitAndExecute; // Give signifcant time for ammo cookoff to occur (perhaps keep the box alive until all cooked off?) + }, [_box, _effects], 3 + random 15] call CBA_fnc_waitAndExecute; +}, _box, 0.5 + random 5] call CBA_fnc_waitAndExecute; diff --git a/addons/cookoff/functions/fnc_detonateAmmunition.sqf b/addons/cookoff/functions/fnc_detonateAmmunition.sqf new file mode 100644 index 00000000000..211e359bb39 --- /dev/null +++ b/addons/cookoff/functions/fnc_detonateAmmunition.sqf @@ -0,0 +1,112 @@ +/* + * Author: Glowbal + * Detonates ammunition from a vehicle until no ammo left + * + * Arguments: + * 0: vehicle + * + * Return Value: + * None + * + * Example: + * [_vehicle, magazinesAmmo _vehicle] call ace_cookoff_fnc_detonateAmmunition + * + * Public: No + */ +#include "script_component.hpp" +#define MAX_TIME_BETWEEN_AMMO_DET 25 + +params ["_vehicle", "_magazines"]; + +if (isNull _vehicle) exitWith {}; // vehicle got deleted +if (_magazines isEqualTo []) exitWith {}; // nothing to detonate anymore +if (underwater _vehicle) exitWith {}; + +private _magazineIndex = floor random(count _magazines); +private _magazine = _magazines select _magazineIndex; +_magazine params ["_magazineClassname", "_amountOfMagazines"]; + +if (_amountOfMagazines > 0) exitWith { + private _newMagCount = _amountOfMagazines - floor(1 + random(6)); + if (_newMagCount <= 0) then { + _magazines deleteAt _magazineIndex; + } else { + _magazine set [1, _newMagCount]; // clear out the magazine + }; + private _ammo = getText (configFile >> "CfgMagazines" >> _magazineClassname >> "ammo"); + + private _timeBetweenAmmoDetonation = (random 7) * (1 / random (_amountOfMagazines)) min MAX_TIME_BETWEEN_AMMO_DET; + _timeBetweenAmmoDetonation = _timeBetweenAmmoDetonation max 0.1; + + private _speedOfAmmo = getNumber (configFile >> "CfgMagazines" >> _magazineClassname >> "initSpeed"); + private _simulationTime = getNumber (configFile >> "CfgAmmo" >> _ammo >> "simulation"); + private _caliber = getNumber (configFile >> "CfgAmmo" >> _ammo >> "caliber"); + private _simType = getText (configFile >> "CfgAmmo" >> _ammo >> "simulation"); + + private _effect2pos = _vehicle selectionPosition "destructionEffect2"; + + private _spawnProjectile = { + params ["_vehicle", "_ammo", "_speed", "_flyAway"]; + + private _spawnPos = _vehicle modelToWorld [-0.2 + (random 0.4), -0.2 + (random 0.4), random 3]; + if (_spawnPos select 2 < 0) then { + _spawnPos set [2, 0]; + }; + private _projectile = _ammo createVehicle [0,0,0]; + _projectile setPos _spawnPos; + if (_flyAway) then { + private _vectorAmmo = [(-1 + (random 2)), (-1 + (random 2)), -0.2 + (random 1)]; + private _velVec = _vectorAmmo vectorMultiply _speed; + _projectile setVectorDir _velVec; + _projectile setVelocity _velVec; + [ACE_player, _projectile, [1,0,0,1]] call EFUNC(frag,addTrack); + } else { + _projectile setDamage 1; + }; + + _projectile; + }; + + private _speed = random (_speedOfAmmo / 10) max 1; + + if (toLower _simType == "shotbullet") then { + private _sound = selectRandom [QUOTE(PATHTO_R(sounds\light_crack_close.wss)), QUOTE(PATHTO_R(sounds\light_crack_close_filtered.wss)), QUOTE(PATHTO_R(sounds\heavy_crack_close.wss)), QUOTE(PATHTO_R(sounds\heavy_crack_close_filtered.wss))]; + playSound3D [_sound, objNull, false, (getPosASL _vehicle), 2, 1, 1250]; + + if (random 1 < 0.6) then { + [_vehicle, _ammo, _speed, true] call _spawnProjectile; + }; + }; + if (toLower _simType == "shotshell") then { + private _sound = selectRandom [QUOTE(PATHTO_R(sounds\heavy_crack_close.wss)), QUOTE(PATHTO_R(sounds\heavy_crack_close_filtered.wss))]; + playSound3D [_sound, objNull, false, (getPosASL _vehicle), 2, 1, 1300]; + + if (random 1 < 0.15) then { + [_vehicle, _ammo, _speed, random 1 < 0.15] call _spawnProjectile; + }; + }; + if (toLower _simType == "shotgrenade") then { + if (random 1 < 0.9) then { + _speed = 0; + }; + [_vehicle, _ammo, _speed, random 1 < 0.5] call _spawnProjectile; + }; + if (toLower _simType == "shotrocket" || {toLower _simType == "shotmissile"}) then { + if (random 1 < 0.1) then { + private _sound = selectRandom [QUOTE(PATHTO_R(sounds\cannon_crack_close.wss)), QUOTE(PATHTO_R(sounds\cannon_crack_close_filtered.wss))]; + playSound3D [_sound, objNull, false, (getPosASL _vehicle), 3, 1, 1600]; + + [_vehicle, _ammo, _speed, random 1 < 0.3] call _spawnProjectile; + } else { + "ACE_ammoExplosionLarge" createvehicle (_vehicle modelToWorld _effect2pos); + }; + }; + if (toLower _simType in ["shotdirectionalbomb", "shotilluminating", "shotmine"]) then { + if (random 1 < 0.5) then { + [_vehicle, _ammo, 0, false] call _spawnProjectile; + }; + }; + + [FUNC(detonateAmmunition), [_vehicle, _magazines], _timeBetweenAmmoDetonation] call CBA_fnc_waitAndExecute; +}; +[FUNC(detonateAmmunition), [_vehicle, _magazines], random 3] call CBA_fnc_waitAndExecute; diff --git a/addons/cookoff/functions/fnc_handleDamage.sqf b/addons/cookoff/functions/fnc_handleDamage.sqf index b6ce9f45c94..331c1d177ad 100644 --- a/addons/cookoff/functions/fnc_handleDamage.sqf +++ b/addons/cookoff/functions/fnc_handleDamage.sqf @@ -16,7 +16,7 @@ #include "script_component.hpp" params ["_simulationType", "_thisHandleDamage"]; -_thisHandleDamage params ["_vehicle", "", "_damage", "", "_ammo", "_hitIndex"]; +_thisHandleDamage params ["_vehicle", "", "_damage", "_source", "_ammo", "_hitIndex", "_shooter"]; // it's already dead, who cares? if (damage _vehicle >= 1) exitWith {}; @@ -58,12 +58,12 @@ if (_simulationType == "car") exitWith { if (_simulationType == "tank") exitWith { // determine ammo storage location - private _ammoLocationHitpoint = getText (_vehicle call CBA_fnc_getObjectConfig >> QGVAR(ammoLocation)); + private _ammoLocationHitpoint = getText (_vehicle call CBA_fnc_getObjectConfig >> QGVAR(ammoLocation)); if (_hitIndex in (GVAR(cacheTankDuplicates) getVariable (typeOf _vehicle))) then { _hitpoint = "#subturret"; }; - + // ammo was hit, high chance for cook-off if (_hitpoint == _ammoLocationHitpoint) then { if (_damage > 0.5 && {random 1 < 0.7}) then { @@ -82,3 +82,37 @@ if (_simulationType == "tank") exitWith { _damage }; }; + +if (_simulationType == "box") exitWith { + if (_hitpoint == "#structural" && _damage > 0.5) then { + // Almost always catch fire when hit by an explosive + if (IS_EXPLOSIVE_AMMO(_ammo)) then { + _vehicle call FUNC(cookOffBox); + } else { + // Need magazine to check for tracers + private _mag = ""; + if (_source == _shooter) then { + _mag = currentMagazine _source; + } else { + _mag = _source currentMagazineTurret ([_shooter] call CBA_fnc_turretPath); + }; + private _magCfg = configFile >> "CfgMagazines" >> _mag; + + // Magazine could have changed during flight time (just ignore if so) + if (getText (_magCfg >> "ammo") == _ammo) then { + // If magazine's tracer density is high enough then low chance for cook off + private _tracers = getNumber (_magCfg >> "tracersEvery"); + if (_tracers >= 1 && {_tracers <= 4}) then { + if (random 1 < _oldDamage*0.05) then { + _vehicle call FUNC(cookOffBox); + }; + }; + }; + }; + + // prevent destruction, let cook-off handle it if necessary + _damage min 0.89 + } else { + _damage + }; +}; diff --git a/addons/cookoff/sounds/cannon_crack_close.wss b/addons/cookoff/sounds/cannon_crack_close.wss new file mode 100644 index 00000000000..b45ea96ab01 Binary files /dev/null and b/addons/cookoff/sounds/cannon_crack_close.wss differ diff --git a/addons/cookoff/sounds/cannon_crack_close_filtered.wss b/addons/cookoff/sounds/cannon_crack_close_filtered.wss new file mode 100644 index 00000000000..4ddb18539b3 Binary files /dev/null and b/addons/cookoff/sounds/cannon_crack_close_filtered.wss differ diff --git a/addons/cookoff/sounds/heavy_crack_close.wss b/addons/cookoff/sounds/heavy_crack_close.wss new file mode 100644 index 00000000000..95cc68d90ec Binary files /dev/null and b/addons/cookoff/sounds/heavy_crack_close.wss differ diff --git a/addons/cookoff/sounds/heavy_crack_close_filtered.wss b/addons/cookoff/sounds/heavy_crack_close_filtered.wss new file mode 100644 index 00000000000..1b4520cd4ad Binary files /dev/null and b/addons/cookoff/sounds/heavy_crack_close_filtered.wss differ diff --git a/addons/cookoff/sounds/light_crack_close.wss b/addons/cookoff/sounds/light_crack_close.wss new file mode 100644 index 00000000000..ef284f24efd Binary files /dev/null and b/addons/cookoff/sounds/light_crack_close.wss differ diff --git a/addons/cookoff/sounds/light_crack_close_filtered.wss b/addons/cookoff/sounds/light_crack_close_filtered.wss new file mode 100644 index 00000000000..b1c76fa8ed2 Binary files /dev/null and b/addons/cookoff/sounds/light_crack_close_filtered.wss differ diff --git a/addons/cookoff/stringtable.xml b/addons/cookoff/stringtable.xml index 60339b2f9a2..21ddb96a7a0 100644 --- a/addons/cookoff/stringtable.xml +++ b/addons/cookoff/stringtable.xml @@ -1,4 +1,4 @@ - + @@ -31,5 +31,17 @@ 잔해(포탑) 残骸(タレット) + + Enable ammo box cook off + + + Enables cooking off of ammo boxes. + + + Enable Ammunition cook off + + + Enables Ammunition cook off. Fires ammunition projectiles while vehicle is on fire and has ammunition. + diff --git a/addons/grenades/functions/fnc_incendiary.sqf b/addons/grenades/functions/fnc_incendiary.sqf index c143a3c63f7..098e2d5e20d 100644 --- a/addons/grenades/functions/fnc_incendiary.sqf +++ b/addons/grenades/functions/fnc_incendiary.sqf @@ -146,9 +146,16 @@ if (isServer) then { //systemChat format ["burn: %1", _x]; // --- destroy nearby static weapons and ammo boxes - if (_x isKindOf "StaticWeapon" || {_x isKindOf "ReammoBox_F"} || {_x isKindOf "ACE_RepairItem_Base"}) then { + if (_x isKindOf "StaticWeapon" || {_x isKindOf "ACE_RepairItem_Base"}) then { _x setDamage 1; }; + if (_x isKindOf "ReammoBox_F") then { + if ("ace_cookoff" call EFUNC(common,isModLoaded) && {EGVAR(cookoff,enable)}) then { + _x call EFUNC(cookoff,cookOffBox); + } else { + _x setDamage 1; + }; + }; // --- delete nearby ground weapon holders if (_x isKindOf "WeaponHolder" || {_x isKindOf "WeaponHolderSimulated"}) then {