mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-16 22:22:25 +00:00
Merge remote-tracking branch 'origin/master-ng' into develop
This commit is contained in:
@@ -855,7 +855,7 @@ end
|
||||
-- @param DCS#Time EventTime The time stamp of the event.
|
||||
-- @param DCS#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventTakeoff( EventTime, Initiator )
|
||||
self:F( { EventTime, Initiator } )
|
||||
--self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_TAKEOFF,
|
||||
|
||||
@@ -2331,9 +2331,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
Takeoff = GROUP.Takeoff.Air
|
||||
spawnonground = false
|
||||
else
|
||||
else
|
||||
if not Takeoff == GROUP.Takeoff.Runway then
|
||||
self:E( string.format( "WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
return nil
|
||||
else
|
||||
Takeoff = GROUP.Takeoff.Runway
|
||||
spawnonground = false
|
||||
self:E( string.format( "WARNING: Group %s set to runway spawning at %s, this only works in Single Player!", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2392,7 +2398,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
else
|
||||
|
||||
-- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set.
|
||||
-- Spawn in air as requested initially. Original template orientation is preserved, altitude is already correctly set.
|
||||
SpawnTemplate.units[UnitID].x = TX
|
||||
SpawnTemplate.units[UnitID].y = TY
|
||||
SpawnTemplate.units[UnitID].alt = PointVec3.y
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
-- * TOR M2
|
||||
-- * C-RAM
|
||||
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
||||
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19, SA-21, S-300VM, S-300V4, S-400
|
||||
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19, S-300VM, S-300V4, S-400 (SA-21)
|
||||
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
|
||||
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2, SAMP/T Block 1, SAMP/T Block 1INT, SAMP/T Block2
|
||||
-- * Other Mods: Nike
|
||||
@@ -135,8 +135,8 @@
|
||||
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
|
||||
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS" with 5P85CE launcher)
|
||||
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS" with 5P85SE launcher)
|
||||
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS 2" for the 9A82 variant and "Red SAM SA-12 HDS 1" for the 9A83 variant)
|
||||
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS 2" for the 9A82ME variant and "Red SAM SA-23 HDS 1" for the 9A83ME variant)
|
||||
-- * SA-12 (e.g. "Red SAM SA-12 HDS")
|
||||
-- * SA-23 (e.g. "Red SAM SA-23 HDS")
|
||||
-- * SAMP/T (launcher dependent range, e.g. "Blue SAM SAMPT Block 1 HDS" for Block 1, "Blue SAM SAMPT Block 1INT HDS", "Blue SAM SAMPT Block 2 HDS")
|
||||
--
|
||||
-- The other HDS types work like the rest of the known SAM systems.
|
||||
@@ -376,7 +376,7 @@ MANTIS.radiusscale[MANTIS.SamType.POINT] = 3
|
||||
-- @field #string Point Point defense capable
|
||||
-- @field ARMCapacit ARMCapacity ie how many (H)ARMs the system can defend at the same time
|
||||
MANTIS.SamData = {
|
||||
["Hawk"] = { Range=45, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km
|
||||
["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km
|
||||
["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS", ARMCapacity=1 }, -- AIM 120B
|
||||
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot str" },
|
||||
["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" },
|
||||
@@ -404,10 +404,9 @@ MANTIS.SamData = {
|
||||
["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17", ARMCapacity=4 },
|
||||
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1", ARMCapacity=16},
|
||||
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2", ARMCapacity=18},
|
||||
["SA-21"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="92N6E"},
|
||||
["S-300VM"] = { Range=200, Blindspot=5, Height=30, Type="Long" , Radar="9S32M", ARMCapacity=4},
|
||||
["S-300V4"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="9S32M", ARMCapacity=4},
|
||||
["S-400"] = { Range=250, Blindspot=5, Height=27, Type="Long" , Radar="92N6E", ARMCapacity=4},
|
||||
["S-300VM"] = { Range=200, Blindspot=5, Height=30, Type="Long" , Radar="9S32ME", ARMCapacity=4}, -- SA-23 Gladiator/Giant
|
||||
["S-300V4"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="9S32M-1E", ARMCapacity=4}, -- SA-23B
|
||||
["S-400"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="92N6E", ARMCapacity=4}, -- SA-21 Growler
|
||||
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
|
||||
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
||||
@@ -417,6 +416,7 @@ MANTIS.SamData = {
|
||||
["Pantsir S1"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1" , Point="true", ARMCapacity=3 },
|
||||
["Tor M2"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true", ARMCapacity=4 },
|
||||
["IRIS-T SLM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM", ARMCapacity=12 }, -- 4 per starter, usually 3 starters in a battery
|
||||
["SON-9"] = { Range=20, Blindspot=0, Height=14, Type="Point", Radar="SON_9", Point="true" }, -- Fire Can FCR for S-60/KS-19 AAA
|
||||
}
|
||||
|
||||
--- SAM data HDS
|
||||
@@ -435,10 +435,8 @@ MANTIS.SamDataHDS = {
|
||||
["SA-10B HDS"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln", ARMCapacity=8}, -- V55RUD
|
||||
["SA-10C HDS"] = { Range=75, Blindspot=5, Height=25, Type="Long" , Radar="5P85SE ln", ARMCapacity=3}, -- V55RUD
|
||||
["SA-17 HDS"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17", ARMCapacity=4 },
|
||||
["SA-12 HDS 2"] = { Range=100, Blindspot=13, Height=30, Type="Long" , Radar="S-300V 9A82 l", ARMCapacity=12},
|
||||
["SA-12 HDS 1"] = { Range=75, Blindspot=6, Height=25, Type="Long" , Radar="S-300V 9A83 l", ARMCapacity=12},
|
||||
["SA-23 HDS 2"] = { Range=200, Blindspot=5, Height=37, Type="Long", Radar="S-300VM 9A82ME", ARMCapacity=14 },
|
||||
["SA-23 HDS 1"] = { Range=100, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A83ME", ARMCapacity=14 },
|
||||
["SA-12 HDS"] = { Range=100, Blindspot=6, Height=30, Type="Long" , Radar="S-300V 9A82 l", ARMCapacity=12},
|
||||
["SA-23 HDS"] = { Range=200, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A82ME", ARMCapacity=14 },
|
||||
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||
["SAMPT Block 1 HDS"] = { Range=120, Blindspot=1, Height=20, Type="long", Radar="SAMPT_MLT_Blk1" }, -- Block 1 Launcher
|
||||
["SAMPT Block 1INT HDS"] = { Range=150, Blindspot=1, Height=25, Type="long", Radar="SAMPT_MLT_Blk1NT" }, -- Block 1-INT Launcher
|
||||
@@ -526,6 +524,157 @@ MANTIS.SamDataCH = {
|
||||
["Lvkv9040M CHM"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS Jammer Extension v2.0.0
|
||||
-- Standoff Jamming (SOJ) aircraft support for MANTIS IADS networks
|
||||
-- Physics-based jamming: asymmetric Gaussian rise + exponential decay
|
||||
-- Compatible with v7 jammer curves
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Jammer Loadout Configurations (v8: Max 3 Pods)
|
||||
-- @type MANTIS.JammerLoadouts
|
||||
MANTIS.JammerLoadouts = {
|
||||
["1xALQ99"]={name="1x AN/ALQ-99",description="Baseline single pod. Full 64MHz-20GHz spectrum.",mult_LOW=1.12,mult_S=1.10,mult_IJ=1.12,mult_OPT=1.00,bt_mod=1.00,range_mod=1.00,tier="ALQ99"},
|
||||
["2xALQ99"]={name="2x AN/ALQ-99",description="Two pods. Improved ERP with log stacking.",mult_LOW=1.40,mult_S=1.36,mult_IJ=1.40,mult_OPT=1.02,bt_mod=1.00,range_mod=1.03,tier="ALQ99"},
|
||||
["3xALQ99"]={name="3x AN/ALQ-99 (Maximum Legacy)",description="Max legacy barrage. Highest broadband ERP.",mult_LOW=1.58,mult_S=1.52,mult_IJ=1.58,mult_OPT=1.04,bt_mod=1.00,range_mod=1.06,tier="ALQ99"},
|
||||
["1xALQ249"]={name="1x AN/ALQ-249 (AESA)",description="AESA 2-18GHz. High peak+wider window. Blind <2GHz.",mult_LOW=0.15,mult_S=1.82,mult_IJ=1.70,mult_OPT=1.00,bt_mod=1.28,range_mod=1.16,tier="ALQ249"},
|
||||
["2xALQ249"]={name="2x AN/ALQ-249",description="Two AESA pods. Strong S/IJ dominance.",mult_LOW=0.15,mult_S=2.30,mult_IJ=2.15,mult_OPT=1.00,bt_mod=1.38,range_mod=1.26,tier="ALQ249"},
|
||||
["3xALQ249"]={name="3x AN/ALQ-249 (Maximum AESA)",description="Max AESA ERP. Extreme S/IJ. No LOW coverage.",mult_LOW=0.15,mult_S=2.75,mult_IJ=2.58,mult_OPT=1.00,bt_mod=1.48,range_mod=1.35,tier="ALQ249"},
|
||||
["1xALQ99_1xALQ249"]={name="1x ALQ-99 + 1x ALQ-249",description="Balanced coverage. AESA S/IJ + ALQ-99 LOW-band.",mult_LOW=1.14,mult_S=1.90,mult_IJ=1.80,mult_OPT=1.01,bt_mod=1.22,range_mod=1.12,tier="Mixed"},
|
||||
["1xALQ99_2xALQ249"]={name="1x ALQ-99 + 2x ALQ-249 [Recommended]",description="Best all-around. High AESA ERP + LOW coverage.",mult_LOW=1.10,mult_S=2.38,mult_IJ=2.22,mult_OPT=1.01,bt_mod=1.35,range_mod=1.24,tier="Mixed"},
|
||||
["2xALQ99_1xALQ249"]={name="2x ALQ-99 + 1x ALQ-249",description="Strong LOW-band + AESA S/IJ boost.",mult_LOW=1.42,mult_S=2.10,mult_IJ=2.04,mult_OPT=1.03,bt_mod=1.25,range_mod=1.15,tier="Mixed"},
|
||||
}
|
||||
|
||||
--- Tiered loadout keys for F10 menu (v8: 3/3/3)
|
||||
-- @type MANTIS.JammerLoadoutTiers
|
||||
MANTIS.JammerLoadoutTiers = {
|
||||
ALQ99 = {"1xALQ99","2xALQ99","3xALQ99"},
|
||||
ALQ249 = {"1xALQ249","2xALQ249","3xALQ249"},
|
||||
Mixed = {"1xALQ99_1xALQ249","1xALQ99_2xALQ249","2xALQ99_1xALQ249"},
|
||||
}
|
||||
|
||||
--- Jitter configuration: ±10% per evaluation (EW Fundamentals Ch.9)
|
||||
-- @type MANTIS.JammerJitterPercent
|
||||
MANTIS.JammerJitterPercent = 0.10
|
||||
|
||||
--- Jammer SAM Parameters — v8 curves {peak%, mu_nm, sigma_L, tail_dist, band, floor%}
|
||||
-- floor: residual effectiveness inside burnthrough (noise injection)
|
||||
-- 5 = legacy radar, no ECCM | 3 = moderate ECCM | 2 = advanced ECCM/AESA | 0 = optical/IR
|
||||
-- @type MANTIS.JammerSAMParams
|
||||
MANTIS.JammerSAMParams = {
|
||||
["Nike"] ={peak=78,mu=35,sigma_L=14,tail_dist=80, band="S", floor=5},
|
||||
["Hawk"] ={peak=30,mu=15,sigma_L=6, tail_dist=28, band="IJ", floor=3},
|
||||
["SA-2"] ={peak=75,mu=40,sigma_L=16,tail_dist=85, band="S", floor=5},
|
||||
["SA-3"] ={peak=45,mu=22,sigma_L=10,tail_dist=50, band="IJ", floor=5},
|
||||
["SA-5"] ={peak=52,mu=60,sigma_L=22,tail_dist=110,band="S", floor=5},
|
||||
["SA-6"] ={peak=33,mu=18,sigma_L=8, tail_dist=42, band="IJ", floor=3},
|
||||
["SA-8"] ={peak=38,mu=10,sigma_L=4, tail_dist=22, band="IJ", floor=3},
|
||||
["SA-9"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["SA-10"] ={peak=32,mu=50,sigma_L=20,tail_dist=90, band="S", floor=3},
|
||||
["SA-11"] ={peak=52,mu=28,sigma_L=12,tail_dist=55, band="IJ", floor=3},
|
||||
["SA-13"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["SA-15"] ={peak=30,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=3},
|
||||
["SA-19"] ={peak=25,mu=18,sigma_L=7, tail_dist=38, band="IJ", floor=2},
|
||||
["SA-10B"] ={peak=30,mu=52,sigma_L=20,tail_dist=95, band="S", floor=3},
|
||||
["SA-17"] ={peak=24,mu=32,sigma_L=14,tail_dist=65, band="IJ", floor=2},
|
||||
["SA-20A"] ={peak=22,mu=58,sigma_L=22,tail_dist=95, band="S", floor=2},
|
||||
["SA-20B"] ={peak=20,mu=60,sigma_L=22,tail_dist=100,band="S", floor=2},
|
||||
["S-300VM"] ={peak=16,mu=70,sigma_L=28,tail_dist=110,band="S", floor=2},
|
||||
["S-300V4"] ={peak=14,mu=75,sigma_L=28,tail_dist=115,band="S", floor=2},
|
||||
["S-400"] ={peak=18,mu=65,sigma_L=25,tail_dist=105,band="S", floor=2},
|
||||
["SA-21"] ={peak=18,mu=65,sigma_L=25,tail_dist=105,band="S", floor=2},
|
||||
-- NATO designation aliases (allow mission designers to use SA-NN naming)
|
||||
["SA-22"] ={peak=10,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=0}, -- alias for Pantsir S1
|
||||
["SA-23"] ={peak=16,mu=70,sigma_L=28,tail_dist=110,band="S", floor=2}, -- alias for S-300VM
|
||||
["SA-23B"] ={peak=14,mu=75,sigma_L=28,tail_dist=115,band="S", floor=2}, -- alias for S-300V4
|
||||
["SA-27"] ={peak=22,mu=38,sigma_L=16,tail_dist=72, band="IJ", floor=2}, -- alias for Buk-M3
|
||||
["SA-28"] ={peak=18,mu=35,sigma_L=15,tail_dist=75, band="IJ", floor=2}, -- alias for S-350
|
||||
["NASAMS"] ={peak=25,mu=28,sigma_L=12,tail_dist=55, band="IJ", floor=2},
|
||||
["Patriot"] ={peak=32,mu=50,sigma_L=20,tail_dist=90, band="S", floor=3},
|
||||
["Rapier"] ={peak=12,mu=8, sigma_L=3, tail_dist=18, band="IJ", floor=0},
|
||||
["Gepard"] ={peak=18,mu=6, sigma_L=2, tail_dist=15, band="IJ", floor=0},
|
||||
["Roland"] ={peak=35,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=3},
|
||||
["HQ-7"] ={peak=38,mu=10,sigma_L=4, tail_dist=22, band="IJ", floor=3},
|
||||
["HQ-2"] ={peak=70,mu=38,sigma_L=15,tail_dist=80, band="S", floor=5},
|
||||
["C-RAM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["Avenger"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["Chaparral"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["Linebacker"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["Silkworm"] ={peak=35,mu=20,sigma_L=8, tail_dist=40, band="IJ", floor=3},
|
||||
["Dog Ear"] ={peak=40,mu=10,sigma_L=4, tail_dist=20, band="IJ", floor=5},
|
||||
["Pantsir S1"] ={peak=10,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=0},
|
||||
["Tor M2"] ={peak=28,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=3},
|
||||
["IRIS-T SLM"] ={peak=18,mu=18,sigma_L=8, tail_dist=40, band="IJ", floor=2},
|
||||
["SON-9"] ={peak=48,mu=20,sigma_L=9, tail_dist=42, band="IJ", floor=5},
|
||||
["TAMIR IDFA"] ={peak=19,mu=25,sigma_L=12,tail_dist=55,band="S", floor=2},
|
||||
["STUNNER IDFA"]={peak=16,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2},
|
||||
["SA-2 HDS"] ={peak=75,mu=40,sigma_L=16,tail_dist=85, band="S", floor=5},
|
||||
["SA-3 HDS"] ={peak=45,mu=22,sigma_L=10,tail_dist=50, band="IJ", floor=5},
|
||||
["SA-10B HDS"] ={peak=30,mu=52,sigma_L=20,tail_dist=95, band="S", floor=3},
|
||||
["SA-10C HDS"] ={peak=30,mu=50,sigma_L=20,tail_dist=92, band="S", floor=3},
|
||||
["SA-17 HDS"] ={peak=24,mu=32,sigma_L=14,tail_dist=65, band="IJ", floor=2},
|
||||
["SA-12 HDS"] ={peak=35,mu=42,sigma_L=16,tail_dist=75, band="S", floor=3},
|
||||
["SA-23 HDS"] ={peak=16,mu=70,sigma_L=28,tail_dist=110,band="S", floor=2},
|
||||
["HQ-2 HDS"] ={peak=70,mu=38,sigma_L=15,tail_dist=80, band="S", floor=5},
|
||||
["SAMPT Block 1 HDS"] ={peak=28,mu=45,sigma_L=18,tail_dist=85, band="S", floor=3},
|
||||
["SAMPT Block 1INT HDS"]={peak=26,mu=48,sigma_L=18,tail_dist=88,band="S", floor=3},
|
||||
["SAMPT Block 2 HDS"] ={peak=22,mu=52,sigma_L=20,tail_dist=92, band="S", floor=2},
|
||||
["RBS98M SMA"] ={peak=25,mu=12,sigma_L=5, tail_dist=25,band="IJ", floor=3},
|
||||
["RBS70 SMA"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS70M SMA"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS90 SMA"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS90M SMA"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS103A SMA"] ={peak=20,mu=55,sigma_L=20,tail_dist=90,band="S", floor=2},
|
||||
["RBS103B SMA"] ={peak=22,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2},
|
||||
["RBS103AM SMA"] ={peak=20,mu=55,sigma_L=20,tail_dist=90,band="S", floor=2},
|
||||
["RBS103BM SMA"] ={peak=22,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2},
|
||||
["Lvkv9040M SMA"]={peak=15,mu=3, sigma_L=1, tail_dist=8, band="OPT",floor=0},
|
||||
["2S38 CHM"] ={peak=8, mu=3, sigma_L=1, tail_dist=8, band="OPT",floor=0},
|
||||
["PantsirS1 CHM"] ={peak=10,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=0},
|
||||
["PantsirS2 CHM"] ={peak=10,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=0},
|
||||
["PGL-625 CHM"] ={peak=12,mu=4, sigma_L=2, tail_dist=10, band="OPT",floor=0},
|
||||
["HQ-17A CHM"] ={peak=30,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=3},
|
||||
["M903PAC2 CHM"] ={peak=32,mu=50,sigma_L=20,tail_dist=90, band="S", floor=3},
|
||||
["M903PAC3 CHM"] ={peak=15,mu=55,sigma_L=22,tail_dist=95, band="S", floor=2},
|
||||
["M903PAC2KAT1 CHM"] ={peak=32,mu=50,sigma_L=20,tail_dist=90, band="S", floor=3},
|
||||
["TorM2 CHM"] ={peak=28,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=3},
|
||||
["TorM2K CHM"] ={peak=28,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=3},
|
||||
["TorM2M CHM"] ={peak=26,mu=16,sigma_L=6, tail_dist=32, band="IJ", floor=3},
|
||||
["NASAMS3-AMRAAMER CHM"] ={peak=20,mu=35,sigma_L=14,tail_dist=65, band="IJ", floor=2},
|
||||
["NASAMS3-AIM9X2 CHM"] ={peak=10,mu=15,sigma_L=6, tail_dist=30, band="IJ", floor=0},
|
||||
["C-RAM CHM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["PGZ-09 CHM"] ={peak=22,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=0},
|
||||
["PGZ-95 CHM"] ={peak=15,mu=4, sigma_L=2, tail_dist=10, band="OPT",floor=0},
|
||||
["S350-9M100 CHM"] ={peak=18,mu=35,sigma_L=15,tail_dist=75, band="IJ", floor=2},
|
||||
["S350-9M96D CHM"] ={peak=18,mu=35,sigma_L=15,tail_dist=75, band="IJ", floor=2},
|
||||
["HQ-22 CHM"] ={peak=20,mu=58,sigma_L=22,tail_dist=100,band="S", floor=2},
|
||||
["LD-3000 CHM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["LD-3000M CHM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["FlaRakRad CHM"] ={peak=35,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=3},
|
||||
["IRIS-T SLM CHM"] ={peak=18,mu=18,sigma_L=8, tail_dist=40, band="IJ", floor=2},
|
||||
["Skynex CHM"] ={peak=12,mu=4, sigma_L=2, tail_dist=10, band="OPT",floor=0},
|
||||
["Skyshield CHM"] ={peak=12,mu=4, sigma_L=2, tail_dist=10, band="OPT",floor=0},
|
||||
["BukM3-9M317M CHM"] ={peak=22,mu=38,sigma_L=16,tail_dist=72, band="IJ", floor=2},
|
||||
["BukM3-9M317MA CHM"] ={peak=22,mu=38,sigma_L=16,tail_dist=72, band="IJ", floor=2},
|
||||
["SkySabre CHM"] ={peak=18,mu=18,sigma_L=7, tail_dist=40, band="IJ", floor=2},
|
||||
["Stormer CHM"] ={peak=10,mu=5, sigma_L=2, tail_dist=12, band="OPT",floor=0},
|
||||
["THAAD CHM"] ={peak=10,mu=80,sigma_L=35,tail_dist=120,band="IJ", floor=0},
|
||||
["LAV-AD CHM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["WieselOzelot CHM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["USInfantryFIM92K CHM"] ={peak=3, mu=3, sigma_L=1, tail_dist=5, band="OPT",floor=0},
|
||||
["RBS98M CHM"] ={peak=25,mu=12,sigma_L=5, tail_dist=25,band="IJ", floor=3},
|
||||
["RBS70 CHM"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS70M CHM"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS90 CHM"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS90M CHM"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0},
|
||||
["RBS103A CHM"] ={peak=20,mu=55,sigma_L=20,tail_dist=90,band="S", floor=2},
|
||||
["RBS103B CHM"] ={peak=22,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2},
|
||||
["RBS103AM CHM"] ={peak=20,mu=55,sigma_L=20,tail_dist=90,band="S", floor=2},
|
||||
["RBS103BM CHM"] ={peak=22,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2},
|
||||
["Lvkv9040M CHM"]={peak=15,mu=3, sigma_L=1, tail_dist=8, band="OPT",floor=0},
|
||||
}
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS System
|
||||
-----------------------------------------------------------------------
|
||||
@@ -908,7 +1057,8 @@ do
|
||||
|
||||
local function SwitchSAMOn(Name,Group)
|
||||
local suppressed = self.SuppressedGroups[Name] or false
|
||||
if not suppressed and self.SamStateTracker[Name] == "GREEN" then
|
||||
local jammed = self._jammerEnabled and self._jammedSAMs and self._jammedSAMs[Name] or false
|
||||
if not suppressed and not jammed and self.SamStateTracker[Name] == "GREEN" then
|
||||
self.SamStateTracker[Name] = "RED"
|
||||
if self.UseEmOnOff then
|
||||
-- DONE: add emissions on/off
|
||||
@@ -2132,6 +2282,18 @@ function MANTIS:SeadAllowSuppression(targetGroup, targetName, attackerGroup, wea
|
||||
self:T(string.format("MANTIS:SeadAllowSuppression REQUEST | target=%s | weapon=%s | tti=%s | delay=%s",tostring(targetName),
|
||||
tostring(weaponName),tostring(tti),tostring(delay)))
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- JAMMER CHECK: if SAM is currently jammed, deny SEAD suppression.
|
||||
-- The jammer is already keeping the SAM off. SEAD suppression would
|
||||
-- cause SuppressionStop to briefly re-enable the radar and relocate
|
||||
-- the SAM (potentially out of jammer range). Let the jammer handle it.
|
||||
----------------------------------------------------------------
|
||||
if self._jammerEnabled and self._jammedSAMs and self._jammedSAMs[targetName] then
|
||||
self:T(string.format("MANTIS:SeadAllowSuppression DECISION -> DENIED (JAMMED %.0f%%) | target=%s",
|
||||
self._jammedSAMs[targetName] * 100, tostring(targetName)))
|
||||
return false
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- LOOK UP ARM CAPACITY FOR THIS SAM
|
||||
----------------------------------------------------------------
|
||||
@@ -2694,6 +2856,657 @@ function MANTIS:SeadAllowSuppression(targetGroup, targetName, attackerGroup, wea
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS Jammer Extension v2.0.0 - Functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Add client jammer aircraft support. Call BEFORE :Start().
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Set#SET_GROUP clientSet SET_GROUP of client jammer aircraft, or a prefix string
|
||||
-- @param #string defaultLoadout Default loadout key (default "1xALQ99_2xALQ249")
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddJammer(clientSet, defaultLoadout)
|
||||
self:T(self.lid .. "AddJammer")
|
||||
self._jammerEnabled = true
|
||||
-- Allow string prefix shorthand
|
||||
if type(clientSet) == "string" then
|
||||
clientSet = SET_GROUP:New():FilterPrefixes(clientSet):FilterActive(true):FilterStart()
|
||||
end
|
||||
self._jammerClientSet = clientSet
|
||||
self._jammerDefaultLoadout = defaultLoadout or "1xALQ99_2xALQ249"
|
||||
self._jammerAircraft = self._jammerAircraft or {}
|
||||
self._jammerAISets = self._jammerAISets or {}
|
||||
self._jammerSnapshot = self._jammerSnapshot or {}
|
||||
self._jammedSAMs = self._jammedSAMs or {}
|
||||
self._jammerMenusBuilt = self._jammerMenusBuilt or {}
|
||||
if not self._jammerHasTransitions then
|
||||
self:AddTransition("*", "JammerSuppression", "*")
|
||||
self:AddTransition("*", "JammerActivated", "*")
|
||||
self:AddTransition("*", "JammerDeactivated", "*")
|
||||
self._jammerHasTransitions = true
|
||||
end
|
||||
-- Start fast client menu scheduler (5s interval) so menus appear quickly
|
||||
-- instead of waiting for the next MANTIS Status cycle (~30s)
|
||||
if not self._jammerMenuScheduler then
|
||||
self._jammerMenuScheduler = SCHEDULER:New(nil, function()
|
||||
if not self._jammerEnabled then return end
|
||||
if self._jammerClientSet then
|
||||
self:_IterateJammerSet(self._jammerClientSet, function(unit, group)
|
||||
local unitName = unit:GetName()
|
||||
if not self._jammerAircraft[unitName] then
|
||||
self._jammerAircraft[unitName] = {
|
||||
loadout = self._jammerDefaultLoadout,
|
||||
active = false,
|
||||
isClient = unit:IsPlayer(),
|
||||
hasLoadout = false,
|
||||
}
|
||||
elseif not self._jammerAircraft[unitName].isClient and unit:IsPlayer() then
|
||||
self._jammerAircraft[unitName].isClient = true
|
||||
end
|
||||
if self._jammerAircraft[unitName].isClient and not self._jammerMenusBuilt[unitName] then
|
||||
self:_SetupJammerMenu(unit, group)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end, {}, 2, 5) -- start in 2s, repeat every 5s
|
||||
end
|
||||
self:I(string.format("%sJammer configured | default=%s", self.lid, self._jammerDefaultLoadout))
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add AI jammer aircraft. Call BEFORE :Start(). Can be called multiple times.
|
||||
-- AI jammers start OFF; use SetJammerActive(unitName, true) to activate via script.
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Set#SET_GROUP aiSet SET_GROUP or prefix string for AI jammer aircraft
|
||||
-- @param #string loadout Loadout key for these AI aircraft (default "1xALQ99_2xALQ249")
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddJammerAI(aiSet, loadout)
|
||||
self:T(self.lid .. "AddJammerAI")
|
||||
self._jammerEnabled = true
|
||||
self._jammerAircraft = self._jammerAircraft or {}
|
||||
self._jammerAISets = self._jammerAISets or {}
|
||||
self._jammerSnapshot = self._jammerSnapshot or {}
|
||||
self._jammedSAMs = self._jammedSAMs or {}
|
||||
self._jammerMenusBuilt = self._jammerMenusBuilt or {}
|
||||
local actualSet = aiSet
|
||||
if type(aiSet) == "string" then
|
||||
actualSet = SET_GROUP:New():FilterPrefixes(aiSet):FilterActive(true):FilterStart()
|
||||
end
|
||||
table.insert(self._jammerAISets, { set = actualSet, loadout = loadout or "1xALQ99_2xALQ249" })
|
||||
if not self._jammerHasTransitions then
|
||||
self:AddTransition("*", "JammerSuppression", "*")
|
||||
self:AddTransition("*", "JammerActivated", "*")
|
||||
self:AddTransition("*", "JammerDeactivated", "*")
|
||||
self._jammerHasTransitions = true
|
||||
end
|
||||
self:I(string.format("%sJammer AI configured | loadout=%s", self.lid, loadout or "1xALQ99_2xALQ249"))
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Core jamming probability curve (v8 engine).
|
||||
-- Includes decoupled bt_mod/range_mod, ±10% jitter, and floor.
|
||||
-- @param #MANTIS self
|
||||
-- @param #number d Distance in nautical miles
|
||||
-- @param #table params {peak, mu, sigma_L, tail_dist, band, floor}
|
||||
-- @param #string loadoutKey Key into JammerLoadouts
|
||||
-- @return #number Jamming probability as fraction (0.0-0.95)
|
||||
function MANTIS:_JamGaussianExp(d, params, loadoutKey)
|
||||
if d < 0 or d > 200 then return 0 end
|
||||
if not params then return 0 end
|
||||
local cfg = self.JammerLoadouts[loadoutKey]
|
||||
if not cfg then return 0 end
|
||||
local peak, mu, sigma_L, tail_dist, band = params.peak, params.mu, params.sigma_L, params.tail_dist, params.band
|
||||
local floor = params.floor or 0
|
||||
local bm
|
||||
if band == "LOW" then bm = cfg.mult_LOW
|
||||
elseif band == "S" then bm = cfg.mult_S
|
||||
elseif band == "IJ" then bm = cfg.mult_IJ
|
||||
else bm = cfg.mult_OPT end
|
||||
-- Decoupled window modifiers (v8)
|
||||
local eff_sigma_L = sigma_L / cfg.bt_mod
|
||||
local eff_tail_dist = tail_dist * cfg.range_mod
|
||||
local eff_peak = math.min(95, peak * bm)
|
||||
local raw
|
||||
if d < mu then
|
||||
raw = eff_peak * math.exp(-0.5 * ((d - mu) / eff_sigma_L) ^ 2)
|
||||
else
|
||||
local lambda = math.log(100.0) / eff_tail_dist
|
||||
raw = eff_peak * math.exp(-lambda * (d - mu))
|
||||
end
|
||||
-- Stochastic jitter ±10% (v8)
|
||||
local jitter = 1.0 + (math.random() * 2 - 1) * (self.JammerJitterPercent or 0.10)
|
||||
raw = raw * jitter
|
||||
-- Minimum effectiveness floor (residual noise injection, v8)
|
||||
local eff_floor = (floor or 0) * bm
|
||||
raw = math.max(eff_floor, raw)
|
||||
return math.max(0, math.min(95, raw)) / 100.0
|
||||
end
|
||||
|
||||
--- [Internal] Build cached sorted key lists for the resolver.
|
||||
-- Called once on first use. Avoids re-scanning/re-sorting JammerSAMParams every cycle.
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_BuildJammerKeyCache()
|
||||
self._jammerSortedHDS = {}
|
||||
self._jammerSortedSMA = {}
|
||||
self._jammerSortedCHM = {}
|
||||
self._jammerSortedBase = {}
|
||||
for key, _ in pairs(self.JammerSAMParams) do
|
||||
if string.find(key, "HDS", 1, true) then table.insert(self._jammerSortedHDS, key)
|
||||
elseif string.find(key, "SMA", 1, true) then table.insert(self._jammerSortedSMA, key)
|
||||
elseif string.find(key, "CHM", 1, true) then table.insert(self._jammerSortedCHM, key)
|
||||
else table.insert(self._jammerSortedBase, key) end
|
||||
end
|
||||
local byLenDesc = function(a, b) return #a > #b end
|
||||
table.sort(self._jammerSortedHDS, byLenDesc)
|
||||
table.sort(self._jammerSortedSMA, byLenDesc)
|
||||
table.sort(self._jammerSortedCHM, byLenDesc)
|
||||
table.sort(self._jammerSortedBase, byLenDesc)
|
||||
self._jammerResolverCache = {}
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Resolve a SAM group name to its jammer parameters.
|
||||
-- Uses cached sorted key lists and per-name memoization for performance.
|
||||
-- Substring matching with digit-boundary protection prevents false matches
|
||||
-- (e.g. "SA-22 Pantsir" no longer matches "SA-2").
|
||||
-- @param #MANTIS self
|
||||
-- @param #string grpname The SAM group name
|
||||
-- @return #table params or nil
|
||||
function MANTIS:_ResolveJammerParams(grpname)
|
||||
if not grpname then return nil end
|
||||
-- Lazy-build cache on first use
|
||||
if not self._jammerSortedBase then self:_BuildJammerKeyCache() end
|
||||
-- Memoization: same group name resolves to same params (params don't change at runtime)
|
||||
local cached = self._jammerResolverCache[grpname]
|
||||
if cached ~= nil then
|
||||
if cached == false then return nil end -- false sentinel = "no match"
|
||||
return cached
|
||||
end
|
||||
-- Helper: check key match with digit-boundary protection
|
||||
local function safeMatch(name, key)
|
||||
local startPos, endPos = string.find(name, key, 1, true)
|
||||
if not startPos then return false end
|
||||
local nextChar = string.sub(name, endPos + 1, endPos + 1)
|
||||
if nextChar == "" then return true end
|
||||
if string.match(nextChar, "%d") then return false end
|
||||
return true
|
||||
end
|
||||
-- Detect mod type and select pre-sorted key list
|
||||
local keyList
|
||||
if string.find(grpname, "HDS", 1, true) then keyList = self._jammerSortedHDS
|
||||
elseif string.find(grpname, "SMA", 1, true) then keyList = self._jammerSortedSMA
|
||||
elseif string.find(grpname, "CHM", 1, true) then keyList = self._jammerSortedCHM end
|
||||
-- First pass: mod-specific keys
|
||||
if keyList then
|
||||
for i = 1, #keyList do
|
||||
if safeMatch(grpname, keyList[i]) then
|
||||
local params = self.JammerSAMParams[keyList[i]]
|
||||
self._jammerResolverCache[grpname] = params
|
||||
return params
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Second pass: base keys
|
||||
local baseKeys = self._jammerSortedBase
|
||||
for i = 1, #baseKeys do
|
||||
if safeMatch(grpname, baseKeys[i]) then
|
||||
local params = self.JammerSAMParams[baseKeys[i]]
|
||||
self._jammerResolverCache[grpname] = params
|
||||
return params
|
||||
end
|
||||
end
|
||||
self._jammerResolverCache[grpname] = false -- cache "no match" too
|
||||
return nil
|
||||
end
|
||||
|
||||
--- [Internal] Count table entries
|
||||
function MANTIS:_CountTable(t)
|
||||
local c = 0
|
||||
for _ in pairs(t) do c = c + 1 end
|
||||
return c
|
||||
end
|
||||
|
||||
--- [Internal] Iterate any supported SET type, calling fn(unit, group) for each alive unit.
|
||||
-- Supports SET_GROUP (via ForEachGroupAlive) and SET_CLIENT/SET_PLAYER (via ForEachClient).
|
||||
-- Note: callback receives ALL alive units — caller must check InAir() for snapshot logic.
|
||||
-- Menu setup happens for ground units too so clients see the menu before takeoff.
|
||||
-- @param #MANTIS self
|
||||
-- @param set The SET object (SET_GROUP, SET_CLIENT, or SET_PLAYER)
|
||||
-- @param #function fn Callback receiving (unit, group)
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_IterateJammerSet(set, fn)
|
||||
if not set then return self end
|
||||
if type(set.ForEachClient) == "function" then
|
||||
set:ForEachClient(function(client)
|
||||
if not client then return end
|
||||
local unit = client:GetClientGroupUnit()
|
||||
if not unit or not unit:IsAlive() then return end
|
||||
local group = unit:GetGroup()
|
||||
if not group then return end
|
||||
fn(unit, group)
|
||||
end)
|
||||
elseif type(set.ForEachGroupAlive) == "function" then
|
||||
set:ForEachGroupAlive(function(group)
|
||||
if not group then return end
|
||||
local units = group:GetUnits()
|
||||
if not units then return end
|
||||
for _, unit in pairs(units) do
|
||||
if unit and unit:IsAlive() then
|
||||
fn(unit, group)
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
self:E(self.lid .. "ERROR: jammer set is not SET_GROUP, SET_CLIENT, or SET_PLAYER (no ForEachGroupAlive or ForEachClient method).")
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Update jammer aircraft states and build snapshot.
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_UpdateJammers()
|
||||
if not self._jammerEnabled then return self end
|
||||
self._jammerSnapshot = {}
|
||||
-- Process client aircraft
|
||||
if self._jammerClientSet then
|
||||
self:_IterateJammerSet(self._jammerClientSet, function(unit, group)
|
||||
local unitName = unit:GetName()
|
||||
if not self._jammerAircraft[unitName] then
|
||||
self._jammerAircraft[unitName] = {
|
||||
loadout = self._jammerDefaultLoadout,
|
||||
active = false,
|
||||
isClient = unit:IsPlayer(),
|
||||
hasLoadout = false,
|
||||
}
|
||||
else
|
||||
-- Re-check IsPlayer in case a human took the slot after first detection
|
||||
if not self._jammerAircraft[unitName].isClient and unit:IsPlayer() then
|
||||
self._jammerAircraft[unitName].isClient = true
|
||||
end
|
||||
end
|
||||
-- Setup menu for clients (works on ground or airborne)
|
||||
if self._jammerAircraft[unitName].isClient and not self._jammerMenusBuilt[unitName] then
|
||||
self:_SetupJammerMenu(unit, group)
|
||||
end
|
||||
-- Snapshot only includes airborne armed jammers
|
||||
local state = self._jammerAircraft[unitName]
|
||||
if state.active and unit:InAir() then
|
||||
local coord = unit:GetCoordinate()
|
||||
if coord then
|
||||
table.insert(self._jammerSnapshot, { coord = coord, loadout = state.loadout, name = unitName })
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
-- Process AI aircraft
|
||||
for _, aiEntry in ipairs(self._jammerAISets or {}) do
|
||||
self:_IterateJammerSet(aiEntry.set, function(unit, group)
|
||||
local unitName = unit:GetName()
|
||||
if not self._jammerAircraft[unitName] then
|
||||
self._jammerAircraft[unitName] = {
|
||||
loadout = aiEntry.loadout,
|
||||
active = false,
|
||||
isClient = false,
|
||||
hasLoadout = true,
|
||||
}
|
||||
end
|
||||
-- Snapshot only includes airborne armed jammers
|
||||
local state = self._jammerAircraft[unitName]
|
||||
if state.active and unit:InAir() then
|
||||
local coord = unit:GetCoordinate()
|
||||
if coord then
|
||||
table.insert(self._jammerSnapshot, { coord = coord, loadout = state.loadout, name = unitName })
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
-- Clean up dead units only (keep ground-bound clients so menus persist)
|
||||
-- Respawn detection: when client respawns, slot may briefly disappear then reappear
|
||||
local toRemove = {}
|
||||
for unitName, state in pairs(self._jammerAircraft) do
|
||||
local unit = UNIT:FindByName(unitName)
|
||||
if not unit or not unit:IsAlive() then
|
||||
table.insert(toRemove, unitName)
|
||||
end
|
||||
end
|
||||
for _, unitName in ipairs(toRemove) do
|
||||
if self._jammerAircraft[unitName] and self._jammerAircraft[unitName].active then
|
||||
self:__JammerDeactivated(1, unitName)
|
||||
end
|
||||
self._jammerAircraft[unitName] = nil
|
||||
self._jammerMenusBuilt[unitName] = nil
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Compute which SAMs are jammed this cycle.
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_ComputeJammedSAMs()
|
||||
self._jammedSAMs = {}
|
||||
if not self._jammerEnabled then return self end
|
||||
if #self._jammerSnapshot == 0 then return self end
|
||||
local M_TO_NM = 1.0 / 1852.0
|
||||
local allSAMs = {}
|
||||
local tables = self.automode
|
||||
and { self.SAM_Table_Long, self.SAM_Table_Medium, self.SAM_Table_Short, self.SAM_Table_PointDef }
|
||||
or { self.SAM_Table }
|
||||
for _, samTable in ipairs(tables) do
|
||||
for _, _data in pairs(samTable) do
|
||||
if not allSAMs[_data[1]] then allSAMs[_data[1]] = _data[2] end
|
||||
end
|
||||
end
|
||||
for samName, samCoord in pairs(allSAMs) do
|
||||
local params = self:_ResolveJammerParams(samName)
|
||||
if params then
|
||||
local survival = 1.0
|
||||
for _, jammer in ipairs(self._jammerSnapshot) do
|
||||
local distNM = samCoord:Get2DDistance(jammer.coord) * M_TO_NM
|
||||
local pJam = self:_JamGaussianExp(distNM, params, jammer.loadout)
|
||||
if pJam > 0 then survival = survival * (1.0 - pJam) end
|
||||
end
|
||||
local combinedProb = 1.0 - survival
|
||||
if combinedProb > 0 and math.random() < combinedProb then
|
||||
self._jammedSAMs[samName] = combinedProb
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Cycle debug logging (mirrors JammerDebug output, gated on MANTIS debug/verbose flags)
|
||||
if self.debug or self.verbose then
|
||||
local activeCount = #(self._jammerSnapshot or {})
|
||||
local jamCount = self:_CountTable(self._jammedSAMs)
|
||||
-- Only log if there's something interesting to report
|
||||
if activeCount > 0 or jamCount > 0 then
|
||||
local lines = {}
|
||||
table.insert(lines, string.format("%sJammer cycle: %d active aircraft, %d SAMs jammed",
|
||||
self.lid, activeCount, jamCount))
|
||||
-- List active jammers
|
||||
for _, jammer in ipairs(self._jammerSnapshot) do
|
||||
local cfg = self.JammerLoadouts[jammer.loadout]
|
||||
table.insert(lines, string.format(" ACTIVE: %s | loadout=%s",
|
||||
jammer.name, cfg and cfg.name or jammer.loadout))
|
||||
end
|
||||
-- List jammed SAMs
|
||||
for samName, prob in pairs(self._jammedSAMs) do
|
||||
table.insert(lines, string.format(" JAMMED: %s @ %.0f%%", samName, prob * 100))
|
||||
end
|
||||
local text = table.concat(lines, "\n")
|
||||
self:I(text)
|
||||
if self.debug then
|
||||
MESSAGE:New(text, 10, "MANTIS"):ToAll()
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Set up tiered F10 jammer menu for a player unit.
|
||||
-- @param #MANTIS self
|
||||
-- @param Wrapper.Unit#UNIT unit
|
||||
-- @param Wrapper.Group#GROUP group
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_SetupJammerMenu(unit, group)
|
||||
local unitName = unit:GetName()
|
||||
local groupName = group:GetName()
|
||||
if self._jammerMenusBuilt[unitName] then return self end
|
||||
self._jammerMenusBuilt[unitName] = true
|
||||
-- If this group already has a menu tree (e.g. respawn), remove it first
|
||||
if self._jammerGroupMenus and self._jammerGroupMenus[groupName] then
|
||||
self._jammerGroupMenus[groupName]:Remove()
|
||||
self._jammerGroupMenus[groupName] = nil
|
||||
end
|
||||
self._jammerGroupMenus = self._jammerGroupMenus or {}
|
||||
local rootMenu = MENU_GROUP:New(group, "Jammer Controls")
|
||||
self._jammerGroupMenus[groupName] = rootMenu
|
||||
-- Tiered loadout submenus
|
||||
local alq99Menu = MENU_GROUP:New(group, "ALQ-99 Loadouts", rootMenu)
|
||||
local alq249Menu = MENU_GROUP:New(group, "ALQ-249 Loadouts", rootMenu)
|
||||
local mixedMenu = MENU_GROUP:New(group, "Mixed Loadouts", rootMenu)
|
||||
local tierMenus = { ALQ99 = alq99Menu, ALQ249 = alq249Menu, Mixed = mixedMenu }
|
||||
for tierName, keys in pairs(self.JammerLoadoutTiers) do
|
||||
local parentMenu = tierMenus[tierName]
|
||||
for _, loadoutKey in ipairs(keys) do
|
||||
local cfg = self.JammerLoadouts[loadoutKey]
|
||||
if cfg then
|
||||
MENU_GROUP_COMMAND:New(group, cfg.name, parentMenu,
|
||||
self._JammerMenuSetLoadout, self, unitName, loadoutKey, group, rootMenu)
|
||||
end
|
||||
end
|
||||
end
|
||||
MESSAGE:New("JAMMER ONLINE\nSelect a loadout from the Jammer Controls menu.", 15, "JAMMER"):ToGroup(group)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] F10 callback: Set loadout and show Music toggle
|
||||
function MANTIS:_JammerMenuSetLoadout(unitName, loadoutKey, group, rootMenu)
|
||||
local state = self._jammerAircraft[unitName]
|
||||
if not state then return end
|
||||
state.loadout = loadoutKey
|
||||
state.hasLoadout = true
|
||||
local cfg = self.JammerLoadouts[loadoutKey]
|
||||
MESSAGE:New(string.format("LOADOUT SELECTED: %s\n%s", cfg and cfg.name or loadoutKey, cfg and cfg.description or ""), 12, "JAMMER"):ToGroup(group)
|
||||
-- Add Music toggle if not already present
|
||||
if not state._musicMenuAdded then
|
||||
state._musicMenuAdded = true
|
||||
state._musicOnMenu = nil
|
||||
state._musicOffMenu = nil
|
||||
self:_ShowMusicOn(unitName, group, rootMenu)
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Show "Music On" menu entry (jammer is OFF, click to turn ON)
|
||||
function MANTIS:_ShowMusicOn(unitName, group, rootMenu)
|
||||
local state = self._jammerAircraft[unitName]
|
||||
if not state then return end
|
||||
if state._musicOffMenu then state._musicOffMenu:Remove() state._musicOffMenu = nil end
|
||||
state._musicOnMenu = MENU_GROUP_COMMAND:New(group, "Music On", rootMenu,
|
||||
function()
|
||||
local s = self._jammerAircraft[unitName]
|
||||
if not s then return end
|
||||
s.active = true
|
||||
MESSAGE:New(string.format("JAMMER ACTIVE\n%s", self.JammerLoadouts[s.loadout] and self.JammerLoadouts[s.loadout].name or s.loadout), 10, "JAMMER"):ToGroup(group)
|
||||
self:__JammerActivated(1, unitName, s.loadout)
|
||||
self:_ShowMusicOff(unitName, group, rootMenu)
|
||||
end)
|
||||
end
|
||||
|
||||
--- [Internal] Show "Music Off" menu entry (jammer is ON, click to turn OFF)
|
||||
function MANTIS:_ShowMusicOff(unitName, group, rootMenu)
|
||||
local state = self._jammerAircraft[unitName]
|
||||
if not state then return end
|
||||
if state._musicOnMenu then state._musicOnMenu:Remove() state._musicOnMenu = nil end
|
||||
state._musicOffMenu = MENU_GROUP_COMMAND:New(group, "Music Off", rootMenu,
|
||||
function()
|
||||
local s = self._jammerAircraft[unitName]
|
||||
if not s then return end
|
||||
s.active = false
|
||||
MESSAGE:New("JAMMER SAFE", 10, "JAMMER"):ToGroup(group)
|
||||
self:__JammerDeactivated(1, unitName)
|
||||
self:_ShowMusicOn(unitName, group, rootMenu)
|
||||
end)
|
||||
end
|
||||
|
||||
--- On After "JammerSuppression" event
|
||||
function MANTIS:onafterJammerSuppression(From, Event, To, Group, Name, Probability)
|
||||
self:T({From, Event, To, Name, Probability})
|
||||
return self
|
||||
end
|
||||
|
||||
--- On After "JammerActivated" event
|
||||
function MANTIS:onafterJammerActivated(From, Event, To, UnitName, Loadout)
|
||||
self:T({From, Event, To, UnitName, Loadout})
|
||||
return self
|
||||
end
|
||||
|
||||
--- On After "JammerDeactivated" event
|
||||
function MANTIS:onafterJammerDeactivated(From, Event, To, UnitName)
|
||||
self:T({From, Event, To, UnitName})
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get table of currently jammed SAM group names.
|
||||
-- @param #MANTIS self
|
||||
-- @return #table {samName = probability}
|
||||
function MANTIS:GetJammedSAMs()
|
||||
return self._jammedSAMs or {}
|
||||
end
|
||||
|
||||
--- Get number of active jammer aircraft.
|
||||
-- @param #MANTIS self
|
||||
-- @return #number
|
||||
function MANTIS:GetActiveJammerCount()
|
||||
return self._jammerSnapshot and #self._jammerSnapshot or 0
|
||||
end
|
||||
|
||||
--- Print a debug report of the jammer system to dcs.log and screen.
|
||||
-- Lists registered aircraft, their states, snapshot count, and currently
|
||||
-- jammed SAMs with their probabilities. Use this to diagnose menu issues,
|
||||
-- missing aircraft, or unexpected SAM matching.
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean toScreen If true, also display report on screen (default false)
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:JammerDebug(toScreen)
|
||||
local lines = {}
|
||||
table.insert(lines, "=== MANTIS JAMMER DEBUG REPORT ===")
|
||||
table.insert(lines, string.format("Enabled: %s | Default loadout: %s",
|
||||
tostring(self._jammerEnabled), tostring(self._jammerDefaultLoadout)))
|
||||
table.insert(lines, string.format("Has client set: %s | AI sets: %d",
|
||||
tostring(self._jammerClientSet ~= nil), #(self._jammerAISets or {})))
|
||||
-- Aircraft state
|
||||
local count = 0
|
||||
for unitName, state in pairs(self._jammerAircraft or {}) do
|
||||
count = count + 1
|
||||
table.insert(lines, string.format(" [%s] active=%s isClient=%s loadout=%s hasLoadout=%s",
|
||||
unitName, tostring(state.active), tostring(state.isClient),
|
||||
tostring(state.loadout), tostring(state.hasLoadout)))
|
||||
end
|
||||
table.insert(lines, string.format("Total tracked aircraft: %d", count))
|
||||
table.insert(lines, string.format("Active jammer snapshot: %d aircraft airborne+armed", #(self._jammerSnapshot or {})))
|
||||
-- Jammed SAMs
|
||||
local jcount = 0
|
||||
for samName, prob in pairs(self._jammedSAMs or {}) do
|
||||
jcount = jcount + 1
|
||||
table.insert(lines, string.format(" JAMMED: %s @ %.0f%%", samName, prob * 100))
|
||||
end
|
||||
table.insert(lines, string.format("Currently jammed SAMs: %d", jcount))
|
||||
table.insert(lines, "=== END REPORT ===")
|
||||
local report = table.concat(lines, "\n")
|
||||
self:I(report)
|
||||
if toScreen then
|
||||
MESSAGE:New(report, 30, "JAMMER DEBUG"):ToAll()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Test the SAM resolver against a hypothetical group name.
|
||||
-- Useful for verifying naming convention before mission start.
|
||||
-- @param #MANTIS self
|
||||
-- @param #string testName The hypothetical SAM group name to test
|
||||
-- @return #string Matched JammerSAMParams key, or "NO MATCH"
|
||||
function MANTIS:JammerTestResolver(testName)
|
||||
local params = self:_ResolveJammerParams(testName)
|
||||
if not params then
|
||||
self:I(string.format("[JammerTestResolver] '%s' -> NO MATCH", testName))
|
||||
return "NO MATCH"
|
||||
end
|
||||
-- Find which key matched
|
||||
for key, p in pairs(self.JammerSAMParams) do
|
||||
if p == params then
|
||||
self:I(string.format("[JammerTestResolver] '%s' -> '%s' {peak=%d, mu=%d, band=%s, floor=%d}",
|
||||
testName, key, params.peak, params.mu, params.band, params.floor or 0))
|
||||
return key
|
||||
end
|
||||
end
|
||||
return "MATCHED (key unknown)"
|
||||
end
|
||||
|
||||
--- Manually set a jammer aircraft loadout (AI scripted control).
|
||||
-- @param #MANTIS self
|
||||
-- @param #string unitName DCS unit name
|
||||
-- @param #string loadoutKey Key into JammerLoadouts
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetJammerLoadout(unitName, loadoutKey)
|
||||
if self._jammerAircraft and self._jammerAircraft[unitName] then
|
||||
self._jammerAircraft[unitName].loadout = loadoutKey
|
||||
self._jammerAircraft[unitName].hasLoadout = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Manually activate/deactivate a jammer aircraft (AI scripted control).
|
||||
-- @param #MANTIS self
|
||||
-- @param #string unitName DCS unit name
|
||||
-- @param #boolean active true=on, false=off
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetJammerActive(unitName, active)
|
||||
if self._jammerAircraft and self._jammerAircraft[unitName] then
|
||||
local wasActive = self._jammerAircraft[unitName].active
|
||||
self._jammerAircraft[unitName].active = active
|
||||
if active and not wasActive then
|
||||
self:__JammerActivated(1, unitName, self._jammerAircraft[unitName].loadout)
|
||||
elseif not active and wasActive then
|
||||
self:__JammerDeactivated(1, unitName)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS Jammer Extension - Hook Overrides
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
-- Guard: only save originals once (prevents infinite recursion if file loaded twice)
|
||||
if not MANTIS._CheckLoopOriginal then
|
||||
MANTIS._CheckLoopOriginal = MANTIS._CheckLoop
|
||||
end
|
||||
if not MANTIS._onbeforeStatusOriginal then
|
||||
MANTIS._onbeforeStatusOriginal = MANTIS.onbeforeStatus
|
||||
end
|
||||
|
||||
--- [Internal] Override: _CheckLoop with jammer suppression.
|
||||
function MANTIS:_CheckLoop(samset, detset, dlink, limit)
|
||||
local r, g, s = self:_CheckLoopOriginal(samset, detset, dlink, limit)
|
||||
if self._jammerEnabled and self._jammedSAMs then
|
||||
for _, _data in pairs(samset) do
|
||||
local name = _data[1]
|
||||
if self._jammedSAMs[name] and self.SamStateTracker[name] == "RED" then
|
||||
local samgroup = GROUP:FindByName(name)
|
||||
if samgroup and samgroup:IsAlive() then
|
||||
if self.UseEmOnOff then
|
||||
samgroup:EnableEmission(false)
|
||||
else
|
||||
samgroup:OptionAlarmStateGreen()
|
||||
end
|
||||
self.SamStateTracker[name] = "GREEN"
|
||||
self:__JammerSuppression(1, samgroup, name, self._jammedSAMs[name])
|
||||
if self.ShoradLink then
|
||||
local Shorad = self.Shorad
|
||||
local shoradradius = self.checkradius
|
||||
local ontime = self.ShoradTime
|
||||
Shorad:WakeUpShorad(name, shoradradius, ontime, nil, true)
|
||||
self:__ShoradActivated(1, name, shoradradius, ontime)
|
||||
end
|
||||
if self.debug or self.verbose then
|
||||
self:T(string.format("%sJAMMED: %s forced GREEN (%.1f%%)", self.lid, name, self._jammedSAMs[name] * 100))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return r, g, s
|
||||
end
|
||||
|
||||
--- [Internal] Override: onbeforeStatus with jammer update.
|
||||
function MANTIS:onbeforeStatus(From, Event, To)
|
||||
if self._jammerEnabled then
|
||||
self:_UpdateJammers()
|
||||
self:_ComputeJammedSAMs()
|
||||
end
|
||||
return self:_onbeforeStatusOriginal(From, Event, To)
|
||||
end
|
||||
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS end
|
||||
|
||||
@@ -795,15 +795,20 @@ do
|
||||
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtcoord1 = tgtgrp1:GetCoordinate()
|
||||
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||
|
||||
if dist1 < dist2 then
|
||||
targetunit = tgtgrp1
|
||||
targetcat = Object.Category.UNIT
|
||||
if tgtgrp2 then
|
||||
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||
|
||||
if dist1 < dist2 then
|
||||
targetunit = tgtgrp1
|
||||
targetcat = Object.Category.UNIT
|
||||
else
|
||||
targetunit = tgtgrp2
|
||||
targetcat = Object.Category.UNIT
|
||||
end
|
||||
else
|
||||
targetunit = tgtgrp2
|
||||
targetunit = tgtgrp1
|
||||
targetcat = Object.Category.UNIT
|
||||
end
|
||||
end
|
||||
|
||||
@@ -204,7 +204,8 @@ do
|
||||
-- my_ctld.C130DynamicCargoAutoBuildMergeSeconds = 0 -- Merge window in seconds for C-130 auto-build handoff; set to 0 to disable batching (default).
|
||||
-- my_ctld.locale = "en" -- Language locale to use.
|
||||
-- -- Supported: "en" English (default), "de" German, "fr" French, "es" Spanish,
|
||||
-- -- "pt-br" Brazilian Portuguese, "ru" Russian, "tr" Turkish, "zh-cn" Simplified Chinese (Mainland China), "zh-tw" Traditional Chinese (Taiwan).
|
||||
-- -- "it" Italian, "pt-br" Brazilian Portuguese, "ru" Russian, "tr" Turkish,
|
||||
-- -- "zh-cn" Simplified Chinese (Mainland China), "zh-tw" Traditional Chinese (Taiwan).
|
||||
--
|
||||
-- ## 2.1 CH-47 Chinook support
|
||||
--
|
||||
@@ -1499,7 +1500,7 @@ function CTLD:_InitLocalization()
|
||||
end
|
||||
|
||||
--- [User] Set a resolver used to select a CTLD locale for group-specific menus and messages.
|
||||
-- Supported locales: "en", "de", "fr", "es", "pt-br", "ru", "tr", "zh-cn", "zh-tw".
|
||||
-- Supported locales: "en", "de", "fr", "es", "it", "pt-br", "ru", "tr", "zh-cn", "zh-tw".
|
||||
-- If unset, CTLD keeps using `self.locale` as before.
|
||||
-- @param #CTLD self
|
||||
-- @param #function Resolver Function called as `Resolver(self, Group)` and expected to return a locale string.
|
||||
@@ -1542,6 +1543,10 @@ function CTLD:_GetMenuPluralSuffix(Count, Kind, Group)
|
||||
return "ов"
|
||||
end
|
||||
if locale == "tr" then return "" end
|
||||
if locale == "it" then
|
||||
if Kind == "crate" then return count > 1 and "e" or "a" end
|
||||
if Kind == "set" then return "" end
|
||||
end
|
||||
if locale == "de" and Kind == "crate" then return count > 1 and "n" or "" end
|
||||
return count > 1 and "s" or ""
|
||||
end
|
||||
|
||||
@@ -955,6 +955,265 @@ FR = {
|
||||
BUILD_YES="SI",
|
||||
BUILD_NO="NO",
|
||||
},
|
||||
IT = {
|
||||
-- ============================================================
|
||||
-- Crate / Cargo Loading
|
||||
-- ============================================================
|
||||
CRATE_LOADED_GROUNDCREW = "Cassa %s caricata dal personale di terra!",
|
||||
CRATE_UNLOADED_GROUNDCREW = "Cassa %s scaricata dal personale di terra!",
|
||||
CRATE_LOADED_ID = "ID cassa %d per %s caricato!",
|
||||
LOADED_FULL = "Caricato %d %s.",
|
||||
LOADED_SETS_LEFTOVER = "Caricato %d %s(s), con %d casse rimanenti.",
|
||||
LOADED_SETS = "Caricato %d %s(s).",
|
||||
LOADED_PARTIAL = "Caricato solo %d/%d casse di %s.",
|
||||
LOADED_PARTIAL_LIMIT = "Caricato solo %d/%d casse di %s. Il limite di carico è stato raggiunto.!",
|
||||
LOADED_BATCH = "Caricato %d %s.",
|
||||
LOADED_BATCH_PARTIAL = "Alcuni set non sono stati caricati completamente.",
|
||||
-- ============================================================
|
||||
-- Dropping / Unloading
|
||||
-- ============================================================
|
||||
DROPPED_FULL = "Scaricati %d %s.",
|
||||
DROPPED_SETS_LEFTOVER = "Sono stati scaricati %d %s(s), con %d casse rimanenti.",
|
||||
DROPPED_SETS = "Scaricati %d %s(s).",
|
||||
DROPPED_PARTIAL = "Scaricati %d/%d cassa(e) di %s.",
|
||||
DROPPED_INTO_ACTION = "Scaricati %s in azione!",
|
||||
DROPPED_BEACON = "Scaricati %s | FM %s Mhz | VHF %s KHz | UHF %s Mhz ",
|
||||
CRATES_POSITIONED = "%d Le casse per %s sono state posizionate vicino a te!",
|
||||
CRATES_DROPPED = "%d casse per %s sono state rilasciate!",
|
||||
-- ============================================================
|
||||
-- Troops
|
||||
-- ============================================================
|
||||
BOARDED = "%s a bordo!",
|
||||
BOARDING = "%s Imbarco!",
|
||||
TROOPS_RETURNED = "Le truppe sono rientrate alla base!",
|
||||
TROOPS_LABEL = "Ttuppe",
|
||||
ENGINEERS_LABEL = "Ingenieri",
|
||||
-- ============================================================
|
||||
-- Deployment
|
||||
-- ============================================================
|
||||
DEPLOYED_NEAR_YOU = "%s sono stati dispiegati vicino a te!",
|
||||
UNITS_REMOVED = "%s sono stati rimossi",
|
||||
-- ============================================================
|
||||
-- Build / Repair
|
||||
-- ============================================================
|
||||
BUILD_STARTED = "Costruzione avviata, pronta tra %d secondi!",
|
||||
REPAIR_STARTED = "Riparazione avviata utilizzando %s e impiegando %d secondi",
|
||||
NO_UNIT_TO_REPAIR = "Nessuna unità è abbastanza vicina per essere riparata!",
|
||||
CANT_REPAIR_WITH = "Non è possibile riparare questa unità con %s",
|
||||
CRATES_MOVE_BEFORE_BUILD = "*** Le casse devono essere spostate prima della costruzione.!",
|
||||
-- ============================================================
|
||||
-- Errors - Chopper / Weight / Capacity
|
||||
-- ============================================================
|
||||
CHOPPER_CANNOT_CARRY = "Spiacenti, questo elicottero non può trasportare casse!",
|
||||
TOO_HEAVY = "Mi dispiace, è troppo pesante da caricare.!",
|
||||
FULLY_LOADED = "Ci dispiace, abbiamo raggiunto la capacità massima!",
|
||||
CRAMMED = "Ci dispiace, siamo già pieni.!",
|
||||
NO_CAPACITY_NOW = "Impossibile caricare altro al momento!",
|
||||
NO_MORE_CAPACITY = "Non c'è più capacità di carico per le casse!",
|
||||
CANNOT_LOAD_NONE_OR_FULL = "Impossibile caricare le casse: non ne sono state trovate oppure non c'è più spazio disponibile.",
|
||||
-- ============================================================
|
||||
-- Errors - Position
|
||||
-- ============================================================
|
||||
NEED_TO_LAND_OR_HOVER_LOAD = "È necessario atterrare o rimanere in posizione per caricare!",
|
||||
HOVER_OVER_CRATES = "Resta in hover sopra le casse per raccoglierle!",
|
||||
LAND_OR_HOVER_OVER_CRATES = "Atterra o sorvola le casse per raccoglierle!",
|
||||
MUST_LAND_OR_HOVER_CRATES = "Devi atterrare o rimanere in volo stazionario per caricare le casse!",
|
||||
NEED_TO_LAND_BUILD = "Devi atterrare/fermarti per costruire qualcosa, pilota!",
|
||||
NOT_CLOSE_ENOUGH_LOGISTICS = "Non sei abbastanza vicino a una zona logistica!",
|
||||
NOT_CLOSE_ENOUGH_DROP = "Non sei abbastanza vicino a una zona di lancio!",
|
||||
NOT_CLOSE_ENOUGH_ZONE_NM = "Negativo, è necessario essere più vicini di %dnm a una zona!",
|
||||
CANNOT_BUILD_LOADING_AREA = "Non è possibile costruire in un'area di carico, Pilota!",
|
||||
-- ============================================================
|
||||
-- Errors - Doors
|
||||
-- ============================================================
|
||||
OPEN_DOORS_LOAD_CARGO = "È necessario aprire la/le porta/e per caricare la merce!",
|
||||
OPEN_DOORS_LOAD_TROOPS = "È necessario aprire la/le porta/e per caricare le truppe!",
|
||||
OPEN_DOORS_EXTRACT_TROOPS = "È necessario aprire la/le porta/e per estrarre le truppe!",
|
||||
OPEN_DOORS_UNLOAD_TROOPS = "Devi aprire la/le porta/e per far scendere le truppe!",
|
||||
OPEN_DOORS_DROP_CARGO = "È necessario aprire la/le porta/e per scaricare il carico!",
|
||||
-- ============================================================
|
||||
-- Errors - Stock / Availability
|
||||
-- ============================================================
|
||||
ALL_GONE = "Spiacenti, tutti i %s sono esauriti!",
|
||||
RAN_OUT_OF = "Ci dispiace, abbiamo finito %s",
|
||||
CARGO_NOT_AVAILABLE_ZONE = "Il carico richiesto non è disponibile in questa zona!",
|
||||
ENOUGH_CRATES_NEARBY = "Ci sono già abbastanza casse qui vicino! Occupati prima di quelle!",
|
||||
NO_CRATES_WITHIN = "Nessuna cassa (caricabile) entro %d metri!",
|
||||
NO_CRATES_WITHIN_PLAIN = "Niente casse entro %d metri!",
|
||||
NO_CRATES_IN_RANGE = "Nessuna cassa trovata nel raggio d'azione!",
|
||||
NO_NAMED_CRATES_IN_RANGE = "Nessuna \"%s\" Casse trovate nel raggio d'azione!",
|
||||
NO_LOADABLE_CRATES = "Spiacenti, nessuna cassa caricabile nelle vicinanze oppure peso massimo del carico raggiunto!",
|
||||
NO_UNITS_TO_EXTRACT = "Nessuna unità è abbastanza vicina per l'estrazione!",
|
||||
NO_UNIT_CONFIG = "Nessuna configurazione dell'unità trovata per %s",
|
||||
CANT_ONBOARD = "Impossibile salire a bordo %s",
|
||||
TOO_MANY_UNITS_NEARBY = "Hai già %d unità nelle vicinanze!",
|
||||
NO_CRATE_GROUPS = "Nessun gruppo di casse trovato per questa unità!",
|
||||
NO_CRATE_SET = "Nessun set di casse trovato o indice non valido!",
|
||||
NO_CRATE_IN_SET = "Nessuna cassa trovata in quel set!",
|
||||
NO_TROOP_CHUNK = "Nessun blocco di carico truppe trovato per l'ID %d!",
|
||||
TROOP_CHUNK_EMPTY = "Il blocco truppe è vuoto per l'ID %d!",
|
||||
-- ============================================================
|
||||
-- Nothing loaded / in stock
|
||||
-- ============================================================
|
||||
NOTHING_LOADED = "Nessun caricamento effettuato!\nLimite truppe: %d | Limite casse: %d | Limite peso: %d kg",
|
||||
NOTHING_LOADED_AIRDROP = "Nessun file caricato o non conforme ai parametri di AirDrop.!",
|
||||
NOTHING_LOADED_HOVER = "Nessun caricamento effettuato o il puntatore del mouse non si trova all'interno dei parametri!",
|
||||
NOTHING_IN_STOCK = "Nessun prodotto disponibile!",
|
||||
NOTHING_TO_PACK = "Nothing to pack at this distance pilot!",
|
||||
NOTHING_TO_REMOVE = "Niente da rimuovere a questa distanza pilota!",
|
||||
-- ============================================================
|
||||
-- Zone / Info
|
||||
-- ============================================================
|
||||
ROGER_ZONE = "Roger, %s Zona %s!",
|
||||
-- ============================================================
|
||||
-- Report: Hover / Flight Parameters
|
||||
-- ============================================================
|
||||
HOVER_PARAMS_METRIC = "Hover parametri (caricamento/rilascio automatico):\n - Altezza minima %dm \n - Altezza massima %dm \n - Velocità massima 2mps \n - Nel parametro: %s",
|
||||
HOVER_PARAMS_IMPERIAL = "Hover parametri (caricamento/rilascio automatico):\n - Altezza minima %dft \n - Altezza massima %dft \n - Velocità massima 6ftps \n - Nel parametro: %s",
|
||||
FLIGHT_PARAMS_IMPERIAL = "Flight parameters (airdrop):\n - Altezza minima %dft \n -Altezza massima %dft \n - Nel parametro: %s",
|
||||
FLIGHT_PARAMS_METRIC = "Flight parameters (airdrop):\n - Altezza minima %dm \n - Altezza massima %dm \n - Nel parametro: %s",
|
||||
-- ============================================================
|
||||
-- Report Titles (REPORT:New())
|
||||
-- ============================================================
|
||||
REPORT_CRATES_FOUND = "Casse trovate nelle vicinanze:",
|
||||
REPORT_REMOVING_CRATES = "Rimozione delle casse trovate nelle vicinanze:",
|
||||
REPORT_TRANSPORT_CHECKOUT = "Documento di verifica del trasporto",
|
||||
REPORT_INVENTORY = "Documento di inventario",
|
||||
REPORT_BUILD_CHECKLIST = "Checklist Buildable Crates",
|
||||
REPORT_REPAIR_CHECKLIST = "Lista di controllo per le riparazioni",
|
||||
REPORT_BEACONS = "Segnalatori di zona attiva",
|
||||
-- ============================================================
|
||||
-- Report Section Headers (report:Add())
|
||||
-- ============================================================
|
||||
REPORT_SECTION_TROOPS = " -- TRUPPE --",
|
||||
REPORT_SECTION_CRATES = " -- CASSE --",
|
||||
REPORT_SECTION_CRATES_GC = " -- CASSE caricate tramite Ground Crew --",
|
||||
REPORT_SECTION_NONE = " NESSUNO",
|
||||
REPORT_SECTION_NONE_ALT = " --- Nessun risultato trovato! ---",
|
||||
REPORT_SECTION_NONE_REPAIR = " --- Nessun risultato trovato ---",
|
||||
REPORT_GC_LOADABLE_HINT = "Probabilmente caricabile dal personale di terra (F8)",
|
||||
REPORT_TOTAL_MASS = "Massa totale: %s kg. Caricabile: %s kg.",
|
||||
REPORT_TROOPS_CRATES_COUNT = "Truppe: %d(%d), Casse: %d(%d)",
|
||||
REPORT_TROOPS_CRATETYPES_COUNT = "Truppe: %d, Tipi di casse: %d",
|
||||
-- ============================================================
|
||||
-- Report Row Templates (per-item lines in reports)
|
||||
-- ============================================================
|
||||
REPORT_ROW_TROOP = "Truppa: %s dimensione %d",
|
||||
REPORT_ROW_CRATE = "Casse: %s %d/%d",
|
||||
REPORT_ROW_CRATE_SIZE1 = "Cassa: %s dimensione 1",
|
||||
REPORT_ROW_GC_CRATE = "GC Cassa caricata: %s dimensione 1",
|
||||
REPORT_ROW_DROPPED_CRATE = "Cassa scaricata per %s, %dkg",
|
||||
REPORT_ROW_CRATE_KG = "Cassa per %s, %dkg",
|
||||
REPORT_ROW_CRATE_REMOVED = "Cassa per %s, %dkg rimossa",
|
||||
REPORT_ROW_UNIT_STOCK = "Unità: %s | Soldati: %d | Stock: %s",
|
||||
REPORT_ROW_TYPE_CRATE_STOCK = "Tipo: %s | Casse per set: %d | Stock: %s",
|
||||
REPORT_ROW_TYPE_STOCK = "Tipo: %s | Stock: %s",
|
||||
REPORT_ROW_BUILD_CHECK = "Tipo: %s | Richiesto %d | Trovato %d | puo' essere costruita %s",
|
||||
REPORT_ROW_REPAIR_CHECK = "Tipo: %s | Richiesto %d | Trovato %d | Riparabile %s",
|
||||
REPORT_ROW_BEACON = " %s | FM %s Mhz | VHF %s KHz | UHF %s Mhz ",
|
||||
-- ============================================================
|
||||
-- Weight / Crate limit tokens
|
||||
-- ============================================================
|
||||
WEIGHT_LIMIT = "Limite di peso raggiunto",
|
||||
CRATE_LIMIT = "Limite di casse raggiunto",
|
||||
-- ============================================================
|
||||
-- Menu labels - Top level
|
||||
-- ============================================================
|
||||
MENU_CTLD = "CTLD",
|
||||
MENU_MANAGE_TROOPS = "Gestire le truppe",
|
||||
MENU_MANAGE_CRATES = "Gestisci casse",
|
||||
MENU_MANAGE_UNITS = "Gestisci unita'",
|
||||
-- ============================================================
|
||||
-- Menu labels - Troops
|
||||
-- ============================================================
|
||||
MENU_LOAD_TROOPS = "Carica truppe",
|
||||
MENU_DROP_TROOPS = "Scarica truppe",
|
||||
MENU_DROP_ALL_TROOPS = "Scarica tutte le truppe",
|
||||
MENU_EXTRACT_TROOPS = "Estrai truppe",
|
||||
MENU_DROP_N_TROOPS = "Scarica (%d) %s",
|
||||
-- ============================================================
|
||||
-- Menu labels - Crates: Get
|
||||
-- ============================================================
|
||||
MENU_GET_CRATES = "Prendi casse",
|
||||
MENU_GET = "Prendi",
|
||||
MENU_GET_AND_LOAD = "Prendi e carica",
|
||||
MENU_GET_ANYWAY = "Prendi comunque",
|
||||
MENU_PARTIALLY_LOAD = "Carico parziale",
|
||||
MENU_OUT_OF_STOCK = "Esaurito",
|
||||
MENU_TROOP_LIMIT = "Limite truppe raggiunto",
|
||||
-- ============================================================
|
||||
-- Menu labels - Crates: Load
|
||||
-- ============================================================
|
||||
MENU_LOAD_CRATES = "Carica casse",
|
||||
MENU_LOAD_ALL = "Carica tutto",
|
||||
MENU_SHOW_LOADABLE_CRATES = "Mostra casse caricate",
|
||||
MENU_NO_CRATES_FOUND_RESCAN = "Nessuna cassa trovata! Esegui una nuova scansione.?",
|
||||
MENU_USE_C130_LOAD = "Utilizzare il sistema di carico C-130",
|
||||
MENU_LOAD_SINGLE = "Carica",
|
||||
-- ============================================================
|
||||
-- Menu labels - Crates: Drop
|
||||
-- ============================================================
|
||||
MENU_DROP_CRATES = "Scarica casse",
|
||||
MENU_DROP_ALL_CRATES = "Scarica tutte le casse",
|
||||
MENU_DROP = "Scarica",
|
||||
MENU_DROP_AND_BUILD = "Scarica e costruisci",
|
||||
MENU_DROP_N_SETS = "Scarica %d Set%s",
|
||||
MENU_NO_CRATES_TO_DROP = "Nessuna cassa da scaricare!",
|
||||
-- ============================================================
|
||||
-- Menu labels - Crates: Build / Repair / Pack / Remove
|
||||
-- ============================================================
|
||||
MENU_BUILD_CRATES = "Sballa le casse",
|
||||
MENU_REPAIR = "Ripara",
|
||||
MENU_PACK_CRATES = "Casse da imballaggio",
|
||||
MENU_PACK = "Imballaggio",
|
||||
MENU_SCAN_PACKABLE_UNITS = "Scansiona le unità imballabili nelle vicinanze",
|
||||
MENU_NO_PACKABLE_UNITS_FOUND_RESCAN = "Nessuna unità imballabile trovata! Esegui una nuova scansione.?",
|
||||
MENU_PACK_ALL = "Pacchetto nelle vicinanze",
|
||||
MENU_PACK_AND_LOAD = "Imballare e caricare",
|
||||
MENU_PACK_AND_LOAD_ALL = "Imballaggio e carico nelle vicinanze",
|
||||
MENU_PACK_AND_REMOVE = "Imballare e rimuovere",
|
||||
MENU_PACK_AND_REMOVE_ALL = "Imballare e rimuovere nelle vicinanze",
|
||||
MENU_REMOVE_CRATES = "Rimuovere le casse",
|
||||
MENU_REMOVE_CRATES_NEARBY = "Rimuovere le casse nelle vicinanze",
|
||||
MENU_LIST_CRATES_NEARBY = "Elenco delle casse nelle vicinanze",
|
||||
MENU_CRATES_NEEDED = "%d cass%s %s (%dkg)",
|
||||
MENU_CRATE_SINGLE = "%s (%dkg)",
|
||||
-- ============================================================
|
||||
-- Menu labels - Units (C-130)
|
||||
-- ============================================================
|
||||
MENU_GET_UNITS = "Ottieni unità",
|
||||
MENU_REMOVE_UNITS_NEARBY = "Rimuovere le unità vicine",
|
||||
-- ============================================================
|
||||
-- Menu labels - Info / Cargo
|
||||
-- ============================================================
|
||||
MENU_LIST_BOARDED_CARGO = "Lista del carico a bordo",
|
||||
MENU_INVENTORY = "Inventario",
|
||||
MENU_LIST_ZONE_BEACONS = "Elenco dei beacon di zona attivi",
|
||||
-- ============================================================
|
||||
-- Menu labels - Smokes / Flares / Beacons
|
||||
-- ============================================================
|
||||
MENU_SMOKES_FLARES_BEACONS = "Fumogeni, razzi, segnalatori luminosi",
|
||||
MENU_SMOKE_ZONES_NEARBY = "Fumogeni nelle vicinanze",
|
||||
MENU_DROP_SMOKE_NOW = "Scarica fumogeni ora",
|
||||
MENU_RED_SMOKE = "Fumogeno rosso",
|
||||
MENU_BLUE_SMOKE = "Fumogeno blu",
|
||||
MENU_GREEN_SMOKE = "Fumogeno verde",
|
||||
MENU_ORANGE_SMOKE = "Fumogeno arancione",
|
||||
MENU_WHITE_SMOKE = "Fumogeno bianco",
|
||||
MENU_FLARE_ZONES_NEARBY = "Razzi di segnalazione nelle vicinanze",
|
||||
MENU_FIRE_FLARE_NOW = "Razzi di segnalazione ora",
|
||||
MENU_DROP_BEACON_NOW = "Scarica segnalatori luminosi",
|
||||
-- ============================================================
|
||||
-- Menu labels - Parameters
|
||||
-- ============================================================
|
||||
MENU_SHOW_FLIGHT_PARAMS = "Mostra i parametri di volo",
|
||||
MENU_SHOW_HOVER_PARAMS = "Mostra parametri di hovering",
|
||||
STOCK_NONE = "Nessuno",
|
||||
STOCK_UNLIMITED = "Illimitato",
|
||||
BUILD_YES = "SI",
|
||||
BUILD_NO = "NO",
|
||||
},
|
||||
|
||||
["PT-BR"] = {
|
||||
-- ============================================================
|
||||
-- Carregamento de caixa / carga
|
||||
|
||||
@@ -118,21 +118,21 @@ TARS_SESSION.debugunitsearch = false
|
||||
-- your mission before loading.
|
||||
--
|
||||
-- ## Player workflow
|
||||
-- 1. Spawn into a recon-capable slot and open the **F10 › Task TARS** radio menu.
|
||||
-- 1. Spawn into a recon-capable slot and open the **F10 › Task TARS** radio menu.
|
||||
-- 2. Select **TARS validation** on the ground. The system checks your loadout and
|
||||
-- reports the platform's altitude band, FOV, and available film.
|
||||
-- The validation item then disappears once approved.
|
||||
-- 3. Take off. **Start filming / STB & Resume / Stop filming** appear in the menu.
|
||||
-- 4. Select **TARS mode : Start filming** to begin recording.
|
||||
-- 5. Fly over enemy units within the sensor's altitude/attitude envelope.
|
||||
-- Each detected unit is confirmed in the HUD (`+1 Captured target`).
|
||||
-- 6. Use **TARS mode : STB & Resume** to pause (e.g. to refuel).
|
||||
-- Film resumes automatically on the next takeoff if the loadout is still valid.
|
||||
-- 7. Select **TARS mode : Stop filming** or let the film timer expire.
|
||||
-- 8. Land at an allied airbase or FARP within `TARS.landingDistance` metres.
|
||||
-- reports the platform's altitude band, FOV, and available film.
|
||||
-- The validation item then disappears once approved.
|
||||
-- 3. Take off. **Start filming / STB & Resume / Stop filming** appear in the menu.
|
||||
-- 4. Select **TARS mode : Start filming** to begin recording.
|
||||
-- 5. Fly over enemy units within the sensor's altitude/attitude envelope.
|
||||
-- Each detected unit is confirmed in the HUD (`+1 Captured target`).
|
||||
-- 6. Use **TARS mode : STB & Resume** to pause (e.g. to refuel).
|
||||
-- Film resumes automatically on the next takeoff if the loadout is still valid.
|
||||
-- 7. Select **TARS mode : Stop filming** or let the film timer expire.
|
||||
-- 8. Land at an allied airbase or FARP within `TARS.landingDistance` metres.
|
||||
-- The film controls disappear. After `landingDelay + debriefDelay` seconds the
|
||||
-- intel marks appear on the F10 map and scoring credits are awarded.
|
||||
-- After the debrief the **TARS validation** item reappears for the next sortie.
|
||||
-- intel marks appear on the F10 map and scoring credits are awarded.
|
||||
-- After the debrief the **TARS validation** item reappears for the next sortie.
|
||||
--
|
||||
-- ## Localization
|
||||
--
|
||||
@@ -274,7 +274,7 @@ TARS = {}
|
||||
-- @field #string playerName Player display name.
|
||||
|
||||
--- Frozen target snapshot.
|
||||
-- @type TARS.Snapshot
|
||||
-- @type TARS.Snapshot #TARS.Snapshot
|
||||
-- @field Wrapper.Unit#UNIT unit MOOSE UNIT wrapper or STATIC wrapper.
|
||||
-- @field DCS#Object dcsObj Raw DCS object reference.
|
||||
-- @field #number category `Object.Category.*` of the detected object.
|
||||
@@ -294,7 +294,7 @@ TARS = {}
|
||||
-------------------------------------------------
|
||||
|
||||
--- @field #string version
|
||||
TARS.version = "v2.3.1"
|
||||
TARS.version = "v2.3.2"
|
||||
|
||||
--- Active locale.
|
||||
-- @field #string locale
|
||||
@@ -1804,7 +1804,7 @@ function TARS:_OnEventBirth(EventData)
|
||||
if not unit then return end
|
||||
local instance = self:GetInstance(unit:GetName())
|
||||
if instance then instance:Delete() end
|
||||
local playerName = unit:GetPlayerName()
|
||||
local playerName = EventData.IniPlayerName --unit:GetPlayerName()
|
||||
if not playerName then return end
|
||||
local pName = playerName
|
||||
timer.scheduleFunction(function()
|
||||
@@ -1818,6 +1818,7 @@ end
|
||||
-- @param #TARS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function TARS:_OnEventEngineStartup(EventData)
|
||||
if EventData.IniPlayerName == nil then return end
|
||||
local unit = EventData.IniUnit
|
||||
if not unit or not unit:GetPlayerName() then return end
|
||||
local pName = unit:GetPlayerName()
|
||||
@@ -1835,7 +1836,7 @@ function TARS:_OnEventDead(EventData)
|
||||
local unit = EventData.IniUnit
|
||||
if not unit then return end
|
||||
local name = unit:GetName()
|
||||
local playerName = unit:GetPlayerName() or unit:GetName()
|
||||
local playerName = EventData.IniPlayerName --unit:GetPlayerName() or unit:GetName()
|
||||
if TARS.groundMenus[playerName] then self:RemoveGroundMenu(playerName) end
|
||||
if self.detectedTargets[name] then
|
||||
local markID = self.marks.blue[name] or self.marks.red[name]
|
||||
@@ -1852,7 +1853,7 @@ end
|
||||
function TARS:_OnEventPlayerLeaveUnit(EventData)
|
||||
local unit = EventData.IniUnit
|
||||
if not unit then return end
|
||||
local playerName = unit:GetPlayerName() or unit:GetName()
|
||||
local playerName = EventData.IniPlayerName --unit:GetPlayerName() or unit:GetName()
|
||||
if TARS.groundMenus[playerName] then self:RemoveGroundMenu(playerName) end
|
||||
end
|
||||
|
||||
@@ -1863,11 +1864,12 @@ end
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function TARS:_OnEventTakeOff(EventData)
|
||||
self:T(self.lid.."_OnEventTakeOff")
|
||||
if EventData.IniPlayerName == nil then return end
|
||||
local unit = EventData.IniUnit
|
||||
if not unit then return end
|
||||
local instance = self:GetInstance(unit:GetName())
|
||||
local now = timer.getTime()
|
||||
|
||||
|
||||
-- Branch 1: auto-resume after ground STB
|
||||
if instance and instance.capturing then
|
||||
if instance.lastTakeoffTime and (now - instance.lastTakeoffTime) < 5 then return end
|
||||
@@ -1903,7 +1905,8 @@ function TARS:_OnEventTakeOff(EventData)
|
||||
if instance and instance.lastTakeoffTime and (now - instance.lastTakeoffTime) < 5 then
|
||||
return
|
||||
end
|
||||
local playerName = unit:GetPlayerName() or unit:GetName()
|
||||
local playerName = EventData.IniPlayerName --unit:GetPlayerName() or unit:GetName()
|
||||
if playerName == nil then return end
|
||||
local groundData = TARS.groundMenus[playerName]
|
||||
if not (groundData and groundData.approved) then return end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user