@jtff/miztemplate-lib 3.8.2 → 3.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lua/lib/Moose_.lua +801 -205
- package/lua/lib/Splash_Damage_main.lua +2747 -630
- package/lua/src/130-airboss.lua +3 -3
- package/package.json +1 -1
|
@@ -1,126 +1,74 @@
|
|
|
1
|
-
--[[
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
- Added missing radio commands for Cascade Scaling
|
|
5
|
-
- Adjust default cascading to 2 (from 1)
|
|
6
|
-
- Adjusted Ural-4320 to be a tanker and ammo carrier for cargo cookoff
|
|
7
|
-
- Prevent weapons not in the list from being tracked
|
|
8
|
-
- Moved some logging behind the debug mode flag
|
|
9
|
-
- Ordnance Protection, added a max height ordnance protection will snap explosion to ground
|
|
10
|
-
- Ordnance Protection, fixed enable/disable option
|
|
11
|
-
- Added Giant Explosion feature
|
|
12
|
-
- Adjusted some hydra70 values on recom. from ETBSmorgan
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
09 March 2025 (Stevey666) - 3.0
|
|
16
|
-
- Added ordinance protection gives a few options - stop the additional larger_explosion that tends to blow up your own bombs if theyre dropped at the same place if its within x m
|
|
17
|
-
- Additional ordnance protection option that will cause a snap to ground larger_explosion if its within x meters of a recent larger explosion and within x seconds (can set in options)
|
|
18
|
-
- Added vehicle scanning around a weapon to allow for..
|
|
19
|
-
- Cook offs - you can set vehicles that will cook off i.e ammo trucks, number of explosions, debris explosions, power adjustable
|
|
20
|
-
- Fuel/Tanker explosion and flames - when a fuel tanker blows it will through up a big flame - adjustable in the scripts
|
|
21
|
-
- Added section for vehicles for the above
|
|
22
|
-
- Added radio commands for everything
|
|
23
|
-
- Added in cluster munitions changes (note: barely tested, its not particularly accurate or that useful at this point so leaving disabled)
|
|
24
|
-
- Potential bug - testing, stacking too many units together may cause a MIST error if you're using mist
|
|
25
|
-
|
|
26
|
-
- Setting this as 3.0 as I'd like to be responsive to requests, updates etc - creating a new fork to track this
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
10 Feb 2025 (Stevey666) - 2.0.7
|
|
30
|
-
- Fixed AGM 154/Adjusted weapons
|
|
31
|
-
- Added overall damage scaling
|
|
32
|
-
- Added modifier for shaped charges (i.e. Mavericks), adjusted weapon list accordingly
|
|
33
|
-
- Adjusted blast radius and damage calculations, created option for dynamic blast radius
|
|
34
|
-
- Adjusted cascading explosions, added additional "cascade_scaling" modifier and cascade explode threshold modifier. Units wont explode on initial impact unless health drops under threshold
|
|
35
|
-
- Added always_cascade_explode option so you can set it to the old ways of making everything in the blast wave go kaboom
|
|
36
|
-
- Added in game radio commands to change the new options ingame without having to reload everything in mission editor to test it out
|
|
37
|
-
|
|
38
|
-
12 November 2024 (by JGi | Quéton 1-1)
|
|
39
|
-
- Tweak down radius 100>90 (Thanks Arhibeau)
|
|
40
|
-
- Tweak down some values
|
|
41
|
-
|
|
42
|
-
20 January 2024 (by JGi | Quéton 1-1)
|
|
43
|
-
- Added missing weapons to explTable
|
|
44
|
-
- Sort weapons in explTable by type
|
|
45
|
-
- Added aircraft type in log when missing
|
|
46
|
-
|
|
47
|
-
03 May 2023 (KERV)
|
|
48
|
-
Correction AGM 154 (https://forum.dcs.world/topic/289290-splash-damage-20-script-make-explosions-better/page/5/#comment-5207760)
|
|
49
|
-
|
|
50
|
-
06 March 2023 (Kerv)
|
|
51
|
-
- Add some data for new ammunition
|
|
52
|
-
|
|
53
|
-
16 April 2022
|
|
54
|
-
spencershepard (GRIMM):
|
|
55
|
-
- Added new/missing weapons to explTable
|
|
56
|
-
- Added new option rocket_multiplier
|
|
1
|
+
--[[-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=
|
|
2
|
+
Latest Changes
|
|
3
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-
|
|
57
4
|
|
|
58
|
-
|
|
59
|
-
spencershepard (GRIMM):
|
|
60
|
-
- Added many new weapons
|
|
61
|
-
- Added filter for weapons.shells events
|
|
62
|
-
- Fixed mission weapon message option
|
|
63
|
-
- Changed default for damage_model option
|
|
64
|
-
|
|
65
|
-
21 December 2021
|
|
66
|
-
spencershepard (GRIMM):
|
|
67
|
-
SPLASH DAMAGE 2.0:
|
|
68
|
-
- Added blast wave effect to add timed and scaled secondary explosions on top of game objects
|
|
69
|
-
- Object geometry within blast wave changes damage intensity
|
|
70
|
-
- Damage boost for structures since they are hard to kill, even if very close to large explosions
|
|
71
|
-
- Increased some rocket values in explTable
|
|
72
|
-
- Missing weapons from explTable will display message to user and log to DCS.log so that we can add what's missing
|
|
73
|
-
- Damage model for ground units that will disable their weapons and ability to move with partial damage before they are killed
|
|
74
|
-
- Added options table to allow easy adjustments before release
|
|
75
|
-
- General refactoring and restructure
|
|
5
|
+
x x 2025 - 3.4
|
|
76
6
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
7
|
+
(Stevey666)
|
|
8
|
+
|
|
9
|
+
- Added in optional kill feed feature, this will try to display kills from DCS engine and kills from the additional explosions by checking pre/post scans of the explosion area
|
|
10
|
+
--SPLASH KILL FEED WORKS IN MP ONLY (you can host your local SP mission as MP for now)
|
|
11
|
+
- Added in Lekas Foothold Integration to allow splash kills to count towards the points, killfeed is required to be enabled for this
|
|
12
|
+
- Added AGM_45B to expl table
|
|
13
|
+
|
|
14
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-
|
|
15
|
+
Full Changelog at the bottom of the script
|
|
16
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-
|
|
81
17
|
|
|
82
|
-
2 October 2020
|
|
83
|
-
FrozenDroid:
|
|
84
|
-
- Added error handling to all event handler and scheduled functions. Lua script errors can no longer bring the server down.
|
|
85
|
-
- Added some extra checks to which weapons to handle, make sure they actually have a warhead (how come S-8KOM's don't have a warhead field...?)
|
|
86
|
-
--]]
|
|
87
18
|
|
|
88
|
-
|
|
19
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-
|
|
20
|
+
##### SCRIPT CONFIGURATION #####
|
|
21
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-]]
|
|
89
22
|
splash_damage_options = {
|
|
90
|
-
|
|
23
|
+
---------------------------------------------------------------------- Debug and Messages ----------------------------------------------------------------
|
|
91
24
|
["game_messages"] = false, --enable some messages on screen
|
|
92
25
|
["debug"] = false, --enable debugging messages
|
|
93
26
|
["weapon_missing_message"] = false, --false disables messages alerting you to weapons missing from the explTable
|
|
94
27
|
["track_pre_explosion_debug"] = false, --Toggle to enable/disable pre-explosion tracking debugging
|
|
28
|
+
["track_groundunitordnance_debug"] = false, --Enable detailed debug messages for ground unit ordnance tracking
|
|
29
|
+
["napalm_unitdamage_debug"] = false, --Enable detailed debug messages for napalm unit damage tracking
|
|
30
|
+
["damage_model_game_messages"] = false, --ground unit movement and weapons disabled notification
|
|
31
|
+
["killfeed_debug"] = false, --Enable detailed debug messages for killfeed
|
|
32
|
+
["events_debug"] = false, --enable debugging for event handling
|
|
95
33
|
|
|
96
|
-
|
|
34
|
+
---------------------------------------------------------------------- Radio -----------------------------------------------------------------------------
|
|
35
|
+
["enable_radio_menu"] = false, --enables the in-game radio menu for modifying settings
|
|
97
36
|
|
|
37
|
+
|
|
38
|
+
---------------------------------------------------------------------- Basic Splash Settings -------------------------------------------------------------
|
|
98
39
|
["static_damage_boost"] = 2000, --apply extra damage to Unit.Category.STRUCTUREs with wave explosions
|
|
99
40
|
["wave_explosions"] = true, --secondary explosions on top of game objects, radiating outward from the impact point and scaled based on size of object and distance from weapon impact point
|
|
100
41
|
["larger_explosions"] = true, --secondary explosions on top of weapon impact points, dictated by the values in the explTable
|
|
101
42
|
["damage_model"] = true, --allow blast wave to affect ground unit movement and weapons
|
|
102
|
-
["blast_search_radius"] = 90, --this is the max size of any blast wave radius, since we will only find objects within this zone
|
|
103
|
-
["
|
|
43
|
+
["blast_search_radius"] = 90, --this is the max size of any blast wave radius, since we will only find objects within this zone. Only used if dynamic is not enabled
|
|
44
|
+
["use_dynamic_blast_radius"] = true, --if true, blast radius is calculated from explosion power; if false, blast_search_radius (90) is used
|
|
45
|
+
["dynamic_blast_radius_modifier"] = 2, --multiplier for the blast radius
|
|
104
46
|
["blast_stun"] = false, --not implemented
|
|
47
|
+
["overall_scaling"] = 1, --overall scaling for explosive power
|
|
48
|
+
|
|
49
|
+
---------------------------------------------------------------------- Units -----------------------------------------------------------------------------
|
|
105
50
|
["unit_disabled_health"] = 30, --if health is below this value after our explosions, disable its movement
|
|
106
51
|
["unit_cant_fire_health"] = 40, --if health is below this value after our explosions, set ROE to HOLD to simulate damage weapon systems
|
|
107
52
|
["infantry_cant_fire_health"] = 60, --if health is below this value after our explosions, set ROE to HOLD to simulate severe injury
|
|
108
53
|
|
|
54
|
+
|
|
55
|
+
---------------------------------------------------------------------- Rockets ---------------------------------------------------------------------------
|
|
109
56
|
["rocket_multiplier"] = 1.3, --multiplied by the explTable value for rockets
|
|
110
|
-
|
|
111
|
-
|
|
57
|
+
|
|
58
|
+
---------------------------------------------------------------------- Shaped Charge ---------------------------------------------------------------------
|
|
112
59
|
["apply_shaped_charge_effects"] = true, --apply reduction in blastwave etc for shaped charge munitions
|
|
113
60
|
["shaped_charge_multiplier"] = 0.2, --multiplier that reduces blast radius and explosion power for shaped charge munitions.
|
|
114
61
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
62
|
+
|
|
63
|
+
---------------------------------------------------------------------- Cascading -------------------------------------------------------------------------
|
|
118
64
|
["cascade_scaling"] = 2, --multiplier for secondary (cascade) blast damage, 1 damage fades out too soon, 2 or 3 damage seems a good balance
|
|
65
|
+
["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.
|
|
119
66
|
["cascade_explode_threshold"] = 60, --only trigger cascade explosion if the unit's current health is <= this percent of its maximum, setting can help blow nearby jeeps but not tanks
|
|
120
67
|
["always_cascade_explode"] = false, --switch if you want everything to explode like with the original script
|
|
121
68
|
|
|
122
|
-
|
|
123
|
-
--track_pre_explosion/enable_cargo_effects should both be the same value
|
|
69
|
+
---------------------------------------------------------------------- Cargo Cook Off/Fuel Explosion ----------------------------------------------------
|
|
70
|
+
--track_pre_explosion/enable_cargo_effects should both be the same value--
|
|
71
|
+
|
|
124
72
|
["track_pre_explosion"] = true, --Toggle to enable/disable pre-explosion tracking
|
|
125
73
|
["enable_cargo_effects"] = true, --Toggle for enabling/disabling cargo explosions and cook-offs
|
|
126
74
|
["cargo_damage_threshold"] = 60, --Health % below which cargo explodes (0 = destroyed only)
|
|
@@ -130,6 +78,13 @@ splash_damage_options = {
|
|
|
130
78
|
["debris_count_max"] = 12, --Maximum debris pieces per cook-off
|
|
131
79
|
["debris_max_distance"] = 10, --Max distance debris can travel (meters), the min distance from the vehicle will be 10% of this
|
|
132
80
|
|
|
81
|
+
["cookoff_flares_enabled"] = false, --Enable/disable flare effects for cook-offs
|
|
82
|
+
["cookoff_flare_color"] = 2,
|
|
83
|
+
["cookoff_flare_count_modifier"] = 1, --Multiplier for flare count (e.g., 1x, 2x cookOffCount from the vehicle table)
|
|
84
|
+
["cookoff_flare_offset"] = 1, --Max offset distance for flares in meters (horizontal)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
---------------------------------------------------------------------- Ordnance Protection --------------------------------------------------------------
|
|
133
88
|
["ordnance_protection"] = true, --Toggle ordinance protection features
|
|
134
89
|
["ordnance_protection_radius"] = 10, --Distance in meters to protect nearby bombs
|
|
135
90
|
["detect_ordnance_destruction"] = true, --Toggle detection of ordnance destroyed by large explosions
|
|
@@ -139,7 +94,8 @@ splash_damage_options = {
|
|
|
139
94
|
["recent_large_explosion_range"] = 100, --range its looking for in meters for a recent large_explosion generated by the script
|
|
140
95
|
["recent_large_explosion_time"] = 4, --in seconds how long ago there was a recent large_explosion generated by the script
|
|
141
96
|
|
|
142
|
-
|
|
97
|
+
|
|
98
|
+
---------------------------------------------------------------------- Cluster Bombs ---------------------------------------------------------------------
|
|
143
99
|
["cluster_enabled"] = false,
|
|
144
100
|
["cluster_base_length"] = 150, --Base forward spread (meters)
|
|
145
101
|
["cluster_base_width"] = 200, --Base lateral spread (meters)
|
|
@@ -150,14 +106,73 @@ splash_damage_options = {
|
|
|
150
106
|
["cluster_bomblet_reductionmodifier"] = true, --Use equation to reduce number of bomblets (to make it look better)
|
|
151
107
|
["cluster_bomblet_damage_modifier"] = 1, --Adjustable global modifier for bomblet explosive power
|
|
152
108
|
|
|
153
|
-
|
|
109
|
+
|
|
110
|
+
---------------------------------------------------------------------- Giant Explosions ------------------------------------------------------------------
|
|
111
|
+
--Remember, any target you want to blow up needs to be named "GiantExplosionTarget(X)" (X) being any value/name etc
|
|
154
112
|
["giant_explosion_enabled"] = true, --Toggle to enable/disable Giant Explosion
|
|
155
113
|
["giant_explosion_power"] = 6000, --Power in kg of TNT (default 8 tons)
|
|
156
114
|
["giant_explosion_scale"] = 1, --Size scale factor (default 1)
|
|
157
115
|
["giant_explosion_duration"] = 3.0, --Total duration in seconds (default 3s)
|
|
158
|
-
["giant_explosion_count"] = 250, --Number of explosions (default
|
|
116
|
+
["giant_explosion_count"] = 250, --Number of explosions (default 250)
|
|
159
117
|
["giant_explosion_target_static"] = true, --Toggle to true for static targets (store position once), false for dynamic (update every second)
|
|
160
118
|
["giant_explosion_poll_rate"] = 1, --Polling rate in seconds for flag checks (default 1s)
|
|
119
|
+
["giantexplosion_ondamage"] = true, --Trigger explosion when unit is damaged
|
|
120
|
+
["giantexplosion_ondeath"] = true, --Trigger explosion when unit is destroyed
|
|
121
|
+
["giantexplosion_testmode"] = true, --Enable test mode with separate array for radio commands
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
---------------------------------------------------------------------- Ground/Ship Ordnance -------------------------------------------------------------
|
|
125
|
+
["track_groundunitordnance"] = false, --Enable tracking of ground unit ordnance (shells)
|
|
126
|
+
["groundunitordnance_damage_modifier"] = 1.0, --Multiplier for ground unit ordnance explosive power
|
|
127
|
+
["groundunitordnance_blastwave_modifier"] = 4.0, --Additional multiplier for blast wave intensity of ground unit ordnance
|
|
128
|
+
["groundunitordnance_maxtrackedcount"] = 100, --Maximum number of ground ordnance shells tracked at once to prevent overload
|
|
129
|
+
["scan_50m_for_groundordnance"] = true, --If true, uses a 50m scan radius for ground ordnance instead of dynamic blast radius
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
---------------------------------------------------------------------- Smoke and Cookoff For All Vehicles -----------------------------------------------
|
|
133
|
+
["smokeandcookoffeffectallvehicles"] = false, --Enable effects for all ground vehicles not in cargoUnits vehicle table
|
|
134
|
+
["allunits_enable_smoke"] = false, -- Enable /disable smoke effects
|
|
135
|
+
["allunits_enable_cookoff"] = false, -- Enable /disable cookoffs
|
|
136
|
+
["allunits_explode_power"] = 50, --Initial power of vehicle exploding
|
|
137
|
+
["allunits_default_flame_size"] = 6, --Default smoke size (called flame here in the code, but it'll be smoke) 5 = small smoke, 6 = medium smoke, 7 = large smoke, 8 = huge smoke
|
|
138
|
+
["allunits_default_flame_duration"] = 60, --Default smoke (called flame here in the code, but it's smoke) duration in seconds for non-cargoUnits vehicles
|
|
139
|
+
["allunits_cookoff_count"] = 4, --number of cookoff explosions to schedule
|
|
140
|
+
["allunits_cookoff_duration"] = 30, --max time window of cookoffs (will be scheduled randomly between 0 seconds and this figure)
|
|
141
|
+
["allunits_cookoff_power"] = 10, --power of the cookoff explosions
|
|
142
|
+
["allunits_cookoff_powerrandom"] = 50, --percentage higher or lower of the cookoff power figure
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
---------------------------------------------------------------------- Napalm ---------------------------------------------------------------------------
|
|
146
|
+
["napalm_mk77_enabled"] = true, --Enable napalm effects for MK77mod0-WPN and MK77mod1-WPN
|
|
147
|
+
["napalmoverride_enabled"] = false, --If true, enables napalm effects for weapons in napalm_override_weapons
|
|
148
|
+
["napalm_override_weapons"] = "Mk_82,SAMP125LD", --Comma-separated list of weapons to override as napalm when overrides enabled, i.e Mk_82,SAMP125LD. Do not pick CBUs
|
|
149
|
+
|
|
150
|
+
["napalm_spread_points"] = 4, --Number of points of explosion per each bomb (aka spawns of dummy fuel tank), so 1 bomb can have 4 fireballs as such. The MK77 0 is bigger and will do a % more by default (i.e 5 instead of 4)
|
|
151
|
+
["napalm_spread_spacing"] = 25, --Distance m between the points
|
|
152
|
+
["napalm_phosphor_enabled"] = true, --If true, enables phosphor flare effects for napalm weapons
|
|
153
|
+
["napalm_phosphor_multiplier"] = 0.5, --Multiplier for number of phosphor flares that shoot out, there is a level of randomisation in the code already
|
|
154
|
+
["napalm_addflame"] = true, --Enable flame effects at napalm spawn points
|
|
155
|
+
["napalm_addflame_size"] = 3, --Flame size (1-8, 4 = huge smoke and fire)
|
|
156
|
+
["napalm_addflame_duration"] = 180, --Flame duration in seconds napalm_destroy_delay
|
|
157
|
+
["napalm_flame_delay"] = 0.01, --Delay in seconds before flame effect
|
|
158
|
+
["napalm_explode_delay"] = 0.01, --Delay in seconds before putting an exlode on the ground to blow up the spawned fuel tank, original script had this as 0.1
|
|
159
|
+
["napalm_destroy_delay"] = 0.02, --Delay in seconds before it destroys the fuel tank object, original script had this as 0.12
|
|
160
|
+
|
|
161
|
+
["napalm_doublewide_enabled"] = false, --Toggle for double-wide napalm (two points per spread point, ~28m width)
|
|
162
|
+
["napalm_doublewide_spread"] = 15, --Meters either side of bomb vector either side to spawn a fuel tank
|
|
163
|
+
|
|
164
|
+
["napalm_unitdamage_enable"] = true, --Enable/disable napalm unit damage
|
|
165
|
+
["napalm_unitdamage_scandistance"] = 70, --Scan radius in meters
|
|
166
|
+
["napalm_unitdamage_startdelay"] = 0.1, --Seconds between Napalm exploding and explosion occurring (can be 0 for no delay)
|
|
167
|
+
["napalm_unitdamage_spreaddelay"] = 0, --If startdelay is greater than 0, explosions are ordered by distance with this gap between each unit
|
|
168
|
+
|
|
169
|
+
---------------------------------------------------------------------- Kill Feed ------------------------------------------------------------------------
|
|
170
|
+
["killfeed_enable"] = false, --Enable killfeed logging and messaging
|
|
171
|
+
["killfeed_game_messages"] = false, --Show killfeed SPLASH KILL FEED WORKS IN MP ONLY (you can host your local SP mission as MP for now)
|
|
172
|
+
["killfeed_game_message_duration"] = 15, --Duration in seconds for game messages (killfeed and SplashKillFeed) - note the message will be delayed to let DCS catch up as per next option
|
|
173
|
+
["killfeed_splashdelay"] = 8, --Duration in seconds delay to allow dcs to see that units are dead before saying the splash damage got them instead of the the players weapon
|
|
174
|
+
["killfeed_lekas_foothold_integration"] = false, --Enable Lekas Foothold integration
|
|
175
|
+
["killfeed_lekas_contribution_delay"] = 240, -- Delay in seconds before processing splash kills into Lekas contributions (default 240 seconds/4mins)
|
|
161
176
|
}
|
|
162
177
|
|
|
163
178
|
local script_enable = 1
|
|
@@ -188,7 +203,7 @@ flamesize:
|
|
|
188
203
|
["r11_volvo_drivable"] = {
|
|
189
204
|
cargoExplosion = true,
|
|
190
205
|
cargoExplosionMult = 2.0,
|
|
191
|
-
cargoExplosionPower =
|
|
206
|
+
cargoExplosionPower = 50,
|
|
192
207
|
cargoCookOff = false,
|
|
193
208
|
cookOffCount = 0,
|
|
194
209
|
cookOffPower = 0,
|
|
@@ -204,7 +219,7 @@ flamesize:
|
|
|
204
219
|
["ATMZ-5"] = {
|
|
205
220
|
cargoExplosion = true,
|
|
206
221
|
cargoExplosionMult = 2.0,
|
|
207
|
-
cargoExplosionPower =
|
|
222
|
+
cargoExplosionPower = 50,
|
|
208
223
|
cargoCookOff = false,
|
|
209
224
|
cookOffCount = 0,
|
|
210
225
|
cookOffPower = 0,
|
|
@@ -220,7 +235,7 @@ flamesize:
|
|
|
220
235
|
["ATZ-10"] = {
|
|
221
236
|
cargoExplosion = true,
|
|
222
237
|
cargoExplosionMult = 2,
|
|
223
|
-
cargoExplosionPower =
|
|
238
|
+
cargoExplosionPower = 50,
|
|
224
239
|
cargoCookOff = false,
|
|
225
240
|
cookOffCount = 0,
|
|
226
241
|
cookOffPower = 0,
|
|
@@ -236,7 +251,7 @@ flamesize:
|
|
|
236
251
|
["ATZ-5"] = {
|
|
237
252
|
cargoExplosion = true,
|
|
238
253
|
cargoExplosionMult = 1.8,
|
|
239
|
-
cargoExplosionPower =
|
|
254
|
+
cargoExplosionPower = 50,
|
|
240
255
|
cargoCookOff = false,
|
|
241
256
|
cookOffCount = 0,
|
|
242
257
|
cookOffPower = 0,
|
|
@@ -252,7 +267,7 @@ flamesize:
|
|
|
252
267
|
["M978 HEMTT Tanker"] = {
|
|
253
268
|
cargoExplosion = true,
|
|
254
269
|
cargoExplosionMult = 2.0,
|
|
255
|
-
cargoExplosionPower =
|
|
270
|
+
cargoExplosionPower = 50,
|
|
256
271
|
cargoCookOff = false,
|
|
257
272
|
cookOffCount = 0,
|
|
258
273
|
cookOffPower = 0,
|
|
@@ -268,7 +283,7 @@ flamesize:
|
|
|
268
283
|
["GAZ-66"] = {
|
|
269
284
|
cargoExplosion = true,
|
|
270
285
|
cargoExplosionMult = 1,
|
|
271
|
-
cargoExplosionPower =
|
|
286
|
+
cargoExplosionPower = 50,
|
|
272
287
|
cargoCookOff = true,
|
|
273
288
|
cookOffCount = 4,
|
|
274
289
|
cookOffPower = 1,
|
|
@@ -280,10 +295,11 @@ flamesize:
|
|
|
280
295
|
flameDuration = 30,
|
|
281
296
|
},
|
|
282
297
|
--#Technically this is both ammo and fuel looking at the model
|
|
283
|
-
|
|
298
|
+
--#Called Ural-4320 in game, but in code its Ural-375
|
|
299
|
+
["Ural-375"] = {
|
|
284
300
|
cargoExplosion = true,
|
|
285
301
|
cargoExplosionMult = 1,
|
|
286
|
-
cargoExplosionPower =
|
|
302
|
+
cargoExplosionPower = 50,
|
|
287
303
|
cargoCookOff = true,
|
|
288
304
|
cookOffCount = 4,
|
|
289
305
|
cookOffPower = 1,
|
|
@@ -298,7 +314,7 @@ flamesize:
|
|
|
298
314
|
["ZIL-135"] = {
|
|
299
315
|
cargoExplosion = true,
|
|
300
316
|
cargoExplosionMult = 1,
|
|
301
|
-
cargoExplosionPower =
|
|
317
|
+
cargoExplosionPower = 50,
|
|
302
318
|
cargoCookOff = true,
|
|
303
319
|
cookOffCount = 5,
|
|
304
320
|
cookOffPower = 1,
|
|
@@ -309,9 +325,118 @@ flamesize:
|
|
|
309
325
|
flameSize = 1,
|
|
310
326
|
flameDuration = 30,
|
|
311
327
|
},
|
|
328
|
+
|
|
329
|
+
--#Ammo Boxes etc
|
|
330
|
+
|
|
331
|
+
--#Long ammo box
|
|
332
|
+
|
|
333
|
+
["Cargo06"] = {
|
|
334
|
+
cargoExplosion = true,
|
|
335
|
+
cargoExplosionMult = 1,
|
|
336
|
+
cargoExplosionPower = 50,
|
|
337
|
+
cargoCookOff = true,
|
|
338
|
+
cookOffCount = 5,
|
|
339
|
+
cookOffPower = 1,
|
|
340
|
+
cookOffDuration = 10,
|
|
341
|
+
cookOffRandomTiming = true,
|
|
342
|
+
cookOffPowerRandom = 50,
|
|
343
|
+
isTanker = false,
|
|
344
|
+
flameSize = 1,
|
|
345
|
+
flameDuration = 30,
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
--#ammo boxes
|
|
349
|
+
|
|
350
|
+
["Cargo03"] = {
|
|
351
|
+
cargoExplosion = true,
|
|
352
|
+
cargoExplosionMult = 1,
|
|
353
|
+
cargoExplosionPower = 10,
|
|
354
|
+
cargoCookOff = true,
|
|
355
|
+
cookOffCount = 10,
|
|
356
|
+
cookOffPower = 1,
|
|
357
|
+
cookOffDuration = 20,
|
|
358
|
+
cookOffRandomTiming = true,
|
|
359
|
+
cookOffPowerRandom = 0,
|
|
360
|
+
isTanker = false,
|
|
361
|
+
flameSize = 1,
|
|
362
|
+
flameDuration = 30,
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
--FuelBarrels
|
|
366
|
+
|
|
367
|
+
["Cargo05"] = {
|
|
368
|
+
cargoExplosion = true,
|
|
369
|
+
cargoExplosionMult = 1,
|
|
370
|
+
cargoExplosionPower = 50,
|
|
371
|
+
cargoCookOff = false,
|
|
372
|
+
cookOffCount = 5,
|
|
373
|
+
cookOffPower = 1,
|
|
374
|
+
cookOffDuration = 10,
|
|
375
|
+
cookOffRandomTiming = true,
|
|
376
|
+
cookOffPowerRandom = 50,
|
|
377
|
+
isTanker = true,
|
|
378
|
+
flameSize = 2,
|
|
379
|
+
flameDuration = 30,
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
--APFC fuel
|
|
383
|
+
|
|
384
|
+
["APFC fuel"] = {
|
|
385
|
+
cargoExplosion = true,
|
|
386
|
+
cargoExplosionMult = 1,
|
|
387
|
+
cargoExplosionPower = 50,
|
|
388
|
+
cargoCookOff = false,
|
|
389
|
+
cookOffCount = 5,
|
|
390
|
+
cookOffPower = 1,
|
|
391
|
+
cookOffDuration = 10,
|
|
392
|
+
cookOffRandomTiming = true,
|
|
393
|
+
cookOffPowerRandom = 50,
|
|
394
|
+
isTanker = true,
|
|
395
|
+
flameSize = 2,
|
|
396
|
+
flameDuration = 30,
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
--Oil Barrel
|
|
400
|
+
|
|
401
|
+
["Oil Barrel"] = {
|
|
402
|
+
cargoExplosion = true,
|
|
403
|
+
cargoExplosionMult = 1,
|
|
404
|
+
cargoExplosionPower = 50,
|
|
405
|
+
cargoCookOff = false,
|
|
406
|
+
cookOffCount = 5,
|
|
407
|
+
cookOffPower = 1,
|
|
408
|
+
cookOffDuration = 10,
|
|
409
|
+
cookOffRandomTiming = true,
|
|
410
|
+
cookOffPowerRandom = 50,
|
|
411
|
+
isTanker = true,
|
|
412
|
+
flameSize = 1,
|
|
413
|
+
flameDuration = 20,
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
--FARP Ammo Dump Coating
|
|
418
|
+
|
|
419
|
+
["FARP Ammo Dump Coating"] = {
|
|
420
|
+
cargoExplosion = true,
|
|
421
|
+
cargoExplosionMult = 1,
|
|
422
|
+
cargoExplosionPower = 50,
|
|
423
|
+
cargoCookOff = true,
|
|
424
|
+
cookOffCount = 5,
|
|
425
|
+
cookOffPower = 1,
|
|
426
|
+
cookOffDuration = 20,
|
|
427
|
+
cookOffRandomTiming = true,
|
|
428
|
+
cookOffPowerRandom = 50,
|
|
429
|
+
isTanker = false,
|
|
430
|
+
flameSize = 1,
|
|
431
|
+
flameDuration = 20,
|
|
432
|
+
},
|
|
312
433
|
}
|
|
434
|
+
--[[
|
|
435
|
+
|
|
313
436
|
|
|
314
|
-
|
|
437
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-
|
|
438
|
+
Weapon Explosive Table
|
|
439
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-]]
|
|
315
440
|
explTable = {
|
|
316
441
|
--*** WWII BOMBS ***
|
|
317
442
|
["British_GP_250LB_Bomb_Mk1"] = { explosive = 100, shaped_charge = false },
|
|
@@ -409,11 +534,11 @@ explTable = {
|
|
|
409
534
|
|
|
410
535
|
--*** CLUSTER BOMBS (CBU) ***
|
|
411
536
|
--I don't have most of these so can't test them with debug on
|
|
537
|
+
["MK77mod0-WPN"] = { explosive = 0, shaped_charge = false, cluster = false, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --napalm skyhawk, have set to cluster (false) for napalm purposes
|
|
538
|
+
["MK77mod1-WPN"] = { explosive = 0, shaped_charge = false, cluster = false, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --napalm skyhawk, have set to cluster (false) for napalm purposes
|
|
412
539
|
["CBU_99"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 247, submunition_explosive = 2, submunition_name = "Mk 118" }, --Mk 20 Rockeye variant, confirmed 247 Mk 118 bomblets
|
|
413
540
|
["ROCKEYE"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 247, submunition_explosive = 2, submunition_name = "Mk 118" }, --Mk 20 Rockeye, confirmed 247 Mk 118 bomblets
|
|
414
541
|
["BLU_3B_GROUP"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 19, submunition_explosive = 0.2, submunition_name = "BLU_3B" }, --Not in datamine, possibly custom or outdated; submunition name guessed
|
|
415
|
-
["MK77mod0-WPN"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --Not in datamine, possibly custom; submunition name guessed
|
|
416
|
-
["MK77mod1-WPN"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 132, submunition_explosive = 0.1, submunition_name = "BLU_1B" }, --Not in datamine, possibly custom; submunition name guessed
|
|
417
542
|
["CBU_87"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 202, submunition_explosive = 0.5, submunition_name = "BLU_97B" }, --Confirmed 202 BLU-97/B bomblets
|
|
418
543
|
["CBU_103"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 202, submunition_explosive = 0.5, submunition_name = "BLU_97B" }, --WCMD variant of CBU-87, confirmed 202 BLU-97/B bomblets
|
|
419
544
|
["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
|
|
@@ -427,8 +552,8 @@ explTable = {
|
|
|
427
552
|
["RBK_500U"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 352, submunition_explosive = 0.2, submunition_name = "OAB_25RT" }, --Confirmed 352 OAB-2.5RT fragmentation bomblets
|
|
428
553
|
["RBK_500AO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 108, submunition_explosive = 0.5, submunition_name = "AO_25RT" }, --Confirmed 108 AO-2.5RT fragmentation bomblets
|
|
429
554
|
["RBK_500U_OAB_2_5RT"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 352, submunition_explosive = 0.2, submunition_name = "OAB_25RT" }, --Confirmed 352 OAB-2.5RT fragmentation bomblets
|
|
430
|
-
|
|
431
|
-
|
|
555
|
+
["RBK_500_255_PTO_1M"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 126, submunition_explosive = 0.5, submunition_name = "PTO_1M" },
|
|
556
|
+
["RBK_500_255_ShO"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 565, submunition_explosive = 0.1, submunition_name = "ShO" },
|
|
432
557
|
--*** INS/GPS BOMBS (JDAM) ***
|
|
433
558
|
["GBU_31"] = { explosive = 582, shaped_charge = false },
|
|
434
559
|
["GBU_31_V_3B"] = { explosive = 582, shaped_charge = false },
|
|
@@ -452,6 +577,7 @@ explTable = {
|
|
|
452
577
|
["GB-6"] = { explosive = 0, shaped_charge = false },
|
|
453
578
|
["GB-6-HE"] = { explosive = 0, shaped_charge = false },
|
|
454
579
|
["GB-6-SFW"] = { explosive = 0, shaped_charge = false },
|
|
580
|
+
["X_65"] = { explosive = 100, shaped_charge = false },
|
|
455
581
|
|
|
456
582
|
--*** AIR GROUND MISSILE (AGM) ***
|
|
457
583
|
["AGM_62"] = { explosive = 400, shaped_charge = false },
|
|
@@ -493,26 +619,34 @@ explTable = {
|
|
|
493
619
|
["X_29L"] = { explosive = 320, shaped_charge = false },
|
|
494
620
|
["X_29T"] = { explosive = 320, shaped_charge = false },
|
|
495
621
|
["X_29TE"] = { explosive = 320, shaped_charge = false },
|
|
496
|
-
|
|
622
|
+
|
|
623
|
+
["AKD-10"] = { explosive = 10, shaped_charge = false }, --drone
|
|
624
|
+
|
|
497
625
|
--*** ANTI-RADAR MISSILE (ARM) ***
|
|
498
|
-
["AGM_88C"] = { explosive =
|
|
499
|
-
["AGM_88"] = { explosive =
|
|
500
|
-
["AGM_122"] = { explosive =
|
|
501
|
-
["LD-10"] = { explosive =
|
|
502
|
-
["AGM_45A"] = { explosive =
|
|
503
|
-
|
|
504
|
-
["
|
|
626
|
+
["AGM_88C"] = { explosive = 69, shaped_charge = false },
|
|
627
|
+
["AGM_88"] = { explosive = 69, shaped_charge = false },
|
|
628
|
+
["AGM_122"] = { explosive = 12, shaped_charge = false },
|
|
629
|
+
["LD-10"] = { explosive = 75, shaped_charge = false },
|
|
630
|
+
["AGM_45A"] = { explosive = 66, shaped_charge = false },
|
|
631
|
+
["AGM_45B"] = { explosive = 66, shaped_charge = false },
|
|
632
|
+
["X_58"] = { explosive = 149, shaped_charge = false },
|
|
633
|
+
["X_25MP"] = { explosive = 90, shaped_charge = false },
|
|
634
|
+
["X_31P"] = { explosive = 90, shaped_charge = false },
|
|
505
635
|
|
|
506
636
|
--*** ANTI-SHIP MISSILE (ASh) ***
|
|
507
637
|
["AGM_84D"] = { explosive = 488, shaped_charge = false },
|
|
508
638
|
["Rb 15F"] = { explosive = 500, shaped_charge = false },
|
|
509
639
|
["C-802AK"] = { explosive = 500, shaped_charge = false },
|
|
510
|
-
|
|
640
|
+
["X_31A"] = { explosive = 89, shaped_charge = false }, --KH-31A ASh
|
|
641
|
+
["X_22"] = { explosive = 1200, shaped_charge = false }, --Ash 1ton RDX = 1600KG TNT
|
|
642
|
+
["X_35"] = { explosive = 145, shaped_charge = true }, --ASh 145KG
|
|
643
|
+
|
|
511
644
|
--*** CRUISE MISSILE ***
|
|
512
|
-
["CM-802AKG"] = { explosive =
|
|
513
|
-
["AGM_84E"] = { explosive =
|
|
514
|
-
["AGM_84H"] = { explosive =
|
|
515
|
-
["X_59M"] = { explosive =
|
|
645
|
+
["CM-802AKG"] = { explosive = 240, shaped_charge = false },
|
|
646
|
+
["AGM_84E"] = { explosive = 360, shaped_charge = false },
|
|
647
|
+
["AGM_84H"] = { explosive = 380, shaped_charge = false },
|
|
648
|
+
["X_59M"] = { explosive = 340, shaped_charge = false },
|
|
649
|
+
["X_65"] = { explosive = 545, shaped_charge = false },
|
|
516
650
|
|
|
517
651
|
--*** ROCKETS ***
|
|
518
652
|
["HYDRA_70M15"] = { explosive = 5, shaped_charge = false },
|
|
@@ -559,11 +693,11 @@ explTable = {
|
|
|
559
693
|
["S-25O"] = { explosive = 150, shaped_charge = false },
|
|
560
694
|
["S-25-O"] = { explosive = 150, shaped_charge = false },
|
|
561
695
|
["S_25L"] = { explosive = 190, shaped_charge = false },
|
|
562
|
-
["S-5M"] = { explosive =
|
|
696
|
+
["S-5M"] = { explosive = 3, shaped_charge = false },
|
|
563
697
|
["C_5"] = { explosive = 8, shaped_charge = false },
|
|
564
698
|
["C5"] = { explosive = 5, shaped_charge = false },
|
|
565
|
-
["C_8"] = { explosive =
|
|
566
|
-
["C_8OFP2"] = { explosive =
|
|
699
|
+
["C_8"] = { explosive = 5, shaped_charge = false },
|
|
700
|
+
["C_8OFP2"] = { explosive = 5, shaped_charge = false },
|
|
567
701
|
["C_13"] = { explosive = 21, shaped_charge = false },
|
|
568
702
|
["C_24"] = { explosive = 123, shaped_charge = false },
|
|
569
703
|
["C_25"] = { explosive = 151, shaped_charge = false },
|
|
@@ -574,21 +708,107 @@ explTable = {
|
|
|
574
708
|
["AGR_20_M282"] = { explosive = 8, shaped_charge = false },
|
|
575
709
|
["Hydra_70_M282_MPP"] = { explosive = 5, shaped_charge = true },
|
|
576
710
|
["BRM-1_90MM"] = { explosive = 8, shaped_charge = false },
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
711
|
|
|
712
|
+
--*** JF17 weapons changes as per Kurdes ***
|
|
713
|
+
["C_701T"] = { explosive = 38, shaped_charge = false },
|
|
714
|
+
["C_701IR"] = { explosive = 38, shaped_charge = false },
|
|
715
|
+
["LS_6_100"] = { explosive = 45, shaped_charge = false },
|
|
716
|
+
["LS_6"] = { explosive = 100, shaped_charge = false },
|
|
717
|
+
["LS_6_500"] = { explosive = 274, shaped_charge = false },
|
|
718
|
+
|
|
719
|
+
--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
|
|
720
|
+
--*** Vehicle/Ship based ***--
|
|
721
|
+
--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
|
|
722
|
+
|
|
723
|
+
--*** Rocketry ***
|
|
724
|
+
["9M22U"] = { explosive = 25, shaped_charge = false, groundordnance = true }, --122mm HE rocket, BM-21 Grad (~20-30 kg TNT equiv)
|
|
725
|
+
["GRAD_9M22U"] = { explosive = 25, shaped_charge = false, groundordnance = true }, --122mm HE rocket, BM-21 Grad (~20-30 kg TNT equiv)
|
|
726
|
+
-- ["M26"] = { explosive = 0, shaped_charge = false, groundordnance = true}, --227mm cluster rocket, M270 MLRS (adjusted for cluster)
|
|
727
|
+
["M26"] = { explosive = 0, shaped_charge = false, cluster = true, submunition_count = 644, submunition_explosive = 0.1, submunition_name = "M77", groundordnance = true }, --227mm cluster rocket, M270 MLRS (adjusted for cluster)
|
|
728
|
+
["SCUD_RAKETA"] = { explosive = 985, shaped_charge = false, groundordnance = true },
|
|
729
|
+
["SMERCH_9M55F"] = { explosive = 46, shaped_charge = false, groundordnance = true }, --220mm HE rocket, (~25-45 kg TNT equiv)
|
|
730
|
+
|
|
731
|
+
["TOW2"] = { explosive = 6.5, shaped_charge = true, groundordnance = true }, --ATGM
|
|
732
|
+
|
|
733
|
+
--*** Shells ***
|
|
734
|
+
["weapons.shells.M_105mm_HE"] = { explosive = 12, shaped_charge = false, groundordnance = true }, --105mm HE shell, M119/M102 (~10-15 kg TNT equiv)
|
|
735
|
+
["weapons.shells.M_155mm_HE"] = { explosive = 60, shaped_charge = false, groundordnance = true }, --155mm HE shell, M777/M109 (~50-70 kg TNT equiv)
|
|
736
|
+
["weapons.shells.2A60_120"] = { explosive = 18, shaped_charge = false, groundordnance = true }, --120mm HE shell, 2B11 mortar (~15-20 kg TNT equiv)
|
|
737
|
+
["weapons.shells.2A18_122"] = { explosive = 22, shaped_charge = false, groundordnance = true }, --122mm HE shell, D-30 (~20-25 kg TNT equiv)
|
|
738
|
+
["weapons.shells.2A33_152"] = { explosive = 50, shaped_charge = false, groundordnance = true }, --152mm HE shell, SAU Akatsia (~40-60 kg TNT equiv)
|
|
739
|
+
["weapons.shells.PLZ_155_HE"] = { explosive = 60, shaped_charge = false, groundordnance = true }, --155mm HE shell, PLZ05 (~50-70 kg TNT equiv)
|
|
740
|
+
["weapons.shells.M185_155"] = { explosive = 60, shaped_charge = false, groundordnance = true }, --155mm HE shell, M109 (~50-70 kg TNT equiv)
|
|
741
|
+
["weapons.shells.2A64_152"] = { explosive = 50, shaped_charge = false, groundordnance = true }, --152mm HE shell, SAU Msta (~40-60 kg TNT equiv)
|
|
742
|
+
|
|
743
|
+
["weapons.shells.2A46M_125_HE"] = { explosive = 5, shaped_charge = false, groundordnance = true }, --125mm HE shell, T-90 (~5-6 kg TNT equiv)
|
|
744
|
+
["weapons.shells.HESH_105"] = { explosive = 6, shaped_charge = false, groundordnance = true }, --105mm HESH shell, M1128 Stryker (~4-6 kg TNT equiv)
|
|
745
|
+
|
|
746
|
+
---*** Naval ***
|
|
747
|
+
["weapons.missiles.AGM_84S"] = { explosive = 225, shaped_charge = false, groundordnance = true }, --Harpoon missile, Ticonderoga (~200-250 kg TNT equiv)
|
|
748
|
+
["weapons.missiles.P_500"] = { explosive = 500, shaped_charge = false, groundordnance = true }, --P-500 Bazalt missile, Moscow (~450-550 kg TNT equiv)
|
|
749
|
+
|
|
750
|
+
["weapons.shells.AK176_76"] = { explosive = 1, shaped_charge = false, groundordnance = true }, --76mm HE shell, AK-176 (~0.7-1 kg TNT equiv)
|
|
751
|
+
["weapons.shells.A222_130"] = { explosive = 5, shaped_charge = false, groundordnance = true }, --130mm HE shell, A-222 Bereg (~4-5 kg TNT equiv)
|
|
752
|
+
["weapons.shells.53-UBR-281U"] = { explosive = 5, shaped_charge = false, groundordnance = true }, --130mm HE shell, SM-2-1 (~4-5 kg TNT equiv)
|
|
753
|
+
["weapons.shells.PJ87_100_PFHE"] = { explosive = 3, shaped_charge = false, groundordnance = true }, --100mm HE-PF shell, Type 052B (~2.4-3.4 kg TNT equiv)
|
|
754
|
+
["weapons.shells.AK100_100"] = { explosive = 3, shaped_charge = false, groundordnance = true }, --100mm HE shell, AK-100 (~2.5-3.5 kg TNT equiv) AK-100 100mm (e.g., on Project 1135 Krivak-class)
|
|
755
|
+
["weapons.shells.AK130_130"] = { explosive = 5, shaped_charge = false, groundordnance = true }, --130mm HE shell, AK-130 (~4-5 kg TNT equiv) AK-130 130mm (e.g., on Project 956 Sovremenny-class)
|
|
756
|
+
["weapons.shells.2A70_100"] = { explosive = 3, shaped_charge = false, groundordnance = true }, --100mm HE shell, 2A70 (~3-3.5 kg TNT equiv) 2A70 100mm (e.g., on Project 775 Ropucha-class)
|
|
757
|
+
["weapons.shells.OTO_76"] = { explosive = 1, shaped_charge = false, groundordnance = true }, --76mm HE shell, OTO Melara (~0.8-1.1 kg TNT equiv) OTO Melara 76mm (e.g., on NATO frigates like Oliver Hazard Perry-class)
|
|
758
|
+
["weapons.shells.MK45_127"] = { explosive = 5, shaped_charge = false, groundordnance = true }, --127mm HE shell, Mark 45 (~4.8-5.6 kg TNT equiv) Mark 45 127mm (e.g., on Arleigh Burke-class destroyers)
|
|
759
|
+
["weapons.shells.PJ26_76_PFHE"] = { explosive = 1, shaped_charge = false, groundordnance = true }, --76mm HE-PF shell, PJ-26 (~0.8-1.1 kg TNT equiv)
|
|
760
|
+
["weapons.shells.53-UOR-281U"] = { explosive = 5, shaped_charge = false, groundordnance = true }, --130mm HE shell, SM-2-1 (~4-5 kg TNT equiv)
|
|
761
|
+
["weapons.shells.MK75_76"] = { explosive = 1, shaped_charge = false, groundordnance = true }, --76mm HE shell, Mk 75 (~0.8-1.1 kg TNT equiv)
|
|
762
|
+
|
|
763
|
+
--*** Bismark Mod Weapon ***
|
|
764
|
+
["weapons.shells.Breda_37_HE"] = { explosive = 70, shaped_charge = false, groundordnance = true }, --380mm HE shell, 38 cm SK C/34 (~60-75 kg TNT equiv)
|
|
765
|
+
--*** Bismark Mod Weapons ***
|
|
766
|
+
["weapons.shells.380mm_HE"] = { explosive = 70, shaped_charge = false, groundordnance = true }, --380mm HE shell, 38 cm SK C/34 (~60-75 kg TNT equiv)
|
|
767
|
+
["weapons.shells.SK_C_33_105_HE"] = { explosive = 15, shaped_charge = false, groundordnance = true }, --105mm HE shell, SK C/33 (~12-16 kg TNT equiv)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
}
|
|
582
773
|
|
|
774
|
+
napalm_unitcat_tabl = {
|
|
775
|
+
["Infantry"] = { maxDamageDistance = 50, explosionPower = 0.5 },
|
|
776
|
+
["Tank"] = { maxDamageDistance = 30, explosionPower = 5 },
|
|
777
|
+
["Artillery"] = { maxDamageDistance = 40, explosionPower = 5 },
|
|
778
|
+
["Armored Vehicle"] = { maxDamageDistance = 35, explosionPower = 5 },
|
|
779
|
+
["Anti-Air"] = { maxDamageDistance = 35, explosionPower = 5 },
|
|
780
|
+
["Helicopter"] = { maxDamageDistance = 45, explosionPower = 5 },
|
|
781
|
+
["Airplane"] = { maxDamageDistance = 40, explosionPower = 5 },
|
|
782
|
+
["Structure"] = { maxDamageDistance = 60, explosionPower = 60 }
|
|
783
|
+
}
|
|
583
784
|
|
|
584
785
|
local effectSmokeId = 1
|
|
585
786
|
|
|
586
787
|
----[[ ##### HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
587
788
|
|
|
588
|
-
|
|
589
|
-
|
|
789
|
+
--Global tables
|
|
790
|
+
local processedUnitIds = {}
|
|
791
|
+
local killfeedTable = {}
|
|
792
|
+
local splashKillfeedTable = {}
|
|
793
|
+
local splashKillfeedTemp = {}
|
|
794
|
+
local LogEventProcessedUnitTable = {}
|
|
795
|
+
|
|
796
|
+
--Function to safely get data with pcall
|
|
797
|
+
local function safeGet(func, default)
|
|
798
|
+
local success, result = pcall(func)
|
|
799
|
+
return success and result or default
|
|
590
800
|
end
|
|
591
|
-
|
|
801
|
+
|
|
802
|
+
--Function to clear processed unit IDs after a delay
|
|
803
|
+
function clearProcessedUnitIds(unitId)
|
|
804
|
+
if processedUnitIds[unitId] then
|
|
805
|
+
processedUnitIds[unitId] = nil
|
|
806
|
+
if splash_damage_options.napalm_unitdamage_debug then
|
|
807
|
+
env.info("scanUnitsForNapalm: Cleared unit ID " .. unitId .. " from processedUnitIds")
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
|
|
592
812
|
local function debugMsg(str)
|
|
593
813
|
if splash_damage_options.debug == true then
|
|
594
814
|
debugCounter = (debugCounter or 0) + 1
|
|
@@ -597,29 +817,303 @@ local function debugMsg(str)
|
|
|
597
817
|
env.info("DEBUG: " .. uniqueStr)
|
|
598
818
|
end
|
|
599
819
|
end
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
820
|
+
|
|
821
|
+
function napalm_phosphor(vec3)
|
|
822
|
+
local baseFlareCount = math.random(0, 8) -- Wider range for variation
|
|
823
|
+
local randomFactor = math.random(0.1, 1) -- Random scaling per call
|
|
824
|
+
local scaledFlareCount = math.max(1, math.floor(baseFlareCount * splash_damage_options.napalm_phosphor_multiplier * randomFactor))
|
|
825
|
+
for i = 1, scaledFlareCount do
|
|
826
|
+
local randomAzimuth = math.random(0, 359) -- Random angle for scatter
|
|
827
|
+
local offsetX = math.random(-15, 15) -- Position offset (meters)
|
|
828
|
+
local offsetZ = math.random(-15, 15)
|
|
829
|
+
local flarePos = { x = vec3.x + offsetX, y = vec3.y, z = vec3.z + offsetZ }
|
|
830
|
+
trigger.action.signalFlare(flarePos, 2, randomAzimuth)
|
|
831
|
+
end
|
|
832
|
+
if splash_damage_options.debug then
|
|
833
|
+
debugMsg("Triggered " .. scaledFlareCount .. " napalm phosphor flares at X: " .. string.format("%.0f", vec3.x) .. ", Z: " .. string.format("%.0f", vec3.z))
|
|
604
834
|
end
|
|
605
835
|
end
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
local
|
|
610
|
-
local
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
local
|
|
616
|
-
local
|
|
617
|
-
|
|
836
|
+
|
|
837
|
+
--getSpreadPoints function
|
|
838
|
+
local function getSpreadPoints(impactPoint, velocity, numPoints, spacing)
|
|
839
|
+
local points = {}
|
|
840
|
+
local mag = math.sqrt(velocity.x^2 + velocity.z^2)
|
|
841
|
+
if mag == 0 then
|
|
842
|
+
table.insert(points, {x = impactPoint.x, y = land.getHeight({x = impactPoint.x, y = impactPoint.z}), z = impactPoint.z})
|
|
843
|
+
return points
|
|
844
|
+
end
|
|
845
|
+
local dir = {x = velocity.x / mag, z = velocity.z / mag}
|
|
846
|
+
local perpDir = {x = -dir.z, z = dir.x} --Perpendicular to velocity direction
|
|
847
|
+
local prevHeight = land.getHeight({x = impactPoint.x, y = impactPoint.z})
|
|
848
|
+
for i = 1, numPoints do
|
|
849
|
+
local offset = (i - 1) * spacing
|
|
850
|
+
if splash_damage_options.napalm_doublewide_enabled then
|
|
851
|
+
--Double-wide: two points with ±15m lateral offset
|
|
852
|
+
local point1 = {
|
|
853
|
+
x = impactPoint.x + dir.x * offset + perpDir.x * splash_damage_options.napalm_doublewide_spread,
|
|
854
|
+
z = impactPoint.z + dir.z * offset + perpDir.z * splash_damage_options.napalm_doublewide_spread
|
|
855
|
+
}
|
|
856
|
+
local terrainHeight1 = land.getHeight({x = point1.x, y = point1.z})
|
|
857
|
+
local heightDiff1 = terrainHeight1 - prevHeight
|
|
858
|
+
point1.y = prevHeight + math.max(math.min(heightDiff1, 30), -30)
|
|
859
|
+
table.insert(points, point1)
|
|
860
|
+
local point2 = {
|
|
861
|
+
x = impactPoint.x + dir.x * offset - perpDir.x * splash_damage_options.napalm_doublewide_spread,
|
|
862
|
+
z = impactPoint.z + dir.z * offset - perpDir.z * splash_damage_options.napalm_doublewide_spread
|
|
863
|
+
}
|
|
864
|
+
local terrainHeight2 = land.getHeight({x = point2.x, y = point2.z})
|
|
865
|
+
local heightDiff2 = terrainHeight2 - prevHeight
|
|
866
|
+
point2.y = prevHeight + math.max(math.min(heightDiff2, 30), -30)
|
|
867
|
+
table.insert(points, point2)
|
|
868
|
+
prevHeight = (terrainHeight1 + terrainHeight2) / 2
|
|
869
|
+
else
|
|
870
|
+
--Single point, linear spread
|
|
871
|
+
local point = {
|
|
872
|
+
x = impactPoint.x + dir.x * offset,
|
|
873
|
+
z = impactPoint.z + dir.z * offset
|
|
874
|
+
}
|
|
875
|
+
local terrainHeight = land.getHeight({x = point.x, y = point.z})
|
|
876
|
+
local heightDiff = terrainHeight - prevHeight
|
|
877
|
+
point.y = prevHeight + math.max(math.min(heightDiff, 30), -30)
|
|
878
|
+
table.insert(points, point)
|
|
879
|
+
prevHeight = terrainHeight
|
|
880
|
+
end
|
|
881
|
+
end
|
|
882
|
+
return points
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
function V3Mag(speedVec)
|
|
887
|
+
local mag = speedVec.x*speedVec.x + speedVec.y*speedVec.y+speedVec.z*speedVec.z
|
|
888
|
+
mag = math.sqrt(mag)
|
|
889
|
+
return mag
|
|
618
890
|
end
|
|
619
891
|
|
|
620
|
-
|
|
621
|
-
local
|
|
622
|
-
local
|
|
892
|
+
function Vhead(speedVec)
|
|
893
|
+
local speed = V3Mag(speedVec)
|
|
894
|
+
local dist = speed * refreshRate * 1.5
|
|
895
|
+
return dist
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
function explodeNapalm(vec3)
|
|
899
|
+
local explosionPos = {
|
|
900
|
+
x = vec3.x,
|
|
901
|
+
y = vec3.y + 1.6, --Add 1.6m to the ground height
|
|
902
|
+
z = vec3.z
|
|
903
|
+
}
|
|
904
|
+
trigger.action.explosion(explosionPos, 10)
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
-- Helper function to calculate 2D distance
|
|
908
|
+
local function getDistance(point1, point2)
|
|
909
|
+
local dX = math.abs(point1.x - point2.x)
|
|
910
|
+
local dZ = math.abs(point1.z - point2.z)
|
|
911
|
+
return math.sqrt(dX * dX + dZ * dZ)
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
--Scan for units around the napalm explosions and apply damage if required
|
|
915
|
+
function scanUnitsForNapalm(posX, posY, posZ)
|
|
916
|
+
if not splash_damage_options.napalm_unitdamage_enable then
|
|
917
|
+
if splash_damage_options.napalm_unitdamage_debug then
|
|
918
|
+
env.info("scanUnitsForNapalm: Napalm unit damage disabled, skipping scan")
|
|
919
|
+
end
|
|
920
|
+
return
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
if splash_damage_options.napalm_unitdamage_debug then
|
|
924
|
+
env.info("scanUnitsForNapalm: Starting scan at (X: " .. posX .. ", Y: " .. posY .. ", Z: " .. posZ .. ") with radius " .. splash_damage_options.napalm_unitdamage_scandistance)
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
local volS = {
|
|
928
|
+
id = world.VolumeType.SPHERE,
|
|
929
|
+
params = {
|
|
930
|
+
point = {x = posX, y = posY, z = posZ},
|
|
931
|
+
radius = splash_damage_options.napalm_unitdamage_scandistance
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
local foundUnits = {}
|
|
936
|
+
local status, err = pcall(function()
|
|
937
|
+
-- Scan for units
|
|
938
|
+
world.searchObjects(Object.Category.UNIT, volS, function(foundObject)
|
|
939
|
+
local success, result = pcall(function()
|
|
940
|
+
if foundObject:isExist() and foundObject:getCategory() == Object.Category.UNIT then
|
|
941
|
+
local unitType = foundObject:getTypeName() or "Unknown"
|
|
942
|
+
-- Exclude Fuel tank
|
|
943
|
+
if unitType ~= "Fuel tank" then
|
|
944
|
+
local unitPos = foundObject:getPoint()
|
|
945
|
+
local distance = getDistance({x = posX, y = posY, z = posZ}, unitPos)
|
|
946
|
+
if distance <= splash_damage_options.napalm_unitdamage_scandistance then
|
|
947
|
+
local category = "Unknown"
|
|
948
|
+
local desc = foundObject:getDesc()
|
|
949
|
+
if desc and foundObject:hasAttribute("Infantry") then
|
|
950
|
+
category = "Infantry"
|
|
951
|
+
elseif desc and foundObject:hasAttribute("Tanks") then
|
|
952
|
+
category = "Tank"
|
|
953
|
+
elseif desc and foundObject:hasAttribute("Artillery") then
|
|
954
|
+
category = "Artillery"
|
|
955
|
+
elseif desc and foundObject:hasAttribute("Armored vehicles") then
|
|
956
|
+
category = "Armored Vehicle"
|
|
957
|
+
elseif desc and foundObject:hasAttribute("AA") then
|
|
958
|
+
category = "Anti-Air"
|
|
959
|
+
elseif desc and foundObject:hasAttribute("Helicopters") then
|
|
960
|
+
category = "Helicopter"
|
|
961
|
+
elseif desc and foundObject:hasAttribute("Planes") then
|
|
962
|
+
category = "Airplane"
|
|
963
|
+
end
|
|
964
|
+
table.insert(foundUnits, {
|
|
965
|
+
unit = foundObject,
|
|
966
|
+
id = foundObject:getID(),
|
|
967
|
+
type = unitType,
|
|
968
|
+
distance = distance,
|
|
969
|
+
category = category,
|
|
970
|
+
position = unitPos
|
|
971
|
+
})
|
|
972
|
+
end
|
|
973
|
+
end
|
|
974
|
+
end
|
|
975
|
+
end)
|
|
976
|
+
if not success and splash_damage_options.napalm_unitdamage_debug then
|
|
977
|
+
env.info("scanUnitsForNapalm: Error processing unit ID " .. (foundObject:getID() or "unknown") .. ": " .. tostring(result))
|
|
978
|
+
end
|
|
979
|
+
return true
|
|
980
|
+
end)
|
|
981
|
+
-- Scan for static objects
|
|
982
|
+
world.searchObjects(Object.Category.STATIC, volS, function(foundObject)
|
|
983
|
+
local success, result = pcall(function()
|
|
984
|
+
if foundObject:isExist() and foundObject:getCategory() == Object.Category.STATIC then
|
|
985
|
+
local unitType = foundObject:getTypeName() or "Unknown"
|
|
986
|
+
-- Exclude Fuel tank
|
|
987
|
+
if unitType ~= "Fuel tank" then
|
|
988
|
+
local unitPos = foundObject:getPoint()
|
|
989
|
+
local distance = getDistance({x = posX, y = posY, z = posZ}, unitPos)
|
|
990
|
+
if distance <= splash_damage_options.napalm_unitdamage_scandistance then
|
|
991
|
+
table.insert(foundUnits, {
|
|
992
|
+
unit = foundObject,
|
|
993
|
+
id = foundObject:getID(),
|
|
994
|
+
type = unitType,
|
|
995
|
+
distance = distance,
|
|
996
|
+
category = "Structure",
|
|
997
|
+
position = unitPos
|
|
998
|
+
})
|
|
999
|
+
end
|
|
1000
|
+
end
|
|
1001
|
+
end
|
|
1002
|
+
end)
|
|
1003
|
+
if not success and splash_damage_options.napalm_unitdamage_debug then
|
|
1004
|
+
env.info("scanUnitsForNapalm: Error processing static object ID " .. (foundObject:getID() or "unknown") .. ": " .. tostring(result))
|
|
1005
|
+
end
|
|
1006
|
+
return true
|
|
1007
|
+
end)
|
|
1008
|
+
end)
|
|
1009
|
+
|
|
1010
|
+
if not status and splash_damage_options.napalm_unitdamage_debug then
|
|
1011
|
+
env.info("scanUnitsForNapalm: Error during scan: " .. tostring(err))
|
|
1012
|
+
return
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
table.sort(foundUnits, function(a, b) return a.distance < b.distance end)
|
|
1016
|
+
|
|
1017
|
+
if splash_damage_options.napalm_unitdamage_debug then
|
|
1018
|
+
env.info("scanUnitsForNapalm: Scan completed, found " .. #foundUnits .. " objects within " .. splash_damage_options.napalm_unitdamage_scandistance .. " meters at position (X: " .. posX .. ", Y: " .. posY .. ", Z: " .. posZ .. ")")
|
|
1019
|
+
-- Log all found objects
|
|
1020
|
+
for _, unitData in ipairs(foundUnits) do
|
|
1021
|
+
env.info("scanUnitsForNapalm: Found object ID " .. tostring(unitData.id) .. " of type: " .. unitData.type .. ", Category: " .. unitData.category .. ", Distance: " .. string.format("%.2f", unitData.distance) .. " meters, Position: (X: " .. string.format("%.2f", unitData.position.x) .. ", Y: " .. string.format("%.2f", unitData.position.y) .. ", Z: " .. string.format("%.2f", unitData.position.z) .. ")")
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
if #foundUnits > 0 then
|
|
1026
|
+
local processedPositions = {} -- Track processed coordinates for this scan
|
|
1027
|
+
local explosionIndex = 0
|
|
1028
|
+
for _, unitData in ipairs(foundUnits) do
|
|
1029
|
+
if napalm_unitcat_tabl[unitData.category] and unitData.distance <= napalm_unitcat_tabl[unitData.category].maxDamageDistance then
|
|
1030
|
+
-- Check if unit ID has already been processed
|
|
1031
|
+
if not processedUnitIds[unitData.id] then
|
|
1032
|
+
-- Check for duplicate position (within 1 meter)
|
|
1033
|
+
local posKey = string.format("%.0f_%.0f_%.0f", unitData.position.x, unitData.position.y, unitData.position.z)
|
|
1034
|
+
if not processedPositions[posKey] then
|
|
1035
|
+
-- Check if unit is still alive (for units) or exists (for statics)
|
|
1036
|
+
local isAlive = unitData.unit:isExist() and (unitData.category == "Structure" or unitData.unit:getLife() > 0)
|
|
1037
|
+
if isAlive then
|
|
1038
|
+
processedPositions[posKey] = true
|
|
1039
|
+
processedUnitIds[unitData.id] = true
|
|
1040
|
+
local power = napalm_unitcat_tabl[unitData.category].explosionPower
|
|
1041
|
+
-- Calculate delay
|
|
1042
|
+
local delay = splash_damage_options.napalm_unitdamage_startdelay
|
|
1043
|
+
if splash_damage_options.napalm_unitdamage_startdelay > 0 then
|
|
1044
|
+
delay = delay + (explosionIndex * splash_damage_options.napalm_unitdamage_spreaddelay)
|
|
1045
|
+
explosionIndex = explosionIndex + 1
|
|
1046
|
+
end
|
|
1047
|
+
-- Adjust position for infantry to reduce ground interaction
|
|
1048
|
+
local explosionPos = unitData.position
|
|
1049
|
+
if unitData.category == "Infantry" then
|
|
1050
|
+
explosionPos = {
|
|
1051
|
+
x = unitData.position.x,
|
|
1052
|
+
y = land.getHeight({x = unitData.position.x, y = unitData.position.z}) + 1.6,
|
|
1053
|
+
z = unitData.position.z
|
|
1054
|
+
}
|
|
1055
|
+
end
|
|
1056
|
+
if splash_damage_options.napalm_unitdamage_debug then
|
|
1057
|
+
env.info("scanUnitsForNapalm: Scheduling explosion on unit ID " .. tostring(unitData.id) .. " (" .. unitData.type .. ") at (X: " .. string.format("%.2f", explosionPos.x) .. ", Z: " .. string.format("%.2f", explosionPos.z) .. ") with power " .. power .. " after " .. string.format("%.2f", delay) .. "s")
|
|
1058
|
+
end
|
|
1059
|
+
timer.scheduleFunction(function(params)
|
|
1060
|
+
trigger.action.explosion(params.position, params.power)
|
|
1061
|
+
end, {position = explosionPos, power = power}, timer.getTime() + delay)
|
|
1062
|
+
-- Schedule cleanup for this unit ID 20 seconds after its explosion
|
|
1063
|
+
timer.scheduleFunction(clearProcessedUnitIds, unitData.id, timer.getTime() + delay + 20)
|
|
1064
|
+
elseif splash_damage_options.napalm_unitdamage_debug then
|
|
1065
|
+
env.info("scanUnitsForNapalm: Skipped explosion for unit ID " .. tostring(unitData.id) .. " (" .. unitData.type .. ") at (X: " .. string.format("%.2f", unitData.position.x) .. ", Z: " .. string.format("%.2f", unitData.position.z) .. ") because unit is not alive (isExist: " .. tostring(unitData.unit:isExist()) .. ", life: " .. (unitData.category == "Structure" and "N/A" or tostring(unitData.unit:getLife())) .. ")")
|
|
1066
|
+
end
|
|
1067
|
+
elseif splash_damage_options.napalm_unitdamage_debug then
|
|
1068
|
+
env.info("scanUnitsForNapalm: Skipped explosion for unit ID " .. tostring(unitData.id) .. " (" .. unitData.type .. ") at (X: " .. string.format("%.2f", unitData.position.x) .. ", Z: " .. string.format("%.2f", unitData.position.z) .. ") due to duplicate position")
|
|
1069
|
+
end
|
|
1070
|
+
elseif splash_damage_options.napalm_unitdamage_debug then
|
|
1071
|
+
env.info("scanUnitsForNapalm: Skipped explosion for unit ID " .. tostring(unitData.id) .. " (" .. unitData.type .. ") at (X: " .. string.format("%.2f", unitData.position.x) .. ", Z: " .. string.format("%.2f", unitData.position.z) .. ") due to already processed unit ID")
|
|
1072
|
+
end
|
|
1073
|
+
end
|
|
1074
|
+
end
|
|
1075
|
+
else
|
|
1076
|
+
if splash_damage_options.napalm_unitdamage_debug then
|
|
1077
|
+
env.info("scanUnitsForNapalm: No objects found in scan area")
|
|
1078
|
+
end
|
|
1079
|
+
end
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
function removeNapalm(staticName)
|
|
1085
|
+
StaticObject.getByName(staticName):destroy()
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
local function tableHasKey(table, key)
|
|
1091
|
+
return table[key] ~= nil
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
local function gameMsg(str)
|
|
1096
|
+
if splash_damage_options.game_messages == true then
|
|
1097
|
+
trigger.action.outText(str, 5)
|
|
1098
|
+
end
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
local function getDistance(point1, point2)
|
|
1102
|
+
local x1 = point1.x
|
|
1103
|
+
local y1 = point1.y
|
|
1104
|
+
local z1 = point1.z
|
|
1105
|
+
local x2 = point2.x
|
|
1106
|
+
local y2 = point2.y
|
|
1107
|
+
local z2 = point2.z
|
|
1108
|
+
local dX = math.abs(x1 - x2)
|
|
1109
|
+
local dZ = math.abs(z1 - z2)
|
|
1110
|
+
local distance = math.sqrt(dX * dX + dZ * dZ)
|
|
1111
|
+
return distance
|
|
1112
|
+
end
|
|
1113
|
+
|
|
1114
|
+
local function getDistance3D(point1, point2)
|
|
1115
|
+
local x1 = point1.x
|
|
1116
|
+
local y1 = point1.y
|
|
623
1117
|
local z1 = point1.z
|
|
624
1118
|
local x2 = point2.x
|
|
625
1119
|
local y2 = point2.y
|
|
@@ -640,7 +1134,167 @@ local function lookahead(speedVec)
|
|
|
640
1134
|
local dist = speed * refreshRate * 1.5
|
|
641
1135
|
return dist
|
|
642
1136
|
end
|
|
643
|
-
|
|
1137
|
+
|
|
1138
|
+
function napalmOnImpact(impactPoint, velocity, weaponName)
|
|
1139
|
+
if not (splash_damage_options.napalmoverride_enabled or (splash_damage_options.napalm_mk77_enabled and (weaponName == "MK77mod0-WPN" or weaponName == "MK77mod1-WPN"))) then return end
|
|
1140
|
+
--For MK77 cluster munitions, snap impact point to ground
|
|
1141
|
+
local finalImpactPoint = impactPoint
|
|
1142
|
+
if splash_damage_options.napalm_mk77_enabled and (weaponName == "MK77mod0-WPN" or weaponName == "MK77mod1-WPN") then
|
|
1143
|
+
local groundHeight = land.getHeight({x = impactPoint.x, y = impactPoint.z})
|
|
1144
|
+
finalImpactPoint = {
|
|
1145
|
+
x = impactPoint.x,
|
|
1146
|
+
y = groundHeight,
|
|
1147
|
+
z = impactPoint.z
|
|
1148
|
+
}
|
|
1149
|
+
if splash_damage_options.debug then
|
|
1150
|
+
debugMsg("Snapped MK77 " .. weaponName .. " impact to ground at X: " .. string.format("%.0f", finalImpactPoint.x) .. ", Z: " .. string.format("%.0f", finalImpactPoint.z))
|
|
1151
|
+
end
|
|
1152
|
+
else
|
|
1153
|
+
--For non-MK77, skip if more than 50m above ground
|
|
1154
|
+
local groundHeight = land.getHeight({x = impactPoint.x, y = impactPoint.z})
|
|
1155
|
+
if impactPoint.y - groundHeight > 50 then return end --Skip if more than 50m above ground
|
|
1156
|
+
end
|
|
1157
|
+
|
|
1158
|
+
--Adjust spread points for MK77mod0-WPN (30% more)
|
|
1159
|
+
local spreadPointsCount = splash_damage_options.napalm_spread_points
|
|
1160
|
+
if weaponName == "MK77mod0-WPN" then
|
|
1161
|
+
spreadPointsCount = math.floor(spreadPointsCount * 1.3 + 0.5) --30% more, rounded
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
--Use horizontal velocity for MK77, full velocity for others
|
|
1165
|
+
local spreadVelocity = velocity
|
|
1166
|
+
if weaponName == "MK77mod0-WPN" or weaponName == "MK77mod1-WPN" then
|
|
1167
|
+
spreadVelocity = {x = velocity.x, z = velocity.z}
|
|
1168
|
+
end
|
|
1169
|
+
local spreadPoints = getSpreadPoints(finalImpactPoint, spreadVelocity, spreadPointsCount, splash_damage_options.napalm_spread_spacing)
|
|
1170
|
+
if splash_damage_options.debug then
|
|
1171
|
+
debugMsg("Generated " .. #spreadPoints .. " spread points for " .. weaponName .. " (expected " .. (splash_damage_options.napalm_doublewide_enabled and spreadPointsCount * 2 or spreadPointsCount) .. ")")
|
|
1172
|
+
for i, point in ipairs(spreadPoints) do
|
|
1173
|
+
debugMsg("Point " .. i .. ": X: " .. string.format("%.0f", point.x) .. ", Y: " .. string.format("%.0f", point.y) .. ", Z: " .. string.format("%.0f", point.z))
|
|
1174
|
+
end
|
|
1175
|
+
end
|
|
1176
|
+
local flamePositions = {} -- Track flame coordinates to avoid duplicates
|
|
1177
|
+
local function spawnAndExplode(pairIndex)
|
|
1178
|
+
if pairIndex > spreadPointsCount then return end
|
|
1179
|
+
local pointsToProcess = {}
|
|
1180
|
+
if splash_damage_options.napalm_doublewide_enabled then
|
|
1181
|
+
-- Process two points (pair) at indices 2*pairIndex-1 and 2*pairIndex
|
|
1182
|
+
local idx1 = 2 * pairIndex - 1
|
|
1183
|
+
local idx2 = 2 * pairIndex
|
|
1184
|
+
if idx1 <= #spreadPoints then
|
|
1185
|
+
table.insert(pointsToProcess, spreadPoints[idx1])
|
|
1186
|
+
end
|
|
1187
|
+
if idx2 <= #spreadPoints then
|
|
1188
|
+
table.insert(pointsToProcess, spreadPoints[idx2])
|
|
1189
|
+
end
|
|
1190
|
+
else
|
|
1191
|
+
-- Process single point at pairIndex
|
|
1192
|
+
if pairIndex <= #spreadPoints then
|
|
1193
|
+
table.insert(pointsToProcess, spreadPoints[pairIndex])
|
|
1194
|
+
end
|
|
1195
|
+
end
|
|
1196
|
+
for _, point in ipairs(pointsToProcess) do
|
|
1197
|
+
local napalmName = "napalmImpact" .. napalmCounter
|
|
1198
|
+
local currentCounter = napalmCounter
|
|
1199
|
+
napalmCounter = napalmCounter + 1
|
|
1200
|
+
local owngroupID = math.random(9999, 99999)
|
|
1201
|
+
local cvnunitID = math.random(9999, 99999)
|
|
1202
|
+
local _dataFuel = {
|
|
1203
|
+
["groupId"] = owngroupID,
|
|
1204
|
+
["category"] = "Fortifications",
|
|
1205
|
+
["shape_name"] = "toplivo-bak",
|
|
1206
|
+
["type"] = "Fuel tank",
|
|
1207
|
+
["unitId"] = cvnunitID,
|
|
1208
|
+
["rate"] = 100,
|
|
1209
|
+
["y"] = point.z,
|
|
1210
|
+
["x"] = point.x,
|
|
1211
|
+
["name"] = napalmName,
|
|
1212
|
+
["heading"] = 0,
|
|
1213
|
+
["dead"] = false,
|
|
1214
|
+
["hidden"] = true,
|
|
1215
|
+
}
|
|
1216
|
+
if splash_damage_options.debug then
|
|
1217
|
+
local staticCount = 0
|
|
1218
|
+
for _, coalitionId in pairs(coalition.side) do
|
|
1219
|
+
local statics = coalition.getStaticObjects(coalitionId)
|
|
1220
|
+
staticCount = staticCount + #statics
|
|
1221
|
+
end
|
|
1222
|
+
debugMsg("Spawning napalm object '" .. napalmName .. "' (Counter: " .. currentCounter .. ") at X: " .. string.format("%.0f", point.x) .. ", Y: " .. string.format("%.0f", point.y) .. ", Z: " .. string.format("%.0f", point.z) .. " (Active static objects: " .. staticCount .. ")")
|
|
1223
|
+
end
|
|
1224
|
+
local status, result = pcall(function()
|
|
1225
|
+
return coalition.addStaticObject(coalition.side.BLUE, _dataFuel)
|
|
1226
|
+
end)
|
|
1227
|
+
local spawnSuccess = status and result and StaticObject.getByName(napalmName) and StaticObject.getByName(napalmName):isExist()
|
|
1228
|
+
if not spawnSuccess then
|
|
1229
|
+
if splash_damage_options.debug then
|
|
1230
|
+
debugMsg("Failed to spawn napalm object '" .. napalmName .. "' at X: " .. string.format("%.0f", point.x) .. ", Y: " .. string.format("%.0f", point.y) .. ", Z: " .. string.format("%.0f", point.z) .. ": " .. (status and "Object not found or does not exist" or tostring(result)))
|
|
1231
|
+
end
|
|
1232
|
+
--Fallback: Trigger explosion without static object
|
|
1233
|
+
--timer.scheduleFunction(explodeNapalm, point, timer.getTime() + splash_damage_options.napalm_explode_delay)
|
|
1234
|
+
else
|
|
1235
|
+
timer.scheduleFunction(explodeNapalm, point, timer.getTime() + splash_damage_options.napalm_explode_delay)
|
|
1236
|
+
timer.scheduleFunction(function(name)
|
|
1237
|
+
if splash_damage_options.debug then
|
|
1238
|
+
debugMsg("Destroying napalm object '" .. name .. "' at X: " .. string.format("%.0f", point.x) .. ", Z: " .. string.format("%.0f", point.z))
|
|
1239
|
+
end
|
|
1240
|
+
removeNapalm(name)
|
|
1241
|
+
end, napalmName, timer.getTime() + splash_damage_options.napalm_destroy_delay)
|
|
1242
|
+
end
|
|
1243
|
+
if splash_damage_options.napalm_phosphor_enabled then
|
|
1244
|
+
timer.scheduleFunction(napalm_phosphor, point, timer.getTime() + splash_damage_options.napalm_explode_delay)
|
|
1245
|
+
local status, err = pcall(function()
|
|
1246
|
+
scanUnitsForNapalm(point.x, point.y, point.z)
|
|
1247
|
+
end)
|
|
1248
|
+
if not status then
|
|
1249
|
+
env.info("napalmOnImpact: Error during unit scan for point (X: " .. point.x .. ", Y: " .. point.y .. ", Z: " .. point.z .. "): " .. tostring(err))
|
|
1250
|
+
end
|
|
1251
|
+
end
|
|
1252
|
+
--Add flame effect if enabled
|
|
1253
|
+
if splash_damage_options.napalm_addflame then
|
|
1254
|
+
local flameSize = splash_damage_options.napalm_addflame_size
|
|
1255
|
+
local flameDuration = splash_damage_options.napalm_addflame_duration
|
|
1256
|
+
local flameDensity = 1.0
|
|
1257
|
+
local effectId = effectSmokeId
|
|
1258
|
+
effectSmokeId = effectSmokeId + 1
|
|
1259
|
+
local isDuplicate = false
|
|
1260
|
+
for _, pos in pairs(flamePositions) do
|
|
1261
|
+
if getDistance3D(point, pos) < 3 then
|
|
1262
|
+
isDuplicate = true
|
|
1263
|
+
if splash_damage_options.debug then
|
|
1264
|
+
debugMsg("Skipping duplicate flame for napalm object '" .. napalmName .. "' near X: " .. string.format("%.0f", pos.x) .. ", Z: " .. string.format("%.0f", pos.z))
|
|
1265
|
+
end
|
|
1266
|
+
break
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1269
|
+
if not isDuplicate then
|
|
1270
|
+
if splash_damage_options.debug then
|
|
1271
|
+
debugMsg("Adding flame effect for napalm object '" .. napalmName .. "' at X: " .. string.format("%.0f", point.x) .. ", Z: " .. string.format("%.0f", point.z) .. " (Size: " .. flameSize .. ", Duration: " .. flameDuration .. "s, ID: " .. effectId .. ")")
|
|
1272
|
+
end
|
|
1273
|
+
timer.scheduleFunction(function(params)
|
|
1274
|
+
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
1275
|
+
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
1276
|
+
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
1277
|
+
end, {point, flameSize, flameDensity, effectId}, timer.getTime() + splash_damage_options.napalm_flame_delay)
|
|
1278
|
+
timer.scheduleFunction(function(id)
|
|
1279
|
+
if splash_damage_options.debug then
|
|
1280
|
+
debugMsg("Stopping flame effect for napalm object (ID: " .. id .. ")")
|
|
1281
|
+
end
|
|
1282
|
+
trigger.action.effectSmokeStop(id)
|
|
1283
|
+
end, effectId, timer.getTime() + splash_damage_options.napalm_flame_delay + flameDuration)
|
|
1284
|
+
table.insert(flamePositions, point)
|
|
1285
|
+
end
|
|
1286
|
+
end
|
|
1287
|
+
end
|
|
1288
|
+
timer.scheduleFunction(spawnAndExplode, pairIndex + 1, timer.getTime() + 0.2)
|
|
1289
|
+
end
|
|
1290
|
+
spawnAndExplode(1)
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
|
|
644
1298
|
--Cluster-specific helper functions from Rockeye script
|
|
645
1299
|
local function normalizeVector(vec)
|
|
646
1300
|
local mag = math.sqrt(vec.x^2 + vec.z^2)
|
|
@@ -668,55 +1322,141 @@ local function calculate_dispersion(velocity, burst_altitude)
|
|
|
668
1322
|
math.max(splash_damage_options.cluster_min_width, math.min(splash_damage_options.cluster_max_width, width_jitter))
|
|
669
1323
|
end
|
|
670
1324
|
|
|
1325
|
+
local function protectedCall(...)
|
|
1326
|
+
local status, retval = pcall(...)
|
|
1327
|
+
if not status then
|
|
1328
|
+
env.warning("Splash damage script error... gracefully caught! " .. retval, true)
|
|
1329
|
+
end
|
|
1330
|
+
end
|
|
671
1331
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
----[[ ##### End of HELPER/UTILITY FUNCTIONS ##### ]]----
|
|
1332
|
+
--[[
|
|
1333
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-
|
|
1334
|
+
##### End of HELPER/UTILITY FUNCTIONS ##### ##### End of HELPER/UTILITY FUNCTIONS ##### ##### End of HELPER/UTILITY FUNCTIONS #####
|
|
1335
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-]]
|
|
680
1336
|
giantExplosionTargets = {}
|
|
1337
|
+
giantExplosionTestTargets = {}
|
|
681
1338
|
cargoEffectsQueue = {}
|
|
682
1339
|
WpnHandler = {}
|
|
683
1340
|
tracked_target_position = nil --Store the last known position of TargetUnit for giant explosion
|
|
684
1341
|
tracked_weapons = {}
|
|
685
1342
|
local processedUnitsGlobal = {}
|
|
1343
|
+
napalmCounter = 1
|
|
1344
|
+
napalmCounter = 1
|
|
686
1345
|
|
|
687
1346
|
function scanGiantExplosionTargets()
|
|
688
|
-
|
|
689
|
-
local function findTargets(obj)
|
|
1347
|
+
local function processObject(obj)
|
|
690
1348
|
if obj:isExist() then
|
|
691
1349
|
local name = obj:getName()
|
|
692
1350
|
if string.find(name, "GiantExplosionTarget") then
|
|
693
|
-
local
|
|
694
|
-
|
|
1351
|
+
local pos = obj:getPoint()
|
|
1352
|
+
local targetData = {
|
|
695
1353
|
name = name,
|
|
696
|
-
flag = flagName,
|
|
697
1354
|
obj = obj,
|
|
698
|
-
pos =
|
|
699
|
-
static = splash_damage_options.giant_explosion_target_static
|
|
700
|
-
|
|
1355
|
+
pos = pos,
|
|
1356
|
+
static = splash_damage_options.giant_explosion_target_static,
|
|
1357
|
+
initialHealth = obj:getLife() or 0
|
|
1358
|
+
}
|
|
1359
|
+
table.insert(giantExplosionTargets, targetData)
|
|
1360
|
+
if splash_damage_options.giantexplosion_testmode then
|
|
1361
|
+
table.insert(giantExplosionTestTargets, {name = name, pos = pos})
|
|
1362
|
+
end
|
|
1363
|
+
debugMsg("Found GiantExplosion unit: " .. name .. " at X:" .. pos.x .. " Y:" .. pos.y .. " Z:" .. pos.z)
|
|
1364
|
+
end
|
|
1365
|
+
end
|
|
1366
|
+
end
|
|
1367
|
+
--Iterate over all coalitions
|
|
1368
|
+
for coa = 0, 2 do
|
|
1369
|
+
--Process units
|
|
1370
|
+
local groups = coalition.getGroups(coa)
|
|
1371
|
+
if groups then
|
|
1372
|
+
for _, group in pairs(groups) do
|
|
1373
|
+
local units = group:getUnits()
|
|
1374
|
+
if units then
|
|
1375
|
+
for _, unit in pairs(units) do
|
|
1376
|
+
processObject(unit)
|
|
1377
|
+
end
|
|
1378
|
+
end
|
|
1379
|
+
end
|
|
1380
|
+
end
|
|
1381
|
+
--Process static objects
|
|
1382
|
+
local statics = coalition.getStaticObjects(coa)
|
|
1383
|
+
if statics then
|
|
1384
|
+
for _, static in pairs(statics) do
|
|
1385
|
+
processObject(static)
|
|
701
1386
|
end
|
|
702
1387
|
end
|
|
703
|
-
return true
|
|
704
1388
|
end
|
|
705
|
-
|
|
706
|
-
if
|
|
707
|
-
timer.scheduleFunction(
|
|
1389
|
+
debugMsg("Total GiantExplosion units found: " .. #giantExplosionTargets)
|
|
1390
|
+
if #giantExplosionTargets > 0 then
|
|
1391
|
+
timer.scheduleFunction(checkGiantExplosionUnits, {}, timer.getTime() + splash_damage_options.giant_explosion_poll_rate)
|
|
708
1392
|
end
|
|
709
1393
|
end
|
|
710
1394
|
|
|
711
|
-
function
|
|
712
|
-
for
|
|
1395
|
+
function updateGiantExplosionPositions()
|
|
1396
|
+
for _, target in ipairs(giantExplosionTargets) do
|
|
713
1397
|
if target.obj:isExist() then
|
|
714
|
-
target.pos = target.obj:
|
|
1398
|
+
target.pos = target.obj:getPoint()
|
|
715
1399
|
end
|
|
716
1400
|
end
|
|
717
1401
|
return timer.getTime() + 1.0
|
|
718
1402
|
end
|
|
719
1403
|
|
|
1404
|
+
function checkGiantExplosionUnits()
|
|
1405
|
+
if not splash_damage_options.giant_explosion_enabled then
|
|
1406
|
+
debugMsg("Giant Explosion is disabled in options.")
|
|
1407
|
+
return
|
|
1408
|
+
end
|
|
1409
|
+
|
|
1410
|
+
local targetsToRemove = {}
|
|
1411
|
+
for i, target in ipairs(giantExplosionTargets) do
|
|
1412
|
+
local triggerExplosion = false
|
|
1413
|
+
local currentPos = target.pos
|
|
1414
|
+
|
|
1415
|
+
if target.obj:isExist() then
|
|
1416
|
+
if not target.static then
|
|
1417
|
+
currentPos = target.obj:getPoint()
|
|
1418
|
+
target.pos = currentPos
|
|
1419
|
+
end
|
|
1420
|
+
if splash_damage_options.giantexplosion_ondamage then
|
|
1421
|
+
local currentHealth = target.obj:getLife() or 0
|
|
1422
|
+
if currentHealth < target.initialHealth then
|
|
1423
|
+
triggerExplosion = true
|
|
1424
|
+
debugMsg("Triggering explosion for " .. target.name .. " due to damage (Health: " .. currentHealth .. "/" .. target.initialHealth .. ")")
|
|
1425
|
+
end
|
|
1426
|
+
end
|
|
1427
|
+
else
|
|
1428
|
+
if splash_damage_options.giantexplosion_ondeath then
|
|
1429
|
+
triggerExplosion = true
|
|
1430
|
+
debugMsg("Triggering explosion for " .. target.name .. " due to destruction")
|
|
1431
|
+
end
|
|
1432
|
+
end
|
|
1433
|
+
|
|
1434
|
+
if triggerExplosion then
|
|
1435
|
+
triggerGiantExplosion({
|
|
1436
|
+
pos = currentPos,
|
|
1437
|
+
power = splash_damage_options.giant_explosion_power,
|
|
1438
|
+
scale = splash_damage_options.giant_explosion_scale,
|
|
1439
|
+
duration = splash_damage_options.giant_explosion_duration,
|
|
1440
|
+
count = splash_damage_options.giant_explosion_count
|
|
1441
|
+
})
|
|
1442
|
+
table.insert(targetsToRemove, i)
|
|
1443
|
+
end
|
|
1444
|
+
end
|
|
1445
|
+
|
|
1446
|
+
--Remove triggered targets in reverse order to avoid index issues
|
|
1447
|
+
for i = #targetsToRemove, 1, -1 do
|
|
1448
|
+
table.remove(giantExplosionTargets, targetsToRemove[i])
|
|
1449
|
+
debugMsg("Removed " .. targetsToRemove[i] .. " from giantExplosionTargets. Remaining: " .. #giantExplosionTargets)
|
|
1450
|
+
end
|
|
1451
|
+
|
|
1452
|
+
--Continue scheduling checks if there are still targets
|
|
1453
|
+
if #giantExplosionTargets > 0 then
|
|
1454
|
+
return timer.getTime() + splash_damage_options.giant_explosion_poll_rate
|
|
1455
|
+
else
|
|
1456
|
+
debugMsg("No GiantExplosion units remaining. Disabling periodic checks.")
|
|
1457
|
+
end
|
|
1458
|
+
end
|
|
1459
|
+
|
|
720
1460
|
|
|
721
1461
|
--Giant Explosion Function
|
|
722
1462
|
function triggerGiantExplosion(params)
|
|
@@ -751,8 +1491,8 @@ function triggerGiantExplosion(params)
|
|
|
751
1491
|
end, pos, timer.getTime() + delay)
|
|
752
1492
|
end
|
|
753
1493
|
|
|
754
|
-
--
|
|
755
|
-
local scanRadius = 1500 * sizeScale --
|
|
1494
|
+
--Pre-explosion scan for cargo units
|
|
1495
|
+
local scanRadius = 1500 * sizeScale --1500m base radius, scaled by sizeScale
|
|
756
1496
|
local preExplosionTargets = {}
|
|
757
1497
|
if splash_damage_options.enable_cargo_effects then
|
|
758
1498
|
local volS = {
|
|
@@ -778,7 +1518,7 @@ function triggerGiantExplosion(params)
|
|
|
778
1518
|
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
779
1519
|
debugMsg("Pre-explosion scan for Giant Explosion: " .. #preExplosionTargets .. " targets found within " .. scanRadius .. "m")
|
|
780
1520
|
end
|
|
781
|
-
--
|
|
1521
|
+
--Trigger the explosion
|
|
782
1522
|
local maxRadius = 200 * sizeScale
|
|
783
1523
|
local maxHeight = 500 * sizeScale
|
|
784
1524
|
local adjustedExplosionCount = math.floor(explosionCount * (sizeScale ^ 2.5))
|
|
@@ -815,7 +1555,7 @@ function triggerGiantExplosion(params)
|
|
|
815
1555
|
|
|
816
1556
|
gameMsg("Expanding giant fireball over " .. totalDuration .. "s (scale " .. sizeScale .. ")!")
|
|
817
1557
|
|
|
818
|
-
--
|
|
1558
|
+
--Post-explosion scan and cargo cook-off queuing
|
|
819
1559
|
if splash_damage_options.enable_cargo_effects then
|
|
820
1560
|
timer.scheduleFunction(function(args)
|
|
821
1561
|
local centerPos = args[1]
|
|
@@ -845,7 +1585,7 @@ function triggerGiantExplosion(params)
|
|
|
845
1585
|
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
846
1586
|
debugMsg("Post-explosion scan for Giant Explosion: " .. #postExplosionTargets .. " targets found within " .. radius .. "m")
|
|
847
1587
|
|
|
848
|
-
--
|
|
1588
|
+
--Compare pre- and post-explosion targets
|
|
849
1589
|
for _, preTarget in ipairs(preTargets) do
|
|
850
1590
|
local found = false
|
|
851
1591
|
local postHealth = 0
|
|
@@ -883,16 +1623,16 @@ function triggerGiantExplosion(params)
|
|
|
883
1623
|
end
|
|
884
1624
|
end
|
|
885
1625
|
|
|
886
|
-
--
|
|
1626
|
+
--Process queued cargo effects with prioritized flames
|
|
887
1627
|
if #cargoEffectsQueue > 0 then
|
|
888
|
-
local flameIndex = 0 --
|
|
889
|
-
local otherIndex = 0 --
|
|
1628
|
+
local flameIndex = 0 --Separate index for flames
|
|
1629
|
+
local otherIndex = 0 --Index for explosions, cook-offs, debris
|
|
890
1630
|
local processedCargoUnits = {}
|
|
891
1631
|
local flamePositions = {}
|
|
892
1632
|
for _, effect in ipairs(cargoEffectsQueue) do
|
|
893
1633
|
local unitKey = effect.name .. "_" .. effect.coords.x .. "_" .. effect.coords.z
|
|
894
1634
|
if not processedUnitsGlobal[unitKey] and not processedCargoUnits[unitKey] then
|
|
895
|
-
--
|
|
1635
|
+
--Handle tanker flames first with minimal delay
|
|
896
1636
|
if effect.isTanker and effect.explosion then
|
|
897
1637
|
debugMsg("Triggering cargo explosion for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. flameIndex .. "s")
|
|
898
1638
|
timer.scheduleFunction(function(params)
|
|
@@ -900,37 +1640,37 @@ function triggerGiantExplosion(params)
|
|
|
900
1640
|
trigger.action.explosion(params[1], params[2])
|
|
901
1641
|
end, {effect.coords, effect.power}, timer.getTime() + flameIndex + 0.1)
|
|
902
1642
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
end
|
|
1643
|
+
local flameSize = effect.flameSize or 3
|
|
1644
|
+
local flameDuration = effect.flameDuration
|
|
1645
|
+
local flameDensity = 1.0
|
|
1646
|
+
local effectId = effectSmokeId
|
|
1647
|
+
effectSmokeId = effectSmokeId + 1
|
|
1648
|
+
local isDuplicate = false
|
|
1649
|
+
for _, pos in pairs(flamePositions) do
|
|
1650
|
+
if getDistance3D(effect.coords, pos) < 3 then
|
|
1651
|
+
isDuplicate = true
|
|
1652
|
+
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))
|
|
1653
|
+
break
|
|
915
1654
|
end
|
|
916
|
-
|
|
1655
|
+
end
|
|
1656
|
+
if not isDuplicate then
|
|
917
1657
|
debugMsg("Adding flame effect for tanker " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m (Size: " .. flameSize .. ", Duration: " .. flameDuration .. "s, ID: " .. effectId .. ") scheduled at " .. flameIndex .. "s")
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1658
|
+
timer.scheduleFunction(function(params)
|
|
1659
|
+
local terrainHeight = land.getHeight({x = params[1].x, y = params[1].z})
|
|
1660
|
+
local adjustedCoords = {x = params[1].x, y = terrainHeight + 2, z = params[1].z}
|
|
1661
|
+
debugMsg("Spawning flame effect at X: " .. string.format("%.0f", adjustedCoords.x) .. ", Y: " .. string.format("%.0f", adjustedCoords.y) .. ", Z: " .. string.format("%.0f", adjustedCoords.z))
|
|
1662
|
+
trigger.action.explosion(adjustedCoords, 10) --Small trigger explosion
|
|
1663
|
+
trigger.action.effectSmokeBig(adjustedCoords, params[2], params[3], params[4])
|
|
924
1664
|
end, {effect.coords, flameSize, flameDensity, effectId}, timer.getTime() + flameIndex + 0.2)
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1665
|
+
timer.scheduleFunction(function(id)
|
|
1666
|
+
debugMsg("Stopping flame effect for " .. effect.name .. " (ID: " .. id .. ")")
|
|
1667
|
+
trigger.action.effectSmokeStop(id)
|
|
928
1668
|
end, effectId, timer.getTime() + flameIndex + flameDuration + 0.2)
|
|
929
|
-
|
|
930
|
-
end
|
|
931
|
-
flameIndex = flameIndex + 0.5 -- Fast spacing for flames (0.5s)
|
|
1669
|
+
table.insert(flamePositions, effect.coords)
|
|
932
1670
|
end
|
|
933
|
-
|
|
1671
|
+
flameIndex = flameIndex + 0.5 --Fast spacing for flames (0.5s)
|
|
1672
|
+
end
|
|
1673
|
+
--Handle non-tanker explosions, cook-offs, and debris
|
|
934
1674
|
if not effect.isTanker or (effect.explosion and not effect.isTanker) then
|
|
935
1675
|
if effect.explosion then
|
|
936
1676
|
debugMsg("Triggering cargo explosion for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m with power " .. effect.power .. " scheduled at " .. otherIndex .. "s")
|
|
@@ -938,76 +1678,56 @@ function triggerGiantExplosion(params)
|
|
|
938
1678
|
debugMsg("Executing cargo explosion at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
939
1679
|
trigger.action.explosion(params[1], params[2])
|
|
940
1680
|
end, {effect.coords, effect.power}, timer.getTime() + otherIndex + 0.1)
|
|
941
|
-
|
|
942
|
-
|
|
1681
|
+
end
|
|
1682
|
+
if effect.cookOff and effect.cookOffCount > 0 then
|
|
943
1683
|
debugMsg("Scheduling " .. effect.cookOffCount .. " cook-off explosions for " .. effect.name .. " at " .. string.format("%.1f", effect.distance) .. "m over " .. effect.cookOffDuration .. "s starting at " .. otherIndex .. "s")
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1684
|
+
for i = 1, effect.cookOffCount do
|
|
1685
|
+
local delay = effect.cookOffRandomTiming and math.random() * effect.cookOffDuration or (i - 1) * (effect.cookOffDuration / effect.cookOffCount)
|
|
1686
|
+
local basePower = effect.cookOffPower
|
|
1687
|
+
local powerVariation = effect.cookOffPowerRandom / 100
|
|
1688
|
+
local cookOffPower = effect.cookOffPowerRandom == 0 and basePower or basePower * (1 + powerVariation * (math.random() * 2 - 1))
|
|
1689
|
+
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))
|
|
1690
|
+
timer.scheduleFunction(function(params)
|
|
1691
|
+
debugMsg("Executing cook-off at X: " .. string.format("%.0f", params[1].x) .. ", Y: " .. string.format("%.0f", params[1].y) .. ", Z: " .. string.format("%.0f", params[1].z) .. " with power " .. params[2])
|
|
1692
|
+
trigger.action.explosion(params[1], params[2])
|
|
953
1693
|
end, {effect.coords, cookOffPower}, timer.getTime() + otherIndex + delay)
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1694
|
+
end
|
|
1695
|
+
if splash_damage_options.debris_effects then
|
|
1696
|
+
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
1697
|
+
for j = 1, debrisCount do
|
|
1698
|
+
local theta = math.random() * 2 * math.pi
|
|
1699
|
+
local phi = math.acos(math.random() * 2 - 1)
|
|
1700
|
+
local minDist = splash_damage_options.debris_max_distance * 0.1
|
|
1701
|
+
local maxDist = splash_damage_options.debris_max_distance
|
|
1702
|
+
local r = math.random() * (maxDist - minDist) + minDist
|
|
1703
|
+
local debrisX = effect.coords.x + r * math.sin(phi) * math.cos(theta)
|
|
1704
|
+
local debrisZ = effect.coords.z + r * math.sin(phi) * math.sin(theta)
|
|
1705
|
+
local terrainY = land.getHeight({x = debrisX, y = debrisZ})
|
|
1706
|
+
local debrisY = terrainY + math.random() * maxDist
|
|
1707
|
+
local debrisPos = {x = debrisX, y = debrisY, z = debrisZ}
|
|
1708
|
+
local debrisPower = splash_damage_options.debris_power
|
|
1709
|
+
local debrisDelay = (j - 1) * (effect.cookOffDuration / debrisCount)
|
|
1710
|
+
timer.scheduleFunction(function(debrisArgs)
|
|
1711
|
+
debugMsg("Debris explosion at X: " .. string.format("%.0f", debrisArgs[1].x) .. ", Y: " .. string.format("%.0f", debrisArgs[1].y) .. ", Z: " .. string.format("%.0f", debrisArgs[1].z) .. " with power " .. debrisArgs[2])
|
|
1712
|
+
trigger.action.explosion(debrisArgs[1], debrisArgs[2])
|
|
973
1713
|
end, {debrisPos, debrisPower}, timer.getTime() + otherIndex + debrisDelay)
|
|
974
1714
|
end
|
|
975
1715
|
end
|
|
976
1716
|
end
|
|
977
|
-
otherIndex = otherIndex + 1 --
|
|
1717
|
+
otherIndex = otherIndex + 1 --Slower spacing for non-flame effects (1s)
|
|
978
1718
|
end
|
|
979
1719
|
processedCargoUnits[unitKey] = true
|
|
980
1720
|
processedUnitsGlobal[unitKey] = true
|
|
981
1721
|
end
|
|
982
1722
|
end
|
|
983
|
-
cargoEffectsQueue = {} --
|
|
1723
|
+
cargoEffectsQueue = {} --Clear the queue after processing
|
|
984
1724
|
end
|
|
985
1725
|
end, {initialPos, scanRadius, preExplosionTargets}, timer.getTime() + totalDuration + 1.0)
|
|
986
1726
|
end
|
|
987
1727
|
end
|
|
988
1728
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
for name, target in pairs(giantExplosionTargets) do
|
|
992
|
-
local flagName = name:gsub("GiantExplosionTarget", "GiantExplosionTarget")
|
|
993
|
-
local flagValue = trigger.misc.getUserFlag(flagName)
|
|
994
|
-
--commenting out as it spams every second
|
|
995
|
-
debugMsg("Checking flag " .. flagName .. ": " .. flagValue)
|
|
996
|
-
if flagValue == 1 then
|
|
997
|
-
debugMsg("Triggering explosion for " .. name .. " at X:" .. target.pos.x .. " Y:" .. target.pos.y .. " Z:" .. target.pos.z)
|
|
998
|
-
triggerGiantExplosion({
|
|
999
|
-
pos = target.pos,
|
|
1000
|
-
power = splash_damage_options.giant_explosion_power,
|
|
1001
|
-
scale = splash_damage_options.giant_explosion_scale,
|
|
1002
|
-
duration = splash_damage_options.giant_explosion_duration,
|
|
1003
|
-
count = splash_damage_options.giant_explosion_count
|
|
1004
|
-
})
|
|
1005
|
-
trigger.action.setUserFlag(flagName, 2)
|
|
1006
|
-
end
|
|
1007
|
-
end
|
|
1008
|
-
return timer.getTime() + splash_damage_options.giant_explosion_poll_rate
|
|
1009
|
-
end
|
|
1010
|
-
|
|
1729
|
+
|
|
1730
|
+
|
|
1011
1731
|
function getWeaponExplosive(name)
|
|
1012
1732
|
local weaponData = explTable[name]
|
|
1013
1733
|
if weaponData then
|
|
@@ -1088,6 +1808,30 @@ end
|
|
|
1088
1808
|
|
|
1089
1809
|
----[[ ##### Updated track_wpns() Function ##### ]]----
|
|
1090
1810
|
local recentExplosions = {}
|
|
1811
|
+
|
|
1812
|
+
|
|
1813
|
+
--function to schedule flares for cook-offs
|
|
1814
|
+
function scheduleCookOffFlares(coords, cookOffCount, cookOffDuration, flareColor)
|
|
1815
|
+
local flareCount = math.floor(cookOffCount * splash_damage_options.cookoff_flare_count_modifier)
|
|
1816
|
+
if flareCount < 1 then return end --Skip if no flares
|
|
1817
|
+
debugMsg("Scheduling " .. flareCount .. " flares for cook-off at X: " .. string.format("%.0f", coords.x) .. ", Z: " .. string.format("%.0f", coords.z) .. " over " .. cookOffDuration .. "s")
|
|
1818
|
+
for i = 1, flareCount do
|
|
1819
|
+
local delay = math.random() * cookOffDuration --Random time within cook-off duration
|
|
1820
|
+
local terrainHeight = land.getHeight({x = coords.x, y = coords.z})
|
|
1821
|
+
local offset = {
|
|
1822
|
+
x = coords.x + math.random(-splash_damage_options.cookoff_flare_offset, splash_damage_options.cookoff_flare_offset),
|
|
1823
|
+
y = terrainHeight, --Start at ground level
|
|
1824
|
+
z = coords.z + math.random(-splash_damage_options.cookoff_flare_offset, splash_damage_options.cookoff_flare_offset)
|
|
1825
|
+
}
|
|
1826
|
+
local azimuth = math.random(1, 360) --Random direction
|
|
1827
|
+
timer.scheduleFunction(function(params)
|
|
1828
|
+
debugMsg("Spawning flare #" .. params[1] .. " at X: " .. string.format("%.0f", params[2].x) .. ", Y: " .. string.format("%.0f", params[2].y) .. ", Z: " .. string.format("%.0f", params[2].z) .. " with color " .. params[3])
|
|
1829
|
+
trigger.action.signalFlare(params[2], params[3], params[4])
|
|
1830
|
+
end, {i, offset, flareColor, azimuth}, timer.getTime() + delay)
|
|
1831
|
+
end
|
|
1832
|
+
end
|
|
1833
|
+
|
|
1834
|
+
|
|
1091
1835
|
function track_wpns()
|
|
1092
1836
|
local weaponsToRemove = {} --Delay removal to ensure all weapons are checked
|
|
1093
1837
|
for wpn_id_, wpnData in pairs(tracked_weapons) do
|
|
@@ -1138,6 +1882,14 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1138
1882
|
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
1139
1883
|
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
1140
1884
|
end
|
|
1885
|
+
if wpnData.isGroundUnitOrdnance and splash_damage_options.track_groundunitordnance then
|
|
1886
|
+
base_explosive = base_explosive * splash_damage_options.groundunitordnance_damage_modifier
|
|
1887
|
+
--Log modifier only once per weapon
|
|
1888
|
+
--if splash_damage_options.track_groundunitordnance_debug and not wpnData.debugLogged then
|
|
1889
|
+
--debugMsg("Applying ground unit ordnance damage modifier " .. splash_damage_options.groundunitordnance_damage_modifier .. " to " .. wpnData.name .. ", base explosive power: " .. base_explosive)
|
|
1890
|
+
--wpnData.debugLogged = true --Mark as logged
|
|
1891
|
+
--end
|
|
1892
|
+
end
|
|
1141
1893
|
|
|
1142
1894
|
local explosionPower = base_explosive
|
|
1143
1895
|
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
@@ -1149,12 +1901,14 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1149
1901
|
blastRadius = math.pow(explosionPower, 1/3) * 10 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1150
1902
|
end
|
|
1151
1903
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1904
|
+
--Set tightRadius, use 50m for ground ordnance if enabled
|
|
1905
|
+
local tightRadius = blastRadius
|
|
1906
|
+
if wpnData.isGroundUnitOrdnance and splash_damage_options.scan_50m_for_groundordnance then
|
|
1907
|
+
tightRadius = 50 --Fixed 50m radius for ground ordnance
|
|
1908
|
+
if splash_damage_options.track_groundunitordnance_debug then
|
|
1909
|
+
debugMsg("Using 50m scan radius for ground ordnance " .. wpnData.name)
|
|
1910
|
+
end
|
|
1911
|
+
end
|
|
1158
1912
|
local volS = {
|
|
1159
1913
|
id = world.VolumeType.SPHERE,
|
|
1160
1914
|
params = {
|
|
@@ -1174,7 +1928,9 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1174
1928
|
health = foundObject:getLife() or 0,
|
|
1175
1929
|
position = foundObject:getPoint(),
|
|
1176
1930
|
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
1177
|
-
unit = foundObject
|
|
1931
|
+
unit = foundObject,
|
|
1932
|
+
id = foundObject:getID(),
|
|
1933
|
+
unitName = foundObject:getName() or "Unknown"
|
|
1178
1934
|
})
|
|
1179
1935
|
end
|
|
1180
1936
|
end
|
|
@@ -1234,15 +1990,54 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1234
1990
|
else
|
|
1235
1991
|
--Weapon has impacted
|
|
1236
1992
|
debugMsg("Weapon " .. wpnData.name .. " no longer exists at " .. timer.getTime() .. "s")
|
|
1237
|
-
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed)) --terrain intersection point with weapon's nose
|
|
1993
|
+
local ip = land.getIP(wpnData.pos, wpnData.dir, lookahead(wpnData.speed)) --terrain intersection point with weapon's nose
|
|
1238
1994
|
local explosionPoint
|
|
1239
1995
|
if not ip then --use last calculated IP
|
|
1240
1996
|
explosionPoint = wpnData.pos
|
|
1241
1997
|
else --use intersection point
|
|
1242
1998
|
explosionPoint = ip
|
|
1999
|
+
end
|
|
2000
|
+
if wpnData.isGroundUnitOrdnance and splash_damage_options.track_groundunitordnance_debug then
|
|
2001
|
+
local base_explosive, isShapedCharge = getWeaponExplosive(wpnData.name)
|
|
2002
|
+
base_explosive = base_explosive * splash_damage_options.overall_scaling
|
|
2003
|
+
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
2004
|
+
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
2005
|
+
end
|
|
2006
|
+
if wpnData.isGroundUnitOrdnance and splash_damage_options.track_groundunitordnance then
|
|
2007
|
+
base_explosive = base_explosive * splash_damage_options.groundunitordnance_damage_modifier
|
|
2008
|
+
end
|
|
2009
|
+
local explosionPower = base_explosive
|
|
2010
|
+
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
2011
|
+
explosionPower = explosionPower * splash_damage_options.shaped_charge_multiplier
|
|
2012
|
+
end
|
|
2013
|
+
debugMsg("Ground unit ordnance " .. wpnData.name .. " impacted at X: " .. string.format("%.0f", explosionPoint.x) .. ", Y: " .. string.format("%.0f", explosionPoint.y) .. ", Z: " .. string.format("%.0f", explosionPoint.z) .. " with power " .. explosionPower)
|
|
1243
2014
|
end
|
|
1244
2015
|
local chosenTargets = wpnData.tightTargets or {}
|
|
1245
2016
|
local safeToBlast = true
|
|
2017
|
+
--Check if weapon is napalm
|
|
2018
|
+
local isNapalm = false
|
|
2019
|
+
--Check for napalm override weapons
|
|
2020
|
+
if splash_damage_options.napalmoverride_enabled then
|
|
2021
|
+
local napalmWeapons = {}
|
|
2022
|
+
for weapon in splash_damage_options.napalm_override_weapons:gmatch("[^,]+") do
|
|
2023
|
+
napalmWeapons[trim(weapon)] = true
|
|
2024
|
+
end
|
|
2025
|
+
if napalmWeapons[wpnData.name] then
|
|
2026
|
+
isNapalm = true
|
|
2027
|
+
debugMsg("Napalm override triggered for " .. wpnData.name .. " at X: " .. string.format("%.0f", explosionPoint.x) .. ", Z: " .. string.format("%.0f", explosionPoint.z))
|
|
2028
|
+
napalmOnImpact(explosionPoint, wpnData.speed, wpnData.name)
|
|
2029
|
+
table.insert(weaponsToRemove, wpn_id_)
|
|
2030
|
+
end
|
|
2031
|
+
end
|
|
2032
|
+
|
|
2033
|
+
--Check for MK77 weapons independently
|
|
2034
|
+
if splash_damage_options.napalm_mk77_enabled and (wpnData.name == "MK77mod0-WPN" or wpnData.name == "MK77mod1-WPN") then
|
|
2035
|
+
isNapalm = true
|
|
2036
|
+
debugMsg("MK77 napalm triggered for " .. wpnData.name .. " at X: " .. string.format("%.0f", explosionPoint.x) .. ", Z: " .. string.format("%.0f", explosionPoint.z))
|
|
2037
|
+
napalmOnImpact(explosionPoint, wpnData.speed, wpnData.name)
|
|
2038
|
+
table.insert(weaponsToRemove, wpn_id_)
|
|
2039
|
+
end
|
|
2040
|
+
if not isNapalm then
|
|
1246
2041
|
if splash_damage_options.ordnance_protection then
|
|
1247
2042
|
local checkVol = { id = world.VolumeType.SPHERE, params = { point = explosionPoint, radius = splash_damage_options.ordnance_protection_radius } }
|
|
1248
2043
|
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")
|
|
@@ -1262,6 +2057,12 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1262
2057
|
if splash_damage_options.rocket_multiplier and wpnData.cat == Weapon.Category.ROCKET then
|
|
1263
2058
|
base_explosive = base_explosive * splash_damage_options.rocket_multiplier
|
|
1264
2059
|
end
|
|
2060
|
+
if wpnData.isGroundUnitOrdnance and splash_damage_options.track_groundunitordnance then
|
|
2061
|
+
base_explosive = base_explosive * splash_damage_options.groundunitordnance_damage_modifier
|
|
2062
|
+
if splash_damage_options.track_groundunitordnance_debug then
|
|
2063
|
+
debugMsg("Applying ground unit ordnance damage modifier " .. splash_damage_options.groundunitordnance_damage_modifier .. " to " .. wpnData.name .. ", base explosive power: " .. base_explosive)
|
|
2064
|
+
end
|
|
2065
|
+
end
|
|
1265
2066
|
|
|
1266
2067
|
local explosionPower = base_explosive
|
|
1267
2068
|
if splash_damage_options.apply_shaped_charge_effects and isShapedCharge then
|
|
@@ -1297,7 +2098,7 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1297
2098
|
local submunitionName = weaponData.submunition_name or "unknown"
|
|
1298
2099
|
--Apply bomblet reduction logic if enabled
|
|
1299
2100
|
if splash_damage_options.cluster_bomblet_reductionmodifier then
|
|
1300
|
-
if submunitionCount > 35 then
|
|
2101
|
+
if submunitionCount > 35 then
|
|
1301
2102
|
local reductionFactor = (60 - 35) / (247 - 35)
|
|
1302
2103
|
submunitionCount = 35 + math.floor((submunitionCount - 35) * reductionFactor)
|
|
1303
2104
|
if submunitionCount > 60 then submunitionCount = 60 end --Cap at 60
|
|
@@ -1312,8 +2113,16 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1312
2113
|
trigger.action.explosion(explosionPoint, explosionPower)
|
|
1313
2114
|
table.insert(recentExplosions, { pos = explosionPoint, time = timer.getTime(), radius = blastRadius })
|
|
1314
2115
|
debugMsg("Added to recentExplosions for '" .. wpnData.name .. "': X: " .. explosionPoint.x .. ", Y: " .. explosionPoint.y .. ", Z: " .. explosionPoint.z .. ", Time: " .. timer.getTime())
|
|
1315
|
-
|
|
1316
|
-
|
|
2116
|
+
--Check for units destroyed by initial explosion
|
|
2117
|
+
local playerName = wpnData.init or "unknown"
|
|
2118
|
+
for _, target in ipairs(chosenTargets) do
|
|
2119
|
+
if target.unit:isExist() and target.health > 0 and target.unit:getLife() <= 0 then
|
|
2120
|
+
debugMsg("Unit " .. target.name .. " destroyed by initial explosion, credited to player: " .. playerName)
|
|
2121
|
+
end
|
|
2122
|
+
end
|
|
2123
|
+
end
|
|
2124
|
+
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.name, explosionPower, isShapedCharge)
|
|
2125
|
+
|
|
1317
2126
|
end
|
|
1318
2127
|
--detect_ordnance_destruction comes before recent_large_explosion_snap in original
|
|
1319
2128
|
if splash_damage_options.ordnance_protection and splash_damage_options.detect_ordnance_destruction and splash_damage_options.larger_explosions then
|
|
@@ -1429,6 +2238,8 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1429
2238
|
end
|
|
1430
2239
|
local status, err = pcall(function()
|
|
1431
2240
|
--Log pre-explosion targets
|
|
2241
|
+
--Sort pre-explosion targets by distance
|
|
2242
|
+
table.sort(chosenTargets, function(a, b) return a.distance < b.distance end)
|
|
1432
2243
|
if splash_damage_options.track_pre_explosion then
|
|
1433
2244
|
if #chosenTargets > 0 then
|
|
1434
2245
|
local msg = "Targets in blast zone for " .. weaponName .. " BEFORE explosion (last frame, using finalPos):\n"
|
|
@@ -1443,7 +2254,7 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1443
2254
|
end
|
|
1444
2255
|
end
|
|
1445
2256
|
|
|
1446
|
-
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.
|
|
2257
|
+
blastWave(explosionPoint, splash_damage_options.blast_search_radius, wpnData.name, explosionPower, isShapedCharge)
|
|
1447
2258
|
|
|
1448
2259
|
--Post-explosion analysis and queue cargo effects
|
|
1449
2260
|
if splash_damage_options.track_pre_explosion then
|
|
@@ -1453,6 +2264,7 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1453
2264
|
local preExplosionTargets = innerArgs[3] or {}
|
|
1454
2265
|
local weaponName = innerArgs[4]
|
|
1455
2266
|
local weaponPower = innerArgs[5]
|
|
2267
|
+
local playerName = innerArgs[6]
|
|
1456
2268
|
if splash_damage_options.debug == true then
|
|
1457
2269
|
debugMsg("Starting post-explosion analysis for " .. weaponName .. " at " .. timer.getTime() .. "s")
|
|
1458
2270
|
end
|
|
@@ -1472,11 +2284,15 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1472
2284
|
local category = foundObject:getCategory()
|
|
1473
2285
|
if (category == Object.Category.UNIT and (foundObject:getDesc().category == Unit.Category.GROUND_UNIT or foundObject:getDesc().category == Unit.Category.AIRPLANE)) or
|
|
1474
2286
|
category == Object.Category.STATIC then
|
|
2287
|
+
local distance = getDistance(impactPoint, foundObject:getPoint())
|
|
1475
2288
|
table.insert(postExplosionTargets, {
|
|
1476
2289
|
name = foundObject:getTypeName(),
|
|
1477
2290
|
health = foundObject:getLife() or 0,
|
|
1478
2291
|
position = foundObject:getPoint(),
|
|
1479
|
-
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0
|
|
2292
|
+
maxHealth = (category == Object.Category.UNIT and foundObject:getDesc().life) or foundObject:getLife() or 0,
|
|
2293
|
+
distance = distance,
|
|
2294
|
+
id = foundObject:getID(),
|
|
2295
|
+
unitName = foundObject:getName() or "Unknown"
|
|
1480
2296
|
})
|
|
1481
2297
|
end
|
|
1482
2298
|
end
|
|
@@ -1484,7 +2300,8 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1484
2300
|
end
|
|
1485
2301
|
|
|
1486
2302
|
world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, volS, ifFound)
|
|
1487
|
-
|
|
2303
|
+
--Sort post-explosion targets by distance
|
|
2304
|
+
table.sort(postExplosionTargets, function(a, b) return a.distance < b.distance end)
|
|
1488
2305
|
local msg = "Post-explosion analysis for " .. weaponName .. ":\n"
|
|
1489
2306
|
|
|
1490
2307
|
--Match pre-detected units
|
|
@@ -1492,11 +2309,13 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1492
2309
|
local found = false
|
|
1493
2310
|
local postHealth = 0
|
|
1494
2311
|
local postPosition = nil
|
|
2312
|
+
local postDistance = 0
|
|
1495
2313
|
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1496
2314
|
if preTarget.name == postTarget.name and getDistance(preTarget.position, postTarget.position) < 1 then
|
|
1497
2315
|
found = true
|
|
1498
2316
|
postHealth = postTarget.health
|
|
1499
2317
|
postPosition = postTarget.position
|
|
2318
|
+
postDistance = postTarget.distance
|
|
1500
2319
|
break
|
|
1501
2320
|
end
|
|
1502
2321
|
end
|
|
@@ -1506,6 +2325,31 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1506
2325
|
|
|
1507
2326
|
if not found or postHealth <= 0 then
|
|
1508
2327
|
status = "WAS FULLY DESTROYED"
|
|
2328
|
+
--killfeed make sure its not an unknown weapon or an ai
|
|
2329
|
+
--if splash_damage_options.killfeed_enable and explTable[weaponName] then
|
|
2330
|
+
if splash_damage_options.killfeed_enable and explTable[weaponName] and playerName ~= "unknown" then
|
|
2331
|
+
local status, isPlayer = pcall(function()
|
|
2332
|
+
local playerList = net.get_player_list() or {}
|
|
2333
|
+
for _, pid in ipairs(playerList) do
|
|
2334
|
+
local pinfo = net.get_player_info(pid)
|
|
2335
|
+
if pinfo and pinfo.name == playerName then
|
|
2336
|
+
return true
|
|
2337
|
+
end
|
|
2338
|
+
end
|
|
2339
|
+
return false
|
|
2340
|
+
end)
|
|
2341
|
+
if status and isPlayer then
|
|
2342
|
+
table.insert(splashKillfeedTemp, {
|
|
2343
|
+
playerName = playerName,
|
|
2344
|
+
weaponName = weaponName,
|
|
2345
|
+
unitName = preTarget.unitName,
|
|
2346
|
+
unitType = preTarget.name,
|
|
2347
|
+
unitId = preTarget.id,
|
|
2348
|
+
time = timer.getTime(),
|
|
2349
|
+
position = coords
|
|
2350
|
+
})
|
|
2351
|
+
end
|
|
2352
|
+
end
|
|
1509
2353
|
elseif healthPercent < splash_damage_options.cargo_damage_threshold then
|
|
1510
2354
|
status = "WAS DAMAGED BELOW THRESHOLD"
|
|
1511
2355
|
else
|
|
@@ -1514,14 +2358,14 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1514
2358
|
|
|
1515
2359
|
--Always include coords in status message
|
|
1516
2360
|
local coords = found and postPosition or preTarget.position
|
|
1517
|
-
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: " .. preTarget.health .. ", Post: " .. postHealth .. ")"
|
|
2361
|
+
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Dist: " .. string.format("%.1f", postDistance) .. "m, Pre: " .. preTarget.health .. ", Post: " .. postHealth .. ")"
|
|
1518
2362
|
--Check if target is in cargoUnits and within blast radius
|
|
1519
2363
|
local cargoData = cargoUnits[preTarget.name]
|
|
1520
2364
|
if cargoData and preTarget.distance <= blastRadius and
|
|
1521
2365
|
(not found or postHealth <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1522
2366
|
|
|
1523
2367
|
if splash_damage_options.enable_cargo_effects then
|
|
1524
|
-
|
|
2368
|
+
local cargoPower = cargoData.cargoExplosionPower or weaponPower
|
|
1525
2369
|
table.insert(cargoEffectsQueue, {
|
|
1526
2370
|
name = preTarget.name,
|
|
1527
2371
|
distance = preTarget.distance,
|
|
@@ -1543,10 +2387,33 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1543
2387
|
statusMsg = statusMsg .. " WITH COOK-OFF (" .. cargoData.cookOffCount .. " blasts over " .. cargoData.cookOffDuration .. "s)"
|
|
1544
2388
|
end
|
|
1545
2389
|
end
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
2390
|
+
elseif splash_damage_options.smokeandcookoffeffectallvehicles and preTarget.distance <= blastRadius and
|
|
2391
|
+
(not found or postHealth <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
2392
|
+
if splash_damage_options.enable_cargo_effects then
|
|
2393
|
+
table.insert(cargoEffectsQueue, {
|
|
2394
|
+
name = preTarget.name,
|
|
2395
|
+
distance = preTarget.distance,
|
|
2396
|
+
coords = coords,
|
|
2397
|
+
power = splash_damage_options.allunits_explode_power, --No explosion
|
|
2398
|
+
explosion = true,
|
|
2399
|
+
cookOff = splash_damage_options.allunits_enable_cookoff,
|
|
2400
|
+
cookOffCount = splash_damage_options.allunits_cookoff_count,
|
|
2401
|
+
cookOffPower = splash_damage_options.allunits_cookoff_power,
|
|
2402
|
+
cookOffDuration = splash_damage_options.allunits_cookoff_duration,
|
|
2403
|
+
cookOffRandomTiming = true,
|
|
2404
|
+
cookOffPowerRandom = splash_damage_options.allunits_cookoff_powerrandom,
|
|
2405
|
+
isTanker = splash_damage_options.allunits_enable_smoke, --Enable smoke
|
|
2406
|
+
flameSize = splash_damage_options.allunits_default_flame_size,
|
|
2407
|
+
flameDuration = splash_damage_options.allunits_default_flame_duration,
|
|
2408
|
+
cargoExplosionMult = 1
|
|
2409
|
+
})
|
|
2410
|
+
statusMsg = statusMsg .. " WITH DEFAULT SMOKE (Size: " .. splash_damage_options.allunits_default_flame_size .. ", Duration: " .. splash_damage_options.allunits_default_flame_duration .. "s)"
|
|
2411
|
+
debugMsg("Queued default smoke effect for " .. preTarget.name .. " at " .. string.format("%.1f", preTarget.distance) .. "m")
|
|
2412
|
+
end
|
|
2413
|
+
end
|
|
2414
|
+
|
|
2415
|
+
msg = msg .. "- " .. preTarget.name .. " " .. statusMsg .. "\n"
|
|
2416
|
+
end
|
|
1550
2417
|
--Check for additional units
|
|
1551
2418
|
for _, postTarget in ipairs(postExplosionTargets) do
|
|
1552
2419
|
local isPreDetected = false
|
|
@@ -1562,7 +2429,7 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1562
2429
|
local status = postTarget.health <= 0 and "WAS FULLY DESTROYED" or
|
|
1563
2430
|
(healthPercent < splash_damage_options.cargo_damage_threshold and "WAS DAMAGED BELOW THRESHOLD" or
|
|
1564
2431
|
"SURVIVED (Health: " .. postTarget.health .. ")")
|
|
1565
|
-
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Pre: Unknown, Post: " .. postTarget.health .. ")"
|
|
2432
|
+
local statusMsg = status .. " AT " .. string.format("X: %.0f, Y: %.0f, Z: %.0f", coords.x, coords.y, coords.z) .. " (Dist: " .. string.format("%.1f", postTarget.distance) .. "m, Pre: Unknown, Post: " .. postTarget.health .. ")"
|
|
1566
2433
|
local cargoData = cargoUnits[postTarget.name]
|
|
1567
2434
|
if cargoData and (postTarget.health <= 0 or healthPercent < splash_damage_options.cargo_damage_threshold) then
|
|
1568
2435
|
if splash_damage_options.enable_cargo_effects then
|
|
@@ -1657,6 +2524,9 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1657
2524
|
trigger.action.explosion(pos, power)
|
|
1658
2525
|
end, {effect.coords, cookOffPower}, timer.getTime() + effectIndex + delay)
|
|
1659
2526
|
end
|
|
2527
|
+
if splash_damage_options.cookoff_flares_enabled then
|
|
2528
|
+
scheduleCookOffFlares(effect.coords, effect.cookOffCount, effect.cookOffDuration, splash_damage_options.cookoff_flare_color)
|
|
2529
|
+
end
|
|
1660
2530
|
--Debris burst only if cook-off is true and enabled
|
|
1661
2531
|
if splash_damage_options.debris_effects then
|
|
1662
2532
|
local debrisCount = math.random(splash_damage_options.debris_count_min, splash_damage_options.debris_count_max)
|
|
@@ -1696,7 +2566,11 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1696
2566
|
|
|
1697
2567
|
debugMsg(msg)
|
|
1698
2568
|
env.info("SplashDamage Post-Explosion: " .. msg)
|
|
1699
|
-
|
|
2569
|
+
-- Schedule splashKillFeed if there are entries
|
|
2570
|
+
if #splashKillfeedTemp > 0 and splash_damage_options.killfeed_enable then
|
|
2571
|
+
timer.scheduleFunction(splashKillFeed, {}, timer.getTime() + splash_damage_options.killfeed_splashdelay)
|
|
2572
|
+
end
|
|
2573
|
+
end, {finalPos, blastRadius, chosenTargets, weaponName, explosionPower, wpnData.init}, timer.getTime() + 1)
|
|
1700
2574
|
end
|
|
1701
2575
|
end)
|
|
1702
2576
|
if not status then
|
|
@@ -1712,6 +2586,7 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1712
2586
|
end
|
|
1713
2587
|
table.insert(weaponsToRemove, wpn_id_)
|
|
1714
2588
|
end
|
|
2589
|
+
end
|
|
1715
2590
|
end)
|
|
1716
2591
|
if not status then
|
|
1717
2592
|
debugMsg("Error in track_wpns for '" .. (wpnData.name or "unknown weapon") .. "': " .. err)
|
|
@@ -1723,251 +2598,1198 @@ world.searchObjects({Object.Category.UNIT, Object.Category.STATIC}, tickVol, fun
|
|
|
1723
2598
|
end
|
|
1724
2599
|
return timer.getTime() + refreshRate
|
|
1725
2600
|
end
|
|
2601
|
+
|
|
1726
2602
|
function onWpnEvent(event)
|
|
1727
|
-
|
|
2603
|
+
if event.id == world.event.S_EVENT_SHOT then
|
|
1728
2604
|
if event.weapon then
|
|
1729
|
-
|
|
1730
|
-
|
|
2605
|
+
local ordnance = event.weapon
|
|
2606
|
+
--verify isExist and getDesc
|
|
2607
|
+
local isValid = false
|
|
2608
|
+
local status, desc = pcall(function() return ordnance:isExist() and ordnance:getDesc() end)
|
|
2609
|
+
if status and desc then
|
|
2610
|
+
isValid = true
|
|
2611
|
+
end
|
|
2612
|
+
if not isValid then
|
|
2613
|
+
if splash_damage_options.debug then
|
|
2614
|
+
env.info("SplashDamage: Invalid weapon object in S_EVENT_SHOT")
|
|
2615
|
+
debugMsg("Invalid weapon object in S_EVENT_SHOT")
|
|
2616
|
+
end
|
|
2617
|
+
return
|
|
2618
|
+
end
|
|
2619
|
+
--Safely get typeName with pcall
|
|
2620
|
+
local status, typeName = pcall(function() return trim(ordnance:getTypeName()) end)
|
|
2621
|
+
if not status or not typeName then
|
|
2622
|
+
if splash_damage_options.debug then
|
|
2623
|
+
env.info("SplashDamage: Failed to get weapon typeName: " .. tostring(typeName))
|
|
2624
|
+
debugMsg("Failed to get weapon typeName: " .. tostring(typeName))
|
|
2625
|
+
end
|
|
2626
|
+
return
|
|
2627
|
+
end
|
|
2628
|
+
|
|
2629
|
+
|
|
2630
|
+
local playerName = "Unknown"
|
|
2631
|
+
if event.initiator then
|
|
2632
|
+
local status, playerNameResult = pcall(function() return event.initiator:getPlayerName() end)
|
|
2633
|
+
if status and playerNameResult then
|
|
2634
|
+
playerName = playerNameResult
|
|
2635
|
+
else
|
|
2636
|
+
local status, unitId = pcall(function() return event.initiator:getID() end)
|
|
2637
|
+
if status and unitId then
|
|
2638
|
+
local playerList = net.get_player_list() or {}
|
|
2639
|
+
for _, pid in ipairs(playerList) do
|
|
2640
|
+
local pinfo = net.get_player_info(pid)
|
|
2641
|
+
if pinfo and pinfo.ucid and (tonumber(pinfo.slot) == unitId or pinfo.slot == event.initiator:getName()) then
|
|
2642
|
+
playerName = pinfo.name or "Unknown"
|
|
2643
|
+
break
|
|
2644
|
+
end
|
|
2645
|
+
end
|
|
2646
|
+
end
|
|
2647
|
+
end
|
|
2648
|
+
end
|
|
2649
|
+
if splash_damage_options.debug then
|
|
2650
|
+
env.info("Weapon [" .. typeName .. "] fired by player " .. playerName)
|
|
2651
|
+
debugMsg("Weapon [" .. typeName .. "] fired by player " .. playerName)
|
|
2652
|
+
end
|
|
2653
|
+
if splash_damage_options.napalmoverride_enabled then
|
|
2654
|
+
local napalmWeapons = {}
|
|
2655
|
+
for weapon in splash_damage_options.napalm_override_weapons:gmatch("[^,]+") do
|
|
2656
|
+
napalmWeapons[trim(weapon)] = true
|
|
2657
|
+
end
|
|
2658
|
+
if napalmWeapons[typeName] then
|
|
2659
|
+
isNapalm = true
|
|
2660
|
+
if splash_damage_options.debug then
|
|
2661
|
+
debugMsg("Tracking napalm override weapon: [" .. typeName .. "]")
|
|
2662
|
+
end
|
|
2663
|
+
end
|
|
2664
|
+
end
|
|
2665
|
+
if splash_damage_options.napalm_mk77_enabled and (typeName == "MK77mod0-WPN" or typeName == "MK77mod1-WPN") then
|
|
2666
|
+
isNapalm = true
|
|
2667
|
+
if splash_damage_options.debug then
|
|
2668
|
+
debugMsg("Tracking MK77 napalm weapon: [" .. typeName .. "]")
|
|
2669
|
+
end
|
|
2670
|
+
end
|
|
2671
|
+
if isNapalm then
|
|
2672
|
+
tracked_weapons[event.weapon.id_] = {
|
|
2673
|
+
wpn = ordnance,
|
|
2674
|
+
init = playerName,
|
|
2675
|
+
pos = ordnance:getPoint(),
|
|
2676
|
+
dir = ordnance:getPosition().x,
|
|
2677
|
+
name = typeName,
|
|
2678
|
+
speed = ordnance:getVelocity(),
|
|
2679
|
+
cat = ordnance:getCategory()
|
|
2680
|
+
}
|
|
2681
|
+
return
|
|
2682
|
+
end
|
|
2683
|
+
--Debug the exact typeName and explTable lookup
|
|
1731
2684
|
if splash_damage_options.debug then
|
|
1732
|
-
|
|
1733
|
-
|
|
2685
|
+
debugMsg("Checking explTable for typeName: [" .. typeName .. "]")
|
|
2686
|
+
end
|
|
2687
|
+
local weaponData = explTable[typeName]
|
|
2688
|
+
if splash_damage_options.debug then
|
|
2689
|
+
if weaponData then
|
|
2690
|
+
debugMsg("Found in explTable: explosive=" .. weaponData.explosive .. ", groundordnance=" .. tostring(weaponData.groundordnance))
|
|
2691
|
+
else
|
|
2692
|
+
debugMsg("Not found in explTable: [" .. typeName .. "]")
|
|
2693
|
+
end
|
|
2694
|
+
end
|
|
2695
|
+
--Handle ground ordnance explicitly
|
|
2696
|
+
if weaponData and weaponData.groundordnance then
|
|
2697
|
+
if splash_damage_options.track_groundunitordnance then
|
|
2698
|
+
--Count tracked ground ordnance
|
|
2699
|
+
local groundOrdnanceCount = 0
|
|
2700
|
+
for _, wpnData in pairs(tracked_weapons) do
|
|
2701
|
+
if wpnData.isGroundUnitOrdnance then
|
|
2702
|
+
groundOrdnanceCount = groundOrdnanceCount + 1
|
|
2703
|
+
end
|
|
2704
|
+
end
|
|
2705
|
+
if groundOrdnanceCount >= splash_damage_options.groundunitordnance_maxtrackedcount then
|
|
2706
|
+
if splash_damage_options.debug then
|
|
2707
|
+
debugMsg("Skipping tracking for " .. typeName .. ": ground ordnance limit reached (" .. groundOrdnanceCount .. "/" .. splash_damage_options.groundunitordnance_maxtrackedcount .. ")")
|
|
2708
|
+
env.info("SplashDamage: Skipping tracking for " .. typeName .. ": ground ordnance limit reached (" .. groundOrdnanceCount .. "/" .. splash_damage_options.groundunitordnance_maxtrackedcount .. ")")
|
|
2709
|
+
end
|
|
2710
|
+
return
|
|
2711
|
+
end
|
|
2712
|
+
if splash_damage_options.track_groundunitordnance_debug then
|
|
2713
|
+
debugMsg("Tracking ground unit ordnance: " .. typeName .. " fired by " .. (event.initiator and event.initiator:getTypeName() or "unknown"))
|
|
2714
|
+
env.info("SplashDamage: Tracking ground unit ordnance: " .. typeName .. " (" .. (event.initiator and event.initiator:getTypeName() or "no initiator") .. ")")
|
|
2715
|
+
end
|
|
2716
|
+
tracked_weapons[event.weapon.id_] = {
|
|
2717
|
+
wpn = ordnance,
|
|
2718
|
+
init = playerName,
|
|
2719
|
+
pos = ordnance:getPoint(),
|
|
2720
|
+
dir = ordnance:getPosition().x,
|
|
2721
|
+
name = typeName,
|
|
2722
|
+
speed = ordnance:getVelocity(),
|
|
2723
|
+
cat = ordnance:getCategory(),
|
|
2724
|
+
isGroundUnitOrdnance = true --Flag for ground ordnance
|
|
2725
|
+
}
|
|
2726
|
+
elseif splash_damage_options.track_groundunitordnance_debug then
|
|
2727
|
+
debugMsg("Event shot, but not tracking ground unit ordnance: " .. typeName)
|
|
2728
|
+
env.info("SplashDamage: event shot, but not tracking ground unit ordnance: " .. typeName .. " (" .. (event.initiator and event.initiator:getTypeName() or "no initiator") .. ")")
|
|
2729
|
+
end
|
|
2730
|
+
return
|
|
2731
|
+
end
|
|
2732
|
+
--Handle other tracked weapons in explTable
|
|
2733
|
+
if weaponData then
|
|
2734
|
+
if (ordnance:getDesc().category ~= 0) and event.initiator then
|
|
2735
|
+
if ordnance:getDesc().category == 1 then --Missiles
|
|
2736
|
+
if (ordnance:getDesc().MissileCategory ~= 1 and ordnance:getDesc().MissileCategory ~= 2) then --Exclude AAM and SAM
|
|
2737
|
+
tracked_weapons[event.weapon.id_] = {
|
|
2738
|
+
wpn = ordnance,
|
|
2739
|
+
init = playerName,
|
|
2740
|
+
pos = ordnance:getPoint(),
|
|
2741
|
+
dir = ordnance:getPosition().x,
|
|
2742
|
+
name = typeName,
|
|
2743
|
+
speed = ordnance:getVelocity(),
|
|
2744
|
+
cat = ordnance:getCategory()
|
|
2745
|
+
}
|
|
2746
|
+
end
|
|
2747
|
+
else --Rockets, bombs, etc.
|
|
2748
|
+
tracked_weapons[event.weapon.id_] = {
|
|
2749
|
+
wpn = ordnance,
|
|
2750
|
+
init = playerName,
|
|
2751
|
+
pos = ordnance:getPoint(),
|
|
2752
|
+
dir = ordnance:getPosition().x,
|
|
2753
|
+
name = typeName,
|
|
2754
|
+
speed = ordnance:getVelocity(),
|
|
2755
|
+
cat = ordnance:getCategory()
|
|
2756
|
+
}
|
|
2757
|
+
end
|
|
2758
|
+
end
|
|
2759
|
+
return --Exit after handling known weapons
|
|
1734
2760
|
end
|
|
2761
|
+
--Handle unknown weapons or non-tracked shells
|
|
1735
2762
|
if string.find(typeName, "weapons.shells") then
|
|
1736
2763
|
if splash_damage_options.debug then
|
|
1737
2764
|
debugMsg("Event shot, but not tracking: " .. typeName)
|
|
1738
|
-
|
|
2765
|
+
env.info("SplashDamage: event shot, but not tracking: " .. typeName .. " (" .. (event.initiator and event.initiator:getTypeName() or "no initiator") .. ")")
|
|
1739
2766
|
end
|
|
1740
2767
|
return
|
|
1741
2768
|
end
|
|
1742
|
-
|
|
1743
|
-
--
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
if splash_damage_options.weapon_missing_message == true then
|
|
2769
|
+
|
|
2770
|
+
--Log missing weapons
|
|
2771
|
+
env.info("SplashDamage: " .. typeName .. " missing from script (" .. (event.initiator and event.initiator:getTypeName() or "no initiator") .. ")")
|
|
2772
|
+
if splash_damage_options.weapon_missing_message then
|
|
1747
2773
|
trigger.action.outText("SplashDamage: " .. typeName .. " missing from script (" .. (event.initiator and event.initiator:isExist() and event.initiator:getTypeName() or "no initiator") .. ")", 3)
|
|
1748
|
-
-- if mist and mist.utils and mist.utils.tableShow then --Only if MiST is present
|
|
1749
|
-
-- local success, desc = pcall(mist.utils.tableShow, ordnance:getDesc())
|
|
1750
|
-
-- if success then
|
|
1751
|
-
-- debugMsg("desc for [" .. typeName .. "]: " .. desc)
|
|
1752
|
-
-- else
|
|
1753
|
-
-- debugMsg("Could not retrieve description for [" .. typeName .. "]. Object may no longer exist.")
|
|
1754
|
-
-- end
|
|
1755
|
-
-- end
|
|
1756
2774
|
env.info("Current keys in explTable:")
|
|
1757
2775
|
for k, v in pairs(explTable) do
|
|
1758
2776
|
env.info("Key: [" .. k .. "]")
|
|
2777
|
+
end
|
|
2778
|
+
|
|
2779
|
+
end
|
|
2780
|
+
end
|
|
2781
|
+
end
|
|
2782
|
+
end
|
|
2783
|
+
|
|
2784
|
+
function splashKillFeed()
|
|
2785
|
+
if not splash_damage_options.killfeed_enable then return end
|
|
2786
|
+
|
|
2787
|
+
local status, err = pcall(function()
|
|
2788
|
+
local tempTable = splashKillfeedTemp
|
|
2789
|
+
splashKillfeedTemp = {}
|
|
2790
|
+
local processedUnitIds = {} -- Track unit IDs processed in this batch
|
|
2791
|
+
|
|
2792
|
+
for _, entry in ipairs(tempTable) do
|
|
2793
|
+
local unitId = entry.unitId
|
|
2794
|
+
local unitName = entry.unitName
|
|
2795
|
+
local unitType = entry.unitType
|
|
2796
|
+
local playerName = entry.playerName
|
|
2797
|
+
local weaponName = entry.weaponName
|
|
2798
|
+
local position = entry.position
|
|
2799
|
+
|
|
2800
|
+
-- Skip if unitType is "Unknown"
|
|
2801
|
+
if unitType == "Unknown" then
|
|
2802
|
+
if splash_damage_options.killfeed_debug then
|
|
2803
|
+
env.info(string.format("SplashKillFeed: Skipped unit ID %s with unknown type at %.2f", unitId, timer.getTime()))
|
|
2804
|
+
end
|
|
2805
|
+
return
|
|
2806
|
+
end
|
|
2807
|
+
|
|
2808
|
+
--Check if unit ID was already processed in this batch
|
|
2809
|
+
if processedUnitIds[unitId] then
|
|
2810
|
+
if splash_damage_options.killfeed_debug then
|
|
2811
|
+
env.info(string.format("SplashKillFeed: Skipped duplicate splash kill in batch for unit ID %s (%s) by %s with %s at %.2f",
|
|
2812
|
+
unitId, unitType, playerName, weaponName, timer.getTime()))
|
|
2813
|
+
end
|
|
2814
|
+
return --Skip to next iteration
|
|
2815
|
+
end
|
|
2816
|
+
|
|
2817
|
+
local unitExists = false
|
|
2818
|
+
local status, exists = pcall(function()
|
|
2819
|
+
local obj = Unit.getByName(unitName) or StaticObject.getByName(unitName)
|
|
2820
|
+
return obj and obj:isExist()
|
|
2821
|
+
end)
|
|
2822
|
+
if status and not exists then
|
|
2823
|
+
unitExists = false
|
|
2824
|
+
elseif status then
|
|
2825
|
+
unitExists = true
|
|
2826
|
+
else
|
|
2827
|
+
if splash_damage_options.killfeed_debug then
|
|
2828
|
+
env.info("SplashKillFeed: Error checking existence of unit ID " .. tostring(unitId) .. ": " .. tostring(exists))
|
|
2829
|
+
end
|
|
2830
|
+
end
|
|
2831
|
+
|
|
2832
|
+
if not unitExists then
|
|
2833
|
+
local isDuplicate = false
|
|
2834
|
+
for _, killEntry in ipairs(killfeedTable) do
|
|
2835
|
+
if killEntry.unitID == unitId then
|
|
2836
|
+
isDuplicate = true
|
|
2837
|
+
if splash_damage_options.killfeed_debug then
|
|
2838
|
+
env.info(string.format("SplashKillFeed: Skipped duplicate splash kill for unit ID %s (%s) by %s with %s at %.2f",
|
|
2839
|
+
unitId, unitType, playerName, weaponName, timer.getTime()))
|
|
1759
2840
|
end
|
|
2841
|
+
break
|
|
1760
2842
|
end
|
|
1761
|
-
return --Skip tracking this weapon since its not in the table
|
|
1762
2843
|
end
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
2844
|
+
|
|
2845
|
+
if not isDuplicate then
|
|
2846
|
+
local msg = string.format("%s destroyed by %s's %s Splash Damage", unitType, playerName, weaponName)
|
|
2847
|
+
if splash_damage_options.killfeed_game_messages then
|
|
2848
|
+
local status, err = pcall(function()
|
|
2849
|
+
trigger.action.outTextForCoalition(2, msg, splash_damage_options.killfeed_game_message_duration)
|
|
2850
|
+
end)
|
|
2851
|
+
if not status then
|
|
2852
|
+
trigger.action.outText(msg, splash_damage_options.killfeed_game_message_duration)
|
|
2853
|
+
if splash_damage_options.killfeed_debug then
|
|
2854
|
+
env.info("SplashKillFeed: Failed coalition message: " .. tostring(err))
|
|
2855
|
+
end
|
|
2856
|
+
end
|
|
1768
2857
|
end
|
|
1769
|
-
|
|
1770
|
-
|
|
2858
|
+
|
|
2859
|
+
table.insert(splashKillfeedTable, {
|
|
2860
|
+
unitName = unitName,
|
|
2861
|
+
unitType = unitType,
|
|
2862
|
+
unitId = unitId,
|
|
2863
|
+
playerName = playerName,
|
|
2864
|
+
weaponName = weaponName,
|
|
2865
|
+
time = timer.getTime(),
|
|
2866
|
+
position = position
|
|
2867
|
+
})
|
|
2868
|
+
|
|
2869
|
+
if splash_damage_options.killfeed_debug then
|
|
2870
|
+
env.info(string.format("SplashKillFeed: %s destroyed by %s's %s Splash Damage [ID: %s] at %.2f",
|
|
2871
|
+
unitType, playerName, weaponName, unitId, timer.getTime()))
|
|
2872
|
+
end
|
|
2873
|
+
processedUnitIds[unitId] = true --Mark unit ID as processed
|
|
1771
2874
|
end
|
|
2875
|
+
elseif splash_damage_options.killfeed_debug then
|
|
2876
|
+
env.info(string.format("SplashKillFeed: Unit ID %s (%s) still exists, skipping splash kill at %.2f",
|
|
2877
|
+
unitId, unitType, timer.getTime()))
|
|
1772
2878
|
end
|
|
1773
2879
|
end
|
|
2880
|
+
end)
|
|
2881
|
+
|
|
2882
|
+
if not status and splash_damage_options.killfeed_debug then
|
|
2883
|
+
env.info("SplashKillFeed: Error: " .. tostring(err))
|
|
1774
2884
|
end
|
|
1775
2885
|
end
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
end
|
|
1783
|
-
|
|
1784
|
-
function WpnHandler:onEvent(event)
|
|
1785
|
-
protectedCall(onWpnEvent, event)
|
|
1786
|
-
end
|
|
1787
|
-
|
|
1788
|
-
function explodeObject(args)
|
|
1789
|
-
local point = args[1]
|
|
1790
|
-
local distance = args[2]
|
|
1791
|
-
local power = args[3]
|
|
1792
|
-
trigger.action.explosion(point, power)
|
|
1793
|
-
end
|
|
1794
|
-
|
|
1795
|
-
function blastWave(_point, _radius, weapon, power, isShapedCharge)
|
|
1796
|
-
if isShapedCharge then
|
|
1797
|
-
_radius = _radius * splash_damage_options.shaped_charge_multiplier
|
|
1798
|
-
end
|
|
1799
|
-
if splash_damage_options.use_dynamic_blast_radius then
|
|
1800
|
-
local dynamicRadius = math.pow(power, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
1801
|
-
if isShapedCharge then
|
|
1802
|
-
_radius = dynamicRadius * splash_damage_options.shaped_charge_multiplier
|
|
1803
|
-
else
|
|
1804
|
-
_radius = dynamicRadius
|
|
2886
|
+
|
|
2887
|
+
|
|
2888
|
+
local function processSplashKillfeed()
|
|
2889
|
+
if not splash_damage_options.killfeed_enable or not splash_damage_options.killfeed_lekas_foothold_integration then
|
|
2890
|
+
if splash_damage_options.killfeed_debug then
|
|
2891
|
+
env.info("SplashDamage: processSplashKillfeed skipped")
|
|
1805
2892
|
end
|
|
2893
|
+
return timer.getTime() + 60
|
|
1806
2894
|
end
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
params = {
|
|
1812
|
-
point = _point,
|
|
1813
|
-
radius = _radius
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
local ifFound = function(foundObject, val)
|
|
1818
|
-
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT and foundObject:getCategory() == Object.Category.UNIT then
|
|
1819
|
-
foundUnits[#foundUnits + 1] = foundObject
|
|
2895
|
+
|
|
2896
|
+
if not bc or type(bc) ~= "table" or not bc.addTempStat then
|
|
2897
|
+
if splash_damage_options.killfeed_debug then
|
|
2898
|
+
env.info("SplashDamage: bc is not accessible or missing addTempStat")
|
|
1820
2899
|
end
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2900
|
+
return timer.getTime() + 60
|
|
2901
|
+
end
|
|
2902
|
+
|
|
2903
|
+
local currentTime = timer.getTime()
|
|
2904
|
+
local entriesToRemove = {}
|
|
2905
|
+
local processedCount = 0
|
|
2906
|
+
|
|
2907
|
+
-- Log bc table state before processing
|
|
2908
|
+
if splash_damage_options.killfeed_debug then
|
|
2909
|
+
env.info("SplashDamage: processSplashKillfeed started at " .. string.format("%.2f", currentTime))
|
|
2910
|
+
env.info("SplashDamage: bc table state: " .. (bc and "exists" or "nil"))
|
|
2911
|
+
env.info("SplashDamage: bc.addTempStat: " .. (bc.addTempStat and "exists" or "nil"))
|
|
2912
|
+
env.info("SplashDamage: bc.context: " .. (bc.context and "exists" or "nil"))
|
|
2913
|
+
if bc.context then
|
|
2914
|
+
env.info("SplashDamage: bc.context.playerContributions: " .. (bc.context.playerContributions and "exists" or "nil"))
|
|
2915
|
+
if bc.context.playerContributions then
|
|
2916
|
+
env.info("SplashDamage: bc.context.playerContributions[2]: " .. (bc.context.playerContributions[2] and "exists" or "nil"))
|
|
1824
2917
|
end
|
|
1825
2918
|
end
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
local
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2919
|
+
end
|
|
2920
|
+
|
|
2921
|
+
for i, entry in ipairs(splashKillfeedTable) do
|
|
2922
|
+
if currentTime - entry.time >= splash_damage_options.killfeed_lekas_contribution_delay then
|
|
2923
|
+
local playerName = entry.playerName
|
|
2924
|
+
local unitType = entry.unitType
|
|
2925
|
+
local unitId = entry.unitId
|
|
2926
|
+
|
|
2927
|
+
-- Log entry details
|
|
2928
|
+
if splash_damage_options.killfeed_debug then
|
|
2929
|
+
env.info(string.format("SplashDamage: Processing splash kill entry %d: unitId=%s, unitType=%s, player=%s, time=%.2f",
|
|
2930
|
+
i, unitId, unitType, playerName, entry.time))
|
|
2931
|
+
end
|
|
2932
|
+
|
|
2933
|
+
local status, result = pcall(function()
|
|
2934
|
+
local statName = "Ground Units"
|
|
2935
|
+
local points = 10
|
|
2936
|
+
if unitType:find("Plane") then
|
|
2937
|
+
statName = "Air"
|
|
2938
|
+
points = 30
|
|
2939
|
+
elseif unitType:find("Helicopter") then
|
|
2940
|
+
statName = "Helo"
|
|
2941
|
+
points = 30
|
|
2942
|
+
elseif unitType:find("SAM") then
|
|
2943
|
+
statName = "SAM"
|
|
2944
|
+
points = 30
|
|
2945
|
+
elseif unitType:find("Infantry") then
|
|
2946
|
+
statName = "Infantry"
|
|
2947
|
+
points = 10
|
|
2948
|
+
elseif unitType:find("Ship") then
|
|
2949
|
+
statName = "Ship"
|
|
2950
|
+
points = 250
|
|
2951
|
+
elseif unitType:find("Building") then
|
|
2952
|
+
statName = "Structure"
|
|
2953
|
+
points = 30
|
|
1840
2954
|
end
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
local
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
local triggerExplosion = false
|
|
1853
|
-
if splash_damage_options.always_cascade_explode then
|
|
1854
|
-
triggerExplosion = true
|
|
1855
|
-
else
|
|
1856
|
-
if obj:getDesc().life then
|
|
1857
|
-
local healthPercent = (obj:getLife() / obj:getDesc().life) * 100
|
|
1858
|
-
if healthPercent <= splash_damage_options.cascade_explode_threshold then
|
|
1859
|
-
triggerExplosion = true
|
|
1860
|
-
end
|
|
1861
|
-
--Queue cargo effects for units below
|
|
1862
|
-
local cargoData = cargoUnits[obj:getTypeName()]
|
|
1863
|
-
if cargoData and healthPercent <= splash_damage_options.cargo_damage_threshold and splash_damage_options.enable_cargo_effects then
|
|
1864
|
-
local cargoPower = power * cargoData.cargoExplosionMult
|
|
1865
|
-
table.insert(cargoEffectsQueue, {
|
|
1866
|
-
name = obj:getTypeName(),
|
|
1867
|
-
distance = dist,
|
|
1868
|
-
coords = obj_location,
|
|
1869
|
-
power = cargoPower,
|
|
1870
|
-
explosion = cargoData.cargoExplosion,
|
|
1871
|
-
cookOff = cargoData.cargoCookOff,
|
|
1872
|
-
cookOffCount = cargoData.cookOffCount,
|
|
1873
|
-
cookOffPower = cargoData.cookOffPower,
|
|
1874
|
-
cookOffDuration = cargoData.cookOffDuration,
|
|
1875
|
-
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
1876
|
-
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
1877
|
-
isTanker = cargoData.isTanker,
|
|
1878
|
-
flameSize = cargoData.flameSize,
|
|
1879
|
-
flameDuration = cargoData.flameDuration
|
|
1880
|
-
})
|
|
1881
|
-
end
|
|
1882
|
-
else
|
|
1883
|
-
triggerExplosion = true
|
|
1884
|
-
end
|
|
1885
|
-
if not triggerExplosion and obj:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
1886
|
-
local health = obj:getLife() or 0
|
|
1887
|
-
if health <= 0 then
|
|
1888
|
-
triggerExplosion = true
|
|
1889
|
-
end
|
|
1890
|
-
end
|
|
2955
|
+
bc:addTempStat(playerName, statName, 1)
|
|
2956
|
+
if splash_damage_options.killfeed_debug then
|
|
2957
|
+
env.info(string.format("SplashDamage: Added temp stat for %s: stat=%s, count=1", playerName, statName))
|
|
2958
|
+
end
|
|
2959
|
+
if bc.context and type(bc.context) == "table" and bc.context.playerContributions and type(bc.context.playerContributions) == "table" then
|
|
2960
|
+
bc.context.playerContributions[2] = bc.context.playerContributions[2] or {}
|
|
2961
|
+
local oldPoints = bc.context.playerContributions[2][playerName] or 0
|
|
2962
|
+
bc.context.playerContributions[2][playerName] = oldPoints + points
|
|
2963
|
+
if splash_damage_options.killfeed_debug then
|
|
2964
|
+
env.info(string.format("SplashDamage: Updated contributions for %s: old=%d, new=%d, added=%d",
|
|
2965
|
+
playerName, oldPoints, bc.context.playerContributions[2][playerName], points))
|
|
1891
2966
|
end
|
|
1892
|
-
|
|
1893
|
-
|
|
2967
|
+
else
|
|
2968
|
+
if splash_damage_options.killfeed_debug then
|
|
2969
|
+
env.info("SplashDamage: Skipped contribution update for " .. playerName .. ": bc.context or bc.context.playerContributions is nil")
|
|
1894
2970
|
end
|
|
1895
2971
|
end
|
|
2972
|
+
processedCount = processedCount + 1
|
|
2973
|
+
if splash_damage_options.killfeed_debug then
|
|
2974
|
+
env.info(string.format("SplashDamage: Processed splash kill for %s by %s: stat=%s, points=%d, unitId=%s",
|
|
2975
|
+
unitType, playerName, statName, points, unitId))
|
|
2976
|
+
end
|
|
2977
|
+
end)
|
|
2978
|
+
if not status and splash_damage_options.killfeed_debug then
|
|
2979
|
+
env.info("SplashDamage: Error processing splash kill for unitId=" .. tostring(unitId) .. ": " .. tostring(result))
|
|
1896
2980
|
end
|
|
2981
|
+
table.insert(entriesToRemove, i)
|
|
1897
2982
|
end
|
|
1898
|
-
return true
|
|
1899
2983
|
end
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
world.searchObjects(Object.Category.SCENERY, volS, ifFound)
|
|
1904
|
-
world.searchObjects(Object.Category.CARGO, volS, ifFound)
|
|
1905
|
-
if splash_damage_options.damage_model then
|
|
1906
|
-
timer.scheduleFunction(modelUnitDamage, foundUnits, timer.getTime() + 1.5)
|
|
2984
|
+
|
|
2985
|
+
for i = #entriesToRemove, 1, -1 do
|
|
2986
|
+
table.remove(splashKillfeedTable, entriesToRemove[i])
|
|
1907
2987
|
end
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
2988
|
+
|
|
2989
|
+
if splash_damage_options.killfeed_debug then
|
|
2990
|
+
if bc.tempStats and type(bc.tempStats) == "table" then
|
|
2991
|
+
env.info("SplashDamage: tempStats contents:")
|
|
2992
|
+
for playerName, stats in pairs(bc.tempStats) do
|
|
2993
|
+
local statStr = ""
|
|
2994
|
+
for statKey, value in pairs(stats) do
|
|
2995
|
+
statStr = statStr .. statKey .. "=" .. tostring(value) .. ", "
|
|
1917
2996
|
end
|
|
2997
|
+
env.info("SplashDamage: " .. playerName .. ": " .. (statStr ~= "" and statStr or "empty"))
|
|
1918
2998
|
end
|
|
1919
|
-
if
|
|
1920
|
-
|
|
1921
|
-
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
1922
|
-
gameMsg(unit:getTypeName() .. " weapons disabled")
|
|
1923
|
-
end
|
|
1924
|
-
if health <= splash_damage_options.unit_disabled_health and health > 0 then
|
|
1925
|
-
unit:getController():setTask({id = 'Hold', params = {}})
|
|
1926
|
-
unit:getController():setOnOff(false)
|
|
1927
|
-
gameMsg(unit:getTypeName() .. " disabled")
|
|
1928
|
-
end
|
|
2999
|
+
if not next(bc.tempStats) then
|
|
3000
|
+
env.info("SplashDamage: tempStats is empty")
|
|
1929
3001
|
end
|
|
3002
|
+
else
|
|
3003
|
+
env.info("SplashDamage: bc.tempStats is nil or not a table")
|
|
1930
3004
|
end
|
|
1931
3005
|
end
|
|
1932
|
-
end
|
|
1933
3006
|
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
env.info("Error: Setting " .. setting .. " does not exist.")
|
|
1937
|
-
return
|
|
3007
|
+
if splash_damage_options.killfeed_debug and processedCount > 0 then
|
|
3008
|
+
env.info("SplashDamage: Processed " .. processedCount .. " splash kills, remaining: " .. #splashKillfeedTable)
|
|
1938
3009
|
end
|
|
1939
3010
|
|
|
1940
|
-
|
|
1941
|
-
env.info("Updating " .. setting .. " from " .. tostring(splash_damage_options[setting]) .. " to " .. tostring(newValue))
|
|
1942
|
-
splash_damage_options[setting] = newValue
|
|
1943
|
-
trigger.action.outText("Updated " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
3011
|
+
return timer.getTime() + 60
|
|
1944
3012
|
end
|
|
1945
3013
|
|
|
1946
|
-
function toggleSplashDamageSetting(setting)
|
|
1947
|
-
splash_damage_options[setting] = not splash_damage_options[setting]
|
|
1948
|
-
trigger.action.outText("Toggled " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
1949
3014
|
|
|
1950
|
-
if setting == "enable_radio_menu" then
|
|
1951
|
-
if splash_damage_options.enable_radio_menu then
|
|
1952
|
-
addSplashDamageMenu()
|
|
1953
|
-
else
|
|
1954
|
-
missionCommands.removeItem(splash_damage_menu)
|
|
1955
|
-
splash_damage_menu = nil
|
|
1956
|
-
end
|
|
1957
|
-
end
|
|
1958
|
-
end
|
|
1959
3015
|
|
|
1960
|
-
function addValueAdjustmentCommands(menu, setting)
|
|
1961
|
-
missionCommands.addCommand("+0.1", menu, updateSplashDamageSetting, setting, 0.1)
|
|
1962
|
-
missionCommands.addCommand("+1", menu, updateSplashDamageSetting, setting, 1)
|
|
1963
|
-
missionCommands.addCommand("+10", menu, updateSplashDamageSetting, setting, 10)
|
|
1964
|
-
missionCommands.addCommand("+100", menu, updateSplashDamageSetting, setting, 100)
|
|
1965
3016
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
3017
|
+
|
|
3018
|
+
--Function to log and process unit data from event
|
|
3019
|
+
function logEvent(eventName, eventData)
|
|
3020
|
+
local logStr = "\n---EVENT: " .. eventName .. " ---\n"
|
|
3021
|
+
|
|
3022
|
+
--Variables to hold unit data
|
|
3023
|
+
local unitID, unitName, unitType, unitPosition, unitLife
|
|
3024
|
+
|
|
3025
|
+
--Handle DEAD event (use initiator)
|
|
3026
|
+
if eventName == "DEAD" and eventData.initiator then
|
|
3027
|
+
unitID = safeGet(function() return eventData.initiator:getID() end, "unavailable")
|
|
3028
|
+
unitName = safeGet(function() return eventData.initiator:getName() end, "unknown")
|
|
3029
|
+
unitType = safeGet(function() return eventData.initiator:getTypeName() end, "unknown")
|
|
3030
|
+
unitPosition = safeGet(function()
|
|
3031
|
+
local pos = eventData.initiator:getPosition().p
|
|
3032
|
+
return string.format("x=%.0f, y=%.0f, z=%.0f", pos.x, pos.y, pos.z)
|
|
3033
|
+
end, "unavailable")
|
|
3034
|
+
unitLife = safeGet(function() return eventData.initiator:getLife() end, 0)
|
|
3035
|
+
|
|
3036
|
+
--Delay DEAD event processing by 0.1 seconds
|
|
3037
|
+
timer.scheduleFunction(function(params)
|
|
3038
|
+
local logStr = "\n---EVENT: " .. params.eventName .. " ---\n"
|
|
3039
|
+
|
|
3040
|
+
--Check if unitID is already processed
|
|
3041
|
+
if LogEventProcessedUnitTable[params.unitID] then
|
|
3042
|
+
logStr = logStr .. "Unit ID " .. params.unitID .. " already processed in LogEventProcessedUnitTable\n"
|
|
3043
|
+
if splash_damage_options.events_debug then
|
|
3044
|
+
env.info(logStr)
|
|
3045
|
+
end
|
|
3046
|
+
return
|
|
3047
|
+
end
|
|
3048
|
+
|
|
3049
|
+
--Log and store unit data if valid
|
|
3050
|
+
if params.unitID ~= "unavailable" and params.unitName ~= "unavailable" and params.unitType ~= "unavailable" and params.unitPosition ~= "unavailable" and params.unitLife ~= "unavailable" then
|
|
3051
|
+
logStr = logStr .. "Stored Unit Data: ID=" .. params.unitID .. ", Name=" .. params.unitName .. ", Type=" .. params.unitType .. ", Position=" .. params.unitPosition .. ", Life=" .. params.unitLife .. "\n"
|
|
3052
|
+
logStr = logStr .. "Processing DEAD event for unit " .. params.unitName .. " (ID: " .. params.unitID .. ") at position " .. params.unitPosition .. "\n"
|
|
3053
|
+
|
|
3054
|
+
--Store in LogEventProcessedUnitTable
|
|
3055
|
+
LogEventProcessedUnitTable[params.unitID] = {
|
|
3056
|
+
id = params.unitID,
|
|
3057
|
+
name = params.unitName,
|
|
3058
|
+
type = params.unitType,
|
|
3059
|
+
position = params.unitPosition,
|
|
3060
|
+
life = params.unitLife,
|
|
3061
|
+
event = params.eventName,
|
|
3062
|
+
time = timer.getTime()
|
|
3063
|
+
}
|
|
3064
|
+
else
|
|
3065
|
+
--logStr = logStr .. "Unit Data Not Available: One or more fields unavailable\n"
|
|
3066
|
+
end
|
|
3067
|
+
|
|
3068
|
+
if splash_damage_options.events_debug then
|
|
3069
|
+
env.info(logStr)
|
|
3070
|
+
end
|
|
3071
|
+
end, {
|
|
3072
|
+
eventName = eventName,
|
|
3073
|
+
unitID = unitID,
|
|
3074
|
+
unitName = unitName,
|
|
3075
|
+
unitType = unitType,
|
|
3076
|
+
unitPosition = unitPosition,
|
|
3077
|
+
unitLife = unitLife
|
|
3078
|
+
}, timer.getTime() + 0.1)
|
|
3079
|
+
return --Exit early to queue the event
|
|
3080
|
+
end
|
|
3081
|
+
|
|
3082
|
+
--Handle HIT and KILL events (use target or object)
|
|
3083
|
+
if (eventName == "HIT" or eventName == "KILL") and (eventData.target or eventData.object) then
|
|
3084
|
+
local tgt = eventData.target or eventData.object
|
|
3085
|
+
unitID = safeGet(function() return tgt:getID() end, "unavailable")
|
|
3086
|
+
unitName = safeGet(function() return tgt:getName() end, "unknown")
|
|
3087
|
+
unitType = safeGet(function() return tgt:getTypeName() end, "unknown")
|
|
3088
|
+
unitPosition = safeGet(function()
|
|
3089
|
+
local pos = tgt:getPosition().p
|
|
3090
|
+
return string.format("x=%.0f, y=%.0f, z=%.0f", pos.x, pos.y, pos.z)
|
|
3091
|
+
end, "unavailable")
|
|
3092
|
+
unitLife = safeGet(function() return tgt:getLife() end, "Alive")
|
|
3093
|
+
end
|
|
3094
|
+
|
|
3095
|
+
--Handle HIT event with delayed processing to capture last hit
|
|
3096
|
+
if eventName == "HIT" and unitID ~= "unavailable" then
|
|
3097
|
+
--Skip if life is 0 or below
|
|
3098
|
+
if type(unitLife) == "number" and unitLife <= 0 then
|
|
3099
|
+
logStr = logStr .. "Unit ID " .. unitID .. " has life <= 0, skipping HIT event\n"
|
|
3100
|
+
if splash_damage_options.events_debug then
|
|
3101
|
+
env.info(logStr)
|
|
3102
|
+
end
|
|
3103
|
+
return
|
|
3104
|
+
end
|
|
3105
|
+
|
|
3106
|
+
--Store event data temporarily
|
|
3107
|
+
local hitData = {
|
|
3108
|
+
unitID = unitID,
|
|
3109
|
+
unitName = unitName,
|
|
3110
|
+
unitType = unitType,
|
|
3111
|
+
unitPosition = unitPosition,
|
|
3112
|
+
unitLife = unitLife,
|
|
3113
|
+
eventName = eventName,
|
|
3114
|
+
timestamp = timer.getTime()
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
--Schedule or reschedule processing after x seconds
|
|
3118
|
+
if not LogEventProcessedUnitTable[unitID] or LogEventProcessedUnitTable[unitID].event ~= "HIT" then
|
|
3119
|
+
LogEventProcessedUnitTable[unitID] = { event = "HIT", timerID = nil } --Initialize entry
|
|
3120
|
+
end
|
|
3121
|
+
|
|
3122
|
+
--Cancel previous timer if exists
|
|
3123
|
+
if LogEventProcessedUnitTable[unitID].timerID then
|
|
3124
|
+
timer.removeFunction(LogEventProcessedUnitTable[unitID].timerID)
|
|
3125
|
+
end
|
|
3126
|
+
|
|
3127
|
+
--Schedule new timer
|
|
3128
|
+
local timerID = timer.scheduleFunction(function(params)
|
|
3129
|
+
local logStr = "\n---EVENT: HIT ---\n"
|
|
3130
|
+
|
|
3131
|
+
--Check if unitID is already processed by another event
|
|
3132
|
+
if LogEventProcessedUnitTable[params.unitID] and LogEventProcessedUnitTable[params.unitID].event ~= "HIT" then
|
|
3133
|
+
logStr = logStr .. "Unit ID " .. params.unitID .. " already processed by " .. LogEventProcessedUnitTable[params.unitID].event .. " event\n"
|
|
3134
|
+
if splash_damage_options.events_debug then
|
|
3135
|
+
env.info(logStr)
|
|
3136
|
+
end
|
|
3137
|
+
return
|
|
3138
|
+
end
|
|
3139
|
+
|
|
3140
|
+
--Log and store unit data if valid
|
|
3141
|
+
if params.unitID ~= "unavailable" and params.unitName ~= "unavailable" and params.unitType ~= "unavailable" and params.unitPosition ~= "unavailable" and params.unitLife ~= "unavailable" then
|
|
3142
|
+
logStr = logStr .. "Stored Unit Data: ID=" .. params.unitID .. ", Name=" .. params.unitName .. ", Type=" .. params.unitType .. ", Position=" .. params.unitPosition .. ", Life=" .. params.unitLife .. "\n"
|
|
3143
|
+
logStr = logStr .. "Processing HIT event for unit " .. params.unitName .. " (ID: " .. params.unitID .. ") with life " .. params.unitLife .. "\n"
|
|
3144
|
+
|
|
3145
|
+
--Store in LogEventProcessedUnitTable
|
|
3146
|
+
LogEventProcessedUnitTable[params.unitID] = {
|
|
3147
|
+
id = params.unitID,
|
|
3148
|
+
name = params.unitName,
|
|
3149
|
+
type = params.unitType,
|
|
3150
|
+
position = params.unitPosition,
|
|
3151
|
+
life = params.unitLife,
|
|
3152
|
+
event = params.eventName,
|
|
3153
|
+
time = params.timestamp
|
|
3154
|
+
}
|
|
3155
|
+
else
|
|
3156
|
+
--logStr = logStr .. "Unit Data Not Available: One or more fields unavailable\n"
|
|
3157
|
+
end
|
|
3158
|
+
|
|
3159
|
+
if splash_damage_options.events_debug then
|
|
3160
|
+
env.info(logStr)
|
|
3161
|
+
end
|
|
3162
|
+
end, hitData, timer.getTime() + 0.15)
|
|
3163
|
+
|
|
3164
|
+
LogEventProcessedUnitTable[unitID].timerID = timerID
|
|
3165
|
+
return --Exit early to queue the hit
|
|
3166
|
+
elseif eventName == "HIT" then
|
|
3167
|
+
--logStr = logStr .. "Unit Data Not Available: One or more fields unavailable\n"
|
|
3168
|
+
if splash_damage_options.events_debug then
|
|
3169
|
+
env.info(logStr)
|
|
3170
|
+
end
|
|
3171
|
+
return
|
|
3172
|
+
end
|
|
3173
|
+
|
|
3174
|
+
--Check if unitID is already processed for KILL
|
|
3175
|
+
if unitID and LogEventProcessedUnitTable[unitID] and eventName == "KILL" then
|
|
3176
|
+
logStr = logStr .. "Unit ID " .. unitID .. " already processed in LogEventProcessedUnitTable\n"
|
|
3177
|
+
if splash_damage_options.events_debug then
|
|
3178
|
+
env.info(logStr)
|
|
3179
|
+
end
|
|
3180
|
+
return
|
|
3181
|
+
end
|
|
3182
|
+
|
|
3183
|
+
--Log and store unit data for KILL if valid
|
|
3184
|
+
if eventName == "KILL" and unitID ~= "unavailable" and unitName ~= "unavailable" and unitType ~= "unavailable" and unitPosition ~= "unavailable" and unitLife ~= "unavailable" then
|
|
3185
|
+
logStr = logStr .. "Stored Unit Data: ID=" .. unitID .. ", Name=" .. unitName .. ", Type=" .. unitType .. ", Position=" .. unitPosition .. ", Life=" .. unitLife .. "\n"
|
|
3186
|
+
logStr = logStr .. "Processing KILL event for unit " .. unitName .. " (ID: " .. unitID .. ") of type " .. unitType .. "\n"
|
|
3187
|
+
|
|
3188
|
+
--Store in LogEventProcessedUnitTable
|
|
3189
|
+
LogEventProcessedUnitTable[unitID] = {
|
|
3190
|
+
id = unitID,
|
|
3191
|
+
name = unitName,
|
|
3192
|
+
type = unitType,
|
|
3193
|
+
position = unitPosition,
|
|
3194
|
+
life = unitLife,
|
|
3195
|
+
event = eventName,
|
|
3196
|
+
time = timer.getTime()
|
|
3197
|
+
}
|
|
3198
|
+
elseif eventName == "KILL" then
|
|
3199
|
+
--logStr = logStr .. "Unit Data Not Available: One or more fields unavailable\n"
|
|
3200
|
+
end
|
|
3201
|
+
|
|
3202
|
+
if splash_damage_options.events_debug then
|
|
3203
|
+
env.info(logStr)
|
|
3204
|
+
end
|
|
3205
|
+
end
|
|
3206
|
+
|
|
3207
|
+
|
|
3208
|
+
|
|
3209
|
+
function WpnHandler:onEvent(event)
|
|
3210
|
+
protectedCall(onWpnEvent, event)
|
|
3211
|
+
if event.id == world.event.S_EVENT_HIT then
|
|
3212
|
+
logEvent("HIT", event)
|
|
3213
|
+
elseif event.id == world.event.S_EVENT_KILL then
|
|
3214
|
+
logEvent("KILL", event)
|
|
3215
|
+
protectedCall(onKillEvent, event)
|
|
3216
|
+
elseif event.id == world.event.S_EVENT_DEAD then
|
|
3217
|
+
logEvent("DEAD", event)
|
|
3218
|
+
end
|
|
3219
|
+
end
|
|
3220
|
+
|
|
3221
|
+
--kill feed event function
|
|
3222
|
+
function onKillEvent(event)
|
|
3223
|
+
if not splash_damage_options.killfeed_enable or event.id ~= world.event.S_EVENT_KILL then return end
|
|
3224
|
+
|
|
3225
|
+
local status, err = pcall(function()
|
|
3226
|
+
local killedUnit = event.target
|
|
3227
|
+
local killer = event.initiator
|
|
3228
|
+
|
|
3229
|
+
if not killedUnit then
|
|
3230
|
+
if splash_damage_options.killfeed_debug then
|
|
3231
|
+
env.info(string.format("KillFeed: Skipped, no target at %.2f", timer.getTime()))
|
|
3232
|
+
end
|
|
3233
|
+
return
|
|
3234
|
+
end
|
|
3235
|
+
|
|
3236
|
+
local unitName = safeGet(function() return killedUnit:getName() end, "unknown")
|
|
3237
|
+
local unitType = safeGet(function() return killedUnit:getTypeName() end, "unknown")
|
|
3238
|
+
local unitID = safeGet(function() return killedUnit:getID() end, "unavailable")
|
|
3239
|
+
local position = safeGet(function()
|
|
3240
|
+
local pos = killedUnit:getPoint()
|
|
3241
|
+
return {x = pos.x, y = pos.y, z = pos.z}
|
|
3242
|
+
end, {x=0, y=0, z=0})
|
|
3243
|
+
|
|
3244
|
+
if unitName == "unknown" or unitType == "unknown" or unitID == "unavailable" or unitID == 0 then
|
|
3245
|
+
if splash_damage_options.killfeed_debug then
|
|
3246
|
+
--env.info(string.format("KillFeed: Skipped unit ID %s with name %s and type %s at %.2f", tostring(unitID), unitName, unitType, timer.getTime()))
|
|
3247
|
+
end
|
|
3248
|
+
return
|
|
3249
|
+
end
|
|
3250
|
+
|
|
3251
|
+
-- Check if unitID is already in killfeedTable
|
|
3252
|
+
for _, entry in ipairs(killfeedTable) do
|
|
3253
|
+
if entry.unitID == unitID then
|
|
3254
|
+
if splash_damage_options.killfeed_debug then
|
|
3255
|
+
env.info(string.format("KillFeed: Skipped unit ID %s (%s) already in killfeedTable at %.2f", unitID, unitType, timer.getTime()))
|
|
3256
|
+
end
|
|
3257
|
+
return
|
|
3258
|
+
end
|
|
3259
|
+
end
|
|
3260
|
+
|
|
3261
|
+
local killerName = "Unknown"
|
|
3262
|
+
local killerUnitName = "Unknown"
|
|
3263
|
+
if killer then
|
|
3264
|
+
local status, unitNameResult = pcall(function() return killer:getName() end)
|
|
3265
|
+
if status and unitNameResult then
|
|
3266
|
+
killerUnitName = unitNameResult
|
|
3267
|
+
end
|
|
3268
|
+
local status, playerNameResult = pcall(function() return killer:getPlayerName() end)
|
|
3269
|
+
if status and playerNameResult then
|
|
3270
|
+
killerName = playerNameResult
|
|
3271
|
+
else
|
|
3272
|
+
local status, unitId = pcall(function() return killer:getID() end)
|
|
3273
|
+
if status and unitId then
|
|
3274
|
+
local playerList = net.get_player_list() or {}
|
|
3275
|
+
for _, pid in ipairs(playerList) do
|
|
3276
|
+
local pinfo = net.get_player_info(pid)
|
|
3277
|
+
if pinfo and pinfo.ucid then
|
|
3278
|
+
local slotUnitId = tonumber(pinfo.slot) or pinfo.slot
|
|
3279
|
+
if slotUnitId == unitId or pinfo.slot == killerUnitName then
|
|
3280
|
+
killerName = pinfo.name or killerUnitName
|
|
3281
|
+
break
|
|
3282
|
+
end
|
|
3283
|
+
end
|
|
3284
|
+
end
|
|
3285
|
+
end
|
|
3286
|
+
end
|
|
3287
|
+
if splash_damage_options.killfeed_debug then
|
|
3288
|
+
env.info(string.format("KillFeed: Killer UnitName: %s, PlayerName: %s, UnitID: %s, Type: %s, Slot: %s",
|
|
3289
|
+
killerUnitName, killerName, unitID, unitType, killer.getID and killer:getID() or "unknown"))
|
|
3290
|
+
end
|
|
3291
|
+
elseif splash_damage_options.killfeed_debug then
|
|
3292
|
+
env.info(string.format("KillFeed: Unit ID %s (%s) killed with no initiator at %.2f",
|
|
3293
|
+
unitID, unitType, timer.getTime()))
|
|
3294
|
+
end
|
|
3295
|
+
|
|
3296
|
+
--Log bc table state for direct kill only if Lekas integration is enabled
|
|
3297
|
+
if splash_damage_options.killfeed_debug and splash_damage_options.killfeed_lekas_foothold_integration then
|
|
3298
|
+
env.info("KillFeed: bc table state for direct kill: " .. (bc and "exists" or "nil"))
|
|
3299
|
+
env.info("KillFeed: bc.addTempStat: " .. (bc and bc.addTempStat and "exists" or "nil"))
|
|
3300
|
+
env.info("KillFeed: bc.context: " .. (bc and bc.context and "exists" or "nil"))
|
|
3301
|
+
if bc and bc.context then
|
|
3302
|
+
env.info("KillFeed: bc.context.playerContributions: " .. (bc.context.playerContributions and "exists" or "nil"))
|
|
3303
|
+
if bc.context.playerContributions then
|
|
3304
|
+
env.info("KillFeed: bc.context.playerContributions[2]: " .. (bc.context.playerContributions[2] and "exists" or "nil"))
|
|
3305
|
+
end
|
|
3306
|
+
end
|
|
3307
|
+
end
|
|
3308
|
+
|
|
3309
|
+
--Check if unitID is in splashKillfeedTable
|
|
3310
|
+
local splashIndex = nil
|
|
3311
|
+
for i, entry in ipairs(splashKillfeedTable) do
|
|
3312
|
+
if entry.unitId == unitID then
|
|
3313
|
+
splashIndex = i
|
|
3314
|
+
break
|
|
3315
|
+
end
|
|
3316
|
+
end
|
|
3317
|
+
if splashIndex then
|
|
3318
|
+
local dupeMsg = string.format("Duplicate kill: %s (%s) [ID: %s]", unitName, unitType, unitID)
|
|
3319
|
+
if splash_damage_options.killfeed_game_messages then
|
|
3320
|
+
local status, err = pcall(function()
|
|
3321
|
+
trigger.action.outTextForCoalition(2, dupeMsg, splash_damage_options.killfeed_game_message_duration)
|
|
3322
|
+
end)
|
|
3323
|
+
if not status then
|
|
3324
|
+
trigger.action.outText(dupeMsg, splash_damage_options.killfeed_game_message_duration)
|
|
3325
|
+
if splash_damage_options.killfeed_debug then
|
|
3326
|
+
env.info("KillFeed: Failed coalition message for duplicate: " .. tostring(err))
|
|
3327
|
+
end
|
|
3328
|
+
end
|
|
3329
|
+
end
|
|
3330
|
+
if splash_damage_options.killfeed_debug then
|
|
3331
|
+
env.info(string.format("KillFeed: %s at %.2f", dupeMsg, timer.getTime()))
|
|
3332
|
+
end
|
|
3333
|
+
table.remove(splashKillfeedTable, splashIndex)
|
|
3334
|
+
if splash_damage_options.killfeed_debug then
|
|
3335
|
+
env.info(string.format("SplashKillFeed: Removed duplicate entry for unit ID %s (%s) from splashKillfeedTable at %.2f",
|
|
3336
|
+
unitID, unitType, timer.getTime()))
|
|
3337
|
+
end
|
|
3338
|
+
else
|
|
3339
|
+
--Process direct kill contribution
|
|
3340
|
+
if killerName ~= "Unknown" and splash_damage_options.killfeed_lekas_foothold_integration then
|
|
3341
|
+
local status, result = pcall(function()
|
|
3342
|
+
local statName = "Ground Units"
|
|
3343
|
+
local points = 10
|
|
3344
|
+
if unitType:find("Plane") then
|
|
3345
|
+
statName = "Air"
|
|
3346
|
+
points = 30
|
|
3347
|
+
elseif unitType:find("Helicopter") then
|
|
3348
|
+
statName = "Helo"
|
|
3349
|
+
points = 30
|
|
3350
|
+
elseif unitType:find("SAM") then
|
|
3351
|
+
statName = "SAM"
|
|
3352
|
+
points = 30
|
|
3353
|
+
elseif unitType:find("Infantry") then
|
|
3354
|
+
statName = "Infantry"
|
|
3355
|
+
points = 10
|
|
3356
|
+
elseif unitType:find("Ship") then
|
|
3357
|
+
statName = "Ship"
|
|
3358
|
+
points = 250
|
|
3359
|
+
elseif unitType:find("Building") then
|
|
3360
|
+
statName = "Structure"
|
|
3361
|
+
points = 30
|
|
3362
|
+
end
|
|
3363
|
+
bc:addTempStat(killerName, statName, 1)
|
|
3364
|
+
if splash_damage_options.killfeed_debug then
|
|
3365
|
+
env.info(string.format("KillFeed: Added temp stat for %s: stat=%s, count=1", killerName, statName))
|
|
3366
|
+
end
|
|
3367
|
+
if bc.context and type(bc.context) == "table" and bc.context.playerContributions and type(bc.context.playerContributions) == "table" then
|
|
3368
|
+
bc.context.playerContributions[2] = bc.context.playerContributions[2] or {}
|
|
3369
|
+
local oldPoints = bc.context.playerContributions[2][killerName] or 0
|
|
3370
|
+
bc.context.playerContributions[2][killerName] = oldPoints + points
|
|
3371
|
+
if splash_damage_options.killfeed_debug then
|
|
3372
|
+
env.info(string.format("KillFeed: Updated contributions for %s: old=%d, new=%d, added=%d",
|
|
3373
|
+
killerName, oldPoints, bc.context.playerContributions[2][killerName], points))
|
|
3374
|
+
end
|
|
3375
|
+
else
|
|
3376
|
+
if splash_damage_options.killfeed_debug then
|
|
3377
|
+
env.info("KillFeed: Skipped contribution update for " .. killerName .. ": bc.context or bc.context.playerContributions is nil")
|
|
3378
|
+
end
|
|
3379
|
+
end
|
|
3380
|
+
end)
|
|
3381
|
+
if not status and splash_damage_options.killfeed_debug then
|
|
3382
|
+
env.info("KillFeed: Error processing direct kill for unitId=" .. tostring(unitID) .. ": " .. tostring(result))
|
|
3383
|
+
end
|
|
3384
|
+
end
|
|
3385
|
+
end
|
|
3386
|
+
|
|
3387
|
+
if unitType ~= "Unknown" then
|
|
3388
|
+
table.insert(killfeedTable, {
|
|
3389
|
+
unitName = unitName,
|
|
3390
|
+
unitType = unitType,
|
|
3391
|
+
unitID = unitID,
|
|
3392
|
+
killer = killerName,
|
|
3393
|
+
time = timer.getTime(),
|
|
3394
|
+
position = position
|
|
3395
|
+
})
|
|
3396
|
+
|
|
3397
|
+
if splash_damage_options.killfeed_game_messages and not splashIndex then
|
|
3398
|
+
local msg = string.format("%s destroyed by %s", unitType, killerName)
|
|
3399
|
+
local status, err = pcall(function()
|
|
3400
|
+
trigger.action.outTextForCoalition(2, msg, splash_damage_options.killfeed_game_message_duration)
|
|
3401
|
+
end)
|
|
3402
|
+
|
|
3403
|
+
if not status then
|
|
3404
|
+
trigger.action.outText(msg, splash_damage_options.killfeed_game_message_duration)
|
|
3405
|
+
if splash_damage_options.killfeed_debug then
|
|
3406
|
+
env.info("KillFeed: Failed coalition message: " .. tostring(err))
|
|
3407
|
+
end
|
|
3408
|
+
end
|
|
3409
|
+
end
|
|
3410
|
+
|
|
3411
|
+
if splash_damage_options.killfeed_debug then
|
|
3412
|
+
env.info(string.format("KillFeed: Recorded %s destroyed by %s [ID: %s] at %.2f",
|
|
3413
|
+
unitType, killerName, unitID, timer.getTime()))
|
|
3414
|
+
end
|
|
3415
|
+
end
|
|
3416
|
+
end)
|
|
3417
|
+
|
|
3418
|
+
if not status and splash_damage_options.killfeed_debug then
|
|
3419
|
+
env.info("KillFeed: Error: " .. tostring(err))
|
|
3420
|
+
end
|
|
3421
|
+
end
|
|
3422
|
+
|
|
3423
|
+
|
|
3424
|
+
--kill feed event function
|
|
3425
|
+
--kill feed event function
|
|
3426
|
+
function onDeadEvent(event)
|
|
3427
|
+
if not splash_damage_options.killfeed_enable or event.id ~= world.event.S_EVENT_DEAD then return end
|
|
3428
|
+
|
|
3429
|
+
local status, err = pcall(function()
|
|
3430
|
+
local deadUnit = event.initiator
|
|
3431
|
+
|
|
3432
|
+
if not deadUnit then
|
|
3433
|
+
if splash_damage_options.killfeed_debug then
|
|
3434
|
+
env.info(string.format("DeadFeed: Skipped, no initiator at %.2f", timer.getTime()))
|
|
3435
|
+
end
|
|
3436
|
+
return
|
|
3437
|
+
end
|
|
3438
|
+
|
|
3439
|
+
--Extract unit data using safeGet, matching logEvent defaults
|
|
3440
|
+
local unitID = safeGet(function() return deadUnit:getID() end, "unavailable")
|
|
3441
|
+
local unitName = safeGet(function() return deadUnit:getName() end, "unknown")
|
|
3442
|
+
local unitType = safeGet(function() return deadUnit:getTypeName() end, "unknown")
|
|
3443
|
+
local position = safeGet(function()
|
|
3444
|
+
local pos = deadUnit:getPoint()
|
|
3445
|
+
return {x = pos.x, y = pos.y, z = pos.z}
|
|
3446
|
+
end, {x=0, y=0, z=0})
|
|
3447
|
+
|
|
3448
|
+
--Skip invalid units (unknown type, unavailable ID, or scenery with ID 0)
|
|
3449
|
+
if unitName == "unknown" or unitType == "unknown" or unitID == "unavailable" or unitID == 0 then
|
|
3450
|
+
if splash_damage_options.killfeed_debug then
|
|
3451
|
+
env.info(string.format("DeadFeed: Skipped unit ID %s with name %s and type %s at %.2f", tostring(unitID), unitName, unitType, timer.getTime()))
|
|
3452
|
+
end
|
|
3453
|
+
return
|
|
3454
|
+
end
|
|
3455
|
+
|
|
3456
|
+
-- Check if unitID is already in killfeedTable before scheduling
|
|
3457
|
+
for _, entry in ipairs(killfeedTable) do
|
|
3458
|
+
if entry.unitID == unitID then
|
|
3459
|
+
if splash_damage_options.killfeed_debug then
|
|
3460
|
+
env.info(string.format("DeadFeed: Skipped unit ID %s (%s) already in killfeedTable at %.2f", unitID, unitType, timer.getTime()))
|
|
3461
|
+
end
|
|
3462
|
+
return
|
|
3463
|
+
end
|
|
3464
|
+
end
|
|
3465
|
+
|
|
3466
|
+
--Delay processing by 2 seconds to allow S_EVENT_KILL to take precedence
|
|
3467
|
+
timer.scheduleFunction(function(params)
|
|
3468
|
+
local unitID = params.unitID
|
|
3469
|
+
local unitName = params.unitName
|
|
3470
|
+
local unitType = params.unitType
|
|
3471
|
+
local position = params.position
|
|
3472
|
+
local currentTime = timer.getTime()
|
|
3473
|
+
|
|
3474
|
+
-- Re-check killfeedTable after delay to ensure no race condition
|
|
3475
|
+
for _, entry in ipairs(killfeedTable) do
|
|
3476
|
+
if entry.unitID == unitID then
|
|
3477
|
+
if splash_damage_options.killfeed_debug then
|
|
3478
|
+
env.info(string.format("DeadFeed: Skipped unit ID %s (%s) already in killfeedTable at %.2f", unitID, unitType, currentTime))
|
|
3479
|
+
end
|
|
3480
|
+
return
|
|
3481
|
+
end
|
|
3482
|
+
end
|
|
3483
|
+
|
|
3484
|
+
--Remove from splashKillfeedTable if present
|
|
3485
|
+
local splashIndex = nil
|
|
3486
|
+
for i, entry in ipairs(splashKillfeedTable) do
|
|
3487
|
+
if entry.unitId == unitID then
|
|
3488
|
+
splashIndex = i
|
|
3489
|
+
break
|
|
3490
|
+
end
|
|
3491
|
+
end
|
|
3492
|
+
if splashIndex then
|
|
3493
|
+
table.remove(splashKillfeedTable, splashIndex)
|
|
3494
|
+
if splash_damage_options.killfeed_debug then
|
|
3495
|
+
env.info(string.format("DeadFeed: Removed unit ID %s (%s) from splashKillfeedTable at %.2f", unitID, unitType, currentTime))
|
|
3496
|
+
end
|
|
3497
|
+
end
|
|
3498
|
+
|
|
3499
|
+
--Remove from splashKillfeedTemp if present
|
|
3500
|
+
local tempIndex = nil
|
|
3501
|
+
for i, entry in ipairs(splashKillfeedTemp) do
|
|
3502
|
+
if entry.unitId == unitID then
|
|
3503
|
+
tempIndex = i
|
|
3504
|
+
break
|
|
3505
|
+
end
|
|
3506
|
+
end
|
|
3507
|
+
if tempIndex then
|
|
3508
|
+
table.remove(splashKillfeedTemp, tempIndex)
|
|
3509
|
+
if splash_damage_options.killfeed_debug then
|
|
3510
|
+
env.info(string.format("DeadFeed: Removed unit ID %s (%s) from splashKillfeedTemp at %.2f", unitID, unitType, currentTime))
|
|
3511
|
+
end
|
|
3512
|
+
end
|
|
3513
|
+
|
|
3514
|
+
--Add to killfeedTable
|
|
3515
|
+
table.insert(killfeedTable, {
|
|
3516
|
+
unitName = unitName,
|
|
3517
|
+
unitType = unitType,
|
|
3518
|
+
unitID = unitID,
|
|
3519
|
+
killer = "unknown",
|
|
3520
|
+
time = currentTime,
|
|
3521
|
+
position = position
|
|
3522
|
+
})
|
|
3523
|
+
|
|
3524
|
+
--Display in-game message
|
|
3525
|
+
if splash_damage_options.killfeed_game_messages then
|
|
3526
|
+
local msg = string.format("%s destroyed", unitType)
|
|
3527
|
+
local status, err = pcall(function()
|
|
3528
|
+
trigger.action.outTextForCoalition(2, msg, splash_damage_options.killfeed_game_message_duration)
|
|
3529
|
+
end)
|
|
3530
|
+
if not status then
|
|
3531
|
+
trigger.action.outText(msg, splash_damage_options.killfeed_game_message_duration)
|
|
3532
|
+
if splash_damage_options.killfeed_debug then
|
|
3533
|
+
env.info("DeadFeed: Failed coalition message: " .. tostring(err))
|
|
3534
|
+
end
|
|
3535
|
+
end
|
|
3536
|
+
end
|
|
3537
|
+
|
|
3538
|
+
if splash_damage_options.killfeed_debug then
|
|
3539
|
+
env.info(string.format("DeadFeed: Recorded %s destroyed [ID: %s] at %.2f", unitType, unitID, currentTime))
|
|
3540
|
+
end
|
|
3541
|
+
end, {
|
|
3542
|
+
unitID = unitID,
|
|
3543
|
+
unitName = unitName,
|
|
3544
|
+
unitType = unitType,
|
|
3545
|
+
position = position
|
|
3546
|
+
}, timer.getTime() + 2)
|
|
3547
|
+
end)
|
|
3548
|
+
|
|
3549
|
+
if not status and splash_damage_options.killfeed_debug then
|
|
3550
|
+
env.info("DeadFeed: Error: " .. tostring(err))
|
|
3551
|
+
end
|
|
3552
|
+
end
|
|
3553
|
+
|
|
3554
|
+
function explodeObject(args)
|
|
3555
|
+
local point = args[1]
|
|
3556
|
+
local distance = args[2]
|
|
3557
|
+
local power = args[3]
|
|
3558
|
+
trigger.action.explosion(point, power)
|
|
3559
|
+
end
|
|
3560
|
+
|
|
3561
|
+
function blastWave(_point, _radius, weapon, power, isShapedCharge)
|
|
3562
|
+
if isShapedCharge then
|
|
3563
|
+
_radius = _radius * splash_damage_options.shaped_charge_multiplier
|
|
3564
|
+
end
|
|
3565
|
+
if splash_damage_options.use_dynamic_blast_radius then
|
|
3566
|
+
local dynamicRadius = math.pow(power, 1/3) * 5 * splash_damage_options.dynamic_blast_radius_modifier
|
|
3567
|
+
_radius = isShapedCharge and dynamicRadius * splash_damage_options.shaped_charge_multiplier or dynamicRadius
|
|
3568
|
+
end
|
|
3569
|
+
if splash_damage_options.debug then
|
|
3570
|
+
debugMsg("blastWave called for weapon '" .. weapon .. "' at X: " .. _point.x .. ", Y: " .. _point.y .. ", Z: " .. _point.z .. " with power " .. power .. " and radius " .. _radius .. "m")
|
|
3571
|
+
end
|
|
3572
|
+
|
|
3573
|
+
local foundUnits = {}
|
|
3574
|
+
local volS = {
|
|
3575
|
+
id = world.VolumeType.SPHERE,
|
|
3576
|
+
params = {
|
|
3577
|
+
point = _point,
|
|
3578
|
+
radius = _radius
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
local ifFound = function(foundObject, val)
|
|
3583
|
+
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT and foundObject:getCategory() == Object.Category.UNIT then
|
|
3584
|
+
foundUnits[#foundUnits + 1] = foundObject
|
|
3585
|
+
end
|
|
3586
|
+
if foundObject:getDesc().category == Unit.Category.GROUND_UNIT and splash_damage_options.blast_stun then
|
|
3587
|
+
--suppressUnit(foundObject, 2, weapon) --Not implemented, commented out
|
|
3588
|
+
end
|
|
3589
|
+
if splash_damage_options.wave_explosions then
|
|
3590
|
+
local obj = foundObject
|
|
3591
|
+
local obj_location = obj:getPoint()
|
|
3592
|
+
local dist = getDistance(_point, obj_location)
|
|
3593
|
+
if dist > 1 then --Avoid re-exploding at exact impact point
|
|
3594
|
+
local timing = dist / 500
|
|
3595
|
+
if obj:isExist() and tableHasKey(obj:getDesc(), "box") then
|
|
3596
|
+
local length = (obj:getDesc().box.max.x + math.abs(obj:getDesc().box.min.x))
|
|
3597
|
+
local height = (obj:getDesc().box.max.y + math.abs(obj:getDesc().box.min.y))
|
|
3598
|
+
local depth = (obj:getDesc().box.max.z + math.abs(obj:getDesc().box.min.z))
|
|
3599
|
+
local _length = length
|
|
3600
|
+
local _depth = depth
|
|
3601
|
+
if depth > length then
|
|
3602
|
+
_length = depth
|
|
3603
|
+
_depth = length
|
|
3604
|
+
end
|
|
3605
|
+
local surface_distance = dist - _depth / 2
|
|
3606
|
+
local scaled_power_factor = 0.006 * power + 1
|
|
3607
|
+
local intensity = (power * scaled_power_factor) / (4 * math.pi * surface_distance^2)
|
|
3608
|
+
--Apply ground ordnance blastwave modifier
|
|
3609
|
+
local weaponData = explTable[weapon] or {}
|
|
3610
|
+
if splash_damage_options.track_groundunitordnance and weaponData.groundordnance then
|
|
3611
|
+
intensity = intensity * splash_damage_options.groundunitordnance_blastwave_modifier
|
|
3612
|
+
if splash_damage_options.track_groundunitordnance_debug then
|
|
3613
|
+
debugMsg("Applied groundunitordnance_blastwave_modifier " .. splash_damage_options.groundunitordnance_blastwave_modifier .. " to " .. weapon .. ", intensity now: " .. intensity)
|
|
3614
|
+
end
|
|
3615
|
+
end
|
|
3616
|
+
local surface_area = _length * height
|
|
3617
|
+
local damage_for_surface = intensity * surface_area
|
|
3618
|
+
if splash_damage_options.debug then
|
|
3619
|
+
debugMsg("Processing unit '" .. obj:getTypeName() .. "' at dist=" .. string.format("%.1f", dist) .. "m: intensity=" .. string.format("%.4f", intensity) .. ", surface_area=" .. string.format("%.2f", surface_area) .. ", damage_for_surface=" .. string.format("%.4f", damage_for_surface))
|
|
3620
|
+
end
|
|
3621
|
+
if damage_for_surface > splash_damage_options.cascade_damage_threshold then
|
|
3622
|
+
local explosion_size = damage_for_surface
|
|
3623
|
+
if obj:getDesc().category == Unit.Category.STRUCTURE then
|
|
3624
|
+
explosion_size = intensity * splash_damage_options.static_damage_boost
|
|
3625
|
+
end
|
|
3626
|
+
if explosion_size > power then explosion_size = power end
|
|
3627
|
+
local triggerExplosion = false
|
|
3628
|
+
if splash_damage_options.always_cascade_explode then
|
|
3629
|
+
triggerExplosion = true
|
|
3630
|
+
if splash_damage_options.debug then
|
|
3631
|
+
debugMsg("Triggering secondary explosion for '" .. obj:getTypeName() .. "' due to always_cascade_explode")
|
|
3632
|
+
end
|
|
3633
|
+
else
|
|
3634
|
+
if obj:getDesc().life then
|
|
3635
|
+
local health = obj:getLife() or 0
|
|
3636
|
+
local maxHealth = obj:getDesc().life or 1
|
|
3637
|
+
local healthPercent = (health / maxHealth) * 100
|
|
3638
|
+
if splash_damage_options.debug then
|
|
3639
|
+
debugMsg("Health check for '" .. obj:getTypeName() .. "': " .. health .. "/" .. maxHealth .. " (" .. string.format("%.2f", healthPercent) .. "%) vs threshold " .. splash_damage_options.cascade_explode_threshold)
|
|
3640
|
+
end
|
|
3641
|
+
if healthPercent <= splash_damage_options.cascade_explode_threshold then
|
|
3642
|
+
triggerExplosion = true
|
|
3643
|
+
end
|
|
3644
|
+
else
|
|
3645
|
+
triggerExplosion = true
|
|
3646
|
+
if splash_damage_options.debug then
|
|
3647
|
+
debugMsg("Triggering secondary explosion for '" .. obj:getTypeName() .. "' (no life data)")
|
|
3648
|
+
end
|
|
3649
|
+
end
|
|
3650
|
+
if not triggerExplosion and obj:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
3651
|
+
local health = obj:getLife() or 0
|
|
3652
|
+
if health <= 0 then
|
|
3653
|
+
triggerExplosion = true
|
|
3654
|
+
if splash_damage_options.debug then
|
|
3655
|
+
debugMsg("Triggering secondary explosion for '" .. obj:getTypeName() .. "' (health <= 0)")
|
|
3656
|
+
end
|
|
3657
|
+
end
|
|
3658
|
+
end
|
|
3659
|
+
end
|
|
3660
|
+
--Queue cargo effects for units below
|
|
3661
|
+
if obj:getDesc().life then
|
|
3662
|
+
local healthPercent = (obj:getLife() / obj:getDesc().life) * 100
|
|
3663
|
+
local cargoData = cargoUnits[obj:getTypeName()]
|
|
3664
|
+
if cargoData and healthPercent <= splash_damage_options.cargo_damage_threshold and splash_damage_options.enable_cargo_effects then
|
|
3665
|
+
local cargoPower = power * cargoData.cargoExplosionMult
|
|
3666
|
+
table.insert(cargoEffectsQueue, {
|
|
3667
|
+
name = obj:getTypeName(),
|
|
3668
|
+
distance = dist,
|
|
3669
|
+
coords = obj_location,
|
|
3670
|
+
power = cargoPower,
|
|
3671
|
+
explosion = cargoData.cargoExplosion,
|
|
3672
|
+
cookOff = cargoData.cargoCookOff,
|
|
3673
|
+
cookOffCount = cargoData.cookOffCount,
|
|
3674
|
+
cookOffPower = cargoData.cookOffPower,
|
|
3675
|
+
cookOffDuration = cargoData.cookOffDuration,
|
|
3676
|
+
cookOffRandomTiming = cargoData.cookOffRandomTiming,
|
|
3677
|
+
cookOffPowerRandom = cargoData.cookOffPowerRandom,
|
|
3678
|
+
isTanker = cargoData.isTanker,
|
|
3679
|
+
flameSize = cargoData.flameSize,
|
|
3680
|
+
flameDuration = cargoData.flameDuration
|
|
3681
|
+
})
|
|
3682
|
+
if splash_damage_options.debug then
|
|
3683
|
+
debugMsg("Queued cargo effect for '" .. obj:getTypeName() .. "' with power " .. cargoPower)
|
|
3684
|
+
end
|
|
3685
|
+
end
|
|
3686
|
+
end
|
|
3687
|
+
if triggerExplosion then
|
|
3688
|
+
local final_power = explosion_size * splash_damage_options.cascade_scaling
|
|
3689
|
+
if splash_damage_options.debug then
|
|
3690
|
+
debugMsg("Scheduling secondary explosion for '" .. obj:getTypeName() .. "' at X: " .. obj_location.x .. ", Y: " .. obj_location.y .. ", Z: " .. obj_location.z .. ", dist=" .. string.format("%.1f", dist) .. "m, power=" .. string.format("%.2f", final_power))
|
|
3691
|
+
end
|
|
3692
|
+
if splash_damage_options.track_groundunitordnance_debug and weaponData.groundordnance then
|
|
3693
|
+
debugMsg("Calculated power for '" .. obj:getTypeName() .. "' at X: " .. obj_location.x .. ", Y: " .. obj_location.y .. ", Z: " .. obj_location.z .. ", distance " .. dist .. "m: " .. final_power)
|
|
3694
|
+
end
|
|
3695
|
+
local playerName = tracked_weapons[weapon] and tracked_weapons[weapon].init or "unknown"
|
|
3696
|
+
timer.scheduleFunction(function(args)
|
|
3697
|
+
local obj = args[1]
|
|
3698
|
+
local playerName = args[2]
|
|
3699
|
+
if obj:isExist() and obj:getLife() <= 0 then
|
|
3700
|
+
debugMsg("Unit '" .. obj:getTypeName() .. "' destroyed by secondary explosion, credited to player: " .. playerName)
|
|
3701
|
+
end
|
|
3702
|
+
end, {obj, playerName}, timer.getTime() + timing + 0.1)
|
|
3703
|
+
timer.scheduleFunction(explodeObject, {obj_location, dist, final_power}, timer.getTime() + timing)
|
|
3704
|
+
else
|
|
3705
|
+
if splash_damage_options.debug then
|
|
3706
|
+
debugMsg("No secondary explosion for '" .. obj:getTypeName() .. "': health above threshold (" .. string.format("%.2f", (obj:getLife() / obj:getDesc().life) * 100) .. "% > " .. splash_damage_options.cascade_explode_threshold .. "%)")
|
|
3707
|
+
end
|
|
3708
|
+
end
|
|
3709
|
+
else
|
|
3710
|
+
if splash_damage_options.debug then
|
|
3711
|
+
debugMsg("No secondary explosion for '" .. obj:getTypeName() .. "': damage_for_surface=" .. string.format("%.4f", damage_for_surface) .. " below threshold " .. splash_damage_options.cascade_damage_threshold)
|
|
3712
|
+
end
|
|
3713
|
+
end
|
|
3714
|
+
end
|
|
3715
|
+
end
|
|
3716
|
+
end
|
|
3717
|
+
return true
|
|
3718
|
+
end
|
|
3719
|
+
|
|
3720
|
+
--Search all relevant object categories
|
|
3721
|
+
if splash_damage_options.debug then
|
|
3722
|
+
debugMsg("Scanning for objects within " .. _radius .. "m radius")
|
|
3723
|
+
end
|
|
3724
|
+
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
|
3725
|
+
world.searchObjects(Object.Category.STATIC, volS, ifFound)
|
|
3726
|
+
world.searchObjects(Object.Category.SCENERY, volS, ifFound)
|
|
3727
|
+
world.searchObjects(Object.Category.CARGO, volS, ifFound)
|
|
3728
|
+
if splash_damage_options.debug then
|
|
3729
|
+
debugMsg("Found " .. #foundUnits .. " ground units for damage modeling")
|
|
3730
|
+
end
|
|
3731
|
+
--Apply damage model if enabled
|
|
3732
|
+
if splash_damage_options.damage_model then
|
|
3733
|
+
timer.scheduleFunction(modelUnitDamage, foundUnits, timer.getTime() + 1.5)
|
|
3734
|
+
end
|
|
3735
|
+
end
|
|
3736
|
+
|
|
3737
|
+
function modelUnitDamage(units)
|
|
3738
|
+
for i, unit in ipairs(units) do
|
|
3739
|
+
if unit:isExist() then
|
|
3740
|
+
local health = (unit:getLife() / unit:getDesc().life) * 100
|
|
3741
|
+
if unit:hasAttribute("Infantry") and health > 0 then
|
|
3742
|
+
if health <= splash_damage_options.infantry_cant_fire_health then
|
|
3743
|
+
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
3744
|
+
end
|
|
3745
|
+
end
|
|
3746
|
+
if unit:getDesc().category == Unit.Category.GROUND_UNIT and (not unit:hasAttribute("Infantry")) and health > 0 then
|
|
3747
|
+
if health <= splash_damage_options.unit_cant_fire_health then
|
|
3748
|
+
unit:getController():setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
|
|
3749
|
+
--gameMsg(unit:getTypeName() .. " weapons disabled")
|
|
3750
|
+
end
|
|
3751
|
+
if health <= splash_damage_options.unit_disabled_health and health > 0 then
|
|
3752
|
+
unit:getController():setTask({id = 'Hold', params = {}})
|
|
3753
|
+
unit:getController():setOnOff(false)
|
|
3754
|
+
--gameMsg(unit:getTypeName() .. " disabled")
|
|
3755
|
+
end
|
|
3756
|
+
end
|
|
3757
|
+
end
|
|
3758
|
+
end
|
|
3759
|
+
end
|
|
3760
|
+
|
|
3761
|
+
function updateSplashDamageSetting(setting, increment)
|
|
3762
|
+
if not splash_damage_options[setting] then
|
|
3763
|
+
env.info("Error: Setting " .. setting .. " does not exist.")
|
|
3764
|
+
return
|
|
3765
|
+
end
|
|
3766
|
+
|
|
3767
|
+
local newValue = math.max(0, splash_damage_options[setting] + increment)
|
|
3768
|
+
env.info("Updating " .. setting .. " from " .. tostring(splash_damage_options[setting]) .. " to " .. tostring(newValue))
|
|
3769
|
+
splash_damage_options[setting] = newValue
|
|
3770
|
+
trigger.action.outText("Updated " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
3771
|
+
end
|
|
3772
|
+
|
|
3773
|
+
function toggleSplashDamageSetting(setting)
|
|
3774
|
+
splash_damage_options[setting] = not splash_damage_options[setting]
|
|
3775
|
+
trigger.action.outText("Toggled " .. setting .. " to: " .. tostring(splash_damage_options[setting]), 5)
|
|
3776
|
+
|
|
3777
|
+
if setting == "enable_radio_menu" then
|
|
3778
|
+
if splash_damage_options.enable_radio_menu then
|
|
3779
|
+
addSplashDamageMenu()
|
|
3780
|
+
else
|
|
3781
|
+
missionCommands.removeItem(splash_damage_menu)
|
|
3782
|
+
splash_damage_menu = nil
|
|
3783
|
+
end
|
|
3784
|
+
end
|
|
3785
|
+
end
|
|
3786
|
+
|
|
3787
|
+
function addValueAdjustmentCommands(menu, setting, increments)
|
|
3788
|
+
for _, inc in ipairs(increments) do
|
|
3789
|
+
missionCommands.addCommand("+" .. inc, menu, updateSplashDamageSetting, setting, inc)
|
|
3790
|
+
missionCommands.addCommand("-" .. inc, menu, updateSplashDamageSetting, setting, -inc)
|
|
3791
|
+
end
|
|
3792
|
+
end
|
|
1971
3793
|
|
|
1972
3794
|
function exitSplashDamageMenu()
|
|
1973
3795
|
if splash_damage_menu then
|
|
@@ -1985,129 +3807,305 @@ function addSplashDamageMenu()
|
|
|
1985
3807
|
|
|
1986
3808
|
splash_damage_menu = missionCommands.addSubMenu("Splash Damage Settings")
|
|
1987
3809
|
|
|
1988
|
-
--
|
|
1989
|
-
local
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
local
|
|
2014
|
-
addValueAdjustmentCommands(
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
missionCommands.
|
|
2033
|
-
missionCommands.
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
missionCommands.
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
missionCommands.
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
missionCommands.
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
local
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
local
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
for
|
|
2082
|
-
local
|
|
2083
|
-
|
|
2084
|
-
|
|
3810
|
+
--1. Debug and Messages
|
|
3811
|
+
local debugMenu = missionCommands.addSubMenu("Debug and Messages", splash_damage_menu)
|
|
3812
|
+
local debugSettings = {
|
|
3813
|
+
"game_messages",
|
|
3814
|
+
"debug",
|
|
3815
|
+
"weapon_missing_message",
|
|
3816
|
+
"track_pre_explosion_debug",
|
|
3817
|
+
"track_groundunitordnance_debug",
|
|
3818
|
+
"napalm_unitdamage_debug"
|
|
3819
|
+
}
|
|
3820
|
+
for _, setting in ipairs(debugSettings) do
|
|
3821
|
+
missionCommands.addCommand("Toggle " .. setting:gsub("_", " "), debugMenu, toggleSplashDamageSetting, setting)
|
|
3822
|
+
end
|
|
3823
|
+
|
|
3824
|
+
--2. Basic Splash Settings
|
|
3825
|
+
local splashMenu = missionCommands.addSubMenu("Basic Splash Settings", splash_damage_menu)
|
|
3826
|
+
local splashToggles = {
|
|
3827
|
+
"wave_explosions",
|
|
3828
|
+
"larger_explosions",
|
|
3829
|
+
"damage_model",
|
|
3830
|
+
"blast_stun"
|
|
3831
|
+
}
|
|
3832
|
+
for _, setting in ipairs(splashToggles) do
|
|
3833
|
+
missionCommands.addCommand("Toggle " .. setting:gsub("_", " "), splashMenu, toggleSplashDamageSetting, setting)
|
|
3834
|
+
end
|
|
3835
|
+
local staticDamageMenu = missionCommands.addSubMenu("Static Damage Boost", splashMenu)
|
|
3836
|
+
addValueAdjustmentCommands(staticDamageMenu, "static_damage_boost", {100, 500, 1000})
|
|
3837
|
+
|
|
3838
|
+
--Submenu: Scaling and Cascading
|
|
3839
|
+
local scalingMenu = missionCommands.addSubMenu("Scaling and Cascading", splashMenu)
|
|
3840
|
+
local scalingSettings = {
|
|
3841
|
+
{name = "Overall Scaling", setting = "overall_scaling", increments = {0.1, 0.5, 1}},
|
|
3842
|
+
{name = "Rocket Multiplier", setting = "rocket_multiplier", increments = {0.1, 0.5, 1}},
|
|
3843
|
+
{name = "Cascade Scaling", setting = "cascade_scaling", increments = {0.1, 0.5, 1}},
|
|
3844
|
+
{name = "Cascade Damage Threshold", setting = "cascade_damage_threshold", increments = {0.01, 0.05, 0.1}},
|
|
3845
|
+
{name = "Cascade Explode Threshold", setting = "cascade_explode_threshold", increments = {5, 10, 25}}
|
|
3846
|
+
}
|
|
3847
|
+
for _, s in ipairs(scalingSettings) do
|
|
3848
|
+
local subMenu = missionCommands.addSubMenu(s.name, scalingMenu)
|
|
3849
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3850
|
+
end
|
|
3851
|
+
missionCommands.addCommand("Toggle Always Cascade Explode", scalingMenu, toggleSplashDamageSetting, "always_cascade_explode")
|
|
3852
|
+
|
|
3853
|
+
--Submenu: Blast Radius & Shaped Charge
|
|
3854
|
+
local blastMenu = missionCommands.addSubMenu("Blast Radius & Shaped Charge", splashMenu)
|
|
3855
|
+
local blastRadiusMenu = missionCommands.addSubMenu("Blast Search Radius", blastMenu)
|
|
3856
|
+
addValueAdjustmentCommands(blastRadiusMenu, "blast_search_radius", {5, 10, 25})
|
|
3857
|
+
missionCommands.addCommand("Toggle Dynamic Blast Radius", blastMenu, toggleSplashDamageSetting, "use_dynamic_blast_radius")
|
|
3858
|
+
local dynamicBlastMenu = missionCommands.addSubMenu("Dynamic Blast Radius Modifier", blastMenu)
|
|
3859
|
+
addValueAdjustmentCommands(dynamicBlastMenu, "dynamic_blast_radius_modifier", {0.1, 0.5, 1})
|
|
3860
|
+
missionCommands.addCommand("Toggle Shaped Charge Effects", blastMenu, toggleSplashDamageSetting, "apply_shaped_charge_effects")
|
|
3861
|
+
local shapedChargeMenu = missionCommands.addSubMenu("Shaped Charge Multiplier", blastMenu)
|
|
3862
|
+
addValueAdjustmentCommands(shapedChargeMenu, "shaped_charge_multiplier", {0.1, 0.5, 1})
|
|
3863
|
+
|
|
3864
|
+
--Submenu: Units
|
|
3865
|
+
local unitsMenu = missionCommands.addSubMenu("Units", splashMenu)
|
|
3866
|
+
local unitSettings = {
|
|
3867
|
+
{name = "Unit Disabled Health", setting = "unit_disabled_health", increments = {5, 10, 25}},
|
|
3868
|
+
{name = "Unit Can't Fire Health", setting = "unit_cant_fire_health", increments = {5, 10, 25}},
|
|
3869
|
+
{name = "Infantry Can't Fire Health", setting = "infantry_cant_fire_health", increments = {5, 10, 25}}
|
|
3870
|
+
}
|
|
3871
|
+
for _, s in ipairs(unitSettings) do
|
|
3872
|
+
local subMenu = missionCommands.addSubMenu(s.name, unitsMenu)
|
|
3873
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3874
|
+
end
|
|
3875
|
+
|
|
3876
|
+
--Submenu: Ground Ordnance Tracking
|
|
3877
|
+
local groundOrdnanceMenu = missionCommands.addSubMenu("Ground Ordnance Tracking", splashMenu)
|
|
3878
|
+
missionCommands.addCommand("Toggle Ground Ordnance Tracking", groundOrdnanceMenu, toggleSplashDamageSetting, "track_groundunitordnance")
|
|
3879
|
+
local groundSettings = {
|
|
3880
|
+
{name = "Damage Modifier", setting = "groundunitordnance_damage_modifier", increments = {0.1, 0.5, 1}},
|
|
3881
|
+
{name = "Blastwave Modifier", setting = "groundunitordnance_blastwave_modifier", increments = {0.1, 0.5, 1}},
|
|
3882
|
+
{name = "Max Tracked Count", setting = "groundunitordnance_maxtrackedcount", increments = {5, 10, 25}}
|
|
3883
|
+
}
|
|
3884
|
+
for _, s in ipairs(groundSettings) do
|
|
3885
|
+
local subMenu = missionCommands.addSubMenu(s.name, groundOrdnanceMenu)
|
|
3886
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3887
|
+
end
|
|
3888
|
+
missionCommands.addCommand("Toggle 50m Scan", groundOrdnanceMenu, toggleSplashDamageSetting, "scan_50m_for_groundordnance")
|
|
3889
|
+
|
|
3890
|
+
--3. Cargo Cook-off & Fuel Explosion
|
|
3891
|
+
local cargoMenu = missionCommands.addSubMenu("Cargo Cook-off & Fuel Explosion", splash_damage_menu)
|
|
3892
|
+
missionCommands.addCommand("Toggle Track Pre-Explosion", cargoMenu, toggleSplashDamageSetting, "track_pre_explosion")
|
|
3893
|
+
missionCommands.addCommand("Toggle Cargo Effects", cargoMenu, toggleSplashDamageSetting, "enable_cargo_effects")
|
|
3894
|
+
local cargoThresholdMenu = missionCommands.addSubMenu("Cargo Damage Threshold", cargoMenu)
|
|
3895
|
+
addValueAdjustmentCommands(cargoThresholdMenu, "cargo_damage_threshold", {5, 10, 25})
|
|
3896
|
+
missionCommands.addCommand("Toggle Debris Effects", cargoMenu, toggleSplashDamageSetting, "debris_effects")
|
|
3897
|
+
local debrisSettings = {
|
|
3898
|
+
{name = "Debris Power", setting = "debris_power", increments = {1, 5, 10}},
|
|
3899
|
+
{name = "Min Debris Count", setting = "debris_count_min", increments = {1, 5, 10}},
|
|
3900
|
+
{name = "Max Debris Count", setting = "debris_count_max", increments = {1, 5, 10}},
|
|
3901
|
+
{name = "Max Debris Distance", setting = "debris_max_distance", increments = {1, 5, 10}}
|
|
3902
|
+
}
|
|
3903
|
+
for _, s in ipairs(debrisSettings) do
|
|
3904
|
+
local subMenu = missionCommands.addSubMenu(s.name, cargoMenu)
|
|
3905
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3906
|
+
end
|
|
3907
|
+
|
|
3908
|
+
--Submenu: Cook-off Flares
|
|
3909
|
+
local flareMenu = missionCommands.addSubMenu("Cook-off Flares", cargoMenu)
|
|
3910
|
+
missionCommands.addCommand("Toggle Cook-off Flares", flareMenu, toggleSplashDamageSetting, "cookoff_flares_enabled")
|
|
3911
|
+
local flareColorMenu = missionCommands.addSubMenu("Flare Color", flareMenu)
|
|
3912
|
+
local flareColors = {
|
|
3913
|
+
{name = "Green", value = 0},
|
|
3914
|
+
{name = "White", value = 1},
|
|
3915
|
+
{name = "Red", value = 2},
|
|
3916
|
+
{name = "Yellow", value = 3}
|
|
3917
|
+
}
|
|
3918
|
+
for _, color in ipairs(flareColors) do
|
|
3919
|
+
missionCommands.addCommand(color.name, flareColorMenu, function()
|
|
3920
|
+
splash_damage_options.cookoff_flare_color = color.value
|
|
3921
|
+
trigger.action.outText("Cook-off flare color set to " .. color.name, 5)
|
|
2085
3922
|
end)
|
|
2086
|
-
|
|
2087
|
-
missionCommands.
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
3923
|
+
end
|
|
3924
|
+
local flareCountMenu = missionCommands.addSubMenu("Flare Count Modifier", flareMenu)
|
|
3925
|
+
addValueAdjustmentCommands(flareCountMenu, "cookoff_flare_count_modifier", {0.1, 0.5, 1})
|
|
3926
|
+
local flareOffsetMenu = missionCommands.addSubMenu("Flare Offset", flareMenu)
|
|
3927
|
+
addValueAdjustmentCommands(flareOffsetMenu, "cookoff_flare_offset", {1, 5, 10})
|
|
3928
|
+
|
|
3929
|
+
--Submenu: All Vehicles Options
|
|
3930
|
+
local allVehiclesMenu = missionCommands.addSubMenu("All Vehicles Options", cargoMenu)
|
|
3931
|
+
local vehicleToggles = {
|
|
3932
|
+
"smokeandcookoffeffectallvehicles",
|
|
3933
|
+
"allunits_enable_smoke",
|
|
3934
|
+
"allunits_enable_cookoff"
|
|
3935
|
+
}
|
|
3936
|
+
for _, setting in ipairs(vehicleToggles) do
|
|
3937
|
+
missionCommands.addCommand("Toggle " .. setting:gsub("_", " "), allVehiclesMenu, toggleSplashDamageSetting, setting)
|
|
3938
|
+
end
|
|
3939
|
+
local vehicleSettings = {
|
|
3940
|
+
{name = "Explosion Power", setting = "allunits_explode_power", increments = {5, 10, 25}},
|
|
3941
|
+
{name = "Default Flame Size", setting = "allunits_default_flame_size", increments = {1, 5, 10}},
|
|
3942
|
+
{name = "Default Flame Duration", setting = "allunits_default_flame_duration", increments = {5, 10, 25}},
|
|
3943
|
+
{name = "Cook-off Count", setting = "allunits_cookoff_count", increments = {1, 5, 10}},
|
|
3944
|
+
{name = "Cook-off Duration", setting = "allunits_cookoff_duration", increments = {5, 10, 25}},
|
|
3945
|
+
{name = "Cook-off Power", setting = "allunits_cookoff_power", increments = {5, 10, 25}},
|
|
3946
|
+
{name = "Cook-off Power Random", setting = "allunits_cookoff_powerrandom", increments = {5, 10, 25}}
|
|
3947
|
+
}
|
|
3948
|
+
for _, s in ipairs(vehicleSettings) do
|
|
3949
|
+
local subMenu = missionCommands.addSubMenu(s.name, allVehiclesMenu)
|
|
3950
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3951
|
+
end
|
|
3952
|
+
|
|
3953
|
+
--4. Ordnance Protection & Cluster
|
|
3954
|
+
local ordnanceMenu = missionCommands.addSubMenu("Ordnance Protection & Cluster", splash_damage_menu)
|
|
3955
|
+
local ordnanceToggles = {
|
|
3956
|
+
"ordnance_protection",
|
|
3957
|
+
"detect_ordnance_destruction",
|
|
3958
|
+
"snap_to_ground_if_destroyed_by_large_explosion",
|
|
3959
|
+
"recent_large_explosion_snap"
|
|
3960
|
+
}
|
|
3961
|
+
for _, setting in ipairs(ordnanceToggles) do
|
|
3962
|
+
missionCommands.addCommand("Toggle " .. setting:gsub("_", " "), ordnanceMenu, toggleSplashDamageSetting, setting)
|
|
3963
|
+
end
|
|
3964
|
+
local ordnanceSettings = {
|
|
3965
|
+
{name = "Ordnance Protection Radius", setting = "ordnance_protection_radius", increments = {5, 10, 25}},
|
|
3966
|
+
{name = "Max Snapped Height", setting = "max_snapped_height", increments = {5, 10, 25}},
|
|
3967
|
+
{name = "Recent Explosion Range", setting = "recent_large_explosion_range", increments = {5, 10, 25}},
|
|
3968
|
+
{name = "Recent Explosion Time", setting = "recent_large_explosion_time", increments = {1, 5, 10}}
|
|
3969
|
+
}
|
|
3970
|
+
for _, s in ipairs(ordnanceSettings) do
|
|
3971
|
+
local subMenu = missionCommands.addSubMenu(s.name, ordnanceMenu)
|
|
3972
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3973
|
+
end
|
|
3974
|
+
|
|
3975
|
+
--Submenu: Cluster Bombs
|
|
3976
|
+
local clusterMenu = missionCommands.addSubMenu("Cluster Bombs", ordnanceMenu)
|
|
3977
|
+
missionCommands.addCommand("Toggle Cluster Enabled", clusterMenu, toggleSplashDamageSetting, "cluster_enabled")
|
|
3978
|
+
local clusterSettings = {
|
|
3979
|
+
{name = "Cluster Base Length", setting = "cluster_base_length", increments = {25, 50, 100}},
|
|
3980
|
+
{name = "Cluster Base Width", setting = "cluster_base_width", increments = {25, 50, 100}},
|
|
3981
|
+
{name = "Cluster Max Length", setting = "cluster_max_length", increments = {25, 50, 100}},
|
|
3982
|
+
{name = "Cluster Max Width", setting = "cluster_max_width", increments = {25, 50, 100}},
|
|
3983
|
+
{name = "Cluster Min Length", setting = "cluster_min_length", increments = {25, 50, 100}},
|
|
3984
|
+
{name = "Cluster Min Width", setting = "cluster_min_width", increments = {25, 50, 100}},
|
|
3985
|
+
{name = "Bomblet Damage Modifier", setting = "cluster_bomblet_damage_modifier", increments = {1, 5, 10}}
|
|
3986
|
+
}
|
|
3987
|
+
for _, s in ipairs(clusterSettings) do
|
|
3988
|
+
local subMenu = missionCommands.addSubMenu(s.name, clusterMenu)
|
|
3989
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
3990
|
+
end
|
|
3991
|
+
missionCommands.addCommand("Toggle Bomblet Reduction", clusterMenu, toggleSplashDamageSetting, "cluster_bomblet_reductionmodifier")
|
|
3992
|
+
|
|
3993
|
+
--5. Giant Explosions
|
|
3994
|
+
local giantExplosionMenu = missionCommands.addSubMenu("Giant Explosions", splash_damage_menu)
|
|
3995
|
+
local giantToggles = {
|
|
3996
|
+
"giant_explosion_enabled",
|
|
3997
|
+
"giant_explosion_target_static",
|
|
3998
|
+
"giantexplosion_ondamage",
|
|
3999
|
+
"giantexplosion_ondeath",
|
|
4000
|
+
}
|
|
4001
|
+
for _, setting in ipairs(giantToggles) do
|
|
4002
|
+
missionCommands.addCommand("Toggle " .. setting:gsub("_", " "), giantExplosionMenu, toggleSplashDamageSetting, setting)
|
|
4003
|
+
end
|
|
4004
|
+
local giantSettings = {
|
|
4005
|
+
{name = "Explosion Power", setting = "giant_explosion_power", increments = {500, 1000, 2000}},
|
|
4006
|
+
{name = "Size Scale", setting = "giant_explosion_scale", increments = {0.1, 0.5, 1, 2}},
|
|
4007
|
+
{name = "Duration", setting = "giant_explosion_duration", increments = {0.1, 0.5, 1, 2}},
|
|
4008
|
+
{name = "Explosion Count", setting = "giant_explosion_count", increments = {25, 50, 100}},
|
|
4009
|
+
}
|
|
4010
|
+
for _, s in ipairs(giantSettings) do
|
|
4011
|
+
local subMenu = missionCommands.addSubMenu(s.name, giantExplosionMenu)
|
|
4012
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
4013
|
+
end
|
|
4014
|
+
local testExplosionMenu = missionCommands.addSubMenu("Test Explosions", giantExplosionMenu)
|
|
4015
|
+
if splash_damage_options.giantexplosion_testmode then
|
|
4016
|
+
for _, target in ipairs(giantExplosionTestTargets) do
|
|
4017
|
+
missionCommands.addCommand("Detonate " .. target.name, testExplosionMenu, function()
|
|
4018
|
+
triggerGiantExplosion({
|
|
4019
|
+
pos = target.pos,
|
|
4020
|
+
power = splash_damage_options.giant_explosion_power,
|
|
4021
|
+
scale = splash_damage_options.giant_explosion_scale,
|
|
4022
|
+
duration = splash_damage_options.giant_explosion_duration,
|
|
4023
|
+
count = splash_damage_options.giant_explosion_count
|
|
4024
|
+
})
|
|
4025
|
+
end)
|
|
2091
4026
|
end
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
4027
|
+
missionCommands.addCommand("Detonate All Giant Targets", testExplosionMenu, function()
|
|
4028
|
+
for _, target in ipairs(giantExplosionTestTargets) do
|
|
4029
|
+
triggerGiantExplosion({
|
|
4030
|
+
pos = target.pos,
|
|
4031
|
+
power = splash_damage_options.giant_explosion_power,
|
|
4032
|
+
scale = splash_damage_options.giant_explosion_scale,
|
|
4033
|
+
duration = splash_damage_options.giant_explosion_duration,
|
|
4034
|
+
count = splash_damage_options.giant_explosion_count
|
|
4035
|
+
})
|
|
4036
|
+
end
|
|
4037
|
+
end)
|
|
4038
|
+
end
|
|
4039
|
+
|
|
4040
|
+
--6. Napalm
|
|
4041
|
+
local napalmMenu = missionCommands.addSubMenu("Napalm", splash_damage_menu)
|
|
4042
|
+
local napalmToggles = {
|
|
4043
|
+
"napalm_mk77_enabled",
|
|
4044
|
+
"napalmoverride_enabled",
|
|
4045
|
+
"napalm_phosphor_enabled",
|
|
4046
|
+
"napalm_addflame"
|
|
4047
|
+
}
|
|
4048
|
+
for _, setting in ipairs(napalmToggles) do
|
|
4049
|
+
missionCommands.addCommand("Toggle " .. setting:gsub("_", " "), napalmMenu, toggleSplashDamageSetting, setting)
|
|
4050
|
+
end
|
|
4051
|
+
|
|
4052
|
+
--Submenu: Spread/Phosphor/Flame
|
|
4053
|
+
local spreadPhosphorFlameMenu = missionCommands.addSubMenu("Spread/Phosphor/Flame", napalmMenu)
|
|
4054
|
+
local napalmSettings = {
|
|
4055
|
+
{name = "Spread Points", setting = "napalm_spread_points", increments = {1, 2, 3}},
|
|
4056
|
+
{name = "Spread Spacing", setting = "napalm_spread_spacing", increments = {1, 5, 10}},
|
|
4057
|
+
{name = "Phosphor Multiplier", setting = "napalm_phosphor_multiplier", increments = {0.1, 0.5, 1}},
|
|
4058
|
+
{name = "Flame Duration", setting = "napalm_addflame_duration", increments = {10, 30, 60}}
|
|
4059
|
+
}
|
|
4060
|
+
for _, s in ipairs(napalmSettings) do
|
|
4061
|
+
local subMenu = missionCommands.addSubMenu(s.name, spreadPhosphorFlameMenu)
|
|
4062
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
4063
|
+
end
|
|
4064
|
+
local napalmFlameSizeMenu = missionCommands.addSubMenu("Flame Size", spreadPhosphorFlameMenu)
|
|
4065
|
+
for i = 1, 8 do
|
|
4066
|
+
missionCommands.addCommand("Set " .. i, napalmFlameSizeMenu, function()
|
|
4067
|
+
splash_damage_options.napalm_addflame_size = i
|
|
4068
|
+
trigger.action.outText("Napalm flame size set to " .. i, 5)
|
|
4069
|
+
end)
|
|
4070
|
+
end
|
|
4071
|
+
|
|
4072
|
+
--Submenu: Delay Settings
|
|
4073
|
+
local napalmDelayMenu = missionCommands.addSubMenu("Delay Settings", napalmMenu)
|
|
4074
|
+
local napalmDelaySettings = {
|
|
4075
|
+
{name = "Explode Delay", setting = "napalm_explode_delay", increments = {0.01, 0.05, 0.1}},
|
|
4076
|
+
{name = "Destroy Delay", setting = "napalm_destroy_delay", increments = {0.01, 0.05, 0.1}},
|
|
4077
|
+
{name = "Flame Delay", setting = "napalm_flame_delay", increments = {0.01, 0.05, 0.1}}
|
|
4078
|
+
}
|
|
4079
|
+
for _, s in ipairs(napalmDelaySettings) do
|
|
4080
|
+
local subMenu = missionCommands.addSubMenu(s.name, napalmDelayMenu)
|
|
4081
|
+
addValueAdjustmentCommands(subMenu, s.setting, s.increments)
|
|
4082
|
+
end
|
|
4083
|
+
|
|
4084
|
+
--Submenu: DoubleWide
|
|
4085
|
+
local doubleWideMenu = missionCommands.addSubMenu("DoubleWide", napalmMenu)
|
|
4086
|
+
missionCommands.addCommand("Toggle DoubleWide Enabled", doubleWideMenu, toggleSplashDamageSetting, "napalm_doublewide_enabled")
|
|
4087
|
+
local doubleWideSpreadMenu = missionCommands.addSubMenu("DoubleWide Spread", doubleWideMenu)
|
|
4088
|
+
addValueAdjustmentCommands(doubleWideSpreadMenu, "napalm_doublewide_spread", {1, 5, 10})
|
|
4089
|
+
|
|
4090
|
+
--Submenu: Unit Damage
|
|
4091
|
+
local unitDamageMenu = missionCommands.addSubMenu("Unit Damage", napalmMenu)
|
|
4092
|
+
missionCommands.addCommand("Toggle Unit Damage Enabled", unitDamageMenu, toggleSplashDamageSetting, "napalm_unitdamage_enable")
|
|
4093
|
+
missionCommands.addCommand("Toggle Infantry Fire", unitDamageMenu, toggleSplashDamageSetting, "napalm_unitdamage_infantryfire")
|
|
4094
|
+
local scanDistanceMenu = missionCommands.addSubMenu("Scan Distance", unitDamageMenu)
|
|
4095
|
+
addValueAdjustmentCommands(scanDistanceMenu, "napalm_unitdamage_scandistance", {20, 25, 50})
|
|
4096
|
+
local startDelayMenu = missionCommands.addSubMenu("Start Delay", unitDamageMenu)
|
|
4097
|
+
addValueAdjustmentCommands(startDelayMenu, "napalm_unitdamage_startdelay", {0.1, 0.2, 0.5})
|
|
4098
|
+
local spreadDelayMenu = missionCommands.addSubMenu("Spread Delay", unitDamageMenu)
|
|
4099
|
+
addValueAdjustmentCommands(spreadDelayMenu, "napalm_unitdamage_spreaddelay", {0.1, 0.2, 0.5})
|
|
4100
|
+
|
|
4101
|
+
|
|
4102
|
+
--7. Exit Menu
|
|
4103
|
+
missionCommands.addCommand("Exit Splash Damage Menu", splash_damage_menu, exitSplashDamageMenu)
|
|
2106
4104
|
end
|
|
2107
4105
|
|
|
2108
4106
|
if (script_enable == 1) then
|
|
2109
|
-
gameMsg("SPLASH DAMAGE 3.
|
|
2110
|
-
env.info("SPLASH DAMAGE 3.
|
|
4107
|
+
gameMsg("SPLASH DAMAGE 3.4 SCRIPT RUNNING")
|
|
4108
|
+
env.info("SPLASH DAMAGE 3.4 SCRIPT RUNNING")
|
|
2111
4109
|
|
|
2112
4110
|
timer.scheduleFunction(function()
|
|
2113
4111
|
protectedCall(track_wpns)
|
|
@@ -2115,37 +4113,156 @@ if (script_enable == 1) then
|
|
|
2115
4113
|
end, {}, timer.getTime() + refreshRate)
|
|
2116
4114
|
|
|
2117
4115
|
if splash_damage_options.giant_explosion_enabled then
|
|
2118
|
-
|
|
2119
|
-
local targetCount = 0
|
|
2120
|
-
for coa = 0, 2 do
|
|
2121
|
-
local groups = coalition.getGroups(coa)
|
|
2122
|
-
if groups then
|
|
2123
|
-
for _, group in pairs(groups) do
|
|
2124
|
-
local units = group:getUnits()
|
|
2125
|
-
if units then
|
|
2126
|
-
for _, unit in pairs(units) do
|
|
2127
|
-
local name = unit:getName()
|
|
2128
|
-
if name:find("GiantExplosionTarget") then
|
|
2129
|
-
local pos = unit:getPosition().p
|
|
2130
|
-
giantExplosionTargets[name] = {obj = unit, pos = pos}
|
|
2131
|
-
if splash_damage_options.giant_explosion_target_static then
|
|
2132
|
-
giantExplosionTargets[name].pos = pos
|
|
2133
|
-
end
|
|
2134
|
-
debugMsg("Found GiantExplosionTarget: " .. name .. " at X:" .. pos.x .. " Y:" .. pos.y .. " Z:" .. pos.z)
|
|
2135
|
-
targetCount = targetCount + 1
|
|
2136
|
-
end
|
|
2137
|
-
end
|
|
2138
|
-
end
|
|
2139
|
-
end
|
|
2140
|
-
end
|
|
2141
|
-
end
|
|
2142
|
-
debugMsg("Total GiantExplosionTargets found: " .. targetCount)
|
|
2143
|
-
timer.scheduleFunction(checkGiantExplosionFlag, {}, timer.getTime() + splash_damage_options.giant_explosion_poll_rate)
|
|
4116
|
+
scanGiantExplosionTargets()
|
|
2144
4117
|
if not splash_damage_options.giant_explosion_target_static then
|
|
2145
|
-
timer.scheduleFunction(
|
|
4118
|
+
timer.scheduleFunction(updateGiantExplosionPositions, {}, timer.getTime() + 1.0)
|
|
2146
4119
|
end
|
|
2147
4120
|
end
|
|
4121
|
+
|
|
4122
|
+
if splash_damage_options.killfeed_enable then
|
|
4123
|
+
world.addEventHandler({ onEvent = function(self, event) protectedCall(onKillEvent, event) end }) --Add kill event handler
|
|
4124
|
+
end
|
|
2148
4125
|
|
|
2149
4126
|
world.addEventHandler(WpnHandler)
|
|
2150
4127
|
addSplashDamageMenu()
|
|
4128
|
+
|
|
4129
|
+
--Lekas integration
|
|
4130
|
+
if splash_damage_options.killfeed_enable and splash_damage_options.killfeed_lekas_foothold_integration then
|
|
4131
|
+
timer.scheduleFunction(processSplashKillfeed, {}, timer.getTime() + 60)
|
|
4132
|
+
if splash_damage_options.killfeed_debug then
|
|
4133
|
+
env.info("SplashDamage: Scheduled processSplashKillfeed for Lekas Foothold integration")
|
|
4134
|
+
end
|
|
4135
|
+
end
|
|
4136
|
+
|
|
2151
4137
|
end
|
|
4138
|
+
|
|
4139
|
+
--[[-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=
|
|
4140
|
+
##### Changelog #####
|
|
4141
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- =--=-=-=-=-=-=-=
|
|
4142
|
+
|
|
4143
|
+
24th May 2025 - 3.3
|
|
4144
|
+
|
|
4145
|
+
(Stevey666)
|
|
4146
|
+
|
|
4147
|
+
- Added some naval weapons into weapon/expl table
|
|
4148
|
+
- Added some ground unit ordnance to explosive table and allowing a wider area to be tracked
|
|
4149
|
+
- Game_mesages and enable_radio_menu options defaulted to false.
|
|
4150
|
+
-Please be advised that the non debug script has these two defaulted to false, so that users don't see that the script is in use nor can they access the test/config radio options.
|
|
4151
|
+
-Set either to true as required. The notice that the Splash Damage 3.x is running uses game_messsages.
|
|
4152
|
+
- Overhauled the radio options
|
|
4153
|
+
- Added optional cook-off effect - signal flares firing at random throughout the cook-off (see cookoff_flares_enabled). Not sure if I like this one so leaving in as optional
|
|
4154
|
+
- Reduced cargo cook off initial explosion values as they were a little too high
|
|
4155
|
+
- New feature: Napalm. MK77 A4 Skyhawk Napalm and Optional Napalm weapon override - Allows napalm effects, overriding specific weapons set in options is possible too.
|
|
4156
|
+
- This feature has been adapated from titi69's Napalm script https://www.digitalcombatsimulator.com/en/files/3340469/ , credit to him and the Olympus mod team for the Napalm method
|
|
4157
|
+
|
|
4158
|
+
(Sniex)
|
|
4159
|
+
|
|
4160
|
+
- Added weapon types in the weapon/expl
|
|
4161
|
+
- Adjusted some rocket explosive power numbers (+1 or 2)
|
|
4162
|
+
- Adjusted explosive power for anti radar, ship missile, cruise missile and some others
|
|
4163
|
+
- Increased script readability
|
|
4164
|
+
|
|
4165
|
+
(Kurdes)
|
|
4166
|
+
|
|
4167
|
+
- Added changed/missing JF17 ordnance to weapons table
|
|
4168
|
+
- Added JF29 mod ordnance to the weapons table
|
|
4169
|
+
|
|
4170
|
+
10 May 2025 (Stevey666) - 3.2
|
|
4171
|
+
- New feature (user request): ground ordnance tracking, this tracks ground artillery etc if in the explosives table, set to false by default.
|
|
4172
|
+
- New feature (user request): option to create additional smoke and cargo cookoff effect for all ground vehicles initially destroyed by your ordnance or the script, set to false by default.
|
|
4173
|
+
- Adjusted blastwave explosion
|
|
4174
|
+
- Changes to debug output, ordering by vehicle distance
|
|
4175
|
+
- Thanks to tae. for the report, adjusted Ural-4320 in vehicle table, had incorrect name so wasn't triggering cook off.
|
|
4176
|
+
- Fixed error popup when using Mig 21's SPRD-99
|
|
4177
|
+
- Added Cargo Cook off / fireball to some static objects i.e crates/barrels
|
|
4178
|
+
- Reworked Giant Explosion tracking - no mission editor trigger needed, just name static unit or unit "GiantExplosionTarget[X]"
|
|
4179
|
+
- Allow for Giant Explosion trigger on damage or on death
|
|
4180
|
+
|
|
4181
|
+
04 April 2025 (Stevey666) - 3.1
|
|
4182
|
+
- Set default cluster munitions option to false, set this to true in the options if you want it
|
|
4183
|
+
- Added missing radio commands for Cascade Scaling
|
|
4184
|
+
- Adjust default cascading to 2 (from 1)
|
|
4185
|
+
- Adjusted Ural-4320 to be a tanker and ammo carrier for cargo cookoff
|
|
4186
|
+
- Prevent weapons not in the list from being tracked
|
|
4187
|
+
- Moved some logging behind the debug mode flag
|
|
4188
|
+
- Ordnance Protection, added a max height ordnance protection will snap explosion to ground
|
|
4189
|
+
- Ordnance Protection, fixed enable/disable option
|
|
4190
|
+
- Added Giant Explosion feature
|
|
4191
|
+
- Adjusted some hydra70 values on recom. from ETBSmorgan
|
|
4192
|
+
|
|
4193
|
+
|
|
4194
|
+
09 March 2025 (Stevey666) - 3.0
|
|
4195
|
+
- 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
|
|
4196
|
+
- 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)
|
|
4197
|
+
- Added vehicle scanning around a weapon to allow for..
|
|
4198
|
+
- Cook offs - you can set vehicles that will cook off i.e ammo trucks, number of explosions, debris explosions, power adjustable
|
|
4199
|
+
- Fuel/Tanker explosion and flames - when a fuel tanker blows it will through up a big flame - adjustable in the scripts
|
|
4200
|
+
- Added section for vehicles for the above
|
|
4201
|
+
- Added radio commands for everything
|
|
4202
|
+
- Added in cluster munitions changes (note: barely tested, its not particularly accurate or that useful at this point so leaving disabled)
|
|
4203
|
+
- Potential bug - testing, stacking too many units together may cause a MIST error if you're using mist
|
|
4204
|
+
|
|
4205
|
+
- Setting this as 3.0 as I'd like to be responsive to requests, updates etc - creating a new fork to track this
|
|
4206
|
+
|
|
4207
|
+
|
|
4208
|
+
10 Feb 2025 (Stevey666) - 2.0.7
|
|
4209
|
+
- Fixed AGM 154/Adjusted weapons
|
|
4210
|
+
- Added overall damage scaling
|
|
4211
|
+
- Added modifier for shaped charges (i.e. Mavericks), adjusted weapon list accordingly
|
|
4212
|
+
- Adjusted blast radius and damage calculations, created option for dynamic blast radius
|
|
4213
|
+
- Adjusted cascading explosions, added additional "cascade_scaling" modifier and cascade explode threshold modifier. Units wont explode on initial impact unless health drops under threshold
|
|
4214
|
+
- Added always_cascade_explode option so you can set it to the old ways of making everything in the blast wave go kaboom
|
|
4215
|
+
- Added in game radio commands to change the new options ingame without having to reload everything in mission editor to test it out
|
|
4216
|
+
|
|
4217
|
+
12 November 2024 (by JGi | Quéton 1-1)
|
|
4218
|
+
- Tweak down radius 100>90 (Thanks Arhibeau)
|
|
4219
|
+
- Tweak down some values
|
|
4220
|
+
|
|
4221
|
+
20 January 2024 (by JGi | Quéton 1-1)
|
|
4222
|
+
- Added missing weapons to explTable
|
|
4223
|
+
- Sort weapons in explTable by type
|
|
4224
|
+
- Added aircraft type in log when missing
|
|
4225
|
+
|
|
4226
|
+
03 May 2023 (KERV)
|
|
4227
|
+
Correction AGM 154 (https://forum.dcs.world/topic/289290-splash-damage-20-script-make-explosions-better/page/5/#comment-5207760)
|
|
4228
|
+
|
|
4229
|
+
06 March 2023 (Kerv)
|
|
4230
|
+
- Add some data for new ammunition
|
|
4231
|
+
|
|
4232
|
+
16 April 2022
|
|
4233
|
+
spencershepard (GRIMM):
|
|
4234
|
+
- Added new/missing weapons to explTable
|
|
4235
|
+
- Added new option rocket_multiplier
|
|
4236
|
+
|
|
4237
|
+
31 December 2021
|
|
4238
|
+
spencershepard (GRIMM):
|
|
4239
|
+
- Added many new weapons
|
|
4240
|
+
- Added filter for weapons.shells events
|
|
4241
|
+
- Fixed mission weapon message option
|
|
4242
|
+
- Changed default for damage_model option
|
|
4243
|
+
|
|
4244
|
+
21 December 2021
|
|
4245
|
+
spencershepard (GRIMM):
|
|
4246
|
+
SPLASH DAMAGE 2.0:
|
|
4247
|
+
- Added blast wave effect to add timed and scaled secondary explosions on top of game objects
|
|
4248
|
+
- Object geometry within blast wave changes damage intensity
|
|
4249
|
+
- Damage boost for structures since they are hard to kill, even if very close to large explosions
|
|
4250
|
+
- Increased some rocket values in explTable
|
|
4251
|
+
- Missing weapons from explTable will display message to user and log to DCS.log so that we can add what's missing
|
|
4252
|
+
- Damage model for ground units that will disable their weapons and ability to move with partial damage before they are killed
|
|
4253
|
+
- Added options table to allow easy adjustments before release
|
|
4254
|
+
- General refactoring and restructure
|
|
4255
|
+
|
|
4256
|
+
28 October 2020
|
|
4257
|
+
FrozenDroid:
|
|
4258
|
+
- Uncommented error logging, actually made it an error log which shows a message box on error.
|
|
4259
|
+
- Fixed the too restrictive weapon filter (took out the HE warhead requirement)
|
|
4260
|
+
|
|
4261
|
+
2 October 2020
|
|
4262
|
+
FrozenDroid:
|
|
4263
|
+
- Added error handling to all event handler and scheduled functions. Lua script errors can no longer bring the server down.
|
|
4264
|
+
- 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...?)
|
|
4265
|
+
|
|
4266
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=
|
|
4267
|
+
##### END of Changelog #####
|
|
4268
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-]]
|