@jtff/miztemplate-lib 3.7.9 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/ci/build.js +0 -0
- package/ci/clean.js +0 -0
- package/ci/ftpupload.js +0 -0
- package/ci/gdrive-upload.js +0 -0
- package/ci/get-mizfiles.js +0 -0
- package/ci/getset-version.js +0 -0
- package/ci/inject-scripts.js +0 -0
- package/ci/prepare-nextversion.js +0 -0
- package/ci/release.js +0 -0
- package/ci/template-update.js +0 -0
- package/index.js +0 -0
- package/lib/jtff-lib-ci.js +0 -0
- package/lib/mizlib.js +0 -0
- package/lua/lib/Hercules_Cargo.lua +0 -0
- package/lua/lib/HoundElint.lua +10861 -10861
- package/lua/lib/Moose_.lua +0 -0
- package/lua/lib/Splash_Damage_main.lua +2151 -2151
- package/lua/lib/funkman.lua +0 -0
- package/lua/lib/mist.lua +0 -0
- package/lua/lib/skynet-iads-compiled.lua +0 -0
- package/lua/settings/settings-RAT.lua +0 -0
- package/lua/settings/settings-airboss.lua +0 -0
- package/lua/settings/settings-atis.lua +0 -0
- package/lua/settings/settings-awacs.lua +0 -0
- package/lua/settings/settings-awacsondemand.lua +0 -0
- package/lua/settings/settings-beacons.lua +0 -0
- package/lua/settings/settings-capwarzone.lua +0 -0
- package/lua/settings/settings-capzone.lua +0 -0
- package/lua/settings/settings-elint.lua +0 -0
- package/lua/settings/settings-fac_ranges.lua +0 -0
- package/lua/settings/settings-foxzone.lua +0 -0
- package/lua/settings/settings-funkman.lua +0 -0
- package/lua/settings/settings-global.lua +0 -0
- package/lua/settings/settings-intercept.lua +0 -0
- package/lua/settings/settings-logistics.lua +0 -0
- package/lua/settings/settings-ondemandawacs.lua +0 -0
- package/lua/settings/settings-ondemandtankers.lua +0 -0
- package/lua/settings/settings-pedros.lua +0 -0
- package/lua/settings/settings-qra.lua +24 -24
- package/lua/settings/settings-ranges.lua +0 -0
- package/lua/settings/settings-reapers.lua +0 -0
- package/lua/settings/settings-sams.lua +0 -0
- package/lua/settings/settings-skynet.lua +0 -0
- package/lua/settings/settings-tankers.lua +0 -0
- package/lua/settings/settings-training_ranges.lua +0 -0
- package/lua/src/010-root_menus.lua +0 -0
- package/lua/src/020-mission_functions.lua +10 -11
- package/lua/src/110-set_clients.lua +123 -25
- package/lua/src/120-tankers.lua +0 -0
- package/lua/src/130-airboss.lua +0 -0
- package/lua/src/135-pedro.lua +0 -0
- package/lua/src/140-beacons.lua +0 -0
- package/lua/src/150-awacs.lua +0 -0
- package/lua/src/160-atis.lua +0 -0
- package/lua/src/170-cap_zone_training.lua +0 -0
- package/lua/src/172-cap_zone_war.lua +0 -0
- package/lua/src/173-fox_zone_training.lua +0 -0
- package/lua/src/174-qra-scenario.lua +362 -362
- package/lua/src/176-random_air_traffic.lua +0 -0
- package/lua/src/178-training-intercept.lua +0 -0
- package/lua/src/180-logistics.lua +0 -0
- package/lua/src/190-ranges.lua +0 -0
- package/lua/src/191-sams.lua +0 -0
- package/lua/src/193-training_ranges.lua +0 -0
- package/lua/src/195-reaper-ondemand.lua +0 -0
- package/lua/src/196-fac_ranges.lua +0 -0
- package/lua/src/197-elint-ondemand.lua +0 -0
- package/lua/src/199-skynet.lua +0 -0
- package/lua/src/200-mission.lua +0 -0
- package/package.json +1 -1
- package/resources/radios/Caucasus/354th-caucasus.lua +0 -0
- package/resources/radios/Caucasus/494th-caucasus.lua +0 -0
- package/resources/radios/Caucasus/79th-SUFA-caucasus.lua +0 -0
- package/resources/radios/Caucasus/79th-caucasus.lua +0 -0
- package/resources/radios/Caucasus/ec25-D-caucasus.lua +0 -0
- package/resources/radios/Caucasus/ec25-caucasus.lua +0 -0
- package/resources/radios/Caucasus/vf84-a-caucasus.lua +0 -0
- package/resources/radios/Caucasus/vf84-b-caucasus.lua +0 -0
- package/resources/radios/Caucasus/vfa-131-caucasus.lua +0 -0
- package/resources/radios/Caucasus/vmfa-314-caucasus.lua +0 -0
- package/resources/radios/MarianaIslands/354th-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/494th-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/79th-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/ec25-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/vf84-a-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/vf84-b-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/vfa-131-marianas.lua +0 -0
- package/resources/radios/MarianaIslands/vmfa-314-marianas.lua +0 -0
- package/resources/radios/Nevada/354th-nttr.lua +0 -0
- package/resources/radios/Nevada/494th-nttr.lua +0 -0
- package/resources/radios/Nevada/79th-nttr.lua +0 -0
- package/resources/radios/Nevada/ec25-nttr.lua +0 -0
- package/resources/radios/Nevada/vf84-a-nttr.lua +0 -0
- package/resources/radios/Nevada/vf84-b-nttr.lua +0 -0
- package/resources/radios/Nevada/vfa-131-nttr.lua +0 -0
- package/resources/radios/Nevada/vmfa-314-nttr.lua +0 -0
- package/resources/radios/PersianGulf/354th-persian.lua +0 -0
- package/resources/radios/PersianGulf/3_30-persian.lua +0 -0
- package/resources/radios/PersianGulf/494th-persian.lua +0 -0
- package/resources/radios/PersianGulf/79th-persian.lua +0 -0
- package/resources/radios/PersianGulf/ec25-persian.lua +0 -0
- package/resources/radios/PersianGulf/vf84-a-persian.lua +0 -0
- package/resources/radios/PersianGulf/vf84-b-persian.lua +0 -0
- package/resources/radios/PersianGulf/vfa-131-persian.lua +0 -0
- package/resources/radios/PersianGulf/vmfa-314-persian.lua +0 -0
- package/resources/radios/SinaiMap/354th-sinai.lua +0 -0
- package/resources/radios/SinaiMap/494th-sinai.lua +0 -0
- package/resources/radios/SinaiMap/79th-SUFA-sinai.lua +0 -0
- package/resources/radios/SinaiMap/79th-sinai.lua +0 -0
- package/resources/radios/SinaiMap/ec25-D-sinai.lua +0 -0
- package/resources/radios/SinaiMap/ec25-sinai.lua +0 -0
- package/resources/radios/SinaiMap/vf84-a-sinai.lua +0 -0
- package/resources/radios/SinaiMap/vf84-b-sinai.lua +0 -0
- package/resources/radios/SinaiMap/vfa-131-sinai.lua +0 -0
- package/resources/radios/SinaiMap/vmfa-314-sinai.lua +0 -0
- package/resources/radios/Syria/354th-syria.lua +0 -0
- package/resources/radios/Syria/494th-syria.lua +0 -0
- package/resources/radios/Syria/79th-SUFA-syria.lua +0 -0
- package/resources/radios/Syria/79th-syria.lua +0 -0
- package/resources/radios/Syria/ec25-D-syria.lua +0 -0
- package/resources/radios/Syria/ec25-syria.lua +0 -0
- package/resources/radios/Syria/vf84-a-syria.lua +0 -0
- package/resources/radios/Syria/vf84-b-syria.lua +0 -0
- package/resources/radios/Syria/vfa-131-syria.lua +0 -0
- package/resources/radios/Syria/vmfa-314-syria.lua +0 -0
- package/resources/sounds/AICSAR/helodown.ogg +0 -0
- package/resources/sounds/AICSAR/initialnotok.ogg +0 -0
- package/resources/sounds/AICSAR/initialok.ogg +0 -0
- package/resources/sounds/AICSAR/pilotdown.ogg +0 -0
- package/resources/sounds/AICSAR/pilotinhelo.ogg +0 -0
- package/resources/sounds/AICSAR/pilotkia.ogg +0 -0
- package/resources/sounds/AICSAR/pilotrescued.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/AIRBOSS-Noise.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/AIRBOSS-RadioClick.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/AIRBOSS-SpinIt.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-BolterBolter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-CallTheBall.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Check.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-ClearedToLand.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-ComeLeft.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-ComeLeft_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-DepartAndReenter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-ExpectHeavyWaveoff.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-ExpectSpot5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-ExpectSpot75.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Fast.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Fast_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-FoulDeck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-High.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-High_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Idle.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-LongInTheGroove.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Low.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Low_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-PaddlesContact.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Power.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Power_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-RightForLineup.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-RightForLineup_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-RogerBall.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Slow.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Slow_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-Stabilized.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-WaveOff.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/LSO-WelcomeAboard.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Affirmative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Altimeter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-BRC.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-CarrierTurnToHeading.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Case.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-CharlieTime.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-ClearedForRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-DeckClosed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Degrees.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Expected.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-FlyYourNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-HoldAtAngels.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Hours.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-MarshalRadial.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Negative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-NewFB.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Ops.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Point.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Recovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-RecoveryOpsStopped.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-RecoveryPausedNotice.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-RecoveryPausedResumed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-ReportSeeMe.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-ResumeRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-Roger.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-SayNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-StackFull.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/MARSHAL-StartingRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Angels.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Ball.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-BingoFuel.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Commencing.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-For.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-GasAtDivert.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-GasAtTanker.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Harrier.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Hawkeye.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Hornet.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-MarkingMoms.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Marshal.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Point.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Skyhawk.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-State.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Tomcat.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundfiles/PILOT-Viking.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-BolterBolter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-CallTheBall.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Check.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-ClearedToLand.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-ComeLeft.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-ComeLeft_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-DepartAndReenter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-ExpectHeavyWaveoff.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-ExpectSpot5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-ExpectSpot75.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Fast.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Fast_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-FoulDeck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-High.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-High_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Idle.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-LongInTheGroove.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Low.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Low_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-PaddlesContact.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Power.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Power_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-RightForLineup.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-RightForLineup_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-RogerBall.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Slow.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Slow_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-Stabilized.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-WaveOff.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO FF/LSO-WelcomeAboard.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-BolterBolter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-CallTheBall.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Check.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-ClearedToLand.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-ComeLeft.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-ComeLeft_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-DepartAndReenter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-ExpectHeavyWaveoff.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-ExpectSpot5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-ExpectSpot75.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Fast.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Fast_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-FoulDeck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-High.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-High_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Idle.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-LongInTheGroove.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Low.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Low_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-PaddlesContact.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Power.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Power_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-RightForLineup.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-RightForLineup_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-RogerBall.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Slow.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Slow_Loud.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-Stabilized.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-WaveOff.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack LSO Raynor/LSO-WelcomeAboard.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Affirmative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Altimeter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-BRC.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-CarrierTurnToHeading.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Case.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-CharlieTime.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-ClearedForRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-DeckClosed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Degrees.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Expected.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-FlyYourNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-HoldAtAngels.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Hours.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-MarshalRadial.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Negative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-NewFB.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Ops.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Point.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Recovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-RecoveryOpsStopped.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-RecoveryPausedNotice.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-RecoveryPausedResumed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-ReportSeeMe.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-ResumeRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-Roger.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-SayNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-StackFull.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal FF/MARSHAL-StartingRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Affirmative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Altimeter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-BRC.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-CarrierTurnToHeading.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Case.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-CharlieTime.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-ClearedForRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-DeckClosed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Degrees.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Expected.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-FlyYourNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-HoldAtAngels.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Hours.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-MarshalRadial.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Negative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-NewFB.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Ops.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Point.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Recovery-2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Recovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-RecoveryOpsStopped.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-RecoveryPausedNotice.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-RecoveryPausedResumed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-ReportSeeMe.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-ResumeRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-Roger.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-SayNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-StackFull.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Gabriella/MARSHAL-StartingRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Affirmative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Altimeter.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-BRC.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-CarrierTurnToHeading.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Case.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-CharlieTime.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-ClearedForRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-DeckClosed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Degrees.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Expected.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-FlyYourNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-HoldAtAngels.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Hours.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-MarshalRadial.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N0.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N1.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N2.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N3.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N4.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N5.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N6.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N7.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N8.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-N9.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Negative.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-NewFB.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Ops.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Point.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-RadioCheck.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Recovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-RecoveryOpsStopped.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-RecoveryPausedNotice.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-RecoveryPausedResumed.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-ReportSeeMe.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-ResumeRecovery.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-Roger.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-SayNeedles.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-StackFull.ogg +0 -0
- package/resources/sounds/AIRBOSS/Airboss Soundpack Marshal Raynor/MARSHAL-StartingRecovery.ogg +0 -0
- package/resources/sounds/CTLD CSAR/beacon.ogg +0 -0
- package/resources/sounds/CTLD CSAR/beaconsilent.ogg +0 -0
- package/resources/sounds/Misc/.gitkeep +0 -0
- package/resources/sounds/Misc/2_Bips.ogg +0 -0
- package/resources/sounds/Misc/Bip.ogg +0 -0
- package/resources/sounds/Misc/SCRAMBLE QRA.ogg +0 -0
- package/resources/sounds/Misc/crash_wood.ogg +0 -0
- package/scripts/build.js +0 -0
- package/scripts/clean.js +0 -0
- package/scripts/ftpupload.js +0 -0
- package/scripts/gdrive-upload.js +0 -0
- package/scripts/get-mizfiles.js +0 -0
- package/scripts/getset-version.js +0 -0
- package/scripts/inject-scripts.js +0 -0
- package/scripts/prepare-nextversion.js +0 -0
- package/scripts/release.js +0 -0
- package/scripts/template-update.js +0 -0
|
@@ -1,2151 +1,2151 @@
|
|
|
1
|
-
--[[
|
|
2
|
-
04 April 2025 (Stevey666) - 3.1
|
|
3
|
-
- Set default cluster munitions option to false, set this to true in the options if you want it
|
|
4
|
-
- Added missing radio commands for Cascade Scaling
|
|
5
|
-
- Adjust default cascading to 2 (from 1)
|
|
6
|
-
- Adjusted Ural-4320 to be a tanker and ammo carrier for cargo cookoff
|
|
7
|
-
- Prevent weapons not in the list from being tracked
|
|
8
|
-
- Moved some logging behind the debug mode flag
|
|
9
|
-
- Ordnance Protection, added a max height ordnance protection will snap explosion to ground
|
|
10
|
-
- Ordnance Protection, fixed enable/disable option
|
|
11
|
-
- Added Giant Explosion feature
|
|
12
|
-
- Adjusted some hydra70 values on recom. from ETBSmorgan
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
09 March 2025 (Stevey666) - 3.0
|
|
16
|
-
- Added ordinance protection gives a few options - stop the additional larger_explosion that tends to blow up your own bombs if theyre dropped at the same place if its within x m
|
|
17
|
-
- Additional ordnance protection option that will cause a snap to ground larger_explosion if its within x meters of a recent larger explosion and within x seconds (can set in options)
|
|
18
|
-
- Added vehicle scanning around a weapon to allow for..
|
|
19
|
-
- Cook offs - you can set vehicles that will cook off i.e ammo trucks, number of explosions, debris explosions, power adjustable
|
|
20
|
-
- Fuel/Tanker explosion and flames - when a fuel tanker blows it will through up a big flame - adjustable in the scripts
|
|
21
|
-
- Added section for vehicles for the above
|
|
22
|
-
- Added radio commands for everything
|
|
23
|
-
- Added in cluster munitions changes (note: barely tested, its not particularly accurate or that useful at this point so leaving disabled)
|
|
24
|
-
- Potential bug - testing, stacking too many units together may cause a MIST error if you're using mist
|
|
25
|
-
|
|
26
|
-
- Setting this as 3.0 as I'd like to be responsive to requests, updates etc - creating a new fork to track this
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
10 Feb 2025 (Stevey666) - 2.0.7
|
|
30
|
-
- Fixed AGM 154/Adjusted weapons
|
|
31
|
-
- Added overall damage scaling
|
|
32
|
-
- Added modifier for shaped charges (i.e. Mavericks), adjusted weapon list accordingly
|
|
33
|
-
- Adjusted blast radius and damage calculations, created option for dynamic blast radius
|
|
34
|
-
- Adjusted cascading explosions, added additional "cascade_scaling" modifier and cascade explode threshold modifier. Units wont explode on initial impact unless health drops under threshold
|
|
35
|
-
- Added always_cascade_explode option so you can set it to the old ways of making everything in the blast wave go kaboom
|
|
36
|
-
- Added in game radio commands to change the new options ingame without having to reload everything in mission editor to test it out
|
|
37
|
-
|
|
38
|
-
12 November 2024 (by JGi | Quéton 1-1)
|
|
39
|
-
- Tweak down radius 100>90 (Thanks Arhibeau)
|
|
40
|
-
- Tweak down some values
|
|
41
|
-
|
|
42
|
-
20 January 2024 (by JGi | Quéton 1-1)
|
|
43
|
-
- Added missing weapons to explTable
|
|
44
|
-
- Sort weapons in explTable by type
|
|
45
|
-
- Added aircraft type in log when missing
|
|
46
|
-
|
|
47
|
-
03 May 2023 (KERV)
|
|
48
|
-
Correction AGM 154 (https://forum.dcs.world/topic/289290-splash-damage-20-script-make-explosions-better/page/5/#comment-5207760)
|
|
49
|
-
|
|
50
|
-
06 March 2023 (Kerv)
|
|
51
|
-
- Add some data for new ammunition
|
|
52
|
-
|
|
53
|
-
16 April 2022
|
|
54
|
-
spencershepard (GRIMM):
|
|
55
|
-
- Added new/missing weapons to explTable
|
|
56
|
-
- Added new option rocket_multiplier
|
|
57
|
-
|
|
58
|
-
31 December 2021
|
|
59
|
-
spencershepard (GRIMM):
|
|
60
|
-
- Added many new weapons
|
|
61
|
-
- Added filter for weapons.shells events
|
|
62
|
-
- Fixed mission weapon message option
|
|
63
|
-
- Changed default for damage_model option
|
|
64
|
-
|
|
65
|
-
21 December 2021
|
|
66
|
-
spencershepard (GRIMM):
|
|
67
|
-
SPLASH DAMAGE 2.0:
|
|
68
|
-
- Added blast wave effect to add timed and scaled secondary explosions on top of game objects
|
|
69
|
-
- Object geometry within blast wave changes damage intensity
|
|
70
|
-
- Damage boost for structures since they are hard to kill, even if very close to large explosions
|
|
71
|
-
- Increased some rocket values in explTable
|
|
72
|
-
- Missing weapons from explTable will display message to user and log to DCS.log so that we can add what's missing
|
|
73
|
-
- Damage model for ground units that will disable their weapons and ability to move with partial damage before they are killed
|
|
74
|
-
- Added options table to allow easy adjustments before release
|
|
75
|
-
- General refactoring and restructure
|
|
76
|
-
|
|
77
|
-
28 October 2020
|
|
78
|
-
FrozenDroid:
|
|
79
|
-
- Uncommented error logging, actually made it an error log which shows a message box on error.
|
|
80
|
-
- Fixed the too restrictive weapon filter (took out the HE warhead requirement)
|
|
81
|
-
|
|
82
|
-
2 October 2020
|
|
83
|
-
FrozenDroid:
|
|
84
|
-
- Added error handling to all event handler and scheduled functions. Lua script errors can no longer bring the server down.
|
|
85
|
-
- Added some extra checks to which weapons to handle, make sure they actually have a warhead (how come S-8KOM's don't have a warhead field...?)
|
|
86
|
-
--]]
|
|
87
|
-
|
|
88
|
-
----[[ ##### SCRIPT CONFIGURATION ##### ]]----
|
|
89
|
-
splash_damage_options = {
|
|
90
|
-
--debug options
|
|
91
|
-
["game_messages"] = false, --enable some messages on screen
|
|
92
|
-
["debug"] = false, --enable debugging messages
|
|
93
|
-
["weapon_missing_message"] = false, --false disables messages alerting you to weapons missing from the explTable
|
|
94
|
-
["track_pre_explosion_debug"] = false, --Toggle to enable/disable pre-explosion tracking debugging
|
|
95
|
-
|
|
96
|
-
["enable_radio_menu"] = true, --enables the in-game radio menu for modifying settings
|
|
97
|
-
|
|
98
|
-
["static_damage_boost"] = 2000, --apply extra damage to Unit.Category.STRUCTUREs with wave explosions
|
|
99
|
-
["wave_explosions"] = true, --secondary explosions on top of game objects, radiating outward from the impact point and scaled based on size of object and distance from weapon impact point
|
|
100
|
-
["larger_explosions"] = true, --secondary explosions on top of weapon impact points, dictated by the values in the explTable
|
|
101
|
-
["damage_model"] = true, --allow blast wave to affect ground unit movement and weapons
|
|
102
|
-
["blast_search_radius"] = 90, --this is the max size of any blast wave radius, since we will only find objects within this zone
|
|
103
|
-
["cascade_damage_threshold"] = 0.1, --if the calculated blast damage doesn't exceed this value, there will be no secondary explosion damage on the unit. If this value is too small, the appearance of explosions far outside of an expected radius looks incorrect.
|
|
104
|
-
["blast_stun"] = false, --not implemented
|
|
105
|
-
["unit_disabled_health"] = 30, --if health is below this value after our explosions, disable its movement
|
|
106
|
-
["unit_cant_fire_health"] = 40, --if health is below this value after our explosions, set ROE to HOLD to simulate damage weapon systems
|
|
107
|
-
["infantry_cant_fire_health"] = 60, --if health is below this value after our explosions, set ROE to HOLD to simulate severe injury
|
|
108
|
-
|
|
109
|
-
["rocket_multiplier"] = 1.3, --multiplied by the explTable value for rockets
|
|
110
|
-
["overall_scaling"] = 1, --overall scaling for explosive power
|
|
111
|
-
|
|
112
|
-
["apply_shaped_charge_effects"] = true, --apply reduction in blastwave etc for shaped charge munitions
|
|
113
|
-
["shaped_charge_multiplier"] = 0.2, --multiplier that reduces blast radius and explosion power for shaped charge munitions.
|
|
114
|
-
|
|
115
|
-
["use_dynamic_blast_radius"] = true, --if true, blast radius is calculated from explosion power; if false, blast_search_radius (90) is used
|
|
116
|
-
["dynamic_blast_radius_modifier"] = 2, --multiplier for the blast radius
|
|
117
|
-
|
|
118
|
-
["cascade_scaling"] = 2, --multiplier for secondary (cascade) blast damage, 1 damage fades out too soon, 2 or 3 damage seems a good balance
|
|
119
|
-
["cascade_explode_threshold"] = 60, --only trigger cascade explosion if the unit's current health is <= this percent of its maximum, setting can help blow nearby jeeps but not tanks
|
|
120
|
-
["always_cascade_explode"] = false, --switch if you want everything to explode like with the original script
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
--track_pre_explosion/enable_cargo_effects should both be the same value
|
|
124
|
-
["track_pre_explosion"] = true, --Toggle to enable/disable pre-explosion tracking
|
|
125
|
-
["enable_cargo_effects"] = true, --Toggle for enabling/disabling cargo explosions and cook-offs
|
|
126
|
-
["cargo_damage_threshold"] = 60, --Health % below which cargo explodes (0 = destroyed only)
|
|
127
|
-
["debris_effects"] = true, --Enable debris from cargo cook-offs
|
|
128
|
-
["debris_power"] = 1, --Power of each debris explosion
|
|
129
|
-
["debris_count_min"] = 6, --Minimum debris pieces per cook-off
|
|
130
|
-
["debris_count_max"] = 12, --Maximum debris pieces per cook-off
|
|
131
|
-
["debris_max_distance"] = 10, --Max distance debris can travel (meters), the min distance from the vehicle will be 10% of this
|
|
132
|
-
|
|
133
|
-
["ordnance_protection"] = true, --Toggle ordinance protection features
|
|
134
|
-
["ordnance_protection_radius"] = 10, --Distance in meters to protect nearby bombs
|
|
135
|
-
["detect_ordnance_destruction"] = true, --Toggle detection of ordnance destroyed by large explosions
|
|
136
|
-
["snap_to_ground_if_destroyed_by_large_explosion"] = true, --If the ordnance protection fails or is disabled we can snap larger_explosions to the ground (if enabled - power as set in weapon list) - so an explosion still does hit the ground
|
|
137
|
-
["max_snapped_height"] = 80, --max height it will snap to ground from
|
|
138
|
-
["recent_large_explosion_snap"] = true, --enable looking for a recent large_explosion generated by the script
|
|
139
|
-
["recent_large_explosion_range"] = 100, --range its looking for in meters for a recent large_explosion generated by the script
|
|
140
|
-
["recent_large_explosion_time"] = 4, --in seconds how long ago there was a recent large_explosion generated by the script
|
|
141
|
-
|
|
142
|
-
--Cluster bomb settings
|
|
143
|
-
["cluster_enabled"] = false,
|
|
144
|
-
["cluster_base_length"] = 150, --Base forward spread (meters)
|
|
145
|
-
["cluster_base_width"] = 200, --Base lateral spread (meters)
|
|
146
|
-
["cluster_max_length"] = 300, --Max forward spread (meters)
|
|
147
|
-
["cluster_max_width"] = 400, --Max lateral spread (meters)
|
|
148
|
-
["cluster_min_length"] = 100, --Min forward spread
|
|
149
|
-
["cluster_min_width"] = 150, --Min lateral spread
|
|
150
|
-
["cluster_bomblet_reductionmodifier"] = true, --Use equation to reduce number of bomblets (to make it look better)
|
|
151
|
-
["cluster_bomblet_damage_modifier"] = 1, --Adjustable global modifier for bomblet explosive power
|
|
152
|
-
|
|
153
|
-
--Giant Explosion Options - Remember, any target you want to blow up needs to be named "GiantExplosionTarget(X)" (X) being any value/name etc
|
|
154
|
-
["giant_explosion_enabled"] = true, --Toggle to enable/disable Giant Explosion
|
|
155
|
-
["giant_explosion_power"] = 6000, --Power in kg of TNT (default 8 tons)
|
|
156
|
-
["giant_explosion_scale"] = 1, --Size scale factor (default 1)
|
|
157
|
-
["giant_explosion_duration"] = 3.0, --Total duration in seconds (default 3s)
|
|
158
|
-
["giant_explosion_count"] = 250, --Number of explosions (default 300)
|
|
159
|
-
["giant_explosion_target_static"] = true, --Toggle to true for static targets (store position once), false for dynamic (update every second)
|
|
160
|
-
["giant_explosion_poll_rate"] = 1, --Polling rate in seconds for flag checks (default 1s)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
local script_enable = 1
|
|
164
|
-
refreshRate = 0.1
|
|
165
|
-
----[[ ##### End of SCRIPT CONFIGURATION ##### ]]----
|
|
166
|
-
|
|
167
|
-
--Helper function: Trim whitespace.
|
|
168
|
-
local function trim(s)
|
|
169
|
-
return s:match("^%s*(.-)%s*$")
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
cargoUnits = {
|
|
173
|
-
|
|
174
|
-
--[[
|
|
175
|
-
flamesize:
|
|
176
|
-
|
|
177
|
-
1 = small smoke and fire
|
|
178
|
-
2 = medium smoke and fire
|
|
179
|
-
3 = large smoke and fire
|
|
180
|
-
4 = huge smoke and fire
|
|
181
|
-
5 = small smoke
|
|
182
|
-
6 = medium smoke
|
|
183
|
-
7 = large smoke
|
|
184
|
-
8 = huge smoke
|
|
185
|
-
]]--
|
|
186
|
-
|
|
187
|
-
--1) M92 R11 Volvo driveable (Fuel Truck Tanker)
|
|
188
|
-
["r11_volvo_drivable"] = {
|
|
189
|
-
cargoExplosion = true,
|
|
190
|
-
cargoExplosionMult = 2.0,
|
|
191
|
-
cargoExplosionPower = 200,
|
|
192
|
-
cargoCookOff = false,
|
|
193
|
-
cookOffCount = 0,
|
|
194
|
-
cookOffPower = 0,
|
|
195
|
-
cookOffDuration = 0,
|
|
196
|
-
cookOffRandomTiming = false,
|
|
197
|
-
cookOffPowerRandom = 50,
|
|
198
|
-
isTanker = true,
|
|
199
|
-
flameSize = 3,
|
|
200
|
-
flameDuration = 5,
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
--2) Refueler ATMZ-5
|
|
204
|
-
["ATMZ-5"] = {
|
|
205
|
-
cargoExplosion = true,
|
|
206
|
-
cargoExplosionMult = 2.0,
|
|
207
|
-
cargoExplosionPower = 200,
|
|
208
|
-
cargoCookOff = false,
|
|
209
|
-
cookOffCount = 0,
|
|
210
|
-
cookOffPower = 0,
|
|
211
|
-
cookOffDuration = 0,
|
|
212
|
-
cookOffRandomTiming = false,
|
|
213
|
-
cookOffPowerRandom = 50,
|
|
214
|
-
isTanker = true,
|
|
215
|
-
flameSize = 3,
|
|
216
|
-
flameDuration = 5,
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
--3) Refueler ATZ-10
|
|
220
|
-
["ATZ-10"] = {
|
|
221
|
-
cargoExplosion = true,
|
|
222
|
-
cargoExplosionMult = 2,
|
|
223
|
-
cargoExplosionPower = 200,
|
|
224
|
-
cargoCookOff = false,
|
|
225
|
-
cookOffCount = 0,
|
|
226
|
-
cookOffPower = 0,
|
|
227
|
-
cookOffDuration = 0,
|
|
228
|
-
cookOffRandomTiming = false,
|
|
229
|
-
cookOffPowerRandom = 50,
|
|
230
|
-
isTanker = true,
|
|
231
|
-
flameSize = 3,
|
|
232
|
-
flameDuration = 5,
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
--4) Refueler ATZ-5
|
|
236
|
-
["ATZ-5"] = {
|
|
237
|
-
cargoExplosion = true,
|
|
238
|
-
cargoExplosionMult = 1.8,
|
|
239
|
-
cargoExplosionPower = 200,
|
|
240
|
-
cargoCookOff = false,
|
|
241
|
-
cookOffCount = 0,
|
|
242
|
-
cookOffPower = 0,
|
|
243
|
-
cookOffDuration = 0,
|
|
244
|
-
cookOffRandomTiming = false,
|
|
245
|
-
cookOffPowerRandom = 50,
|
|
246
|
-
isTanker = true,
|
|
247
|
-
flameSize = 3,
|
|
248
|
-
flameDuration = 5,
|
|
249
|
-
},
|
|
250
|
-
|
|
251
|
-
--5) Refueler M978 HEMTT (Fuel truck tanker)
|
|
252
|
-
["M978 HEMTT Tanker"] = {
|
|
253
|
-
cargoExplosion = true,
|
|
254
|
-
cargoExplosionMult = 2.0,
|
|
255
|
-
cargoExplosionPower = 200,
|
|
256
|
-
cargoCookOff = false,
|
|
257
|
-
cookOffCount = 0,
|
|
258
|
-
cookOffPower = 0,
|
|
259
|
-
cookOffDuration = 0,
|
|
260
|
-
cookOffRandomTiming = false,
|
|
261
|
-
cookOffPowerRandom = 50,
|
|
262
|
-
isTanker = true,
|
|
263
|
-
flameSize = 3,
|
|
264
|
-
flameDuration = 5,
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
--##### AMMO CARRIERS #####
|
|
268
|
-
["GAZ-66"] = {
|
|
269
|
-
cargoExplosion = true,
|
|
270
|
-
cargoExplosionMult = 1,
|
|
271
|
-
cargoExplosionPower = 200,
|
|
272
|
-
cargoCookOff = true,
|
|
273
|
-
cookOffCount = 4,
|
|
274
|
-
cookOffPower = 1,
|
|
275
|
-
cookOffDuration = 20,
|
|
276
|
-
cookOffRandomTiming = true,
|
|
277
|
-
cookOffPowerRandom = 50,
|
|
278
|
-
isTanker = false,
|
|
279
|
-
flameSize = 1,
|
|
280
|
-
flameDuration = 30,
|
|
281
|
-
},
|
|
282
|
-
--#Technically this is both ammo and fuel looking at the model
|
|
283
|
-
["Ural-4320"] = {
|
|
284
|
-
cargoExplosion = true,
|
|
285
|
-
cargoExplosionMult = 1,
|
|
286
|
-
cargoExplosionPower = 200,
|
|
287
|
-
cargoCookOff = true,
|
|
288
|
-
cookOffCount = 4,
|
|
289
|
-
cookOffPower = 1,
|
|
290
|
-
cookOffDuration = 20,
|
|
291
|
-
cookOffRandomTiming = true,
|
|
292
|
-
cookOffPowerRandom = 50,
|
|
293
|
-
isTanker = true,
|
|
294
|
-
flameSize = 1,
|
|
295
|
-
flameDuration = 30,
|
|
296
|
-
},
|
|
297
|
-
|
|
298
|
-
["ZIL-135"] = {
|
|
299
|
-
cargoExplosion = true,
|
|
300
|
-
cargoExplosionMult = 1,
|
|
301
|
-
cargoExplosionPower = 200,
|
|
302
|
-
cargoCookOff = true,
|
|
303
|
-
cookOffCount = 5,
|
|
304
|
-
cookOffPower = 1,
|
|
305
|
-
cookOffDuration = 20,
|
|
306
|
-
cookOffRandomTiming = true,
|
|
307
|
-
cookOffPowerRandom = 50,
|
|
308
|
-
isTanker = false,
|
|
309
|
-
flameSize = 1,
|
|
310
|
-
flameDuration = 30,
|
|
311
|
-
},
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
--Weapon Explosive Table
|
|
315
|
-
explTable = {
|
|
316
|
-
--*** WWII BOMBS ***
|
|
317
|
-
["British_GP_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
318
|
-
["British_GP_250LB_Bomb_Mk4"] = { explosive = 100, shaped_charge = false },
|
|
319
|
-
["British_GP_250LB_Bomb_Mk5"] = { explosive = 100, shaped_charge = false },
|
|
320
|
-
["British_GP_500LB_Bomb_Mk1"] = { explosive = 213, shaped_charge = false },
|
|
321
|
-
["British_GP_500LB_Bomb_Mk4"] = { explosive = 213, shaped_charge = false },
|
|
322
|
-
["British_GP_500LB_Bomb_Mk4_Short"] = { explosive = 213, shaped_charge = false },
|
|
323
|
-
["British_GP_500LB_Bomb_Mk5"] = { explosive = 213, shaped_charge = false },
|
|
324
|
-
["British_MC_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
325
|
-
["British_MC_250LB_Bomb_Mk2"] = { explosive = 100, shaped_charge = false },
|
|
326
|
-
["British_MC_500LB_Bomb_Mk1_Short"] = { explosive = 213, shaped_charge = false },
|
|
327
|
-
["British_MC_500LB_Bomb_Mk2"] = { explosive = 213, shaped_charge = false },
|
|
328
|
-
["British_SAP_250LB_Bomb_Mk5"] = { explosive = 100, shaped_charge = false },
|
|
329
|
-
["British_SAP_500LB_Bomb_Mk5"] = { explosive = 213, shaped_charge = false },
|
|
330
|
-
["British_AP_25LBNo1_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
331
|
-
["British_HE_60LBSAPNo2_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
332
|
-
["British_HE_60LBFNo1_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
333
|
-
|
|
334
|
-
["SC_50"] = { explosive = 20, shaped_charge = false },
|
|
335
|
-
["ER_4_SC50"] = { explosive = 20, shaped_charge = false },
|
|
336
|
-
["SC_250_T1_L2"] = { explosive = 100, shaped_charge = false },
|
|
337
|
-
["SC_501_SC250"] = { explosive = 100, shaped_charge = false },
|
|
338
|
-
["Schloss500XIIC1_SC_250_T3_J"] = { explosive = 100, shaped_charge = false },
|
|
339
|
-
["SC_501_SC500"] = { explosive = 213, shaped_charge = false },
|
|
340
|
-
["SC_500_L2"] = { explosive = 213, shaped_charge = false },
|
|
341
|
-
["SD_250_Stg"] = { explosive = 100, shaped_charge = false },
|
|
342
|
-
["SD_500_A"] = { explosive = 213, shaped_charge = false },
|
|
343
|
-
|
|
344
|
-
--*** WWII CBU ***
|
|
345
|
-
["AB_250_2_SD_2"] = { explosive = 100, shaped_charge = false },
|
|
346
|
-
["AB_250_2_SD_10A"] = { explosive = 100, shaped_charge = false },
|
|
347
|
-
["AB_500_1_SD_10A"] = { explosive = 213, shaped_charge = false },
|
|
348
|
-
|
|
349
|
-
--*** WWII ROCKETS ***
|
|
350
|
-
["3xM8_ROCKETS_IN_TUBES"] = { explosive = 4, shaped_charge = false },
|
|
351
|
-
["WGr21"] = { explosive = 4, shaped_charge = false },
|
|
352
|
-
|
|
353
|
-
--*** UNGUIDED BOMBS (UGB) ***
|
|
354
|
-
["M_117"] = { explosive = 201, shaped_charge = false },
|
|
355
|
-
["AN_M30A1"] = { explosive = 45, shaped_charge = false },
|
|
356
|
-
["AN_M57"] = { explosive = 100, shaped_charge = false },
|
|
357
|
-
["AN_M64"] = { explosive = 121, shaped_charge = false },
|
|
358
|
-
["AN_M65"] = { explosive = 400, shaped_charge = false },
|
|
359
|
-
["AN_M66"] = { explosive = 800, shaped_charge = false },
|
|
360
|
-
["AN-M66A2"] = { explosive = 536, shaped_charge = false },
|
|
361
|
-
["AN-M81"] = { explosive = 100, shaped_charge = false },
|
|
362
|
-
["AN-M88"] = { explosive = 100, shaped_charge = false },
|
|
363
|
-
|
|
364
|
-
["Mk_81"] = { explosive = 60, shaped_charge = false },
|
|
365
|
-
["MK-81SE"] = { explosive = 60, shaped_charge = false },
|
|
366
|
-
["Mk_82"] = { explosive = 100, shaped_charge = false },
|
|
367
|
-
["MK_82AIR"] = { explosive = 100, shaped_charge = false },
|
|
368
|
-
["MK_82SNAKEYE"] = { explosive = 100, shaped_charge = false },
|
|
369
|
-
["Mk_83"] = { explosive = 274, shaped_charge = false },
|
|
370
|
-
["Mk_84"] = { explosive = 582, shaped_charge = false },
|
|
371
|
-
|
|
372
|
-
["HEBOMB"] = { explosive = 40, shaped_charge = false },
|
|
373
|
-
["HEBOMBD"] = { explosive = 40, shaped_charge = false },
|
|
374
|
-
|
|
375
|
-
["SAMP125LD"] = { explosive = 60, shaped_charge = false },
|
|
376
|
-
["SAMP250LD"] = { explosive = 118, shaped_charge = false },
|
|
377
|
-
["SAMP250HD"] = { explosive = 118, shaped_charge = false },
|
|
378
|
-
["SAMP400LD"] = { explosive = 274, shaped_charge = false },
|
|
379
|
-
["SAMP400HD"] = { explosive = 274, shaped_charge = false },
|
|
380
|
-
|
|
381
|
-
["BR_250"] = { explosive = 100, shaped_charge = false },
|
|
382
|
-
["BR_500"] = { explosive = 100, shaped_charge = false },
|
|
383
|
-
|
|
384
|
-
["FAB_100"] = { explosive = 45, shaped_charge = false },
|
|
385
|
-
["FAB_250"] = { explosive = 118, shaped_charge = false },
|
|
386
|
-
["FAB_250M54TU"] = { explosive = 118, shaped_charge = false },
|
|
387
|
-
["FAB-250-M62"] = { explosive = 118, shaped_charge = false },
|
|
388
|
-
["FAB_500"] = { explosive = 213, shaped_charge = false },
|
|
389
|
-
["FAB_1500"] = { explosive = 675, shaped_charge = false },
|
|
390
|
-
|
|
391
|
-
--*** UNGUIDED BOMBS WITH PENETRATOR / ANTI-RUNWAY ***
|
|
392
|
-
["Durandal"] = { explosive = 64, shaped_charge = false },
|
|
393
|
-
["BLU107B_DURANDAL"] = { explosive = 64, shaped_charge = false },
|
|
394
|
-
["BAP_100"] = { explosive = 32, shaped_charge = false },
|
|
395
|
-
["BAP-100"] = { explosive = 32, shaped_charge = false },
|
|
396
|
-
["BAT-120"] = { explosive = 32, shaped_charge = false },
|
|
397
|
-
["TYPE-200A"] = { explosive = 107, shaped_charge = false },
|
|
398
|
-
["BetAB_500"] = { explosive = 98, shaped_charge = false },
|
|
399
|
-
["BetAB_500ShP"] = { explosive = 107, shaped_charge = false },
|
|
400
|
-
|
|
401
|
-
--*** GUIDED BOMBS (GBU) ***
|
|
402
|
-
["GBU_10"] = { explosive = 582, shaped_charge = false },
|
|
403
|
-
["GBU_12"] = { explosive = 100, shaped_charge = false },
|
|
404
|
-
["GBU_16"] = { explosive = 274, shaped_charge = false },
|
|
405
|
-
["GBU_24"] = { explosive = 582, shaped_charge = false },
|
|
406
|
-
["KAB_1500Kr"] = { explosive = 675, shaped_charge = false },
|
|
407
|
-
["KAB_500Kr"] = { explosive = 213, shaped_charge = false },
|
|
408
|
-
["KAB_500"] = { explosive = 213, shaped_charge = false },
|
|
409
|
-
|
|
410
|
-
--*** CLUSTER BOMBS (CBU) ***
|
|
411
|
-
--I don't have most of these so can't test them with debug on
|
|
412
|
-
["CBU_99"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 247, submunition_explosive = 2, submunition_name = "Mk 118" }, --Mk 20 Rockeye variant, confirmed 247 Mk 118 bomblets
|
|
413
|
-
["ROCKEYE"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 247, submunition_explosive = 2, submunition_name = "Mk 118" }, --Mk 20 Rockeye, confirmed 247 Mk 118 bomblets
|
|
414
|
-
["BLU_3B_GROUP"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 19, submunition_explosive = 0.2, submunition_name = "BLU_3B" }, --Not in datamine, possibly custom or outdated; submunition name guessed
|
|
415
|
-
["MK77mod0-WPN"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --Not in datamine, possibly custom; submunition name guessed
|
|
416
|
-
["MK77mod1-WPN"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --Not in datamine, possibly custom; submunition name guessed
|
|
417
|
-
["CBU_87"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 202, submunition_explosive = 0.5, submunition_name = "BLU_97B" }, --Confirmed 202 BLU-97/B bomblets
|
|
418
|
-
["CBU_103"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 202, submunition_explosive = 0.5, submunition_name = "BLU_97B" }, --WCMD variant of CBU-87, confirmed 202 BLU-97/B bomblets
|
|
419
|
-
["CBU_97"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 10, submunition_explosive = 15, submunition_name = "BLU_108" }, --Confirmed 10 BLU-108 submunitions, each with 4 skeets
|
|
420
|
-
["CBU_105"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 10, submunition_explosive = 15, submunition_name = "BLU_108" }, --WCMD variant of CBU-97, confirmed 10 BLU-108 submunitions
|
|
421
|
-
["BELOUGA"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 151, submunition_explosive = 0.3, submunition_name = "grenade_AC" }, --Confirmed 151 grenade_AC bomblets (French BLG-66)
|
|
422
|
-
["BLG66_BELOUGA"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 151, submunition_explosive = 0.3, submunition_name = "grenade_AC" }, --Alias for BELOUGA, confirmed 151 grenade_AC bomblets
|
|
423
|
-
["BL_755"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 147, submunition_explosive = 0.4, submunition_name = "BL_755_bomblet" }, --Confirmed 147 bomblets, submunition name from your table
|
|
424
|
-
["RBK_250"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 60, submunition_explosive = 0.5, submunition_name = "PTAB_25M" }, --Confirmed 60 PTAB-2.5M anti-tank bomblets
|
|
425
|
-
["RBK_250_275_AO_1SCH"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 150, submunition_explosive = 0.2, submunition_name = "AO_1SCh" }, --Confirmed 150 AO-1SCh fragmentation bomblets
|
|
426
|
-
["RBK_500"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 108, submunition_explosive = 0.5, submunition_name = "PTAB_10_5" }, --Confirmed 108 PTAB-10-5 anti-tank bomblets
|
|
427
|
-
["RBK_500U"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 352, submunition_explosive = 0.2, submunition_name = "OAB_25RT" }, --Confirmed 352 OAB-2.5RT fragmentation bomblets
|
|
428
|
-
["RBK_500AO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 108, submunition_explosive = 0.5, submunition_name = "AO_25RT" }, --Confirmed 108 AO-2.5RT fragmentation bomblets
|
|
429
|
-
["RBK_500U_OAB_2_5RT"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 352, submunition_explosive = 0.2, submunition_name = "OAB_25RT" }, --Confirmed 352 OAB-2.5RT fragmentation bomblets
|
|
430
|
-
["RBK_500_255_PTO_1M"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 126, submunition_explosive = 0.5, submunition_name = "PTO_1M" },
|
|
431
|
-
["RBK_500_255_ShO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 565, submunition_explosive = 0.1, submunition_name = "ShO" },
|
|
432
|
-
--*** INS/GPS BOMBS (JDAM) ***
|
|
433
|
-
["GBU_31"] = { explosive = 582, shaped_charge = false },
|
|
434
|
-
["GBU_31_V_3B"] = { explosive = 582, shaped_charge = false },
|
|
435
|
-
["GBU_31_V_2B"] = { explosive = 582, shaped_charge = false },
|
|
436
|
-
["GBU_31_V_4B"] = { explosive = 582, shaped_charge = false },
|
|
437
|
-
["GBU_32_V_2B"] = { explosive = 202, shaped_charge = false },
|
|
438
|
-
["GBU_38"] = { explosive = 100, shaped_charge = false },
|
|
439
|
-
["GBU_54_V_1B"] = { explosive = 100, shaped_charge = false },
|
|
440
|
-
|
|
441
|
-
--*** GLIDE BOMBS (JSOW) ***
|
|
442
|
-
["AGM_154A"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 145, submunition_explosive = 2, submunition_name = "BLU-97/B" }, --JSOW-A, confirmed 145 BLU-97 bomblets from datamine
|
|
443
|
-
["AGM_154C"] = { explosive = 305, shaped_charge = false },
|
|
444
|
-
["AGM_154"] = { explosive = 305, shaped_charge = false },
|
|
445
|
-
["BK90_MJ1"] = { explosive = 0, shaped_charge = false },
|
|
446
|
-
["BK90_MJ1_MJ2"] = { explosive = 0, shaped_charge = false },
|
|
447
|
-
["BK90_MJ2"] = { explosive = 0, shaped_charge = false },
|
|
448
|
-
|
|
449
|
-
["LS-6-100"] = { explosive = 45, shaped_charge = false },
|
|
450
|
-
["LS-6-250"] = { explosive = 100, shaped_charge = false },
|
|
451
|
-
["LS-6-500"] = { explosive = 274, shaped_charge = false },
|
|
452
|
-
["GB-6"] = { explosive = 0, shaped_charge = false },
|
|
453
|
-
["GB-6-HE"] = { explosive = 0, shaped_charge = false },
|
|
454
|
-
["GB-6-SFW"] = { explosive = 0, shaped_charge = false },
|
|
455
|
-
|
|
456
|
-
--*** AIR GROUND MISSILE (AGM) ***
|
|
457
|
-
["AGM_62"] = { explosive = 400, shaped_charge = false },
|
|
458
|
-
["AGM_65D"] = { explosive = 38, shaped_charge = true },
|
|
459
|
-
["AGM_65E"] = { explosive = 80, shaped_charge = true },
|
|
460
|
-
["AGM_65F"] = { explosive = 80, shaped_charge = true },
|
|
461
|
-
["AGM_65G"] = { explosive = 80, shaped_charge = true },
|
|
462
|
-
["AGM_65H"] = { explosive = 38, shaped_charge = true },
|
|
463
|
-
["AGM_65K"] = { explosive = 80, shaped_charge = true },
|
|
464
|
-
["AGM_65L"] = { explosive = 80, shaped_charge = true },
|
|
465
|
-
["AGM_123"] = { explosive = 274, shaped_charge = false },
|
|
466
|
-
["AGM_130"] = { explosive = 582, shaped_charge = false },
|
|
467
|
-
["AGM_119"] = { explosive = 176, shaped_charge = false },
|
|
468
|
-
["AGM_114"] = { explosive = 10, shaped_charge = true },
|
|
469
|
-
["AGM_114K"] = { explosive = 10, shaped_charge = true },
|
|
470
|
-
|
|
471
|
-
["Rb 05A"] = { explosive = 217, shaped_charge = false },
|
|
472
|
-
["RB75"] = { explosive = 38, shaped_charge = false },
|
|
473
|
-
["RB75A"] = { explosive = 38, shaped_charge = false },
|
|
474
|
-
["RB75B"] = { explosive = 38, shaped_charge = false },
|
|
475
|
-
["RB75T"] = { explosive = 80, shaped_charge = false },
|
|
476
|
-
["HOT3_MBDA"] = { explosive = 15, shaped_charge = false },
|
|
477
|
-
["C-701T"] = { explosive = 38, shaped_charge = false },
|
|
478
|
-
["C-701IR"] = { explosive = 38, shaped_charge = false },
|
|
479
|
-
|
|
480
|
-
["Vikhr_M"] = { explosive = 11, shaped_charge = false },
|
|
481
|
-
["Vikhr_9M127_1"] = { explosive = 11, shaped_charge = false },
|
|
482
|
-
["AT_6"] = { explosive = 11, shaped_charge = false },
|
|
483
|
-
["Ataka_9M120"] = { explosive = 11, shaped_charge = false },
|
|
484
|
-
["Ataka_9M120F"] = { explosive = 11, shaped_charge = false },
|
|
485
|
-
["P_9M117"] = { explosive = 0, shaped_charge = false },
|
|
486
|
-
|
|
487
|
-
["KH-66_Grom"] = { explosive = 108, shaped_charge = false },
|
|
488
|
-
["X_23"] = { explosive = 111, shaped_charge = false },
|
|
489
|
-
["X_23L"] = { explosive = 111, shaped_charge = false },
|
|
490
|
-
["X_28"] = { explosive = 160, shaped_charge = false },
|
|
491
|
-
["X_25ML"] = { explosive = 89, shaped_charge = false },
|
|
492
|
-
["X_25MR"] = { explosive = 140, shaped_charge = false },
|
|
493
|
-
["X_29L"] = { explosive = 320, shaped_charge = false },
|
|
494
|
-
["X_29T"] = { explosive = 320, shaped_charge = false },
|
|
495
|
-
["X_29TE"] = { explosive = 320, shaped_charge = false },
|
|
496
|
-
|
|
497
|
-
--*** ANTI-RADAR MISSILE (ARM) ***
|
|
498
|
-
["AGM_88C"] = { explosive = 89, shaped_charge = false },
|
|
499
|
-
["AGM_88"] = { explosive = 89, shaped_charge = false },
|
|
500
|
-
["AGM_122"] = { explosive = 15, shaped_charge = false },
|
|
501
|
-
["LD-10"] = { explosive = 89, shaped_charge = false },
|
|
502
|
-
["AGM_45A"] = { explosive = 38, shaped_charge = false },
|
|
503
|
-
["X_58"] = { explosive = 140, shaped_charge = false },
|
|
504
|
-
["X_25MP"] = { explosive = 89, shaped_charge = false },
|
|
505
|
-
|
|
506
|
-
--*** ANTI-SHIP MISSILE (ASh) ***
|
|
507
|
-
["AGM_84D"] = { explosive = 488, shaped_charge = false },
|
|
508
|
-
["Rb 15F"] = { explosive = 500, shaped_charge = false },
|
|
509
|
-
["C-802AK"] = { explosive = 500, shaped_charge = false },
|
|
510
|
-
|
|
511
|
-
--*** CRUISE MISSILE ***
|
|
512
|
-
["CM-802AKG"] = { explosive = 488, shaped_charge = false },
|
|
513
|
-
["AGM_84E"] = { explosive = 488, shaped_charge = false },
|
|
514
|
-
["AGM_84H"] = { explosive = 488, shaped_charge = false },
|
|
515
|
-
["X_59M"] = { explosive = 488, shaped_charge = false },
|
|
516
|
-
|
|
517
|
-
--*** ROCKETS ***
|
|
518
|
-
["HYDRA_70M15"] = { explosive = 5, shaped_charge = false },
|
|
519
|
-
["HYDRA_70_MK1"] = { explosive = 5, shaped_charge = false },
|
|
520
|
-
["HYDRA_70_MK5"] = { explosive = 8, shaped_charge = false },
|
|
521
|
-
["HYDRA_70_M151"] = { explosive = 5, shaped_charge = false },
|
|
522
|
-
["HYDRA_70_M151_M433"] = { explosive = 5, shaped_charge = false },
|
|
523
|
-
["HYDRA_70_M229"] = { explosive = 10, shaped_charge = false },
|
|
524
|
-
["FFAR Mk1 HE"] = { explosive = 5, shaped_charge = false },
|
|
525
|
-
["FFAR Mk5 HEAT"] = { explosive = 8, shaped_charge = false },
|
|
526
|
-
["HVAR"] = { explosive = 5, shaped_charge = false },
|
|
527
|
-
["Zuni_127"] = { explosive = 8, shaped_charge = false },
|
|
528
|
-
["ARAKM70BHE"] = { explosive = 5, shaped_charge = false },
|
|
529
|
-
["ARAKM70BAP"] = { explosive = 8, shaped_charge = false },
|
|
530
|
-
["SNEB_TYPE251_F1B"] = { explosive = 4, shaped_charge = false },
|
|
531
|
-
["SNEB_TYPE252_F1B"] = { explosive = 4, shaped_charge = false },
|
|
532
|
-
["SNEB_TYPE253_F1B"] = { explosive = 5, shaped_charge = false },
|
|
533
|
-
["SNEB_TYPE256_F1B"] = { explosive = 6, shaped_charge = false },
|
|
534
|
-
["SNEB_TYPE257_F1B"] = { explosive = 8, shaped_charge = false },
|
|
535
|
-
["SNEB_TYPE251_F4B"] = { explosive = 4, shaped_charge = false },
|
|
536
|
-
["SNEB_TYPE252_F4B"] = { explosive = 4, shaped_charge = false },
|
|
537
|
-
["SNEB_TYPE253_F4B"] = { explosive = 5, shaped_charge = false },
|
|
538
|
-
["SNEB_TYPE256_F4B"] = { explosive = 6, shaped_charge = false },
|
|
539
|
-
["SNEB_TYPE257_F4B"] = { explosive = 8, shaped_charge = false },
|
|
540
|
-
["SNEB_TYPE251_H1"] = { explosive = 4, shaped_charge = false },
|
|
541
|
-
["SNEB_TYPE252_H1"] = { explosive = 4, shaped_charge = false },
|
|
542
|
-
["SNEB_TYPE253_H1"] = { explosive = 5, shaped_charge = false },
|
|
543
|
-
["SNEB_TYPE256_H1"] = { explosive = 6, shaped_charge = false },
|
|
544
|
-
["SNEB_TYPE257_H1"] = { explosive = 8, shaped_charge = false },
|
|
545
|
-
["MATRA_F4_SNEBT251"] = { explosive = 8, shaped_charge = false },
|
|
546
|
-
["MATRA_F4_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
547
|
-
["MATRA_F4_SNEBT256"] = { explosive = 8, shaped_charge = false },
|
|
548
|
-
["MATRA_F1_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
549
|
-
["MATRA_F1_SNEBT256"] = { explosive = 8, shaped_charge = false },
|
|
550
|
-
["TELSON8_SNEBT251"] = { explosive = 4, shaped_charge = false },
|
|
551
|
-
["TELSON8_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
552
|
-
["TELSON8_SNEBT256"] = { explosive = 4, shaped_charge = false },
|
|
553
|
-
["TELSON8_SNEBT257"] = { explosive = 6, shaped_charge = false },
|
|
554
|
-
["ARF8M3API"] = { explosive = 8, shaped_charge = false },
|
|
555
|
-
["UG_90MM"] = { explosive = 8, shaped_charge = false },
|
|
556
|
-
["S-24A"] = { explosive = 24, shaped_charge = false },
|
|
557
|
-
["S-25OF"] = { explosive = 194, shaped_charge = false },
|
|
558
|
-
["S-25OFM"] = { explosive = 150, shaped_charge = false },
|
|
559
|
-
["S-25O"] = { explosive = 150, shaped_charge = false },
|
|
560
|
-
["S-25-O"] = { explosive = 150, shaped_charge = false },
|
|
561
|
-
["S_25L"] = { explosive = 190, shaped_charge = false },
|
|
562
|
-
["S-5M"] = { explosive = 1, shaped_charge = false },
|
|
563
|
-
["C_5"] = { explosive = 8, shaped_charge = false },
|
|
564
|
-
["C5"] = { explosive = 5, shaped_charge = false },
|
|
565
|
-
["C_8"] = { explosive = 4, shaped_charge = false },
|
|
566
|
-
["C_8OFP2"] = { explosive = 3, shaped_charge = false },
|
|
567
|
-
["C_13"] = { explosive = 21, shaped_charge = false },
|
|
568
|
-
["C_24"] = { explosive = 123, shaped_charge = false },
|
|
569
|
-
["C_25"] = { explosive = 151, shaped_charge = false },
|
|
570
|
-
|
|
571
|
-
--*** LASER ROCKETS ***
|
|
572
|
-
["AGR_20"] = { explosive = 8, shaped_charge = false },
|
|
573
|
-
["AGR_20A"] = { explosive = 8, shaped_charge = false },
|
|
574
|
-
["AGR_20_M282"] = { explosive = 8, shaped_charge = false },
|
|
575
|
-
["Hydra_70_M282_MPP"] = { explosive = 5, shaped_charge = true },
|
|
576
|
-
["BRM-1_90MM"] = { explosive = 8, shaped_charge = false },
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
local effectSmokeId = 1
|
|
585
|
-
|
|
586
|
-
----[[ ##### HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
587
|
-
|
|
588
|
-
local function tableHasKey(table, key)
|
|
589
|
-
return table[key] ~= nil
|
|
590
|
-
end
|
|
591
|
-
|
|
592
|
-
local function debugMsg(str)
|
|
593
|
-
if splash_damage_options.debug == true then
|
|
594
|
-
debugCounter = (debugCounter or 0) + 1
|
|
595
|
-
local uniqueStr = str .. " [" .. timer.getTime() .. " - " .. debugCounter .. "]"
|
|
596
|
-
trigger.action.outText(uniqueStr, 5)
|
|
597
|
-
env.info("DEBUG: " .. uniqueStr)
|
|
598
|
-
end
|
|
599
|
-
end
|
|
600
|
-
|
|
601
|
-
local function gameMsg(str)
|
|
602
|
-
if splash_damage_options.game_messages == true then
|
|
603
|
-
trigger.action.outText(str, 5)
|
|
604
|
-
end
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
local function getDistance(point1, point2)
|
|
608
|
-
local x1 = point1.x
|
|
609
|
-
local y1 = point1.y
|
|
610
|
-
local z1 = point1.z
|
|
611
|
-
local x2 = point2.x
|
|
612
|
-
local y2 = point2.y
|
|
613
|
-
local z2 = point2.z
|
|
614
|
-
local dX = math.abs(x1 - x2)
|
|
615
|
-
local dZ = math.abs(z1 - z2)
|
|
616
|
-
local distance = math.sqrt(dX * dX + dZ * dZ)
|
|
617
|
-
return distance
|
|
618
|
-
end
|
|
619
|
-
|
|
620
|
-
local function getDistance3D(point1, point2)
|
|
621
|
-
local x1 = point1.x
|
|
622
|
-
local y1 = point1.y
|
|
623
|
-
local z1 = point1.z
|
|
624
|
-
local x2 = point2.x
|
|
625
|
-
local y2 = point2.y
|
|
626
|
-
local z2 = point2.z
|
|
627
|
-
local dX = math.abs(x1 - x2)
|
|
628
|
-
local dY = math.abs(y1 - y2)
|
|
629
|
-
local dZ = math.abs(z1 - z2)
|
|
630
|
-
local distance = math.sqrt(dX * dX + dZ * dZ + dY * dY)
|
|
631
|
-
return distance
|
|
632
|
-
end
|
|
633
|
-
|
|
634
|
-
local function vec3Mag(speedVec)
|
|
635
|
-
return math.sqrt(speedVec.x^2 + speedVec.y^2 + speedVec.z^2)
|
|
636
|
-
end
|
|
637
|
-
|
|
638
|
-
local function lookahead(speedVec)
|
|
639
|
-
local speed = vec3Mag(speedVec)
|
|
640
|
-
local dist = speed * refreshRate * 1.5
|
|
641
|
-
return dist
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
--Cluster-specific helper functions from Rockeye script
|
|
645
|
-
local function normalizeVector(vec)
|
|
646
|
-
local mag = math.sqrt(vec.x^2 + vec.z^2)
|
|
647
|
-
if mag > 0 then
|
|
648
|
-
return { x = vec.x / mag, z = vec.z / mag }
|
|
649
|
-
else
|
|
650
|
-
return { x = 1, z = 0 }
|
|
651
|
-
end
|
|
652
|
-
end
|
|
653
|
-
local function calculate_drop_angle(velocity)
|
|
654
|
-
local horizontal_speed = math.sqrt((velocity.x or 0)^2 + (velocity.z or 0)^2)
|
|
655
|
-
local vertical_speed = math.abs(velocity.y or 0)
|
|
656
|
-
if horizontal_speed == 0 then return 90 end
|
|
657
|
-
local angle_rad = math.atan(vertical_speed / horizontal_speed)
|
|
658
|
-
return math.deg(angle_rad)
|
|
659
|
-
end
|
|
660
|
-
local function calculate_dispersion(velocity, burst_altitude)
|
|
661
|
-
local velocity_magnitude = math.sqrt((velocity.x or 0)^2 + (velocity.z or 0)^2)
|
|
662
|
-
local drop_angle = calculate_drop_angle(velocity)
|
|
663
|
-
local length = splash_damage_options.cluster_base_length * (1 + velocity_magnitude / 200)
|
|
664
|
-
local width = splash_damage_options.cluster_base_width * (1 + burst_altitude / 6000)
|
|
665
|
-
local length_jitter = length * (0.85 + math.random() * 0.3)
|
|
666
|
-
local width_jitter = width * (0.85 + math.random() * 0.3)
|
|
667
|
-
return math.max(splash_damage_options.cluster_min_length, math.min(splash_damage_options.cluster_max_length, length_jitter)),
|
|
668
|
-
math.max(splash_damage_options.cluster_min_width, math.min(splash_damage_options.cluster_max_width, width_jitter))
|
|
669
|
-
end
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
----[[ ##### End of HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
680
|
-
giantExplosionTargets = {}
|
|
681
|
-
cargoEffectsQueue = {}
|
|
682
|
-
WpnHandler = {}
|
|
683
|
-
tracked_target_position = nil --Store the last known position of TargetUnit for giant explosion
|
|
684
|
-
tracked_weapons = {}
|
|
685
|
-
local processedUnitsGlobal = {}
|
|
686
|
-
|
|
687
|
-
function scanGiantExplosionTargets()
|
|
688
|
-
giantExplosionTargets = {}
|
|
689
|
-
local function findTargets(obj)
|
|
690
|
-
if obj:isExist() then
|
|
691
|
-
local name = obj:getName()
|
|
692
|
-
if string.find(name, "GiantExplosionTarget") then
|
|
693
|
-
local flagName = string.gsub(name, "Target", "")
|
|
694
|
-
table.insert(giantExplosionTargets, {
|
|
695
|
-
name = name,
|
|
696
|
-
flag = flagName,
|
|
697
|
-
obj = obj,
|
|
698
|
-
pos = obj:getPoint(),
|
|
699
|
-
static = splash_damage_options.giant_explosion_target_static
|
|
700
|
-
})
|
|
701
|
-
end
|
|
702
|
-
end
|
|
703
|
-
return true
|
|
704
|
-
end
|
|
705
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, {id = world.VolumeType.ALL}, findTargets)
|
|
706
|
-
if not splash_damage_options.giant_explosion_target_static then
|
|
707
|
-
timer.scheduleFunction(updateGiantExplosionPositions, {}, timer.getTime() + 1.0)
|
|
708
|
-
end
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
function updateTargetPosition()
|
|
712
|
-
for name, target in pairs(giantExplosionTargets) do
|
|
713
|
-
if target.obj:isExist() then
|
|
714
|
-
target.pos = target.obj:getPosition().p
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
return timer.getTime() + 1.0
|
|
718
|
-
end
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
--Giant Explosion Function
|
|
722
|
-
function triggerGiantExplosion(params)
|
|
723
|
-
if not splash_damage_options.giant_explosion_enabled then
|
|
724
|
-
debugMsg("Giant Explosion is disabled in options.")
|
|
725
|
-
return
|
|
726
|
-
end
|
|
727
|
-
|
|
728
|
-
local initialPos = params.pos or {x = 0, y = 0, z = 0}
|
|
729
|
-
local explosionPower = params.power or splash_damage_options.giant_explosion_power
|
|
730
|
-
local sizeScale = params.scale or splash_damage_options.giant_explosion_scale
|
|
731
|
-
local totalDuration = params.duration or splash_damage_options.giant_explosion_duration
|
|
732
|
-
local explosionCount = params.count or splash_damage_options.giant_explosion_count
|
|
733
|
-
|
|
734
|
-
if not initialPos.x or not initialPos.y or not initialPos.z then
|
|
735
|
-
gameMsg("Error: Invalid position for giant explosion!")
|
|
736
|
-
debugMsg("No valid initial position set for giant explosion!")
|
|
737
|
-
return
|
|
738
|
-
end
|
|
739
|
-
|
|
740
|
-
debugMsg("Triggering giant fireball at X: " .. initialPos.x .. ", Y: " .. initialPos.y .. ", Z: " .. initialPos.z)
|
|
741
|
-
|
|
742
|
-
local function scheduleExplosion(pos, delay)
|
|
743
|
-
if not pos or not pos.x or not pos.y or not pos.z then
|
|
744
|
-
debugMsg("Error: Invalid position for explosion - pos: " .. tostring(pos))
|
|
745
|
-
return
|
|
746
|
-
end
|
|
747
|
-
timer.scheduleFunction(function(p)
|
|
748
|
-
if p and p.x and p.y and p.z then
|
|
749
|
-
trigger.action.explosion(p, explosionPower)
|
|
750
|
-
end
|
|
751
|
-
end, pos, timer.getTime() + delay)
|
|
752
|
-
end
|
|
753
|
-
|
|
754
|
-
-- Pre-explosion scan for cargo units
|
|
755
|
-
local scanRadius = 1500 * sizeScale -- 1500m base radius, scaled by sizeScale
|
|
756
|
-
local preExplosionTargets = {}
|
|
757
|
-
if splash_damage_options.enable_cargo_effects then
|
|
758
|
-
local volS = {
|
|
759
|
-
id = world.VolumeType.SPHERE,
|
|
760
|
-
params = { point = initialPos, radius = scanRadius }
|
|
761
|
-
}
|
|
762
|
-
local ifFound = function(foundObject)
|
|
763
|
-
if foundObject:isExist() then
|
|
764
|
-
local category = foundObject:getCategory()
|
|
765
|
-
if (category == Object.Category.UNIT and foundObject:getDesc().category == Unit.Category.GROUND_UNIT) or
|
|
766
|
-
category == Object.Category.STATIC then
|
|
767
|
-
table.insert(preExplosionTargets, {
|
|
768
|
-
name = foundObject:getTypeName(),
|
|
769
|
-
health = foundObject:getLife() or 0,
|
|
770
|
-
position = foundObject:getPoint(),
|
|
771
|
-
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
772
|
-
unit = foundObject
|
|
773
|
-
})
|
|
774
|
-
end
|
|
775
|
-
end
|
|
776
|
-
return true
|
|
777
|
-
end
|
|
778
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
779
|
-
debugMsg("Pre-explosion scan for Giant Explosion: " .. #preExplosionTargets .. " targets found within " .. scanRadius .. "m")
|
|
780
|
-
end
|
|
781
|
-
-- Trigger the explosion
|
|
782
|
-
local maxRadius = 200 * sizeScale
|
|
783
|
-
local maxHeight = 500 * sizeScale
|
|
784
|
-
local adjustedExplosionCount = math.floor(explosionCount * (sizeScale ^ 2.5))
|
|
785
|
-
local stepTime = totalDuration / adjustedExplosionCount
|
|
786
|
-
local variance = 0.25 --Fixed at 25%
|
|
787
|
-
|
|
788
|
-
for i = 1, adjustedExplosionCount do
|
|
789
|
-
local progress = i / adjustedExplosionCount
|
|
790
|
-
local currentRadius = maxRadius * progress
|
|
791
|
-
local r = currentRadius * (0.9 + math.random() * 0.1)
|
|
792
|
-
local theta = math.random() * 2 * math.pi
|
|
793
|
-
local phi = math.acos(math.random())
|
|
794
|
-
|
|
795
|
-
local offsetX = r * math.sin(phi) * math.cos(theta)
|
|
796
|
-
local offsetZ = r * math.sin(phi) * math.sin(theta)
|
|
797
|
-
local offsetY = r * math.cos(phi)
|
|
798
|
-
|
|
799
|
-
offsetX = offsetX * (1 + (math.random() - 0.5) * variance)
|
|
800
|
-
offsetZ = offsetZ * (1 + (math.random() - 0.5) * variance)
|
|
801
|
-
offsetY = offsetY * (1 + (math.random() - 0.5) * variance * 0.5)
|
|
802
|
-
|
|
803
|
-
local blastPos = {
|
|
804
|
-
x = initialPos.x + offsetX,
|
|
805
|
-
y = land.getHeight({x = initialPos.x, y = initialPos.z}) + offsetY,
|
|
806
|
-
z = initialPos.z + offsetZ
|
|
807
|
-
}
|
|
808
|
-
if blastPos.y < land.getHeight({x = blastPos.x, y = blastPos.z}) then
|
|
809
|
-
blastPos.y = land.getHeight({x = blastPos.x, y = blastPos.z})
|
|
810
|
-
end
|
|
811
|
-
|
|
812
|
-
local delay = (i - 1) * stepTime + (math.random() - 0.5) * stepTime * variance
|
|
813
|
-
scheduleExplosion(blastPos, delay)
|
|
814
|
-
end
|
|
815
|
-
|
|
816
|
-
gameMsg("Expanding giant fireball over " .. totalDuration .. "s (scale " .. sizeScale .. ")!")
|
|
817
|
-
|
|
818
|
-
-- Post-explosion scan and cargo cook-off queuing
|
|
819
|
-
if splash_damage_options.enable_cargo_effects then
|
|
820
|
-
timer.scheduleFunction(function(args)
|
|
821
|
-
local centerPos = args[1]
|
|
822
|
-
local radius = args[2]
|
|
823
|
-
local preTargets = args[3]
|
|
824
|
-
|
|
825
|
-
local postExplosionTargets = {}
|
|
826
|
-
local volS = {
|
|
827
|
-
id = world.VolumeType.SPHERE,
|
|
828
|
-
params = { point = centerPos, radius = radius }
|
|
829
|
-
}
|
|
830
|
-
local ifFound = function(foundObject)
|
|
831
|
-
if foundObject:isExist() then
|
|
832
|
-
local category = foundObject:getCategory()
|
|
833
|
-
if (category == Object.Category.UNIT and foundObject:getDesc().category == Unit.Category.GROUND_UNIT) or
|
|
834
|
-
category == Object.Category.STATIC then
|
|
835
|
-
table.insert(postExplosionTargets, {
|
|
836
|
-
name = foundObject:getTypeName(),
|
|
837
|
-
health = foundObject:getLife() or 0,
|
|
838
|
-
position = foundObject:getPoint(),
|
|
839
|
-
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0
|
|
840
|
-
})
|
|
841
|
-
end
|
|
842
|
-
end
|
|
843
|
-
return true
|
|
844
|
-
end
|
|
845
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
846
|
-
debugMsg("Post-explosion scan for Giant Explosion: " .. #postExplosionTargets .. " targets found within " .. radius .. "m")
|
|
847
|
-
|
|
848
|
-
-- Compare pre- and post-explosion targets
|
|
849
|
-
for _, preTarget in ipairs(preTargets) do
|
|
850
|
-
local found = false
|
|
851
|
-
local postHealth = 0
|
|
852
|
-
for _, postTarget in ipairs(postExplosionTargets) do
|
|
853
|
-
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
854
|
-
found = true
|
|
855
|
-
postHealth = postTarget.health
|
|
856
|
-
break
|
|
857
|
-
end
|
|
858
|
-
end
|
|
859
|
-
|
|
860
|
-
local cargoData = cargoUnits[preTarget.name]
|
|
861
|
-
if cargoData and (not found or postHealth <= 0) then
|
|
862
|
-
local distance = getDistance(initialPos, preTarget.position)
|
|
863
|
-
if distance <= radius then
|
|
864
|
-
local cargoPower = cargoData.cargoExplosionPower or explosionPower
|
|
865
|
-
table.insert(cargoEffectsQueue, {
|
|
866
|
-
name = preTarget.name,
|
|
867
|
-
distance = distance,
|
|
868
|
-
coords = preTarget.position,
|
|
869
|
-
power = cargoPower,
|
|
870
|
-
explosion = cargoData.cargoExplosion,
|
|
871
|
-
cookOff = cargoData.cargoCookOff,
|
|
872
|
-
cookOffCount = cargoData.cookOffCount,
|
|
873
|
-
cookOffPower = cargoData.cookOffPower,
|
|
874
|
-
cookOffDuration = cargoData.cookOffDuration,
|
|
875
|
-
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
876
|
-
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
877
|
-
isTanker = cargoData.isTanker,
|
|
878
|
-
flameSize = cargoData.flameSize,
|
|
879
|
-
flameDuration = cargoData.flameDuration
|
|
880
|
-
})
|
|
881
|
-
debugMsg("Queued cargo effect for " .. preTarget.name .. " destroyed by Giant Explosion at " .. string.format("%.1f", distance) .. "m")
|
|
882
|
-
end
|
|
883
|
-
end
|
|
884
|
-
end
|
|
885
|
-
|
|
886
|
-
-- Process queued cargo effects with prioritized flames
|
|
887
|
-
if #cargoEffectsQueue > 0 then
|
|
888
|
-
local flameIndex = 0 -- Separate index for flames
|
|
889
|
-
local otherIndex = 0 -- Index for explosions, cook-offs, debris
|
|
890
|
-
local processedCargoUnits = {}
|
|
891
|
-
local flamePositions = {}
|
|
892
|
-
for _, effect in ipairs(cargoEffectsQueue) do
|
|
893
|
-
local unitKey = effect.name .. "_" .. effect.coords.x .. "_" .. effect.coords.z
|
|
894
|
-
if not processedUnitsGlobal[unitKey] and not processedCargoUnits[unitKey] then
|
|
895
|
-
-- Handle tanker flames first with minimal delay
|
|
896
|
-
if effect.isTanker and effect.explosion then
|
|
897
|
-
debugMsg("Triggering cargo explosion for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. flameIndex .. "s")
|
|
898
|
-
timer.scheduleFunction(function(params)
|
|
899
|
-
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
900
|
-
trigger.action.explosion(params[1], params[2])
|
|
901
|
-
end, {effect.coords, effect.power}, timer.getTime() + flameIndex + 0.1)
|
|
902
|
-
|
|
903
|
-
local flameSize = effect.flameSize or 3
|
|
904
|
-
local flameDuration = effect.flameDuration
|
|
905
|
-
local flameDensity = 1.0
|
|
906
|
-
local effectId = effectSmokeId
|
|
907
|
-
effectSmokeId = effectSmokeId + 1
|
|
908
|
-
local isDuplicate = false
|
|
909
|
-
for _, pos in pairs(flamePositions) do
|
|
910
|
-
if getDistance3D(effect.coords, pos) < 3 then
|
|
911
|
-
isDuplicate = true
|
|
912
|
-
debugMsg("Skipping duplicate flame for " .. effect.name .. " near X: " .. string.format("%.0f", pos.x) .. ", Y: " .. string.format("%.0f", pos.y) .. ", Z: " .. string.format("%.0f", pos.z))
|
|
913
|
-
break
|
|
914
|
-
end
|
|
915
|
-
end
|
|
916
|
-
if not isDuplicate then
|
|
917
|
-
debugMsg("Adding flame effect for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m (Size: " .. flameSize .. ", Duration: " .. flameDuration .. "s, ID: " .. effectId .. ") scheduled at " .. flameIndex .. "s")
|
|
918
|
-
timer.scheduleFunction(function(params)
|
|
919
|
-
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
920
|
-
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
921
|
-
debugMsg("Spawning flame effect at X: " .. string.format("%.0f", adjustedCoords.x) .. ", Y: " .. string.format("%.0f", adjustedCoords.y) .. ", Z: " .. string.format("%.0f", adjustedCoords.z))
|
|
922
|
-
trigger.action.explosion(adjustedCoords, 10) -- Small trigger explosion
|
|
923
|
-
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
924
|
-
end, {effect.coords, flameSize, flameDensity, effectId}, timer.getTime() + flameIndex + 0.2)
|
|
925
|
-
timer.scheduleFunction(function(id)
|
|
926
|
-
debugMsg("Stopping flame effect for " .. effect.name .. " (ID: " .. id .. ")")
|
|
927
|
-
trigger.action.effectSmokeStop(id)
|
|
928
|
-
end, effectId, timer.getTime() + flameIndex + flameDuration + 0.2)
|
|
929
|
-
table.insert(flamePositions, effect.coords)
|
|
930
|
-
end
|
|
931
|
-
flameIndex = flameIndex + 0.5 -- Fast spacing for flames (0.5s)
|
|
932
|
-
end
|
|
933
|
-
-- Handle non-tanker explosions, cook-offs, and debris
|
|
934
|
-
if not effect.isTanker or (effect.explosion and not effect.isTanker) then
|
|
935
|
-
if effect.explosion then
|
|
936
|
-
debugMsg("Triggering cargo explosion for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. otherIndex .. "s")
|
|
937
|
-
timer.scheduleFunction(function(params)
|
|
938
|
-
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
939
|
-
trigger.action.explosion(params[1], params[2])
|
|
940
|
-
end, {effect.coords, effect.power}, timer.getTime() + otherIndex + 0.1)
|
|
941
|
-
end
|
|
942
|
-
if effect.cookOff and effect.cookOffCount > 0 then
|
|
943
|
-
debugMsg("Scheduling " .. effect.cookOffCount .. " cook-off explosions for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m over " .. effect.cookOffDuration .. "s starting at " .. otherIndex .. "s")
|
|
944
|
-
for i = 1, effect.cookOffCount do
|
|
945
|
-
local delay = effect.cookOffRandomTiming and math.random() * effect.cookOffDuration or (i - 1) * (effect.cookOffDuration / effect.cookOffCount)
|
|
946
|
-
local basePower = effect.cookOffPower
|
|
947
|
-
local powerVariation = effect.cookOffPowerRandom / 100
|
|
948
|
-
local cookOffPower = effect.cookOffPowerRandom == 0 and basePower or basePower * (1 + powerVariation * (math.random() * 2 - 1))
|
|
949
|
-
debugMsg("Cook-off #" .. i .. " for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m scheduled at " .. string.format("%.3f", delay) .. "s with power " .. string.format("%.2f", cookOffPower))
|
|
950
|
-
timer.scheduleFunction(function(params)
|
|
951
|
-
debugMsg("Executing cook-off at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
952
|
-
trigger.action.explosion(params[1], params[2])
|
|
953
|
-
end, {effect.coords, cookOffPower}, timer.getTime() + otherIndex + delay)
|
|
954
|
-
end
|
|
955
|
-
if splash_damage_options.debris_effects then
|
|
956
|
-
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
957
|
-
for j = 1, debrisCount do
|
|
958
|
-
local theta = math.random() * 2 * math.pi
|
|
959
|
-
local phi = math.acos(math.random() * 2 - 1)
|
|
960
|
-
local minDist = splash_damage_options.debris_max_distance * 0.1
|
|
961
|
-
local maxDist = splash_damage_options.debris_max_distance
|
|
962
|
-
local r = math.random() * (maxDist - minDist) + minDist
|
|
963
|
-
local debrisX = effect.coords.x + r * math.sin(phi) * math.cos(theta)
|
|
964
|
-
local debrisZ = effect.coords.z + r * math.sin(phi) * math.sin(theta)
|
|
965
|
-
local terrainY = land.getHeight({x = debrisX, y = debrisZ})
|
|
966
|
-
local debrisY = terrainY + math.random() * maxDist
|
|
967
|
-
local debrisPos = {x = debrisX, y = debrisY, z = debrisZ}
|
|
968
|
-
local debrisPower = splash_damage_options.debris_power
|
|
969
|
-
local debrisDelay = (j - 1) * (effect.cookOffDuration / debrisCount)
|
|
970
|
-
timer.scheduleFunction(function(debrisArgs)
|
|
971
|
-
debugMsg("Debris explosion at X: " .. string.format("%.0f", debrisArgs[1].x) .. ", Y: " .. string.format("%.0f", debrisArgs[1].y) .. ", Z: " .. string.format("%.0f", debrisArgs[1].z) .. " with power " .. debrisArgs[2])
|
|
972
|
-
trigger.action.explosion(debrisArgs[1], debrisArgs[2])
|
|
973
|
-
end, {debrisPos, debrisPower}, timer.getTime() + otherIndex + debrisDelay)
|
|
974
|
-
end
|
|
975
|
-
end
|
|
976
|
-
end
|
|
977
|
-
otherIndex = otherIndex + 1 -- Slower spacing for non-flame effects (1s)
|
|
978
|
-
end
|
|
979
|
-
processedCargoUnits[unitKey] = true
|
|
980
|
-
processedUnitsGlobal[unitKey] = true
|
|
981
|
-
end
|
|
982
|
-
end
|
|
983
|
-
cargoEffectsQueue = {} -- Clear the queue after processing
|
|
984
|
-
end
|
|
985
|
-
end, {initialPos, scanRadius, preExplosionTargets}, timer.getTime() + totalDuration + 1.0)
|
|
986
|
-
end
|
|
987
|
-
end
|
|
988
|
-
|
|
989
|
-
--Flag Checker for mission editor
|
|
990
|
-
function checkGiantExplosionFlag()
|
|
991
|
-
for name, target in pairs(giantExplosionTargets) do
|
|
992
|
-
local flagName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
993
|
-
local flagValue = trigger.misc.getUserFlag(flagName)
|
|
994
|
-
--commenting out as it spams every second
|
|
995
|
-
debugMsg("Checking flag " .. flagName .. ": " .. flagValue)
|
|
996
|
-
if flagValue == 1 then
|
|
997
|
-
debugMsg("Triggering explosion for " .. name .. " at X:" .. target.pos.x .. " Y:" .. target.pos.y .. " Z:" .. target.pos.z)
|
|
998
|
-
triggerGiantExplosion({
|
|
999
|
-
pos = target.pos,
|
|
1000
|
-
power = splash_damage_options.giant_explosion_power,
|
|
1001
|
-
scale = splash_damage_options.giant_explosion_scale,
|
|
1002
|
-
duration = splash_damage_options.giant_explosion_duration,
|
|
1003
|
-
count = splash_damage_options.giant_explosion_count
|
|
1004
|
-
})
|
|
1005
|
-
trigger.action.setUserFlag(flagName, 2)
|
|
1006
|
-
end
|
|
1007
|
-
end
|
|
1008
|
-
return timer.getTime() + splash_damage_options.giant_explosion_poll_rate
|
|
1009
|
-
end
|
|
1010
|
-
|
|
1011
|
-
function getWeaponExplosive(name)
|
|
1012
|
-
local weaponData = explTable[name]
|
|
1013
|
-
if weaponData then
|
|
1014
|
-
return weaponData.explosive, weaponData.shaped_charge
|
|
1015
|
-
else
|
|
1016
|
-
return 0, false
|
|
1017
|
-
end
|
|
1018
|
-
end
|
|
1019
|
-
|
|
1020
|
-
function track_wpns_cluster_scan(args)
|
|
1021
|
-
local parentPos = args[1]
|
|
1022
|
-
local parentDir = args[2]
|
|
1023
|
-
local parentName = args[3]
|
|
1024
|
-
local subName = args[4]
|
|
1025
|
-
local subCount = args[5]
|
|
1026
|
-
local subPower = args[6]
|
|
1027
|
-
local parentVel = args[7]
|
|
1028
|
-
local attempt = args[8] or 1
|
|
1029
|
-
local maxAttempts = 3
|
|
1030
|
-
local scanVol = {
|
|
1031
|
-
id = world.VolumeType.SPHERE,
|
|
1032
|
-
params = { point = parentPos, radius = 400 }
|
|
1033
|
-
}
|
|
1034
|
-
local bombletsFound = {}
|
|
1035
|
-
local allWeaponsFound = {}
|
|
1036
|
-
--General scan for all weapons
|
|
1037
|
-
world.searchObjects(Object.Category.WEAPON, scanVol, function(wpn)
|
|
1038
|
-
if wpn:isExist() then
|
|
1039
|
-
local wpnId = wpn.id_
|
|
1040
|
-
local wpnType = wpn:getTypeName()
|
|
1041
|
-
local wpnPos = wpn:getPosition().p
|
|
1042
|
-
table.insert(allWeaponsFound, { id = wpnId, type = wpnType, x = wpnPos.x, y = wpnPos.y, z = wpnPos.z })
|
|
1043
|
-
if wpnType == subName and not tracked_weapons[wpnId] then
|
|
1044
|
-
tracked_weapons[wpnId] = {
|
|
1045
|
-
wpn = wpn,
|
|
1046
|
-
pos = wpnPos,
|
|
1047
|
-
speed = wpn:getVelocity(),
|
|
1048
|
-
name = wpnType,
|
|
1049
|
-
parent = parentName,
|
|
1050
|
-
parentVelocity = parentVel
|
|
1051
|
-
}
|
|
1052
|
-
table.insert(bombletsFound, wpnId)
|
|
1053
|
-
debugMsg("Detected expected submunition '" .. wpnType .. "' from '" .. parentName .. "' at X: " .. string.format("%.0f", wpnPos.x) .. ", Y: " .. string.format("%.0f", wpnPos.y) .. ", Z: " .. string.format("%.0f", wpnPos.z) .. " (Attempt " .. attempt .. ")")
|
|
1054
|
-
end
|
|
1055
|
-
end
|
|
1056
|
-
return true
|
|
1057
|
-
end)
|
|
1058
|
-
--Log results
|
|
1059
|
-
debugMsg("Scanned for submunition '" .. subName .. "' bomblets from '" .. parentName .. "': " .. #bombletsFound .. " found (Attempt " .. attempt .. ")")
|
|
1060
|
-
if #allWeaponsFound > 0 then
|
|
1061
|
-
local msg = "General scan for '" .. parentName .. "': " .. #allWeaponsFound .. " bomblets released, expected " .. subCount .. " '" .. subName .. "'"
|
|
1062
|
-
local typeMismatch = false
|
|
1063
|
-
for _, wpn in ipairs(allWeaponsFound) do
|
|
1064
|
-
if wpn.type ~= subName then
|
|
1065
|
-
typeMismatch = true
|
|
1066
|
-
break
|
|
1067
|
-
end
|
|
1068
|
-
end
|
|
1069
|
-
if typeMismatch then
|
|
1070
|
-
msg = msg .. " - Mismatch detected! Actual bomblets: "
|
|
1071
|
-
for _, wpn in ipairs(allWeaponsFound) do
|
|
1072
|
-
msg = msg .. "'" .. wpn.type .. "' (X: " .. string.format("%.0f", wpn.x) .. ", Y: " .. string.format("%.0f", wpn.y) .. ", Z: " .. string.format("%.0f", wpn.z) .. ") "
|
|
1073
|
-
end
|
|
1074
|
-
msg = msg .. "Script may need changing."
|
|
1075
|
-
end
|
|
1076
|
-
debugMsg(msg)
|
|
1077
|
-
elseif #bombletsFound == 0 and #allWeaponsFound == 0 then
|
|
1078
|
-
debugMsg("No bomblets of any type detected for '" .. parentName .. "' (Attempt " .. attempt .. ")")
|
|
1079
|
-
end
|
|
1080
|
-
--Retry if no expected submunitions found
|
|
1081
|
-
if #bombletsFound == 0 and attempt < maxAttempts then
|
|
1082
|
-
debugMsg("No expected submunition '" .. subName .. "' found on attempt " .. attempt .. ", retrying in 0.5s")
|
|
1083
|
-
timer.scheduleFunction(track_wpns_cluster_scan, {parentPos, parentDir, parentName, subName, subCount, subPower, parentVel, attempt + 1}, timer.getTime() + 0.5)
|
|
1084
|
-
elseif #bombletsFound == 0 and attempt == maxAttempts then
|
|
1085
|
-
debugMsg("No submunition '" .. subName .. "' spawned by DCS for '" .. parentName .. "' after " .. maxAttempts .. " attempts - skipping additional explosions")
|
|
1086
|
-
end
|
|
1087
|
-
end
|
|
1088
|
-
|
|
1089
|
-
----[[ ##### Updated track_wpns() Function ##### ]]----
|
|
1090
|
-
local recentExplosions = {}
|
|
1091
|
-
function track_wpns()
|
|
1092
|
-
local weaponsToRemove = {} --Delay removal to ensure all weapons are checked
|
|
1093
|
-
for wpn_id_, wpnData in pairs(tracked_weapons) do
|
|
1094
|
-
local status, err = pcall(function()
|
|
1095
|
-
if wpnData.wpn:isExist() then
|
|
1096
|
-
--Update position, direction, speed
|
|
1097
|
-
wpnData.pos = wpnData.wpn:getPosition().p
|
|
1098
|
-
wpnData.dir = wpnData.wpn:getPosition().x
|
|
1099
|
-
wpnData.speed = wpnData.wpn:getVelocity()
|
|
1100
|
-
--[[
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
--Tick-by-tick tracking from weapon's actual position
|
|
1104
|
-
local tickVol = {
|
|
1105
|
-
id = world.VolumeType.SPHERE,
|
|
1106
|
-
params = {
|
|
1107
|
-
point = wpnData.pos, --Real weapon position
|
|
1108
|
-
radius = 150 --150m radius
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
local tickTargets = {}
|
|
1112
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, function(obj)
|
|
1113
|
-
if obj:isExist() then
|
|
1114
|
-
table.insert(tickTargets, {
|
|
1115
|
-
name = obj:getTypeName(),
|
|
1116
|
-
distance = getDistance3D(wpnData.pos, obj:getPoint()), --3D distance
|
|
1117
|
-
position = obj:getPoint(),
|
|
1118
|
-
health = obj:getLife() or 0
|
|
1119
|
-
})
|
|
1120
|
-
end
|
|
1121
|
-
return true
|
|
1122
|
-
end)
|
|
1123
|
-
debugMsg("Tick Track for " .. wpnData.name .. " at X: " .. string.format("%.0f", wpnData.pos.x) .. ", Y: " .. string.format("%.0f", wpnData.pos.y) .. ", Z: " .. string.format("%.0f", wpnData.pos.z) .. " - " .. #tickTargets .. " targets")
|
|
1124
|
-
for i, target in ipairs(tickTargets) do
|
|
1125
|
-
debugMsg("Tick Target #" .. i .. ": " .. target.name .. " at X: " .. string.format("%.0f", target.position.x) .. ", Y: " .. string.format("%.0f", target.position.y) .. ", Z: " .. string.format("%.0f", target.position.z) .. ", Dist: " .. string.format("%.1f", target.distance) .. "m, Health: " .. target.health)
|
|
1126
|
-
end
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
]]--
|
|
1130
|
-
|
|
1131
|
-
--Scan potential blast zone in the last frame before impact
|
|
1132
|
-
if splash_damage_options.track_pre_explosion then
|
|
1133
|
-
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed))
|
|
1134
|
-
local predictedImpact = ip or wpnData.pos
|
|
1135
|
-
|
|
1136
|
-
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
1137
|
-
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
1138
|
-
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
1139
|
-
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
1140
|
-
end
|
|
1141
|
-
|
|
1142
|
-
local explosionPower = base_explosive
|
|
1143
|
-
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1144
|
-
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
1145
|
-
end
|
|
1146
|
-
|
|
1147
|
-
local blastRadius = splash_damage_options.blast_search_radius * 2 --Wider post-scan (180m default)
|
|
1148
|
-
if splash_damage_options.use_dynamic_blast_radius then
|
|
1149
|
-
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1150
|
-
end
|
|
1151
|
-
|
|
1152
|
-
--Tight scan while weapon exists
|
|
1153
|
-
--local tightRadius = 50
|
|
1154
|
-
--if splash_damage_options.use_dynamic_blast_radius then
|
|
1155
|
-
--tightRadius = math.pow(explosionPower, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1156
|
-
--end
|
|
1157
|
-
local tightRadius = blastRadius --Use already calculated blastRadius
|
|
1158
|
-
local volS = {
|
|
1159
|
-
id = world.VolumeType.SPHERE,
|
|
1160
|
-
params = {
|
|
1161
|
-
point = wpnData.pos, --Use current pos
|
|
1162
|
-
radius = tightRadius
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
local tightTargets = {}
|
|
1166
|
-
local ifFound = function(foundObject, targets, center)
|
|
1167
|
-
if foundObject:isExist() then
|
|
1168
|
-
local category = foundObject:getCategory()
|
|
1169
|
-
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
1170
|
-
category == Object.Category.STATIC then
|
|
1171
|
-
table.insert(targets, {
|
|
1172
|
-
name = foundObject:getTypeName(),
|
|
1173
|
-
distance = getDistance(center, foundObject:getPoint()),
|
|
1174
|
-
health = foundObject:getLife() or 0,
|
|
1175
|
-
position = foundObject:getPoint(),
|
|
1176
|
-
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
1177
|
-
unit = foundObject
|
|
1178
|
-
})
|
|
1179
|
-
end
|
|
1180
|
-
end
|
|
1181
|
-
return true
|
|
1182
|
-
end
|
|
1183
|
-
if splash_damage_options.track_pre_explosion_debug then
|
|
1184
|
-
debugMsg("Scanning tight radius " .. tightRadius .. "m at current pos while weapon exists")
|
|
1185
|
-
end
|
|
1186
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, function(obj) ifFound(obj, tightTargets, wpnData.pos) end)
|
|
1187
|
-
wpnData.tightTargets = tightTargets --Store for impact
|
|
1188
|
-
|
|
1189
|
-
--Wider scan for lastKnownTargets
|
|
1190
|
-
volS.params.point = predictedImpact
|
|
1191
|
-
volS.params.radius = blastRadius
|
|
1192
|
-
local foundTargets = {}
|
|
1193
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, function(obj) ifFound(obj, foundTargets, predictedImpact) end)
|
|
1194
|
-
wpnData.lastKnownTargets = foundTargets
|
|
1195
|
-
end
|
|
1196
|
-
--Submunition impact handling
|
|
1197
|
-
local weaponData = explTable[wpnData.parent or wpnData.name] or { submunition_name = "unknown" }
|
|
1198
|
-
if wpnData.name == weaponData.submunition_name then
|
|
1199
|
-
local groundHeight = land.getHeight({x = wpnData.pos.x, y = wpnData.pos.z})
|
|
1200
|
-
if wpnData.pos.y - groundHeight < 50 then --Impact threshold like old script
|
|
1201
|
-
debugMsg("Submunition '" .. wpnData.name .. "' from '" .. (wpnData.parent or "unknown") .. "' impacted at X: " .. string.format("%.0f", wpnData.pos.x) .. ", Z: " .. string.format("%.0f", wpnData.pos.z))
|
|
1202
|
-
local parentWeaponData = explTable[wpnData.parent] or { submunition_count = 30, submunition_explosive = 1 }
|
|
1203
|
-
local submunitionCount = parentWeaponData.submunition_count or 30
|
|
1204
|
-
local submunitionPower = (parentWeaponData.submunition_explosive or 1) * splash_damage_options.cluster_bomblet_damage_modifier * splash_damage_options.overall_scaling
|
|
1205
|
-
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
1206
|
-
if submunitionCount > 35 then
|
|
1207
|
-
local reductionFactor = (60 - 35) / (247 - 35)
|
|
1208
|
-
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
1209
|
-
if submunitionCount > 60 then submunitionCount = 60 end
|
|
1210
|
-
end
|
|
1211
|
-
end
|
|
1212
|
-
--Use parent velocity if available, else submunition speed
|
|
1213
|
-
local parentDir = wpnData.parentVelocity or wpnData.speed
|
|
1214
|
-
local dispersionLength, dispersionWidth = calculate_dispersion(parentDir, 2000) --Match original 2000m
|
|
1215
|
-
local dirMag = math.sqrt(parentDir.x^2 + parentDir.z^2)
|
|
1216
|
-
local dir = dirMag > 0 and {x = parentDir.x / dirMag, z = parentDir.z / dirMag} or {x = 1, z = 0}
|
|
1217
|
-
debugMsg("Simulating " .. submunitionCount .. " bomblets for submunition '" .. wpnData.name .. "' from '" .. (wpnData.parent or "unknown") .. "' over " .. string.format("%.0f", dispersionLength) .. "m x " .. string.format("%.0f", dispersionWidth) .. "m")
|
|
1218
|
-
for i = 1, submunitionCount do
|
|
1219
|
-
local theta = math.random() * 2 * math.pi
|
|
1220
|
-
local r = math.sqrt(math.random())
|
|
1221
|
-
local xOffset = r * dispersionLength * 0.5 * math.cos(theta)
|
|
1222
|
-
local zOffset = r * dispersionWidth * 0.5 * math.sin(theta)
|
|
1223
|
-
local subPos = {
|
|
1224
|
-
x = wpnData.pos.x + (xOffset * dir.x - zOffset * dir.z),
|
|
1225
|
-
z = wpnData.pos.z + (xOffset * dir.z + zOffset * dir.x)
|
|
1226
|
-
}
|
|
1227
|
-
subPos.y = land.getHeight({x = subPos.x, y = subPos.z})
|
|
1228
|
-
debugMsg("Triggering bomblet #" .. i .. " for submunition '" .. wpnData.name .. "' at X: " .. string.format("%.0f", subPos.x) .. ", Z: " .. string.format("%.0f", subPos.z) .. " with power " .. submunitionPower)
|
|
1229
|
-
trigger.action.explosion(subPos, submunitionPower)
|
|
1230
|
-
end
|
|
1231
|
-
table.insert(weaponsToRemove, wpn_id_)
|
|
1232
|
-
end
|
|
1233
|
-
end
|
|
1234
|
-
else
|
|
1235
|
-
--Weapon has impacted
|
|
1236
|
-
debugMsg("Weapon " .. wpnData.name .. " no longer exists at " .. timer.getTime() .. "s")
|
|
1237
|
-
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed)) --terrain intersection point with weapon's nose. Only search out 20 meters though.
|
|
1238
|
-
local explosionPoint
|
|
1239
|
-
if not ip then --use last calculated IP
|
|
1240
|
-
explosionPoint = wpnData.pos
|
|
1241
|
-
else --use intersection point
|
|
1242
|
-
explosionPoint = ip
|
|
1243
|
-
end
|
|
1244
|
-
local chosenTargets = wpnData.tightTargets or {}
|
|
1245
|
-
local safeToBlast = true
|
|
1246
|
-
if splash_damage_options.ordnance_protection then
|
|
1247
|
-
local checkVol = { id = world.VolumeType.SPHERE, params = { point = explosionPoint, radius = splash_damage_options.ordnance_protection_radius } }
|
|
1248
|
-
debugMsg("Checking ordnance protection for '" .. wpnData.name .. "' at X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. " with radius " .. splash_damage_options.ordnance_protection_radius .. "m")
|
|
1249
|
-
world.searchObjects(Object.Category.WEAPON, checkVol, function(obj)
|
|
1250
|
-
if obj:isExist() and tracked_weapons[obj.id_] then
|
|
1251
|
-
safeToBlast = false
|
|
1252
|
-
debugMsg("Skipping explosion for '" .. wpnData.name .. "' - nearby bomb '" .. tracked_weapons[obj.id_].name .. "' within " .. splash_damage_options.ordnance_protection_radius .. "m")
|
|
1253
|
-
return false
|
|
1254
|
-
end
|
|
1255
|
-
return true
|
|
1256
|
-
end)
|
|
1257
|
-
end
|
|
1258
|
-
if safeToBlast then
|
|
1259
|
-
debugMsg("FinalPos Check for '" .. wpnData.name .. "': X: " .. string.format("%.0f", explosionPoint.x) .. ", Y: " .. string.format("%.0f", explosionPoint.y) .. ", Z: " .. string.format("%.0f", explosionPoint.z) .. ")")
|
|
1260
|
-
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
1261
|
-
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
1262
|
-
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
1263
|
-
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
1264
|
-
end
|
|
1265
|
-
|
|
1266
|
-
local explosionPower = base_explosive
|
|
1267
|
-
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1268
|
-
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
1269
|
-
end
|
|
1270
|
-
|
|
1271
|
-
local blastRadius = splash_damage_options.blast_search_radius * 2 --Wider post-scan (180m default)
|
|
1272
|
-
if splash_damage_options.use_dynamic_blast_radius then
|
|
1273
|
-
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1274
|
-
end
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
--Store pre-explosion state of all tracked weapons for detection
|
|
1278
|
-
local preExplosionWeapons = {}
|
|
1279
|
-
if splash_damage_options.ordnance_protection and splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
1280
|
-
for id, data in pairs(tracked_weapons) do
|
|
1281
|
-
if data.wpn:isExist() then
|
|
1282
|
-
preExplosionWeapons[id] = {
|
|
1283
|
-
name = data.name,
|
|
1284
|
-
pos = data.wpn:getPosition().p,
|
|
1285
|
-
distance = getDistance3D(explosionPoint, data.wpn:getPosition().p),
|
|
1286
|
-
explosive = getWeaponExplosive(data.name) --Store the explosive power
|
|
1287
|
-
}
|
|
1288
|
-
end
|
|
1289
|
-
end
|
|
1290
|
-
end
|
|
1291
|
-
--Cluster Bomb Handling
|
|
1292
|
-
local weaponData = explTable[wpnData.name] or { explosive = 0, shaped_charge = false }
|
|
1293
|
-
local isCluster = weaponData.cluster or false
|
|
1294
|
-
if splash_damage_options.cluster_enabled and isCluster then
|
|
1295
|
-
local submunitionCount = weaponData.submunition_count or 30
|
|
1296
|
-
local submunitionPower = (weaponData.submunition_explosive or 1) * splash_damage_options.cluster_bomblet_damage_modifier * splash_damage_options.overall_scaling
|
|
1297
|
-
local submunitionName = weaponData.submunition_name or "unknown"
|
|
1298
|
-
--Apply bomblet reduction logic if enabled
|
|
1299
|
-
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
1300
|
-
if submunitionCount > 35 then
|
|
1301
|
-
local reductionFactor = (60 - 35) / (247 - 35)
|
|
1302
|
-
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
1303
|
-
if submunitionCount > 60 then submunitionCount = 60 end --Cap at 60
|
|
1304
|
-
end
|
|
1305
|
-
end
|
|
1306
|
-
--Extended scan with general bomblet detection
|
|
1307
|
-
timer.scheduleFunction(track_wpns_cluster_scan, {explosionPoint, wpnData.dir, wpnData.name, submunitionName, submunitionCount, submunitionPower, wpnData.speed}, timer.getTime() + 0.3)
|
|
1308
|
-
else
|
|
1309
|
-
--Standard explosion handling
|
|
1310
|
-
if splash_damage_options.larger_explosions then
|
|
1311
|
-
debugMsg("Triggering initial explosion for '" .. wpnData.name .. "' at power " .. explosionPower)
|
|
1312
|
-
trigger.action.explosion(explosionPoint, explosionPower)
|
|
1313
|
-
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
1314
|
-
debugMsg("Added to recentExplosions for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
1315
|
-
end
|
|
1316
|
-
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosionPower, isShapedCharge)
|
|
1317
|
-
end
|
|
1318
|
-
--detect_ordnance_destruction comes before recent_large_explosion_snap in original
|
|
1319
|
-
if splash_damage_options.ordnance_protection and splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
1320
|
-
timer.scheduleFunction(function(args)
|
|
1321
|
-
local explosionPoint = args[1]
|
|
1322
|
-
local blastRadius = args[2]
|
|
1323
|
-
local triggeringWeapon = args[3]
|
|
1324
|
-
local preExplosionWeapons = args[4]
|
|
1325
|
-
for id, preData in pairs(preExplosionWeapons) do
|
|
1326
|
-
if tracked_weapons[id] and not tracked_weapons[id].wpn:isExist() then
|
|
1327
|
-
if preData.distance <= blastRadius then
|
|
1328
|
-
local msg = "WARNING: " .. preData.name .. " destroyed by large explosion from " .. triggeringWeapon .. " at " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", explosionPoint.x, explosionPoint.y, explosionPoint.z)
|
|
1329
|
-
gameMsg(msg)
|
|
1330
|
-
debugMsg(msg)
|
|
1331
|
-
env.info(msg)
|
|
1332
|
-
if splash_damage_options.snap_to_ground_if_destroyed_by_large_explosion then
|
|
1333
|
-
local groundPos = {
|
|
1334
|
-
x = preData.pos.x,
|
|
1335
|
-
y = land.getHeight({x = preData.pos.x, y = preData.pos.z}),
|
|
1336
|
-
z = preData.pos.z
|
|
1337
|
-
}
|
|
1338
|
-
local destroyedWeaponPower, isShapedCharge = preData.explosive
|
|
1339
|
-
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.overall_scaling
|
|
1340
|
-
if splash_damage_options.rocket_multiplier and tracked_weapons[id].cat == Weapon.Category.ROCKET then
|
|
1341
|
-
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.rocket_multiplier
|
|
1342
|
-
end
|
|
1343
|
-
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1344
|
-
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.shaped_charge_multiplier
|
|
1345
|
-
end
|
|
1346
|
-
debugMsg("Triggering ground explosion for destroyed " .. preData.name .. " (detect_ordnance_destruction) at X: " .. string.format("%.0f", groundPos.x) .. ", Y: " .. string.format("%.0f", groundPos.y) .. ", Z: " .. string.format("%.0f", groundPos.z) .. " with power " .. destroyedWeaponPower)
|
|
1347
|
-
trigger.action.explosion(groundPos, destroyedWeaponPower)
|
|
1348
|
-
end
|
|
1349
|
-
end
|
|
1350
|
-
end
|
|
1351
|
-
end
|
|
1352
|
-
end, {explosionPoint, blastRadius, wpnData.name, preExplosionWeapons}, timer.getTime() + 0.2)
|
|
1353
|
-
end
|
|
1354
|
-
--recent_large_explosion_snap comes after main explosion and detect_ordnance_destruction
|
|
1355
|
-
if splash_damage_options.ordnance_protection and splash_damage_options.larger_explosions and splash_damage_options.recent_large_explosion_snap and splash_damage_options.snap_to_ground_if_destroyed_by_large_explosion then
|
|
1356
|
-
local currentTime = timer.getTime()
|
|
1357
|
-
for id, data in pairs(tracked_weapons) do
|
|
1358
|
-
if id ~= wpn_id_ and not data.wpn:isExist() then
|
|
1359
|
-
local terrainHeight = land.getHeight({x = data.pos.x, y = data.pos.z})
|
|
1360
|
-
local weaponHeight = data.pos.y - terrainHeight --Calculate height above ground
|
|
1361
|
-
local isMidAir = weaponHeight > 5 --Still checks if above ground
|
|
1362
|
-
local snapTriggered = false
|
|
1363
|
-
for _, explosion in ipairs(recentExplosions) do
|
|
1364
|
-
local timeDiff = currentTime - explosion.time
|
|
1365
|
-
local distance = getDistance3D(data.pos, explosion.pos)
|
|
1366
|
-
debugMsg("Checking " .. data.name .. " at X: " .. data.pos.x .. ", Y: " .. data.pos.y .. ", Z: " .. data.pos.z .. " against explosion at X: " .. explosion.pos.x .. ", Y: " .. explosion.pos.y .. ", Z: " .. explosion.pos.z .. " - Distance: " .. distance .. "m, TimeDiff: " .. timeDiff .. "s")
|
|
1367
|
-
if timeDiff <= splash_damage_options.recent_large_explosion_time and distance <= splash_damage_options.recent_large_explosion_range then
|
|
1368
|
-
if isMidAir and weaponHeight <= splash_damage_options.max_snapped_height then --New height check
|
|
1369
|
-
local groundPos = { x = data.pos.x, y = terrainHeight, z = data.pos.z }
|
|
1370
|
-
local destroyedWeaponPower, isShapedCharge = getWeaponExplosive(data.name)
|
|
1371
|
-
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.overall_scaling
|
|
1372
|
-
if splash_damage_options.rocket_multiplier and data.cat == Weapon.Category.ROCKET then
|
|
1373
|
-
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.rocket_multiplier
|
|
1374
|
-
end
|
|
1375
|
-
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1376
|
-
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.shaped_charge_multiplier
|
|
1377
|
-
end
|
|
1378
|
-
debugMsg("Weapon " .. data.name .. " detected recent large explosion within " .. splash_damage_options.recent_large_explosion_range .. "m and " .. splash_damage_options.recent_large_explosion_time .. "s, snapping to ground at X: " .. string.format("%.0f", groundPos.x) .. ", Y: " .. string.format("%.0f", groundPos.y) .. ", Z: " .. string.format("%.0f", groundPos.z) .. " with power " .. destroyedWeaponPower .. " (Height: " .. string.format("%.0f", weaponHeight) .. "m)")
|
|
1379
|
-
trigger.action.explosion(groundPos, destroyedWeaponPower)
|
|
1380
|
-
snapTriggered = true
|
|
1381
|
-
table.insert(weaponsToRemove, id)
|
|
1382
|
-
break
|
|
1383
|
-
elseif isMidAir then
|
|
1384
|
-
debugMsg("Weapon " .. data.name .. " destroyed above max_snapped_height (" .. splash_damage_options.max_snapped_height .. "m) at " .. string.format("%.0f", weaponHeight) .. "m, skipping snap")
|
|
1385
|
-
else
|
|
1386
|
-
debugMsg("Weapon " .. data.name .. " impacted ground within recent_large_explosion_range (" .. splash_damage_options.recent_large_explosion_range .. "m) and time (" .. splash_damage_options.recent_large_explosion_time .. "s), no snap needed")
|
|
1387
|
-
snapTriggered = true
|
|
1388
|
-
break
|
|
1389
|
-
end
|
|
1390
|
-
end
|
|
1391
|
-
end
|
|
1392
|
-
if not snapTriggered then
|
|
1393
|
-
if isMidAir then
|
|
1394
|
-
debugMsg("Weapon " .. data.name .. " destroyed in air, but no recent large explosion within " .. splash_damage_options.recent_large_explosion_range .. "m or " .. splash_damage_options.recent_large_explosion_time .. "s")
|
|
1395
|
-
else
|
|
1396
|
-
debugMsg("Weapon " .. data.name .. " impacted ground, not processed by recent large explosion settings")
|
|
1397
|
-
end
|
|
1398
|
-
end
|
|
1399
|
-
end
|
|
1400
|
-
end
|
|
1401
|
-
local newExplosions = {}
|
|
1402
|
-
for _, explosion in ipairs(recentExplosions) do
|
|
1403
|
-
if currentTime - explosion.time <= splash_damage_options.recent_large_explosion_time then
|
|
1404
|
-
table.insert(newExplosions, explosion)
|
|
1405
|
-
end
|
|
1406
|
-
end
|
|
1407
|
-
recentExplosions = newExplosions
|
|
1408
|
-
end
|
|
1409
|
-
--Mark units as destroyed to avoid MiST accessing them
|
|
1410
|
-
local destroyedUnits = {}
|
|
1411
|
-
for _, target in ipairs(chosenTargets) do
|
|
1412
|
-
if target.unit:isExist() and target.health > 0 and target.unit:getLife() <= 0 then
|
|
1413
|
-
destroyedUnits[target.name] = true
|
|
1414
|
-
debugMsg("Marked " .. target.name .. " as destroyed pre-impact")
|
|
1415
|
-
end
|
|
1416
|
-
end
|
|
1417
|
-
--Schedule explosion handling with original 0.1-second delay, enhanced error handling
|
|
1418
|
-
timer.scheduleFunction(function(args)
|
|
1419
|
-
local finalPos = args[1]
|
|
1420
|
-
local explosionPoint = args[2]
|
|
1421
|
-
local explosionPower = args[3]
|
|
1422
|
-
local isShapedCharge = args[4]
|
|
1423
|
-
local blastRadius = args[5]
|
|
1424
|
-
local chosenTargets = args[6]
|
|
1425
|
-
local weaponName = args[7]
|
|
1426
|
-
local wpnData = args[8]
|
|
1427
|
-
if splash_damage_options.debug then
|
|
1428
|
-
debugMsg("Starting impact handling for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1429
|
-
end
|
|
1430
|
-
local status, err = pcall(function()
|
|
1431
|
-
--Log pre-explosion targets
|
|
1432
|
-
if splash_damage_options.track_pre_explosion then
|
|
1433
|
-
if #chosenTargets > 0 then
|
|
1434
|
-
local msg = "Targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame, using finalPos):\n"
|
|
1435
|
-
for i, target in ipairs(chosenTargets) do
|
|
1436
|
-
msg = msg .. "- " .. target.name .. " (Dist: " .. string.format("%.1f", target.distance) .. "m, Health: " .. target.health .. ")\n"
|
|
1437
|
-
end
|
|
1438
|
-
debugMsg(msg)
|
|
1439
|
-
env.info("SplashDamage Pre-Explosion (Last Frame): " .. msg)
|
|
1440
|
-
else
|
|
1441
|
-
debugMsg("No targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame)")
|
|
1442
|
-
env.info("SplashDamage Pre-Explosion (Last Frame): No targets in blast zone for " .. weaponName)
|
|
1443
|
-
end
|
|
1444
|
-
end
|
|
1445
|
-
|
|
1446
|
-
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosionPower, isShapedCharge)
|
|
1447
|
-
|
|
1448
|
-
--Post-explosion analysis and queue cargo effects
|
|
1449
|
-
if splash_damage_options.track_pre_explosion then
|
|
1450
|
-
timer.scheduleFunction(function(innerArgs)
|
|
1451
|
-
local impactPoint = innerArgs[1]
|
|
1452
|
-
local blastRadius = innerArgs[2]
|
|
1453
|
-
local preExplosionTargets = innerArgs[3] or {}
|
|
1454
|
-
local weaponName = innerArgs[4]
|
|
1455
|
-
local weaponPower = innerArgs[5]
|
|
1456
|
-
if splash_damage_options.debug == true then
|
|
1457
|
-
debugMsg("Starting post-explosion analysis for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1458
|
-
end
|
|
1459
|
-
|
|
1460
|
-
--Scan all units in wider radius
|
|
1461
|
-
local postExplosionTargets = {}
|
|
1462
|
-
local volS = {
|
|
1463
|
-
id = world.VolumeType.SPHERE,
|
|
1464
|
-
params = {
|
|
1465
|
-
point = impactPoint,
|
|
1466
|
-
radius = blastRadius
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
local ifFound = function(foundObject)
|
|
1471
|
-
if foundObject:isExist() then
|
|
1472
|
-
local category = foundObject:getCategory()
|
|
1473
|
-
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
1474
|
-
category == Object.Category.STATIC then
|
|
1475
|
-
table.insert(postExplosionTargets, {
|
|
1476
|
-
name = foundObject:getTypeName(),
|
|
1477
|
-
health = foundObject:getLife() or 0,
|
|
1478
|
-
position = foundObject:getPoint(),
|
|
1479
|
-
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0
|
|
1480
|
-
})
|
|
1481
|
-
end
|
|
1482
|
-
end
|
|
1483
|
-
return true
|
|
1484
|
-
end
|
|
1485
|
-
|
|
1486
|
-
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
1487
|
-
|
|
1488
|
-
local msg = "Post-explosion analysis for " .. weaponName .. ":\n"
|
|
1489
|
-
|
|
1490
|
-
--Match pre-detected units
|
|
1491
|
-
for _, preTarget in ipairs(preExplosionTargets) do
|
|
1492
|
-
local found = false
|
|
1493
|
-
local postHealth = 0
|
|
1494
|
-
local postPosition = nil
|
|
1495
|
-
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1496
|
-
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1497
|
-
found = true
|
|
1498
|
-
postHealth = postTarget.health
|
|
1499
|
-
postPosition = postTarget.position
|
|
1500
|
-
break
|
|
1501
|
-
end
|
|
1502
|
-
end
|
|
1503
|
-
|
|
1504
|
-
local healthPercent = preTarget.maxHealth > 0 and (postHealth / preTarget.maxHealth * 100) or 0
|
|
1505
|
-
local status = ""
|
|
1506
|
-
|
|
1507
|
-
if not found or postHealth <= 0 then
|
|
1508
|
-
status = "WAS FULLY DESTROYED"
|
|
1509
|
-
elseif healthPercent < splash_damage_options.cargo_damage_threshold then
|
|
1510
|
-
status = "WAS DAMAGED BELOW THRESHOLD"
|
|
1511
|
-
else
|
|
1512
|
-
status = "SURVIVED (Health: " .. postHealth .. ")"
|
|
1513
|
-
end
|
|
1514
|
-
|
|
1515
|
-
--Always include coords in status message
|
|
1516
|
-
local coords = found and postPosition or preTarget.position
|
|
1517
|
-
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: " .. preTarget.health .. ", Post: " .. postHealth .. ")"
|
|
1518
|
-
--Check if target is in cargoUnits and within blast radius
|
|
1519
|
-
local cargoData = cargoUnits[preTarget.name]
|
|
1520
|
-
if cargoData and preTarget.distance <= blastRadius and
|
|
1521
|
-
(not found or postHealth <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1522
|
-
|
|
1523
|
-
if splash_damage_options.enable_cargo_effects then
|
|
1524
|
-
local cargoPower = cargoData.cargoExplosionPower or weaponPower --Use fixed power or fallback
|
|
1525
|
-
table.insert(cargoEffectsQueue, {
|
|
1526
|
-
name = preTarget.name,
|
|
1527
|
-
distance = preTarget.distance,
|
|
1528
|
-
coords = coords,
|
|
1529
|
-
power = cargoPower,
|
|
1530
|
-
explosion = cargoData.cargoExplosion,
|
|
1531
|
-
cookOff = cargoData.cargoCookOff,
|
|
1532
|
-
cookOffCount = cargoData.cookOffCount,
|
|
1533
|
-
cookOffPower = cargoData.cookOffPower,
|
|
1534
|
-
cookOffDuration = cargoData.cookOffDuration,
|
|
1535
|
-
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1536
|
-
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1537
|
-
isTanker = cargoData.isTanker,
|
|
1538
|
-
flameSize = cargoData.flameSize,
|
|
1539
|
-
flameDuration = cargoData.flameDuration
|
|
1540
|
-
})
|
|
1541
|
-
statusMsg = statusMsg .. " WITH CARGO EXPLOSION (Power: " .. cargoPower .. ")"
|
|
1542
|
-
if cargoData.cargoCookOff and cargoData.cookOffCount > 0 then
|
|
1543
|
-
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1544
|
-
end
|
|
1545
|
-
end
|
|
1546
|
-
end
|
|
1547
|
-
|
|
1548
|
-
msg = msg .. "- " .. preTarget.name .. " " .. statusMsg .. "\n"
|
|
1549
|
-
end
|
|
1550
|
-
--Check for additional units
|
|
1551
|
-
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1552
|
-
local isPreDetected = false
|
|
1553
|
-
for _, preTarget in ipairs(preExplosionTargets) do
|
|
1554
|
-
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1555
|
-
isPreDetected = true
|
|
1556
|
-
break
|
|
1557
|
-
end
|
|
1558
|
-
end
|
|
1559
|
-
if not isPreDetected then
|
|
1560
|
-
local coords = postTarget.position
|
|
1561
|
-
local healthPercent = postTarget.maxHealth > 0 and (postTarget.health / postTarget.maxHealth * 100) or 0
|
|
1562
|
-
local status = postTarget.health <= 0 and "WAS FULLY DESTROYED" or
|
|
1563
|
-
(healthPercent < splash_damage_options.cargo_damage_threshold and "WAS DAMAGED BELOW THRESHOLD" or
|
|
1564
|
-
"SURVIVED (Health: " .. postTarget.health .. ")")
|
|
1565
|
-
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: Unknown, Post: " .. postTarget.health .. ")"
|
|
1566
|
-
local cargoData = cargoUnits[postTarget.name]
|
|
1567
|
-
if cargoData and (postTarget.health <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1568
|
-
if splash_damage_options.enable_cargo_effects then
|
|
1569
|
-
local cargoPower = cargoData.cargoExplosionPower or weaponPower --Use fixed power or fallback
|
|
1570
|
-
local distance = getDistance(impactPoint, coords)
|
|
1571
|
-
table.insert(cargoEffectsQueue, {
|
|
1572
|
-
name = postTarget.name,
|
|
1573
|
-
distance = distance,
|
|
1574
|
-
coords = coords,
|
|
1575
|
-
power = cargoPower,
|
|
1576
|
-
explosion = cargoData.cargoExplosion,
|
|
1577
|
-
cookOff = cargoData.cargoCookOff,
|
|
1578
|
-
cookOffCount = cargoData.cookOffCount,
|
|
1579
|
-
cookOffPower = cargoData.cookOffPower,
|
|
1580
|
-
cookOffDuration = cargoData.cookOffDuration,
|
|
1581
|
-
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1582
|
-
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1583
|
-
isTanker = cargoData.isTanker,
|
|
1584
|
-
flameSize = cargoData.flameSize,
|
|
1585
|
-
flameDuration = cargoData.flameDuration
|
|
1586
|
-
})
|
|
1587
|
-
statusMsg = statusMsg .. " WITH CARGO EXPLOSION (Power: " .. cargoPower .. ")"
|
|
1588
|
-
if cargoData.cargoCookOff and cargoData.cookOffCount > 0 then
|
|
1589
|
-
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1590
|
-
end
|
|
1591
|
-
end
|
|
1592
|
-
end
|
|
1593
|
-
msg = msg .. "- " .. postTarget.name .. " " .. statusMsg .. "\n"
|
|
1594
|
-
end
|
|
1595
|
-
end
|
|
1596
|
-
|
|
1597
|
-
--Schedule all queued cargo effects
|
|
1598
|
-
if #cargoEffectsQueue > 0 then
|
|
1599
|
-
local effectIndex = 0
|
|
1600
|
-
local processedCargoUnits = {} --Track processed units
|
|
1601
|
-
local flamePositions = {} --Track flame coords with 3m radius
|
|
1602
|
-
for _, effect in ipairs(cargoEffectsQueue) do
|
|
1603
|
-
local unitKey = effect.name .. "_" .. effect.coords.x .. "_" .. effect.coords.z
|
|
1604
|
-
if not processedUnitsGlobal[unitKey] and not processedCargoUnits[unitKey] then
|
|
1605
|
-
if effect.explosion then
|
|
1606
|
-
debugMsg("Triggering cargo explosion for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. effectIndex .. "s")
|
|
1607
|
-
timer.scheduleFunction(function(params)
|
|
1608
|
-
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
1609
|
-
trigger.action.explosion(params[1], params[2])
|
|
1610
|
-
end, {effect.coords, effect.power}, timer.getTime() + effectIndex + 0.1) --Slight delay for visibility
|
|
1611
|
-
if effect.isTanker then
|
|
1612
|
-
local flameSize = effect.flameSize or 3
|
|
1613
|
-
local flameDuration = effect.flameDuration --Use cargoUnits value directly, no default
|
|
1614
|
-
local flameDensity = 1.0 --Max density for visibility
|
|
1615
|
-
local effectId = effectSmokeId
|
|
1616
|
-
effectSmokeId = effectSmokeId + 1
|
|
1617
|
-
--Check for nearby flames within 3m
|
|
1618
|
-
local isDuplicate = false
|
|
1619
|
-
for _, pos in pairs(flamePositions) do
|
|
1620
|
-
if getDistance3D(effect.coords, pos) < 3 then
|
|
1621
|
-
isDuplicate = true
|
|
1622
|
-
debugMsg("Skipping duplicate flame for " .. effect.name .. " near X: " .. string.format("%.0f", pos.x) .. ", Y: " .. string.format("%.0f", pos.y) .. ", Z: " .. string.format("%.0f", pos.z))
|
|
1623
|
-
break
|
|
1624
|
-
end
|
|
1625
|
-
end
|
|
1626
|
-
if not isDuplicate then
|
|
1627
|
-
debugMsg("Adding flame effect for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m (Size: " .. flameSize .. ", Duration: " .. flameDuration .. "s, ID: " .. effectId .. ") scheduled at " .. effectIndex .. "s")
|
|
1628
|
-
timer.scheduleFunction(function(params)
|
|
1629
|
-
--Adjust Y-coordinate to terrain height + offset
|
|
1630
|
-
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
1631
|
-
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
1632
|
-
debugMsg("Spawning flame effect at X: " .. string.format("%.0f", adjustedCoords.x) .. ", Y: " .. string.format("%.0f", adjustedCoords.y) .. ", Z: " .. string.format("%.0f", adjustedCoords.z))
|
|
1633
|
-
trigger.action.explosion(adjustedCoords, 10) --Small explosion to force visibility
|
|
1634
|
-
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
1635
|
-
end, {effect.coords, flameSize, flameDensity, effectId}, timer.getTime() + effectIndex + 0.2) --Slight delay
|
|
1636
|
-
timer.scheduleFunction(function(id)
|
|
1637
|
-
debugMsg("Stopping flame effect for " .. effect.name .. " (ID: " .. id .. ")")
|
|
1638
|
-
trigger.action.effectSmokeStop(id)
|
|
1639
|
-
end, effectId, timer.getTime() + effectIndex + flameDuration + 0.2)
|
|
1640
|
-
table.insert(flamePositions, effect.coords)
|
|
1641
|
-
end
|
|
1642
|
-
end
|
|
1643
|
-
end
|
|
1644
|
-
debugMsg("Checking cook-off for " .. effect.name .. ": cookOff=" .. tostring(effect.cookOff) .. ", count=" .. tostring(effect.cookOffCount))
|
|
1645
|
-
if effect.cookOff and effect.cookOffCount > 0 then
|
|
1646
|
-
debugMsg("Scheduling " .. effect.cookOffCount .. " cook-off explosions for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m over " .. effect.cookOffDuration .. "s starting at " .. effectIndex .. "s")
|
|
1647
|
-
for i = 1, effect.cookOffCount do
|
|
1648
|
-
local delay = effect.cookOffRandomTiming and math.random() * effect.cookOffDuration or (i - 1) * (effect.cookOffDuration / effect.cookOffCount)
|
|
1649
|
-
local basePower = effect.cookOffPower
|
|
1650
|
-
local powerVariation = effect.cookOffPowerRandom / 100
|
|
1651
|
-
local cookOffPower = effect.cookOffPowerRandom == 0 and basePower or basePower * (1 + powerVariation * (math.random() * 2 - 1))
|
|
1652
|
-
debugMsg("Cook-off #" .. i .. " for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m scheduled at " .. string.format("%.3f", delay) .. "s with power " .. string.format("%.2f", cookOffPower))
|
|
1653
|
-
timer.scheduleFunction(function(params)
|
|
1654
|
-
local pos = params[1]
|
|
1655
|
-
local power = params[2]
|
|
1656
|
-
debugMsg("Executing cook-off at " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", pos.x, pos.y, pos.z) .. " with power " .. power)
|
|
1657
|
-
trigger.action.explosion(pos, power)
|
|
1658
|
-
end, {effect.coords, cookOffPower}, timer.getTime() + effectIndex + delay)
|
|
1659
|
-
end
|
|
1660
|
-
--Debris burst only if cook-off is true and enabled
|
|
1661
|
-
if splash_damage_options.debris_effects then
|
|
1662
|
-
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
1663
|
-
for j = 1, debrisCount do
|
|
1664
|
-
--Random spherical offset
|
|
1665
|
-
local theta = math.random() * 2 * math.pi --Horizontal angle
|
|
1666
|
-
local phi = math.acos(math.random() * 2 - 1) --Vertical angle for sphere
|
|
1667
|
-
local minDist = splash_damage_options.debris_max_distance * 0.1 --10% of max
|
|
1668
|
-
local maxDist = splash_damage_options.debris_max_distance
|
|
1669
|
-
local r = math.random() * (maxDist - minDist) + minDist --10% to full max distance
|
|
1670
|
-
local debrisX = effect.coords.x + r * math.sin(phi) * math.cos(theta)
|
|
1671
|
-
local debrisZ = effect.coords.z + r * math.sin(phi) * math.sin(theta)
|
|
1672
|
-
local terrainY = land.getHeight({x = debrisX, y = debrisZ})
|
|
1673
|
-
local debrisY = terrainY + math.random() * maxDist --0 to max_distance above ground
|
|
1674
|
-
local debrisPos = {x = debrisX, y = debrisY, z = debrisZ}
|
|
1675
|
-
local debrisPower = splash_damage_options.debris_power
|
|
1676
|
-
local debrisDelay = (j - 1) * (effect.cookOffDuration / debrisCount) --Spread over cook-off duration
|
|
1677
|
-
timer.scheduleFunction(function(debrisArgs)
|
|
1678
|
-
local dPos = debrisArgs[1]
|
|
1679
|
-
local dPower = debrisArgs[2]
|
|
1680
|
-
debugMsg("Debris explosion at X: " .. string.format("%.0f", dPos.x) .. ", Y: " .. string.format("%.0f", dPos.y) .. ", Z: " .. string.format("%.0f", dPos.z) .. " with power " .. dPower)
|
|
1681
|
-
trigger.action.explosion(dPos, dPower)
|
|
1682
|
-
end, {debrisPos, debrisPower}, timer.getTime() + effectIndex + debrisDelay)
|
|
1683
|
-
end
|
|
1684
|
-
end
|
|
1685
|
-
end
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
processedCargoUnits[unitKey] = true
|
|
1689
|
-
processedUnitsGlobal[unitKey] = true
|
|
1690
|
-
effectIndex = effectIndex + 3 --3 secs spacing if not random
|
|
1691
|
-
end
|
|
1692
|
-
end
|
|
1693
|
-
--Clear the queue after scheduling
|
|
1694
|
-
cargoEffectsQueue = {}
|
|
1695
|
-
end
|
|
1696
|
-
|
|
1697
|
-
debugMsg(msg)
|
|
1698
|
-
env.info("SplashDamage Post-Explosion: " .. msg)
|
|
1699
|
-
end, {finalPos, blastRadius, chosenTargets, weaponName, explosionPower}, timer.getTime() + 1)
|
|
1700
|
-
end
|
|
1701
|
-
end)
|
|
1702
|
-
if not status then
|
|
1703
|
-
debugMsg("Impact handling error for '" .. weaponName .. "': " .. err)
|
|
1704
|
-
end
|
|
1705
|
-
end, {explosionPoint, explosionPoint, explosionPower, isShapedCharge, blastRadius, chosenTargets, wpnData.name, wpnData}, timer.getTime() + 0.1)
|
|
1706
|
-
else
|
|
1707
|
-
debugMsg("Explosion skipped due to ordnance protection for '" .. wpnData.name .. "'")
|
|
1708
|
-
if splash_damage_options.larger_explosions then
|
|
1709
|
-
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
1710
|
-
debugMsg("Skipped explosion logged for snap check for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
1711
|
-
end
|
|
1712
|
-
end
|
|
1713
|
-
table.insert(weaponsToRemove, wpn_id_)
|
|
1714
|
-
end
|
|
1715
|
-
end)
|
|
1716
|
-
if not status then
|
|
1717
|
-
debugMsg("Error in track_wpns for '" .. (wpnData.name or "unknown weapon") .. "': " .. err)
|
|
1718
|
-
end
|
|
1719
|
-
end
|
|
1720
|
-
--Perform all removals after iteration
|
|
1721
|
-
for _, id in ipairs(weaponsToRemove) do
|
|
1722
|
-
tracked_weapons[id] = nil
|
|
1723
|
-
end
|
|
1724
|
-
return timer.getTime() + refreshRate
|
|
1725
|
-
end
|
|
1726
|
-
function onWpnEvent(event)
|
|
1727
|
-
if event.id == world.event.S_EVENT_SHOT then
|
|
1728
|
-
if event.weapon then
|
|
1729
|
-
local ordnance = event.weapon
|
|
1730
|
-
local typeName = trim(ordnance:getTypeName())
|
|
1731
|
-
if splash_damage_options.debug then
|
|
1732
|
-
env.info("Weapon fired: [" .. typeName .. "]")
|
|
1733
|
-
debugMsg("Weapon fired: [" .. typeName .. "]")
|
|
1734
|
-
end
|
|
1735
|
-
if string.find(typeName, "weapons.shells") then
|
|
1736
|
-
if splash_damage_options.debug then
|
|
1737
|
-
debugMsg("Event shot, but not tracking: " .. typeName)
|
|
1738
|
-
env.info("SplashDamage: event shot, but not tracking: " .. typeName .. " (" .. event.initiator:getTypeName() .. ")")
|
|
1739
|
-
end
|
|
1740
|
-
return
|
|
1741
|
-
end
|
|
1742
|
-
|
|
1743
|
-
--Check if weapon is in explTable before tracking
|
|
1744
|
-
if not explTable[typeName] then
|
|
1745
|
-
env.info("SplashDamage: " .. typeName .. " missing from script (" .. event.initiator:getTypeName() .. ")")
|
|
1746
|
-
if splash_damage_options.weapon_missing_message == true then
|
|
1747
|
-
trigger.action.outText("SplashDamage: " .. typeName .. " missing from script (" .. (event.initiator and event.initiator:isExist() and event.initiator:getTypeName() or "no initiator") .. ")", 3)
|
|
1748
|
-
-- if mist and mist.utils and mist.utils.tableShow then --Only if MiST is present
|
|
1749
|
-
-- local success, desc = pcall(mist.utils.tableShow, ordnance:getDesc())
|
|
1750
|
-
-- if success then
|
|
1751
|
-
-- debugMsg("desc for [" .. typeName .. "]: " .. desc)
|
|
1752
|
-
-- else
|
|
1753
|
-
-- debugMsg("Could not retrieve description for [" .. typeName .. "]. Object may no longer exist.")
|
|
1754
|
-
-- end
|
|
1755
|
-
-- end
|
|
1756
|
-
env.info("Current keys in explTable:")
|
|
1757
|
-
for k, v in pairs(explTable) do
|
|
1758
|
-
env.info("Key: [" .. k .. "]")
|
|
1759
|
-
end
|
|
1760
|
-
end
|
|
1761
|
-
return --Skip tracking this weapon since its not in the table
|
|
1762
|
-
end
|
|
1763
|
-
|
|
1764
|
-
if (ordnance:getDesc().category ~= 0) and event.initiator then
|
|
1765
|
-
if ordnance:getDesc().category == 1 then
|
|
1766
|
-
if (ordnance:getDesc().MissileCategory ~= 1 and ordnance:getDesc().MissileCategory ~= 2) then
|
|
1767
|
-
tracked_weapons[event.weapon.id_] = { wpn = ordnance, init = event.initiator:getName(), pos = ordnance:getPoint(), dir = ordnance:getPosition().x, name = typeName, speed = ordnance:getVelocity(), cat = ordnance:getCategory() }
|
|
1768
|
-
end
|
|
1769
|
-
else
|
|
1770
|
-
tracked_weapons[event.weapon.id_] = { wpn = ordnance, init = event.initiator:getName(), pos = ordnance:getPoint(), dir = ordnance:getPosition().x, name = typeName, speed = ordnance:getVelocity(), cat = ordnance:getCategory() }
|
|
1771
|
-
end
|
|
1772
|
-
end
|
|
1773
|
-
end
|
|
1774
|
-
end
|
|
1775
|
-
end
|
|
1776
|
-
|
|
1777
|
-
local function protectedCall(...)
|
|
1778
|
-
local status, retval = pcall(...)
|
|
1779
|
-
if not status then
|
|
1780
|
-
env.warning("Splash damage script error... gracefully caught! " .. retval, true)
|
|
1781
|
-
end
|
|
1782
|
-
end
|
|
1783
|
-
|
|
1784
|
-
function WpnHandler:onEvent(event)
|
|
1785
|
-
protectedCall(onWpnEvent, event)
|
|
1786
|
-
end
|
|
1787
|
-
|
|
1788
|
-
function explodeObject(args)
|
|
1789
|
-
local point = args[1]
|
|
1790
|
-
local distance = args[2]
|
|
1791
|
-
local power = args[3]
|
|
1792
|
-
trigger.action.explosion(point, power)
|
|
1793
|
-
end
|
|
1794
|
-
|
|
1795
|
-
function blastWave(_point, _radius, weapon, power, isShapedCharge)
|
|
1796
|
-
if isShapedCharge then
|
|
1797
|
-
_radius = _radius * splash_damage_options.shaped_charge_multiplier
|
|
1798
|
-
end
|
|
1799
|
-
if splash_damage_options.use_dynamic_blast_radius then
|
|
1800
|
-
local dynamicRadius = math.pow(power, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1801
|
-
if isShapedCharge then
|
|
1802
|
-
_radius = dynamicRadius * splash_damage_options.shaped_charge_multiplier
|
|
1803
|
-
else
|
|
1804
|
-
_radius = dynamicRadius
|
|
1805
|
-
end
|
|
1806
|
-
end
|
|
1807
|
-
|
|
1808
|
-
local foundUnits = {}
|
|
1809
|
-
local volS = {
|
|
1810
|
-
id = world.VolumeType.SPHERE,
|
|
1811
|
-
params = {
|
|
1812
|
-
point = _point,
|
|
1813
|
-
radius = _radius
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
local ifFound = function(foundObject, val)
|
|
1818
|
-
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT and foundObject:getCategory() == Object.Category.UNIT then
|
|
1819
|
-
foundUnits[#foundUnits + 1] = foundObject
|
|
1820
|
-
end
|
|
1821
|
-
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1822
|
-
if splash_damage_options.blast_stun == true then
|
|
1823
|
-
--suppressUnit(foundObject, 2, weapon)
|
|
1824
|
-
end
|
|
1825
|
-
end
|
|
1826
|
-
if splash_damage_options.wave_explosions then
|
|
1827
|
-
local obj = foundObject
|
|
1828
|
-
local obj_location = obj:getPoint()
|
|
1829
|
-
local dist = getDistance(_point, obj_location)
|
|
1830
|
-
local timing = dist / 500
|
|
1831
|
-
if obj:isExist() and tableHasKey(obj:getDesc(), "box") then
|
|
1832
|
-
local length = (obj:getDesc().box.max.x + math.abs(obj:getDesc().box.min.x))
|
|
1833
|
-
local height = (obj:getDesc().box.max.y + math.abs(obj:getDesc().box.min.y))
|
|
1834
|
-
local depth = (obj:getDesc().box.max.z + math.abs(obj:getDesc().box.min.z))
|
|
1835
|
-
local _length = length
|
|
1836
|
-
local _depth = depth
|
|
1837
|
-
if depth > length then
|
|
1838
|
-
_length = depth
|
|
1839
|
-
_depth = length
|
|
1840
|
-
end
|
|
1841
|
-
local surface_distance = dist - _depth / 2
|
|
1842
|
-
local scaled_power_factor = 0.006 * power + 1
|
|
1843
|
-
local intensity = (power * scaled_power_factor) / (4 * math.pi * surface_distance^2)
|
|
1844
|
-
local surface_area = _length * height
|
|
1845
|
-
local damage_for_surface = intensity * surface_area
|
|
1846
|
-
if damage_for_surface > splash_damage_options.cascade_damage_threshold then
|
|
1847
|
-
local explosion_size = damage_for_surface
|
|
1848
|
-
if obj:getDesc().category == Unit.Category.STRUCTURE then
|
|
1849
|
-
explosion_size = intensity * splash_damage_options.static_damage_boost
|
|
1850
|
-
end
|
|
1851
|
-
if explosion_size > power then explosion_size = power end
|
|
1852
|
-
local triggerExplosion = false
|
|
1853
|
-
if splash_damage_options.always_cascade_explode then
|
|
1854
|
-
triggerExplosion = true
|
|
1855
|
-
else
|
|
1856
|
-
if obj:getDesc().life then
|
|
1857
|
-
local healthPercent = (obj:getLife() / obj:getDesc().life) * 100
|
|
1858
|
-
if healthPercent <= splash_damage_options.cascade_explode_threshold then
|
|
1859
|
-
triggerExplosion = true
|
|
1860
|
-
end
|
|
1861
|
-
--Queue cargo effects for units below
|
|
1862
|
-
local cargoData = cargoUnits[obj:getTypeName()]
|
|
1863
|
-
if cargoData and healthPercent <= splash_damage_options.cargo_damage_threshold and splash_damage_options.enable_cargo_effects then
|
|
1864
|
-
local cargoPower = power * cargoData.cargoExplosionMult
|
|
1865
|
-
table.insert(cargoEffectsQueue, {
|
|
1866
|
-
name = obj:getTypeName(),
|
|
1867
|
-
distance = dist,
|
|
1868
|
-
coords = obj_location,
|
|
1869
|
-
power = cargoPower,
|
|
1870
|
-
explosion = cargoData.cargoExplosion,
|
|
1871
|
-
cookOff = cargoData.cargoCookOff,
|
|
1872
|
-
cookOffCount = cargoData.cookOffCount,
|
|
1873
|
-
cookOffPower = cargoData.cookOffPower,
|
|
1874
|
-
cookOffDuration = cargoData.cookOffDuration,
|
|
1875
|
-
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1876
|
-
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1877
|
-
isTanker = cargoData.isTanker,
|
|
1878
|
-
flameSize = cargoData.flameSize,
|
|
1879
|
-
flameDuration = cargoData.flameDuration
|
|
1880
|
-
})
|
|
1881
|
-
end
|
|
1882
|
-
else
|
|
1883
|
-
triggerExplosion = true
|
|
1884
|
-
end
|
|
1885
|
-
if not triggerExplosion and obj:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1886
|
-
local health = obj:getLife() or 0
|
|
1887
|
-
if health <= 0 then
|
|
1888
|
-
triggerExplosion = true
|
|
1889
|
-
end
|
|
1890
|
-
end
|
|
1891
|
-
end
|
|
1892
|
-
if triggerExplosion then
|
|
1893
|
-
timer.scheduleFunction(explodeObject, {obj_location, dist, explosion_size * splash_damage_options.cascade_scaling}, timer.getTime() + timing)
|
|
1894
|
-
end
|
|
1895
|
-
end
|
|
1896
|
-
end
|
|
1897
|
-
end
|
|
1898
|
-
return true
|
|
1899
|
-
end
|
|
1900
|
-
|
|
1901
|
-
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
|
1902
|
-
world.searchObjects(Object.Category.STATIC, volS, ifFound)
|
|
1903
|
-
world.searchObjects(Object.Category.SCENERY, volS, ifFound)
|
|
1904
|
-
world.searchObjects(Object.Category.CARGO, volS, ifFound)
|
|
1905
|
-
if splash_damage_options.damage_model then
|
|
1906
|
-
timer.scheduleFunction(modelUnitDamage, foundUnits, timer.getTime() + 1.5)
|
|
1907
|
-
end
|
|
1908
|
-
end
|
|
1909
|
-
|
|
1910
|
-
function modelUnitDamage(units)
|
|
1911
|
-
for i, unit in ipairs(units) do
|
|
1912
|
-
if unit:isExist() then
|
|
1913
|
-
local health = (unit:getLife() / unit:getDesc().life) * 100
|
|
1914
|
-
if unit:hasAttribute("Infantry") and health > 0 then
|
|
1915
|
-
if health <= splash_damage_options.infantry_cant_fire_health then
|
|
1916
|
-
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1917
|
-
end
|
|
1918
|
-
end
|
|
1919
|
-
if unit:getDesc().category == Unit.Category.GROUND_UNIT and (not unit:hasAttribute("Infantry")) and health > 0 then
|
|
1920
|
-
if health <= splash_damage_options.unit_cant_fire_health then
|
|
1921
|
-
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1922
|
-
gameMsg(unit:getTypeName() .. " weapons disabled")
|
|
1923
|
-
end
|
|
1924
|
-
if health <= splash_damage_options.unit_disabled_health and health > 0 then
|
|
1925
|
-
unit:getController():setTask({id = 'Hold', params = {}})
|
|
1926
|
-
unit:getController():setOnOff(false)
|
|
1927
|
-
gameMsg(unit:getTypeName() .. " disabled")
|
|
1928
|
-
end
|
|
1929
|
-
end
|
|
1930
|
-
end
|
|
1931
|
-
end
|
|
1932
|
-
end
|
|
1933
|
-
|
|
1934
|
-
function updateSplashDamageSetting(setting, increment)
|
|
1935
|
-
if not splash_damage_options[setting] then
|
|
1936
|
-
env.info("Error: Setting " .. setting .. " does not exist.")
|
|
1937
|
-
return
|
|
1938
|
-
end
|
|
1939
|
-
|
|
1940
|
-
local newValue = math.max(0, splash_damage_options[setting] + increment)
|
|
1941
|
-
env.info("Updating " .. setting .. " from " .. tostring(splash_damage_options[setting]) .. " to " .. tostring(newValue))
|
|
1942
|
-
splash_damage_options[setting] = newValue
|
|
1943
|
-
trigger.action.outText("Updated " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1944
|
-
end
|
|
1945
|
-
|
|
1946
|
-
function toggleSplashDamageSetting(setting)
|
|
1947
|
-
splash_damage_options[setting] = not splash_damage_options[setting]
|
|
1948
|
-
trigger.action.outText("Toggled " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1949
|
-
|
|
1950
|
-
if setting == "enable_radio_menu" then
|
|
1951
|
-
if splash_damage_options.enable_radio_menu then
|
|
1952
|
-
addSplashDamageMenu()
|
|
1953
|
-
else
|
|
1954
|
-
missionCommands.removeItem(splash_damage_menu)
|
|
1955
|
-
splash_damage_menu = nil
|
|
1956
|
-
end
|
|
1957
|
-
end
|
|
1958
|
-
end
|
|
1959
|
-
|
|
1960
|
-
function addValueAdjustmentCommands(menu, setting)
|
|
1961
|
-
missionCommands.addCommand("+0.1", menu, updateSplashDamageSetting, setting, 0.1)
|
|
1962
|
-
missionCommands.addCommand("+1", menu, updateSplashDamageSetting, setting, 1)
|
|
1963
|
-
missionCommands.addCommand("+10", menu, updateSplashDamageSetting, setting, 10)
|
|
1964
|
-
missionCommands.addCommand("+100", menu, updateSplashDamageSetting, setting, 100)
|
|
1965
|
-
|
|
1966
|
-
missionCommands.addCommand("-0.1", menu, updateSplashDamageSetting, setting, -0.1)
|
|
1967
|
-
missionCommands.addCommand("-1", menu, updateSplashDamageSetting, setting, -1)
|
|
1968
|
-
missionCommands.addCommand("-10", menu, updateSplashDamageSetting, setting, -10)
|
|
1969
|
-
missionCommands.addCommand("-100", menu, updateSplashDamageSetting, setting, -100)
|
|
1970
|
-
end
|
|
1971
|
-
|
|
1972
|
-
function exitSplashDamageMenu()
|
|
1973
|
-
if splash_damage_menu then
|
|
1974
|
-
missionCommands.removeItem(splash_damage_menu)
|
|
1975
|
-
splash_damage_menu = nil
|
|
1976
|
-
end
|
|
1977
|
-
end
|
|
1978
|
-
|
|
1979
|
-
function addSplashDamageMenu()
|
|
1980
|
-
if not splash_damage_options.enable_radio_menu then return end
|
|
1981
|
-
|
|
1982
|
-
if splash_damage_menu then
|
|
1983
|
-
missionCommands.removeItem(splash_damage_menu)
|
|
1984
|
-
end
|
|
1985
|
-
|
|
1986
|
-
splash_damage_menu = missionCommands.addSubMenu("Splash Damage Settings")
|
|
1987
|
-
|
|
1988
|
-
--Page 1: Debug & General Settings
|
|
1989
|
-
local debugGeneralMenu = missionCommands.addSubMenu("Debug & General Settings", splash_damage_menu)
|
|
1990
|
-
missionCommands.addCommand("Toggle Game Messages", debugGeneralMenu, toggleSplashDamageSetting, "game_messages")
|
|
1991
|
-
missionCommands.addCommand("Toggle Debug Messages", debugGeneralMenu, toggleSplashDamageSetting, "debug")
|
|
1992
|
-
missionCommands.addCommand("Toggle Weapon Missing Messages", debugGeneralMenu, toggleSplashDamageSetting, "weapon_missing_message")
|
|
1993
|
-
missionCommands.addCommand("Toggle Pre-Explosion Debug", debugGeneralMenu, toggleSplashDamageSetting, "track_pre_explosion_debug")
|
|
1994
|
-
missionCommands.addCommand("Toggle Damage Model", debugGeneralMenu, toggleSplashDamageSetting, "damage_model")
|
|
1995
|
-
missionCommands.addCommand("Toggle Blast Stun", debugGeneralMenu, toggleSplashDamageSetting, "blast_stun")
|
|
1996
|
-
local unitDisabledMenu = missionCommands.addSubMenu("Unit Disabled Health", debugGeneralMenu)
|
|
1997
|
-
addValueAdjustmentCommands(unitDisabledMenu, "unit_disabled_health")
|
|
1998
|
-
local unitCantFireMenu = missionCommands.addSubMenu("Unit Cant Fire Health", debugGeneralMenu)
|
|
1999
|
-
addValueAdjustmentCommands(unitCantFireMenu, "unit_cant_fire_health")
|
|
2000
|
-
local infantryCantFireMenu = missionCommands.addSubMenu("Infantry Cant Fire Health", debugGeneralMenu)
|
|
2001
|
-
addValueAdjustmentCommands(infantryCantFireMenu, "infantry_cant_fire_health")
|
|
2002
|
-
local rocketMultiplierMenu = missionCommands.addSubMenu("Rocket Multiplier", debugGeneralMenu)
|
|
2003
|
-
addValueAdjustmentCommands(rocketMultiplierMenu, "rocket_multiplier")
|
|
2004
|
-
--Page 2/3: Explosions
|
|
2005
|
-
local explosionCargoMenu = missionCommands.addSubMenu("Explosion Settings", splash_damage_menu)
|
|
2006
|
-
local staticDamageMenu = missionCommands.addSubMenu("Static Damage Boost", explosionCargoMenu)
|
|
2007
|
-
addValueAdjustmentCommands(staticDamageMenu, "static_damage_boost")
|
|
2008
|
-
missionCommands.addCommand("Toggle Wave Explosions", explosionCargoMenu, toggleSplashDamageSetting, "wave_explosions")
|
|
2009
|
-
missionCommands.addCommand("Toggle Larger Explosions", explosionCargoMenu, toggleSplashDamageSetting, "larger_explosions")
|
|
2010
|
-
local blastRadiusMenu = missionCommands.addSubMenu("Blast Search Radius", explosionCargoMenu)
|
|
2011
|
-
addValueAdjustmentCommands(blastRadiusMenu, "blast_search_radius")
|
|
2012
|
-
|
|
2013
|
-
local overallScalingMenu = missionCommands.addSubMenu("Overall Scaling", explosionCargoMenu)
|
|
2014
|
-
addValueAdjustmentCommands(overallScalingMenu, "overall_scaling")
|
|
2015
|
-
missionCommands.addCommand("Toggle Shaped Charge Effects", explosionCargoMenu, toggleSplashDamageSetting, "apply_shaped_charge_effects")
|
|
2016
|
-
local shapedChargeMenu = missionCommands.addSubMenu("Shaped Charge Multiplier", explosionCargoMenu)
|
|
2017
|
-
addValueAdjustmentCommands(shapedChargeMenu, "shaped_charge_multiplier")
|
|
2018
|
-
missionCommands.addCommand("Toggle Dynamic Blast Radius", explosionCargoMenu, toggleSplashDamageSetting, "use_dynamic_blast_radius")
|
|
2019
|
-
local dynamicBlastMenu = missionCommands.addSubMenu("Dynamic Blast Radius Modifier", explosionCargoMenu)
|
|
2020
|
-
addValueAdjustmentCommands(dynamicBlastMenu, "dynamic_blast_radius_modifier")
|
|
2021
|
-
|
|
2022
|
-
local explosionCargoMenu = missionCommands.addSubMenu("Cascade Settings", splash_damage_menu)
|
|
2023
|
-
local cascadeScalingMenu = missionCommands.addSubMenu("Cascade Scaling", explosionCargoMenu)
|
|
2024
|
-
addValueAdjustmentCommands(cascadeScalingMenu, "cascade_scaling")
|
|
2025
|
-
local cascadeExplodeThresholdMenu = missionCommands.addSubMenu("Cascade Explode Threshold", explosionCargoMenu)
|
|
2026
|
-
addValueAdjustmentCommands(cascadeExplodeThresholdMenu, "cascade_explode_threshold")
|
|
2027
|
-
local cascadeThresholdMenu = missionCommands.addSubMenu("Cascade Damage Threshold", explosionCargoMenu)
|
|
2028
|
-
addValueAdjustmentCommands(cascadeThresholdMenu, "cascade_damage_threshold")
|
|
2029
|
-
|
|
2030
|
-
--Page 4: Cargo and Ordnance Protection
|
|
2031
|
-
local explosionCargoMenu = missionCommands.addSubMenu("Cargo and Ordnance", splash_damage_menu)
|
|
2032
|
-
missionCommands.addCommand("Toggle Always Cascade Explode", explosionCargoMenu, toggleSplashDamageSetting, "always_cascade_explode")
|
|
2033
|
-
missionCommands.addCommand("Toggle Tracking & Cargo Effects", explosionCargoMenu, toggleSplashDamageSetting, "track_pre_explosion")
|
|
2034
|
-
local cargoThresholdMenu = missionCommands.addSubMenu("Cargo Damage Threshold", explosionCargoMenu)
|
|
2035
|
-
addValueAdjustmentCommands(cargoThresholdMenu, "cargo_damage_threshold")
|
|
2036
|
-
missionCommands.addCommand("Toggle Ordnance Protection", explosionCargoMenu, toggleSplashDamageSetting, "ordnance_protection")
|
|
2037
|
-
local ordnanceRadiusMenu = missionCommands.addSubMenu("Ordnance Protection Radius", explosionCargoMenu)
|
|
2038
|
-
addValueAdjustmentCommands(ordnanceRadiusMenu, "ordnance_protection_radius")
|
|
2039
|
-
missionCommands.addCommand("Toggle Snap To Ground If Destroyed By LE", explosionCargoMenu, toggleSplashDamageSetting, "snap_to_ground_if_destroyed_by_large_explosion")
|
|
2040
|
-
local ordnanceRadiusMenu = missionCommands.addSubMenu("Ordnance Protection Radius", explosionCargoMenu)
|
|
2041
|
-
local cargoThresholdMenu = missionCommands.addSubMenu("Max Snap Height", explosionCargoMenu)
|
|
2042
|
-
addValueAdjustmentCommands(cargoThresholdMenu, "max_snapped_height")
|
|
2043
|
-
missionCommands.addCommand("Toggle Recent Expl Track Snap", explosionCargoMenu, toggleSplashDamageSetting, "recent_large_explosion_snap")
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
--Page 5: Debris Settings
|
|
2047
|
-
local debrisMenu = missionCommands.addSubMenu("Debris Settings", splash_damage_menu)
|
|
2048
|
-
missionCommands.addCommand("Toggle Debris Effects", debrisMenu, toggleSplashDamageSetting, "debris_effects")
|
|
2049
|
-
local debrisCountMinMenu = missionCommands.addSubMenu("Min Debris Count", debrisMenu)
|
|
2050
|
-
addValueAdjustmentCommands(debrisCountMinMenu, "debris_count_min")
|
|
2051
|
-
local debrisCountMaxMenu = missionCommands.addSubMenu("Max Debris Count", debrisMenu)
|
|
2052
|
-
addValueAdjustmentCommands(debrisCountMaxMenu, "debris_count_max")
|
|
2053
|
-
local debrisDistanceMenu = missionCommands.addSubMenu("Max Debris Distance", debrisMenu)
|
|
2054
|
-
addValueAdjustmentCommands(debrisDistanceMenu, "debris_max_distance")
|
|
2055
|
-
local debrisPowerMenu = missionCommands.addSubMenu("Debris Power", debrisMenu)
|
|
2056
|
-
addValueAdjustmentCommands(debrisPowerMenu, "debris_power")
|
|
2057
|
-
|
|
2058
|
-
--Page 6: Cluster Settings
|
|
2059
|
-
local clusterMenu = missionCommands.addSubMenu("Cluster Settings", splash_damage_menu)
|
|
2060
|
-
missionCommands.addCommand("Toggle Cluster Enabled", clusterMenu, toggleSplashDamageSetting, "cluster_enabled")
|
|
2061
|
-
local clusterBaseLengthMenu = missionCommands.addSubMenu("Cluster Base Length", clusterMenu)
|
|
2062
|
-
addValueAdjustmentCommands(clusterBaseLengthMenu, "cluster_base_length")
|
|
2063
|
-
local clusterBaseWidthMenu = missionCommands.addSubMenu("Cluster Base Width", clusterMenu)
|
|
2064
|
-
addValueAdjustmentCommands(clusterBaseWidthMenu, "cluster_base_width")
|
|
2065
|
-
local clusterMaxLengthMenu = missionCommands.addSubMenu("Cluster Max Length", clusterMenu)
|
|
2066
|
-
addValueAdjustmentCommands(clusterMaxLengthMenu, "cluster_max_length")
|
|
2067
|
-
local clusterMaxWidthMenu = missionCommands.addSubMenu("Cluster Max Width", clusterMenu)
|
|
2068
|
-
addValueAdjustmentCommands(clusterMaxWidthMenu, "cluster_max_width")
|
|
2069
|
-
local clusterMinLengthMenu = missionCommands.addSubMenu("Cluster Min Length", clusterMenu)
|
|
2070
|
-
addValueAdjustmentCommands(clusterMinLengthMenu, "cluster_min_length")
|
|
2071
|
-
local clusterMinWidthMenu = missionCommands.addSubMenu("Cluster Min Width", clusterMenu)
|
|
2072
|
-
addValueAdjustmentCommands(clusterMinWidthMenu, "cluster_min_width")
|
|
2073
|
-
missionCommands.addCommand("Toggle Bomblet Reduction Modifier", clusterMenu, toggleSplashDamageSetting, "cluster_bomblet_reductionmodifier")
|
|
2074
|
-
local clusterBombletDamageMenu = missionCommands.addSubMenu("Bomblet Damage Modifier", clusterMenu)
|
|
2075
|
-
addValueAdjustmentCommands(clusterBombletDamageMenu, "cluster_bomblet_damage_modifier")
|
|
2076
|
-
|
|
2077
|
-
--Page 7: Giant Explosion Settings
|
|
2078
|
-
local giantExplosionMenu = missionCommands.addSubMenu("Giant Explosion Settings", splash_damage_menu)
|
|
2079
|
-
missionCommands.addCommand("Toggle Giant Explosion", giantExplosionMenu, toggleSplashDamageSetting, "giant_explosion_enabled")
|
|
2080
|
-
missionCommands.addCommand("Toggle Static Target", giantExplosionMenu, toggleSplashDamageSetting, "giant_explosion_target_static")
|
|
2081
|
-
for name, target in pairs(giantExplosionTargets) do
|
|
2082
|
-
local displayName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
2083
|
-
missionCommands.addCommand("Detonate " .. displayName, giantExplosionMenu, function()
|
|
2084
|
-
trigger.action.setUserFlag(displayName, 1)
|
|
2085
|
-
end)
|
|
2086
|
-
end
|
|
2087
|
-
missionCommands.addCommand("Detonate All Giant Targets", giantExplosionMenu, function()
|
|
2088
|
-
for name, target in pairs(giantExplosionTargets) do
|
|
2089
|
-
local flagName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
2090
|
-
trigger.action.setUserFlag(flagName, 1)
|
|
2091
|
-
end
|
|
2092
|
-
end)
|
|
2093
|
-
local powerMenu = missionCommands.addSubMenu("Explosion Power", giantExplosionMenu)
|
|
2094
|
-
addValueAdjustmentCommands(powerMenu, "giant_explosion_power")
|
|
2095
|
-
local scaleMenu = missionCommands.addSubMenu("Size Scale", giantExplosionMenu)
|
|
2096
|
-
missionCommands.addCommand("+0.1", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", 0.1)
|
|
2097
|
-
missionCommands.addCommand("+0.5", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", 0.5)
|
|
2098
|
-
missionCommands.addCommand("-0.1", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", -0.1)
|
|
2099
|
-
missionCommands.addCommand("-0.5", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", -0.5)
|
|
2100
|
-
local durationMenu = missionCommands.addSubMenu("Duration", giantExplosionMenu)
|
|
2101
|
-
missionCommands.addCommand("+0.25s", durationMenu, updateSplashDamageSetting, "giant_explosion_duration", 0.25)
|
|
2102
|
-
missionCommands.addCommand("-0.25s", durationMenu, updateSplashDamageSetting, "giant_explosion_duration", -0.25)
|
|
2103
|
-
local countMenu = missionCommands.addSubMenu("Explosion Count", giantExplosionMenu)
|
|
2104
|
-
addValueAdjustmentCommands(countMenu, "giant_explosion_count")
|
|
2105
|
-
|
|
2106
|
-
end
|
|
2107
|
-
|
|
2108
|
-
if (script_enable == 1) then
|
|
2109
|
-
gameMsg("SPLASH DAMAGE 3.1 SCRIPT RUNNING")
|
|
2110
|
-
env.info("SPLASH DAMAGE 3.1 SCRIPT RUNNING")
|
|
2111
|
-
|
|
2112
|
-
timer.scheduleFunction(function()
|
|
2113
|
-
protectedCall(track_wpns)
|
|
2114
|
-
return timer.getTime() + refreshRate
|
|
2115
|
-
end, {}, timer.getTime() + refreshRate)
|
|
2116
|
-
|
|
2117
|
-
if splash_damage_options.giant_explosion_enabled then
|
|
2118
|
-
giantExplosionTargets = {} -- Ensure it’s fresh
|
|
2119
|
-
local targetCount = 0
|
|
2120
|
-
for coa = 0, 2 do
|
|
2121
|
-
local groups = coalition.getGroups(coa)
|
|
2122
|
-
if groups then
|
|
2123
|
-
for _, group in pairs(groups) do
|
|
2124
|
-
local units = group:getUnits()
|
|
2125
|
-
if units then
|
|
2126
|
-
for _, unit in pairs(units) do
|
|
2127
|
-
local name = unit:getName()
|
|
2128
|
-
if name:find("GiantExplosionTarget") then
|
|
2129
|
-
local pos = unit:getPosition().p
|
|
2130
|
-
giantExplosionTargets[name] = {obj = unit, pos = pos}
|
|
2131
|
-
if splash_damage_options.giant_explosion_target_static then
|
|
2132
|
-
giantExplosionTargets[name].pos = pos
|
|
2133
|
-
end
|
|
2134
|
-
debugMsg("Found GiantExplosionTarget: " .. name .. " at X:" .. pos.x .. " Y:" .. pos.y .. " Z:" .. pos.z)
|
|
2135
|
-
targetCount = targetCount + 1
|
|
2136
|
-
end
|
|
2137
|
-
end
|
|
2138
|
-
end
|
|
2139
|
-
end
|
|
2140
|
-
end
|
|
2141
|
-
end
|
|
2142
|
-
debugMsg("Total GiantExplosionTargets found: " .. targetCount)
|
|
2143
|
-
timer.scheduleFunction(checkGiantExplosionFlag, {}, timer.getTime() + splash_damage_options.giant_explosion_poll_rate)
|
|
2144
|
-
if not splash_damage_options.giant_explosion_target_static then
|
|
2145
|
-
timer.scheduleFunction(updateTargetPosition, {}, timer.getTime() + 1.0)
|
|
2146
|
-
end
|
|
2147
|
-
end
|
|
2148
|
-
|
|
2149
|
-
world.addEventHandler(WpnHandler)
|
|
2150
|
-
addSplashDamageMenu()
|
|
2151
|
-
end
|
|
1
|
+
--[[
|
|
2
|
+
04 April 2025 (Stevey666) - 3.1
|
|
3
|
+
- Set default cluster munitions option to false, set this to true in the options if you want it
|
|
4
|
+
- Added missing radio commands for Cascade Scaling
|
|
5
|
+
- Adjust default cascading to 2 (from 1)
|
|
6
|
+
- Adjusted Ural-4320 to be a tanker and ammo carrier for cargo cookoff
|
|
7
|
+
- Prevent weapons not in the list from being tracked
|
|
8
|
+
- Moved some logging behind the debug mode flag
|
|
9
|
+
- Ordnance Protection, added a max height ordnance protection will snap explosion to ground
|
|
10
|
+
- Ordnance Protection, fixed enable/disable option
|
|
11
|
+
- Added Giant Explosion feature
|
|
12
|
+
- Adjusted some hydra70 values on recom. from ETBSmorgan
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
09 March 2025 (Stevey666) - 3.0
|
|
16
|
+
- Added ordinance protection gives a few options - stop the additional larger_explosion that tends to blow up your own bombs if theyre dropped at the same place if its within x m
|
|
17
|
+
- Additional ordnance protection option that will cause a snap to ground larger_explosion if its within x meters of a recent larger explosion and within x seconds (can set in options)
|
|
18
|
+
- Added vehicle scanning around a weapon to allow for..
|
|
19
|
+
- Cook offs - you can set vehicles that will cook off i.e ammo trucks, number of explosions, debris explosions, power adjustable
|
|
20
|
+
- Fuel/Tanker explosion and flames - when a fuel tanker blows it will through up a big flame - adjustable in the scripts
|
|
21
|
+
- Added section for vehicles for the above
|
|
22
|
+
- Added radio commands for everything
|
|
23
|
+
- Added in cluster munitions changes (note: barely tested, its not particularly accurate or that useful at this point so leaving disabled)
|
|
24
|
+
- Potential bug - testing, stacking too many units together may cause a MIST error if you're using mist
|
|
25
|
+
|
|
26
|
+
- Setting this as 3.0 as I'd like to be responsive to requests, updates etc - creating a new fork to track this
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
10 Feb 2025 (Stevey666) - 2.0.7
|
|
30
|
+
- Fixed AGM 154/Adjusted weapons
|
|
31
|
+
- Added overall damage scaling
|
|
32
|
+
- Added modifier for shaped charges (i.e. Mavericks), adjusted weapon list accordingly
|
|
33
|
+
- Adjusted blast radius and damage calculations, created option for dynamic blast radius
|
|
34
|
+
- Adjusted cascading explosions, added additional "cascade_scaling" modifier and cascade explode threshold modifier. Units wont explode on initial impact unless health drops under threshold
|
|
35
|
+
- Added always_cascade_explode option so you can set it to the old ways of making everything in the blast wave go kaboom
|
|
36
|
+
- Added in game radio commands to change the new options ingame without having to reload everything in mission editor to test it out
|
|
37
|
+
|
|
38
|
+
12 November 2024 (by JGi | Quéton 1-1)
|
|
39
|
+
- Tweak down radius 100>90 (Thanks Arhibeau)
|
|
40
|
+
- Tweak down some values
|
|
41
|
+
|
|
42
|
+
20 January 2024 (by JGi | Quéton 1-1)
|
|
43
|
+
- Added missing weapons to explTable
|
|
44
|
+
- Sort weapons in explTable by type
|
|
45
|
+
- Added aircraft type in log when missing
|
|
46
|
+
|
|
47
|
+
03 May 2023 (KERV)
|
|
48
|
+
Correction AGM 154 (https://forum.dcs.world/topic/289290-splash-damage-20-script-make-explosions-better/page/5/#comment-5207760)
|
|
49
|
+
|
|
50
|
+
06 March 2023 (Kerv)
|
|
51
|
+
- Add some data for new ammunition
|
|
52
|
+
|
|
53
|
+
16 April 2022
|
|
54
|
+
spencershepard (GRIMM):
|
|
55
|
+
- Added new/missing weapons to explTable
|
|
56
|
+
- Added new option rocket_multiplier
|
|
57
|
+
|
|
58
|
+
31 December 2021
|
|
59
|
+
spencershepard (GRIMM):
|
|
60
|
+
- Added many new weapons
|
|
61
|
+
- Added filter for weapons.shells events
|
|
62
|
+
- Fixed mission weapon message option
|
|
63
|
+
- Changed default for damage_model option
|
|
64
|
+
|
|
65
|
+
21 December 2021
|
|
66
|
+
spencershepard (GRIMM):
|
|
67
|
+
SPLASH DAMAGE 2.0:
|
|
68
|
+
- Added blast wave effect to add timed and scaled secondary explosions on top of game objects
|
|
69
|
+
- Object geometry within blast wave changes damage intensity
|
|
70
|
+
- Damage boost for structures since they are hard to kill, even if very close to large explosions
|
|
71
|
+
- Increased some rocket values in explTable
|
|
72
|
+
- Missing weapons from explTable will display message to user and log to DCS.log so that we can add what's missing
|
|
73
|
+
- Damage model for ground units that will disable their weapons and ability to move with partial damage before they are killed
|
|
74
|
+
- Added options table to allow easy adjustments before release
|
|
75
|
+
- General refactoring and restructure
|
|
76
|
+
|
|
77
|
+
28 October 2020
|
|
78
|
+
FrozenDroid:
|
|
79
|
+
- Uncommented error logging, actually made it an error log which shows a message box on error.
|
|
80
|
+
- Fixed the too restrictive weapon filter (took out the HE warhead requirement)
|
|
81
|
+
|
|
82
|
+
2 October 2020
|
|
83
|
+
FrozenDroid:
|
|
84
|
+
- Added error handling to all event handler and scheduled functions. Lua script errors can no longer bring the server down.
|
|
85
|
+
- Added some extra checks to which weapons to handle, make sure they actually have a warhead (how come S-8KOM's don't have a warhead field...?)
|
|
86
|
+
--]]
|
|
87
|
+
|
|
88
|
+
----[[ ##### SCRIPT CONFIGURATION ##### ]]----
|
|
89
|
+
splash_damage_options = {
|
|
90
|
+
--debug options
|
|
91
|
+
["game_messages"] = false, --enable some messages on screen
|
|
92
|
+
["debug"] = false, --enable debugging messages
|
|
93
|
+
["weapon_missing_message"] = false, --false disables messages alerting you to weapons missing from the explTable
|
|
94
|
+
["track_pre_explosion_debug"] = false, --Toggle to enable/disable pre-explosion tracking debugging
|
|
95
|
+
|
|
96
|
+
["enable_radio_menu"] = true, --enables the in-game radio menu for modifying settings
|
|
97
|
+
|
|
98
|
+
["static_damage_boost"] = 2000, --apply extra damage to Unit.Category.STRUCTUREs with wave explosions
|
|
99
|
+
["wave_explosions"] = true, --secondary explosions on top of game objects, radiating outward from the impact point and scaled based on size of object and distance from weapon impact point
|
|
100
|
+
["larger_explosions"] = true, --secondary explosions on top of weapon impact points, dictated by the values in the explTable
|
|
101
|
+
["damage_model"] = true, --allow blast wave to affect ground unit movement and weapons
|
|
102
|
+
["blast_search_radius"] = 90, --this is the max size of any blast wave radius, since we will only find objects within this zone
|
|
103
|
+
["cascade_damage_threshold"] = 0.1, --if the calculated blast damage doesn't exceed this value, there will be no secondary explosion damage on the unit. If this value is too small, the appearance of explosions far outside of an expected radius looks incorrect.
|
|
104
|
+
["blast_stun"] = false, --not implemented
|
|
105
|
+
["unit_disabled_health"] = 30, --if health is below this value after our explosions, disable its movement
|
|
106
|
+
["unit_cant_fire_health"] = 40, --if health is below this value after our explosions, set ROE to HOLD to simulate damage weapon systems
|
|
107
|
+
["infantry_cant_fire_health"] = 60, --if health is below this value after our explosions, set ROE to HOLD to simulate severe injury
|
|
108
|
+
|
|
109
|
+
["rocket_multiplier"] = 1.3, --multiplied by the explTable value for rockets
|
|
110
|
+
["overall_scaling"] = 1, --overall scaling for explosive power
|
|
111
|
+
|
|
112
|
+
["apply_shaped_charge_effects"] = true, --apply reduction in blastwave etc for shaped charge munitions
|
|
113
|
+
["shaped_charge_multiplier"] = 0.2, --multiplier that reduces blast radius and explosion power for shaped charge munitions.
|
|
114
|
+
|
|
115
|
+
["use_dynamic_blast_radius"] = true, --if true, blast radius is calculated from explosion power; if false, blast_search_radius (90) is used
|
|
116
|
+
["dynamic_blast_radius_modifier"] = 2, --multiplier for the blast radius
|
|
117
|
+
|
|
118
|
+
["cascade_scaling"] = 2, --multiplier for secondary (cascade) blast damage, 1 damage fades out too soon, 2 or 3 damage seems a good balance
|
|
119
|
+
["cascade_explode_threshold"] = 60, --only trigger cascade explosion if the unit's current health is <= this percent of its maximum, setting can help blow nearby jeeps but not tanks
|
|
120
|
+
["always_cascade_explode"] = false, --switch if you want everything to explode like with the original script
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
--track_pre_explosion/enable_cargo_effects should both be the same value
|
|
124
|
+
["track_pre_explosion"] = true, --Toggle to enable/disable pre-explosion tracking
|
|
125
|
+
["enable_cargo_effects"] = true, --Toggle for enabling/disabling cargo explosions and cook-offs
|
|
126
|
+
["cargo_damage_threshold"] = 60, --Health % below which cargo explodes (0 = destroyed only)
|
|
127
|
+
["debris_effects"] = true, --Enable debris from cargo cook-offs
|
|
128
|
+
["debris_power"] = 1, --Power of each debris explosion
|
|
129
|
+
["debris_count_min"] = 6, --Minimum debris pieces per cook-off
|
|
130
|
+
["debris_count_max"] = 12, --Maximum debris pieces per cook-off
|
|
131
|
+
["debris_max_distance"] = 10, --Max distance debris can travel (meters), the min distance from the vehicle will be 10% of this
|
|
132
|
+
|
|
133
|
+
["ordnance_protection"] = true, --Toggle ordinance protection features
|
|
134
|
+
["ordnance_protection_radius"] = 10, --Distance in meters to protect nearby bombs
|
|
135
|
+
["detect_ordnance_destruction"] = true, --Toggle detection of ordnance destroyed by large explosions
|
|
136
|
+
["snap_to_ground_if_destroyed_by_large_explosion"] = true, --If the ordnance protection fails or is disabled we can snap larger_explosions to the ground (if enabled - power as set in weapon list) - so an explosion still does hit the ground
|
|
137
|
+
["max_snapped_height"] = 80, --max height it will snap to ground from
|
|
138
|
+
["recent_large_explosion_snap"] = true, --enable looking for a recent large_explosion generated by the script
|
|
139
|
+
["recent_large_explosion_range"] = 100, --range its looking for in meters for a recent large_explosion generated by the script
|
|
140
|
+
["recent_large_explosion_time"] = 4, --in seconds how long ago there was a recent large_explosion generated by the script
|
|
141
|
+
|
|
142
|
+
--Cluster bomb settings
|
|
143
|
+
["cluster_enabled"] = false,
|
|
144
|
+
["cluster_base_length"] = 150, --Base forward spread (meters)
|
|
145
|
+
["cluster_base_width"] = 200, --Base lateral spread (meters)
|
|
146
|
+
["cluster_max_length"] = 300, --Max forward spread (meters)
|
|
147
|
+
["cluster_max_width"] = 400, --Max lateral spread (meters)
|
|
148
|
+
["cluster_min_length"] = 100, --Min forward spread
|
|
149
|
+
["cluster_min_width"] = 150, --Min lateral spread
|
|
150
|
+
["cluster_bomblet_reductionmodifier"] = true, --Use equation to reduce number of bomblets (to make it look better)
|
|
151
|
+
["cluster_bomblet_damage_modifier"] = 1, --Adjustable global modifier for bomblet explosive power
|
|
152
|
+
|
|
153
|
+
--Giant Explosion Options - Remember, any target you want to blow up needs to be named "GiantExplosionTarget(X)" (X) being any value/name etc
|
|
154
|
+
["giant_explosion_enabled"] = true, --Toggle to enable/disable Giant Explosion
|
|
155
|
+
["giant_explosion_power"] = 6000, --Power in kg of TNT (default 8 tons)
|
|
156
|
+
["giant_explosion_scale"] = 1, --Size scale factor (default 1)
|
|
157
|
+
["giant_explosion_duration"] = 3.0, --Total duration in seconds (default 3s)
|
|
158
|
+
["giant_explosion_count"] = 250, --Number of explosions (default 300)
|
|
159
|
+
["giant_explosion_target_static"] = true, --Toggle to true for static targets (store position once), false for dynamic (update every second)
|
|
160
|
+
["giant_explosion_poll_rate"] = 1, --Polling rate in seconds for flag checks (default 1s)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
local script_enable = 1
|
|
164
|
+
refreshRate = 0.1
|
|
165
|
+
----[[ ##### End of SCRIPT CONFIGURATION ##### ]]----
|
|
166
|
+
|
|
167
|
+
--Helper function: Trim whitespace.
|
|
168
|
+
local function trim(s)
|
|
169
|
+
return s:match("^%s*(.-)%s*$")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
cargoUnits = {
|
|
173
|
+
|
|
174
|
+
--[[
|
|
175
|
+
flamesize:
|
|
176
|
+
|
|
177
|
+
1 = small smoke and fire
|
|
178
|
+
2 = medium smoke and fire
|
|
179
|
+
3 = large smoke and fire
|
|
180
|
+
4 = huge smoke and fire
|
|
181
|
+
5 = small smoke
|
|
182
|
+
6 = medium smoke
|
|
183
|
+
7 = large smoke
|
|
184
|
+
8 = huge smoke
|
|
185
|
+
]]--
|
|
186
|
+
|
|
187
|
+
--1) M92 R11 Volvo driveable (Fuel Truck Tanker)
|
|
188
|
+
["r11_volvo_drivable"] = {
|
|
189
|
+
cargoExplosion = true,
|
|
190
|
+
cargoExplosionMult = 2.0,
|
|
191
|
+
cargoExplosionPower = 200,
|
|
192
|
+
cargoCookOff = false,
|
|
193
|
+
cookOffCount = 0,
|
|
194
|
+
cookOffPower = 0,
|
|
195
|
+
cookOffDuration = 0,
|
|
196
|
+
cookOffRandomTiming = false,
|
|
197
|
+
cookOffPowerRandom = 50,
|
|
198
|
+
isTanker = true,
|
|
199
|
+
flameSize = 3,
|
|
200
|
+
flameDuration = 5,
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
--2) Refueler ATMZ-5
|
|
204
|
+
["ATMZ-5"] = {
|
|
205
|
+
cargoExplosion = true,
|
|
206
|
+
cargoExplosionMult = 2.0,
|
|
207
|
+
cargoExplosionPower = 200,
|
|
208
|
+
cargoCookOff = false,
|
|
209
|
+
cookOffCount = 0,
|
|
210
|
+
cookOffPower = 0,
|
|
211
|
+
cookOffDuration = 0,
|
|
212
|
+
cookOffRandomTiming = false,
|
|
213
|
+
cookOffPowerRandom = 50,
|
|
214
|
+
isTanker = true,
|
|
215
|
+
flameSize = 3,
|
|
216
|
+
flameDuration = 5,
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
--3) Refueler ATZ-10
|
|
220
|
+
["ATZ-10"] = {
|
|
221
|
+
cargoExplosion = true,
|
|
222
|
+
cargoExplosionMult = 2,
|
|
223
|
+
cargoExplosionPower = 200,
|
|
224
|
+
cargoCookOff = false,
|
|
225
|
+
cookOffCount = 0,
|
|
226
|
+
cookOffPower = 0,
|
|
227
|
+
cookOffDuration = 0,
|
|
228
|
+
cookOffRandomTiming = false,
|
|
229
|
+
cookOffPowerRandom = 50,
|
|
230
|
+
isTanker = true,
|
|
231
|
+
flameSize = 3,
|
|
232
|
+
flameDuration = 5,
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
--4) Refueler ATZ-5
|
|
236
|
+
["ATZ-5"] = {
|
|
237
|
+
cargoExplosion = true,
|
|
238
|
+
cargoExplosionMult = 1.8,
|
|
239
|
+
cargoExplosionPower = 200,
|
|
240
|
+
cargoCookOff = false,
|
|
241
|
+
cookOffCount = 0,
|
|
242
|
+
cookOffPower = 0,
|
|
243
|
+
cookOffDuration = 0,
|
|
244
|
+
cookOffRandomTiming = false,
|
|
245
|
+
cookOffPowerRandom = 50,
|
|
246
|
+
isTanker = true,
|
|
247
|
+
flameSize = 3,
|
|
248
|
+
flameDuration = 5,
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
--5) Refueler M978 HEMTT (Fuel truck tanker)
|
|
252
|
+
["M978 HEMTT Tanker"] = {
|
|
253
|
+
cargoExplosion = true,
|
|
254
|
+
cargoExplosionMult = 2.0,
|
|
255
|
+
cargoExplosionPower = 200,
|
|
256
|
+
cargoCookOff = false,
|
|
257
|
+
cookOffCount = 0,
|
|
258
|
+
cookOffPower = 0,
|
|
259
|
+
cookOffDuration = 0,
|
|
260
|
+
cookOffRandomTiming = false,
|
|
261
|
+
cookOffPowerRandom = 50,
|
|
262
|
+
isTanker = true,
|
|
263
|
+
flameSize = 3,
|
|
264
|
+
flameDuration = 5,
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
--##### AMMO CARRIERS #####
|
|
268
|
+
["GAZ-66"] = {
|
|
269
|
+
cargoExplosion = true,
|
|
270
|
+
cargoExplosionMult = 1,
|
|
271
|
+
cargoExplosionPower = 200,
|
|
272
|
+
cargoCookOff = true,
|
|
273
|
+
cookOffCount = 4,
|
|
274
|
+
cookOffPower = 1,
|
|
275
|
+
cookOffDuration = 20,
|
|
276
|
+
cookOffRandomTiming = true,
|
|
277
|
+
cookOffPowerRandom = 50,
|
|
278
|
+
isTanker = false,
|
|
279
|
+
flameSize = 1,
|
|
280
|
+
flameDuration = 30,
|
|
281
|
+
},
|
|
282
|
+
--#Technically this is both ammo and fuel looking at the model
|
|
283
|
+
["Ural-4320"] = {
|
|
284
|
+
cargoExplosion = true,
|
|
285
|
+
cargoExplosionMult = 1,
|
|
286
|
+
cargoExplosionPower = 200,
|
|
287
|
+
cargoCookOff = true,
|
|
288
|
+
cookOffCount = 4,
|
|
289
|
+
cookOffPower = 1,
|
|
290
|
+
cookOffDuration = 20,
|
|
291
|
+
cookOffRandomTiming = true,
|
|
292
|
+
cookOffPowerRandom = 50,
|
|
293
|
+
isTanker = true,
|
|
294
|
+
flameSize = 1,
|
|
295
|
+
flameDuration = 30,
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
["ZIL-135"] = {
|
|
299
|
+
cargoExplosion = true,
|
|
300
|
+
cargoExplosionMult = 1,
|
|
301
|
+
cargoExplosionPower = 200,
|
|
302
|
+
cargoCookOff = true,
|
|
303
|
+
cookOffCount = 5,
|
|
304
|
+
cookOffPower = 1,
|
|
305
|
+
cookOffDuration = 20,
|
|
306
|
+
cookOffRandomTiming = true,
|
|
307
|
+
cookOffPowerRandom = 50,
|
|
308
|
+
isTanker = false,
|
|
309
|
+
flameSize = 1,
|
|
310
|
+
flameDuration = 30,
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
--Weapon Explosive Table
|
|
315
|
+
explTable = {
|
|
316
|
+
--*** WWII BOMBS ***
|
|
317
|
+
["British_GP_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
318
|
+
["British_GP_250LB_Bomb_Mk4"] = { explosive = 100, shaped_charge = false },
|
|
319
|
+
["British_GP_250LB_Bomb_Mk5"] = { explosive = 100, shaped_charge = false },
|
|
320
|
+
["British_GP_500LB_Bomb_Mk1"] = { explosive = 213, shaped_charge = false },
|
|
321
|
+
["British_GP_500LB_Bomb_Mk4"] = { explosive = 213, shaped_charge = false },
|
|
322
|
+
["British_GP_500LB_Bomb_Mk4_Short"] = { explosive = 213, shaped_charge = false },
|
|
323
|
+
["British_GP_500LB_Bomb_Mk5"] = { explosive = 213, shaped_charge = false },
|
|
324
|
+
["British_MC_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
325
|
+
["British_MC_250LB_Bomb_Mk2"] = { explosive = 100, shaped_charge = false },
|
|
326
|
+
["British_MC_500LB_Bomb_Mk1_Short"] = { explosive = 213, shaped_charge = false },
|
|
327
|
+
["British_MC_500LB_Bomb_Mk2"] = { explosive = 213, shaped_charge = false },
|
|
328
|
+
["British_SAP_250LB_Bomb_Mk5"] = { explosive = 100, shaped_charge = false },
|
|
329
|
+
["British_SAP_500LB_Bomb_Mk5"] = { explosive = 213, shaped_charge = false },
|
|
330
|
+
["British_AP_25LBNo1_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
331
|
+
["British_HE_60LBSAPNo2_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
332
|
+
["British_HE_60LBFNo1_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
333
|
+
|
|
334
|
+
["SC_50"] = { explosive = 20, shaped_charge = false },
|
|
335
|
+
["ER_4_SC50"] = { explosive = 20, shaped_charge = false },
|
|
336
|
+
["SC_250_T1_L2"] = { explosive = 100, shaped_charge = false },
|
|
337
|
+
["SC_501_SC250"] = { explosive = 100, shaped_charge = false },
|
|
338
|
+
["Schloss500XIIC1_SC_250_T3_J"] = { explosive = 100, shaped_charge = false },
|
|
339
|
+
["SC_501_SC500"] = { explosive = 213, shaped_charge = false },
|
|
340
|
+
["SC_500_L2"] = { explosive = 213, shaped_charge = false },
|
|
341
|
+
["SD_250_Stg"] = { explosive = 100, shaped_charge = false },
|
|
342
|
+
["SD_500_A"] = { explosive = 213, shaped_charge = false },
|
|
343
|
+
|
|
344
|
+
--*** WWII CBU ***
|
|
345
|
+
["AB_250_2_SD_2"] = { explosive = 100, shaped_charge = false },
|
|
346
|
+
["AB_250_2_SD_10A"] = { explosive = 100, shaped_charge = false },
|
|
347
|
+
["AB_500_1_SD_10A"] = { explosive = 213, shaped_charge = false },
|
|
348
|
+
|
|
349
|
+
--*** WWII ROCKETS ***
|
|
350
|
+
["3xM8_ROCKETS_IN_TUBES"] = { explosive = 4, shaped_charge = false },
|
|
351
|
+
["WGr21"] = { explosive = 4, shaped_charge = false },
|
|
352
|
+
|
|
353
|
+
--*** UNGUIDED BOMBS (UGB) ***
|
|
354
|
+
["M_117"] = { explosive = 201, shaped_charge = false },
|
|
355
|
+
["AN_M30A1"] = { explosive = 45, shaped_charge = false },
|
|
356
|
+
["AN_M57"] = { explosive = 100, shaped_charge = false },
|
|
357
|
+
["AN_M64"] = { explosive = 121, shaped_charge = false },
|
|
358
|
+
["AN_M65"] = { explosive = 400, shaped_charge = false },
|
|
359
|
+
["AN_M66"] = { explosive = 800, shaped_charge = false },
|
|
360
|
+
["AN-M66A2"] = { explosive = 536, shaped_charge = false },
|
|
361
|
+
["AN-M81"] = { explosive = 100, shaped_charge = false },
|
|
362
|
+
["AN-M88"] = { explosive = 100, shaped_charge = false },
|
|
363
|
+
|
|
364
|
+
["Mk_81"] = { explosive = 60, shaped_charge = false },
|
|
365
|
+
["MK-81SE"] = { explosive = 60, shaped_charge = false },
|
|
366
|
+
["Mk_82"] = { explosive = 100, shaped_charge = false },
|
|
367
|
+
["MK_82AIR"] = { explosive = 100, shaped_charge = false },
|
|
368
|
+
["MK_82SNAKEYE"] = { explosive = 100, shaped_charge = false },
|
|
369
|
+
["Mk_83"] = { explosive = 274, shaped_charge = false },
|
|
370
|
+
["Mk_84"] = { explosive = 582, shaped_charge = false },
|
|
371
|
+
|
|
372
|
+
["HEBOMB"] = { explosive = 40, shaped_charge = false },
|
|
373
|
+
["HEBOMBD"] = { explosive = 40, shaped_charge = false },
|
|
374
|
+
|
|
375
|
+
["SAMP125LD"] = { explosive = 60, shaped_charge = false },
|
|
376
|
+
["SAMP250LD"] = { explosive = 118, shaped_charge = false },
|
|
377
|
+
["SAMP250HD"] = { explosive = 118, shaped_charge = false },
|
|
378
|
+
["SAMP400LD"] = { explosive = 274, shaped_charge = false },
|
|
379
|
+
["SAMP400HD"] = { explosive = 274, shaped_charge = false },
|
|
380
|
+
|
|
381
|
+
["BR_250"] = { explosive = 100, shaped_charge = false },
|
|
382
|
+
["BR_500"] = { explosive = 100, shaped_charge = false },
|
|
383
|
+
|
|
384
|
+
["FAB_100"] = { explosive = 45, shaped_charge = false },
|
|
385
|
+
["FAB_250"] = { explosive = 118, shaped_charge = false },
|
|
386
|
+
["FAB_250M54TU"] = { explosive = 118, shaped_charge = false },
|
|
387
|
+
["FAB-250-M62"] = { explosive = 118, shaped_charge = false },
|
|
388
|
+
["FAB_500"] = { explosive = 213, shaped_charge = false },
|
|
389
|
+
["FAB_1500"] = { explosive = 675, shaped_charge = false },
|
|
390
|
+
|
|
391
|
+
--*** UNGUIDED BOMBS WITH PENETRATOR / ANTI-RUNWAY ***
|
|
392
|
+
["Durandal"] = { explosive = 64, shaped_charge = false },
|
|
393
|
+
["BLU107B_DURANDAL"] = { explosive = 64, shaped_charge = false },
|
|
394
|
+
["BAP_100"] = { explosive = 32, shaped_charge = false },
|
|
395
|
+
["BAP-100"] = { explosive = 32, shaped_charge = false },
|
|
396
|
+
["BAT-120"] = { explosive = 32, shaped_charge = false },
|
|
397
|
+
["TYPE-200A"] = { explosive = 107, shaped_charge = false },
|
|
398
|
+
["BetAB_500"] = { explosive = 98, shaped_charge = false },
|
|
399
|
+
["BetAB_500ShP"] = { explosive = 107, shaped_charge = false },
|
|
400
|
+
|
|
401
|
+
--*** GUIDED BOMBS (GBU) ***
|
|
402
|
+
["GBU_10"] = { explosive = 582, shaped_charge = false },
|
|
403
|
+
["GBU_12"] = { explosive = 100, shaped_charge = false },
|
|
404
|
+
["GBU_16"] = { explosive = 274, shaped_charge = false },
|
|
405
|
+
["GBU_24"] = { explosive = 582, shaped_charge = false },
|
|
406
|
+
["KAB_1500Kr"] = { explosive = 675, shaped_charge = false },
|
|
407
|
+
["KAB_500Kr"] = { explosive = 213, shaped_charge = false },
|
|
408
|
+
["KAB_500"] = { explosive = 213, shaped_charge = false },
|
|
409
|
+
|
|
410
|
+
--*** CLUSTER BOMBS (CBU) ***
|
|
411
|
+
--I don't have most of these so can't test them with debug on
|
|
412
|
+
["CBU_99"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 247, submunition_explosive = 2, submunition_name = "Mk 118" }, --Mk 20 Rockeye variant, confirmed 247 Mk 118 bomblets
|
|
413
|
+
["ROCKEYE"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 247, submunition_explosive = 2, submunition_name = "Mk 118" }, --Mk 20 Rockeye, confirmed 247 Mk 118 bomblets
|
|
414
|
+
["BLU_3B_GROUP"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 19, submunition_explosive = 0.2, submunition_name = "BLU_3B" }, --Not in datamine, possibly custom or outdated; submunition name guessed
|
|
415
|
+
["MK77mod0-WPN"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --Not in datamine, possibly custom; submunition name guessed
|
|
416
|
+
["MK77mod1-WPN"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --Not in datamine, possibly custom; submunition name guessed
|
|
417
|
+
["CBU_87"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 202, submunition_explosive = 0.5, submunition_name = "BLU_97B" }, --Confirmed 202 BLU-97/B bomblets
|
|
418
|
+
["CBU_103"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 202, submunition_explosive = 0.5, submunition_name = "BLU_97B" }, --WCMD variant of CBU-87, confirmed 202 BLU-97/B bomblets
|
|
419
|
+
["CBU_97"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 10, submunition_explosive = 15, submunition_name = "BLU_108" }, --Confirmed 10 BLU-108 submunitions, each with 4 skeets
|
|
420
|
+
["CBU_105"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 10, submunition_explosive = 15, submunition_name = "BLU_108" }, --WCMD variant of CBU-97, confirmed 10 BLU-108 submunitions
|
|
421
|
+
["BELOUGA"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 151, submunition_explosive = 0.3, submunition_name = "grenade_AC" }, --Confirmed 151 grenade_AC bomblets (French BLG-66)
|
|
422
|
+
["BLG66_BELOUGA"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 151, submunition_explosive = 0.3, submunition_name = "grenade_AC" }, --Alias for BELOUGA, confirmed 151 grenade_AC bomblets
|
|
423
|
+
["BL_755"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 147, submunition_explosive = 0.4, submunition_name = "BL_755_bomblet" }, --Confirmed 147 bomblets, submunition name from your table
|
|
424
|
+
["RBK_250"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 60, submunition_explosive = 0.5, submunition_name = "PTAB_25M" }, --Confirmed 60 PTAB-2.5M anti-tank bomblets
|
|
425
|
+
["RBK_250_275_AO_1SCH"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 150, submunition_explosive = 0.2, submunition_name = "AO_1SCh" }, --Confirmed 150 AO-1SCh fragmentation bomblets
|
|
426
|
+
["RBK_500"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 108, submunition_explosive = 0.5, submunition_name = "PTAB_10_5" }, --Confirmed 108 PTAB-10-5 anti-tank bomblets
|
|
427
|
+
["RBK_500U"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 352, submunition_explosive = 0.2, submunition_name = "OAB_25RT" }, --Confirmed 352 OAB-2.5RT fragmentation bomblets
|
|
428
|
+
["RBK_500AO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 108, submunition_explosive = 0.5, submunition_name = "AO_25RT" }, --Confirmed 108 AO-2.5RT fragmentation bomblets
|
|
429
|
+
["RBK_500U_OAB_2_5RT"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 352, submunition_explosive = 0.2, submunition_name = "OAB_25RT" }, --Confirmed 352 OAB-2.5RT fragmentation bomblets
|
|
430
|
+
["RBK_500_255_PTO_1M"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 126, submunition_explosive = 0.5, submunition_name = "PTO_1M" },
|
|
431
|
+
["RBK_500_255_ShO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 565, submunition_explosive = 0.1, submunition_name = "ShO" },
|
|
432
|
+
--*** INS/GPS BOMBS (JDAM) ***
|
|
433
|
+
["GBU_31"] = { explosive = 582, shaped_charge = false },
|
|
434
|
+
["GBU_31_V_3B"] = { explosive = 582, shaped_charge = false },
|
|
435
|
+
["GBU_31_V_2B"] = { explosive = 582, shaped_charge = false },
|
|
436
|
+
["GBU_31_V_4B"] = { explosive = 582, shaped_charge = false },
|
|
437
|
+
["GBU_32_V_2B"] = { explosive = 202, shaped_charge = false },
|
|
438
|
+
["GBU_38"] = { explosive = 100, shaped_charge = false },
|
|
439
|
+
["GBU_54_V_1B"] = { explosive = 100, shaped_charge = false },
|
|
440
|
+
|
|
441
|
+
--*** GLIDE BOMBS (JSOW) ***
|
|
442
|
+
["AGM_154A"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 145, submunition_explosive = 2, submunition_name = "BLU-97/B" }, --JSOW-A, confirmed 145 BLU-97 bomblets from datamine
|
|
443
|
+
["AGM_154C"] = { explosive = 305, shaped_charge = false },
|
|
444
|
+
["AGM_154"] = { explosive = 305, shaped_charge = false },
|
|
445
|
+
["BK90_MJ1"] = { explosive = 0, shaped_charge = false },
|
|
446
|
+
["BK90_MJ1_MJ2"] = { explosive = 0, shaped_charge = false },
|
|
447
|
+
["BK90_MJ2"] = { explosive = 0, shaped_charge = false },
|
|
448
|
+
|
|
449
|
+
["LS-6-100"] = { explosive = 45, shaped_charge = false },
|
|
450
|
+
["LS-6-250"] = { explosive = 100, shaped_charge = false },
|
|
451
|
+
["LS-6-500"] = { explosive = 274, shaped_charge = false },
|
|
452
|
+
["GB-6"] = { explosive = 0, shaped_charge = false },
|
|
453
|
+
["GB-6-HE"] = { explosive = 0, shaped_charge = false },
|
|
454
|
+
["GB-6-SFW"] = { explosive = 0, shaped_charge = false },
|
|
455
|
+
|
|
456
|
+
--*** AIR GROUND MISSILE (AGM) ***
|
|
457
|
+
["AGM_62"] = { explosive = 400, shaped_charge = false },
|
|
458
|
+
["AGM_65D"] = { explosive = 38, shaped_charge = true },
|
|
459
|
+
["AGM_65E"] = { explosive = 80, shaped_charge = true },
|
|
460
|
+
["AGM_65F"] = { explosive = 80, shaped_charge = true },
|
|
461
|
+
["AGM_65G"] = { explosive = 80, shaped_charge = true },
|
|
462
|
+
["AGM_65H"] = { explosive = 38, shaped_charge = true },
|
|
463
|
+
["AGM_65K"] = { explosive = 80, shaped_charge = true },
|
|
464
|
+
["AGM_65L"] = { explosive = 80, shaped_charge = true },
|
|
465
|
+
["AGM_123"] = { explosive = 274, shaped_charge = false },
|
|
466
|
+
["AGM_130"] = { explosive = 582, shaped_charge = false },
|
|
467
|
+
["AGM_119"] = { explosive = 176, shaped_charge = false },
|
|
468
|
+
["AGM_114"] = { explosive = 10, shaped_charge = true },
|
|
469
|
+
["AGM_114K"] = { explosive = 10, shaped_charge = true },
|
|
470
|
+
|
|
471
|
+
["Rb 05A"] = { explosive = 217, shaped_charge = false },
|
|
472
|
+
["RB75"] = { explosive = 38, shaped_charge = false },
|
|
473
|
+
["RB75A"] = { explosive = 38, shaped_charge = false },
|
|
474
|
+
["RB75B"] = { explosive = 38, shaped_charge = false },
|
|
475
|
+
["RB75T"] = { explosive = 80, shaped_charge = false },
|
|
476
|
+
["HOT3_MBDA"] = { explosive = 15, shaped_charge = false },
|
|
477
|
+
["C-701T"] = { explosive = 38, shaped_charge = false },
|
|
478
|
+
["C-701IR"] = { explosive = 38, shaped_charge = false },
|
|
479
|
+
|
|
480
|
+
["Vikhr_M"] = { explosive = 11, shaped_charge = false },
|
|
481
|
+
["Vikhr_9M127_1"] = { explosive = 11, shaped_charge = false },
|
|
482
|
+
["AT_6"] = { explosive = 11, shaped_charge = false },
|
|
483
|
+
["Ataka_9M120"] = { explosive = 11, shaped_charge = false },
|
|
484
|
+
["Ataka_9M120F"] = { explosive = 11, shaped_charge = false },
|
|
485
|
+
["P_9M117"] = { explosive = 0, shaped_charge = false },
|
|
486
|
+
|
|
487
|
+
["KH-66_Grom"] = { explosive = 108, shaped_charge = false },
|
|
488
|
+
["X_23"] = { explosive = 111, shaped_charge = false },
|
|
489
|
+
["X_23L"] = { explosive = 111, shaped_charge = false },
|
|
490
|
+
["X_28"] = { explosive = 160, shaped_charge = false },
|
|
491
|
+
["X_25ML"] = { explosive = 89, shaped_charge = false },
|
|
492
|
+
["X_25MR"] = { explosive = 140, shaped_charge = false },
|
|
493
|
+
["X_29L"] = { explosive = 320, shaped_charge = false },
|
|
494
|
+
["X_29T"] = { explosive = 320, shaped_charge = false },
|
|
495
|
+
["X_29TE"] = { explosive = 320, shaped_charge = false },
|
|
496
|
+
|
|
497
|
+
--*** ANTI-RADAR MISSILE (ARM) ***
|
|
498
|
+
["AGM_88C"] = { explosive = 89, shaped_charge = false },
|
|
499
|
+
["AGM_88"] = { explosive = 89, shaped_charge = false },
|
|
500
|
+
["AGM_122"] = { explosive = 15, shaped_charge = false },
|
|
501
|
+
["LD-10"] = { explosive = 89, shaped_charge = false },
|
|
502
|
+
["AGM_45A"] = { explosive = 38, shaped_charge = false },
|
|
503
|
+
["X_58"] = { explosive = 140, shaped_charge = false },
|
|
504
|
+
["X_25MP"] = { explosive = 89, shaped_charge = false },
|
|
505
|
+
|
|
506
|
+
--*** ANTI-SHIP MISSILE (ASh) ***
|
|
507
|
+
["AGM_84D"] = { explosive = 488, shaped_charge = false },
|
|
508
|
+
["Rb 15F"] = { explosive = 500, shaped_charge = false },
|
|
509
|
+
["C-802AK"] = { explosive = 500, shaped_charge = false },
|
|
510
|
+
|
|
511
|
+
--*** CRUISE MISSILE ***
|
|
512
|
+
["CM-802AKG"] = { explosive = 488, shaped_charge = false },
|
|
513
|
+
["AGM_84E"] = { explosive = 488, shaped_charge = false },
|
|
514
|
+
["AGM_84H"] = { explosive = 488, shaped_charge = false },
|
|
515
|
+
["X_59M"] = { explosive = 488, shaped_charge = false },
|
|
516
|
+
|
|
517
|
+
--*** ROCKETS ***
|
|
518
|
+
["HYDRA_70M15"] = { explosive = 5, shaped_charge = false },
|
|
519
|
+
["HYDRA_70_MK1"] = { explosive = 5, shaped_charge = false },
|
|
520
|
+
["HYDRA_70_MK5"] = { explosive = 8, shaped_charge = false },
|
|
521
|
+
["HYDRA_70_M151"] = { explosive = 5, shaped_charge = false },
|
|
522
|
+
["HYDRA_70_M151_M433"] = { explosive = 5, shaped_charge = false },
|
|
523
|
+
["HYDRA_70_M229"] = { explosive = 10, shaped_charge = false },
|
|
524
|
+
["FFAR Mk1 HE"] = { explosive = 5, shaped_charge = false },
|
|
525
|
+
["FFAR Mk5 HEAT"] = { explosive = 8, shaped_charge = false },
|
|
526
|
+
["HVAR"] = { explosive = 5, shaped_charge = false },
|
|
527
|
+
["Zuni_127"] = { explosive = 8, shaped_charge = false },
|
|
528
|
+
["ARAKM70BHE"] = { explosive = 5, shaped_charge = false },
|
|
529
|
+
["ARAKM70BAP"] = { explosive = 8, shaped_charge = false },
|
|
530
|
+
["SNEB_TYPE251_F1B"] = { explosive = 4, shaped_charge = false },
|
|
531
|
+
["SNEB_TYPE252_F1B"] = { explosive = 4, shaped_charge = false },
|
|
532
|
+
["SNEB_TYPE253_F1B"] = { explosive = 5, shaped_charge = false },
|
|
533
|
+
["SNEB_TYPE256_F1B"] = { explosive = 6, shaped_charge = false },
|
|
534
|
+
["SNEB_TYPE257_F1B"] = { explosive = 8, shaped_charge = false },
|
|
535
|
+
["SNEB_TYPE251_F4B"] = { explosive = 4, shaped_charge = false },
|
|
536
|
+
["SNEB_TYPE252_F4B"] = { explosive = 4, shaped_charge = false },
|
|
537
|
+
["SNEB_TYPE253_F4B"] = { explosive = 5, shaped_charge = false },
|
|
538
|
+
["SNEB_TYPE256_F4B"] = { explosive = 6, shaped_charge = false },
|
|
539
|
+
["SNEB_TYPE257_F4B"] = { explosive = 8, shaped_charge = false },
|
|
540
|
+
["SNEB_TYPE251_H1"] = { explosive = 4, shaped_charge = false },
|
|
541
|
+
["SNEB_TYPE252_H1"] = { explosive = 4, shaped_charge = false },
|
|
542
|
+
["SNEB_TYPE253_H1"] = { explosive = 5, shaped_charge = false },
|
|
543
|
+
["SNEB_TYPE256_H1"] = { explosive = 6, shaped_charge = false },
|
|
544
|
+
["SNEB_TYPE257_H1"] = { explosive = 8, shaped_charge = false },
|
|
545
|
+
["MATRA_F4_SNEBT251"] = { explosive = 8, shaped_charge = false },
|
|
546
|
+
["MATRA_F4_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
547
|
+
["MATRA_F4_SNEBT256"] = { explosive = 8, shaped_charge = false },
|
|
548
|
+
["MATRA_F1_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
549
|
+
["MATRA_F1_SNEBT256"] = { explosive = 8, shaped_charge = false },
|
|
550
|
+
["TELSON8_SNEBT251"] = { explosive = 4, shaped_charge = false },
|
|
551
|
+
["TELSON8_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
552
|
+
["TELSON8_SNEBT256"] = { explosive = 4, shaped_charge = false },
|
|
553
|
+
["TELSON8_SNEBT257"] = { explosive = 6, shaped_charge = false },
|
|
554
|
+
["ARF8M3API"] = { explosive = 8, shaped_charge = false },
|
|
555
|
+
["UG_90MM"] = { explosive = 8, shaped_charge = false },
|
|
556
|
+
["S-24A"] = { explosive = 24, shaped_charge = false },
|
|
557
|
+
["S-25OF"] = { explosive = 194, shaped_charge = false },
|
|
558
|
+
["S-25OFM"] = { explosive = 150, shaped_charge = false },
|
|
559
|
+
["S-25O"] = { explosive = 150, shaped_charge = false },
|
|
560
|
+
["S-25-O"] = { explosive = 150, shaped_charge = false },
|
|
561
|
+
["S_25L"] = { explosive = 190, shaped_charge = false },
|
|
562
|
+
["S-5M"] = { explosive = 1, shaped_charge = false },
|
|
563
|
+
["C_5"] = { explosive = 8, shaped_charge = false },
|
|
564
|
+
["C5"] = { explosive = 5, shaped_charge = false },
|
|
565
|
+
["C_8"] = { explosive = 4, shaped_charge = false },
|
|
566
|
+
["C_8OFP2"] = { explosive = 3, shaped_charge = false },
|
|
567
|
+
["C_13"] = { explosive = 21, shaped_charge = false },
|
|
568
|
+
["C_24"] = { explosive = 123, shaped_charge = false },
|
|
569
|
+
["C_25"] = { explosive = 151, shaped_charge = false },
|
|
570
|
+
|
|
571
|
+
--*** LASER ROCKETS ***
|
|
572
|
+
["AGR_20"] = { explosive = 8, shaped_charge = false },
|
|
573
|
+
["AGR_20A"] = { explosive = 8, shaped_charge = false },
|
|
574
|
+
["AGR_20_M282"] = { explosive = 8, shaped_charge = false },
|
|
575
|
+
["Hydra_70_M282_MPP"] = { explosive = 5, shaped_charge = true },
|
|
576
|
+
["BRM-1_90MM"] = { explosive = 8, shaped_charge = false },
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
local effectSmokeId = 1
|
|
585
|
+
|
|
586
|
+
----[[ ##### HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
587
|
+
|
|
588
|
+
local function tableHasKey(table, key)
|
|
589
|
+
return table[key] ~= nil
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
local function debugMsg(str)
|
|
593
|
+
if splash_damage_options.debug == true then
|
|
594
|
+
debugCounter = (debugCounter or 0) + 1
|
|
595
|
+
local uniqueStr = str .. " [" .. timer.getTime() .. " - " .. debugCounter .. "]"
|
|
596
|
+
trigger.action.outText(uniqueStr, 5)
|
|
597
|
+
env.info("DEBUG: " .. uniqueStr)
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
local function gameMsg(str)
|
|
602
|
+
if splash_damage_options.game_messages == true then
|
|
603
|
+
trigger.action.outText(str, 5)
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
local function getDistance(point1, point2)
|
|
608
|
+
local x1 = point1.x
|
|
609
|
+
local y1 = point1.y
|
|
610
|
+
local z1 = point1.z
|
|
611
|
+
local x2 = point2.x
|
|
612
|
+
local y2 = point2.y
|
|
613
|
+
local z2 = point2.z
|
|
614
|
+
local dX = math.abs(x1 - x2)
|
|
615
|
+
local dZ = math.abs(z1 - z2)
|
|
616
|
+
local distance = math.sqrt(dX * dX + dZ * dZ)
|
|
617
|
+
return distance
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
local function getDistance3D(point1, point2)
|
|
621
|
+
local x1 = point1.x
|
|
622
|
+
local y1 = point1.y
|
|
623
|
+
local z1 = point1.z
|
|
624
|
+
local x2 = point2.x
|
|
625
|
+
local y2 = point2.y
|
|
626
|
+
local z2 = point2.z
|
|
627
|
+
local dX = math.abs(x1 - x2)
|
|
628
|
+
local dY = math.abs(y1 - y2)
|
|
629
|
+
local dZ = math.abs(z1 - z2)
|
|
630
|
+
local distance = math.sqrt(dX * dX + dZ * dZ + dY * dY)
|
|
631
|
+
return distance
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
local function vec3Mag(speedVec)
|
|
635
|
+
return math.sqrt(speedVec.x^2 + speedVec.y^2 + speedVec.z^2)
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
local function lookahead(speedVec)
|
|
639
|
+
local speed = vec3Mag(speedVec)
|
|
640
|
+
local dist = speed * refreshRate * 1.5
|
|
641
|
+
return dist
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
--Cluster-specific helper functions from Rockeye script
|
|
645
|
+
local function normalizeVector(vec)
|
|
646
|
+
local mag = math.sqrt(vec.x^2 + vec.z^2)
|
|
647
|
+
if mag > 0 then
|
|
648
|
+
return { x = vec.x / mag, z = vec.z / mag }
|
|
649
|
+
else
|
|
650
|
+
return { x = 1, z = 0 }
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
local function calculate_drop_angle(velocity)
|
|
654
|
+
local horizontal_speed = math.sqrt((velocity.x or 0)^2 + (velocity.z or 0)^2)
|
|
655
|
+
local vertical_speed = math.abs(velocity.y or 0)
|
|
656
|
+
if horizontal_speed == 0 then return 90 end
|
|
657
|
+
local angle_rad = math.atan(vertical_speed / horizontal_speed)
|
|
658
|
+
return math.deg(angle_rad)
|
|
659
|
+
end
|
|
660
|
+
local function calculate_dispersion(velocity, burst_altitude)
|
|
661
|
+
local velocity_magnitude = math.sqrt((velocity.x or 0)^2 + (velocity.z or 0)^2)
|
|
662
|
+
local drop_angle = calculate_drop_angle(velocity)
|
|
663
|
+
local length = splash_damage_options.cluster_base_length * (1 + velocity_magnitude / 200)
|
|
664
|
+
local width = splash_damage_options.cluster_base_width * (1 + burst_altitude / 6000)
|
|
665
|
+
local length_jitter = length * (0.85 + math.random() * 0.3)
|
|
666
|
+
local width_jitter = width * (0.85 + math.random() * 0.3)
|
|
667
|
+
return math.max(splash_damage_options.cluster_min_length, math.min(splash_damage_options.cluster_max_length, length_jitter)),
|
|
668
|
+
math.max(splash_damage_options.cluster_min_width, math.min(splash_damage_options.cluster_max_width, width_jitter))
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
----[[ ##### End of HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
680
|
+
giantExplosionTargets = {}
|
|
681
|
+
cargoEffectsQueue = {}
|
|
682
|
+
WpnHandler = {}
|
|
683
|
+
tracked_target_position = nil --Store the last known position of TargetUnit for giant explosion
|
|
684
|
+
tracked_weapons = {}
|
|
685
|
+
local processedUnitsGlobal = {}
|
|
686
|
+
|
|
687
|
+
function scanGiantExplosionTargets()
|
|
688
|
+
giantExplosionTargets = {}
|
|
689
|
+
local function findTargets(obj)
|
|
690
|
+
if obj:isExist() then
|
|
691
|
+
local name = obj:getName()
|
|
692
|
+
if string.find(name, "GiantExplosionTarget") then
|
|
693
|
+
local flagName = string.gsub(name, "Target", "")
|
|
694
|
+
table.insert(giantExplosionTargets, {
|
|
695
|
+
name = name,
|
|
696
|
+
flag = flagName,
|
|
697
|
+
obj = obj,
|
|
698
|
+
pos = obj:getPoint(),
|
|
699
|
+
static = splash_damage_options.giant_explosion_target_static
|
|
700
|
+
})
|
|
701
|
+
end
|
|
702
|
+
end
|
|
703
|
+
return true
|
|
704
|
+
end
|
|
705
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, {id = world.VolumeType.ALL}, findTargets)
|
|
706
|
+
if not splash_damage_options.giant_explosion_target_static then
|
|
707
|
+
timer.scheduleFunction(updateGiantExplosionPositions, {}, timer.getTime() + 1.0)
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
function updateTargetPosition()
|
|
712
|
+
for name, target in pairs(giantExplosionTargets) do
|
|
713
|
+
if target.obj:isExist() then
|
|
714
|
+
target.pos = target.obj:getPosition().p
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
return timer.getTime() + 1.0
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
--Giant Explosion Function
|
|
722
|
+
function triggerGiantExplosion(params)
|
|
723
|
+
if not splash_damage_options.giant_explosion_enabled then
|
|
724
|
+
debugMsg("Giant Explosion is disabled in options.")
|
|
725
|
+
return
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
local initialPos = params.pos or {x = 0, y = 0, z = 0}
|
|
729
|
+
local explosionPower = params.power or splash_damage_options.giant_explosion_power
|
|
730
|
+
local sizeScale = params.scale or splash_damage_options.giant_explosion_scale
|
|
731
|
+
local totalDuration = params.duration or splash_damage_options.giant_explosion_duration
|
|
732
|
+
local explosionCount = params.count or splash_damage_options.giant_explosion_count
|
|
733
|
+
|
|
734
|
+
if not initialPos.x or not initialPos.y or not initialPos.z then
|
|
735
|
+
gameMsg("Error: Invalid position for giant explosion!")
|
|
736
|
+
debugMsg("No valid initial position set for giant explosion!")
|
|
737
|
+
return
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
debugMsg("Triggering giant fireball at X: " .. initialPos.x .. ", Y: " .. initialPos.y .. ", Z: " .. initialPos.z)
|
|
741
|
+
|
|
742
|
+
local function scheduleExplosion(pos, delay)
|
|
743
|
+
if not pos or not pos.x or not pos.y or not pos.z then
|
|
744
|
+
debugMsg("Error: Invalid position for explosion - pos: " .. tostring(pos))
|
|
745
|
+
return
|
|
746
|
+
end
|
|
747
|
+
timer.scheduleFunction(function(p)
|
|
748
|
+
if p and p.x and p.y and p.z then
|
|
749
|
+
trigger.action.explosion(p, explosionPower)
|
|
750
|
+
end
|
|
751
|
+
end, pos, timer.getTime() + delay)
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
-- Pre-explosion scan for cargo units
|
|
755
|
+
local scanRadius = 1500 * sizeScale -- 1500m base radius, scaled by sizeScale
|
|
756
|
+
local preExplosionTargets = {}
|
|
757
|
+
if splash_damage_options.enable_cargo_effects then
|
|
758
|
+
local volS = {
|
|
759
|
+
id = world.VolumeType.SPHERE,
|
|
760
|
+
params = { point = initialPos, radius = scanRadius }
|
|
761
|
+
}
|
|
762
|
+
local ifFound = function(foundObject)
|
|
763
|
+
if foundObject:isExist() then
|
|
764
|
+
local category = foundObject:getCategory()
|
|
765
|
+
if (category == Object.Category.UNIT and foundObject:getDesc().category == Unit.Category.GROUND_UNIT) or
|
|
766
|
+
category == Object.Category.STATIC then
|
|
767
|
+
table.insert(preExplosionTargets, {
|
|
768
|
+
name = foundObject:getTypeName(),
|
|
769
|
+
health = foundObject:getLife() or 0,
|
|
770
|
+
position = foundObject:getPoint(),
|
|
771
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
772
|
+
unit = foundObject
|
|
773
|
+
})
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
return true
|
|
777
|
+
end
|
|
778
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
779
|
+
debugMsg("Pre-explosion scan for Giant Explosion: " .. #preExplosionTargets .. " targets found within " .. scanRadius .. "m")
|
|
780
|
+
end
|
|
781
|
+
-- Trigger the explosion
|
|
782
|
+
local maxRadius = 200 * sizeScale
|
|
783
|
+
local maxHeight = 500 * sizeScale
|
|
784
|
+
local adjustedExplosionCount = math.floor(explosionCount * (sizeScale ^ 2.5))
|
|
785
|
+
local stepTime = totalDuration / adjustedExplosionCount
|
|
786
|
+
local variance = 0.25 --Fixed at 25%
|
|
787
|
+
|
|
788
|
+
for i = 1, adjustedExplosionCount do
|
|
789
|
+
local progress = i / adjustedExplosionCount
|
|
790
|
+
local currentRadius = maxRadius * progress
|
|
791
|
+
local r = currentRadius * (0.9 + math.random() * 0.1)
|
|
792
|
+
local theta = math.random() * 2 * math.pi
|
|
793
|
+
local phi = math.acos(math.random())
|
|
794
|
+
|
|
795
|
+
local offsetX = r * math.sin(phi) * math.cos(theta)
|
|
796
|
+
local offsetZ = r * math.sin(phi) * math.sin(theta)
|
|
797
|
+
local offsetY = r * math.cos(phi)
|
|
798
|
+
|
|
799
|
+
offsetX = offsetX * (1 + (math.random() - 0.5) * variance)
|
|
800
|
+
offsetZ = offsetZ * (1 + (math.random() - 0.5) * variance)
|
|
801
|
+
offsetY = offsetY * (1 + (math.random() - 0.5) * variance * 0.5)
|
|
802
|
+
|
|
803
|
+
local blastPos = {
|
|
804
|
+
x = initialPos.x + offsetX,
|
|
805
|
+
y = land.getHeight({x = initialPos.x, y = initialPos.z}) + offsetY,
|
|
806
|
+
z = initialPos.z + offsetZ
|
|
807
|
+
}
|
|
808
|
+
if blastPos.y < land.getHeight({x = blastPos.x, y = blastPos.z}) then
|
|
809
|
+
blastPos.y = land.getHeight({x = blastPos.x, y = blastPos.z})
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
local delay = (i - 1) * stepTime + (math.random() - 0.5) * stepTime * variance
|
|
813
|
+
scheduleExplosion(blastPos, delay)
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
gameMsg("Expanding giant fireball over " .. totalDuration .. "s (scale " .. sizeScale .. ")!")
|
|
817
|
+
|
|
818
|
+
-- Post-explosion scan and cargo cook-off queuing
|
|
819
|
+
if splash_damage_options.enable_cargo_effects then
|
|
820
|
+
timer.scheduleFunction(function(args)
|
|
821
|
+
local centerPos = args[1]
|
|
822
|
+
local radius = args[2]
|
|
823
|
+
local preTargets = args[3]
|
|
824
|
+
|
|
825
|
+
local postExplosionTargets = {}
|
|
826
|
+
local volS = {
|
|
827
|
+
id = world.VolumeType.SPHERE,
|
|
828
|
+
params = { point = centerPos, radius = radius }
|
|
829
|
+
}
|
|
830
|
+
local ifFound = function(foundObject)
|
|
831
|
+
if foundObject:isExist() then
|
|
832
|
+
local category = foundObject:getCategory()
|
|
833
|
+
if (category == Object.Category.UNIT and foundObject:getDesc().category == Unit.Category.GROUND_UNIT) or
|
|
834
|
+
category == Object.Category.STATIC then
|
|
835
|
+
table.insert(postExplosionTargets, {
|
|
836
|
+
name = foundObject:getTypeName(),
|
|
837
|
+
health = foundObject:getLife() or 0,
|
|
838
|
+
position = foundObject:getPoint(),
|
|
839
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0
|
|
840
|
+
})
|
|
841
|
+
end
|
|
842
|
+
end
|
|
843
|
+
return true
|
|
844
|
+
end
|
|
845
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
846
|
+
debugMsg("Post-explosion scan for Giant Explosion: " .. #postExplosionTargets .. " targets found within " .. radius .. "m")
|
|
847
|
+
|
|
848
|
+
-- Compare pre- and post-explosion targets
|
|
849
|
+
for _, preTarget in ipairs(preTargets) do
|
|
850
|
+
local found = false
|
|
851
|
+
local postHealth = 0
|
|
852
|
+
for _, postTarget in ipairs(postExplosionTargets) do
|
|
853
|
+
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
854
|
+
found = true
|
|
855
|
+
postHealth = postTarget.health
|
|
856
|
+
break
|
|
857
|
+
end
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
local cargoData = cargoUnits[preTarget.name]
|
|
861
|
+
if cargoData and (not found or postHealth <= 0) then
|
|
862
|
+
local distance = getDistance(initialPos, preTarget.position)
|
|
863
|
+
if distance <= radius then
|
|
864
|
+
local cargoPower = cargoData.cargoExplosionPower or explosionPower
|
|
865
|
+
table.insert(cargoEffectsQueue, {
|
|
866
|
+
name = preTarget.name,
|
|
867
|
+
distance = distance,
|
|
868
|
+
coords = preTarget.position,
|
|
869
|
+
power = cargoPower,
|
|
870
|
+
explosion = cargoData.cargoExplosion,
|
|
871
|
+
cookOff = cargoData.cargoCookOff,
|
|
872
|
+
cookOffCount = cargoData.cookOffCount,
|
|
873
|
+
cookOffPower = cargoData.cookOffPower,
|
|
874
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
875
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
876
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
877
|
+
isTanker = cargoData.isTanker,
|
|
878
|
+
flameSize = cargoData.flameSize,
|
|
879
|
+
flameDuration = cargoData.flameDuration
|
|
880
|
+
})
|
|
881
|
+
debugMsg("Queued cargo effect for " .. preTarget.name .. " destroyed by Giant Explosion at " .. string.format("%.1f", distance) .. "m")
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
-- Process queued cargo effects with prioritized flames
|
|
887
|
+
if #cargoEffectsQueue > 0 then
|
|
888
|
+
local flameIndex = 0 -- Separate index for flames
|
|
889
|
+
local otherIndex = 0 -- Index for explosions, cook-offs, debris
|
|
890
|
+
local processedCargoUnits = {}
|
|
891
|
+
local flamePositions = {}
|
|
892
|
+
for _, effect in ipairs(cargoEffectsQueue) do
|
|
893
|
+
local unitKey = effect.name .. "_" .. effect.coords.x .. "_" .. effect.coords.z
|
|
894
|
+
if not processedUnitsGlobal[unitKey] and not processedCargoUnits[unitKey] then
|
|
895
|
+
-- Handle tanker flames first with minimal delay
|
|
896
|
+
if effect.isTanker and effect.explosion then
|
|
897
|
+
debugMsg("Triggering cargo explosion for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. flameIndex .. "s")
|
|
898
|
+
timer.scheduleFunction(function(params)
|
|
899
|
+
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
900
|
+
trigger.action.explosion(params[1], params[2])
|
|
901
|
+
end, {effect.coords, effect.power}, timer.getTime() + flameIndex + 0.1)
|
|
902
|
+
|
|
903
|
+
local flameSize = effect.flameSize or 3
|
|
904
|
+
local flameDuration = effect.flameDuration
|
|
905
|
+
local flameDensity = 1.0
|
|
906
|
+
local effectId = effectSmokeId
|
|
907
|
+
effectSmokeId = effectSmokeId + 1
|
|
908
|
+
local isDuplicate = false
|
|
909
|
+
for _, pos in pairs(flamePositions) do
|
|
910
|
+
if getDistance3D(effect.coords, pos) < 3 then
|
|
911
|
+
isDuplicate = true
|
|
912
|
+
debugMsg("Skipping duplicate flame for " .. effect.name .. " near X: " .. string.format("%.0f", pos.x) .. ", Y: " .. string.format("%.0f", pos.y) .. ", Z: " .. string.format("%.0f", pos.z))
|
|
913
|
+
break
|
|
914
|
+
end
|
|
915
|
+
end
|
|
916
|
+
if not isDuplicate then
|
|
917
|
+
debugMsg("Adding flame effect for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m (Size: " .. flameSize .. ", Duration: " .. flameDuration .. "s, ID: " .. effectId .. ") scheduled at " .. flameIndex .. "s")
|
|
918
|
+
timer.scheduleFunction(function(params)
|
|
919
|
+
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
920
|
+
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
921
|
+
debugMsg("Spawning flame effect at X: " .. string.format("%.0f", adjustedCoords.x) .. ", Y: " .. string.format("%.0f", adjustedCoords.y) .. ", Z: " .. string.format("%.0f", adjustedCoords.z))
|
|
922
|
+
trigger.action.explosion(adjustedCoords, 10) -- Small trigger explosion
|
|
923
|
+
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
924
|
+
end, {effect.coords, flameSize, flameDensity, effectId}, timer.getTime() + flameIndex + 0.2)
|
|
925
|
+
timer.scheduleFunction(function(id)
|
|
926
|
+
debugMsg("Stopping flame effect for " .. effect.name .. " (ID: " .. id .. ")")
|
|
927
|
+
trigger.action.effectSmokeStop(id)
|
|
928
|
+
end, effectId, timer.getTime() + flameIndex + flameDuration + 0.2)
|
|
929
|
+
table.insert(flamePositions, effect.coords)
|
|
930
|
+
end
|
|
931
|
+
flameIndex = flameIndex + 0.5 -- Fast spacing for flames (0.5s)
|
|
932
|
+
end
|
|
933
|
+
-- Handle non-tanker explosions, cook-offs, and debris
|
|
934
|
+
if not effect.isTanker or (effect.explosion and not effect.isTanker) then
|
|
935
|
+
if effect.explosion then
|
|
936
|
+
debugMsg("Triggering cargo explosion for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. otherIndex .. "s")
|
|
937
|
+
timer.scheduleFunction(function(params)
|
|
938
|
+
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
939
|
+
trigger.action.explosion(params[1], params[2])
|
|
940
|
+
end, {effect.coords, effect.power}, timer.getTime() + otherIndex + 0.1)
|
|
941
|
+
end
|
|
942
|
+
if effect.cookOff and effect.cookOffCount > 0 then
|
|
943
|
+
debugMsg("Scheduling " .. effect.cookOffCount .. " cook-off explosions for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m over " .. effect.cookOffDuration .. "s starting at " .. otherIndex .. "s")
|
|
944
|
+
for i = 1, effect.cookOffCount do
|
|
945
|
+
local delay = effect.cookOffRandomTiming and math.random() * effect.cookOffDuration or (i - 1) * (effect.cookOffDuration / effect.cookOffCount)
|
|
946
|
+
local basePower = effect.cookOffPower
|
|
947
|
+
local powerVariation = effect.cookOffPowerRandom / 100
|
|
948
|
+
local cookOffPower = effect.cookOffPowerRandom == 0 and basePower or basePower * (1 + powerVariation * (math.random() * 2 - 1))
|
|
949
|
+
debugMsg("Cook-off #" .. i .. " for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m scheduled at " .. string.format("%.3f", delay) .. "s with power " .. string.format("%.2f", cookOffPower))
|
|
950
|
+
timer.scheduleFunction(function(params)
|
|
951
|
+
debugMsg("Executing cook-off at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
952
|
+
trigger.action.explosion(params[1], params[2])
|
|
953
|
+
end, {effect.coords, cookOffPower}, timer.getTime() + otherIndex + delay)
|
|
954
|
+
end
|
|
955
|
+
if splash_damage_options.debris_effects then
|
|
956
|
+
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
957
|
+
for j = 1, debrisCount do
|
|
958
|
+
local theta = math.random() * 2 * math.pi
|
|
959
|
+
local phi = math.acos(math.random() * 2 - 1)
|
|
960
|
+
local minDist = splash_damage_options.debris_max_distance * 0.1
|
|
961
|
+
local maxDist = splash_damage_options.debris_max_distance
|
|
962
|
+
local r = math.random() * (maxDist - minDist) + minDist
|
|
963
|
+
local debrisX = effect.coords.x + r * math.sin(phi) * math.cos(theta)
|
|
964
|
+
local debrisZ = effect.coords.z + r * math.sin(phi) * math.sin(theta)
|
|
965
|
+
local terrainY = land.getHeight({x = debrisX, y = debrisZ})
|
|
966
|
+
local debrisY = terrainY + math.random() * maxDist
|
|
967
|
+
local debrisPos = {x = debrisX, y = debrisY, z = debrisZ}
|
|
968
|
+
local debrisPower = splash_damage_options.debris_power
|
|
969
|
+
local debrisDelay = (j - 1) * (effect.cookOffDuration / debrisCount)
|
|
970
|
+
timer.scheduleFunction(function(debrisArgs)
|
|
971
|
+
debugMsg("Debris explosion at X: " .. string.format("%.0f", debrisArgs[1].x) .. ", Y: " .. string.format("%.0f", debrisArgs[1].y) .. ", Z: " .. string.format("%.0f", debrisArgs[1].z) .. " with power " .. debrisArgs[2])
|
|
972
|
+
trigger.action.explosion(debrisArgs[1], debrisArgs[2])
|
|
973
|
+
end, {debrisPos, debrisPower}, timer.getTime() + otherIndex + debrisDelay)
|
|
974
|
+
end
|
|
975
|
+
end
|
|
976
|
+
end
|
|
977
|
+
otherIndex = otherIndex + 1 -- Slower spacing for non-flame effects (1s)
|
|
978
|
+
end
|
|
979
|
+
processedCargoUnits[unitKey] = true
|
|
980
|
+
processedUnitsGlobal[unitKey] = true
|
|
981
|
+
end
|
|
982
|
+
end
|
|
983
|
+
cargoEffectsQueue = {} -- Clear the queue after processing
|
|
984
|
+
end
|
|
985
|
+
end, {initialPos, scanRadius, preExplosionTargets}, timer.getTime() + totalDuration + 1.0)
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
--Flag Checker for mission editor
|
|
990
|
+
function checkGiantExplosionFlag()
|
|
991
|
+
for name, target in pairs(giantExplosionTargets) do
|
|
992
|
+
local flagName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
993
|
+
local flagValue = trigger.misc.getUserFlag(flagName)
|
|
994
|
+
--commenting out as it spams every second
|
|
995
|
+
debugMsg("Checking flag " .. flagName .. ": " .. flagValue)
|
|
996
|
+
if flagValue == 1 then
|
|
997
|
+
debugMsg("Triggering explosion for " .. name .. " at X:" .. target.pos.x .. " Y:" .. target.pos.y .. " Z:" .. target.pos.z)
|
|
998
|
+
triggerGiantExplosion({
|
|
999
|
+
pos = target.pos,
|
|
1000
|
+
power = splash_damage_options.giant_explosion_power,
|
|
1001
|
+
scale = splash_damage_options.giant_explosion_scale,
|
|
1002
|
+
duration = splash_damage_options.giant_explosion_duration,
|
|
1003
|
+
count = splash_damage_options.giant_explosion_count
|
|
1004
|
+
})
|
|
1005
|
+
trigger.action.setUserFlag(flagName, 2)
|
|
1006
|
+
end
|
|
1007
|
+
end
|
|
1008
|
+
return timer.getTime() + splash_damage_options.giant_explosion_poll_rate
|
|
1009
|
+
end
|
|
1010
|
+
|
|
1011
|
+
function getWeaponExplosive(name)
|
|
1012
|
+
local weaponData = explTable[name]
|
|
1013
|
+
if weaponData then
|
|
1014
|
+
return weaponData.explosive, weaponData.shaped_charge
|
|
1015
|
+
else
|
|
1016
|
+
return 0, false
|
|
1017
|
+
end
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
function track_wpns_cluster_scan(args)
|
|
1021
|
+
local parentPos = args[1]
|
|
1022
|
+
local parentDir = args[2]
|
|
1023
|
+
local parentName = args[3]
|
|
1024
|
+
local subName = args[4]
|
|
1025
|
+
local subCount = args[5]
|
|
1026
|
+
local subPower = args[6]
|
|
1027
|
+
local parentVel = args[7]
|
|
1028
|
+
local attempt = args[8] or 1
|
|
1029
|
+
local maxAttempts = 3
|
|
1030
|
+
local scanVol = {
|
|
1031
|
+
id = world.VolumeType.SPHERE,
|
|
1032
|
+
params = { point = parentPos, radius = 400 }
|
|
1033
|
+
}
|
|
1034
|
+
local bombletsFound = {}
|
|
1035
|
+
local allWeaponsFound = {}
|
|
1036
|
+
--General scan for all weapons
|
|
1037
|
+
world.searchObjects(Object.Category.WEAPON, scanVol, function(wpn)
|
|
1038
|
+
if wpn:isExist() then
|
|
1039
|
+
local wpnId = wpn.id_
|
|
1040
|
+
local wpnType = wpn:getTypeName()
|
|
1041
|
+
local wpnPos = wpn:getPosition().p
|
|
1042
|
+
table.insert(allWeaponsFound, { id = wpnId, type = wpnType, x = wpnPos.x, y = wpnPos.y, z = wpnPos.z })
|
|
1043
|
+
if wpnType == subName and not tracked_weapons[wpnId] then
|
|
1044
|
+
tracked_weapons[wpnId] = {
|
|
1045
|
+
wpn = wpn,
|
|
1046
|
+
pos = wpnPos,
|
|
1047
|
+
speed = wpn:getVelocity(),
|
|
1048
|
+
name = wpnType,
|
|
1049
|
+
parent = parentName,
|
|
1050
|
+
parentVelocity = parentVel
|
|
1051
|
+
}
|
|
1052
|
+
table.insert(bombletsFound, wpnId)
|
|
1053
|
+
debugMsg("Detected expected submunition '" .. wpnType .. "' from '" .. parentName .. "' at X: " .. string.format("%.0f", wpnPos.x) .. ", Y: " .. string.format("%.0f", wpnPos.y) .. ", Z: " .. string.format("%.0f", wpnPos.z) .. " (Attempt " .. attempt .. ")")
|
|
1054
|
+
end
|
|
1055
|
+
end
|
|
1056
|
+
return true
|
|
1057
|
+
end)
|
|
1058
|
+
--Log results
|
|
1059
|
+
debugMsg("Scanned for submunition '" .. subName .. "' bomblets from '" .. parentName .. "': " .. #bombletsFound .. " found (Attempt " .. attempt .. ")")
|
|
1060
|
+
if #allWeaponsFound > 0 then
|
|
1061
|
+
local msg = "General scan for '" .. parentName .. "': " .. #allWeaponsFound .. " bomblets released, expected " .. subCount .. " '" .. subName .. "'"
|
|
1062
|
+
local typeMismatch = false
|
|
1063
|
+
for _, wpn in ipairs(allWeaponsFound) do
|
|
1064
|
+
if wpn.type ~= subName then
|
|
1065
|
+
typeMismatch = true
|
|
1066
|
+
break
|
|
1067
|
+
end
|
|
1068
|
+
end
|
|
1069
|
+
if typeMismatch then
|
|
1070
|
+
msg = msg .. " - Mismatch detected! Actual bomblets: "
|
|
1071
|
+
for _, wpn in ipairs(allWeaponsFound) do
|
|
1072
|
+
msg = msg .. "'" .. wpn.type .. "' (X: " .. string.format("%.0f", wpn.x) .. ", Y: " .. string.format("%.0f", wpn.y) .. ", Z: " .. string.format("%.0f", wpn.z) .. ") "
|
|
1073
|
+
end
|
|
1074
|
+
msg = msg .. "Script may need changing."
|
|
1075
|
+
end
|
|
1076
|
+
debugMsg(msg)
|
|
1077
|
+
elseif #bombletsFound == 0 and #allWeaponsFound == 0 then
|
|
1078
|
+
debugMsg("No bomblets of any type detected for '" .. parentName .. "' (Attempt " .. attempt .. ")")
|
|
1079
|
+
end
|
|
1080
|
+
--Retry if no expected submunitions found
|
|
1081
|
+
if #bombletsFound == 0 and attempt < maxAttempts then
|
|
1082
|
+
debugMsg("No expected submunition '" .. subName .. "' found on attempt " .. attempt .. ", retrying in 0.5s")
|
|
1083
|
+
timer.scheduleFunction(track_wpns_cluster_scan, {parentPos, parentDir, parentName, subName, subCount, subPower, parentVel, attempt + 1}, timer.getTime() + 0.5)
|
|
1084
|
+
elseif #bombletsFound == 0 and attempt == maxAttempts then
|
|
1085
|
+
debugMsg("No submunition '" .. subName .. "' spawned by DCS for '" .. parentName .. "' after " .. maxAttempts .. " attempts - skipping additional explosions")
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
----[[ ##### Updated track_wpns() Function ##### ]]----
|
|
1090
|
+
local recentExplosions = {}
|
|
1091
|
+
function track_wpns()
|
|
1092
|
+
local weaponsToRemove = {} --Delay removal to ensure all weapons are checked
|
|
1093
|
+
for wpn_id_, wpnData in pairs(tracked_weapons) do
|
|
1094
|
+
local status, err = pcall(function()
|
|
1095
|
+
if wpnData.wpn:isExist() then
|
|
1096
|
+
--Update position, direction, speed
|
|
1097
|
+
wpnData.pos = wpnData.wpn:getPosition().p
|
|
1098
|
+
wpnData.dir = wpnData.wpn:getPosition().x
|
|
1099
|
+
wpnData.speed = wpnData.wpn:getVelocity()
|
|
1100
|
+
--[[
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
--Tick-by-tick tracking from weapon's actual position
|
|
1104
|
+
local tickVol = {
|
|
1105
|
+
id = world.VolumeType.SPHERE,
|
|
1106
|
+
params = {
|
|
1107
|
+
point = wpnData.pos, --Real weapon position
|
|
1108
|
+
radius = 150 --150m radius
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
local tickTargets = {}
|
|
1112
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, function(obj)
|
|
1113
|
+
if obj:isExist() then
|
|
1114
|
+
table.insert(tickTargets, {
|
|
1115
|
+
name = obj:getTypeName(),
|
|
1116
|
+
distance = getDistance3D(wpnData.pos, obj:getPoint()), --3D distance
|
|
1117
|
+
position = obj:getPoint(),
|
|
1118
|
+
health = obj:getLife() or 0
|
|
1119
|
+
})
|
|
1120
|
+
end
|
|
1121
|
+
return true
|
|
1122
|
+
end)
|
|
1123
|
+
debugMsg("Tick Track for " .. wpnData.name .. " at X: " .. string.format("%.0f", wpnData.pos.x) .. ", Y: " .. string.format("%.0f", wpnData.pos.y) .. ", Z: " .. string.format("%.0f", wpnData.pos.z) .. " - " .. #tickTargets .. " targets")
|
|
1124
|
+
for i, target in ipairs(tickTargets) do
|
|
1125
|
+
debugMsg("Tick Target #" .. i .. ": " .. target.name .. " at X: " .. string.format("%.0f", target.position.x) .. ", Y: " .. string.format("%.0f", target.position.y) .. ", Z: " .. string.format("%.0f", target.position.z) .. ", Dist: " .. string.format("%.1f", target.distance) .. "m, Health: " .. target.health)
|
|
1126
|
+
end
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
]]--
|
|
1130
|
+
|
|
1131
|
+
--Scan potential blast zone in the last frame before impact
|
|
1132
|
+
if splash_damage_options.track_pre_explosion then
|
|
1133
|
+
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed))
|
|
1134
|
+
local predictedImpact = ip or wpnData.pos
|
|
1135
|
+
|
|
1136
|
+
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
1137
|
+
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
1138
|
+
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
1139
|
+
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
local explosionPower = base_explosive
|
|
1143
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1144
|
+
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
local blastRadius = splash_damage_options.blast_search_radius * 2 --Wider post-scan (180m default)
|
|
1148
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
1149
|
+
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1150
|
+
end
|
|
1151
|
+
|
|
1152
|
+
--Tight scan while weapon exists
|
|
1153
|
+
--local tightRadius = 50
|
|
1154
|
+
--if splash_damage_options.use_dynamic_blast_radius then
|
|
1155
|
+
--tightRadius = math.pow(explosionPower, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1156
|
+
--end
|
|
1157
|
+
local tightRadius = blastRadius --Use already calculated blastRadius
|
|
1158
|
+
local volS = {
|
|
1159
|
+
id = world.VolumeType.SPHERE,
|
|
1160
|
+
params = {
|
|
1161
|
+
point = wpnData.pos, --Use current pos
|
|
1162
|
+
radius = tightRadius
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
local tightTargets = {}
|
|
1166
|
+
local ifFound = function(foundObject, targets, center)
|
|
1167
|
+
if foundObject:isExist() then
|
|
1168
|
+
local category = foundObject:getCategory()
|
|
1169
|
+
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
1170
|
+
category == Object.Category.STATIC then
|
|
1171
|
+
table.insert(targets, {
|
|
1172
|
+
name = foundObject:getTypeName(),
|
|
1173
|
+
distance = getDistance(center, foundObject:getPoint()),
|
|
1174
|
+
health = foundObject:getLife() or 0,
|
|
1175
|
+
position = foundObject:getPoint(),
|
|
1176
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
1177
|
+
unit = foundObject
|
|
1178
|
+
})
|
|
1179
|
+
end
|
|
1180
|
+
end
|
|
1181
|
+
return true
|
|
1182
|
+
end
|
|
1183
|
+
if splash_damage_options.track_pre_explosion_debug then
|
|
1184
|
+
debugMsg("Scanning tight radius " .. tightRadius .. "m at current pos while weapon exists")
|
|
1185
|
+
end
|
|
1186
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, function(obj) ifFound(obj, tightTargets, wpnData.pos) end)
|
|
1187
|
+
wpnData.tightTargets = tightTargets --Store for impact
|
|
1188
|
+
|
|
1189
|
+
--Wider scan for lastKnownTargets
|
|
1190
|
+
volS.params.point = predictedImpact
|
|
1191
|
+
volS.params.radius = blastRadius
|
|
1192
|
+
local foundTargets = {}
|
|
1193
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, function(obj) ifFound(obj, foundTargets, predictedImpact) end)
|
|
1194
|
+
wpnData.lastKnownTargets = foundTargets
|
|
1195
|
+
end
|
|
1196
|
+
--Submunition impact handling
|
|
1197
|
+
local weaponData = explTable[wpnData.parent or wpnData.name] or { submunition_name = "unknown" }
|
|
1198
|
+
if wpnData.name == weaponData.submunition_name then
|
|
1199
|
+
local groundHeight = land.getHeight({x = wpnData.pos.x, y = wpnData.pos.z})
|
|
1200
|
+
if wpnData.pos.y - groundHeight < 50 then --Impact threshold like old script
|
|
1201
|
+
debugMsg("Submunition '" .. wpnData.name .. "' from '" .. (wpnData.parent or "unknown") .. "' impacted at X: " .. string.format("%.0f", wpnData.pos.x) .. ", Z: " .. string.format("%.0f", wpnData.pos.z))
|
|
1202
|
+
local parentWeaponData = explTable[wpnData.parent] or { submunition_count = 30, submunition_explosive = 1 }
|
|
1203
|
+
local submunitionCount = parentWeaponData.submunition_count or 30
|
|
1204
|
+
local submunitionPower = (parentWeaponData.submunition_explosive or 1) * splash_damage_options.cluster_bomblet_damage_modifier * splash_damage_options.overall_scaling
|
|
1205
|
+
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
1206
|
+
if submunitionCount > 35 then
|
|
1207
|
+
local reductionFactor = (60 - 35) / (247 - 35)
|
|
1208
|
+
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
1209
|
+
if submunitionCount > 60 then submunitionCount = 60 end
|
|
1210
|
+
end
|
|
1211
|
+
end
|
|
1212
|
+
--Use parent velocity if available, else submunition speed
|
|
1213
|
+
local parentDir = wpnData.parentVelocity or wpnData.speed
|
|
1214
|
+
local dispersionLength, dispersionWidth = calculate_dispersion(parentDir, 2000) --Match original 2000m
|
|
1215
|
+
local dirMag = math.sqrt(parentDir.x^2 + parentDir.z^2)
|
|
1216
|
+
local dir = dirMag > 0 and {x = parentDir.x / dirMag, z = parentDir.z / dirMag} or {x = 1, z = 0}
|
|
1217
|
+
debugMsg("Simulating " .. submunitionCount .. " bomblets for submunition '" .. wpnData.name .. "' from '" .. (wpnData.parent or "unknown") .. "' over " .. string.format("%.0f", dispersionLength) .. "m x " .. string.format("%.0f", dispersionWidth) .. "m")
|
|
1218
|
+
for i = 1, submunitionCount do
|
|
1219
|
+
local theta = math.random() * 2 * math.pi
|
|
1220
|
+
local r = math.sqrt(math.random())
|
|
1221
|
+
local xOffset = r * dispersionLength * 0.5 * math.cos(theta)
|
|
1222
|
+
local zOffset = r * dispersionWidth * 0.5 * math.sin(theta)
|
|
1223
|
+
local subPos = {
|
|
1224
|
+
x = wpnData.pos.x + (xOffset * dir.x - zOffset * dir.z),
|
|
1225
|
+
z = wpnData.pos.z + (xOffset * dir.z + zOffset * dir.x)
|
|
1226
|
+
}
|
|
1227
|
+
subPos.y = land.getHeight({x = subPos.x, y = subPos.z})
|
|
1228
|
+
debugMsg("Triggering bomblet #" .. i .. " for submunition '" .. wpnData.name .. "' at X: " .. string.format("%.0f", subPos.x) .. ", Z: " .. string.format("%.0f", subPos.z) .. " with power " .. submunitionPower)
|
|
1229
|
+
trigger.action.explosion(subPos, submunitionPower)
|
|
1230
|
+
end
|
|
1231
|
+
table.insert(weaponsToRemove, wpn_id_)
|
|
1232
|
+
end
|
|
1233
|
+
end
|
|
1234
|
+
else
|
|
1235
|
+
--Weapon has impacted
|
|
1236
|
+
debugMsg("Weapon " .. wpnData.name .. " no longer exists at " .. timer.getTime() .. "s")
|
|
1237
|
+
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed)) --terrain intersection point with weapon's nose. Only search out 20 meters though.
|
|
1238
|
+
local explosionPoint
|
|
1239
|
+
if not ip then --use last calculated IP
|
|
1240
|
+
explosionPoint = wpnData.pos
|
|
1241
|
+
else --use intersection point
|
|
1242
|
+
explosionPoint = ip
|
|
1243
|
+
end
|
|
1244
|
+
local chosenTargets = wpnData.tightTargets or {}
|
|
1245
|
+
local safeToBlast = true
|
|
1246
|
+
if splash_damage_options.ordnance_protection then
|
|
1247
|
+
local checkVol = { id = world.VolumeType.SPHERE, params = { point = explosionPoint, radius = splash_damage_options.ordnance_protection_radius } }
|
|
1248
|
+
debugMsg("Checking ordnance protection for '" .. wpnData.name .. "' at X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. " with radius " .. splash_damage_options.ordnance_protection_radius .. "m")
|
|
1249
|
+
world.searchObjects(Object.Category.WEAPON, checkVol, function(obj)
|
|
1250
|
+
if obj:isExist() and tracked_weapons[obj.id_] then
|
|
1251
|
+
safeToBlast = false
|
|
1252
|
+
debugMsg("Skipping explosion for '" .. wpnData.name .. "' - nearby bomb '" .. tracked_weapons[obj.id_].name .. "' within " .. splash_damage_options.ordnance_protection_radius .. "m")
|
|
1253
|
+
return false
|
|
1254
|
+
end
|
|
1255
|
+
return true
|
|
1256
|
+
end)
|
|
1257
|
+
end
|
|
1258
|
+
if safeToBlast then
|
|
1259
|
+
debugMsg("FinalPos Check for '" .. wpnData.name .. "': X: " .. string.format("%.0f", explosionPoint.x) .. ", Y: " .. string.format("%.0f", explosionPoint.y) .. ", Z: " .. string.format("%.0f", explosionPoint.z) .. ")")
|
|
1260
|
+
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
1261
|
+
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
1262
|
+
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
1263
|
+
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
1264
|
+
end
|
|
1265
|
+
|
|
1266
|
+
local explosionPower = base_explosive
|
|
1267
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1268
|
+
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
local blastRadius = splash_damage_options.blast_search_radius * 2 --Wider post-scan (180m default)
|
|
1272
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
1273
|
+
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1274
|
+
end
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
--Store pre-explosion state of all tracked weapons for detection
|
|
1278
|
+
local preExplosionWeapons = {}
|
|
1279
|
+
if splash_damage_options.ordnance_protection and splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
1280
|
+
for id, data in pairs(tracked_weapons) do
|
|
1281
|
+
if data.wpn:isExist() then
|
|
1282
|
+
preExplosionWeapons[id] = {
|
|
1283
|
+
name = data.name,
|
|
1284
|
+
pos = data.wpn:getPosition().p,
|
|
1285
|
+
distance = getDistance3D(explosionPoint, data.wpn:getPosition().p),
|
|
1286
|
+
explosive = getWeaponExplosive(data.name) --Store the explosive power
|
|
1287
|
+
}
|
|
1288
|
+
end
|
|
1289
|
+
end
|
|
1290
|
+
end
|
|
1291
|
+
--Cluster Bomb Handling
|
|
1292
|
+
local weaponData = explTable[wpnData.name] or { explosive = 0, shaped_charge = false }
|
|
1293
|
+
local isCluster = weaponData.cluster or false
|
|
1294
|
+
if splash_damage_options.cluster_enabled and isCluster then
|
|
1295
|
+
local submunitionCount = weaponData.submunition_count or 30
|
|
1296
|
+
local submunitionPower = (weaponData.submunition_explosive or 1) * splash_damage_options.cluster_bomblet_damage_modifier * splash_damage_options.overall_scaling
|
|
1297
|
+
local submunitionName = weaponData.submunition_name or "unknown"
|
|
1298
|
+
--Apply bomblet reduction logic if enabled
|
|
1299
|
+
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
1300
|
+
if submunitionCount > 35 then
|
|
1301
|
+
local reductionFactor = (60 - 35) / (247 - 35)
|
|
1302
|
+
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
1303
|
+
if submunitionCount > 60 then submunitionCount = 60 end --Cap at 60
|
|
1304
|
+
end
|
|
1305
|
+
end
|
|
1306
|
+
--Extended scan with general bomblet detection
|
|
1307
|
+
timer.scheduleFunction(track_wpns_cluster_scan, {explosionPoint, wpnData.dir, wpnData.name, submunitionName, submunitionCount, submunitionPower, wpnData.speed}, timer.getTime() + 0.3)
|
|
1308
|
+
else
|
|
1309
|
+
--Standard explosion handling
|
|
1310
|
+
if splash_damage_options.larger_explosions then
|
|
1311
|
+
debugMsg("Triggering initial explosion for '" .. wpnData.name .. "' at power " .. explosionPower)
|
|
1312
|
+
trigger.action.explosion(explosionPoint, explosionPower)
|
|
1313
|
+
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
1314
|
+
debugMsg("Added to recentExplosions for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
1315
|
+
end
|
|
1316
|
+
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosionPower, isShapedCharge)
|
|
1317
|
+
end
|
|
1318
|
+
--detect_ordnance_destruction comes before recent_large_explosion_snap in original
|
|
1319
|
+
if splash_damage_options.ordnance_protection and splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
1320
|
+
timer.scheduleFunction(function(args)
|
|
1321
|
+
local explosionPoint = args[1]
|
|
1322
|
+
local blastRadius = args[2]
|
|
1323
|
+
local triggeringWeapon = args[3]
|
|
1324
|
+
local preExplosionWeapons = args[4]
|
|
1325
|
+
for id, preData in pairs(preExplosionWeapons) do
|
|
1326
|
+
if tracked_weapons[id] and not tracked_weapons[id].wpn:isExist() then
|
|
1327
|
+
if preData.distance <= blastRadius then
|
|
1328
|
+
local msg = "WARNING: " .. preData.name .. " destroyed by large explosion from " .. triggeringWeapon .. " at " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", explosionPoint.x, explosionPoint.y, explosionPoint.z)
|
|
1329
|
+
gameMsg(msg)
|
|
1330
|
+
debugMsg(msg)
|
|
1331
|
+
env.info(msg)
|
|
1332
|
+
if splash_damage_options.snap_to_ground_if_destroyed_by_large_explosion then
|
|
1333
|
+
local groundPos = {
|
|
1334
|
+
x = preData.pos.x,
|
|
1335
|
+
y = land.getHeight({x = preData.pos.x, y = preData.pos.z}),
|
|
1336
|
+
z = preData.pos.z
|
|
1337
|
+
}
|
|
1338
|
+
local destroyedWeaponPower, isShapedCharge = preData.explosive
|
|
1339
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.overall_scaling
|
|
1340
|
+
if splash_damage_options.rocket_multiplier and tracked_weapons[id].cat == Weapon.Category.ROCKET then
|
|
1341
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.rocket_multiplier
|
|
1342
|
+
end
|
|
1343
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1344
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.shaped_charge_multiplier
|
|
1345
|
+
end
|
|
1346
|
+
debugMsg("Triggering ground explosion for destroyed " .. preData.name .. " (detect_ordnance_destruction) at X: " .. string.format("%.0f", groundPos.x) .. ", Y: " .. string.format("%.0f", groundPos.y) .. ", Z: " .. string.format("%.0f", groundPos.z) .. " with power " .. destroyedWeaponPower)
|
|
1347
|
+
trigger.action.explosion(groundPos, destroyedWeaponPower)
|
|
1348
|
+
end
|
|
1349
|
+
end
|
|
1350
|
+
end
|
|
1351
|
+
end
|
|
1352
|
+
end, {explosionPoint, blastRadius, wpnData.name, preExplosionWeapons}, timer.getTime() + 0.2)
|
|
1353
|
+
end
|
|
1354
|
+
--recent_large_explosion_snap comes after main explosion and detect_ordnance_destruction
|
|
1355
|
+
if splash_damage_options.ordnance_protection and splash_damage_options.larger_explosions and splash_damage_options.recent_large_explosion_snap and splash_damage_options.snap_to_ground_if_destroyed_by_large_explosion then
|
|
1356
|
+
local currentTime = timer.getTime()
|
|
1357
|
+
for id, data in pairs(tracked_weapons) do
|
|
1358
|
+
if id ~= wpn_id_ and not data.wpn:isExist() then
|
|
1359
|
+
local terrainHeight = land.getHeight({x = data.pos.x, y = data.pos.z})
|
|
1360
|
+
local weaponHeight = data.pos.y - terrainHeight --Calculate height above ground
|
|
1361
|
+
local isMidAir = weaponHeight > 5 --Still checks if above ground
|
|
1362
|
+
local snapTriggered = false
|
|
1363
|
+
for _, explosion in ipairs(recentExplosions) do
|
|
1364
|
+
local timeDiff = currentTime - explosion.time
|
|
1365
|
+
local distance = getDistance3D(data.pos, explosion.pos)
|
|
1366
|
+
debugMsg("Checking " .. data.name .. " at X: " .. data.pos.x .. ", Y: " .. data.pos.y .. ", Z: " .. data.pos.z .. " against explosion at X: " .. explosion.pos.x .. ", Y: " .. explosion.pos.y .. ", Z: " .. explosion.pos.z .. " - Distance: " .. distance .. "m, TimeDiff: " .. timeDiff .. "s")
|
|
1367
|
+
if timeDiff <= splash_damage_options.recent_large_explosion_time and distance <= splash_damage_options.recent_large_explosion_range then
|
|
1368
|
+
if isMidAir and weaponHeight <= splash_damage_options.max_snapped_height then --New height check
|
|
1369
|
+
local groundPos = { x = data.pos.x, y = terrainHeight, z = data.pos.z }
|
|
1370
|
+
local destroyedWeaponPower, isShapedCharge = getWeaponExplosive(data.name)
|
|
1371
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.overall_scaling
|
|
1372
|
+
if splash_damage_options.rocket_multiplier and data.cat == Weapon.Category.ROCKET then
|
|
1373
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.rocket_multiplier
|
|
1374
|
+
end
|
|
1375
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1376
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.shaped_charge_multiplier
|
|
1377
|
+
end
|
|
1378
|
+
debugMsg("Weapon " .. data.name .. " detected recent large explosion within " .. splash_damage_options.recent_large_explosion_range .. "m and " .. splash_damage_options.recent_large_explosion_time .. "s, snapping to ground at X: " .. string.format("%.0f", groundPos.x) .. ", Y: " .. string.format("%.0f", groundPos.y) .. ", Z: " .. string.format("%.0f", groundPos.z) .. " with power " .. destroyedWeaponPower .. " (Height: " .. string.format("%.0f", weaponHeight) .. "m)")
|
|
1379
|
+
trigger.action.explosion(groundPos, destroyedWeaponPower)
|
|
1380
|
+
snapTriggered = true
|
|
1381
|
+
table.insert(weaponsToRemove, id)
|
|
1382
|
+
break
|
|
1383
|
+
elseif isMidAir then
|
|
1384
|
+
debugMsg("Weapon " .. data.name .. " destroyed above max_snapped_height (" .. splash_damage_options.max_snapped_height .. "m) at " .. string.format("%.0f", weaponHeight) .. "m, skipping snap")
|
|
1385
|
+
else
|
|
1386
|
+
debugMsg("Weapon " .. data.name .. " impacted ground within recent_large_explosion_range (" .. splash_damage_options.recent_large_explosion_range .. "m) and time (" .. splash_damage_options.recent_large_explosion_time .. "s), no snap needed")
|
|
1387
|
+
snapTriggered = true
|
|
1388
|
+
break
|
|
1389
|
+
end
|
|
1390
|
+
end
|
|
1391
|
+
end
|
|
1392
|
+
if not snapTriggered then
|
|
1393
|
+
if isMidAir then
|
|
1394
|
+
debugMsg("Weapon " .. data.name .. " destroyed in air, but no recent large explosion within " .. splash_damage_options.recent_large_explosion_range .. "m or " .. splash_damage_options.recent_large_explosion_time .. "s")
|
|
1395
|
+
else
|
|
1396
|
+
debugMsg("Weapon " .. data.name .. " impacted ground, not processed by recent large explosion settings")
|
|
1397
|
+
end
|
|
1398
|
+
end
|
|
1399
|
+
end
|
|
1400
|
+
end
|
|
1401
|
+
local newExplosions = {}
|
|
1402
|
+
for _, explosion in ipairs(recentExplosions) do
|
|
1403
|
+
if currentTime - explosion.time <= splash_damage_options.recent_large_explosion_time then
|
|
1404
|
+
table.insert(newExplosions, explosion)
|
|
1405
|
+
end
|
|
1406
|
+
end
|
|
1407
|
+
recentExplosions = newExplosions
|
|
1408
|
+
end
|
|
1409
|
+
--Mark units as destroyed to avoid MiST accessing them
|
|
1410
|
+
local destroyedUnits = {}
|
|
1411
|
+
for _, target in ipairs(chosenTargets) do
|
|
1412
|
+
if target.unit:isExist() and target.health > 0 and target.unit:getLife() <= 0 then
|
|
1413
|
+
destroyedUnits[target.name] = true
|
|
1414
|
+
debugMsg("Marked " .. target.name .. " as destroyed pre-impact")
|
|
1415
|
+
end
|
|
1416
|
+
end
|
|
1417
|
+
--Schedule explosion handling with original 0.1-second delay, enhanced error handling
|
|
1418
|
+
timer.scheduleFunction(function(args)
|
|
1419
|
+
local finalPos = args[1]
|
|
1420
|
+
local explosionPoint = args[2]
|
|
1421
|
+
local explosionPower = args[3]
|
|
1422
|
+
local isShapedCharge = args[4]
|
|
1423
|
+
local blastRadius = args[5]
|
|
1424
|
+
local chosenTargets = args[6]
|
|
1425
|
+
local weaponName = args[7]
|
|
1426
|
+
local wpnData = args[8]
|
|
1427
|
+
if splash_damage_options.debug then
|
|
1428
|
+
debugMsg("Starting impact handling for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1429
|
+
end
|
|
1430
|
+
local status, err = pcall(function()
|
|
1431
|
+
--Log pre-explosion targets
|
|
1432
|
+
if splash_damage_options.track_pre_explosion then
|
|
1433
|
+
if #chosenTargets > 0 then
|
|
1434
|
+
local msg = "Targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame, using finalPos):\n"
|
|
1435
|
+
for i, target in ipairs(chosenTargets) do
|
|
1436
|
+
msg = msg .. "- " .. target.name .. " (Dist: " .. string.format("%.1f", target.distance) .. "m, Health: " .. target.health .. ")\n"
|
|
1437
|
+
end
|
|
1438
|
+
debugMsg(msg)
|
|
1439
|
+
env.info("SplashDamage Pre-Explosion (Last Frame): " .. msg)
|
|
1440
|
+
else
|
|
1441
|
+
debugMsg("No targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame)")
|
|
1442
|
+
env.info("SplashDamage Pre-Explosion (Last Frame): No targets in blast zone for " .. weaponName)
|
|
1443
|
+
end
|
|
1444
|
+
end
|
|
1445
|
+
|
|
1446
|
+
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosionPower, isShapedCharge)
|
|
1447
|
+
|
|
1448
|
+
--Post-explosion analysis and queue cargo effects
|
|
1449
|
+
if splash_damage_options.track_pre_explosion then
|
|
1450
|
+
timer.scheduleFunction(function(innerArgs)
|
|
1451
|
+
local impactPoint = innerArgs[1]
|
|
1452
|
+
local blastRadius = innerArgs[2]
|
|
1453
|
+
local preExplosionTargets = innerArgs[3] or {}
|
|
1454
|
+
local weaponName = innerArgs[4]
|
|
1455
|
+
local weaponPower = innerArgs[5]
|
|
1456
|
+
if splash_damage_options.debug == true then
|
|
1457
|
+
debugMsg("Starting post-explosion analysis for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1458
|
+
end
|
|
1459
|
+
|
|
1460
|
+
--Scan all units in wider radius
|
|
1461
|
+
local postExplosionTargets = {}
|
|
1462
|
+
local volS = {
|
|
1463
|
+
id = world.VolumeType.SPHERE,
|
|
1464
|
+
params = {
|
|
1465
|
+
point = impactPoint,
|
|
1466
|
+
radius = blastRadius
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
local ifFound = function(foundObject)
|
|
1471
|
+
if foundObject:isExist() then
|
|
1472
|
+
local category = foundObject:getCategory()
|
|
1473
|
+
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
1474
|
+
category == Object.Category.STATIC then
|
|
1475
|
+
table.insert(postExplosionTargets, {
|
|
1476
|
+
name = foundObject:getTypeName(),
|
|
1477
|
+
health = foundObject:getLife() or 0,
|
|
1478
|
+
position = foundObject:getPoint(),
|
|
1479
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0
|
|
1480
|
+
})
|
|
1481
|
+
end
|
|
1482
|
+
end
|
|
1483
|
+
return true
|
|
1484
|
+
end
|
|
1485
|
+
|
|
1486
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
1487
|
+
|
|
1488
|
+
local msg = "Post-explosion analysis for " .. weaponName .. ":\n"
|
|
1489
|
+
|
|
1490
|
+
--Match pre-detected units
|
|
1491
|
+
for _, preTarget in ipairs(preExplosionTargets) do
|
|
1492
|
+
local found = false
|
|
1493
|
+
local postHealth = 0
|
|
1494
|
+
local postPosition = nil
|
|
1495
|
+
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1496
|
+
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1497
|
+
found = true
|
|
1498
|
+
postHealth = postTarget.health
|
|
1499
|
+
postPosition = postTarget.position
|
|
1500
|
+
break
|
|
1501
|
+
end
|
|
1502
|
+
end
|
|
1503
|
+
|
|
1504
|
+
local healthPercent = preTarget.maxHealth > 0 and (postHealth / preTarget.maxHealth * 100) or 0
|
|
1505
|
+
local status = ""
|
|
1506
|
+
|
|
1507
|
+
if not found or postHealth <= 0 then
|
|
1508
|
+
status = "WAS FULLY DESTROYED"
|
|
1509
|
+
elseif healthPercent < splash_damage_options.cargo_damage_threshold then
|
|
1510
|
+
status = "WAS DAMAGED BELOW THRESHOLD"
|
|
1511
|
+
else
|
|
1512
|
+
status = "SURVIVED (Health: " .. postHealth .. ")"
|
|
1513
|
+
end
|
|
1514
|
+
|
|
1515
|
+
--Always include coords in status message
|
|
1516
|
+
local coords = found and postPosition or preTarget.position
|
|
1517
|
+
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: " .. preTarget.health .. ", Post: " .. postHealth .. ")"
|
|
1518
|
+
--Check if target is in cargoUnits and within blast radius
|
|
1519
|
+
local cargoData = cargoUnits[preTarget.name]
|
|
1520
|
+
if cargoData and preTarget.distance <= blastRadius and
|
|
1521
|
+
(not found or postHealth <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1522
|
+
|
|
1523
|
+
if splash_damage_options.enable_cargo_effects then
|
|
1524
|
+
local cargoPower = cargoData.cargoExplosionPower or weaponPower --Use fixed power or fallback
|
|
1525
|
+
table.insert(cargoEffectsQueue, {
|
|
1526
|
+
name = preTarget.name,
|
|
1527
|
+
distance = preTarget.distance,
|
|
1528
|
+
coords = coords,
|
|
1529
|
+
power = cargoPower,
|
|
1530
|
+
explosion = cargoData.cargoExplosion,
|
|
1531
|
+
cookOff = cargoData.cargoCookOff,
|
|
1532
|
+
cookOffCount = cargoData.cookOffCount,
|
|
1533
|
+
cookOffPower = cargoData.cookOffPower,
|
|
1534
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
1535
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1536
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1537
|
+
isTanker = cargoData.isTanker,
|
|
1538
|
+
flameSize = cargoData.flameSize,
|
|
1539
|
+
flameDuration = cargoData.flameDuration
|
|
1540
|
+
})
|
|
1541
|
+
statusMsg = statusMsg .. " WITH CARGO EXPLOSION (Power: " .. cargoPower .. ")"
|
|
1542
|
+
if cargoData.cargoCookOff and cargoData.cookOffCount > 0 then
|
|
1543
|
+
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1544
|
+
end
|
|
1545
|
+
end
|
|
1546
|
+
end
|
|
1547
|
+
|
|
1548
|
+
msg = msg .. "- " .. preTarget.name .. " " .. statusMsg .. "\n"
|
|
1549
|
+
end
|
|
1550
|
+
--Check for additional units
|
|
1551
|
+
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1552
|
+
local isPreDetected = false
|
|
1553
|
+
for _, preTarget in ipairs(preExplosionTargets) do
|
|
1554
|
+
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1555
|
+
isPreDetected = true
|
|
1556
|
+
break
|
|
1557
|
+
end
|
|
1558
|
+
end
|
|
1559
|
+
if not isPreDetected then
|
|
1560
|
+
local coords = postTarget.position
|
|
1561
|
+
local healthPercent = postTarget.maxHealth > 0 and (postTarget.health / postTarget.maxHealth * 100) or 0
|
|
1562
|
+
local status = postTarget.health <= 0 and "WAS FULLY DESTROYED" or
|
|
1563
|
+
(healthPercent < splash_damage_options.cargo_damage_threshold and "WAS DAMAGED BELOW THRESHOLD" or
|
|
1564
|
+
"SURVIVED (Health: " .. postTarget.health .. ")")
|
|
1565
|
+
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: Unknown, Post: " .. postTarget.health .. ")"
|
|
1566
|
+
local cargoData = cargoUnits[postTarget.name]
|
|
1567
|
+
if cargoData and (postTarget.health <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1568
|
+
if splash_damage_options.enable_cargo_effects then
|
|
1569
|
+
local cargoPower = cargoData.cargoExplosionPower or weaponPower --Use fixed power or fallback
|
|
1570
|
+
local distance = getDistance(impactPoint, coords)
|
|
1571
|
+
table.insert(cargoEffectsQueue, {
|
|
1572
|
+
name = postTarget.name,
|
|
1573
|
+
distance = distance,
|
|
1574
|
+
coords = coords,
|
|
1575
|
+
power = cargoPower,
|
|
1576
|
+
explosion = cargoData.cargoExplosion,
|
|
1577
|
+
cookOff = cargoData.cargoCookOff,
|
|
1578
|
+
cookOffCount = cargoData.cookOffCount,
|
|
1579
|
+
cookOffPower = cargoData.cookOffPower,
|
|
1580
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
1581
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1582
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1583
|
+
isTanker = cargoData.isTanker,
|
|
1584
|
+
flameSize = cargoData.flameSize,
|
|
1585
|
+
flameDuration = cargoData.flameDuration
|
|
1586
|
+
})
|
|
1587
|
+
statusMsg = statusMsg .. " WITH CARGO EXPLOSION (Power: " .. cargoPower .. ")"
|
|
1588
|
+
if cargoData.cargoCookOff and cargoData.cookOffCount > 0 then
|
|
1589
|
+
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1590
|
+
end
|
|
1591
|
+
end
|
|
1592
|
+
end
|
|
1593
|
+
msg = msg .. "- " .. postTarget.name .. " " .. statusMsg .. "\n"
|
|
1594
|
+
end
|
|
1595
|
+
end
|
|
1596
|
+
|
|
1597
|
+
--Schedule all queued cargo effects
|
|
1598
|
+
if #cargoEffectsQueue > 0 then
|
|
1599
|
+
local effectIndex = 0
|
|
1600
|
+
local processedCargoUnits = {} --Track processed units
|
|
1601
|
+
local flamePositions = {} --Track flame coords with 3m radius
|
|
1602
|
+
for _, effect in ipairs(cargoEffectsQueue) do
|
|
1603
|
+
local unitKey = effect.name .. "_" .. effect.coords.x .. "_" .. effect.coords.z
|
|
1604
|
+
if not processedUnitsGlobal[unitKey] and not processedCargoUnits[unitKey] then
|
|
1605
|
+
if effect.explosion then
|
|
1606
|
+
debugMsg("Triggering cargo explosion for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. effectIndex .. "s")
|
|
1607
|
+
timer.scheduleFunction(function(params)
|
|
1608
|
+
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
1609
|
+
trigger.action.explosion(params[1], params[2])
|
|
1610
|
+
end, {effect.coords, effect.power}, timer.getTime() + effectIndex + 0.1) --Slight delay for visibility
|
|
1611
|
+
if effect.isTanker then
|
|
1612
|
+
local flameSize = effect.flameSize or 3
|
|
1613
|
+
local flameDuration = effect.flameDuration --Use cargoUnits value directly, no default
|
|
1614
|
+
local flameDensity = 1.0 --Max density for visibility
|
|
1615
|
+
local effectId = effectSmokeId
|
|
1616
|
+
effectSmokeId = effectSmokeId + 1
|
|
1617
|
+
--Check for nearby flames within 3m
|
|
1618
|
+
local isDuplicate = false
|
|
1619
|
+
for _, pos in pairs(flamePositions) do
|
|
1620
|
+
if getDistance3D(effect.coords, pos) < 3 then
|
|
1621
|
+
isDuplicate = true
|
|
1622
|
+
debugMsg("Skipping duplicate flame for " .. effect.name .. " near X: " .. string.format("%.0f", pos.x) .. ", Y: " .. string.format("%.0f", pos.y) .. ", Z: " .. string.format("%.0f", pos.z))
|
|
1623
|
+
break
|
|
1624
|
+
end
|
|
1625
|
+
end
|
|
1626
|
+
if not isDuplicate then
|
|
1627
|
+
debugMsg("Adding flame effect for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m (Size: " .. flameSize .. ", Duration: " .. flameDuration .. "s, ID: " .. effectId .. ") scheduled at " .. effectIndex .. "s")
|
|
1628
|
+
timer.scheduleFunction(function(params)
|
|
1629
|
+
--Adjust Y-coordinate to terrain height + offset
|
|
1630
|
+
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
1631
|
+
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
1632
|
+
debugMsg("Spawning flame effect at X: " .. string.format("%.0f", adjustedCoords.x) .. ", Y: " .. string.format("%.0f", adjustedCoords.y) .. ", Z: " .. string.format("%.0f", adjustedCoords.z))
|
|
1633
|
+
trigger.action.explosion(adjustedCoords, 10) --Small explosion to force visibility
|
|
1634
|
+
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
1635
|
+
end, {effect.coords, flameSize, flameDensity, effectId}, timer.getTime() + effectIndex + 0.2) --Slight delay
|
|
1636
|
+
timer.scheduleFunction(function(id)
|
|
1637
|
+
debugMsg("Stopping flame effect for " .. effect.name .. " (ID: " .. id .. ")")
|
|
1638
|
+
trigger.action.effectSmokeStop(id)
|
|
1639
|
+
end, effectId, timer.getTime() + effectIndex + flameDuration + 0.2)
|
|
1640
|
+
table.insert(flamePositions, effect.coords)
|
|
1641
|
+
end
|
|
1642
|
+
end
|
|
1643
|
+
end
|
|
1644
|
+
debugMsg("Checking cook-off for " .. effect.name .. ": cookOff=" .. tostring(effect.cookOff) .. ", count=" .. tostring(effect.cookOffCount))
|
|
1645
|
+
if effect.cookOff and effect.cookOffCount > 0 then
|
|
1646
|
+
debugMsg("Scheduling " .. effect.cookOffCount .. " cook-off explosions for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m over " .. effect.cookOffDuration .. "s starting at " .. effectIndex .. "s")
|
|
1647
|
+
for i = 1, effect.cookOffCount do
|
|
1648
|
+
local delay = effect.cookOffRandomTiming and math.random() * effect.cookOffDuration or (i - 1) * (effect.cookOffDuration / effect.cookOffCount)
|
|
1649
|
+
local basePower = effect.cookOffPower
|
|
1650
|
+
local powerVariation = effect.cookOffPowerRandom / 100
|
|
1651
|
+
local cookOffPower = effect.cookOffPowerRandom == 0 and basePower or basePower * (1 + powerVariation * (math.random() * 2 - 1))
|
|
1652
|
+
debugMsg("Cook-off #" .. i .. " for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m scheduled at " .. string.format("%.3f", delay) .. "s with power " .. string.format("%.2f", cookOffPower))
|
|
1653
|
+
timer.scheduleFunction(function(params)
|
|
1654
|
+
local pos = params[1]
|
|
1655
|
+
local power = params[2]
|
|
1656
|
+
debugMsg("Executing cook-off at " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", pos.x, pos.y, pos.z) .. " with power " .. power)
|
|
1657
|
+
trigger.action.explosion(pos, power)
|
|
1658
|
+
end, {effect.coords, cookOffPower}, timer.getTime() + effectIndex + delay)
|
|
1659
|
+
end
|
|
1660
|
+
--Debris burst only if cook-off is true and enabled
|
|
1661
|
+
if splash_damage_options.debris_effects then
|
|
1662
|
+
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
1663
|
+
for j = 1, debrisCount do
|
|
1664
|
+
--Random spherical offset
|
|
1665
|
+
local theta = math.random() * 2 * math.pi --Horizontal angle
|
|
1666
|
+
local phi = math.acos(math.random() * 2 - 1) --Vertical angle for sphere
|
|
1667
|
+
local minDist = splash_damage_options.debris_max_distance * 0.1 --10% of max
|
|
1668
|
+
local maxDist = splash_damage_options.debris_max_distance
|
|
1669
|
+
local r = math.random() * (maxDist - minDist) + minDist --10% to full max distance
|
|
1670
|
+
local debrisX = effect.coords.x + r * math.sin(phi) * math.cos(theta)
|
|
1671
|
+
local debrisZ = effect.coords.z + r * math.sin(phi) * math.sin(theta)
|
|
1672
|
+
local terrainY = land.getHeight({x = debrisX, y = debrisZ})
|
|
1673
|
+
local debrisY = terrainY + math.random() * maxDist --0 to max_distance above ground
|
|
1674
|
+
local debrisPos = {x = debrisX, y = debrisY, z = debrisZ}
|
|
1675
|
+
local debrisPower = splash_damage_options.debris_power
|
|
1676
|
+
local debrisDelay = (j - 1) * (effect.cookOffDuration / debrisCount) --Spread over cook-off duration
|
|
1677
|
+
timer.scheduleFunction(function(debrisArgs)
|
|
1678
|
+
local dPos = debrisArgs[1]
|
|
1679
|
+
local dPower = debrisArgs[2]
|
|
1680
|
+
debugMsg("Debris explosion at X: " .. string.format("%.0f", dPos.x) .. ", Y: " .. string.format("%.0f", dPos.y) .. ", Z: " .. string.format("%.0f", dPos.z) .. " with power " .. dPower)
|
|
1681
|
+
trigger.action.explosion(dPos, dPower)
|
|
1682
|
+
end, {debrisPos, debrisPower}, timer.getTime() + effectIndex + debrisDelay)
|
|
1683
|
+
end
|
|
1684
|
+
end
|
|
1685
|
+
end
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
processedCargoUnits[unitKey] = true
|
|
1689
|
+
processedUnitsGlobal[unitKey] = true
|
|
1690
|
+
effectIndex = effectIndex + 3 --3 secs spacing if not random
|
|
1691
|
+
end
|
|
1692
|
+
end
|
|
1693
|
+
--Clear the queue after scheduling
|
|
1694
|
+
cargoEffectsQueue = {}
|
|
1695
|
+
end
|
|
1696
|
+
|
|
1697
|
+
debugMsg(msg)
|
|
1698
|
+
env.info("SplashDamage Post-Explosion: " .. msg)
|
|
1699
|
+
end, {finalPos, blastRadius, chosenTargets, weaponName, explosionPower}, timer.getTime() + 1)
|
|
1700
|
+
end
|
|
1701
|
+
end)
|
|
1702
|
+
if not status then
|
|
1703
|
+
debugMsg("Impact handling error for '" .. weaponName .. "': " .. err)
|
|
1704
|
+
end
|
|
1705
|
+
end, {explosionPoint, explosionPoint, explosionPower, isShapedCharge, blastRadius, chosenTargets, wpnData.name, wpnData}, timer.getTime() + 0.1)
|
|
1706
|
+
else
|
|
1707
|
+
debugMsg("Explosion skipped due to ordnance protection for '" .. wpnData.name .. "'")
|
|
1708
|
+
if splash_damage_options.larger_explosions then
|
|
1709
|
+
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
1710
|
+
debugMsg("Skipped explosion logged for snap check for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
1711
|
+
end
|
|
1712
|
+
end
|
|
1713
|
+
table.insert(weaponsToRemove, wpn_id_)
|
|
1714
|
+
end
|
|
1715
|
+
end)
|
|
1716
|
+
if not status then
|
|
1717
|
+
debugMsg("Error in track_wpns for '" .. (wpnData.name or "unknown weapon") .. "': " .. err)
|
|
1718
|
+
end
|
|
1719
|
+
end
|
|
1720
|
+
--Perform all removals after iteration
|
|
1721
|
+
for _, id in ipairs(weaponsToRemove) do
|
|
1722
|
+
tracked_weapons[id] = nil
|
|
1723
|
+
end
|
|
1724
|
+
return timer.getTime() + refreshRate
|
|
1725
|
+
end
|
|
1726
|
+
function onWpnEvent(event)
|
|
1727
|
+
if event.id == world.event.S_EVENT_SHOT then
|
|
1728
|
+
if event.weapon then
|
|
1729
|
+
local ordnance = event.weapon
|
|
1730
|
+
local typeName = trim(ordnance:getTypeName())
|
|
1731
|
+
if splash_damage_options.debug then
|
|
1732
|
+
env.info("Weapon fired: [" .. typeName .. "]")
|
|
1733
|
+
debugMsg("Weapon fired: [" .. typeName .. "]")
|
|
1734
|
+
end
|
|
1735
|
+
if string.find(typeName, "weapons.shells") then
|
|
1736
|
+
if splash_damage_options.debug then
|
|
1737
|
+
debugMsg("Event shot, but not tracking: " .. typeName)
|
|
1738
|
+
env.info("SplashDamage: event shot, but not tracking: " .. typeName .. " (" .. event.initiator:getTypeName() .. ")")
|
|
1739
|
+
end
|
|
1740
|
+
return
|
|
1741
|
+
end
|
|
1742
|
+
|
|
1743
|
+
--Check if weapon is in explTable before tracking
|
|
1744
|
+
if not explTable[typeName] then
|
|
1745
|
+
env.info("SplashDamage: " .. typeName .. " missing from script (" .. event.initiator:getTypeName() .. ")")
|
|
1746
|
+
if splash_damage_options.weapon_missing_message == true then
|
|
1747
|
+
trigger.action.outText("SplashDamage: " .. typeName .. " missing from script (" .. (event.initiator and event.initiator:isExist() and event.initiator:getTypeName() or "no initiator") .. ")", 3)
|
|
1748
|
+
-- if mist and mist.utils and mist.utils.tableShow then --Only if MiST is present
|
|
1749
|
+
-- local success, desc = pcall(mist.utils.tableShow, ordnance:getDesc())
|
|
1750
|
+
-- if success then
|
|
1751
|
+
-- debugMsg("desc for [" .. typeName .. "]: " .. desc)
|
|
1752
|
+
-- else
|
|
1753
|
+
-- debugMsg("Could not retrieve description for [" .. typeName .. "]. Object may no longer exist.")
|
|
1754
|
+
-- end
|
|
1755
|
+
-- end
|
|
1756
|
+
env.info("Current keys in explTable:")
|
|
1757
|
+
for k, v in pairs(explTable) do
|
|
1758
|
+
env.info("Key: [" .. k .. "]")
|
|
1759
|
+
end
|
|
1760
|
+
end
|
|
1761
|
+
return --Skip tracking this weapon since its not in the table
|
|
1762
|
+
end
|
|
1763
|
+
|
|
1764
|
+
if (ordnance:getDesc().category ~= 0) and event.initiator then
|
|
1765
|
+
if ordnance:getDesc().category == 1 then
|
|
1766
|
+
if (ordnance:getDesc().MissileCategory ~= 1 and ordnance:getDesc().MissileCategory ~= 2) then
|
|
1767
|
+
tracked_weapons[event.weapon.id_] = { wpn = ordnance, init = event.initiator:getName(), pos = ordnance:getPoint(), dir = ordnance:getPosition().x, name = typeName, speed = ordnance:getVelocity(), cat = ordnance:getCategory() }
|
|
1768
|
+
end
|
|
1769
|
+
else
|
|
1770
|
+
tracked_weapons[event.weapon.id_] = { wpn = ordnance, init = event.initiator:getName(), pos = ordnance:getPoint(), dir = ordnance:getPosition().x, name = typeName, speed = ordnance:getVelocity(), cat = ordnance:getCategory() }
|
|
1771
|
+
end
|
|
1772
|
+
end
|
|
1773
|
+
end
|
|
1774
|
+
end
|
|
1775
|
+
end
|
|
1776
|
+
|
|
1777
|
+
local function protectedCall(...)
|
|
1778
|
+
local status, retval = pcall(...)
|
|
1779
|
+
if not status then
|
|
1780
|
+
env.warning("Splash damage script error... gracefully caught! " .. retval, true)
|
|
1781
|
+
end
|
|
1782
|
+
end
|
|
1783
|
+
|
|
1784
|
+
function WpnHandler:onEvent(event)
|
|
1785
|
+
protectedCall(onWpnEvent, event)
|
|
1786
|
+
end
|
|
1787
|
+
|
|
1788
|
+
function explodeObject(args)
|
|
1789
|
+
local point = args[1]
|
|
1790
|
+
local distance = args[2]
|
|
1791
|
+
local power = args[3]
|
|
1792
|
+
trigger.action.explosion(point, power)
|
|
1793
|
+
end
|
|
1794
|
+
|
|
1795
|
+
function blastWave(_point, _radius, weapon, power, isShapedCharge)
|
|
1796
|
+
if isShapedCharge then
|
|
1797
|
+
_radius = _radius * splash_damage_options.shaped_charge_multiplier
|
|
1798
|
+
end
|
|
1799
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
1800
|
+
local dynamicRadius = math.pow(power, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1801
|
+
if isShapedCharge then
|
|
1802
|
+
_radius = dynamicRadius * splash_damage_options.shaped_charge_multiplier
|
|
1803
|
+
else
|
|
1804
|
+
_radius = dynamicRadius
|
|
1805
|
+
end
|
|
1806
|
+
end
|
|
1807
|
+
|
|
1808
|
+
local foundUnits = {}
|
|
1809
|
+
local volS = {
|
|
1810
|
+
id = world.VolumeType.SPHERE,
|
|
1811
|
+
params = {
|
|
1812
|
+
point = _point,
|
|
1813
|
+
radius = _radius
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
local ifFound = function(foundObject, val)
|
|
1818
|
+
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT and foundObject:getCategory() == Object.Category.UNIT then
|
|
1819
|
+
foundUnits[#foundUnits + 1] = foundObject
|
|
1820
|
+
end
|
|
1821
|
+
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1822
|
+
if splash_damage_options.blast_stun == true then
|
|
1823
|
+
--suppressUnit(foundObject, 2, weapon)
|
|
1824
|
+
end
|
|
1825
|
+
end
|
|
1826
|
+
if splash_damage_options.wave_explosions then
|
|
1827
|
+
local obj = foundObject
|
|
1828
|
+
local obj_location = obj:getPoint()
|
|
1829
|
+
local dist = getDistance(_point, obj_location)
|
|
1830
|
+
local timing = dist / 500
|
|
1831
|
+
if obj:isExist() and tableHasKey(obj:getDesc(), "box") then
|
|
1832
|
+
local length = (obj:getDesc().box.max.x + math.abs(obj:getDesc().box.min.x))
|
|
1833
|
+
local height = (obj:getDesc().box.max.y + math.abs(obj:getDesc().box.min.y))
|
|
1834
|
+
local depth = (obj:getDesc().box.max.z + math.abs(obj:getDesc().box.min.z))
|
|
1835
|
+
local _length = length
|
|
1836
|
+
local _depth = depth
|
|
1837
|
+
if depth > length then
|
|
1838
|
+
_length = depth
|
|
1839
|
+
_depth = length
|
|
1840
|
+
end
|
|
1841
|
+
local surface_distance = dist - _depth / 2
|
|
1842
|
+
local scaled_power_factor = 0.006 * power + 1
|
|
1843
|
+
local intensity = (power * scaled_power_factor) / (4 * math.pi * surface_distance^2)
|
|
1844
|
+
local surface_area = _length * height
|
|
1845
|
+
local damage_for_surface = intensity * surface_area
|
|
1846
|
+
if damage_for_surface > splash_damage_options.cascade_damage_threshold then
|
|
1847
|
+
local explosion_size = damage_for_surface
|
|
1848
|
+
if obj:getDesc().category == Unit.Category.STRUCTURE then
|
|
1849
|
+
explosion_size = intensity * splash_damage_options.static_damage_boost
|
|
1850
|
+
end
|
|
1851
|
+
if explosion_size > power then explosion_size = power end
|
|
1852
|
+
local triggerExplosion = false
|
|
1853
|
+
if splash_damage_options.always_cascade_explode then
|
|
1854
|
+
triggerExplosion = true
|
|
1855
|
+
else
|
|
1856
|
+
if obj:getDesc().life then
|
|
1857
|
+
local healthPercent = (obj:getLife() / obj:getDesc().life) * 100
|
|
1858
|
+
if healthPercent <= splash_damage_options.cascade_explode_threshold then
|
|
1859
|
+
triggerExplosion = true
|
|
1860
|
+
end
|
|
1861
|
+
--Queue cargo effects for units below
|
|
1862
|
+
local cargoData = cargoUnits[obj:getTypeName()]
|
|
1863
|
+
if cargoData and healthPercent <= splash_damage_options.cargo_damage_threshold and splash_damage_options.enable_cargo_effects then
|
|
1864
|
+
local cargoPower = power * cargoData.cargoExplosionMult
|
|
1865
|
+
table.insert(cargoEffectsQueue, {
|
|
1866
|
+
name = obj:getTypeName(),
|
|
1867
|
+
distance = dist,
|
|
1868
|
+
coords = obj_location,
|
|
1869
|
+
power = cargoPower,
|
|
1870
|
+
explosion = cargoData.cargoExplosion,
|
|
1871
|
+
cookOff = cargoData.cargoCookOff,
|
|
1872
|
+
cookOffCount = cargoData.cookOffCount,
|
|
1873
|
+
cookOffPower = cargoData.cookOffPower,
|
|
1874
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
1875
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1876
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1877
|
+
isTanker = cargoData.isTanker,
|
|
1878
|
+
flameSize = cargoData.flameSize,
|
|
1879
|
+
flameDuration = cargoData.flameDuration
|
|
1880
|
+
})
|
|
1881
|
+
end
|
|
1882
|
+
else
|
|
1883
|
+
triggerExplosion = true
|
|
1884
|
+
end
|
|
1885
|
+
if not triggerExplosion and obj:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1886
|
+
local health = obj:getLife() or 0
|
|
1887
|
+
if health <= 0 then
|
|
1888
|
+
triggerExplosion = true
|
|
1889
|
+
end
|
|
1890
|
+
end
|
|
1891
|
+
end
|
|
1892
|
+
if triggerExplosion then
|
|
1893
|
+
timer.scheduleFunction(explodeObject, {obj_location, dist, explosion_size * splash_damage_options.cascade_scaling}, timer.getTime() + timing)
|
|
1894
|
+
end
|
|
1895
|
+
end
|
|
1896
|
+
end
|
|
1897
|
+
end
|
|
1898
|
+
return true
|
|
1899
|
+
end
|
|
1900
|
+
|
|
1901
|
+
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
|
1902
|
+
world.searchObjects(Object.Category.STATIC, volS, ifFound)
|
|
1903
|
+
world.searchObjects(Object.Category.SCENERY, volS, ifFound)
|
|
1904
|
+
world.searchObjects(Object.Category.CARGO, volS, ifFound)
|
|
1905
|
+
if splash_damage_options.damage_model then
|
|
1906
|
+
timer.scheduleFunction(modelUnitDamage, foundUnits, timer.getTime() + 1.5)
|
|
1907
|
+
end
|
|
1908
|
+
end
|
|
1909
|
+
|
|
1910
|
+
function modelUnitDamage(units)
|
|
1911
|
+
for i, unit in ipairs(units) do
|
|
1912
|
+
if unit:isExist() then
|
|
1913
|
+
local health = (unit:getLife() / unit:getDesc().life) * 100
|
|
1914
|
+
if unit:hasAttribute("Infantry") and health > 0 then
|
|
1915
|
+
if health <= splash_damage_options.infantry_cant_fire_health then
|
|
1916
|
+
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1917
|
+
end
|
|
1918
|
+
end
|
|
1919
|
+
if unit:getDesc().category == Unit.Category.GROUND_UNIT and (not unit:hasAttribute("Infantry")) and health > 0 then
|
|
1920
|
+
if health <= splash_damage_options.unit_cant_fire_health then
|
|
1921
|
+
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1922
|
+
gameMsg(unit:getTypeName() .. " weapons disabled")
|
|
1923
|
+
end
|
|
1924
|
+
if health <= splash_damage_options.unit_disabled_health and health > 0 then
|
|
1925
|
+
unit:getController():setTask({id = 'Hold', params = {}})
|
|
1926
|
+
unit:getController():setOnOff(false)
|
|
1927
|
+
gameMsg(unit:getTypeName() .. " disabled")
|
|
1928
|
+
end
|
|
1929
|
+
end
|
|
1930
|
+
end
|
|
1931
|
+
end
|
|
1932
|
+
end
|
|
1933
|
+
|
|
1934
|
+
function updateSplashDamageSetting(setting, increment)
|
|
1935
|
+
if not splash_damage_options[setting] then
|
|
1936
|
+
env.info("Error: Setting " .. setting .. " does not exist.")
|
|
1937
|
+
return
|
|
1938
|
+
end
|
|
1939
|
+
|
|
1940
|
+
local newValue = math.max(0, splash_damage_options[setting] + increment)
|
|
1941
|
+
env.info("Updating " .. setting .. " from " .. tostring(splash_damage_options[setting]) .. " to " .. tostring(newValue))
|
|
1942
|
+
splash_damage_options[setting] = newValue
|
|
1943
|
+
trigger.action.outText("Updated " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1944
|
+
end
|
|
1945
|
+
|
|
1946
|
+
function toggleSplashDamageSetting(setting)
|
|
1947
|
+
splash_damage_options[setting] = not splash_damage_options[setting]
|
|
1948
|
+
trigger.action.outText("Toggled " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1949
|
+
|
|
1950
|
+
if setting == "enable_radio_menu" then
|
|
1951
|
+
if splash_damage_options.enable_radio_menu then
|
|
1952
|
+
addSplashDamageMenu()
|
|
1953
|
+
else
|
|
1954
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
1955
|
+
splash_damage_menu = nil
|
|
1956
|
+
end
|
|
1957
|
+
end
|
|
1958
|
+
end
|
|
1959
|
+
|
|
1960
|
+
function addValueAdjustmentCommands(menu, setting)
|
|
1961
|
+
missionCommands.addCommand("+0.1", menu, updateSplashDamageSetting, setting, 0.1)
|
|
1962
|
+
missionCommands.addCommand("+1", menu, updateSplashDamageSetting, setting, 1)
|
|
1963
|
+
missionCommands.addCommand("+10", menu, updateSplashDamageSetting, setting, 10)
|
|
1964
|
+
missionCommands.addCommand("+100", menu, updateSplashDamageSetting, setting, 100)
|
|
1965
|
+
|
|
1966
|
+
missionCommands.addCommand("-0.1", menu, updateSplashDamageSetting, setting, -0.1)
|
|
1967
|
+
missionCommands.addCommand("-1", menu, updateSplashDamageSetting, setting, -1)
|
|
1968
|
+
missionCommands.addCommand("-10", menu, updateSplashDamageSetting, setting, -10)
|
|
1969
|
+
missionCommands.addCommand("-100", menu, updateSplashDamageSetting, setting, -100)
|
|
1970
|
+
end
|
|
1971
|
+
|
|
1972
|
+
function exitSplashDamageMenu()
|
|
1973
|
+
if splash_damage_menu then
|
|
1974
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
1975
|
+
splash_damage_menu = nil
|
|
1976
|
+
end
|
|
1977
|
+
end
|
|
1978
|
+
|
|
1979
|
+
function addSplashDamageMenu()
|
|
1980
|
+
if not splash_damage_options.enable_radio_menu then return end
|
|
1981
|
+
|
|
1982
|
+
if splash_damage_menu then
|
|
1983
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1986
|
+
splash_damage_menu = missionCommands.addSubMenu("Splash Damage Settings")
|
|
1987
|
+
|
|
1988
|
+
--Page 1: Debug & General Settings
|
|
1989
|
+
local debugGeneralMenu = missionCommands.addSubMenu("Debug & General Settings", splash_damage_menu)
|
|
1990
|
+
missionCommands.addCommand("Toggle Game Messages", debugGeneralMenu, toggleSplashDamageSetting, "game_messages")
|
|
1991
|
+
missionCommands.addCommand("Toggle Debug Messages", debugGeneralMenu, toggleSplashDamageSetting, "debug")
|
|
1992
|
+
missionCommands.addCommand("Toggle Weapon Missing Messages", debugGeneralMenu, toggleSplashDamageSetting, "weapon_missing_message")
|
|
1993
|
+
missionCommands.addCommand("Toggle Pre-Explosion Debug", debugGeneralMenu, toggleSplashDamageSetting, "track_pre_explosion_debug")
|
|
1994
|
+
missionCommands.addCommand("Toggle Damage Model", debugGeneralMenu, toggleSplashDamageSetting, "damage_model")
|
|
1995
|
+
missionCommands.addCommand("Toggle Blast Stun", debugGeneralMenu, toggleSplashDamageSetting, "blast_stun")
|
|
1996
|
+
local unitDisabledMenu = missionCommands.addSubMenu("Unit Disabled Health", debugGeneralMenu)
|
|
1997
|
+
addValueAdjustmentCommands(unitDisabledMenu, "unit_disabled_health")
|
|
1998
|
+
local unitCantFireMenu = missionCommands.addSubMenu("Unit Cant Fire Health", debugGeneralMenu)
|
|
1999
|
+
addValueAdjustmentCommands(unitCantFireMenu, "unit_cant_fire_health")
|
|
2000
|
+
local infantryCantFireMenu = missionCommands.addSubMenu("Infantry Cant Fire Health", debugGeneralMenu)
|
|
2001
|
+
addValueAdjustmentCommands(infantryCantFireMenu, "infantry_cant_fire_health")
|
|
2002
|
+
local rocketMultiplierMenu = missionCommands.addSubMenu("Rocket Multiplier", debugGeneralMenu)
|
|
2003
|
+
addValueAdjustmentCommands(rocketMultiplierMenu, "rocket_multiplier")
|
|
2004
|
+
--Page 2/3: Explosions
|
|
2005
|
+
local explosionCargoMenu = missionCommands.addSubMenu("Explosion Settings", splash_damage_menu)
|
|
2006
|
+
local staticDamageMenu = missionCommands.addSubMenu("Static Damage Boost", explosionCargoMenu)
|
|
2007
|
+
addValueAdjustmentCommands(staticDamageMenu, "static_damage_boost")
|
|
2008
|
+
missionCommands.addCommand("Toggle Wave Explosions", explosionCargoMenu, toggleSplashDamageSetting, "wave_explosions")
|
|
2009
|
+
missionCommands.addCommand("Toggle Larger Explosions", explosionCargoMenu, toggleSplashDamageSetting, "larger_explosions")
|
|
2010
|
+
local blastRadiusMenu = missionCommands.addSubMenu("Blast Search Radius", explosionCargoMenu)
|
|
2011
|
+
addValueAdjustmentCommands(blastRadiusMenu, "blast_search_radius")
|
|
2012
|
+
|
|
2013
|
+
local overallScalingMenu = missionCommands.addSubMenu("Overall Scaling", explosionCargoMenu)
|
|
2014
|
+
addValueAdjustmentCommands(overallScalingMenu, "overall_scaling")
|
|
2015
|
+
missionCommands.addCommand("Toggle Shaped Charge Effects", explosionCargoMenu, toggleSplashDamageSetting, "apply_shaped_charge_effects")
|
|
2016
|
+
local shapedChargeMenu = missionCommands.addSubMenu("Shaped Charge Multiplier", explosionCargoMenu)
|
|
2017
|
+
addValueAdjustmentCommands(shapedChargeMenu, "shaped_charge_multiplier")
|
|
2018
|
+
missionCommands.addCommand("Toggle Dynamic Blast Radius", explosionCargoMenu, toggleSplashDamageSetting, "use_dynamic_blast_radius")
|
|
2019
|
+
local dynamicBlastMenu = missionCommands.addSubMenu("Dynamic Blast Radius Modifier", explosionCargoMenu)
|
|
2020
|
+
addValueAdjustmentCommands(dynamicBlastMenu, "dynamic_blast_radius_modifier")
|
|
2021
|
+
|
|
2022
|
+
local explosionCargoMenu = missionCommands.addSubMenu("Cascade Settings", splash_damage_menu)
|
|
2023
|
+
local cascadeScalingMenu = missionCommands.addSubMenu("Cascade Scaling", explosionCargoMenu)
|
|
2024
|
+
addValueAdjustmentCommands(cascadeScalingMenu, "cascade_scaling")
|
|
2025
|
+
local cascadeExplodeThresholdMenu = missionCommands.addSubMenu("Cascade Explode Threshold", explosionCargoMenu)
|
|
2026
|
+
addValueAdjustmentCommands(cascadeExplodeThresholdMenu, "cascade_explode_threshold")
|
|
2027
|
+
local cascadeThresholdMenu = missionCommands.addSubMenu("Cascade Damage Threshold", explosionCargoMenu)
|
|
2028
|
+
addValueAdjustmentCommands(cascadeThresholdMenu, "cascade_damage_threshold")
|
|
2029
|
+
|
|
2030
|
+
--Page 4: Cargo and Ordnance Protection
|
|
2031
|
+
local explosionCargoMenu = missionCommands.addSubMenu("Cargo and Ordnance", splash_damage_menu)
|
|
2032
|
+
missionCommands.addCommand("Toggle Always Cascade Explode", explosionCargoMenu, toggleSplashDamageSetting, "always_cascade_explode")
|
|
2033
|
+
missionCommands.addCommand("Toggle Tracking & Cargo Effects", explosionCargoMenu, toggleSplashDamageSetting, "track_pre_explosion")
|
|
2034
|
+
local cargoThresholdMenu = missionCommands.addSubMenu("Cargo Damage Threshold", explosionCargoMenu)
|
|
2035
|
+
addValueAdjustmentCommands(cargoThresholdMenu, "cargo_damage_threshold")
|
|
2036
|
+
missionCommands.addCommand("Toggle Ordnance Protection", explosionCargoMenu, toggleSplashDamageSetting, "ordnance_protection")
|
|
2037
|
+
local ordnanceRadiusMenu = missionCommands.addSubMenu("Ordnance Protection Radius", explosionCargoMenu)
|
|
2038
|
+
addValueAdjustmentCommands(ordnanceRadiusMenu, "ordnance_protection_radius")
|
|
2039
|
+
missionCommands.addCommand("Toggle Snap To Ground If Destroyed By LE", explosionCargoMenu, toggleSplashDamageSetting, "snap_to_ground_if_destroyed_by_large_explosion")
|
|
2040
|
+
local ordnanceRadiusMenu = missionCommands.addSubMenu("Ordnance Protection Radius", explosionCargoMenu)
|
|
2041
|
+
local cargoThresholdMenu = missionCommands.addSubMenu("Max Snap Height", explosionCargoMenu)
|
|
2042
|
+
addValueAdjustmentCommands(cargoThresholdMenu, "max_snapped_height")
|
|
2043
|
+
missionCommands.addCommand("Toggle Recent Expl Track Snap", explosionCargoMenu, toggleSplashDamageSetting, "recent_large_explosion_snap")
|
|
2044
|
+
|
|
2045
|
+
|
|
2046
|
+
--Page 5: Debris Settings
|
|
2047
|
+
local debrisMenu = missionCommands.addSubMenu("Debris Settings", splash_damage_menu)
|
|
2048
|
+
missionCommands.addCommand("Toggle Debris Effects", debrisMenu, toggleSplashDamageSetting, "debris_effects")
|
|
2049
|
+
local debrisCountMinMenu = missionCommands.addSubMenu("Min Debris Count", debrisMenu)
|
|
2050
|
+
addValueAdjustmentCommands(debrisCountMinMenu, "debris_count_min")
|
|
2051
|
+
local debrisCountMaxMenu = missionCommands.addSubMenu("Max Debris Count", debrisMenu)
|
|
2052
|
+
addValueAdjustmentCommands(debrisCountMaxMenu, "debris_count_max")
|
|
2053
|
+
local debrisDistanceMenu = missionCommands.addSubMenu("Max Debris Distance", debrisMenu)
|
|
2054
|
+
addValueAdjustmentCommands(debrisDistanceMenu, "debris_max_distance")
|
|
2055
|
+
local debrisPowerMenu = missionCommands.addSubMenu("Debris Power", debrisMenu)
|
|
2056
|
+
addValueAdjustmentCommands(debrisPowerMenu, "debris_power")
|
|
2057
|
+
|
|
2058
|
+
--Page 6: Cluster Settings
|
|
2059
|
+
local clusterMenu = missionCommands.addSubMenu("Cluster Settings", splash_damage_menu)
|
|
2060
|
+
missionCommands.addCommand("Toggle Cluster Enabled", clusterMenu, toggleSplashDamageSetting, "cluster_enabled")
|
|
2061
|
+
local clusterBaseLengthMenu = missionCommands.addSubMenu("Cluster Base Length", clusterMenu)
|
|
2062
|
+
addValueAdjustmentCommands(clusterBaseLengthMenu, "cluster_base_length")
|
|
2063
|
+
local clusterBaseWidthMenu = missionCommands.addSubMenu("Cluster Base Width", clusterMenu)
|
|
2064
|
+
addValueAdjustmentCommands(clusterBaseWidthMenu, "cluster_base_width")
|
|
2065
|
+
local clusterMaxLengthMenu = missionCommands.addSubMenu("Cluster Max Length", clusterMenu)
|
|
2066
|
+
addValueAdjustmentCommands(clusterMaxLengthMenu, "cluster_max_length")
|
|
2067
|
+
local clusterMaxWidthMenu = missionCommands.addSubMenu("Cluster Max Width", clusterMenu)
|
|
2068
|
+
addValueAdjustmentCommands(clusterMaxWidthMenu, "cluster_max_width")
|
|
2069
|
+
local clusterMinLengthMenu = missionCommands.addSubMenu("Cluster Min Length", clusterMenu)
|
|
2070
|
+
addValueAdjustmentCommands(clusterMinLengthMenu, "cluster_min_length")
|
|
2071
|
+
local clusterMinWidthMenu = missionCommands.addSubMenu("Cluster Min Width", clusterMenu)
|
|
2072
|
+
addValueAdjustmentCommands(clusterMinWidthMenu, "cluster_min_width")
|
|
2073
|
+
missionCommands.addCommand("Toggle Bomblet Reduction Modifier", clusterMenu, toggleSplashDamageSetting, "cluster_bomblet_reductionmodifier")
|
|
2074
|
+
local clusterBombletDamageMenu = missionCommands.addSubMenu("Bomblet Damage Modifier", clusterMenu)
|
|
2075
|
+
addValueAdjustmentCommands(clusterBombletDamageMenu, "cluster_bomblet_damage_modifier")
|
|
2076
|
+
|
|
2077
|
+
--Page 7: Giant Explosion Settings
|
|
2078
|
+
local giantExplosionMenu = missionCommands.addSubMenu("Giant Explosion Settings", splash_damage_menu)
|
|
2079
|
+
missionCommands.addCommand("Toggle Giant Explosion", giantExplosionMenu, toggleSplashDamageSetting, "giant_explosion_enabled")
|
|
2080
|
+
missionCommands.addCommand("Toggle Static Target", giantExplosionMenu, toggleSplashDamageSetting, "giant_explosion_target_static")
|
|
2081
|
+
for name, target in pairs(giantExplosionTargets) do
|
|
2082
|
+
local displayName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
2083
|
+
missionCommands.addCommand("Detonate " .. displayName, giantExplosionMenu, function()
|
|
2084
|
+
trigger.action.setUserFlag(displayName, 1)
|
|
2085
|
+
end)
|
|
2086
|
+
end
|
|
2087
|
+
missionCommands.addCommand("Detonate All Giant Targets", giantExplosionMenu, function()
|
|
2088
|
+
for name, target in pairs(giantExplosionTargets) do
|
|
2089
|
+
local flagName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
2090
|
+
trigger.action.setUserFlag(flagName, 1)
|
|
2091
|
+
end
|
|
2092
|
+
end)
|
|
2093
|
+
local powerMenu = missionCommands.addSubMenu("Explosion Power", giantExplosionMenu)
|
|
2094
|
+
addValueAdjustmentCommands(powerMenu, "giant_explosion_power")
|
|
2095
|
+
local scaleMenu = missionCommands.addSubMenu("Size Scale", giantExplosionMenu)
|
|
2096
|
+
missionCommands.addCommand("+0.1", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", 0.1)
|
|
2097
|
+
missionCommands.addCommand("+0.5", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", 0.5)
|
|
2098
|
+
missionCommands.addCommand("-0.1", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", -0.1)
|
|
2099
|
+
missionCommands.addCommand("-0.5", scaleMenu, updateSplashDamageSetting, "giant_explosion_scale", -0.5)
|
|
2100
|
+
local durationMenu = missionCommands.addSubMenu("Duration", giantExplosionMenu)
|
|
2101
|
+
missionCommands.addCommand("+0.25s", durationMenu, updateSplashDamageSetting, "giant_explosion_duration", 0.25)
|
|
2102
|
+
missionCommands.addCommand("-0.25s", durationMenu, updateSplashDamageSetting, "giant_explosion_duration", -0.25)
|
|
2103
|
+
local countMenu = missionCommands.addSubMenu("Explosion Count", giantExplosionMenu)
|
|
2104
|
+
addValueAdjustmentCommands(countMenu, "giant_explosion_count")
|
|
2105
|
+
|
|
2106
|
+
end
|
|
2107
|
+
|
|
2108
|
+
if (script_enable == 1) then
|
|
2109
|
+
gameMsg("SPLASH DAMAGE 3.1 SCRIPT RUNNING")
|
|
2110
|
+
env.info("SPLASH DAMAGE 3.1 SCRIPT RUNNING")
|
|
2111
|
+
|
|
2112
|
+
timer.scheduleFunction(function()
|
|
2113
|
+
protectedCall(track_wpns)
|
|
2114
|
+
return timer.getTime() + refreshRate
|
|
2115
|
+
end, {}, timer.getTime() + refreshRate)
|
|
2116
|
+
|
|
2117
|
+
if splash_damage_options.giant_explosion_enabled then
|
|
2118
|
+
giantExplosionTargets = {} -- Ensure it’s fresh
|
|
2119
|
+
local targetCount = 0
|
|
2120
|
+
for coa = 0, 2 do
|
|
2121
|
+
local groups = coalition.getGroups(coa)
|
|
2122
|
+
if groups then
|
|
2123
|
+
for _, group in pairs(groups) do
|
|
2124
|
+
local units = group:getUnits()
|
|
2125
|
+
if units then
|
|
2126
|
+
for _, unit in pairs(units) do
|
|
2127
|
+
local name = unit:getName()
|
|
2128
|
+
if name:find("GiantExplosionTarget") then
|
|
2129
|
+
local pos = unit:getPosition().p
|
|
2130
|
+
giantExplosionTargets[name] = {obj = unit, pos = pos}
|
|
2131
|
+
if splash_damage_options.giant_explosion_target_static then
|
|
2132
|
+
giantExplosionTargets[name].pos = pos
|
|
2133
|
+
end
|
|
2134
|
+
debugMsg("Found GiantExplosionTarget: " .. name .. " at X:" .. pos.x .. " Y:" .. pos.y .. " Z:" .. pos.z)
|
|
2135
|
+
targetCount = targetCount + 1
|
|
2136
|
+
end
|
|
2137
|
+
end
|
|
2138
|
+
end
|
|
2139
|
+
end
|
|
2140
|
+
end
|
|
2141
|
+
end
|
|
2142
|
+
debugMsg("Total GiantExplosionTargets found: " .. targetCount)
|
|
2143
|
+
timer.scheduleFunction(checkGiantExplosionFlag, {}, timer.getTime() + splash_damage_options.giant_explosion_poll_rate)
|
|
2144
|
+
if not splash_damage_options.giant_explosion_target_static then
|
|
2145
|
+
timer.scheduleFunction(updateTargetPosition, {}, timer.getTime() + 1.0)
|
|
2146
|
+
end
|
|
2147
|
+
end
|
|
2148
|
+
|
|
2149
|
+
world.addEventHandler(WpnHandler)
|
|
2150
|
+
addSplashDamageMenu()
|
|
2151
|
+
end
|