@jtff/miztemplate-lib 3.6.5 → 3.7.1
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.
|
@@ -0,0 +1,1716 @@
|
|
|
1
|
+
--[[
|
|
2
|
+
|
|
3
|
+
09 March 2025 (Stevey666) - 3.0
|
|
4
|
+
- 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
|
|
5
|
+
- 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)
|
|
6
|
+
- Added vehicle scanning around a weapon to allow for..
|
|
7
|
+
- Cook offs - you can set vehicles that will cook off i.e ammo trucks, number of explosions, debris explosions, power adjustable
|
|
8
|
+
- Fuel/Tanker explosion and flames - when a fuel tanker blows it will through up a big flame - adjustable in the scripts
|
|
9
|
+
- Added section for vehicles for the above
|
|
10
|
+
- Added radio commands for everything
|
|
11
|
+
- Added in cluster munitions changes (note: barely tested, its not particularly accurate or that useful at this point so leaving disabled)
|
|
12
|
+
- Potential bug - testing, stacking too many units together may cause a MIST error if you're using mist
|
|
13
|
+
|
|
14
|
+
- Setting this as 3.0 as I'd like to be responsive to requests, updates etc - creating a new fork to track this
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
10 Feb 2025 (Stevey666) - 2.0.7
|
|
18
|
+
- Fixed AGM 154/Adjusted weapons
|
|
19
|
+
- Added overall damage scaling
|
|
20
|
+
- Added modifier for shaped charges (i.e. Mavericks), adjusted weapon list accordingly
|
|
21
|
+
- Adjusted blast radius and damage calculations, created option for dynamic blast radius
|
|
22
|
+
- Adjusted cascading explosions, added additional "cascade_scaling" modifier and cascade explode threshold modifier. Units wont explode on initial impact unless health drops under threshold
|
|
23
|
+
- Added always_cascade_explode option so you can set it to the old ways of making everything in the blast wave go kaboom
|
|
24
|
+
- Added in game radio commands to change the new options ingame without having to reload everything in mission editor to test it out
|
|
25
|
+
|
|
26
|
+
12 November 2024 (by JGi | Quéton 1-1)
|
|
27
|
+
- Tweak down radius 100>90 (Thanks Arhibeau)
|
|
28
|
+
- Tweak down some values
|
|
29
|
+
|
|
30
|
+
20 January 2024 (by JGi | Quéton 1-1)
|
|
31
|
+
- Added missing weapons to explTable
|
|
32
|
+
- Sort weapons in explTable by type
|
|
33
|
+
- Added aircraft type in log when missing
|
|
34
|
+
|
|
35
|
+
03 May 2023 (KERV)
|
|
36
|
+
Correction AGM 154 (https://forum.dcs.world/topic/289290-splash-damage-20-script-make-explosions-better/page/5/#comment-5207760)
|
|
37
|
+
|
|
38
|
+
06 March 2023 (Kerv)
|
|
39
|
+
- Add some data for new ammunition
|
|
40
|
+
|
|
41
|
+
16 April 2022
|
|
42
|
+
spencershepard (GRIMM):
|
|
43
|
+
- Added new/missing weapons to explTable
|
|
44
|
+
- Added new option rocket_multiplier
|
|
45
|
+
|
|
46
|
+
31 December 2021
|
|
47
|
+
spencershepard (GRIMM):
|
|
48
|
+
- Added many new weapons
|
|
49
|
+
- Added filter for weapons.shells events
|
|
50
|
+
- Fixed mission weapon message option
|
|
51
|
+
- Changed default for damage_model option
|
|
52
|
+
|
|
53
|
+
21 December 2021
|
|
54
|
+
spencershepard (GRIMM):
|
|
55
|
+
SPLASH DAMAGE 2.0:
|
|
56
|
+
- Added blast wave effect to add timed and scaled secondary explosions on top of game objects
|
|
57
|
+
- Object geometry within blast wave changes damage intensity
|
|
58
|
+
- Damage boost for structures since they are hard to kill, even if very close to large explosions
|
|
59
|
+
- Increased some rocket values in explTable
|
|
60
|
+
- Missing weapons from explTable will display message to user and log to DCS.log so that we can add what's missing
|
|
61
|
+
- Damage model for ground units that will disable their weapons and ability to move with partial damage before they are killed
|
|
62
|
+
- Added options table to allow easy adjustments before release
|
|
63
|
+
- General refactoring and restructure
|
|
64
|
+
|
|
65
|
+
28 October 2020
|
|
66
|
+
FrozenDroid:
|
|
67
|
+
- Uncommented error logging, actually made it an error log which shows a message box on error.
|
|
68
|
+
- Fixed the too restrictive weapon filter (took out the HE warhead requirement)
|
|
69
|
+
|
|
70
|
+
2 October 2020
|
|
71
|
+
FrozenDroid:
|
|
72
|
+
- Added error handling to all event handler and scheduled functions. Lua script errors can no longer bring the server down.
|
|
73
|
+
- 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...?)
|
|
74
|
+
--]]
|
|
75
|
+
|
|
76
|
+
----[[ ##### SCRIPT CONFIGURATION ##### ]]----
|
|
77
|
+
splash_damage_options = {
|
|
78
|
+
--debug options
|
|
79
|
+
["game_messages"] = false, --enable some messages on screen
|
|
80
|
+
["debug"] = false, --enable debugging messages
|
|
81
|
+
["weapon_missing_message"] = false, --false disables messages alerting you to weapons missing from the explTable
|
|
82
|
+
|
|
83
|
+
["track_pre_explosion_debug"] = false, --Toggle to enable/disable pre-explosion tracking debugging
|
|
84
|
+
|
|
85
|
+
["enable_radio_menu"] = true, --enables the in-game radio menu for modifying settings
|
|
86
|
+
|
|
87
|
+
["static_damage_boost"] = 2000, --apply extra damage to Unit.Category.STRUCTUREs with wave explosions
|
|
88
|
+
["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
|
|
89
|
+
["larger_explosions"] = true, --secondary explosions on top of weapon impact points, dictated by the values in the explTable
|
|
90
|
+
["damage_model"] = true, --allow blast wave to affect ground unit movement and weapons
|
|
91
|
+
["blast_search_radius"] = 90, --this is the max size of any blast wave radius, since we will only find objects within this zone
|
|
92
|
+
["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.
|
|
93
|
+
["blast_stun"] = false, --not implemented
|
|
94
|
+
["unit_disabled_health"] = 30, --if health is below this value after our explosions, disable its movement
|
|
95
|
+
["unit_cant_fire_health"] = 40, --if health is below this value after our explosions, set ROE to HOLD to simulate damage weapon systems
|
|
96
|
+
["infantry_cant_fire_health"] = 60, --if health is below this value after our explosions, set ROE to HOLD to simulate severe injury
|
|
97
|
+
|
|
98
|
+
["rocket_multiplier"] = 1.3, --multiplied by the explTable value for rockets
|
|
99
|
+
["overall_scaling"] = 1, --overall scaling for explosive power
|
|
100
|
+
|
|
101
|
+
["apply_shaped_charge_effects"] = true, --apply reduction in blastwave etc for shaped charge munitions
|
|
102
|
+
["shaped_charge_multiplier"] = 0.2, --multiplier that reduces blast radius and explosion power for shaped charge munitions.
|
|
103
|
+
|
|
104
|
+
["use_dynamic_blast_radius"] = true, --if true, blast radius is calculated from explosion power; if false, blast_search_radius (90) is used
|
|
105
|
+
["dynamic_blast_radius_modifier"] = 2, --multiplier for the blast radius
|
|
106
|
+
|
|
107
|
+
["cascade_scaling"] = 1, --multiplier for secondary (cascade) blast damage, 1 damage fades out too soon, 3 damage seems a good balance
|
|
108
|
+
["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
|
|
109
|
+
["always_cascade_explode"] = false, --switch if you want everything to explode like with the original script
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
--track_pre_explosion/enable_cargo_effects should both be the same value
|
|
113
|
+
["track_pre_explosion"] = true, --Toggle to enable/disable pre-explosion tracking
|
|
114
|
+
["enable_cargo_effects"] = true, --Toggle for enabling/disabling cargo explosions and cook-offs
|
|
115
|
+
["cargo_damage_threshold"] = 60, --Health % below which cargo explodes (0 = destroyed only)
|
|
116
|
+
["debris_effects"] = true, --Enable debris from cargo cook-offs
|
|
117
|
+
["debris_power"] = 1, --Power of each debris explosion
|
|
118
|
+
["debris_count_min"] = 6, --Minimum debris pieces per cook-off
|
|
119
|
+
["debris_count_max"] = 12, --Maximum debris pieces per cook-off
|
|
120
|
+
["debris_max_distance"] = 10, --Max distance debris can travel (meters), the min distance from the vehicle will be 10% of this
|
|
121
|
+
|
|
122
|
+
["ordnance_protection"] = true, --Toggle ordinance protection features
|
|
123
|
+
["ordnance_protection_radius"] = 10, --Distance in meters to protect nearby bombs
|
|
124
|
+
["detect_ordnance_destruction"] = true, --Toggle detection of ordnance destroyed by large explosions
|
|
125
|
+
["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
|
|
126
|
+
["recent_large_explosion_snap"] = true, --enable looking for a recent large_explosion generated by the script
|
|
127
|
+
["recent_large_explosion_range"] = 200, --range its looking for in meters for a recent large_explosion generated by the script
|
|
128
|
+
["recent_large_explosion_time"] = 4, --in seconds how long ago there was a recent large_explosion generated by the script
|
|
129
|
+
|
|
130
|
+
-- Cluster bomb settings
|
|
131
|
+
["cluster_enabled"] = false,
|
|
132
|
+
["cluster_base_length"] = 150, -- Base forward spread (meters)
|
|
133
|
+
["cluster_base_width"] = 200, -- Base lateral spread (meters)
|
|
134
|
+
["cluster_max_length"] = 300, -- Max forward spread (meters)
|
|
135
|
+
["cluster_max_width"] = 400, -- Max lateral spread (meters)
|
|
136
|
+
["cluster_min_length"] = 100, -- Min forward spread
|
|
137
|
+
["cluster_min_width"] = 150, -- Min lateral spread
|
|
138
|
+
["cluster_bomblet_reductionmodifier"] = true, -- Use equation to reduce number of bomblets (to make it look better)
|
|
139
|
+
["cluster_bomblet_damage_modifier"] = 1, -- Adjustable global modifier for bomblet explosive power
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
local script_enable = 1
|
|
143
|
+
refreshRate = 0.1
|
|
144
|
+
----[[ ##### End of SCRIPT CONFIGURATION ##### ]]----
|
|
145
|
+
|
|
146
|
+
--Helper function: Trim whitespace.
|
|
147
|
+
local function trim(s)
|
|
148
|
+
return s:match("^%s*(.-)%s*$")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
cargoUnits = {
|
|
152
|
+
|
|
153
|
+
--[[
|
|
154
|
+
flamesize:
|
|
155
|
+
|
|
156
|
+
1 = small smoke and fire
|
|
157
|
+
2 = medium smoke and fire
|
|
158
|
+
3 = large smoke and fire
|
|
159
|
+
4 = huge smoke and fire
|
|
160
|
+
5 = small smoke
|
|
161
|
+
6 = medium smoke
|
|
162
|
+
7 = large smoke
|
|
163
|
+
8 = huge smoke
|
|
164
|
+
]]--
|
|
165
|
+
|
|
166
|
+
--1) M92 R11 Volvo driveable (Fuel Truck Tanker)
|
|
167
|
+
["r11_volvo_drivable"] = {
|
|
168
|
+
cargoExplosion = true,
|
|
169
|
+
cargoExplosionMult = 2.0,
|
|
170
|
+
cargoExplosionPower = 200,
|
|
171
|
+
cargoCookOff = false,
|
|
172
|
+
cookOffCount = 0,
|
|
173
|
+
cookOffPower = 0,
|
|
174
|
+
cookOffDuration = 0,
|
|
175
|
+
cookOffRandomTiming = false,
|
|
176
|
+
cookOffPowerRandom = 50,
|
|
177
|
+
isTanker = true,
|
|
178
|
+
flameSize = 3,
|
|
179
|
+
flameDuration = 5,
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
--2) Refueler ATMZ-5
|
|
183
|
+
["ATMZ-5"] = {
|
|
184
|
+
cargoExplosion = true,
|
|
185
|
+
cargoExplosionMult = 2.0,
|
|
186
|
+
cargoExplosionPower = 200,
|
|
187
|
+
cargoCookOff = false,
|
|
188
|
+
cookOffCount = 0,
|
|
189
|
+
cookOffPower = 0,
|
|
190
|
+
cookOffDuration = 0,
|
|
191
|
+
cookOffRandomTiming = false,
|
|
192
|
+
cookOffPowerRandom = 50,
|
|
193
|
+
isTanker = true,
|
|
194
|
+
flameSize = 3,
|
|
195
|
+
flameDuration = 5,
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
--3) Refueler ATZ-10
|
|
199
|
+
["ATZ-10"] = {
|
|
200
|
+
cargoExplosion = true,
|
|
201
|
+
cargoExplosionMult = 1.8,
|
|
202
|
+
cargoExplosionPower = 200,
|
|
203
|
+
cargoCookOff = false,
|
|
204
|
+
cookOffCount = 0,
|
|
205
|
+
cookOffPower = 0,
|
|
206
|
+
cookOffDuration = 0,
|
|
207
|
+
cookOffRandomTiming = false,
|
|
208
|
+
cookOffPowerRandom = 50,
|
|
209
|
+
isTanker = true,
|
|
210
|
+
flameSize = 3,
|
|
211
|
+
flameDuration = 5,
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
--4) Refueler ATZ-5
|
|
215
|
+
["ATZ-5"] = {
|
|
216
|
+
cargoExplosion = true,
|
|
217
|
+
cargoExplosionMult = 1.8,
|
|
218
|
+
cargoExplosionPower = 200,
|
|
219
|
+
cargoCookOff = false,
|
|
220
|
+
cookOffCount = 0,
|
|
221
|
+
cookOffPower = 0,
|
|
222
|
+
cookOffDuration = 0,
|
|
223
|
+
cookOffRandomTiming = false,
|
|
224
|
+
cookOffPowerRandom = 50,
|
|
225
|
+
isTanker = true,
|
|
226
|
+
flameSize = 3,
|
|
227
|
+
flameDuration = 5,
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
--5) Refueler M978 HEMTT (Fuel truck tanker)
|
|
231
|
+
["M978 HEMTT Tanker"] = {
|
|
232
|
+
cargoExplosion = true,
|
|
233
|
+
cargoExplosionMult = 2.0,
|
|
234
|
+
cargoExplosionPower = 200,
|
|
235
|
+
cargoCookOff = false,
|
|
236
|
+
cookOffCount = 0,
|
|
237
|
+
cookOffPower = 0,
|
|
238
|
+
cookOffDuration = 0,
|
|
239
|
+
cookOffRandomTiming = false,
|
|
240
|
+
cookOffPowerRandom = 50,
|
|
241
|
+
isTanker = true,
|
|
242
|
+
flameSize = 3,
|
|
243
|
+
flameDuration = 5,
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
--##### AMMO CARRIERS #####
|
|
247
|
+
["GAZ-66"] = {
|
|
248
|
+
cargoExplosion = true,
|
|
249
|
+
cargoExplosionMult = 1,
|
|
250
|
+
cargoExplosionPower = 200,
|
|
251
|
+
cargoCookOff = true,
|
|
252
|
+
cookOffCount = 4,
|
|
253
|
+
cookOffPower = 1,
|
|
254
|
+
cookOffDuration = 20,
|
|
255
|
+
cookOffRandomTiming = true,
|
|
256
|
+
cookOffPowerRandom = 50,
|
|
257
|
+
isTanker = false,
|
|
258
|
+
flameSize = 1,
|
|
259
|
+
flameDuration = 30,
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
["Ural-4320"] = {
|
|
263
|
+
cargoExplosion = true,
|
|
264
|
+
cargoExplosionMult = 1,
|
|
265
|
+
cargoExplosionPower = 200,
|
|
266
|
+
cargoCookOff = true,
|
|
267
|
+
cookOffCount = 4,
|
|
268
|
+
cookOffPower = 1,
|
|
269
|
+
cookOffDuration = 20,
|
|
270
|
+
cookOffRandomTiming = true,
|
|
271
|
+
cookOffPowerRandom = 50,
|
|
272
|
+
isTanker = false,
|
|
273
|
+
flameSize = 1,
|
|
274
|
+
flameDuration = 30,
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
["ZIL-135"] = {
|
|
278
|
+
cargoExplosion = true,
|
|
279
|
+
cargoExplosionMult = 1,
|
|
280
|
+
cargoExplosionPower = 200,
|
|
281
|
+
cargoCookOff = true,
|
|
282
|
+
cookOffCount = 5,
|
|
283
|
+
cookOffPower = 1,
|
|
284
|
+
cookOffDuration = 20,
|
|
285
|
+
cookOffRandomTiming = true,
|
|
286
|
+
cookOffPowerRandom = 50,
|
|
287
|
+
isTanker = false,
|
|
288
|
+
flameSize = 1,
|
|
289
|
+
flameDuration = 30,
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
--Weapon Explosive Table
|
|
294
|
+
explTable = {
|
|
295
|
+
--*** WWII BOMBS ***
|
|
296
|
+
["British_GP_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
297
|
+
["British_GP_250LB_Bomb_Mk4"] = { explosive = 100, shaped_charge = false },
|
|
298
|
+
["British_GP_250LB_Bomb_Mk5"] = { explosive = 100, shaped_charge = false },
|
|
299
|
+
["British_GP_500LB_Bomb_Mk1"] = { explosive = 213, shaped_charge = false },
|
|
300
|
+
["British_GP_500LB_Bomb_Mk4"] = { explosive = 213, shaped_charge = false },
|
|
301
|
+
["British_GP_500LB_Bomb_Mk4_Short"] = { explosive = 213, shaped_charge = false },
|
|
302
|
+
["British_GP_500LB_Bomb_Mk5"] = { explosive = 213, shaped_charge = false },
|
|
303
|
+
["British_MC_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
304
|
+
["British_MC_250LB_Bomb_Mk2"] = { explosive = 100, shaped_charge = false },
|
|
305
|
+
["British_MC_500LB_Bomb_Mk1_Short"] = { explosive = 213, shaped_charge = false },
|
|
306
|
+
["British_MC_500LB_Bomb_Mk2"] = { explosive = 213, shaped_charge = false },
|
|
307
|
+
["British_SAP_250LB_Bomb_Mk5"] = { explosive = 100, shaped_charge = false },
|
|
308
|
+
["British_SAP_500LB_Bomb_Mk5"] = { explosive = 213, shaped_charge = false },
|
|
309
|
+
["British_AP_25LBNo1_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
310
|
+
["British_HE_60LBSAPNo2_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
311
|
+
["British_HE_60LBFNo1_3INCHNo1"] = { explosive = 4, shaped_charge = false },
|
|
312
|
+
|
|
313
|
+
["SC_50"] = { explosive = 20, shaped_charge = false },
|
|
314
|
+
["ER_4_SC50"] = { explosive = 20, shaped_charge = false },
|
|
315
|
+
["SC_250_T1_L2"] = { explosive = 100, shaped_charge = false },
|
|
316
|
+
["SC_501_SC250"] = { explosive = 100, shaped_charge = false },
|
|
317
|
+
["Schloss500XIIC1_SC_250_T3_J"] = { explosive = 100, shaped_charge = false },
|
|
318
|
+
["SC_501_SC500"] = { explosive = 213, shaped_charge = false },
|
|
319
|
+
["SC_500_L2"] = { explosive = 213, shaped_charge = false },
|
|
320
|
+
["SD_250_Stg"] = { explosive = 100, shaped_charge = false },
|
|
321
|
+
["SD_500_A"] = { explosive = 213, shaped_charge = false },
|
|
322
|
+
|
|
323
|
+
--*** WWII CBU ***
|
|
324
|
+
["AB_250_2_SD_2"] = { explosive = 100, shaped_charge = false },
|
|
325
|
+
["AB_250_2_SD_10A"] = { explosive = 100, shaped_charge = false },
|
|
326
|
+
["AB_500_1_SD_10A"] = { explosive = 213, shaped_charge = false },
|
|
327
|
+
|
|
328
|
+
--*** WWII ROCKETS ***
|
|
329
|
+
["3xM8_ROCKETS_IN_TUBES"] = { explosive = 4, shaped_charge = false },
|
|
330
|
+
["WGr21"] = { explosive = 4, shaped_charge = false },
|
|
331
|
+
|
|
332
|
+
--*** UNGUIDED BOMBS (UGB) ***
|
|
333
|
+
["M_117"] = { explosive = 201, shaped_charge = false },
|
|
334
|
+
["AN_M30A1"] = { explosive = 45, shaped_charge = false },
|
|
335
|
+
["AN_M57"] = { explosive = 100, shaped_charge = false },
|
|
336
|
+
["AN_M64"] = { explosive = 121, shaped_charge = false },
|
|
337
|
+
["AN_M65"] = { explosive = 400, shaped_charge = false },
|
|
338
|
+
["AN_M66"] = { explosive = 800, shaped_charge = false },
|
|
339
|
+
["AN-M66A2"] = { explosive = 536, shaped_charge = false },
|
|
340
|
+
["AN-M81"] = { explosive = 100, shaped_charge = false },
|
|
341
|
+
["AN-M88"] = { explosive = 100, shaped_charge = false },
|
|
342
|
+
|
|
343
|
+
["Mk_81"] = { explosive = 60, shaped_charge = false },
|
|
344
|
+
["MK-81SE"] = { explosive = 60, shaped_charge = false },
|
|
345
|
+
["Mk_82"] = { explosive = 100, shaped_charge = false },
|
|
346
|
+
["MK_82AIR"] = { explosive = 100, shaped_charge = false },
|
|
347
|
+
["MK_82SNAKEYE"] = { explosive = 100, shaped_charge = false },
|
|
348
|
+
["Mk_83"] = { explosive = 274, shaped_charge = false },
|
|
349
|
+
["Mk_84"] = { explosive = 582, shaped_charge = false },
|
|
350
|
+
|
|
351
|
+
["HEBOMB"] = { explosive = 40, shaped_charge = false },
|
|
352
|
+
["HEBOMBD"] = { explosive = 40, shaped_charge = false },
|
|
353
|
+
|
|
354
|
+
["SAMP125LD"] = { explosive = 60, shaped_charge = false },
|
|
355
|
+
["SAMP250LD"] = { explosive = 118, shaped_charge = false },
|
|
356
|
+
["SAMP250HD"] = { explosive = 118, shaped_charge = false },
|
|
357
|
+
["SAMP400LD"] = { explosive = 274, shaped_charge = false },
|
|
358
|
+
["SAMP400HD"] = { explosive = 274, shaped_charge = false },
|
|
359
|
+
|
|
360
|
+
["BR_250"] = { explosive = 100, shaped_charge = false },
|
|
361
|
+
["BR_500"] = { explosive = 100, shaped_charge = false },
|
|
362
|
+
|
|
363
|
+
["FAB_100"] = { explosive = 45, shaped_charge = false },
|
|
364
|
+
["FAB_250"] = { explosive = 118, shaped_charge = false },
|
|
365
|
+
["FAB_250M54TU"] = { explosive = 118, shaped_charge = false },
|
|
366
|
+
["FAB-250-M62"] = { explosive = 118, shaped_charge = false },
|
|
367
|
+
["FAB_500"] = { explosive = 213, shaped_charge = false },
|
|
368
|
+
["FAB_1500"] = { explosive = 675, shaped_charge = false },
|
|
369
|
+
|
|
370
|
+
--*** UNGUIDED BOMBS WITH PENETRATOR / ANTI-RUNWAY ***
|
|
371
|
+
["Durandal"] = { explosive = 64, shaped_charge = false },
|
|
372
|
+
["BLU107B_DURANDAL"] = { explosive = 64, shaped_charge = false },
|
|
373
|
+
["BAP_100"] = { explosive = 32, shaped_charge = false },
|
|
374
|
+
["BAP-100"] = { explosive = 32, shaped_charge = false },
|
|
375
|
+
["BAT-120"] = { explosive = 32, shaped_charge = false },
|
|
376
|
+
["TYPE-200A"] = { explosive = 107, shaped_charge = false },
|
|
377
|
+
["BetAB_500"] = { explosive = 98, shaped_charge = false },
|
|
378
|
+
["BetAB_500ShP"] = { explosive = 107, shaped_charge = false },
|
|
379
|
+
|
|
380
|
+
--*** GUIDED BOMBS (GBU) ***
|
|
381
|
+
["GBU_10"] = { explosive = 582, shaped_charge = false },
|
|
382
|
+
["GBU_12"] = { explosive = 100, shaped_charge = false },
|
|
383
|
+
["GBU_16"] = { explosive = 274, shaped_charge = false },
|
|
384
|
+
["GBU_24"] = { explosive = 582, shaped_charge = false },
|
|
385
|
+
["KAB_1500Kr"] = { explosive = 675, shaped_charge = false },
|
|
386
|
+
["KAB_500Kr"] = { explosive = 213, shaped_charge = false },
|
|
387
|
+
["KAB_500"] = { explosive = 213, shaped_charge = false },
|
|
388
|
+
|
|
389
|
+
--*** CLUSTER BOMBS (CBU) ***
|
|
390
|
+
--I don't have most of these so can't test them with debug on
|
|
391
|
+
["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
|
|
392
|
+
["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
|
|
393
|
+
["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
|
|
394
|
+
["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
|
|
395
|
+
["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
|
|
396
|
+
["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
|
|
397
|
+
["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
|
|
398
|
+
["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
|
|
399
|
+
["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
|
|
400
|
+
["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)
|
|
401
|
+
["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
|
|
402
|
+
["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
|
|
403
|
+
["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
|
|
404
|
+
["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
|
|
405
|
+
["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
|
|
406
|
+
["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
|
|
407
|
+
["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
|
|
408
|
+
["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
|
|
409
|
+
["RBK_500_255_PTO_1M"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 126, submunition_explosive = 0.5, submunition_name = "PTO_1M" },
|
|
410
|
+
["RBK_500_255_ShO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 565, submunition_explosive = 0.1, submunition_name = "ShO" },
|
|
411
|
+
--*** INS/GPS BOMBS (JDAM) ***
|
|
412
|
+
["GBU_31"] = { explosive = 582, shaped_charge = false },
|
|
413
|
+
["GBU_31_V_3B"] = { explosive = 582, shaped_charge = false },
|
|
414
|
+
["GBU_31_V_2B"] = { explosive = 582, shaped_charge = false },
|
|
415
|
+
["GBU_31_V_4B"] = { explosive = 582, shaped_charge = false },
|
|
416
|
+
["GBU_32_V_2B"] = { explosive = 202, shaped_charge = false },
|
|
417
|
+
["GBU_38"] = { explosive = 100, shaped_charge = false },
|
|
418
|
+
["GBU_54_V_1B"] = { explosive = 100, shaped_charge = false },
|
|
419
|
+
|
|
420
|
+
--*** GLIDE BOMBS (JSOW) ***
|
|
421
|
+
["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
|
|
422
|
+
["AGM_154C"] = { explosive = 305, shaped_charge = false },
|
|
423
|
+
["AGM_154"] = { explosive = 305, shaped_charge = false },
|
|
424
|
+
["BK90_MJ1"] = { explosive = 0, shaped_charge = false },
|
|
425
|
+
["BK90_MJ1_MJ2"] = { explosive = 0, shaped_charge = false },
|
|
426
|
+
["BK90_MJ2"] = { explosive = 0, shaped_charge = false },
|
|
427
|
+
|
|
428
|
+
["LS-6-100"] = { explosive = 45, shaped_charge = false },
|
|
429
|
+
["LS-6-250"] = { explosive = 100, shaped_charge = false },
|
|
430
|
+
["LS-6-500"] = { explosive = 274, shaped_charge = false },
|
|
431
|
+
["GB-6"] = { explosive = 0, shaped_charge = false },
|
|
432
|
+
["GB-6-HE"] = { explosive = 0, shaped_charge = false },
|
|
433
|
+
["GB-6-SFW"] = { explosive = 0, shaped_charge = false },
|
|
434
|
+
|
|
435
|
+
--*** AIR GROUND MISSILE (AGM) ***
|
|
436
|
+
["AGM_62"] = { explosive = 400, shaped_charge = false },
|
|
437
|
+
["AGM_65D"] = { explosive = 38, shaped_charge = true },
|
|
438
|
+
["AGM_65E"] = { explosive = 80, shaped_charge = true },
|
|
439
|
+
["AGM_65F"] = { explosive = 80, shaped_charge = true },
|
|
440
|
+
["AGM_65G"] = { explosive = 80, shaped_charge = true },
|
|
441
|
+
["AGM_65H"] = { explosive = 38, shaped_charge = true },
|
|
442
|
+
["AGM_65K"] = { explosive = 80, shaped_charge = true },
|
|
443
|
+
["AGM_65L"] = { explosive = 80, shaped_charge = true },
|
|
444
|
+
["AGM_123"] = { explosive = 274, shaped_charge = false },
|
|
445
|
+
["AGM_130"] = { explosive = 582, shaped_charge = false },
|
|
446
|
+
["AGM_119"] = { explosive = 176, shaped_charge = false },
|
|
447
|
+
["AGM_114"] = { explosive = 10, shaped_charge = true },
|
|
448
|
+
["AGM_114K"] = { explosive = 10, shaped_charge = true },
|
|
449
|
+
|
|
450
|
+
["Rb 05A"] = { explosive = 217, shaped_charge = false },
|
|
451
|
+
["RB75"] = { explosive = 38, shaped_charge = false },
|
|
452
|
+
["RB75A"] = { explosive = 38, shaped_charge = false },
|
|
453
|
+
["RB75B"] = { explosive = 38, shaped_charge = false },
|
|
454
|
+
["RB75T"] = { explosive = 80, shaped_charge = false },
|
|
455
|
+
["HOT3_MBDA"] = { explosive = 15, shaped_charge = false },
|
|
456
|
+
["C-701T"] = { explosive = 38, shaped_charge = false },
|
|
457
|
+
["C-701IR"] = { explosive = 38, shaped_charge = false },
|
|
458
|
+
|
|
459
|
+
["Vikhr_M"] = { explosive = 11, shaped_charge = false },
|
|
460
|
+
["Vikhr_9M127_1"] = { explosive = 11, shaped_charge = false },
|
|
461
|
+
["AT_6"] = { explosive = 11, shaped_charge = false },
|
|
462
|
+
["Ataka_9M120"] = { explosive = 11, shaped_charge = false },
|
|
463
|
+
["Ataka_9M120F"] = { explosive = 11, shaped_charge = false },
|
|
464
|
+
["P_9M117"] = { explosive = 0, shaped_charge = false },
|
|
465
|
+
|
|
466
|
+
["KH-66_Grom"] = { explosive = 108, shaped_charge = false },
|
|
467
|
+
["X_23"] = { explosive = 111, shaped_charge = false },
|
|
468
|
+
["X_23L"] = { explosive = 111, shaped_charge = false },
|
|
469
|
+
["X_28"] = { explosive = 160, shaped_charge = false },
|
|
470
|
+
["X_25ML"] = { explosive = 89, shaped_charge = false },
|
|
471
|
+
["X_25MR"] = { explosive = 140, shaped_charge = false },
|
|
472
|
+
["X_29L"] = { explosive = 320, shaped_charge = false },
|
|
473
|
+
["X_29T"] = { explosive = 320, shaped_charge = false },
|
|
474
|
+
["X_29TE"] = { explosive = 320, shaped_charge = false },
|
|
475
|
+
|
|
476
|
+
--*** ANTI-RADAR MISSILE (ARM) ***
|
|
477
|
+
["AGM_88C"] = { explosive = 89, shaped_charge = false },
|
|
478
|
+
["AGM_88"] = { explosive = 89, shaped_charge = false },
|
|
479
|
+
["AGM_122"] = { explosive = 15, shaped_charge = false },
|
|
480
|
+
["LD-10"] = { explosive = 89, shaped_charge = false },
|
|
481
|
+
["AGM_45A"] = { explosive = 38, shaped_charge = false },
|
|
482
|
+
["X_58"] = { explosive = 140, shaped_charge = false },
|
|
483
|
+
["X_25MP"] = { explosive = 89, shaped_charge = false },
|
|
484
|
+
|
|
485
|
+
--*** ANTI-SHIP MISSILE (ASh) ***
|
|
486
|
+
["AGM_84D"] = { explosive = 488, shaped_charge = false },
|
|
487
|
+
["Rb 15F"] = { explosive = 500, shaped_charge = false },
|
|
488
|
+
["C-802AK"] = { explosive = 500, shaped_charge = false },
|
|
489
|
+
|
|
490
|
+
--*** CRUISE MISSILE ***
|
|
491
|
+
["CM-802AKG"] = { explosive = 488, shaped_charge = false },
|
|
492
|
+
["AGM_84E"] = { explosive = 488, shaped_charge = false },
|
|
493
|
+
["AGM_84H"] = { explosive = 488, shaped_charge = false },
|
|
494
|
+
["X_59M"] = { explosive = 488, shaped_charge = false },
|
|
495
|
+
|
|
496
|
+
--*** ROCKETS ***
|
|
497
|
+
["HYDRA_70M15"] = { explosive = 5, shaped_charge = false },
|
|
498
|
+
["HYDRA_70_MK1"] = { explosive = 5, shaped_charge = false },
|
|
499
|
+
["HYDRA_70_MK5"] = { explosive = 8, shaped_charge = false },
|
|
500
|
+
["HYDRA_70_M151"] = { explosive = 5, shaped_charge = false },
|
|
501
|
+
["HYDRA_70_M151_M433"] = { explosive = 5, shaped_charge = false },
|
|
502
|
+
["HYDRA_70_M229"] = { explosive = 5, shaped_charge = false },
|
|
503
|
+
["FFAR Mk1 HE"] = { explosive = 5, shaped_charge = false },
|
|
504
|
+
["FFAR Mk5 HEAT"] = { explosive = 8, shaped_charge = false },
|
|
505
|
+
["HVAR"] = { explosive = 5, shaped_charge = false },
|
|
506
|
+
["Zuni_127"] = { explosive = 8, shaped_charge = false },
|
|
507
|
+
["ARAKM70BHE"] = { explosive = 5, shaped_charge = false },
|
|
508
|
+
["ARAKM70BAP"] = { explosive = 8, shaped_charge = false },
|
|
509
|
+
["SNEB_TYPE251_F1B"] = { explosive = 4, shaped_charge = false },
|
|
510
|
+
["SNEB_TYPE252_F1B"] = { explosive = 4, shaped_charge = false },
|
|
511
|
+
["SNEB_TYPE253_F1B"] = { explosive = 5, shaped_charge = false },
|
|
512
|
+
["SNEB_TYPE256_F1B"] = { explosive = 6, shaped_charge = false },
|
|
513
|
+
["SNEB_TYPE257_F1B"] = { explosive = 8, shaped_charge = false },
|
|
514
|
+
["SNEB_TYPE251_F4B"] = { explosive = 4, shaped_charge = false },
|
|
515
|
+
["SNEB_TYPE252_F4B"] = { explosive = 4, shaped_charge = false },
|
|
516
|
+
["SNEB_TYPE253_F4B"] = { explosive = 5, shaped_charge = false },
|
|
517
|
+
["SNEB_TYPE256_F4B"] = { explosive = 6, shaped_charge = false },
|
|
518
|
+
["SNEB_TYPE257_F4B"] = { explosive = 8, shaped_charge = false },
|
|
519
|
+
["SNEB_TYPE251_H1"] = { explosive = 4, shaped_charge = false },
|
|
520
|
+
["SNEB_TYPE252_H1"] = { explosive = 4, shaped_charge = false },
|
|
521
|
+
["SNEB_TYPE253_H1"] = { explosive = 5, shaped_charge = false },
|
|
522
|
+
["SNEB_TYPE256_H1"] = { explosive = 6, shaped_charge = false },
|
|
523
|
+
["SNEB_TYPE257_H1"] = { explosive = 8, shaped_charge = false },
|
|
524
|
+
["MATRA_F4_SNEBT251"] = { explosive = 8, shaped_charge = false },
|
|
525
|
+
["MATRA_F4_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
526
|
+
["MATRA_F4_SNEBT256"] = { explosive = 8, shaped_charge = false },
|
|
527
|
+
["MATRA_F1_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
528
|
+
["MATRA_F1_SNEBT256"] = { explosive = 8, shaped_charge = false },
|
|
529
|
+
["TELSON8_SNEBT251"] = { explosive = 4, shaped_charge = false },
|
|
530
|
+
["TELSON8_SNEBT253"] = { explosive = 8, shaped_charge = false },
|
|
531
|
+
["TELSON8_SNEBT256"] = { explosive = 4, shaped_charge = false },
|
|
532
|
+
["TELSON8_SNEBT257"] = { explosive = 6, shaped_charge = false },
|
|
533
|
+
["ARF8M3API"] = { explosive = 8, shaped_charge = false },
|
|
534
|
+
["UG_90MM"] = { explosive = 8, shaped_charge = false },
|
|
535
|
+
["S-24A"] = { explosive = 24, shaped_charge = false },
|
|
536
|
+
["S-25OF"] = { explosive = 194, shaped_charge = false },
|
|
537
|
+
["S-25OFM"] = { explosive = 150, shaped_charge = false },
|
|
538
|
+
["S-25O"] = { explosive = 150, shaped_charge = false },
|
|
539
|
+
["S-25-O"] = { explosive = 150, shaped_charge = false },
|
|
540
|
+
["S_25L"] = { explosive = 190, shaped_charge = false },
|
|
541
|
+
["S-5M"] = { explosive = 1, shaped_charge = false },
|
|
542
|
+
["C_5"] = { explosive = 8, shaped_charge = false },
|
|
543
|
+
["C5"] = { explosive = 5, shaped_charge = false },
|
|
544
|
+
["C_8"] = { explosive = 4, shaped_charge = false },
|
|
545
|
+
["C_8OFP2"] = { explosive = 3, shaped_charge = false },
|
|
546
|
+
["C_13"] = { explosive = 21, shaped_charge = false },
|
|
547
|
+
["C_24"] = { explosive = 123, shaped_charge = false },
|
|
548
|
+
["C_25"] = { explosive = 151, shaped_charge = false },
|
|
549
|
+
|
|
550
|
+
--*** LASER ROCKETS ***
|
|
551
|
+
["AGR_20"] = { explosive = 8, shaped_charge = false },
|
|
552
|
+
["AGR_20A"] = { explosive = 8, shaped_charge = false },
|
|
553
|
+
["AGR_20_M282"] = { explosive = 8, shaped_charge = false },
|
|
554
|
+
["Hydra_70_M282_MPP"] = { explosive = 8, shaped_charge = false },
|
|
555
|
+
["BRM-1_90MM"] = { explosive = 8, shaped_charge = false },
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
local effectSmokeId = 1
|
|
564
|
+
|
|
565
|
+
----[[ ##### HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
566
|
+
|
|
567
|
+
local function tableHasKey(table, key)
|
|
568
|
+
return table[key] ~= nil
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
local function debugMsg(str)
|
|
572
|
+
if splash_damage_options.debug == true then
|
|
573
|
+
debugCounter = (debugCounter or 0) + 1
|
|
574
|
+
local uniqueStr = str .. " [" .. timer.getTime() .. " - " .. debugCounter .. "]"
|
|
575
|
+
trigger.action.outText(uniqueStr, 5)
|
|
576
|
+
env.info("DEBUG: " .. uniqueStr)
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
local function gameMsg(str)
|
|
581
|
+
if splash_damage_options.game_messages == true then
|
|
582
|
+
trigger.action.outText(str, 5)
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
local function getDistance(point1, point2)
|
|
587
|
+
local x1 = point1.x
|
|
588
|
+
local y1 = point1.y
|
|
589
|
+
local z1 = point1.z
|
|
590
|
+
local x2 = point2.x
|
|
591
|
+
local y2 = point2.y
|
|
592
|
+
local z2 = point2.z
|
|
593
|
+
local dX = math.abs(x1 - x2)
|
|
594
|
+
local dZ = math.abs(z1 - z2)
|
|
595
|
+
local distance = math.sqrt(dX * dX + dZ * dZ)
|
|
596
|
+
return distance
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
local function getDistance3D(point1, point2)
|
|
600
|
+
local x1 = point1.x
|
|
601
|
+
local y1 = point1.y
|
|
602
|
+
local z1 = point1.z
|
|
603
|
+
local x2 = point2.x
|
|
604
|
+
local y2 = point2.y
|
|
605
|
+
local z2 = point2.z
|
|
606
|
+
local dX = math.abs(x1 - x2)
|
|
607
|
+
local dY = math.abs(y1 - y2)
|
|
608
|
+
local dZ = math.abs(z1 - z2)
|
|
609
|
+
local distance = math.sqrt(dX * dX + dZ * dZ + dY * dY)
|
|
610
|
+
return distance
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
local function vec3Mag(speedVec)
|
|
614
|
+
return math.sqrt(speedVec.x^2 + speedVec.y^2 + speedVec.z^2)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
local function lookahead(speedVec)
|
|
618
|
+
local speed = vec3Mag(speedVec)
|
|
619
|
+
local dist = speed * refreshRate * 1.5
|
|
620
|
+
return dist
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
--Cluster-specific helper functions from Rockeye script
|
|
624
|
+
local function normalizeVector(vec)
|
|
625
|
+
local mag = math.sqrt(vec.x^2 + vec.z^2)
|
|
626
|
+
if mag > 0 then
|
|
627
|
+
return { x = vec.x / mag, z = vec.z / mag }
|
|
628
|
+
else
|
|
629
|
+
return { x = 1, z = 0 }
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
local function calculate_drop_angle(velocity)
|
|
633
|
+
local horizontal_speed = math.sqrt((velocity.x or 0)^2 + (velocity.z or 0)^2)
|
|
634
|
+
local vertical_speed = math.abs(velocity.y or 0)
|
|
635
|
+
if horizontal_speed == 0 then return 90 end
|
|
636
|
+
local angle_rad = math.atan(vertical_speed / horizontal_speed)
|
|
637
|
+
return math.deg(angle_rad)
|
|
638
|
+
end
|
|
639
|
+
local function calculate_dispersion(velocity, burst_altitude)
|
|
640
|
+
local velocity_magnitude = math.sqrt((velocity.x or 0)^2 + (velocity.z or 0)^2)
|
|
641
|
+
local drop_angle = calculate_drop_angle(velocity)
|
|
642
|
+
local length = splash_damage_options.cluster_base_length * (1 + velocity_magnitude / 200)
|
|
643
|
+
local width = splash_damage_options.cluster_base_width * (1 + burst_altitude / 6000)
|
|
644
|
+
local length_jitter = length * (0.85 + math.random() * 0.3)
|
|
645
|
+
local width_jitter = width * (0.85 + math.random() * 0.3)
|
|
646
|
+
return math.max(splash_damage_options.cluster_min_length, math.min(splash_damage_options.cluster_max_length, length_jitter)),
|
|
647
|
+
math.max(splash_damage_options.cluster_min_width, math.min(splash_damage_options.cluster_max_width, width_jitter))
|
|
648
|
+
end
|
|
649
|
+
----[[ ##### End of HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
650
|
+
|
|
651
|
+
cargoEffectsQueue = {}
|
|
652
|
+
WpnHandler = {}
|
|
653
|
+
tracked_weapons = {}
|
|
654
|
+
local processedUnitsGlobal = {}
|
|
655
|
+
|
|
656
|
+
function getWeaponExplosive(name)
|
|
657
|
+
local weaponData = explTable[name]
|
|
658
|
+
if weaponData then
|
|
659
|
+
return weaponData.explosive, weaponData.shaped_charge
|
|
660
|
+
else
|
|
661
|
+
return 0, false
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
function track_wpns_cluster_scan(args)
|
|
666
|
+
local parentPos = args[1]
|
|
667
|
+
local parentDir = args[2]
|
|
668
|
+
local parentName = args[3]
|
|
669
|
+
local subName = args[4]
|
|
670
|
+
local subCount = args[5]
|
|
671
|
+
local subPower = args[6]
|
|
672
|
+
local parentVel = args[7]
|
|
673
|
+
local attempt = args[8] or 1
|
|
674
|
+
local maxAttempts = 3
|
|
675
|
+
local scanVol = {
|
|
676
|
+
id = world.VolumeType.SPHERE,
|
|
677
|
+
params = { point = parentPos, radius = 400 }
|
|
678
|
+
}
|
|
679
|
+
local bombletsFound = {}
|
|
680
|
+
local allWeaponsFound = {}
|
|
681
|
+
-- General scan for all weapons
|
|
682
|
+
world.searchObjects(Object.Category.WEAPON, scanVol, function(wpn)
|
|
683
|
+
if wpn:isExist() then
|
|
684
|
+
local wpnId = wpn.id_
|
|
685
|
+
local wpnType = wpn:getTypeName()
|
|
686
|
+
local wpnPos = wpn:getPosition().p
|
|
687
|
+
table.insert(allWeaponsFound, { id = wpnId, type = wpnType, x = wpnPos.x, y = wpnPos.y, z = wpnPos.z })
|
|
688
|
+
if wpnType == subName and not tracked_weapons[wpnId] then
|
|
689
|
+
tracked_weapons[wpnId] = {
|
|
690
|
+
wpn = wpn,
|
|
691
|
+
pos = wpnPos,
|
|
692
|
+
speed = wpn:getVelocity(),
|
|
693
|
+
name = wpnType,
|
|
694
|
+
parent = parentName,
|
|
695
|
+
parentVelocity = parentVel
|
|
696
|
+
}
|
|
697
|
+
table.insert(bombletsFound, wpnId)
|
|
698
|
+
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 .. ")")
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
return true
|
|
702
|
+
end)
|
|
703
|
+
-- Log results
|
|
704
|
+
debugMsg("Scanned for submunition '" .. subName .. "' bomblets from '" .. parentName .. "': " .. #bombletsFound .. " found (Attempt " .. attempt .. ")")
|
|
705
|
+
if #allWeaponsFound > 0 then
|
|
706
|
+
local msg = "General scan for '" .. parentName .. "': " .. #allWeaponsFound .. " bomblets released, expected " .. subCount .. " '" .. subName .. "'"
|
|
707
|
+
local typeMismatch = false
|
|
708
|
+
for _, wpn in ipairs(allWeaponsFound) do
|
|
709
|
+
if wpn.type ~= subName then
|
|
710
|
+
typeMismatch = true
|
|
711
|
+
break
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
if typeMismatch then
|
|
715
|
+
msg = msg .. " - Mismatch detected! Actual bomblets: "
|
|
716
|
+
for _, wpn in ipairs(allWeaponsFound) do
|
|
717
|
+
msg = msg .. "'" .. wpn.type .. "' (X: " .. string.format("%.0f", wpn.x) .. ", Y: " .. string.format("%.0f", wpn.y) .. ", Z: " .. string.format("%.0f", wpn.z) .. ") "
|
|
718
|
+
end
|
|
719
|
+
msg = msg .. "Script may need changing."
|
|
720
|
+
end
|
|
721
|
+
debugMsg(msg)
|
|
722
|
+
elseif #bombletsFound == 0 and #allWeaponsFound == 0 then
|
|
723
|
+
debugMsg("No bomblets of any type detected for '" .. parentName .. "' (Attempt " .. attempt .. ")")
|
|
724
|
+
end
|
|
725
|
+
-- Retry if no expected submunitions found
|
|
726
|
+
if #bombletsFound == 0 and attempt < maxAttempts then
|
|
727
|
+
debugMsg("No expected submunition '" .. subName .. "' found on attempt " .. attempt .. ", retrying in 0.5s")
|
|
728
|
+
timer.scheduleFunction(track_wpns_cluster_scan, {parentPos, parentDir, parentName, subName, subCount, subPower, parentVel, attempt + 1}, timer.getTime() + 0.5)
|
|
729
|
+
elseif #bombletsFound == 0 and attempt == maxAttempts then
|
|
730
|
+
debugMsg("No submunition '" .. subName .. "' spawned by DCS for '" .. parentName .. "' after " .. maxAttempts .. " attempts - skipping additional explosions")
|
|
731
|
+
end
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
----[[ ##### Updated track_wpns() Function ##### ]]----
|
|
735
|
+
local recentExplosions = {}
|
|
736
|
+
function track_wpns()
|
|
737
|
+
local weaponsToRemove = {} --Delay removal to ensure all weapons are checked
|
|
738
|
+
for wpn_id_, wpnData in pairs(tracked_weapons) do
|
|
739
|
+
local status, err = pcall(function()
|
|
740
|
+
if wpnData.wpn:isExist() then
|
|
741
|
+
--Update position, direction, speed
|
|
742
|
+
wpnData.pos = wpnData.wpn:getPosition().p
|
|
743
|
+
wpnData.dir = wpnData.wpn:getPosition().x
|
|
744
|
+
wpnData.speed = wpnData.wpn:getVelocity()
|
|
745
|
+
--[[
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
--Tick-by-tick tracking from weapon's actual position
|
|
749
|
+
local tickVol = {
|
|
750
|
+
id = world.VolumeType.SPHERE,
|
|
751
|
+
params = {
|
|
752
|
+
point = wpnData.pos, --Real weapon position
|
|
753
|
+
radius = 150 --150m radius
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
local tickTargets = {}
|
|
757
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, function(obj)
|
|
758
|
+
if obj:isExist() then
|
|
759
|
+
table.insert(tickTargets, {
|
|
760
|
+
name = obj:getTypeName(),
|
|
761
|
+
distance = getDistance3D(wpnData.pos, obj:getPoint()), --3D distance
|
|
762
|
+
position = obj:getPoint(),
|
|
763
|
+
health = obj:getLife() or 0
|
|
764
|
+
})
|
|
765
|
+
end
|
|
766
|
+
return true
|
|
767
|
+
end)
|
|
768
|
+
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")
|
|
769
|
+
for i, target in ipairs(tickTargets) do
|
|
770
|
+
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)
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
]]--
|
|
775
|
+
|
|
776
|
+
--Scan potential blast zone in the last frame before impact
|
|
777
|
+
if splash_damage_options.track_pre_explosion then
|
|
778
|
+
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed))
|
|
779
|
+
local predictedImpact = ip or wpnData.pos
|
|
780
|
+
|
|
781
|
+
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
782
|
+
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
783
|
+
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
784
|
+
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
local explosionPower = base_explosive
|
|
788
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
789
|
+
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
local blastRadius = splash_damage_options.blast_search_radius * 2 --Wider post-scan (180m default)
|
|
793
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
794
|
+
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
--Tight scan while weapon exists
|
|
798
|
+
--local tightRadius = 50
|
|
799
|
+
--if splash_damage_options.use_dynamic_blast_radius then
|
|
800
|
+
-- tightRadius = math.pow(explosionPower, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
801
|
+
--end
|
|
802
|
+
local tightRadius = blastRadius --Use already calculated blastRadius
|
|
803
|
+
local volS = {
|
|
804
|
+
id = world.VolumeType.SPHERE,
|
|
805
|
+
params = {
|
|
806
|
+
point = wpnData.pos, --Use current pos
|
|
807
|
+
radius = tightRadius
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
local tightTargets = {}
|
|
811
|
+
local ifFound = function(foundObject, targets, center)
|
|
812
|
+
if foundObject:isExist() then
|
|
813
|
+
local category = foundObject:getCategory()
|
|
814
|
+
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
815
|
+
category == Object.Category.STATIC then
|
|
816
|
+
table.insert(targets, {
|
|
817
|
+
name = foundObject:getTypeName(),
|
|
818
|
+
distance = getDistance(center, foundObject:getPoint()),
|
|
819
|
+
health = foundObject:getLife() or 0,
|
|
820
|
+
position = foundObject:getPoint(),
|
|
821
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
822
|
+
unit = foundObject
|
|
823
|
+
})
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
return true
|
|
827
|
+
end
|
|
828
|
+
if splash_damage_options.track_pre_explosion_debug then
|
|
829
|
+
debugMsg("Scanning tight radius " .. tightRadius .. "m at current pos while weapon exists")
|
|
830
|
+
end
|
|
831
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, function(obj) ifFound(obj, tightTargets, wpnData.pos) end)
|
|
832
|
+
wpnData.tightTargets = tightTargets --Store for impact
|
|
833
|
+
|
|
834
|
+
--Wider scan for lastKnownTargets
|
|
835
|
+
volS.params.point = predictedImpact
|
|
836
|
+
volS.params.radius = blastRadius
|
|
837
|
+
local foundTargets = {}
|
|
838
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, function(obj) ifFound(obj, foundTargets, predictedImpact) end)
|
|
839
|
+
wpnData.lastKnownTargets = foundTargets
|
|
840
|
+
end
|
|
841
|
+
--Submunition impact handling
|
|
842
|
+
local weaponData = explTable[wpnData.parent or wpnData.name] or { submunition_name = "unknown" }
|
|
843
|
+
if wpnData.name == weaponData.submunition_name then
|
|
844
|
+
local groundHeight = land.getHeight({x = wpnData.pos.x, y = wpnData.pos.z})
|
|
845
|
+
if wpnData.pos.y - groundHeight < 50 then --Impact threshold like old script
|
|
846
|
+
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))
|
|
847
|
+
local parentWeaponData = explTable[wpnData.parent] or { submunition_count = 30, submunition_explosive = 1 }
|
|
848
|
+
local submunitionCount = parentWeaponData.submunition_count or 30
|
|
849
|
+
local submunitionPower = (parentWeaponData.submunition_explosive or 1) * splash_damage_options.cluster_bomblet_damage_modifier * splash_damage_options.overall_scaling
|
|
850
|
+
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
851
|
+
if submunitionCount > 35 then
|
|
852
|
+
local reductionFactor = (60 - 35) / (247 - 35)
|
|
853
|
+
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
854
|
+
if submunitionCount > 60 then submunitionCount = 60 end
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
--Use parent velocity if available, else submunition speed
|
|
858
|
+
local parentDir = wpnData.parentVelocity or wpnData.speed
|
|
859
|
+
local dispersionLength, dispersionWidth = calculate_dispersion(parentDir, 2000) --Match original 2000m
|
|
860
|
+
local dirMag = math.sqrt(parentDir.x^2 + parentDir.z^2)
|
|
861
|
+
local dir = dirMag > 0 and {x = parentDir.x / dirMag, z = parentDir.z / dirMag} or {x = 1, z = 0}
|
|
862
|
+
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")
|
|
863
|
+
for i = 1, submunitionCount do
|
|
864
|
+
local theta = math.random() * 2 * math.pi
|
|
865
|
+
local r = math.sqrt(math.random())
|
|
866
|
+
local xOffset = r * dispersionLength * 0.5 * math.cos(theta)
|
|
867
|
+
local zOffset = r * dispersionWidth * 0.5 * math.sin(theta)
|
|
868
|
+
local subPos = {
|
|
869
|
+
x = wpnData.pos.x + (xOffset * dir.x - zOffset * dir.z),
|
|
870
|
+
z = wpnData.pos.z + (xOffset * dir.z + zOffset * dir.x)
|
|
871
|
+
}
|
|
872
|
+
subPos.y = land.getHeight({x = subPos.x, y = subPos.z})
|
|
873
|
+
debugMsg("Triggering bomblet #" .. i .. " for submunition '" .. wpnData.name .. "' at X: " .. string.format("%.0f", subPos.x) .. ", Z: " .. string.format("%.0f", subPos.z) .. " with power " .. submunitionPower)
|
|
874
|
+
trigger.action.explosion(subPos, submunitionPower)
|
|
875
|
+
end
|
|
876
|
+
table.insert(weaponsToRemove, wpn_id_)
|
|
877
|
+
end
|
|
878
|
+
end
|
|
879
|
+
else
|
|
880
|
+
--Weapon has impacted
|
|
881
|
+
debugMsg("Weapon " .. wpnData.name .. " no longer exists at " .. timer.getTime() .. "s")
|
|
882
|
+
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed)) --terrain intersection point with weapon's nose. Only search out 20 meters though.
|
|
883
|
+
local explosionPoint
|
|
884
|
+
if not ip then --use last calculated IP
|
|
885
|
+
explosionPoint = wpnData.pos
|
|
886
|
+
else --use intersection point
|
|
887
|
+
explosionPoint = ip
|
|
888
|
+
end
|
|
889
|
+
local chosenTargets = wpnData.tightTargets or {}
|
|
890
|
+
local safeToBlast = true
|
|
891
|
+
if splash_damage_options.ordnance_protection then
|
|
892
|
+
local checkVol = { id = world.VolumeType.SPHERE, params = { point = explosionPoint, radius = splash_damage_options.ordnance_protection_radius } }
|
|
893
|
+
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")
|
|
894
|
+
world.searchObjects(Object.Category.WEAPON, checkVol, function(obj)
|
|
895
|
+
if obj:isExist() and tracked_weapons[obj.id_] then
|
|
896
|
+
safeToBlast = false
|
|
897
|
+
debugMsg("Skipping explosion for '" .. wpnData.name .. "' - nearby bomb '" .. tracked_weapons[obj.id_].name .. "' within " .. splash_damage_options.ordnance_protection_radius .. "m")
|
|
898
|
+
return false
|
|
899
|
+
end
|
|
900
|
+
return true
|
|
901
|
+
end)
|
|
902
|
+
end
|
|
903
|
+
if safeToBlast then
|
|
904
|
+
debugMsg("FinalPos Check for '" .. wpnData.name .. "': X: " .. string.format("%.0f", explosionPoint.x) .. ", Y: " .. string.format("%.0f", explosionPoint.y) .. ", Z: " .. string.format("%.0f", explosionPoint.z) .. ")")
|
|
905
|
+
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
906
|
+
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
907
|
+
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
908
|
+
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
local explosionPower = base_explosive
|
|
912
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
913
|
+
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
local blastRadius = splash_damage_options.blast_search_radius * 2 --Wider post-scan (180m default)
|
|
917
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
918
|
+
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
--Store pre-explosion state of all tracked weapons for detection
|
|
923
|
+
local preExplosionWeapons = {}
|
|
924
|
+
if splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
925
|
+
for id, data in pairs(tracked_weapons) do
|
|
926
|
+
if data.wpn:isExist() then
|
|
927
|
+
preExplosionWeapons[id] = {
|
|
928
|
+
name = data.name,
|
|
929
|
+
pos = data.wpn:getPosition().p,
|
|
930
|
+
distance = getDistance3D(explosionPoint, data.wpn:getPosition().p),
|
|
931
|
+
explosive = getWeaponExplosive(data.name) --Store the explosive power
|
|
932
|
+
}
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
--Cluster Bomb Handling
|
|
937
|
+
local weaponData = explTable[wpnData.name] or { explosive = 0, shaped_charge = false }
|
|
938
|
+
local isCluster = weaponData.cluster or false
|
|
939
|
+
if splash_damage_options.cluster_enabled and isCluster then
|
|
940
|
+
local submunitionCount = weaponData.submunition_count or 30
|
|
941
|
+
local submunitionPower = (weaponData.submunition_explosive or 1) * splash_damage_options.cluster_bomblet_damage_modifier * splash_damage_options.overall_scaling
|
|
942
|
+
local submunitionName = weaponData.submunition_name or "unknown"
|
|
943
|
+
--Apply bomblet reduction logic if enabled
|
|
944
|
+
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
945
|
+
if submunitionCount > 35 then
|
|
946
|
+
local reductionFactor = (60 - 35) / (247 - 35)
|
|
947
|
+
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
948
|
+
if submunitionCount > 60 then submunitionCount = 60 end --Cap at 60
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
-- Extended scan with general bomblet detection
|
|
952
|
+
timer.scheduleFunction(track_wpns_cluster_scan, {explosionPoint, wpnData.dir, wpnData.name, submunitionName, submunitionCount, submunitionPower, wpnData.speed}, timer.getTime() + 0.3)
|
|
953
|
+
else
|
|
954
|
+
--Standard explosion handling
|
|
955
|
+
if splash_damage_options.larger_explosions then
|
|
956
|
+
debugMsg("Triggering initial explosion for '" .. wpnData.name .. "' at power " .. explosionPower)
|
|
957
|
+
trigger.action.explosion(explosionPoint, explosionPower)
|
|
958
|
+
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
959
|
+
debugMsg("Added to recentExplosions for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
960
|
+
end
|
|
961
|
+
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosionPower, isShapedCharge)
|
|
962
|
+
end
|
|
963
|
+
--detect_ordnance_destruction comes before recent_large_explosion_snap in original
|
|
964
|
+
if splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
965
|
+
timer.scheduleFunction(function(args)
|
|
966
|
+
local explosionPoint = args[1]
|
|
967
|
+
local blastRadius = args[2]
|
|
968
|
+
local triggeringWeapon = args[3]
|
|
969
|
+
local preExplosionWeapons = args[4]
|
|
970
|
+
for id, preData in pairs(preExplosionWeapons) do
|
|
971
|
+
if tracked_weapons[id] and not tracked_weapons[id].wpn:isExist() then
|
|
972
|
+
if preData.distance <= blastRadius then
|
|
973
|
+
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)
|
|
974
|
+
gameMsg(msg)
|
|
975
|
+
debugMsg(msg)
|
|
976
|
+
env.info(msg)
|
|
977
|
+
if splash_damage_options.snap_to_ground_if_destroyed_by_large_explosion then
|
|
978
|
+
local groundPos = {
|
|
979
|
+
x = preData.pos.x,
|
|
980
|
+
y = land.getHeight({x = preData.pos.x, y = preData.pos.z}),
|
|
981
|
+
z = preData.pos.z
|
|
982
|
+
}
|
|
983
|
+
local destroyedWeaponPower, isShapedCharge = preData.explosive
|
|
984
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.overall_scaling
|
|
985
|
+
if splash_damage_options.rocket_multiplier and tracked_weapons[id].cat == Weapon.Category.ROCKET then
|
|
986
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.rocket_multiplier
|
|
987
|
+
end
|
|
988
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
989
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.shaped_charge_multiplier
|
|
990
|
+
end
|
|
991
|
+
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)
|
|
992
|
+
trigger.action.explosion(groundPos, destroyedWeaponPower)
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
end
|
|
996
|
+
end
|
|
997
|
+
end, {explosionPoint, blastRadius, wpnData.name, preExplosionWeapons}, timer.getTime() + 0.2)
|
|
998
|
+
end
|
|
999
|
+
--recent_large_explosion_snap comes after main explosion and detect_ordnance_destruction
|
|
1000
|
+
if splash_damage_options.larger_explosions and splash_damage_options.recent_large_explosion_snap then
|
|
1001
|
+
local currentTime = timer.getTime()
|
|
1002
|
+
for id, data in pairs(tracked_weapons) do
|
|
1003
|
+
if id ~= wpn_id_ and not data.wpn:isExist() then
|
|
1004
|
+
local terrainHeight = land.getHeight({x = data.pos.x, y = data.pos.z})
|
|
1005
|
+
local isMidAir = data.pos.y > terrainHeight + 5
|
|
1006
|
+
local snapTriggered = false
|
|
1007
|
+
for _, explosion in ipairs(recentExplosions) do
|
|
1008
|
+
local timeDiff = currentTime - explosion.time
|
|
1009
|
+
local distance = getDistance3D(data.pos, explosion.pos)
|
|
1010
|
+
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")
|
|
1011
|
+
if timeDiff <= splash_damage_options.recent_large_explosion_time and distance <= splash_damage_options.recent_large_explosion_range then
|
|
1012
|
+
if isMidAir then
|
|
1013
|
+
local groundPos = { x = data.pos.x, y = terrainHeight, z = data.pos.z }
|
|
1014
|
+
local destroyedWeaponPower, isShapedCharge = getWeaponExplosive(data.name)
|
|
1015
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.overall_scaling
|
|
1016
|
+
if splash_damage_options.rocket_multiplier and data.cat == Weapon.Category.ROCKET then
|
|
1017
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.rocket_multiplier
|
|
1018
|
+
end
|
|
1019
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
1020
|
+
destroyedWeaponPower = destroyedWeaponPower * splash_damage_options.shaped_charge_multiplier
|
|
1021
|
+
end
|
|
1022
|
+
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)
|
|
1023
|
+
trigger.action.explosion(groundPos, destroyedWeaponPower)
|
|
1024
|
+
snapTriggered = true
|
|
1025
|
+
table.insert(weaponsToRemove, id)
|
|
1026
|
+
break
|
|
1027
|
+
else
|
|
1028
|
+
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")
|
|
1029
|
+
snapTriggered = true
|
|
1030
|
+
break
|
|
1031
|
+
end
|
|
1032
|
+
end
|
|
1033
|
+
end
|
|
1034
|
+
if not snapTriggered then
|
|
1035
|
+
if isMidAir then
|
|
1036
|
+
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")
|
|
1037
|
+
else
|
|
1038
|
+
debugMsg("Weapon " .. data.name .. " impacted ground, not processed by recent large explosion settings")
|
|
1039
|
+
end
|
|
1040
|
+
end
|
|
1041
|
+
end
|
|
1042
|
+
end
|
|
1043
|
+
local newExplosions = {}
|
|
1044
|
+
for _, explosion in ipairs(recentExplosions) do
|
|
1045
|
+
if currentTime - explosion.time <= splash_damage_options.recent_large_explosion_time then
|
|
1046
|
+
table.insert(newExplosions, explosion)
|
|
1047
|
+
end
|
|
1048
|
+
end
|
|
1049
|
+
recentExplosions = newExplosions
|
|
1050
|
+
end
|
|
1051
|
+
--Mark units as destroyed to avoid MiST accessing them
|
|
1052
|
+
local destroyedUnits = {}
|
|
1053
|
+
for _, target in ipairs(chosenTargets) do
|
|
1054
|
+
if target.unit:isExist() and target.health > 0 and target.unit:getLife() <= 0 then
|
|
1055
|
+
destroyedUnits[target.name] = true
|
|
1056
|
+
debugMsg("Marked " .. target.name .. " as destroyed pre-impact")
|
|
1057
|
+
end
|
|
1058
|
+
end
|
|
1059
|
+
--Schedule explosion handling with original 0.1-second delay, enhanced error handling
|
|
1060
|
+
timer.scheduleFunction(function(args)
|
|
1061
|
+
local finalPos = args[1]
|
|
1062
|
+
local explosionPoint = args[2]
|
|
1063
|
+
local explosionPower = args[3]
|
|
1064
|
+
local isShapedCharge = args[4]
|
|
1065
|
+
local blastRadius = args[5]
|
|
1066
|
+
local chosenTargets = args[6]
|
|
1067
|
+
local weaponName = args[7]
|
|
1068
|
+
local wpnData = args[8]
|
|
1069
|
+
debugMsg("Starting impact handling for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1070
|
+
local status, err = pcall(function()
|
|
1071
|
+
--Log pre-explosion targets
|
|
1072
|
+
if splash_damage_options.track_pre_explosion then
|
|
1073
|
+
if #chosenTargets > 0 then
|
|
1074
|
+
local msg = "Targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame, using finalPos):\n"
|
|
1075
|
+
for i, target in ipairs(chosenTargets) do
|
|
1076
|
+
msg = msg .. "- " .. target.name .. " (Dist: " .. string.format("%.1f", target.distance) .. "m, Health: " .. target.health .. ")\n"
|
|
1077
|
+
end
|
|
1078
|
+
debugMsg(msg)
|
|
1079
|
+
env.info("SplashDamage Pre-Explosion (Last Frame): " .. msg)
|
|
1080
|
+
else
|
|
1081
|
+
debugMsg("No targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame)")
|
|
1082
|
+
env.info("SplashDamage Pre-Explosion (Last Frame): No targets in blast zone for " .. weaponName)
|
|
1083
|
+
end
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.ordnance, explosionPower, isShapedCharge)
|
|
1087
|
+
|
|
1088
|
+
--Post-explosion analysis and queue cargo effects
|
|
1089
|
+
if splash_damage_options.track_pre_explosion then
|
|
1090
|
+
timer.scheduleFunction(function(innerArgs)
|
|
1091
|
+
local impactPoint = innerArgs[1]
|
|
1092
|
+
local blastRadius = innerArgs[2]
|
|
1093
|
+
local preExplosionTargets = innerArgs[3] or {}
|
|
1094
|
+
local weaponName = innerArgs[4]
|
|
1095
|
+
local weaponPower = innerArgs[5]
|
|
1096
|
+
debugMsg("Starting post-explosion analysis for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1097
|
+
|
|
1098
|
+
--Scan all units in wider radius
|
|
1099
|
+
local postExplosionTargets = {}
|
|
1100
|
+
local volS = {
|
|
1101
|
+
id = world.VolumeType.SPHERE,
|
|
1102
|
+
params = {
|
|
1103
|
+
point = impactPoint,
|
|
1104
|
+
radius = blastRadius
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
local ifFound = function(foundObject)
|
|
1109
|
+
if foundObject:isExist() then
|
|
1110
|
+
local category = foundObject:getCategory()
|
|
1111
|
+
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
1112
|
+
category == Object.Category.STATIC then
|
|
1113
|
+
table.insert(postExplosionTargets, {
|
|
1114
|
+
name = foundObject:getTypeName(),
|
|
1115
|
+
health = foundObject:getLife() or 0,
|
|
1116
|
+
position = foundObject:getPoint(),
|
|
1117
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0
|
|
1118
|
+
})
|
|
1119
|
+
end
|
|
1120
|
+
end
|
|
1121
|
+
return true
|
|
1122
|
+
end
|
|
1123
|
+
|
|
1124
|
+
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
1125
|
+
|
|
1126
|
+
local msg = "Post-explosion analysis for " .. weaponName .. ":\n"
|
|
1127
|
+
|
|
1128
|
+
--Match pre-detected units
|
|
1129
|
+
for _, preTarget in ipairs(preExplosionTargets) do
|
|
1130
|
+
local found = false
|
|
1131
|
+
local postHealth = 0
|
|
1132
|
+
local postPosition = nil
|
|
1133
|
+
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1134
|
+
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1135
|
+
found = true
|
|
1136
|
+
postHealth = postTarget.health
|
|
1137
|
+
postPosition = postTarget.position
|
|
1138
|
+
break
|
|
1139
|
+
end
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
local healthPercent = preTarget.maxHealth > 0 and (postHealth / preTarget.maxHealth * 100) or 0
|
|
1143
|
+
local status = ""
|
|
1144
|
+
|
|
1145
|
+
if not found or postHealth <= 0 then
|
|
1146
|
+
status = "WAS FULLY DESTROYED"
|
|
1147
|
+
elseif healthPercent < splash_damage_options.cargo_damage_threshold then
|
|
1148
|
+
status = "WAS DAMAGED BELOW THRESHOLD"
|
|
1149
|
+
else
|
|
1150
|
+
status = "SURVIVED (Health: " .. postHealth .. ")"
|
|
1151
|
+
end
|
|
1152
|
+
|
|
1153
|
+
--Always include coords in status message
|
|
1154
|
+
local coords = found and postPosition or preTarget.position
|
|
1155
|
+
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: " .. preTarget.health .. ", Post: " .. postHealth .. ")"
|
|
1156
|
+
--Check if target is in cargoUnits and within blast radius
|
|
1157
|
+
local cargoData = cargoUnits[preTarget.name]
|
|
1158
|
+
if cargoData and preTarget.distance <= blastRadius and
|
|
1159
|
+
(not found or postHealth <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1160
|
+
|
|
1161
|
+
if splash_damage_options.enable_cargo_effects then
|
|
1162
|
+
local cargoPower = cargoData.cargoExplosionPower or weaponPower --Use fixed power or fallback
|
|
1163
|
+
table.insert(cargoEffectsQueue, {
|
|
1164
|
+
name = preTarget.name,
|
|
1165
|
+
distance = preTarget.distance,
|
|
1166
|
+
coords = coords,
|
|
1167
|
+
power = cargoPower,
|
|
1168
|
+
explosion = cargoData.cargoExplosion,
|
|
1169
|
+
cookOff = cargoData.cargoCookOff,
|
|
1170
|
+
cookOffCount = cargoData.cookOffCount,
|
|
1171
|
+
cookOffPower = cargoData.cookOffPower,
|
|
1172
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
1173
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1174
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1175
|
+
isTanker = cargoData.isTanker,
|
|
1176
|
+
flameSize = cargoData.flameSize,
|
|
1177
|
+
flameDuration = cargoData.flameDuration
|
|
1178
|
+
})
|
|
1179
|
+
statusMsg = statusMsg .. " WITH CARGO EXPLOSION (Power: " .. cargoPower .. ")"
|
|
1180
|
+
if cargoData.cargoCookOff and cargoData.cookOffCount > 0 then
|
|
1181
|
+
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1182
|
+
end
|
|
1183
|
+
end
|
|
1184
|
+
end
|
|
1185
|
+
|
|
1186
|
+
msg = msg .. "- " .. preTarget.name .. " " .. statusMsg .. "\n"
|
|
1187
|
+
end
|
|
1188
|
+
--Check for additional units
|
|
1189
|
+
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1190
|
+
local isPreDetected = false
|
|
1191
|
+
for _, preTarget in ipairs(preExplosionTargets) do
|
|
1192
|
+
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1193
|
+
isPreDetected = true
|
|
1194
|
+
break
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
if not isPreDetected then
|
|
1198
|
+
local coords = postTarget.position
|
|
1199
|
+
local healthPercent = postTarget.maxHealth > 0 and (postTarget.health / postTarget.maxHealth * 100) or 0
|
|
1200
|
+
local status = postTarget.health <= 0 and "WAS FULLY DESTROYED" or
|
|
1201
|
+
(healthPercent < splash_damage_options.cargo_damage_threshold and "WAS DAMAGED BELOW THRESHOLD" or
|
|
1202
|
+
"SURVIVED (Health: " .. postTarget.health .. ")")
|
|
1203
|
+
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: Unknown, Post: " .. postTarget.health .. ")"
|
|
1204
|
+
local cargoData = cargoUnits[postTarget.name]
|
|
1205
|
+
if cargoData and (postTarget.health <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1206
|
+
if splash_damage_options.enable_cargo_effects then
|
|
1207
|
+
local cargoPower = cargoData.cargoExplosionPower or weaponPower --Use fixed power or fallback
|
|
1208
|
+
local distance = getDistance(impactPoint, coords)
|
|
1209
|
+
table.insert(cargoEffectsQueue, {
|
|
1210
|
+
name = postTarget.name,
|
|
1211
|
+
distance = distance,
|
|
1212
|
+
coords = coords,
|
|
1213
|
+
power = cargoPower,
|
|
1214
|
+
explosion = cargoData.cargoExplosion,
|
|
1215
|
+
cookOff = cargoData.cargoCookOff,
|
|
1216
|
+
cookOffCount = cargoData.cookOffCount,
|
|
1217
|
+
cookOffPower = cargoData.cookOffPower,
|
|
1218
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
1219
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1220
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1221
|
+
isTanker = cargoData.isTanker,
|
|
1222
|
+
flameSize = cargoData.flameSize,
|
|
1223
|
+
flameDuration = cargoData.flameDuration
|
|
1224
|
+
})
|
|
1225
|
+
statusMsg = statusMsg .. " WITH CARGO EXPLOSION (Power: " .. cargoPower .. ")"
|
|
1226
|
+
if cargoData.cargoCookOff and cargoData.cookOffCount > 0 then
|
|
1227
|
+
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1228
|
+
end
|
|
1229
|
+
end
|
|
1230
|
+
end
|
|
1231
|
+
msg = msg .. "- " .. postTarget.name .. " " .. statusMsg .. "\n"
|
|
1232
|
+
end
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
--Schedule all queued cargo effects
|
|
1236
|
+
if #cargoEffectsQueue > 0 then
|
|
1237
|
+
local effectIndex = 0
|
|
1238
|
+
local processedCargoUnits = {} --Track processed units
|
|
1239
|
+
local flamePositions = {} --Track flame coords with 3m radius
|
|
1240
|
+
for _, effect in ipairs(cargoEffectsQueue) do
|
|
1241
|
+
local unitKey = effect.name .. "_" .. effect.coords.x .. "_" .. effect.coords.z
|
|
1242
|
+
if not processedUnitsGlobal[unitKey] and not processedCargoUnits[unitKey] then
|
|
1243
|
+
if effect.explosion then
|
|
1244
|
+
debugMsg("Triggering cargo explosion for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. effectIndex .. "s")
|
|
1245
|
+
timer.scheduleFunction(function(params)
|
|
1246
|
+
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])
|
|
1247
|
+
trigger.action.explosion(params[1], params[2])
|
|
1248
|
+
end, {effect.coords, effect.power}, timer.getTime() + effectIndex + 0.1) --Slight delay for visibility
|
|
1249
|
+
if effect.isTanker then
|
|
1250
|
+
local flameSize = effect.flameSize or 3
|
|
1251
|
+
local flameDuration = effect.flameDuration --Use cargoUnits value directly, no default
|
|
1252
|
+
local flameDensity = 1.0 --Max density for visibility
|
|
1253
|
+
local effectId = effectSmokeId
|
|
1254
|
+
effectSmokeId = effectSmokeId + 1
|
|
1255
|
+
--Check for nearby flames within 3m
|
|
1256
|
+
local isDuplicate = false
|
|
1257
|
+
for _, pos in pairs(flamePositions) do
|
|
1258
|
+
if getDistance3D(effect.coords, pos) < 3 then
|
|
1259
|
+
isDuplicate = true
|
|
1260
|
+
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))
|
|
1261
|
+
break
|
|
1262
|
+
end
|
|
1263
|
+
end
|
|
1264
|
+
if not isDuplicate then
|
|
1265
|
+
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")
|
|
1266
|
+
timer.scheduleFunction(function(params)
|
|
1267
|
+
--Adjust Y-coordinate to terrain height + offset
|
|
1268
|
+
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
1269
|
+
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
1270
|
+
debugMsg("Spawning flame effect at X: " .. string.format("%.0f", adjustedCoords.x) .. ", Y: " .. string.format("%.0f", adjustedCoords.y) .. ", Z: " .. string.format("%.0f", adjustedCoords.z))
|
|
1271
|
+
trigger.action.explosion(adjustedCoords, 10) --Small explosion to force visibility
|
|
1272
|
+
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
1273
|
+
end, {effect.coords, flameSize, flameDensity, effectId}, timer.getTime() + effectIndex + 0.2) --Slight delay
|
|
1274
|
+
timer.scheduleFunction(function(id)
|
|
1275
|
+
debugMsg("Stopping flame effect for " .. effect.name .. " (ID: " .. id .. ")")
|
|
1276
|
+
trigger.action.effectSmokeStop(id)
|
|
1277
|
+
end, effectId, timer.getTime() + effectIndex + flameDuration + 0.2)
|
|
1278
|
+
table.insert(flamePositions, effect.coords)
|
|
1279
|
+
end
|
|
1280
|
+
end
|
|
1281
|
+
end
|
|
1282
|
+
debugMsg("Checking cook-off for " .. effect.name .. ": cookOff=" .. tostring(effect.cookOff) .. ", count=" .. tostring(effect.cookOffCount))
|
|
1283
|
+
if effect.cookOff and effect.cookOffCount > 0 then
|
|
1284
|
+
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")
|
|
1285
|
+
for i = 1, effect.cookOffCount do
|
|
1286
|
+
local delay = effect.cookOffRandomTiming and math.random() * effect.cookOffDuration or (i - 1) * (effect.cookOffDuration / effect.cookOffCount)
|
|
1287
|
+
local basePower = effect.cookOffPower
|
|
1288
|
+
local powerVariation = effect.cookOffPowerRandom / 100
|
|
1289
|
+
local cookOffPower = effect.cookOffPowerRandom == 0 and basePower or basePower * (1 + powerVariation * (math.random() * 2 - 1))
|
|
1290
|
+
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))
|
|
1291
|
+
timer.scheduleFunction(function(params)
|
|
1292
|
+
local pos = params[1]
|
|
1293
|
+
local power = params[2]
|
|
1294
|
+
debugMsg("Executing cook-off at " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", pos.x, pos.y, pos.z) .. " with power " .. power)
|
|
1295
|
+
trigger.action.explosion(pos, power)
|
|
1296
|
+
end, {effect.coords, cookOffPower}, timer.getTime() + effectIndex + delay)
|
|
1297
|
+
end
|
|
1298
|
+
--Debris burst only if cook-off is true and enabled
|
|
1299
|
+
if splash_damage_options.debris_effects then
|
|
1300
|
+
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
1301
|
+
for j = 1, debrisCount do
|
|
1302
|
+
--Random spherical offset
|
|
1303
|
+
local theta = math.random() * 2 * math.pi --Horizontal angle
|
|
1304
|
+
local phi = math.acos(math.random() * 2 - 1) --Vertical angle for sphere
|
|
1305
|
+
local minDist = splash_damage_options.debris_max_distance * 0.1 --10% of max
|
|
1306
|
+
local maxDist = splash_damage_options.debris_max_distance
|
|
1307
|
+
local r = math.random() * (maxDist - minDist) + minDist --10% to full max distance
|
|
1308
|
+
local debrisX = effect.coords.x + r * math.sin(phi) * math.cos(theta)
|
|
1309
|
+
local debrisZ = effect.coords.z + r * math.sin(phi) * math.sin(theta)
|
|
1310
|
+
local terrainY = land.getHeight({x = debrisX, y = debrisZ})
|
|
1311
|
+
local debrisY = terrainY + math.random() * maxDist --0 to max_distance above ground
|
|
1312
|
+
local debrisPos = {x = debrisX, y = debrisY, z = debrisZ}
|
|
1313
|
+
local debrisPower = splash_damage_options.debris_power
|
|
1314
|
+
local debrisDelay = (j - 1) * (effect.cookOffDuration / debrisCount) --Spread over cook-off duration
|
|
1315
|
+
timer.scheduleFunction(function(debrisArgs)
|
|
1316
|
+
local dPos = debrisArgs[1]
|
|
1317
|
+
local dPower = debrisArgs[2]
|
|
1318
|
+
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)
|
|
1319
|
+
trigger.action.explosion(dPos, dPower)
|
|
1320
|
+
end, {debrisPos, debrisPower}, timer.getTime() + effectIndex + debrisDelay)
|
|
1321
|
+
end
|
|
1322
|
+
end
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
processedCargoUnits[unitKey] = true
|
|
1327
|
+
processedUnitsGlobal[unitKey] = true
|
|
1328
|
+
effectIndex = effectIndex + 3 --3 secs spacing if not random
|
|
1329
|
+
end
|
|
1330
|
+
end
|
|
1331
|
+
--Clear the queue after scheduling
|
|
1332
|
+
cargoEffectsQueue = {}
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
debugMsg(msg)
|
|
1336
|
+
env.info("SplashDamage Post-Explosion: " .. msg)
|
|
1337
|
+
end, {finalPos, blastRadius, chosenTargets, weaponName, explosionPower}, timer.getTime() + 1)
|
|
1338
|
+
end
|
|
1339
|
+
end)
|
|
1340
|
+
if not status then
|
|
1341
|
+
debugMsg("Impact handling error for '" .. weaponName .. "': " .. err)
|
|
1342
|
+
end
|
|
1343
|
+
end, {explosionPoint, explosionPoint, explosionPower, isShapedCharge, blastRadius, chosenTargets, wpnData.name, wpnData}, timer.getTime() + 0.1)
|
|
1344
|
+
else
|
|
1345
|
+
debugMsg("Explosion skipped due to ordnance protection for '" .. wpnData.name .. "'")
|
|
1346
|
+
if splash_damage_options.larger_explosions then
|
|
1347
|
+
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
1348
|
+
debugMsg("Skipped explosion logged for snap check for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
1349
|
+
end
|
|
1350
|
+
end
|
|
1351
|
+
table.insert(weaponsToRemove, wpn_id_)
|
|
1352
|
+
end
|
|
1353
|
+
end)
|
|
1354
|
+
if not status then
|
|
1355
|
+
debugMsg("Error in track_wpns for '" .. (wpnData.name or "unknown weapon") .. "': " .. err)
|
|
1356
|
+
end
|
|
1357
|
+
end
|
|
1358
|
+
--Perform all removals after iteration
|
|
1359
|
+
for _, id in ipairs(weaponsToRemove) do
|
|
1360
|
+
tracked_weapons[id] = nil
|
|
1361
|
+
end
|
|
1362
|
+
return timer.getTime() + refreshRate
|
|
1363
|
+
end
|
|
1364
|
+
function onWpnEvent(event)
|
|
1365
|
+
if event.id == world.event.S_EVENT_SHOT then
|
|
1366
|
+
if event.weapon then
|
|
1367
|
+
local ordnance = event.weapon
|
|
1368
|
+
local typeName = trim(ordnance:getTypeName())
|
|
1369
|
+
env.info("Weapon fired: [" .. typeName .. "]")
|
|
1370
|
+
debugMsg("Weapon fired: [" .. typeName .. "]")
|
|
1371
|
+
|
|
1372
|
+
if string.find(typeName, "weapons.shells") then
|
|
1373
|
+
debugMsg("Event shot, but not tracking: " .. typeName)
|
|
1374
|
+
env.info("SplashDamage: event shot, but not tracking: " .. typeName .. " (" .. event.initiator:getTypeName() .. ")")
|
|
1375
|
+
return
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1378
|
+
if not explTable[typeName] then
|
|
1379
|
+
env.info("SplashDamage: " .. typeName .. " missing from script (" .. event.initiator:getTypeName() .. ")")
|
|
1380
|
+
if splash_damage_options.weapon_missing_message == true then
|
|
1381
|
+
trigger.action.outText("SplashDamage: " .. typeName .. " missing from script (" .. (event.initiator and event.initiator:isExist() and event.initiator:getTypeName() or "no initiator") .. ")", 3)
|
|
1382
|
+
-- if mist and mist.utils and mist.utils.tableShow then --Only if MiST is present
|
|
1383
|
+
-- local success, desc = pcall(mist.utils.tableShow, ordnance:getDesc())
|
|
1384
|
+
-- if success then
|
|
1385
|
+
-- debugMsg("desc for [" .. typeName .. "]: " .. desc)
|
|
1386
|
+
-- else
|
|
1387
|
+
-- debugMsg("Could not retrieve description for [" .. typeName .. "]. Object may no longer exist.")
|
|
1388
|
+
-- end
|
|
1389
|
+
-- end
|
|
1390
|
+
env.info("Current keys in explTable:")
|
|
1391
|
+
for k, v in pairs(explTable) do
|
|
1392
|
+
env.info("Key: [" .. k .. "]")
|
|
1393
|
+
end
|
|
1394
|
+
end
|
|
1395
|
+
end
|
|
1396
|
+
|
|
1397
|
+
if (ordnance:getDesc().category ~= 0) and event.initiator then
|
|
1398
|
+
if ordnance:getDesc().category == 1 then
|
|
1399
|
+
if (ordnance:getDesc().MissileCategory ~= 1 and ordnance:getDesc().MissileCategory ~= 2) then
|
|
1400
|
+
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() }
|
|
1401
|
+
end
|
|
1402
|
+
else
|
|
1403
|
+
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() }
|
|
1404
|
+
end
|
|
1405
|
+
end
|
|
1406
|
+
end
|
|
1407
|
+
end
|
|
1408
|
+
end
|
|
1409
|
+
|
|
1410
|
+
local function protectedCall(...)
|
|
1411
|
+
local status, retval = pcall(...)
|
|
1412
|
+
if not status then
|
|
1413
|
+
env.warning("Splash damage script error... gracefully caught! " .. retval, true)
|
|
1414
|
+
end
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1417
|
+
function WpnHandler:onEvent(event)
|
|
1418
|
+
protectedCall(onWpnEvent, event)
|
|
1419
|
+
end
|
|
1420
|
+
|
|
1421
|
+
function explodeObject(args)
|
|
1422
|
+
local point = args[1]
|
|
1423
|
+
local distance = args[2]
|
|
1424
|
+
local power = args[3]
|
|
1425
|
+
trigger.action.explosion(point, power)
|
|
1426
|
+
end
|
|
1427
|
+
|
|
1428
|
+
function blastWave(_point, _radius, weapon, power, isShapedCharge)
|
|
1429
|
+
if isShapedCharge then
|
|
1430
|
+
_radius = _radius * splash_damage_options.shaped_charge_multiplier
|
|
1431
|
+
end
|
|
1432
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
1433
|
+
local dynamicRadius = math.pow(power, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1434
|
+
if isShapedCharge then
|
|
1435
|
+
_radius = dynamicRadius * splash_damage_options.shaped_charge_multiplier
|
|
1436
|
+
else
|
|
1437
|
+
_radius = dynamicRadius
|
|
1438
|
+
end
|
|
1439
|
+
end
|
|
1440
|
+
|
|
1441
|
+
local foundUnits = {}
|
|
1442
|
+
local volS = {
|
|
1443
|
+
id = world.VolumeType.SPHERE,
|
|
1444
|
+
params = {
|
|
1445
|
+
point = _point,
|
|
1446
|
+
radius = _radius
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
local ifFound = function(foundObject, val)
|
|
1451
|
+
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT and foundObject:getCategory() == Object.Category.UNIT then
|
|
1452
|
+
foundUnits[#foundUnits + 1] = foundObject
|
|
1453
|
+
end
|
|
1454
|
+
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1455
|
+
if splash_damage_options.blast_stun == true then
|
|
1456
|
+
--suppressUnit(foundObject, 2, weapon)
|
|
1457
|
+
end
|
|
1458
|
+
end
|
|
1459
|
+
if splash_damage_options.wave_explosions then
|
|
1460
|
+
local obj = foundObject
|
|
1461
|
+
local obj_location = obj:getPoint()
|
|
1462
|
+
local dist = getDistance(_point, obj_location)
|
|
1463
|
+
local timing = dist / 500
|
|
1464
|
+
if obj:isExist() and tableHasKey(obj:getDesc(), "box") then
|
|
1465
|
+
local length = (obj:getDesc().box.max.x + math.abs(obj:getDesc().box.min.x))
|
|
1466
|
+
local height = (obj:getDesc().box.max.y + math.abs(obj:getDesc().box.min.y))
|
|
1467
|
+
local depth = (obj:getDesc().box.max.z + math.abs(obj:getDesc().box.min.z))
|
|
1468
|
+
local _length = length
|
|
1469
|
+
local _depth = depth
|
|
1470
|
+
if depth > length then
|
|
1471
|
+
_length = depth
|
|
1472
|
+
_depth = length
|
|
1473
|
+
end
|
|
1474
|
+
local surface_distance = dist - _depth / 2
|
|
1475
|
+
local scaled_power_factor = 0.006 * power + 1
|
|
1476
|
+
local intensity = (power * scaled_power_factor) / (4 * math.pi * surface_distance^2)
|
|
1477
|
+
local surface_area = _length * height
|
|
1478
|
+
local damage_for_surface = intensity * surface_area
|
|
1479
|
+
if damage_for_surface > splash_damage_options.cascade_damage_threshold then
|
|
1480
|
+
local explosion_size = damage_for_surface
|
|
1481
|
+
if obj:getDesc().category == Unit.Category.STRUCTURE then
|
|
1482
|
+
explosion_size = intensity * splash_damage_options.static_damage_boost
|
|
1483
|
+
end
|
|
1484
|
+
if explosion_size > power then explosion_size = power end
|
|
1485
|
+
local triggerExplosion = false
|
|
1486
|
+
if splash_damage_options.always_cascade_explode then
|
|
1487
|
+
triggerExplosion = true
|
|
1488
|
+
else
|
|
1489
|
+
if obj:getDesc().life then
|
|
1490
|
+
local healthPercent = (obj:getLife() / obj:getDesc().life) * 100
|
|
1491
|
+
if healthPercent <= splash_damage_options.cascade_explode_threshold then
|
|
1492
|
+
triggerExplosion = true
|
|
1493
|
+
end
|
|
1494
|
+
--Queue cargo effects for units below
|
|
1495
|
+
local cargoData = cargoUnits[obj:getTypeName()]
|
|
1496
|
+
if cargoData and healthPercent <= splash_damage_options.cargo_damage_threshold and splash_damage_options.enable_cargo_effects then
|
|
1497
|
+
local cargoPower = power * cargoData.cargoExplosionMult
|
|
1498
|
+
table.insert(cargoEffectsQueue, {
|
|
1499
|
+
name = obj:getTypeName(),
|
|
1500
|
+
distance = dist,
|
|
1501
|
+
coords = obj_location,
|
|
1502
|
+
power = cargoPower,
|
|
1503
|
+
explosion = cargoData.cargoExplosion,
|
|
1504
|
+
cookOff = cargoData.cargoCookOff,
|
|
1505
|
+
cookOffCount = cargoData.cookOffCount,
|
|
1506
|
+
cookOffPower = cargoData.cookOffPower,
|
|
1507
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
1508
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1509
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1510
|
+
isTanker = cargoData.isTanker,
|
|
1511
|
+
flameSize = cargoData.flameSize,
|
|
1512
|
+
flameDuration = cargoData.flameDuration
|
|
1513
|
+
})
|
|
1514
|
+
end
|
|
1515
|
+
else
|
|
1516
|
+
triggerExplosion = true
|
|
1517
|
+
end
|
|
1518
|
+
if not triggerExplosion and obj:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1519
|
+
local health = obj:getLife() or 0
|
|
1520
|
+
if health <= 0 then
|
|
1521
|
+
triggerExplosion = true
|
|
1522
|
+
end
|
|
1523
|
+
end
|
|
1524
|
+
end
|
|
1525
|
+
if triggerExplosion then
|
|
1526
|
+
timer.scheduleFunction(explodeObject, {obj_location, dist, explosion_size * splash_damage_options.cascade_scaling}, timer.getTime() + timing)
|
|
1527
|
+
end
|
|
1528
|
+
end
|
|
1529
|
+
end
|
|
1530
|
+
end
|
|
1531
|
+
return true
|
|
1532
|
+
end
|
|
1533
|
+
|
|
1534
|
+
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
|
1535
|
+
world.searchObjects(Object.Category.STATIC, volS, ifFound)
|
|
1536
|
+
world.searchObjects(Object.Category.SCENERY, volS, ifFound)
|
|
1537
|
+
world.searchObjects(Object.Category.CARGO, volS, ifFound)
|
|
1538
|
+
if splash_damage_options.damage_model then
|
|
1539
|
+
timer.scheduleFunction(modelUnitDamage, foundUnits, timer.getTime() + 1.5)
|
|
1540
|
+
end
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1543
|
+
function modelUnitDamage(units)
|
|
1544
|
+
for i, unit in ipairs(units) do
|
|
1545
|
+
if unit:isExist() then
|
|
1546
|
+
local health = (unit:getLife() / unit:getDesc().life) * 100
|
|
1547
|
+
if unit:hasAttribute("Infantry") and health > 0 then
|
|
1548
|
+
if health <= splash_damage_options.infantry_cant_fire_health then
|
|
1549
|
+
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1550
|
+
end
|
|
1551
|
+
end
|
|
1552
|
+
if unit:getDesc().category == Unit.Category.GROUND_UNIT and (not unit:hasAttribute("Infantry")) and health > 0 then
|
|
1553
|
+
if health <= splash_damage_options.unit_cant_fire_health then
|
|
1554
|
+
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1555
|
+
gameMsg(unit:getTypeName() .. " weapons disabled")
|
|
1556
|
+
end
|
|
1557
|
+
if health <= splash_damage_options.unit_disabled_health and health > 0 then
|
|
1558
|
+
unit:getController():setTask({id = 'Hold', params = {}})
|
|
1559
|
+
unit:getController():setOnOff(false)
|
|
1560
|
+
gameMsg(unit:getTypeName() .. " disabled")
|
|
1561
|
+
end
|
|
1562
|
+
end
|
|
1563
|
+
end
|
|
1564
|
+
end
|
|
1565
|
+
end
|
|
1566
|
+
|
|
1567
|
+
function updateSplashDamageSetting(setting, increment)
|
|
1568
|
+
if not splash_damage_options[setting] then
|
|
1569
|
+
env.info("Error: Setting " .. setting .. " does not exist.")
|
|
1570
|
+
return
|
|
1571
|
+
end
|
|
1572
|
+
|
|
1573
|
+
local newValue = math.max(0, splash_damage_options[setting] + increment)
|
|
1574
|
+
env.info("Updating " .. setting .. " from " .. tostring(splash_damage_options[setting]) .. " to " .. tostring(newValue))
|
|
1575
|
+
splash_damage_options[setting] = newValue
|
|
1576
|
+
trigger.action.outText("Updated " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1577
|
+
end
|
|
1578
|
+
|
|
1579
|
+
function toggleSplashDamageSetting(setting)
|
|
1580
|
+
splash_damage_options[setting] = not splash_damage_options[setting]
|
|
1581
|
+
trigger.action.outText("Toggled " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1582
|
+
|
|
1583
|
+
if setting == "enable_radio_menu" then
|
|
1584
|
+
if splash_damage_options.enable_radio_menu then
|
|
1585
|
+
addSplashDamageMenu()
|
|
1586
|
+
else
|
|
1587
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
1588
|
+
splash_damage_menu = nil
|
|
1589
|
+
end
|
|
1590
|
+
end
|
|
1591
|
+
end
|
|
1592
|
+
|
|
1593
|
+
function addValueAdjustmentCommands(menu, setting)
|
|
1594
|
+
missionCommands.addCommand("+0.1", menu, updateSplashDamageSetting, setting, 0.1)
|
|
1595
|
+
missionCommands.addCommand("+1", menu, updateSplashDamageSetting, setting, 1)
|
|
1596
|
+
missionCommands.addCommand("+10", menu, updateSplashDamageSetting, setting, 10)
|
|
1597
|
+
missionCommands.addCommand("+100", menu, updateSplashDamageSetting, setting, 100)
|
|
1598
|
+
|
|
1599
|
+
missionCommands.addCommand("-0.1", menu, updateSplashDamageSetting, setting, -0.1)
|
|
1600
|
+
missionCommands.addCommand("-1", menu, updateSplashDamageSetting, setting, -1)
|
|
1601
|
+
missionCommands.addCommand("-10", menu, updateSplashDamageSetting, setting, -10)
|
|
1602
|
+
missionCommands.addCommand("-100", menu, updateSplashDamageSetting, setting, -100)
|
|
1603
|
+
end
|
|
1604
|
+
|
|
1605
|
+
function exitSplashDamageMenu()
|
|
1606
|
+
if splash_damage_menu then
|
|
1607
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
1608
|
+
splash_damage_menu = nil
|
|
1609
|
+
end
|
|
1610
|
+
end
|
|
1611
|
+
|
|
1612
|
+
function addSplashDamageMenu()
|
|
1613
|
+
if not splash_damage_options.enable_radio_menu then return end
|
|
1614
|
+
|
|
1615
|
+
if splash_damage_menu then
|
|
1616
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
1617
|
+
end
|
|
1618
|
+
|
|
1619
|
+
splash_damage_menu = missionCommands.addSubMenu("Splash Damage Settings")
|
|
1620
|
+
|
|
1621
|
+
--Page 1: Debug & General Settings
|
|
1622
|
+
local debugGeneralMenu = missionCommands.addSubMenu("Debug & General Settings", splash_damage_menu)
|
|
1623
|
+
missionCommands.addCommand("Toggle Game Messages", debugGeneralMenu, toggleSplashDamageSetting, "game_messages")
|
|
1624
|
+
missionCommands.addCommand("Toggle Debug Messages", debugGeneralMenu, toggleSplashDamageSetting, "debug")
|
|
1625
|
+
missionCommands.addCommand("Toggle Weapon Missing Messages", debugGeneralMenu, toggleSplashDamageSetting, "weapon_missing_message")
|
|
1626
|
+
missionCommands.addCommand("Toggle Pre-Explosion Debug", debugGeneralMenu, toggleSplashDamageSetting, "track_pre_explosion_debug")
|
|
1627
|
+
missionCommands.addCommand("Toggle Damage Model", debugGeneralMenu, toggleSplashDamageSetting, "damage_model")
|
|
1628
|
+
missionCommands.addCommand("Toggle Blast Stun", debugGeneralMenu, toggleSplashDamageSetting, "blast_stun")
|
|
1629
|
+
local unitDisabledMenu = missionCommands.addSubMenu("Unit Disabled Health", debugGeneralMenu)
|
|
1630
|
+
addValueAdjustmentCommands(unitDisabledMenu, "unit_disabled_health")
|
|
1631
|
+
local unitCantFireMenu = missionCommands.addSubMenu("Unit Cant Fire Health", debugGeneralMenu)
|
|
1632
|
+
addValueAdjustmentCommands(unitCantFireMenu, "unit_cant_fire_health")
|
|
1633
|
+
local infantryCantFireMenu = missionCommands.addSubMenu("Infantry Cant Fire Health", debugGeneralMenu)
|
|
1634
|
+
addValueAdjustmentCommands(infantryCantFireMenu, "infantry_cant_fire_health")
|
|
1635
|
+
local rocketMultiplierMenu = missionCommands.addSubMenu("Rocket Multiplier", debugGeneralMenu)
|
|
1636
|
+
addValueAdjustmentCommands(rocketMultiplierMenu, "rocket_multiplier")
|
|
1637
|
+
--Page 2: Explosion
|
|
1638
|
+
local explosionCargoMenu = missionCommands.addSubMenu("Explosion Settings", splash_damage_menu)
|
|
1639
|
+
local staticDamageMenu = missionCommands.addSubMenu("Static Damage Boost", explosionCargoMenu)
|
|
1640
|
+
addValueAdjustmentCommands(staticDamageMenu, "static_damage_boost")
|
|
1641
|
+
missionCommands.addCommand("Toggle Wave Explosions", explosionCargoMenu, toggleSplashDamageSetting, "wave_explosions")
|
|
1642
|
+
missionCommands.addCommand("Toggle Larger Explosions", explosionCargoMenu, toggleSplashDamageSetting, "larger_explosions")
|
|
1643
|
+
local blastRadiusMenu = missionCommands.addSubMenu("Blast Search Radius", explosionCargoMenu)
|
|
1644
|
+
addValueAdjustmentCommands(blastRadiusMenu, "blast_search_radius")
|
|
1645
|
+
local cascadeThresholdMenu = missionCommands.addSubMenu("Cascade Damage Threshold", explosionCargoMenu)
|
|
1646
|
+
addValueAdjustmentCommands(cascadeThresholdMenu, "cascade_damage_threshold")
|
|
1647
|
+
local overallScalingMenu = missionCommands.addSubMenu("Overall Scaling", explosionCargoMenu)
|
|
1648
|
+
addValueAdjustmentCommands(overallScalingMenu, "overall_scaling")
|
|
1649
|
+
missionCommands.addCommand("Toggle Shaped Charge Effects", explosionCargoMenu, toggleSplashDamageSetting, "apply_shaped_charge_effects")
|
|
1650
|
+
local shapedChargeMenu = missionCommands.addSubMenu("Shaped Charge Multiplier", explosionCargoMenu)
|
|
1651
|
+
addValueAdjustmentCommands(shapedChargeMenu, "shaped_charge_multiplier")
|
|
1652
|
+
missionCommands.addCommand("Toggle Dynamic Blast Radius", explosionCargoMenu, toggleSplashDamageSetting, "use_dynamic_blast_radius")
|
|
1653
|
+
local dynamicBlastMenu = missionCommands.addSubMenu("Dynamic Blast Radius Modifier", explosionCargoMenu)
|
|
1654
|
+
addValueAdjustmentCommands(dynamicBlastMenu, "dynamic_blast_radius_modifier")
|
|
1655
|
+
local cascadeScalingMenu = missionCommands.addSubMenu("Cascade Scaling", explosionCargoMenu)
|
|
1656
|
+
addValueAdjustmentCommands(cascadeScalingMenu, "cascade_scaling")
|
|
1657
|
+
local cascadeExplodeThresholdMenu = missionCommands.addSubMenu("Cascade Explode Threshold", explosionCargoMenu)
|
|
1658
|
+
addValueAdjustmentCommands(cascadeExplodeThresholdMenu, "cascade_explode_threshold")
|
|
1659
|
+
|
|
1660
|
+
|
|
1661
|
+
--Page 3: Cargo and Ordnance Protection
|
|
1662
|
+
local explosionCargoMenu = missionCommands.addSubMenu("Cargo and Ordnance", splash_damage_menu)
|
|
1663
|
+
missionCommands.addCommand("Toggle Always Cascade Explode", explosionCargoMenu, toggleSplashDamageSetting, "always_cascade_explode")
|
|
1664
|
+
missionCommands.addCommand("Toggle Tracking & Cargo Effects", explosionCargoMenu, toggleSplashDamageSetting, "track_pre_explosion")
|
|
1665
|
+
local cargoThresholdMenu = missionCommands.addSubMenu("Cargo Damage Threshold", explosionCargoMenu)
|
|
1666
|
+
addValueAdjustmentCommands(cargoThresholdMenu, "cargo_damage_threshold")
|
|
1667
|
+
missionCommands.addCommand("Toggle Ordnance Protection", explosionCargoMenu, toggleSplashDamageSetting, "ordnance_protection")
|
|
1668
|
+
local ordnanceRadiusMenu = missionCommands.addSubMenu("Ordnance Protection Radius", explosionCargoMenu)
|
|
1669
|
+
addValueAdjustmentCommands(ordnanceRadiusMenu, "ordnance_protection_radius")
|
|
1670
|
+
missionCommands.addCommand("Toggle Snap To Ground If Destroyed By LE", explosionCargoMenu, toggleSplashDamageSetting, "snap_to_ground_if_destroyed_by_large_explosion")
|
|
1671
|
+
|
|
1672
|
+
--Page 4: Debris Settings
|
|
1673
|
+
local debrisMenu = missionCommands.addSubMenu("Debris Settings", splash_damage_menu)
|
|
1674
|
+
missionCommands.addCommand("Toggle Debris Effects", debrisMenu, toggleSplashDamageSetting, "debris_effects")
|
|
1675
|
+
local debrisCountMinMenu = missionCommands.addSubMenu("Min Debris Count", debrisMenu)
|
|
1676
|
+
addValueAdjustmentCommands(debrisCountMinMenu, "debris_count_min")
|
|
1677
|
+
local debrisCountMaxMenu = missionCommands.addSubMenu("Max Debris Count", debrisMenu)
|
|
1678
|
+
addValueAdjustmentCommands(debrisCountMaxMenu, "debris_count_max")
|
|
1679
|
+
local debrisDistanceMenu = missionCommands.addSubMenu("Max Debris Distance", debrisMenu)
|
|
1680
|
+
addValueAdjustmentCommands(debrisDistanceMenu, "debris_max_distance")
|
|
1681
|
+
local debrisPowerMenu = missionCommands.addSubMenu("Debris Power", debrisMenu)
|
|
1682
|
+
addValueAdjustmentCommands(debrisPowerMenu, "debris_power")
|
|
1683
|
+
|
|
1684
|
+
--Page 5: Cluster Settings
|
|
1685
|
+
local clusterMenu = missionCommands.addSubMenu("Cluster Settings", splash_damage_menu)
|
|
1686
|
+
missionCommands.addCommand("Toggle Cluster Enabled", clusterMenu, toggleSplashDamageSetting, "cluster_enabled")
|
|
1687
|
+
local clusterBaseLengthMenu = missionCommands.addSubMenu("Cluster Base Length", clusterMenu)
|
|
1688
|
+
addValueAdjustmentCommands(clusterBaseLengthMenu, "cluster_base_length")
|
|
1689
|
+
local clusterBaseWidthMenu = missionCommands.addSubMenu("Cluster Base Width", clusterMenu)
|
|
1690
|
+
addValueAdjustmentCommands(clusterBaseWidthMenu, "cluster_base_width")
|
|
1691
|
+
local clusterMaxLengthMenu = missionCommands.addSubMenu("Cluster Max Length", clusterMenu)
|
|
1692
|
+
addValueAdjustmentCommands(clusterMaxLengthMenu, "cluster_max_length")
|
|
1693
|
+
local clusterMaxWidthMenu = missionCommands.addSubMenu("Cluster Max Width", clusterMenu)
|
|
1694
|
+
addValueAdjustmentCommands(clusterMaxWidthMenu, "cluster_max_width")
|
|
1695
|
+
local clusterMinLengthMenu = missionCommands.addSubMenu("Cluster Min Length", clusterMenu)
|
|
1696
|
+
addValueAdjustmentCommands(clusterMinLengthMenu, "cluster_min_length")
|
|
1697
|
+
local clusterMinWidthMenu = missionCommands.addSubMenu("Cluster Min Width", clusterMenu)
|
|
1698
|
+
addValueAdjustmentCommands(clusterMinWidthMenu, "cluster_min_width")
|
|
1699
|
+
missionCommands.addCommand("Toggle Bomblet Reduction Modifier", clusterMenu, toggleSplashDamageSetting, "cluster_bomblet_reductionmodifier")
|
|
1700
|
+
local clusterBombletDamageMenu = missionCommands.addSubMenu("Bomblet Damage Modifier", clusterMenu)
|
|
1701
|
+
addValueAdjustmentCommands(clusterBombletDamageMenu, "cluster_bomblet_damage_modifier")
|
|
1702
|
+
|
|
1703
|
+
end
|
|
1704
|
+
|
|
1705
|
+
if (script_enable == 1) then
|
|
1706
|
+
gameMsg("SPLASH DAMAGE 3.0 SCRIPT RUNNING")
|
|
1707
|
+
env.info("SPLASH DAMAGE 2. SCRIPT RUNNING")
|
|
1708
|
+
|
|
1709
|
+
timer.scheduleFunction(function()
|
|
1710
|
+
protectedCall(track_wpns)
|
|
1711
|
+
return timer.getTime() + refreshRate
|
|
1712
|
+
end, {}, timer.getTime() + refreshRate)
|
|
1713
|
+
|
|
1714
|
+
world.addEventHandler(WpnHandler)
|
|
1715
|
+
addSplashDamageMenu()
|
|
1716
|
+
end
|