mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-21 06:04:49 +00:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8f5214e0b | |||
| b6ea6606fc | |||
| e4c7e809cb | |||
| aab16dd966 | |||
| 8b356f0913 | |||
| 5005203206 | |||
| a3455855b8 | |||
| d1e4dd3fe9 | |||
| 9326cfad1c | |||
| b8e114101b | |||
| f083b77390 | |||
| db3bc3742e | |||
| bf10196662 | |||
| a0420e1cb6 | |||
| c3086b0265 | |||
| 599a2ba6fb | |||
| 27bd27a2e5 | |||
| 8e219c5877 | |||
| 3d1dcd78a3 | |||
| 9cda1b88ea | |||
| 09f99b9a74 | |||
| ff5205e686 | |||
| ebd105f80d | |||
| cc8a4e0bad | |||
| 5f70e2d6b1 | |||
| 95f38fbf55 | |||
| aec3466f13 | |||
| 696630fe4d | |||
| 47aa524677 | |||
| 79f03c164c | |||
| ca7d3a48a7 | |||
| 2556dbe145 | |||
| af1e9546e5 | |||
| 3203a22668 | |||
| b69ac2f86f | |||
| 0d410f3e55 | |||
| cbe6756330 | |||
| 07045be32b | |||
| 52a2132463 | |||
| 9272b1f18c | |||
| b84880c0dd | |||
| a9be18b284 | |||
| 93178df67f | |||
| f34bf1e052 | |||
| e159eb83b7 | |||
| 2581836058 | |||
| d9651e1dea | |||
| 0347f185a5 | |||
| 5e2218de38 | |||
| 6db5441bb1 | |||
| 13b7e43729 | |||
| a72f7298d0 | |||
| 801cc105da | |||
| f186b4bd58 | |||
| 79f13fa6ae | |||
| d38fc4ec8f | |||
| ba1575d674 | |||
| 391182e23f | |||
| 6d5fb64fde | |||
| 5df1fb88f1 | |||
| 2e4d4f056a | |||
| a9ceda4edd | |||
| e76fd23396 | |||
| 7df1eb963f | |||
| aff1d3c7db | |||
| d15f6cdb44 | |||
| c59b668eec | |||
| 4620a83317 | |||
| 87d0c4e9d4 | |||
| 74361f6a3a | |||
| b82b882e02 | |||
| 347783d4b7 | |||
| 0b2f07ca6c | |||
| 7c5d5543e0 | |||
| 3d10716b1f | |||
| c48a7d4400 | |||
| 79130d4fe2 | |||
| aebb6ac0db | |||
| 3eb910b47e | |||
| 6223b8e1a7 | |||
| 533a4dfa6c | |||
| dbc752253b | |||
| 7f074781f4 | |||
| ab77fcbc1f | |||
| 56520092c3 | |||
| 252a1b0761 | |||
| 51e5f53965 | |||
| b0976d1613 | |||
| a737ca71e6 | |||
| 1071300a7f | |||
| 2490958090 | |||
| b78b76e579 | |||
| efff22fc18 | |||
| 4b27e92210 | |||
| f2063f41f6 | |||
| 12241e931a | |||
| 4cb6debef8 | |||
| efe4bf3912 |
@@ -855,7 +855,7 @@ end
|
|||||||
-- @param DCS#Time EventTime The time stamp of the event.
|
-- @param DCS#Time EventTime The time stamp of the event.
|
||||||
-- @param DCS#Object Initiator The initiating object of the event.
|
-- @param DCS#Object Initiator The initiating object of the event.
|
||||||
function BASE:CreateEventTakeoff( EventTime, Initiator )
|
function BASE:CreateEventTakeoff( EventTime, Initiator )
|
||||||
self:F( { EventTime, Initiator } )
|
--self:F( { EventTime, Initiator } )
|
||||||
|
|
||||||
local Event = {
|
local Event = {
|
||||||
id = world.event.S_EVENT_TAKEOFF,
|
id = world.event.S_EVENT_TAKEOFF,
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ DATABASE = {
|
|||||||
ZONES_GOAL = {},
|
ZONES_GOAL = {},
|
||||||
WAREHOUSES = {},
|
WAREHOUSES = {},
|
||||||
FLIGHTGROUPS = {},
|
FLIGHTGROUPS = {},
|
||||||
|
COHORTS={},
|
||||||
|
LEGIONS={},
|
||||||
FLIGHTCONTROLS = {},
|
FLIGHTCONTROLS = {},
|
||||||
OPSZONES = {},
|
OPSZONES = {},
|
||||||
PATHLINES = {},
|
PATHLINES = {},
|
||||||
@@ -1380,12 +1382,12 @@ function DATABASE:_RegisterDynamicGroup(Groupname)
|
|||||||
local DCSUnitName = DCSUnit:getName()
|
local DCSUnitName = DCSUnit:getName()
|
||||||
|
|
||||||
-- Add unit.
|
-- Add unit.
|
||||||
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
self:T(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||||
self:AddUnit( tostring(DCSUnitName), true )
|
self:AddUnit( tostring(DCSUnitName), true )
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E({"Group does not exist: ", DCSGroup})
|
self:T({"Group does not exist: ", DCSGroup})
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -1488,27 +1490,28 @@ end
|
|||||||
-- @return #DATABASE self
|
-- @return #DATABASE self
|
||||||
function DATABASE:_RegisterAirbase(airbase)
|
function DATABASE:_RegisterAirbase(airbase)
|
||||||
|
|
||||||
local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
|
--local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
|
||||||
local countHSyria = 0
|
--local countHSyria = 0
|
||||||
|
|
||||||
if airbase then
|
if airbase then
|
||||||
|
|
||||||
-- Get the airbase name.
|
-- Get the airbase name.
|
||||||
local DCSAirbaseName = airbase:getName()
|
local DCSAirbaseName = airbase:getName()
|
||||||
|
|
||||||
-- DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
|
--[[ DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
|
||||||
if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then
|
if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then
|
||||||
--[[
|
--
|
||||||
local p = airbase:getPosition().p
|
local p = airbase:getPosition().p
|
||||||
local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS()
|
local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS()
|
||||||
self:I("Airbase on Syria map named H @ "..mgrs)
|
self:I("Airbase on Syria map named H @ "..mgrs)
|
||||||
countHSyria = countHSyria + 1
|
countHSyria = countHSyria + 1
|
||||||
if countHSyria > 1 then return self end
|
if countHSyria > 1 then return self end
|
||||||
--]]
|
--
|
||||||
return self
|
return self
|
||||||
elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then
|
elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then
|
||||||
countHSyria = countHSyria + 1
|
countHSyria = countHSyria + 1
|
||||||
end
|
end
|
||||||
|
--]]
|
||||||
|
|
||||||
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
|
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
|
||||||
local airbaseID=airbase:getID()
|
local airbaseID=airbase:getID()
|
||||||
@@ -2086,6 +2089,40 @@ function DATABASE:FindOpsGroupFromUnit(unitname)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
--- Add an OPS COHORT (SQUADRON, PLATOON, FLOTILLA) to the data base.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param Ops.Cohort#COHORT cohort The cohort added to the DB.
|
||||||
|
function DATABASE:AddCohort(cohort)
|
||||||
|
self.COHORTS[cohort.name]=cohort
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find an OPS COHORT (SQUADRON, PLATOON, FLOTILLA) in the data base.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string cohortname Name of the cohort.
|
||||||
|
-- @return Ops.Cohort#COHORT Cohort object.
|
||||||
|
function DATABASE:FindCohort(cohortname)
|
||||||
|
return self.COHORTS[cohortname]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add an OPS LEGION (AIRWING, BRIGADE, FLEET) to the data base.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param Ops.Legion#LEGION legion The legion added to the DB.
|
||||||
|
function DATABASE:AddLegion(legion)
|
||||||
|
self.LEGIONS[legion.alias]=legion
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find an OPS LEGION (AIRWING, BRIGADE, FLEET) in the data base.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string legionname Name of the legion.
|
||||||
|
-- @return Ops.Legion#LEGION Legion object.
|
||||||
|
function DATABASE:FindLegion(legionname)
|
||||||
|
return self.LEGIONS[legionname]
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
--- Add a flight control to the data base.
|
--- Add a flight control to the data base.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param OPS.FlightControl#FLIGHTCONTROL flightcontrol
|
-- @param OPS.FlightControl#FLIGHTCONTROL flightcontrol
|
||||||
|
|||||||
@@ -1453,7 +1453,12 @@ function EVENT:onEvent( Event )
|
|||||||
-- Weapon.
|
-- Weapon.
|
||||||
if Event.weapon and type(Event.weapon) == "table" and Event.weapon.isExist and Event.weapon:isExist() then
|
if Event.weapon and type(Event.weapon) == "table" and Event.weapon.isExist and Event.weapon:isExist() then
|
||||||
Event.Weapon = Event.weapon
|
Event.Weapon = Event.weapon
|
||||||
Event.WeaponName = Event.weapon:isExist() and Event.weapon:getTypeName() or "Unknown Weapon"
|
if Event.weapon_name == "ZELL Booster" then
|
||||||
|
Event.WeaponName = "ZELL Booster"
|
||||||
|
else
|
||||||
|
Event.WeaponName = Event.weapon:isExist() and Event.weapon.getTypeName and Event.weapon:getTypeName() or "Unknown Weapon"
|
||||||
|
end
|
||||||
|
if Event.weapon_name == "ZELL Booster" then Event.WeaponName = "ZELL Booster" end
|
||||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
|
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
|
||||||
--Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
--Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||||
|
|||||||
@@ -869,10 +869,21 @@ do
|
|||||||
local MenuTable = {}
|
local MenuTable = {}
|
||||||
for MenuText, Menu in pairs( self.Menus or {} ) do
|
for MenuText, Menu in pairs( self.Menus or {} ) do
|
||||||
local tag = Menu.MenuTag or math.random(1,10000)
|
local tag = Menu.MenuTag or math.random(1,10000)
|
||||||
MenuTable[#MenuTable+1] = {Tag=tag, Enty=Menu}
|
MenuTable[#MenuTable+1] = {Tag=tag, Entry=Menu}
|
||||||
end
|
end
|
||||||
table.sort(MenuTable, function (k1, k2) return k1.tag < k2.tag end )
|
local function SortTable(k1,k2)
|
||||||
for _, Menu in pairs( MenuTable ) do
|
if not k1 then
|
||||||
|
if not k2 then return true else return false end
|
||||||
|
elseif not k2 then
|
||||||
|
if not k1 then return true else return false end
|
||||||
|
else
|
||||||
|
return (k1.Tag or 15) <= (k2.Tag or 15)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
table.sort(MenuTable, SortTable)
|
||||||
|
--table.sort(MenuTable, function (k1, k2) return (k1.tag or 15) <= (k2.tag or 15) end )
|
||||||
|
for _, Menu in ipairs( MenuTable ) do
|
||||||
Menu.Entry:Refresh()
|
Menu.Entry:Refresh()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -246,11 +246,11 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
-- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
-- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||||
return ScheduleTime -- returns the next time the function needs to be called.
|
return ScheduleTime -- returns the next time the function needs to be called.
|
||||||
else
|
else
|
||||||
self:Stop( Scheduler, CallID )
|
self:_Reclaim( Scheduler, CallID )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:Stop( Scheduler, CallID )
|
self:_Reclaim( Scheduler, CallID )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||||
@@ -375,3 +375,13 @@ function SCHEDULEDISPATCHER:NoTrace( Scheduler )
|
|||||||
Scheduler.ShowTrace = false
|
Scheduler.ShowTrace = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Helper for memory cleanup for self stopping schedulers
|
||||||
|
-- @param #SCHEDULEDISPATCHER self
|
||||||
|
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||||
|
-- @param #string CallID (Optional) Scheduler Call ID.
|
||||||
|
function SCHEDULEDISPATCHER:_Reclaim( Scheduler, CallID )
|
||||||
|
self:Stop( Scheduler, CallID ) -- remove DCS timer, nil ScheduleID
|
||||||
|
if self.Schedule[Scheduler] then self.Schedule[Scheduler][CallID] = nil end
|
||||||
|
self.ObjectSchedulers[CallID] = nil
|
||||||
|
self.PersistentSchedulers[CallID] = nil
|
||||||
|
end
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ do -- SET_BASE
|
|||||||
-- @field #table List Unused table.
|
-- @field #table List Unused table.
|
||||||
-- @field Core.Scheduler#SCHEDULER CallScheduler
|
-- @field Core.Scheduler#SCHEDULER CallScheduler
|
||||||
-- @field #SET_BASE.Filters Filter Filters
|
-- @field #SET_BASE.Filters Filter Filters
|
||||||
-- @field #booleaen filterNoRegex If true, FilterPrefix ignores special characters and evaluates plain string.
|
-- @field #boolean filterNoRegex If true, FilterPrefix ignores special characters and evaluates plain string. Defaults to false.
|
||||||
|
-- @field #boolean filterReplaceDash If true then if filterNoRegex is false, replace dashes with regex pattern friendly pattern. Defaults to true
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects.
|
--- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects.
|
||||||
@@ -102,6 +103,7 @@ do -- SET_BASE
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
filterNoRegex=false,
|
filterNoRegex=false,
|
||||||
|
filterReplaceDash=true,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Filters
|
--- Filters
|
||||||
@@ -256,7 +258,8 @@ do -- SET_BASE
|
|||||||
-- @return #boolean Returns `true`, if the pattern is contained in the name and false otherwise.
|
-- @return #boolean Returns `true`, if the pattern is contained in the name and false otherwise.
|
||||||
function SET_BASE:_SearchPattern(Name, Pattern, NoRegex, ReplaceDash)
|
function SET_BASE:_SearchPattern(Name, Pattern, NoRegex, ReplaceDash)
|
||||||
NoRegex=NoRegex or self.filterNoRegex
|
NoRegex=NoRegex or self.filterNoRegex
|
||||||
if ReplaceDash==true then
|
ReplaceDash=ReplaceDash or self.filterReplaceDash
|
||||||
|
if ReplaceDash==true and NoRegex ~= true then
|
||||||
-- Not sure why "-" is replaced by "%-" ?! - So we can still match group names with a dash in them
|
-- Not sure why "-" is replaced by "%-" ?! - So we can still match group names with a dash in them
|
||||||
-- reason is that the string is interpreted as a pattern and "-" is a special character then. For interpreting it as a string, fourth parameter needs to be set to true.
|
-- reason is that the string is interpreted as a pattern and "-" is a special character then. For interpreting it as a string, fourth parameter needs to be set to true.
|
||||||
Pattern=Pattern:gsub("-", "%%-")
|
Pattern=Pattern:gsub("-", "%%-")
|
||||||
@@ -265,6 +268,16 @@ do -- SET_BASE
|
|||||||
return contain
|
return contain
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @param #SET_BASE self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_BASE self
|
||||||
|
function SET_BASE:FilterSetRegex(NoRegex,ReplaceDash)
|
||||||
|
if NoRegex ~= nil then self.filterNoRegex = NoRegex end
|
||||||
|
self.filterReplaceDash = ReplaceDash or true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Gets the Set.
|
--- Gets the Set.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
@@ -1198,7 +1211,21 @@ do
|
|||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @return #SET_GROUP self
|
-- @return #SET_GROUP self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_GROUP] FilterSetRegex
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_GROUP self
|
||||||
|
|
||||||
|
--- Builds a set of objects of same coalitions.
|
||||||
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
|
-- @function [parent=#SET_GROUP] FilterCoalitions
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
|
-- @return #SET_GROUP self
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a *new* set table that only contains alive groups.
|
--- Get a *new* set table that only contains alive groups.
|
||||||
@@ -2116,7 +2143,7 @@ do
|
|||||||
if self.Filter.GroupPrefixes and MGroupInclude then
|
if self.Filter.GroupPrefixes and MGroupInclude then
|
||||||
local MGroupPrefix = false
|
local MGroupPrefix = false
|
||||||
for GroupPrefixId, GroupPrefix in pairs(self.Filter.GroupPrefixes) do
|
for GroupPrefixId, GroupPrefix in pairs(self.Filter.GroupPrefixes) do
|
||||||
if self:_SearchPattern(MGroup:GetName(), GroupPrefix, false, true) then
|
if self:_SearchPattern(MGroup:GetName(), GroupPrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MGroupPrefix = true
|
MGroupPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2337,6 +2364,26 @@ do -- SET_UNIT
|
|||||||
-- @param #SET_UNIT self
|
-- @param #SET_UNIT self
|
||||||
-- @return #SET_UNIT self
|
-- @return #SET_UNIT self
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_UNIT] FilterOnce
|
||||||
|
-- @param #SET_UNIT self
|
||||||
|
-- @return #SET_UNIT self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_UNIT] FilterSetRegex
|
||||||
|
-- @param #SET_UNIT self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_UNIT self
|
||||||
|
|
||||||
|
--- Builds a set of objects of same coalitions.
|
||||||
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
|
-- @function [parent=#SET_UNIT] FilterCoalitions
|
||||||
|
-- @param #SET_UNIT self
|
||||||
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
|
-- @return #SET_UNIT self
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3348,7 +3395,7 @@ do -- SET_UNIT
|
|||||||
if self.Filter.UnitPrefixes and MUnitInclude then
|
if self.Filter.UnitPrefixes and MUnitInclude then
|
||||||
local MUnitPrefix = false
|
local MUnitPrefix = false
|
||||||
for UnitPrefixId, UnitPrefix in pairs(self.Filter.UnitPrefixes) do
|
for UnitPrefixId, UnitPrefix in pairs(self.Filter.UnitPrefixes) do
|
||||||
if self:_SearchPattern(MUnit:GetName(), UnitPrefix, false, true) then
|
if self:_SearchPattern(MUnit:GetName(), UnitPrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MUnitPrefix = true
|
MUnitPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3544,7 +3591,27 @@ do -- SET_STATIC
|
|||||||
|
|
||||||
-- Inherits from BASE
|
-- Inherits from BASE
|
||||||
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.STATICS)) -- Core.Set#SET_STATIC
|
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.STATICS)) -- Core.Set#SET_STATIC
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_STATIC] FilterOnce
|
||||||
|
-- @param #SET_STATIC self
|
||||||
|
-- @return #SET_STATIC self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_STATIC] FilterSetRegex
|
||||||
|
-- @param #SET_STATIC self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_STATIC self
|
||||||
|
|
||||||
|
--- Builds a set of objects of same coalitions.
|
||||||
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
|
-- @function [parent=#SET_STATIC] FilterCoalitions
|
||||||
|
-- @param #SET_STATIC self
|
||||||
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
|
-- @return #SET_STATIC self
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4119,7 +4186,7 @@ do -- SET_STATIC
|
|||||||
if self.Filter.StaticPrefixes then
|
if self.Filter.StaticPrefixes then
|
||||||
local MStaticPrefix = false
|
local MStaticPrefix = false
|
||||||
for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do
|
for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do
|
||||||
if self:_SearchPattern(MStatic:GetName(), StaticPrefix, false, true) then
|
if self:_SearchPattern(MStatic:GetName(), StaticPrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MStaticPrefix = true
|
MStaticPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4303,7 +4370,27 @@ do -- SET_CLIENT
|
|||||||
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.CLIENTS)) -- #SET_CLIENT
|
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.CLIENTS)) -- #SET_CLIENT
|
||||||
|
|
||||||
self:FilterActive(false)
|
self:FilterActive(false)
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_CLIENT] FilterOnce
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_CLIENT] FilterSetRegex
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
|
||||||
|
--- Builds a set of objects of same coalitions.
|
||||||
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
|
-- @function [parent=#SET_CLIENT] FilterCoalitions
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4902,7 +4989,7 @@ do -- SET_CLIENT
|
|||||||
if self.Filter.ClientPrefixes and MClientInclude then
|
if self.Filter.ClientPrefixes and MClientInclude then
|
||||||
local MClientPrefix = false
|
local MClientPrefix = false
|
||||||
for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do
|
for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do
|
||||||
if self:_SearchPattern(MClient.UnitName, ClientPrefix) then
|
if self:_SearchPattern(MClient.UnitName, ClientPrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MClientPrefix = true
|
MClientPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4926,7 +5013,7 @@ do -- SET_CLIENT
|
|||||||
local playername = MClient:GetPlayerName() or "Unknown"
|
local playername = MClient:GetPlayerName() or "Unknown"
|
||||||
--self:T(playername)
|
--self:T(playername)
|
||||||
for _,_Playername in pairs(self.Filter.Playernames) do
|
for _,_Playername in pairs(self.Filter.Playernames) do
|
||||||
if playername and self:_SearchPattern(playername,_Playername) then
|
if playername and self:_SearchPattern(playername,_Playername,self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MClientPlayername = true
|
MClientPlayername = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4939,7 +5026,7 @@ do -- SET_CLIENT
|
|||||||
local callsign = MClient:GetCallsign()
|
local callsign = MClient:GetCallsign()
|
||||||
--self:I(callsign)
|
--self:I(callsign)
|
||||||
for _,_Callsign in pairs(self.Filter.Callsigns) do
|
for _,_Callsign in pairs(self.Filter.Callsigns) do
|
||||||
if callsign and self:_SearchPattern(callsign,_Callsign, true) then
|
if callsign and self:_SearchPattern(callsign,_Callsign, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MClientCallsigns = true
|
MClientCallsigns = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -5360,7 +5447,7 @@ do -- SET_PLAYER
|
|||||||
if self.Filter.ClientPrefixes then
|
if self.Filter.ClientPrefixes then
|
||||||
local MClientPrefix = false
|
local MClientPrefix = false
|
||||||
for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do
|
for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do
|
||||||
if self:_SearchPattern(MClient.UnitName,ClientPrefix) then
|
if self:_SearchPattern(MClient.UnitName,ClientPrefix,self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MClientPrefix = true
|
MClientPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -5830,7 +5917,19 @@ do -- SET_ZONE
|
|||||||
function SET_ZONE:New()
|
function SET_ZONE:New()
|
||||||
-- Inherits from BASE
|
-- Inherits from BASE
|
||||||
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.ZONES))
|
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.ZONES))
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_ZONE] FilterOnce
|
||||||
|
-- @param #SET_ZONE self
|
||||||
|
-- @return #SET_ZONE self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_ZONE] FilterSetRegex
|
||||||
|
-- @param #SET_ZONE self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_ZONE self
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -6068,7 +6167,7 @@ do -- SET_ZONE
|
|||||||
if self.Filter.Prefixes then
|
if self.Filter.Prefixes then
|
||||||
local MZonePrefix = false
|
local MZonePrefix = false
|
||||||
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
||||||
if self:_SearchPattern(MZoneName, ZonePrefix, false, true) then
|
if self:_SearchPattern(MZoneName, ZonePrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MZonePrefix = true
|
MZonePrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -6577,7 +6676,7 @@ do -- SET_ZONE_GOAL
|
|||||||
if self.Filter.Prefixes then
|
if self.Filter.Prefixes then
|
||||||
local MZonePrefix = false
|
local MZonePrefix = false
|
||||||
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
||||||
if self:_SearchPattern(MZoneName, ZonePrefix, false, true) then
|
if self:_SearchPattern(MZoneName, ZonePrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MZonePrefix = true
|
MZonePrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -6713,7 +6812,19 @@ do -- SET_OPSZONE
|
|||||||
|
|
||||||
-- Inherits from BASE
|
-- Inherits from BASE
|
||||||
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.OPSZONES))
|
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.OPSZONES))
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_OPSZONE] FilterOnce
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_OPSZONE] FilterSetRegex
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -6938,7 +7049,7 @@ do -- SET_OPSZONE
|
|||||||
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
||||||
|
|
||||||
-- Prefix
|
-- Prefix
|
||||||
if self:_SearchPattern(MZoneName, ZonePrefix, false, true) then
|
if self:_SearchPattern(MZoneName, ZonePrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MZonePrefix = true
|
MZonePrefix = true
|
||||||
break --Break the loop as we found the prefix.
|
break --Break the loop as we found the prefix.
|
||||||
end
|
end
|
||||||
@@ -7732,7 +7843,7 @@ function SET_OPSGROUP:_EventOnBirth(Event)
|
|||||||
local MGroupPrefix = false
|
local MGroupPrefix = false
|
||||||
|
|
||||||
for GroupPrefixId, GroupPrefix in pairs(self.Filter.GroupPrefixes) do
|
for GroupPrefixId, GroupPrefix in pairs(self.Filter.GroupPrefixes) do
|
||||||
if self:_SearchPattern(MGroup:GetName(), GroupPrefix, false, true) then
|
if self:_SearchPattern(MGroup:GetName(), GroupPrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MGroupPrefix = true
|
MGroupPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -8066,7 +8177,7 @@ do -- SET_SCENERY
|
|||||||
if self.Filter.Prefixes then
|
if self.Filter.Prefixes then
|
||||||
local MSceneryPrefix = false
|
local MSceneryPrefix = false
|
||||||
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do
|
||||||
if self:_SearchPattern(MSceneryName, ZonePrefix, false, true) then
|
if self:_SearchPattern(MSceneryName, ZonePrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
MSceneryPrefix = true
|
MSceneryPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -8289,7 +8400,19 @@ do -- SET_DYNAMICCARGO
|
|||||||
|
|
||||||
--- Inherits from BASE
|
--- Inherits from BASE
|
||||||
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.DYNAMICCARGO)) -- Core.Set#SET_DYNAMICCARGO
|
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.DYNAMICCARGO)) -- Core.Set#SET_DYNAMICCARGO
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_DYNAMICCARGO] FilterOnce
|
||||||
|
-- @param #SET_DYNAMICCARGO self
|
||||||
|
-- @return #SET_DYNAMICCARGO self
|
||||||
|
|
||||||
|
---Set Regex Options for FilterPrefix function.
|
||||||
|
-- @function [parent=#SET_DYNAMICCARGO] FilterSetRegex
|
||||||
|
-- @param #SET_DYNAMICCARGO self
|
||||||
|
-- @param #boolean NoRegex If true, switch off Regex pattern matching for FilterPrefixes.
|
||||||
|
-- @param #boolean ReplaceDash If false, switch off dash "-" replacement in strings for FilterPrefixes.
|
||||||
|
-- @return #SET_DYNAMICCARGO self
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -8337,7 +8460,7 @@ do -- SET_DYNAMICCARGO
|
|||||||
if self.Filter.StaticPrefixes then
|
if self.Filter.StaticPrefixes then
|
||||||
local DCargoPrefix = false
|
local DCargoPrefix = false
|
||||||
for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do
|
for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do
|
||||||
if self:_SearchPattern(DCargo:GetName(), StaticPrefix, false, true) then
|
if self:_SearchPattern(DCargo:GetName(), StaticPrefix, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
DCargoPrefix = true
|
DCargoPrefix = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -8505,7 +8628,7 @@ do -- SET_DYNAMICCARGO
|
|||||||
function SET_DYNAMICCARGO:FilterCurrentOwner(PlayerName)
|
function SET_DYNAMICCARGO:FilterCurrentOwner(PlayerName)
|
||||||
self:FilterFunction(
|
self:FilterFunction(
|
||||||
function(cargo)
|
function(cargo)
|
||||||
if cargo and cargo.Owner and self:_SearchPattern(cargo.Owner, PlayerName, true) then
|
if cargo and cargo.Owner and self:_SearchPattern(cargo.Owner, PlayerName, self.filterNoRegex, self.filterReplaceDash) then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -1184,6 +1184,7 @@ end
|
|||||||
|
|
||||||
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
|
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If nil or empty, the method returns self without effect.
|
||||||
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
|
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
|
||||||
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
||||||
-- @return #SPAWN self
|
-- @return #SPAWN self
|
||||||
@@ -1201,6 +1202,9 @@ end
|
|||||||
function SPAWN:InitRandomizeZones( SpawnZoneTable, RandomizePositionInZone )
|
function SPAWN:InitRandomizeZones( SpawnZoneTable, RandomizePositionInZone )
|
||||||
--self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } )
|
--self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } )
|
||||||
|
|
||||||
|
if not SpawnZoneTable then
|
||||||
|
return self
|
||||||
|
end
|
||||||
local temptable = {}
|
local temptable = {}
|
||||||
for _,_temp in pairs(SpawnZoneTable) do
|
for _,_temp in pairs(SpawnZoneTable) do
|
||||||
temptable[#temptable+1] = _temp
|
temptable[#temptable+1] = _temp
|
||||||
@@ -2149,6 +2153,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
local parkingspots = {}
|
local parkingspots = {}
|
||||||
local parkingindex = {}
|
local parkingindex = {}
|
||||||
local spots
|
local spots
|
||||||
|
local useexplicitspots = false
|
||||||
|
|
||||||
-- Spawn happens on ground, i.e. at an airbase, a FARP or a ship.
|
-- Spawn happens on ground, i.e. at an airbase, a FARP or a ship.
|
||||||
if spawnonground and not SpawnTemplate.parked then
|
if spawnonground and not SpawnTemplate.parked then
|
||||||
@@ -2178,15 +2183,41 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
local scanscenery = false
|
local scanscenery = false
|
||||||
local verysafe = false
|
local verysafe = false
|
||||||
|
|
||||||
|
-- Use exact parking data when provided, otherwise let helicopters on ships/FARPs
|
||||||
|
-- use the smarter parking search before falling back to the procedural queue path.
|
||||||
-- Number of free parking spots at the airbase.
|
-- Number of free parking spots at the airbase.
|
||||||
if autoparking then
|
if Parkingdata~=nil then
|
||||||
|
-- Parking data explicitly set by user as input parameter.
|
||||||
|
nfree = #Parkingdata
|
||||||
|
spots = Parkingdata
|
||||||
|
useexplicitspots = true
|
||||||
|
elseif autoparking and AirbaseCategory == Airbase.Category.HELIPAD and ishelo then
|
||||||
|
if termtype == nil then
|
||||||
|
-- Helo is spawned. Try exclusive helo spots first.
|
||||||
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||||
|
nfree = #spots
|
||||||
|
if nfree < nunits then
|
||||||
|
-- Not enough helo ports. Let's try also other terminal types.
|
||||||
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||||
|
nfree = #spots
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Terminal type explicitly given.
|
||||||
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||||
|
nfree = #spots
|
||||||
|
end
|
||||||
|
|
||||||
|
if nfree >= nunits then
|
||||||
|
useexplicitspots = true
|
||||||
|
else
|
||||||
|
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||||
|
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||||
|
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||||
|
end
|
||||||
|
elseif autoparking then
|
||||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||||
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||||
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||||
elseif Parkingdata~=nil then
|
|
||||||
-- Parking data explicitly set by user as input parameter. (This was commented out for some unknown reason. But I need it this way.)
|
|
||||||
nfree=#Parkingdata
|
|
||||||
spots=Parkingdata
|
|
||||||
else
|
else
|
||||||
if ishelo then
|
if ishelo then
|
||||||
if termtype == nil then
|
if termtype == nil then
|
||||||
@@ -2238,7 +2269,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
local _notenough = false
|
local _notenough = false
|
||||||
|
|
||||||
-- Need to differentiate some cases again.
|
-- Need to differentiate some cases again.
|
||||||
if autoparking then
|
if autoparking and not useexplicitspots then
|
||||||
|
|
||||||
-- On free spot required in these cases.
|
-- On free spot required in these cases.
|
||||||
if nfree >= 1 then
|
if nfree >= 1 then
|
||||||
@@ -2271,6 +2302,10 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if useexplicitspots and parkingspots[1] then
|
||||||
|
PointVec3 = parkingspots[1]
|
||||||
|
end
|
||||||
|
|
||||||
-- Not enough spots ==> Prepare airstart.
|
-- Not enough spots ==> Prepare airstart.
|
||||||
if _notenough then
|
if _notenough then
|
||||||
|
|
||||||
@@ -2296,9 +2331,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
|
|
||||||
Takeoff = GROUP.Takeoff.Air
|
Takeoff = GROUP.Takeoff.Air
|
||||||
spawnonground = false
|
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() ) )
|
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
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2339,7 +2380,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
if spawnonground then
|
if spawnonground then
|
||||||
|
|
||||||
-- Ships and FARPS seem to have a build in queue.
|
-- Ships and FARPS seem to have a build in queue.
|
||||||
if autoparking then
|
if autoparking and not useexplicitspots then
|
||||||
|
|
||||||
-- Spawn on ship. We take only the position of the ship.
|
-- Spawn on ship. We take only the position of the ship.
|
||||||
SpawnTemplate.units[UnitID].x = PointVec3.x -- TX
|
SpawnTemplate.units[UnitID].x = PointVec3.x -- TX
|
||||||
@@ -2357,7 +2398,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
|
|
||||||
else
|
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].x = TX
|
||||||
SpawnTemplate.units[UnitID].y = TY
|
SpawnTemplate.units[UnitID].y = TY
|
||||||
SpawnTemplate.units[UnitID].alt = PointVec3.y
|
SpawnTemplate.units[UnitID].alt = PointVec3.y
|
||||||
@@ -2390,7 +2431,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
if Takeoff == GROUP.Takeoff.Air then
|
if Takeoff == GROUP.Takeoff.Air then
|
||||||
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
|
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
|
||||||
--SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() }, 5 ) --No need to create a new SCHEDULER instance every time!
|
--SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() }, 5 ) --No need to create a new SCHEDULER instance every time!
|
||||||
self:ScheduleOnce(5, BASE.CreateEventTakeoff, {GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject()})
|
self:ScheduleOnce(5, BASE.CreateEventTakeoff, GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2582,21 +2623,51 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
|||||||
local scanscenery = false
|
local scanscenery = false
|
||||||
local verysafe = false
|
local verysafe = false
|
||||||
|
|
||||||
-- Number of free parking spots at the airbase.
|
-- Use exact parking data when provided, otherwise let helicopters on FARPs
|
||||||
if spawnonship or spawnonfarp or spawnonrunway then
|
-- use the smarter parking search before falling back to the procedural queue path.
|
||||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
local useexplicitspots = false
|
||||||
--self:T2( string.format( "Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
|
||||||
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
-- Number of free parking spots at the airbase.
|
||||||
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
if Parkingdata~=nil then
|
||||||
--[[
|
-- Parking data explicitly set by user as input parameter.
|
||||||
elseif Parkingdata~=nil then
|
nfree = #Parkingdata
|
||||||
-- Parking data explicitly set by user as input parameter.
|
spots = Parkingdata
|
||||||
nfree=#Parkingdata
|
useexplicitspots = true
|
||||||
spots=Parkingdata
|
elseif spawnonfarp and ishelo then
|
||||||
]]
|
|
||||||
else
|
|
||||||
if ishelo then
|
|
||||||
if termtype == nil then
|
if termtype == nil then
|
||||||
|
-- Helo is spawned. Try exclusive helo spots first.
|
||||||
|
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly ) )
|
||||||
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||||
|
nfree = #spots
|
||||||
|
if nfree < nunits then
|
||||||
|
-- Not enough helo ports. Let's try also other terminal types.
|
||||||
|
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable ) )
|
||||||
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||||
|
nfree = #spots
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Terminal type explicitly given.
|
||||||
|
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype ) )
|
||||||
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||||
|
nfree = #spots
|
||||||
|
end
|
||||||
|
|
||||||
|
if nfree >= nunits then
|
||||||
|
useexplicitspots = true
|
||||||
|
else
|
||||||
|
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||||
|
--self:T2( string.format( "Group %s is spawned on farp %s using procedural spots.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||||
|
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||||
|
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||||
|
end
|
||||||
|
elseif spawnonship or spawnonfarp or spawnonrunway then
|
||||||
|
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||||
|
--self:T2( string.format( "Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||||
|
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||||
|
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||||
|
else
|
||||||
|
if ishelo then
|
||||||
|
if termtype == nil then
|
||||||
-- Helo is spawned. Try exclusive helo spots first.
|
-- Helo is spawned. Try exclusive helo spots first.
|
||||||
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly ) )
|
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly ) )
|
||||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata )
|
||||||
@@ -2659,7 +2730,7 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
|||||||
local _notenough = false
|
local _notenough = false
|
||||||
|
|
||||||
-- Need to differentiate some cases again.
|
-- Need to differentiate some cases again.
|
||||||
if spawnonship or spawnonfarp or spawnonrunway then
|
if (spawnonship or spawnonfarp or spawnonrunway) and not useexplicitspots then
|
||||||
|
|
||||||
-- On free spot required in these cases.
|
-- On free spot required in these cases.
|
||||||
if nfree >= 1 then
|
if nfree >= 1 then
|
||||||
|
|||||||
@@ -1848,7 +1848,7 @@ do -- DETECTION_BASE
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Validate if the detected item is locked.
|
--- Validate if the detected item is locked (not in the radar sense, though!).
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
@@ -1858,7 +1858,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Lock a detected item.
|
--- Lock a detected item (not in the radar sense, though!).
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||||
-- @return #DETECTION_BASE
|
-- @return #DETECTION_BASE
|
||||||
@@ -1869,7 +1869,7 @@ do -- DETECTION_BASE
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Unlock a detected item.
|
--- Unlock a detected item (not in the radar sense, though!).
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||||
-- @return #DETECTION_BASE
|
-- @return #DETECTION_BASE
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
-- * TOR M2
|
-- * TOR M2
|
||||||
-- * C-RAM
|
-- * C-RAM
|
||||||
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
-- * 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 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
|
-- * 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
|
-- * Other Mods: Nike
|
||||||
@@ -135,8 +135,8 @@
|
|||||||
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
|
-- * 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-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-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-12 (e.g. "Red SAM SA-12 HDS")
|
||||||
-- * 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-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")
|
-- * 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.
|
-- The other HDS types work like the rest of the known SAM systems.
|
||||||
@@ -404,10 +404,9 @@ MANTIS.SamData = {
|
|||||||
["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17", ARMCapacity=4 },
|
["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-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-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="9S32ME", ARMCapacity=4}, -- SA-23 Gladiator/Giant
|
||||||
["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-1E", ARMCapacity=4}, -- SA-23B
|
||||||
["S-300V4"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="9S32M", ARMCapacity=4},
|
["S-400"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="92N6E", ARMCapacity=4}, -- SA-21 Growler
|
||||||
["S-400"] = { Range=250, Blindspot=5, Height=27, Type="Long" , Radar="92N6E", ARMCapacity=4},
|
|
||||||
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
["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" },
|
["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" },
|
["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 },
|
["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 },
|
["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
|
["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
|
--- 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-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-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-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"] = { Range=100, Blindspot=6, 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"] = { Range=200, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A82ME", ARMCapacity=14 },
|
||||||
["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 },
|
|
||||||
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
["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 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
|
["SAMPT Block 1INT HDS"] = { Range=150, Blindspot=1, Height=25, Type="long", Radar="SAMPT_MLT_Blk1NT" }, -- Block 1-INT Launcher
|
||||||
@@ -526,6 +524,187 @@ MANTIS.SamDataCH = {
|
|||||||
["Lvkv9040M CHM"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
|
["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 — v9 curves (v8.1) {peak%, mu_nm, sigma_L, tail_dist, band, floor%}
|
||||||
|
-- v9 changes from v8:
|
||||||
|
-- * Width rework: curves now track radar detection/track envelope (not just missile range)
|
||||||
|
-- * Long-range systems widened (SA-5, SA-21, SA-23, Nike, PAC-2, S-300/400 family)
|
||||||
|
-- * Short-range systems tightened (SA-19, SON-9, HQ-7, Rapier)
|
||||||
|
-- * HAWK widened to match CW illuminator envelope (~90nm)
|
||||||
|
-- floor: residual effectiveness inside burnthrough (now tapers past mu with R^2 falloff)
|
||||||
|
-- 5 = legacy radar, no ECCM | 3 = moderate ECCM | 2 = advanced ECCM/AESA | 0 = optical/IR
|
||||||
|
-- @type MANTIS.JammerSAMParams
|
||||||
|
MANTIS.JammerSAMParams = {
|
||||||
|
-- Standard SamData (canonical entries — aliases below point to these)
|
||||||
|
["Nike"] ={peak=78,mu=55,sigma_L=22,tail_dist=110,band="S", floor=5},
|
||||||
|
["Hawk"] ={peak=30,mu=22,sigma_L=9, tail_dist=55, band="IJ", floor=3},
|
||||||
|
["SA-2"] ={peak=75,mu=40,sigma_L=16,tail_dist=90, band="S", floor=5},
|
||||||
|
["SA-3"] ={peak=45,mu=22,sigma_L=10,tail_dist=52, band="IJ", floor=5},
|
||||||
|
["SA-5"] ={peak=52,mu=75,sigma_L=26,tail_dist=145,band="S", floor=5},
|
||||||
|
["SA-6"] ={peak=33,mu=18,sigma_L=8, tail_dist=48, band="IJ", floor=3},
|
||||||
|
["SA-8"] ={peak=38,mu=10,sigma_L=4, tail_dist=24, 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=95, band="S", floor=3},
|
||||||
|
["SA-10B"] ={peak=30,mu=52,sigma_L=20,tail_dist=100,band="S", floor=3},
|
||||||
|
["SA-11"] ={peak=52,mu=28,sigma_L=12,tail_dist=58, 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=11,sigma_L=5, tail_dist=22, band="IJ", floor=3},
|
||||||
|
["SA-17"] ={peak=24,mu=32,sigma_L=14,tail_dist=68, band="IJ", floor=2},
|
||||||
|
["SA-19"] ={peak=25,mu=8, sigma_L=3.5,tail_dist=16,band="IJ", floor=2},
|
||||||
|
["SA-20A"] ={peak=22,mu=65,sigma_L=24,tail_dist=115,band="S", floor=2},
|
||||||
|
["SA-20B"] ={peak=20,mu=68,sigma_L=24,tail_dist=120,band="S", floor=2},
|
||||||
|
["S-300VM"] ={peak=16,mu=80,sigma_L=30,tail_dist=135,band="S", floor=2}, -- SA-23 canonical
|
||||||
|
["S-300V4"] ={peak=14,mu=85,sigma_L=30,tail_dist=145,band="S", floor=2}, -- SA-23B canonical
|
||||||
|
["S-400"] ={peak=18,mu=80,sigma_L=30,tail_dist=150,band="S", floor=2}, -- SA-21 canonical
|
||||||
|
["NASAMS"] ={peak=25,mu=28,sigma_L=12,tail_dist=52, band="IJ", floor=2},
|
||||||
|
["Patriot"] ={peak=32,mu=62,sigma_L=24,tail_dist=110,band="S", floor=3},
|
||||||
|
["Rapier"] ={peak=12,mu=6, sigma_L=2.5,tail_dist=14,band="IJ", floor=0},
|
||||||
|
["Gepard"] ={peak=18,mu=6, sigma_L=2, tail_dist=18, band="IJ", floor=0},
|
||||||
|
["Roland"] ={peak=35,mu=5, sigma_L=2, tail_dist=12, band="IJ", floor=3},
|
||||||
|
["HQ-7"] ={peak=38,mu=8, sigma_L=3.5,tail_dist=18,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=9, sigma_L=4, tail_dist=20, band="IJ", floor=0}, -- SA-22 canonical
|
||||||
|
["Tor M2"] ={peak=28,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=3},
|
||||||
|
["IRIS-T SLM"] ={peak=18,mu=22,sigma_L=10,tail_dist=50, band="IJ", floor=2},
|
||||||
|
["SON-9"] ={peak=48,mu=14,sigma_L=6, tail_dist=30, band="IJ", floor=5},
|
||||||
|
-- IDF Mod
|
||||||
|
["TAMIR IDFA"] ={peak=19,mu=25,sigma_L=12,tail_dist=52,band="S", floor=2},
|
||||||
|
["STUNNER IDFA"]={peak=16,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2},
|
||||||
|
-- HDS Mod (canonical for keys with no base equivalent)
|
||||||
|
["SA-10C HDS"] ={peak=30,mu=50,sigma_L=20,tail_dist=92, band="S", floor=3},
|
||||||
|
["SA-12 HDS"] ={peak=35,mu=50,sigma_L=18,tail_dist=90, band="S", floor=3},
|
||||||
|
["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},
|
||||||
|
-- SMA Mod (canonical for SMA-only keys)
|
||||||
|
["RBS70 SMA"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0}, -- canonical for RBS70 family
|
||||||
|
["RBS90 SMA"] ={peak=5, mu=4, sigma_L=2, tail_dist=10,band="OPT",floor=0}, -- canonical for RBS90 family
|
||||||
|
["RBS98M SMA"] ={peak=25,mu=12,sigma_L=5, tail_dist=25,band="IJ", floor=3}, -- canonical
|
||||||
|
["RBS103A SMA"] ={peak=20,mu=55,sigma_L=20,tail_dist=90,band="S", floor=2}, -- canonical
|
||||||
|
["RBS103B SMA"] ={peak=22,mu=45,sigma_L=18,tail_dist=80,band="S", floor=2}, -- canonical
|
||||||
|
["Lvkv9040M SMA"]={peak=15,mu=3, sigma_L=1, tail_dist=8, band="OPT",floor=0}, -- canonical
|
||||||
|
-- CH Mod (canonical for CHM-only keys)
|
||||||
|
["2S38 CHM"] ={peak=8, mu=3, sigma_L=1, tail_dist=8, band="OPT",floor=0},
|
||||||
|
["PGL-625 CHM"] ={peak=12,mu=6, sigma_L=2.5,tail_dist=14,band="OPT",floor=0}, -- Type-08
|
||||||
|
["HQ-17A CHM"] ={peak=30,mu=11,sigma_L=5, tail_dist=24, band="IJ", floor=3},
|
||||||
|
["M903PAC3 CHM"] ={peak=15,mu=58,sigma_L=22,tail_dist=105,band="S", floor=2},
|
||||||
|
["TorM2M CHM"] ={peak=26,mu=16,sigma_L=6, tail_dist=32, band="IJ", floor=3}, -- variant
|
||||||
|
["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},
|
||||||
|
["PGZ-09 CHM"] ={peak=22,mu=5, sigma_L=2, tail_dist=14, band="IJ", floor=0}, -- Type-09
|
||||||
|
["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=78, band="IJ", floor=2}, -- SA-28 canonical
|
||||||
|
["HQ-22 CHM"] ={peak=20,mu=70,sigma_L=26,tail_dist=120,band="S", floor=2},
|
||||||
|
["IRIS-T SLM CHM"] ={peak=18,mu=22,sigma_L=10,tail_dist=50, band="IJ", floor=2}, -- will be aliased
|
||||||
|
["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=75, band="IJ", floor=2}, -- SA-27 canonical
|
||||||
|
["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=90,sigma_L=38,tail_dist=140,band="IJ", 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},
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Alias entries: point alias keys to the canonical entry's table.
|
||||||
|
-- Modifying any canonical entry above automatically updates all its aliases.
|
||||||
|
-- This prevents parameter drift when updating values.
|
||||||
|
do
|
||||||
|
local p = MANTIS.JammerSAMParams
|
||||||
|
-- NATO designation aliases
|
||||||
|
p["SA-21"] = p["S-400"] -- SA-21 Growler
|
||||||
|
p["SA-22"] = p["Pantsir S1"] -- SA-22 Greyhound
|
||||||
|
p["SA-23"] = p["S-300VM"] -- SA-23 Gladiator
|
||||||
|
p["SA-23B"] = p["S-300V4"] -- SA-23B Antey-2500 (improved)
|
||||||
|
p["SA-27"] = p["BukM3-9M317M CHM"] -- SA-27 Gollum (Buk-M3)
|
||||||
|
p["SA-28"] = p["S350-9M100 CHM"] -- SA-28 Vityaz (S-350)
|
||||||
|
-- Buk-M3 variants (same radar)
|
||||||
|
p["BukM3-9M317MA CHM"] = p["BukM3-9M317M CHM"]
|
||||||
|
-- S-350 variants (same radar)
|
||||||
|
p["S350-9M96D CHM"] = p["S350-9M100 CHM"]
|
||||||
|
-- Pantsir variants
|
||||||
|
p["PantsirS1 CHM"] = p["Pantsir S1"]
|
||||||
|
p["PantsirS2 CHM"] = p["Pantsir S1"]
|
||||||
|
-- Tor-M2 variants
|
||||||
|
p["TorM2 CHM"] = p["Tor M2"]
|
||||||
|
p["TorM2K CHM"] = p["Tor M2"]
|
||||||
|
-- Patriot PAC-2 (same MPQ-65 radar)
|
||||||
|
p["M903PAC2 CHM"] = p["Patriot"]
|
||||||
|
p["M903PAC2KAT1 CHM"] = p["Patriot"]
|
||||||
|
-- IRIS-T (base and CHM are same system)
|
||||||
|
p["IRIS-T SLM CHM"] = p["IRIS-T SLM"]
|
||||||
|
-- HDS variants (same radar as base entry)
|
||||||
|
p["SA-2 HDS"] = p["SA-2"]
|
||||||
|
p["SA-3 HDS"] = p["SA-3"]
|
||||||
|
p["SA-10B HDS"] = p["SA-10B"]
|
||||||
|
p["SA-17 HDS"] = p["SA-17"]
|
||||||
|
p["SA-23 HDS"] = p["S-300VM"]
|
||||||
|
p["HQ-2 HDS"] = p["HQ-2"]
|
||||||
|
-- Roland / FlaRakRad (same system)
|
||||||
|
p["FlaRakRad CHM"] = p["Roland"]
|
||||||
|
-- Phalanx-class CIWS (Ka-band, essentially unjammable)
|
||||||
|
p["C-RAM CHM"] = p["C-RAM"]
|
||||||
|
p["LD-3000 CHM"] = p["C-RAM"]
|
||||||
|
p["LD-3000M CHM"] = p["C-RAM"]
|
||||||
|
-- SMA / CHM Swedish duplicates (CH mod re-implements SMA assets)
|
||||||
|
p["RBS70 CHM"] = p["RBS70 SMA"]
|
||||||
|
p["RBS70M SMA"] = p["RBS70 SMA"]
|
||||||
|
p["RBS70M CHM"] = p["RBS70 SMA"]
|
||||||
|
p["RBS90 CHM"] = p["RBS90 SMA"]
|
||||||
|
p["RBS90M SMA"] = p["RBS90 SMA"]
|
||||||
|
p["RBS90M CHM"] = p["RBS90 SMA"]
|
||||||
|
p["RBS98M CHM"] = p["RBS98M SMA"]
|
||||||
|
p["RBS103A CHM"] = p["RBS103A SMA"]
|
||||||
|
p["RBS103AM SMA"] = p["RBS103A SMA"]
|
||||||
|
p["RBS103AM CHM"] = p["RBS103A SMA"]
|
||||||
|
p["RBS103B CHM"] = p["RBS103B SMA"]
|
||||||
|
p["RBS103BM SMA"] = p["RBS103B SMA"]
|
||||||
|
p["RBS103BM CHM"] = p["RBS103B SMA"]
|
||||||
|
p["Lvkv9040M CHM"]= p["Lvkv9040M SMA"]
|
||||||
|
-- IR MANPADS (all near-zero, different systems but share params)
|
||||||
|
p["LAV-AD CHM"] = p["SA-9"]
|
||||||
|
-- NOTE: SA-13, Avenger, Chaparral, Linebacker kept separate (different real systems)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
-- MANTIS System
|
-- MANTIS System
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
@@ -908,7 +1087,8 @@ do
|
|||||||
|
|
||||||
local function SwitchSAMOn(Name,Group)
|
local function SwitchSAMOn(Name,Group)
|
||||||
local suppressed = self.SuppressedGroups[Name] or false
|
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"
|
self.SamStateTracker[Name] = "RED"
|
||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
-- DONE: add emissions on/off
|
-- DONE: add emissions on/off
|
||||||
@@ -2132,6 +2312,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),
|
self:T(string.format("MANTIS:SeadAllowSuppression REQUEST | target=%s | weapon=%s | tti=%s | delay=%s",tostring(targetName),
|
||||||
tostring(weaponName),tostring(tti),tostring(delay)))
|
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
|
-- LOOK UP ARM CAPACITY FOR THIS SAM
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
@@ -2694,6 +2886,664 @@ function MANTIS:SeadAllowSuppression(targetGroup, targetName, attackerGroup, wea
|
|||||||
return self
|
return self
|
||||||
end
|
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 (v9 / v8.1 engine).
|
||||||
|
-- Features: decoupled bt_mod/range_mod, tapering residual floor, ±10% jitter.
|
||||||
|
-- @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
|
||||||
|
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
|
||||||
|
-- Tapering residual floor (v9): floor full inside engagement band (d <= mu),
|
||||||
|
-- attenuates with R^2-like falloff past mu over 2.5x the main decay distance.
|
||||||
|
-- Prevents artificial hard cliff at 200nm cap; matches real radar/jammer physics.
|
||||||
|
local eff_floor = floor * bm
|
||||||
|
if eff_floor > 0 and d > mu then
|
||||||
|
local floorLambda = math.log(100.0) / (eff_tail_dist * 2.5)
|
||||||
|
eff_floor = eff_floor * math.exp(-floorLambda * (d - mu))
|
||||||
|
end
|
||||||
|
raw = math.max(eff_floor, raw)
|
||||||
|
-- Stochastic jitter ±10% applied AFTER floor (v9), so residual also varies.
|
||||||
|
-- Prevents gameable flat long-range value. Reflects S/N, RCS, atmospheric fluctuation.
|
||||||
|
local jitter = 1.0 + (math.random() * 2 - 1) * (self.JammerJitterPercent or 0.10)
|
||||||
|
raw = raw * jitter
|
||||||
|
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
|
end
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
-- MANTIS end
|
-- MANTIS end
|
||||||
|
|||||||
@@ -795,15 +795,20 @@ do
|
|||||||
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
local tgtcoord1 = tgtgrp1:GetCoordinate()
|
local tgtcoord1 = tgtgrp1:GetCoordinate()
|
||||||
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
|
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
if tgtgrp2 then
|
||||||
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||||
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||||
|
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||||
if dist1 < dist2 then
|
|
||||||
targetunit = tgtgrp1
|
if dist1 < dist2 then
|
||||||
targetcat = Object.Category.UNIT
|
targetunit = tgtgrp1
|
||||||
|
targetcat = Object.Category.UNIT
|
||||||
|
else
|
||||||
|
targetunit = tgtgrp2
|
||||||
|
targetcat = Object.Category.UNIT
|
||||||
|
end
|
||||||
else
|
else
|
||||||
targetunit = tgtgrp2
|
targetunit = tgtgrp1
|
||||||
targetcat = Object.Category.UNIT
|
targetcat = Object.Category.UNIT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1485,7 +1485,7 @@ function SUPPRESSION:onafterStop(Controllable, From, Event, To)
|
|||||||
|
|
||||||
local text=string.format("Stopping SUPPRESSION for group %s", self.Controllable:GetName())
|
local text=string.format("Stopping SUPPRESSION for group %s", self.Controllable:GetName())
|
||||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||||
sefl:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
-- Clear all pending schedules
|
-- Clear all pending schedules
|
||||||
self.CallScheduler:Clear()
|
self.CallScheduler:Clear()
|
||||||
|
|||||||
@@ -4097,7 +4097,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
|
|||||||
-- Get name of template group.
|
-- Get name of template group.
|
||||||
local templategroupname=group:GetName()
|
local templategroupname=group:GetName()
|
||||||
local unit = group:GetUnit(1)
|
local unit = group:GetUnit(1)
|
||||||
local Descriptors= (unit and unit:IsAlive()) and unit:GetDesc() or {}
|
local Descriptors= (unit and unit:IsAlive()~=nil) and unit:GetDesc() or {}
|
||||||
local Category=group:GetCategory()
|
local Category=group:GetCategory()
|
||||||
local TypeName=group:GetTypeName() or "none"
|
local TypeName=group:GetTypeName() or "none"
|
||||||
local SpeedMax=group:GetSpeedMax()
|
local SpeedMax=group:GetSpeedMax()
|
||||||
@@ -4115,7 +4115,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
|
|||||||
for _i,_unit in pairs(group:GetUnits()) do
|
for _i,_unit in pairs(group:GetUnits()) do
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
local Desc=unit:GetDesc()
|
local Desc=unit:GetDesc()
|
||||||
|
|
||||||
-- Weight. We sum up all units in the group.
|
-- Weight. We sum up all units in the group.
|
||||||
local unitweight=forceweight or Desc.massEmpty
|
local unitweight=forceweight or Desc.massEmpty
|
||||||
if unitweight then
|
if unitweight then
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Squadron.lua' )
|
|||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Target.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Target.lua' )
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyGCICAP.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyGCICAP.lua' )
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyA2G.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyA2G.lua' )
|
||||||
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/TARS.lua' )
|
||||||
|
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/ShapeBase.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/ShapeBase.lua' )
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Circle.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Circle.lua' )
|
||||||
|
|||||||
@@ -290,6 +290,9 @@ function AIRWING:New(warehousename, airwingname)
|
|||||||
-- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FLIGHTGROUP on mission.
|
-- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FLIGHTGROUP on mission.
|
||||||
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
||||||
|
|
||||||
|
-- Add legion to DB
|
||||||
|
_DATABASE:AddLegion(self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -541,6 +544,47 @@ function AIRWING:AddPayloadCapability(Payload, MissionTypes, Performance)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Filter available payloads for a given aircraft type and mission type.
|
||||||
|
-- @param #AIRWING self
|
||||||
|
-- @param #string UnitType The type of the unit.
|
||||||
|
-- @param #string MissionType The mission type.
|
||||||
|
-- @param #table Payloads Specific payloads only to be considered.
|
||||||
|
-- @return #AIRWING.Payload Payload table or *nil*.
|
||||||
|
function AIRWING:_FilterPlayloads(UnitType, MissionType, Payloads)
|
||||||
|
|
||||||
|
local function _checkPayloads(payload)
|
||||||
|
if Payloads then
|
||||||
|
for _,Payload in pairs(Payloads) do
|
||||||
|
if Payload.uid==payload.uid then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Payload was not specified.
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pre-selection: filter out only those payloads that are valid for the airframe and mission type and are available.
|
||||||
|
local payloads={}
|
||||||
|
|
||||||
|
for _,_payload in pairs(self.payloads) do
|
||||||
|
local payload=_payload --#AIRWING.Payload
|
||||||
|
|
||||||
|
local specialpayload=_checkPayloads(payload)
|
||||||
|
local compatible=AUFTRAG.CheckMissionCapability(MissionType, payload.capabilities)
|
||||||
|
|
||||||
|
local goforit = specialpayload or (specialpayload==nil and compatible)
|
||||||
|
|
||||||
|
if payload.aircrafttype==UnitType and payload.navail>0 and goforit then
|
||||||
|
table.insert(payloads, payload)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return payloads
|
||||||
|
end
|
||||||
|
|
||||||
--- Fetch a payload from the airwing resources for a given unit and mission type.
|
--- Fetch a payload from the airwing resources for a given unit and mission type.
|
||||||
-- The payload with the highest priority is preferred.
|
-- The payload with the highest priority is preferred.
|
||||||
-- @param #AIRWING self
|
-- @param #AIRWING self
|
||||||
@@ -589,34 +633,7 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType, Payloads)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _checkPayloads(payload)
|
local payloads=self:_FilterPlayloads(UnitType, MissionType, Payloads)
|
||||||
if Payloads then
|
|
||||||
for _,Payload in pairs(Payloads) do
|
|
||||||
if Payload.uid==payload.uid then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Payload was not specified.
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pre-selection: filter out only those payloads that are valid for the airframe and mission type and are available.
|
|
||||||
local payloads={}
|
|
||||||
for _,_payload in pairs(self.payloads) do
|
|
||||||
local payload=_payload --#AIRWING.Payload
|
|
||||||
|
|
||||||
local specialpayload=_checkPayloads(payload)
|
|
||||||
local compatible=AUFTRAG.CheckMissionCapability(MissionType, payload.capabilities)
|
|
||||||
|
|
||||||
local goforit = specialpayload or (specialpayload==nil and compatible)
|
|
||||||
|
|
||||||
if payload.aircrafttype==UnitType and payload.navail>0 and goforit then
|
|
||||||
table.insert(payloads, payload)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Debug.
|
-- Debug.
|
||||||
if self.verbose>=4 then
|
if self.verbose>=4 then
|
||||||
|
|||||||
@@ -2717,17 +2717,27 @@ function AIRBOSS:DeleteRecoveryWindow( Window, Delay )
|
|||||||
self:ScheduleOnce( Delay, self.DeleteRecoveryWindow, self, Window )
|
self:ScheduleOnce( Delay, self.DeleteRecoveryWindow, self, Window )
|
||||||
else
|
else
|
||||||
|
|
||||||
for i, _recovery in pairs( self.recoverytimes ) do
|
if not Window then
|
||||||
local recovery = _recovery -- #AIRBOSS.Recovery
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if Window and Window.ID == recovery.ID then
|
-- If this window is currently open, stop recovery first. Mark it OVER so the
|
||||||
if Window.OPEN then
|
-- recovery time check cannot re-open it before/while we remove it.
|
||||||
-- Window is currently open.
|
if Window.OPEN then
|
||||||
self:RecoveryStop()
|
Window.OPEN = false
|
||||||
else
|
Window.OVER = true
|
||||||
table.remove( self.recoverytimes, i )
|
if self:IsRecovering() then
|
||||||
end
|
self:RecoveryStop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove the window from the queue by its unique ID. Iterate over a numerically
|
||||||
|
-- indexed copy of the keys and remove via ipairs-safe reverse loop so that the
|
||||||
|
-- removal does not corrupt traversal (the window may appear once).
|
||||||
|
for i = #self.recoverytimes, 1, -1 do
|
||||||
|
local recovery = self.recoverytimes[i] -- #AIRBOSS.Recovery
|
||||||
|
if recovery and recovery.ID == Window.ID then
|
||||||
|
table.remove( self.recoverytimes, i )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4078,15 +4088,23 @@ function AIRBOSS:_CheckRecoveryTimes()
|
|||||||
if self:IsRecovering() then
|
if self:IsRecovering() then
|
||||||
-- Carrier is already recovering.
|
-- Carrier is already recovering.
|
||||||
state = "in progress"
|
state = "in progress"
|
||||||
else
|
elseif not recovery.OVER then
|
||||||
-- Start recovery.
|
-- Start recovery. Only if the window has not already been closed/cancelled.
|
||||||
|
-- The OVER guard prevents a window that was stopped manually (e.g. via the
|
||||||
|
-- Skipper "Stop Recovery" menu) from being immediately re-opened on the next
|
||||||
|
-- status tick while its [START,STOP) range is still active.
|
||||||
self:RecoveryStart( recovery.CASE, recovery.OFFSET )
|
self:RecoveryStart( recovery.CASE, recovery.OFFSET )
|
||||||
state = "starting now"
|
state = "starting now"
|
||||||
recovery.OPEN = true
|
recovery.OPEN = true
|
||||||
|
else
|
||||||
|
-- Window was already closed/cancelled within its active time range.
|
||||||
|
state = "cancelled"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set current recovery window.
|
-- Set current recovery window (unless this window has been cancelled).
|
||||||
currwindow = recovery
|
if not recovery.OVER then
|
||||||
|
currwindow = recovery
|
||||||
|
end
|
||||||
|
|
||||||
else -- Stop time HAS passed.
|
else -- Stop time HAS passed.
|
||||||
|
|
||||||
@@ -4183,13 +4201,16 @@ function AIRBOSS:_CheckRecoveryTimes()
|
|||||||
-- Time into the wind 1 day or if longer recovery time + the 5 min early.
|
-- Time into the wind 1 day or if longer recovery time + the 5 min early.
|
||||||
local t = math.max( nextwindow.STOP - nextwindow.START + self.dTturn, 60 * 60 * 24 )
|
local t = math.max( nextwindow.STOP - nextwindow.START + self.dTturn, 60 * 60 * 24 )
|
||||||
|
|
||||||
-- Recovery wind on deck in knots.
|
-- Recovery wind on deck in knots.
|
||||||
|
-- NOTE: Do NOT clamp the desired wind-over-deck (WOD) to the carrier's max hull
|
||||||
|
-- speed here. WOD = carrier speed + headwind, so a WOD target above the hull's
|
||||||
|
-- top speed is achievable whenever there is wind. CarrierTurnIntoWind ->
|
||||||
|
-- GetHeadingIntoWind already converts the WOD target into the required hull
|
||||||
|
-- speed and caps THAT at the carrier's max speed (Vmax) internally. Clamping the
|
||||||
|
-- WOD target itself capped achievable WOD at ~30 kts (the supercarrier's max
|
||||||
|
-- hull speed) even in strong wind, which made high-WOD recovery windows fall
|
||||||
|
-- short of their requested value.
|
||||||
local v = UTILS.KnotsToMps( nextwindow.SPEED )
|
local v = UTILS.KnotsToMps( nextwindow.SPEED )
|
||||||
|
|
||||||
-- Check that we do not go above max possible speed.
|
|
||||||
local vmax = self.carrier:GetSpeedMax() / 3.6 -- convert to m/s
|
|
||||||
v = math.min( v, vmax )
|
|
||||||
|
|
||||||
-- Route carrier into the wind. Sets self.turnintowind=true
|
-- Route carrier into the wind. Sets self.turnintowind=true
|
||||||
self:CarrierTurnIntoWind( t, v, uturn )
|
self:CarrierTurnIntoWind( t, v, uturn )
|
||||||
|
|
||||||
@@ -4361,11 +4382,20 @@ function AIRBOSS:onafterRecoveryStop( From, Event, To )
|
|||||||
self:CarrierResumeRoute( coord )
|
self:CarrierResumeRoute( coord )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Delete current recovery window if open.
|
-- Mark the current recovery window closed and cancelled, then remove it from the
|
||||||
if self.recoverywindow and self.recoverywindow.OPEN == true then
|
-- queue. We do NOT gate this on Window.OPEN: that flag is only set by
|
||||||
|
-- _CheckRecoveryTimes (not by RecoveryStart), and the recovery time check nils and
|
||||||
|
-- rebuilds self.recoverywindow every status tick, so OPEN is not a reliable signal
|
||||||
|
-- here. Setting OVER=true is what actually prevents the window from being re-opened
|
||||||
|
-- on the next tick while its [START,STOP) range is still active.
|
||||||
|
--
|
||||||
|
-- The removal is deferred by one tick (Delay>0) so it does not mutate the
|
||||||
|
-- recoverytimes table while _CheckRecoveryTimes may be iterating over it (the natural
|
||||||
|
-- close path calls RecoveryStop() from inside that loop).
|
||||||
|
if self.recoverywindow then
|
||||||
self.recoverywindow.OPEN = false
|
self.recoverywindow.OPEN = false
|
||||||
self.recoverywindow.OVER = true
|
self.recoverywindow.OVER = true
|
||||||
self:DeleteRecoveryWindow( self.recoverywindow )
|
self:DeleteRecoveryWindow( self.recoverywindow, 1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check recovery windows. This sets self.recoverywindow to the next window.
|
-- Check recovery windows. This sets self.recoverywindow to the next window.
|
||||||
@@ -12903,6 +12933,14 @@ function AIRBOSS:_LSOgrade( playerData )
|
|||||||
-- Circuit edit only take points awary from a 1 wire if there are more than 4 other deviations
|
-- Circuit edit only take points awary from a 1 wire if there are more than 4 other deviations
|
||||||
if playerData.wire == 1 and points >= 3 and N > 4 then
|
if playerData.wire == 1 and points >= 3 and N > 4 then
|
||||||
points = points -1
|
points = points -1
|
||||||
|
-- We also need to change the grade based on the new points.
|
||||||
|
if points == 4 then
|
||||||
|
grade = "OK"
|
||||||
|
elseif points == 3 then
|
||||||
|
grade = "(OK)"
|
||||||
|
elseif points == 2 then
|
||||||
|
grade = "--"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info("Returning: " .. grade .. " " .. points .. " " .. G)
|
env.info("Returning: " .. grade .. " " .. points .. " " .. G)
|
||||||
|
|||||||
@@ -5120,7 +5120,6 @@ end
|
|||||||
-- FSM Functions
|
-- FSM Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
--- On after "Planned" event.
|
--- On after "Planned" event.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
@@ -6610,7 +6609,7 @@ function AUFTRAG:GetDCSMissionTask(MissionGroup)
|
|||||||
local SeadUnitSet = SET_UNIT:New()
|
local SeadUnitSet = SET_UNIT:New()
|
||||||
for _,_unit in pairs (ScanUnitSet.Set) do
|
for _,_unit in pairs (ScanUnitSet.Set) do
|
||||||
local unit = _unit -- Wrapper.Unit#UNTI
|
local unit = _unit -- Wrapper.Unit#UNTI
|
||||||
if unit and unit:IsAlive() and unit:HasSEAD() then
|
if unit and unit:IsAlive() and unit.HasSEAD and unit:HasSEAD() then
|
||||||
self:T("Adding UNIT for SEAD: "..unit:GetName())
|
self:T("Adding UNIT for SEAD: "..unit:GetName())
|
||||||
local task = CONTROLLABLE.TaskAttackUnit(nil,unit,GroupAttack,AI.Task.WeaponExpend.ALL,1,Direction,self.engageAltitude,2956984318)
|
local task = CONTROLLABLE.TaskAttackUnit(nil,unit,GroupAttack,AI.Task.WeaponExpend.ALL,1,Direction,self.engageAltitude,2956984318)
|
||||||
table.insert(DCStasks, task)
|
table.insert(DCStasks, task)
|
||||||
|
|||||||
@@ -150,6 +150,9 @@ function BRIGADE:New(WarehouseName, BrigadeName)
|
|||||||
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
|
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
|
||||||
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
||||||
|
|
||||||
|
-- Add legion to DB
|
||||||
|
_DATABASE:AddLegion(self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -504,6 +504,9 @@ CSAR.AircraftType["OH58D"] = 2
|
|||||||
CSAR.AircraftType["CH-47Fbl1"] = 31
|
CSAR.AircraftType["CH-47Fbl1"] = 31
|
||||||
CSAR.AircraftType["AH-6J"] = 2
|
CSAR.AircraftType["AH-6J"] = 2
|
||||||
CSAR.AircraftType["MH-6J"] = 2
|
CSAR.AircraftType["MH-6J"] = 2
|
||||||
|
CSAR.AircraftType["Ka-50_3"] = 0
|
||||||
|
CSAR.AircraftType["Ka-50"] = 0
|
||||||
|
CSAR.AircraftType["AV8BNA"] = 0
|
||||||
|
|
||||||
--- CSAR class version.
|
--- CSAR class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
@@ -2538,8 +2541,11 @@ function CSAR:_AddMedevacMenuItem()
|
|||||||
if _unit then
|
if _unit then
|
||||||
--self:T("Unitname ".._unit:GetName().." IsAlive "..tostring(_unit:IsAlive()).." IsPlayer "..tostring(_unit:IsPlayer()))
|
--self:T("Unitname ".._unit:GetName().." IsAlive "..tostring(_unit:IsAlive()).." IsPlayer "..tostring(_unit:IsPlayer()))
|
||||||
if _unit:IsAlive() and _unit:IsPlayer() then
|
if _unit:IsAlive() and _unit:IsPlayer() then
|
||||||
local unitName = _unit:GetName()
|
local _maxUnits = self.AircraftType[_unit:GetTypeName()]
|
||||||
_UnitList[unitName] = unitName
|
if _maxUnits == nil or _maxUnits > 0 then
|
||||||
|
local unitName = _unit:GetName()
|
||||||
|
_UnitList[unitName] = unitName
|
||||||
|
end
|
||||||
end -- end isAlive
|
end -- end isAlive
|
||||||
end -- end if _unit
|
end -- end if _unit
|
||||||
end -- end for
|
end -- end for
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
|||||||
--
|
--
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
-- Date: September 2023
|
-- Date: September 2023
|
||||||
-- Last Update: Mar 2026
|
-- Last Update: June 2026
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--
|
--
|
||||||
--- **Ops** - Easy GCI & CAP Manager
|
--- **Ops** - Easy GCI & CAP Manager
|
||||||
@@ -286,7 +286,7 @@ EASYGCICAP = {
|
|||||||
|
|
||||||
--- EASYGCICAP class version.
|
--- EASYGCICAP class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
EASYGCICAP.version="0.1.35"
|
EASYGCICAP.version="0.1.36"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
--
|
--
|
||||||
@@ -854,16 +854,18 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.noalert5 > 0 then
|
if self.noalert5 > 0 then
|
||||||
local alert
|
for i=1,self.noalert5 do
|
||||||
if self.ClassName == "EASYGCICAP" then
|
local alert
|
||||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
|
if self.ClassName == "EASYGCICAP" then
|
||||||
elseif self.ClassName == "EASYA2G" then
|
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
|
||||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.BAI)
|
elseif self.ClassName == "EASYA2G" then
|
||||||
|
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.BAI)
|
||||||
|
end
|
||||||
|
alert:SetRequiredAssets(self.capgrouping)
|
||||||
|
alert:SetRepeat(99)
|
||||||
|
CAP_Wing:AddMission(alert)
|
||||||
|
table.insert(self.ListOfAuftrag,alert)
|
||||||
end
|
end
|
||||||
alert:SetRequiredAssets(self.noalert5)
|
|
||||||
alert:SetRepeat(99)
|
|
||||||
CAP_Wing:AddMission(alert)
|
|
||||||
table.insert(self.ListOfAuftrag,alert)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename }
|
self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename }
|
||||||
@@ -1835,9 +1837,10 @@ function EASYGCICAP:onafterRestart(From,Event,To)
|
|||||||
-- self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename }
|
-- self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename }
|
||||||
for _,_wing in pairs(self.wings or {}) do
|
for _,_wing in pairs(self.wings or {}) do
|
||||||
for _,_aw in pairs(_wing) do
|
for _,_aw in pairs(_wing) do
|
||||||
_wing[1]:Start()
|
_wing[1]:Restart()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
self:__Status(5)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -167,6 +167,9 @@ function FLEET:New(WarehouseName, FleetName)
|
|||||||
-- @param Ops.NavyGroup#NAVYGROUP NavyGroup The NAVYGROUP on mission.
|
-- @param Ops.NavyGroup#NAVYGROUP NavyGroup The NAVYGROUP on mission.
|
||||||
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
||||||
|
|
||||||
|
-- Add legion to DB
|
||||||
|
_DATABASE:AddLegion(self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ function FLOTILLA:New(TemplateGroupName, Ngroups, FlotillaName)
|
|||||||
-- Get initial ammo.
|
-- Get initial ammo.
|
||||||
self.ammo=self:_CheckAmmo()
|
self.ammo=self:_CheckAmmo()
|
||||||
|
|
||||||
|
-- Add cohort to DB
|
||||||
|
_DATABASE:AddCohort(self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1270,6 +1270,15 @@ function INTEL:_UpdateContact(Contact)
|
|||||||
else
|
else
|
||||||
Contact.maneuvering = false
|
Contact.maneuvering = false
|
||||||
end
|
end
|
||||||
|
local typename = Contact.group:GetTypeName()
|
||||||
|
local base_rcs = INTEL.RCS_Table[typename]
|
||||||
|
|
||||||
|
if not base_rcs then
|
||||||
|
-- Fallback: category default
|
||||||
|
local cat = Contact.group:GetCategory()
|
||||||
|
base_rcs = (cat and INTEL.RCS_CategoryDefault[cat]) or INTEL.RCS_Reference
|
||||||
|
end
|
||||||
|
Contact.rcs = base_rcs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -306,7 +306,6 @@ function LEGION:New(WarehouseName, LegionName)
|
|||||||
-- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to.
|
-- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to.
|
||||||
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
|
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
|
||||||
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2581,7 +2580,7 @@ end
|
|||||||
-- @return #boolean Returns `true` if given cohort can meet all requirements.
|
-- @return #boolean Returns `true` if given cohort can meet all requirements.
|
||||||
function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight, RangeMin)
|
function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight, RangeMin)
|
||||||
|
|
||||||
RangeMin = RangeMin or 0
|
RangeMin = RangeMin or -1
|
||||||
|
|
||||||
--- Function to check category.
|
--- Function to check category.
|
||||||
local function CheckCategory(_cohort)
|
local function CheckCategory(_cohort)
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ function PLATOON:New(TemplateGroupName, Ngroups, PlatoonName)
|
|||||||
|
|
||||||
-- Get ammo.
|
-- Get ammo.
|
||||||
self.ammo=self:_CheckAmmo()
|
self.ammo=self:_CheckAmmo()
|
||||||
|
|
||||||
|
-- Add cohort to DB
|
||||||
|
_DATABASE:AddCohort(self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
|
|||||||
|
|
||||||
-- See COHORT class
|
-- See COHORT class
|
||||||
|
|
||||||
|
-- Add cohort to DB
|
||||||
|
_DATABASE:AddCohort(self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -153,7 +153,7 @@ _TARGETID=0
|
|||||||
|
|
||||||
--- TARGET class version.
|
--- TARGET class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
TARGET.version="0.7.1"
|
TARGET.version="0.8.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -206,6 +206,9 @@ function TARGET:New(TargetObject)
|
|||||||
self:AddTransition("*", "Status", "*") -- Status update.
|
self:AddTransition("*", "Status", "*") -- Status update.
|
||||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
|
self:AddTransition("*", "ElementDestroyed", "*") -- A target element was destroyed.
|
||||||
|
self:AddTransition("*", "ElementDead", "*") -- A target element is dead (destroyed or despawned).
|
||||||
|
|
||||||
self:AddTransition("*", "ObjectDamaged", "*") -- A target object was damaged.
|
self:AddTransition("*", "ObjectDamaged", "*") -- A target object was damaged.
|
||||||
self:AddTransition("*", "ObjectDestroyed", "*") -- A target object was destroyed.
|
self:AddTransition("*", "ObjectDestroyed", "*") -- A target object was destroyed.
|
||||||
self:AddTransition("*", "ObjectDead", "*") -- A target object is dead (destroyed or despawned).
|
self:AddTransition("*", "ObjectDead", "*") -- A target object is dead (destroyed or despawned).
|
||||||
@@ -245,6 +248,19 @@ function TARGET:New(TargetObject)
|
|||||||
-- @param #number delay Delay in seconds.
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
|
||||||
|
--- Triggers the FSM event "ElementDestroyed".
|
||||||
|
-- @function [parent=#TARGET] ElementDestroyed
|
||||||
|
-- @param #TARGET self
|
||||||
|
-- @param #string ElementName Name of the element.
|
||||||
|
-- @param #TARGET.Object Target Target object.
|
||||||
|
|
||||||
|
--- Triggers the FSM event "ElementDead".
|
||||||
|
-- @function [parent=#TARGET] ElementDead
|
||||||
|
-- @param #TARGET self
|
||||||
|
-- @param #string ElementName Name of the element.
|
||||||
|
-- @param #TARGET.Object Target Target object.
|
||||||
|
|
||||||
|
|
||||||
--- Triggers the FSM event "ObjectDamaged".
|
--- Triggers the FSM event "ObjectDamaged".
|
||||||
-- @function [parent=#TARGET] ObjectDamaged
|
-- @function [parent=#TARGET] ObjectDamaged
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
@@ -642,6 +658,19 @@ function TARGET:onafterStatus(From, Event, To)
|
|||||||
|
|
||||||
-- FSM state.
|
-- FSM state.
|
||||||
local fsmstate=self:GetState()
|
local fsmstate=self:GetState()
|
||||||
|
|
||||||
|
-- First we check any target has been destroyed and the dead/unitlost event was not fired
|
||||||
|
for i,_target in pairs(self.targets) do
|
||||||
|
local target=_target --#TARGET.Object
|
||||||
|
local life=self:GetTargetLife(target)
|
||||||
|
if life<1 and target.Status~=TARGET.ObjectStatus.DEAD then
|
||||||
|
self:E(self.lid..string.format("FF life is zero but no object dead event fired ==> waiting for target object %s events!", tostring(target.Name)))
|
||||||
|
-- We wait 60 seconds for the events to occur
|
||||||
|
self:__Status(-60)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
-- Update damage.
|
-- Update damage.
|
||||||
local damaged=false
|
local damaged=false
|
||||||
@@ -736,6 +765,61 @@ end
|
|||||||
-- FSM Events
|
-- FSM Events
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- On after "ElementDestroyed" event.
|
||||||
|
-- @param #TARGET self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param #string Name Name of the element.
|
||||||
|
-- @param #TARGET.Object Target Target object.
|
||||||
|
function TARGET:onafterElementDestroyed(From, Event, To, Name, Target)
|
||||||
|
-- Debug message.
|
||||||
|
self:T(self.lid..string.format("Element %s of target object %s destroyed", Name, Target.Name))
|
||||||
|
|
||||||
|
-- Increase destroyed counter.
|
||||||
|
Target.Ndestroyed=Target.Ndestroyed+1
|
||||||
|
|
||||||
|
-- Increase dead counter.
|
||||||
|
self.Ndestroyed=self.Ndestroyed+1
|
||||||
|
|
||||||
|
self:ElementDead(Name, Target)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- On after "ElementDead" event.
|
||||||
|
-- @param #TARGET self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param #string Name Name of the element.
|
||||||
|
-- @param #TARGET.Object Target Target object.
|
||||||
|
function TARGET:onafterElementDead(From, Event, To, Name, Target)
|
||||||
|
-- Debug message.
|
||||||
|
self:T(self.lid..string.format("Element %s of target object %s dead", Name, Target.Name))
|
||||||
|
|
||||||
|
-- Increase dead counter.
|
||||||
|
Target.Ndead=Target.Ndead+1
|
||||||
|
|
||||||
|
-- Increase dead counter.
|
||||||
|
self.Ndead=self.Ndead+1
|
||||||
|
|
||||||
|
-- All dead ==> Trigger event.
|
||||||
|
if Target.Ndestroyed==Target.N0 then
|
||||||
|
|
||||||
|
self:ObjectDestroyed(Target)
|
||||||
|
|
||||||
|
elseif Target.Ndead==Target.N0 then
|
||||||
|
|
||||||
|
self:ObjectDead(Target)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- On after "ObjectDamaged" event.
|
--- On after "ObjectDamaged" event.
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
@@ -761,11 +845,6 @@ function TARGET:onafterObjectDestroyed(From, Event, To, Target)
|
|||||||
-- Debug message.
|
-- Debug message.
|
||||||
self:T(self.lid..string.format("Object %s destroyed", Target.Name))
|
self:T(self.lid..string.format("Object %s destroyed", Target.Name))
|
||||||
|
|
||||||
-- Increase destroyed counter.
|
|
||||||
self.Ndestroyed=self.Ndestroyed+1
|
|
||||||
|
|
||||||
Target.Ndestroyed=Target.Ndestroyed+1
|
|
||||||
|
|
||||||
Target.Life=0
|
Target.Life=0
|
||||||
|
|
||||||
-- Call object dead event.
|
-- Call object dead event.
|
||||||
@@ -787,16 +866,10 @@ function TARGET:onafterObjectDead(From, Event, To, Target)
|
|||||||
|
|
||||||
-- Set target status.
|
-- Set target status.
|
||||||
Target.Status=TARGET.ObjectStatus.DEAD
|
Target.Status=TARGET.ObjectStatus.DEAD
|
||||||
|
|
||||||
-- Increase dead object counter
|
|
||||||
Target.Ndead=Target.Ndead+1
|
|
||||||
|
|
||||||
-- Set target object life to 0.
|
-- Set target object life to 0.
|
||||||
Target.Life=0
|
Target.Life=0
|
||||||
|
|
||||||
-- Increase dead counter.
|
|
||||||
self.Ndead=self.Ndead+1
|
|
||||||
|
|
||||||
-- Check if anyone is alive?
|
-- Check if anyone is alive?
|
||||||
local dead=true
|
local dead=true
|
||||||
for _,_target in pairs(self.targets) do
|
for _,_target in pairs(self.targets) do
|
||||||
@@ -889,54 +962,21 @@ function TARGET:OnEventUnitDeadOrLost(EventData)
|
|||||||
-- Add to the list of casualties.
|
-- Add to the list of casualties.
|
||||||
table.insert(self.casualties, Name)
|
table.insert(self.casualties, Name)
|
||||||
|
|
||||||
-- Try to get target Group.
|
-- Get target from Group or Unit.
|
||||||
local target=self:GetTargetByName(EventData.IniGroupName)
|
local target=self:GetTargetByName(EventData.IniGroupName) or self:GetTargetByName(EventData.IniUnitName)
|
||||||
|
|
||||||
-- Try unit target.
|
|
||||||
if not target then
|
|
||||||
target=self:GetTargetByName(EventData.IniUnitName)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if we could find a target object.
|
-- Check if we could find a target object.
|
||||||
if target then
|
if target then
|
||||||
|
|
||||||
local Ndead=target.Ndead
|
-- Increase dead/destroyed counter
|
||||||
local Ndestroyed=target.Ndestroyed
|
|
||||||
if EventData.id==EVENTS.RemoveUnit then
|
if EventData.id==EVENTS.RemoveUnit then
|
||||||
Ndead=Ndead+1
|
self:ElementDead(Name, target)
|
||||||
else
|
else
|
||||||
Ndestroyed=Ndestroyed+1
|
self:ElementDestroyed(Name, target)
|
||||||
Ndead=Ndead+1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Check if ALL objects are dead
|
|
||||||
if Ndead==target.N0 then
|
|
||||||
|
|
||||||
if Ndestroyed>=target.N0 then
|
|
||||||
|
|
||||||
-- Debug message.
|
|
||||||
self:T2(self.lid..string.format("EVENT ID=%d: target %s dead/lost ==> destroyed", EventData.id, tostring(target.Name)))
|
|
||||||
|
|
||||||
target.Life = 0
|
|
||||||
|
|
||||||
-- Trigger object destroyed event. This sets the Life to zero and increases Ndestroyed
|
|
||||||
self:ObjectDestroyed(target)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Debug message.
|
|
||||||
self:T2(self.lid..string.format("EVENT ID=%d: target %s removed ==> dead", EventData.id, tostring(target.Name)))
|
|
||||||
|
|
||||||
target.Life = 0
|
|
||||||
|
|
||||||
-- Trigger object dead event. This sets the Life to zero and increases Ndead counter
|
|
||||||
self:ObjectDead(target)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
self:E(self.lid..string.format("ERROR: Could not get target from IniGroup or IniUnit name when event Dead or UnitLost occured! Stats are not correctly updated :("))
|
||||||
end -- Event belongs to this TARGET
|
end -- Event belongs to this TARGET
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -61,24 +61,21 @@
|
|||||||
--
|
--
|
||||||
-- # The MSRS Concept
|
-- # The MSRS Concept
|
||||||
--
|
--
|
||||||
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
|
-- This class allows to broadcast sound files or text to speech via Simple Radio Standalone (SRS).
|
||||||
--
|
--
|
||||||
-- ## Prerequisites
|
-- ## Prerequisites
|
||||||
--
|
--
|
||||||
-- * This script needs SRS version >= 1.9.6
|
|
||||||
-- * You need to de-sanitize os, io and lfs in the missionscripting.lua
|
-- * You need to de-sanitize os, io and lfs in the missionscripting.lua
|
||||||
-- * Optional: DCS-gRPC as backend to communicate with SRS (via infra)
|
-- * Optional but recommended: HOUND TTS as backend to communicate with SRS (via plugin)
|
||||||
-- * Optional: HOUNDTTS as backend to communicate with SRS (via infra)
|
-- * Optional: DCS-gRPC as backend to communicate with SRS (via plugin)
|
||||||
|
--
|
||||||
|
-- ## Known Issues
|
||||||
--
|
--
|
||||||
-- ## Knwon Issues
|
-- ### Pop-up Window (with backend windows)
|
||||||
--
|
|
||||||
-- ### Pop-up Window
|
|
||||||
--
|
--
|
||||||
-- The text-to-speech conversion of SRS is done via an external exe file. When this file is called, a windows `cmd` window is briefly opended. That puts DCS out of focus, which is annoying,
|
-- The text-to-speech conversion of SRS is done via an external exe file. When this file is called, a windows `cmd` window is briefly opended. That puts DCS out of focus, which is annoying,
|
||||||
-- expecially in VR but unavoidable (if you have a solution, please feel free to share!).
|
-- expecially in VR, but unavoidable unless you use HOUD or gRPC as backends.
|
||||||
--
|
--
|
||||||
-- NOTE that this is not an issue if the mission is running on a server.
|
|
||||||
-- Also NOTE that using DCS-gRPC and Hound-TTS as backend create no pop-up window.
|
|
||||||
--
|
--
|
||||||
-- # Play Sound Files
|
-- # Play Sound Files
|
||||||
--
|
--
|
||||||
@@ -137,16 +134,17 @@
|
|||||||
--
|
--
|
||||||
-- See @{#MSRS.LoadConfigFile} for details on how to set this up.
|
-- See @{#MSRS.LoadConfigFile} for details on how to set this up.
|
||||||
--
|
--
|
||||||
-- ## TTS Providers
|
-- ## TTS (text-to-speech) Providers
|
||||||
--
|
--
|
||||||
-- The default provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use.
|
-- The **currently recommended** backend is HOUND TTS (no pop-ups), which has a wide variety of TTS providers.
|
||||||
|
-- The *default* provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use.
|
||||||
--
|
--
|
||||||
-- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing
|
-- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing
|
||||||
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
|
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
|
||||||
-- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
|
-- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
|
||||||
-- plays a message on 255 MHz AM for the blue coalition in-game.
|
-- plays a message on 255 MHz AM for the blue coalition in-game.
|
||||||
--
|
--
|
||||||
-- ### Google
|
-- ### Google (best speech synthesis)
|
||||||
--
|
--
|
||||||
-- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsGoogle} functions:
|
-- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsGoogle} functions:
|
||||||
--
|
--
|
||||||
@@ -162,7 +160,7 @@
|
|||||||
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
|
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
|
||||||
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
|
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
|
||||||
--
|
--
|
||||||
-- ### Amazon Web Service [Only DCS-gRPC backend]
|
-- ### Amazon Web Service [Only HOUDN and DCS-gRPC backend]
|
||||||
--
|
--
|
||||||
-- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAmazon} functions:
|
-- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAmazon} functions:
|
||||||
--
|
--
|
||||||
@@ -173,7 +171,7 @@
|
|||||||
--
|
--
|
||||||
-- You can set the voice to use with AWS via @{#MSRS.SetVoiceAmazon}.
|
-- You can set the voice to use with AWS via @{#MSRS.SetVoiceAmazon}.
|
||||||
--
|
--
|
||||||
-- ### Microsoft Azure [Only DCS-gRPC backend]
|
-- ### Microsoft Azure [Only HOUND and DCS-gRPC backend]
|
||||||
--
|
--
|
||||||
-- In order to use Microsoft Azure for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAzure} functions:
|
-- In order to use Microsoft Azure for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAzure} functions:
|
||||||
--
|
--
|
||||||
@@ -184,6 +182,12 @@
|
|||||||
--
|
--
|
||||||
-- You can set the voice to use with Azure via @{#MSRS.SetVoiceAzure}.
|
-- You can set the voice to use with Azure via @{#MSRS.SetVoiceAzure}.
|
||||||
--
|
--
|
||||||
|
-- ### Piper, OpenAI and further provider options [Only HOUND backend]
|
||||||
|
--
|
||||||
|
-- You can use further providers with HOUND TTS as a backend, see [Hound-TTS](https://github.com/uriba107/HoundTTS/releases) for setup.
|
||||||
|
--
|
||||||
|
-- msrs:SetProvider(MSRS.Provider.PIPER) -- e.g. for piper
|
||||||
|
--
|
||||||
-- ## Backend
|
-- ## Backend
|
||||||
--
|
--
|
||||||
-- The default interface to SRS is via calling the 'DCS-SR-ExternalAudio.exe'. As noted above, this has the unavoidable drawback that a pop-up briefly appears
|
-- The default interface to SRS is via calling the 'DCS-SR-ExternalAudio.exe'. As noted above, this has the unavoidable drawback that a pop-up briefly appears
|
||||||
@@ -191,13 +195,13 @@
|
|||||||
--
|
--
|
||||||
-- ## Hound TTS as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS
|
-- ## Hound TTS as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS
|
||||||
--
|
--
|
||||||
-- An alternative interface to SRS is [Hound-TTS](https://github.com/uriba107/HoundTTS/releases). This does not call an exe file and therefore avoids the annoying pop-up window.
|
-- the recommended alternative interface to SRS is [Hound-TTS](https://github.com/uriba107/HoundTTS/releases). This does not call an exe file and therefore avoids the annoying pop-up window.
|
||||||
-- In addition to Windows and Google cloud, it also offers Piper local voice creation and others as providers for TTS.
|
-- In addition to Windows and Google cloud, it also offers Piper local voice creation and others as providers for TTS.
|
||||||
--
|
--
|
||||||
-- Use @{#MSRS.SetDefaultBackendHound} to enable [Hound-TTS](https://github.com/uriba107/HoundTTS/releases) as an alternate backend.
|
-- Use @{#MSRS.SetDefaultBackendHound} to enable [Hound-TTS](https://github.com/uriba107/HoundTTS/releases) as an alternate backend.
|
||||||
-- This can be useful if the popup should be avoided or to use Piper or others for TTS. Please note, only text-to-speech is supported and it it cannot be used to transmit audio files.
|
-- This can be useful if the popup should be avoided or to use Piper or others for TTS. Please note, only text-to-speech is supported and it it cannot be used to transmit audio files.
|
||||||
--
|
--
|
||||||
-- Hound TTS must be installed and configured per the [Hound TTS](https://github.com/uriba107/HoundTTS#installation) prior to use.
|
-- Hound TTS must be installed and configured per the [Hound TTS](https://github.com/uriba107/HoundTTS#installation) GitHub prior to use.
|
||||||
-- If a cloud TTS provider is being used, the API key(s) must be set as per the documentation.
|
-- If a cloud TTS provider is being used, the API key(s) must be set as per the documentation.
|
||||||
-- Hound TTS can be used both with DCS dedicated server and regular DCS installations.
|
-- Hound TTS can be used both with DCS dedicated server and regular DCS installations.
|
||||||
--
|
--
|
||||||
@@ -280,7 +284,7 @@ MSRS = {
|
|||||||
|
|
||||||
--- MSRS class version.
|
--- MSRS class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
MSRS.version="0.3.5"
|
MSRS.version="0.3.7"
|
||||||
|
|
||||||
--- Voices
|
--- Voices
|
||||||
-- @type MSRS.Voices
|
-- @type MSRS.Voices
|
||||||
@@ -731,14 +735,16 @@ MSRS.Backend = {
|
|||||||
-- @field #string AZURE Microsoft Azure (`azure`). Only possible with DCS-gRPC backend.
|
-- @field #string AZURE Microsoft Azure (`azure`). Only possible with DCS-gRPC backend.
|
||||||
-- @field #string AMAZON Amazon Web Service (`aws`). Only possible with DCS-gRPC backend.
|
-- @field #string AMAZON Amazon Web Service (`aws`). Only possible with DCS-gRPC backend.
|
||||||
-- @field #string PIPER Piper local voice service. Only possible with Hound-TTS backend.
|
-- @field #string PIPER Piper local voice service. Only possible with Hound-TTS backend.
|
||||||
-- @field #string KITTEN Kitten voice server. Only possible with Hound-TTS backend.
|
-- @field #string KITTEN Kitten voice server. Only possible with Hound-TTS backend. Superseded by OpenAI
|
||||||
|
-- @field #string OPENAI OpenAI voice server. Only possible with Hound-TTS backend.
|
||||||
MSRS.Provider = {
|
MSRS.Provider = {
|
||||||
WINDOWS = "win",
|
WINDOWS = "win",
|
||||||
GOOGLE = "gcloud",
|
GOOGLE = "gcloud",
|
||||||
AZURE = "azure",
|
AZURE = "azure",
|
||||||
AMAZON = "aws",
|
AMAZON = "aws",
|
||||||
PIPER = "piper",
|
PIPER = "piper",
|
||||||
KITTEN = "kitten",
|
KITTEN = "openai",
|
||||||
|
OPENAI = "openai",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Function for UUID.
|
--- Function for UUID.
|
||||||
@@ -1471,6 +1477,15 @@ function MSRS:SetTTSProviderKitten()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Use OpenAI to provide text-to-speech. Only supported if used in combination with Hound-TTS as backend.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetTTSProviderOpenAI()
|
||||||
|
self:F()
|
||||||
|
self:SetProvider(MSRS.Provider.OPENAI)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Print SRS help to DCS log file.
|
--- Print SRS help to DCS log file.
|
||||||
-- @param #MSRS self
|
-- @param #MSRS self
|
||||||
-- @return #MSRS self
|
-- @return #MSRS self
|
||||||
@@ -1605,7 +1620,7 @@ function MSRS:PlayText(Text, Delay, Coordinate, Speed, Speaker)
|
|||||||
else
|
else
|
||||||
|
|
||||||
local speaker = Speaker or self.Speaker
|
local speaker = Speaker or self.Speaker
|
||||||
|
|
||||||
if self.backend==MSRS.Backend.GRPC then
|
if self.backend==MSRS.Backend.GRPC then
|
||||||
self:T(self.lid.."Transmitting")
|
self:T(self.lid.."Transmitting")
|
||||||
self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate)
|
self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate)
|
||||||
@@ -2088,19 +2103,16 @@ function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Co
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
Frequencies = UTILS.EnsureTable(Frequencies)
|
Frequencies = UTILS.EnsureTable(Frequencies or self.frequencies)
|
||||||
Modulations = UTILS.EnsureTable(Modulations)
|
Modulations = UTILS.EnsureTable(Modulations or self.modulations)
|
||||||
|
|
||||||
local ffs = {}
|
local ffs = {}
|
||||||
for _,_f in pairs(Frequencies or self.frequencies) do
|
for _,_f in pairs(Frequencies) do
|
||||||
table.insert(ffs,string.format("%.1f",_f))
|
table.insert(ffs,string.format("%.1f",_f))
|
||||||
end
|
end
|
||||||
|
|
||||||
local freqs = table.concat(ffs, ",")
|
local freqs = table.concat(ffs, ",")
|
||||||
|
local modus = table.concat(Modulations, ",")
|
||||||
|
|
||||||
|
|
||||||
local modus = table.concat(Modulations or self.modulations, ",")
|
|
||||||
|
|
||||||
local coal=Coalition or self.coalition
|
local coal=Coalition or self.coalition
|
||||||
local gender=Gender or self.gender
|
local gender=Gender or self.gender
|
||||||
@@ -2144,7 +2156,7 @@ function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Co
|
|||||||
gender = gender,
|
gender = gender,
|
||||||
speaker = Speaker or self.Speaker,
|
speaker = Speaker or self.Speaker,
|
||||||
}
|
}
|
||||||
|
|
||||||
local speechtime = HoundTTS.Transmit(Message, TransmissionP, ProviderP)
|
local speechtime = HoundTTS.Transmit(Message, TransmissionP, ProviderP)
|
||||||
|
|
||||||
return speechtime
|
return speechtime
|
||||||
@@ -2258,6 +2270,71 @@ function MSRS._HoundTranslate(Message,Parameters,CallbackFunction)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Create and run a radio jammer for a number of seconds on given frequencies. Requires HOUND backend v0.2.0-beta5 or better.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Frequencies The table of frequencies to use.
|
||||||
|
-- @param #table Modulations The table of modulations to use.
|
||||||
|
-- @param #number Coalition (Optional) The coalition to use. Defaults to previously set coalition on this instance.
|
||||||
|
-- @param #string Noisetype (Optional) One of "white" (default) | "chirp" | "harsh" | "jam".
|
||||||
|
-- @param #number Volume (Optional) Volume 0.0–1.0 (default 1.0).
|
||||||
|
-- @param #number Seconds (Optional) How long in seconds this jammer runs. Defaults to 30 seconds.
|
||||||
|
-- @param #string Label (Optional) The label to use, defaults to "MSRS".
|
||||||
|
-- @param DCS#Vec3 Vec3 (Optional) Vec3 of the sender's position.
|
||||||
|
-- @param #boolean Encrypt (Optional) If true, use the SRS encrypt option.
|
||||||
|
-- @param #number EncKey (Optional) If encrypt is true, use this encrypt key.
|
||||||
|
-- @return #number ID The Jammer ID to switch the jamming off again with `MSRS.RadioJammerOff()`
|
||||||
|
function MSRS:RadioJammerOn(Frequencies, Modulations, Coalition, Noisetype, Volume, Seconds, Label, Vec3, Encrypt, EncKey)
|
||||||
|
self:T(self.lid.."RadioJammerOn")
|
||||||
|
|
||||||
|
Frequencies = UTILS.EnsureTable(Frequencies)
|
||||||
|
Modulations = UTILS.EnsureTable(Modulations)
|
||||||
|
|
||||||
|
local ffs = {}
|
||||||
|
for _,_f in pairs(Frequencies or self.frequencies) do
|
||||||
|
table.insert(ffs,string.format("%.1f",_f))
|
||||||
|
end
|
||||||
|
|
||||||
|
local freqs = table.concat(ffs, ",")
|
||||||
|
local modus = table.concat(Modulations or self.modulations, ",")
|
||||||
|
-- Replace modulation
|
||||||
|
modus=modus:gsub("0", "AM")
|
||||||
|
modus=modus:gsub("1", "FM")
|
||||||
|
|
||||||
|
local coal=Coalition or self.coalition or coalition.side.RED
|
||||||
|
local secs = Seconds or 30
|
||||||
|
|
||||||
|
local TransmissionP = {}
|
||||||
|
local ProviderP = {}
|
||||||
|
|
||||||
|
TransmissionP.transmitter = "srs"
|
||||||
|
TransmissionP.freqs = freqs
|
||||||
|
TransmissionP.modulations = modus
|
||||||
|
TransmissionP.coalition = coal or self.coalition -- number 0=spectator, 1=red, 2=blue
|
||||||
|
TransmissionP.name = Label or self.Label -- string client name shown in SRS
|
||||||
|
TransmissionP.point = Vec3 -- DCS Vec3 (optional) — initial transmitter position
|
||||||
|
TransmissionP.encrypt = Encrypt -- bool
|
||||||
|
TransmissionP.encKey = EncKey -- number 0–255
|
||||||
|
|
||||||
|
ProviderP.noiseType = Noisetype or "white"
|
||||||
|
ProviderP.volume = Volume or 1
|
||||||
|
|
||||||
|
local ID = HoundTTS.TransmitNoise(TransmissionP, ProviderP)
|
||||||
|
|
||||||
|
self.NoiseID = ID
|
||||||
|
|
||||||
|
self:ScheduleOnce(secs,MSRS.RadioJammerOff,self,ID)
|
||||||
|
|
||||||
|
return ID
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch off a running Jammer using its ID.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number ID
|
||||||
|
function MSRS:RadioJammerOff(ID)
|
||||||
|
return HoundTTS.KillSession(ID or self.NoiseID)
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Config File
|
-- Config File
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
-- @field #boolean isAirdrome Airbase is an airdrome.
|
-- @field #boolean isAirdrome Airbase is an airdrome.
|
||||||
-- @field #boolean isHelipad Airbase is a helipad.
|
-- @field #boolean isHelipad Airbase is a helipad.
|
||||||
-- @field #boolean isShip Airbase is a ship.
|
-- @field #boolean isShip Airbase is a ship.
|
||||||
|
-- @field #boolean isZell Airbase is a ZELL Booster.
|
||||||
-- @field #table parking Parking spot data.
|
-- @field #table parking Parking spot data.
|
||||||
-- @field #table parkingByID Parking spot data table with ID as key.
|
-- @field #table parkingByID Parking spot data table with ID as key.
|
||||||
-- @field #table parkingWhitelist List of parking spot terminal IDs considered for spawning.
|
-- @field #table parkingWhitelist List of parking spot terminal IDs considered for spawning.
|
||||||
@@ -458,115 +459,420 @@ AIRBASE.TheChannel = {
|
|||||||
|
|
||||||
--- Airbases of the Syria map
|
--- Airbases of the Syria map
|
||||||
--
|
--
|
||||||
-- * `AIRBASE.Syria.Abu_al_Duhur` Abu al-Duhur
|
-- * AIRBASE.Syria.Abu_al_Duhur
|
||||||
-- * `AIRBASE.Syria.Adana_Sakirpasa` Adana Sakirpasa
|
-- * AIRBASE.Syria.Adana_Sakirpasa
|
||||||
-- * `AIRBASE.Syria.Akrotiri` Akrotiri
|
-- * AIRBASE.Syria.Adiyaman
|
||||||
-- * `AIRBASE.Syria.Al_Qusayr` Al Qusayr
|
-- * AIRBASE.Syria.Akrotiri
|
||||||
-- * `AIRBASE.Syria.Al_Dumayr` Al-Dumayr
|
-- * AIRBASE.Syria.Al_Dumayr
|
||||||
-- * `AIRBASE.Syria.Aleppo` Aleppo
|
-- * AIRBASE.Syria.Al_Qusayr
|
||||||
-- * `AIRBASE.Syria.An_Nasiriyah` An Nasiriyah
|
-- * AIRBASE.Syria.Aleppo
|
||||||
-- * `AIRBASE.Syria.At_Tanf` At Tanf
|
-- * AIRBASE.Syria.An_Nasiriyah
|
||||||
-- * `AIRBASE.Syria.Bassel_Al_Assad` Bassel Al-Assad
|
-- * AIRBASE.Syria.At_Tanf
|
||||||
-- * `AIRBASE.Syria.Beirut_Rafic_Hariri` Beirut-Rafic Hariri
|
-- * AIRBASE.Syria.Bassel_Al_Assad
|
||||||
-- * `AIRBASE.Syria.Ben_Gurion` Ben Gurion
|
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
||||||
-- * `AIRBASE.Syria.Damascus` Damascus
|
-- * AIRBASE.Syria.Ben_Gurion
|
||||||
-- * `AIRBASE.Syria.Deir_ez_Zor` Deir ez-Zor
|
-- * AIRBASE.Syria.Chukurova
|
||||||
-- * `AIRBASE.Syria.Ercan` Ercan
|
-- * AIRBASE.Syria.Damascus
|
||||||
-- * `AIRBASE.Syria.Eyn_Shemer` Eyn Shemer
|
-- * AIRBASE.Syria.Deir_ez_Zor
|
||||||
-- * `AIRBASE.Syria.Gaziantep` Gaziantep
|
-- * AIRBASE.Syria.Diyarbakir
|
||||||
-- * `AIRBASE.Syria.Gazipasa` Gazipasa
|
-- * AIRBASE.Syria.Ercan
|
||||||
-- * `AIRBASE.Syria.Gecitkale` Gecitkale
|
-- * AIRBASE.Syria.Eyn_Shemer
|
||||||
-- * `AIRBASE.Syria.H` H
|
-- * AIRBASE.Syria.Gaziantep
|
||||||
-- * `AIRBASE.Syria.H3` H3
|
-- * AIRBASE.Syria.Gazipasa
|
||||||
-- * `AIRBASE.Syria.H3_Northwest` H3 Northwest
|
-- * AIRBASE.Syria.Gecitkale
|
||||||
-- * `AIRBASE.Syria.H3_Southwest` H3 Southwest
|
-- * AIRBASE.Syria.Gulechoba
|
||||||
-- * `AIRBASE.Syria.H4` H4
|
-- * AIRBASE.Syria.H3
|
||||||
-- * `AIRBASE.Syria.Haifa` Haifa
|
-- * AIRBASE.Syria.H3_Northwest
|
||||||
-- * `AIRBASE.Syria.Hama` Hama
|
-- * AIRBASE.Syria.H3_Southwest
|
||||||
-- * `AIRBASE.Syria.Hatay` Hatay
|
-- * AIRBASE.Syria.H4
|
||||||
-- * `AIRBASE.Syria.Hatzor` Hatzor
|
-- * AIRBASE.Syria.H4_Emergency
|
||||||
-- * `AIRBASE.Syria.Herzliya` Herzliya
|
-- * AIRBASE.Syria.HC01
|
||||||
-- * `AIRBASE.Syria.Incirlik` Incirlik
|
-- * AIRBASE.Syria.HC02
|
||||||
-- * `AIRBASE.Syria.Jirah` Jirah
|
-- * AIRBASE.Syria.HC03
|
||||||
-- * `AIRBASE.Syria.Khalkhalah` Khalkhalah
|
-- * AIRBASE.Syria.HC04
|
||||||
-- * `AIRBASE.Syria.Kharab_Ishk` Kharab Ishk
|
-- * AIRBASE.Syria.HC05
|
||||||
-- * `AIRBASE.Syria.King_Abdullah_II` King Abdullah II
|
-- * AIRBASE.Syria.HC06
|
||||||
-- * `AIRBASE.Syria.King_Hussein_Air_College` King Hussein Air College
|
-- * AIRBASE.Syria.HI01
|
||||||
-- * `AIRBASE.Syria.Kingsfield` Kingsfield
|
-- * AIRBASE.Syria.HI02
|
||||||
-- * `AIRBASE.Syria.Kiryat_Shmona` Kiryat Shmona
|
-- * AIRBASE.Syria.HI03
|
||||||
-- * `AIRBASE.Syria.Kuweires` Kuweires
|
-- * AIRBASE.Syria.HI05
|
||||||
-- * `AIRBASE.Syria.Lakatamia` Lakatamia
|
-- * AIRBASE.Syria.HI06
|
||||||
-- * `AIRBASE.Syria.Larnaca` Larnaca
|
-- * AIRBASE.Syria.HI07
|
||||||
-- * `AIRBASE.Syria.Marj_Ruhayyil` Marj Ruhayyil
|
-- * AIRBASE.Syria.HI08
|
||||||
-- * `AIRBASE.Syria.Marj_as_Sultan_North` Marj as Sultan North
|
-- * AIRBASE.Syria.HI09
|
||||||
-- * `AIRBASE.Syria.Marj_as_Sultan_South` Marj as Sultan South
|
-- * AIRBASE.Syria.HI11
|
||||||
-- * `AIRBASE.Syria.Marka` Marka
|
-- * AIRBASE.Syria.HI12
|
||||||
-- * `AIRBASE.Syria.Megiddo` Megiddo
|
-- * AIRBASE.Syria.HI13
|
||||||
-- * `AIRBASE.Syria.Mezzeh` Mezzeh
|
-- * AIRBASE.Syria.HI14
|
||||||
-- * `AIRBASE.Syria.Minakh` Minakh
|
-- * AIRBASE.Syria.HI15
|
||||||
-- * `AIRBASE.Syria.Muwaffaq_Salti` Muwaffaq Salti
|
-- * AIRBASE.Syria.HI16
|
||||||
-- * `AIRBASE.Syria.Naqoura` Naqoura
|
-- * AIRBASE.Syria.HI17
|
||||||
-- * `AIRBASE.Syria.Nicosia` Nicosia
|
-- * AIRBASE.Syria.HI18
|
||||||
-- * `AIRBASE.Syria.Palmachim` Palmachim
|
-- * AIRBASE.Syria.HI20
|
||||||
-- * `AIRBASE.Syria.Palmyra` Palmyra
|
-- * AIRBASE.Syria.HI21
|
||||||
-- * `AIRBASE.Syria.Paphos` Paphos
|
-- * AIRBASE.Syria.HI22
|
||||||
-- * `AIRBASE.Syria.Pinarbashi` Pinarbashi
|
-- * AIRBASE.Syria.HI23
|
||||||
-- * `AIRBASE.Syria.Prince_Hassan` Prince Hassan
|
-- * AIRBASE.Syria.HI24
|
||||||
-- * `AIRBASE.Syria.Qabr_as_Sitt` Qabr as Sitt
|
-- * AIRBASE.Syria.HI25
|
||||||
-- * `AIRBASE.Syria.Ramat_David` Ramat David
|
-- * AIRBASE.Syria.HI26
|
||||||
-- * `AIRBASE.Syria.Rayak` Rayak
|
-- * AIRBASE.Syria.HJ01
|
||||||
-- * `AIRBASE.Syria.Rene_Mouawad` Rene Mouawad
|
-- * AIRBASE.Syria.HJ02
|
||||||
-- * `AIRBASE.Syria.Rosh_Pina` Rosh Pina
|
-- * AIRBASE.Syria.HJ03
|
||||||
-- * `AIRBASE.Syria.Ruwayshid` Ruwayshid
|
-- * AIRBASE.Syria.HJ04
|
||||||
-- * `AIRBASE.Syria.Sanliurfa` Sanliurfa
|
-- * AIRBASE.Syria.HL01
|
||||||
-- * `AIRBASE.Syria.Sayqal` Sayqal
|
-- * AIRBASE.Syria.HL02
|
||||||
-- * `AIRBASE.Syria.Shayrat` Shayrat
|
-- * AIRBASE.Syria.HL03
|
||||||
-- * `AIRBASE.Syria.Tabqa` Tabqa
|
-- * AIRBASE.Syria.HL04
|
||||||
-- * `AIRBASE.Syria.Taftanaz` Taftanaz
|
-- * AIRBASE.Syria.HL05
|
||||||
-- * `AIRBASE.Syria.Tal_Siman` Tal Siman
|
-- * AIRBASE.Syria.HL06
|
||||||
-- * `AIRBASE.Syria.Tel_Nof` Tel Nof
|
-- * AIRBASE.Syria.HL07
|
||||||
-- * `AIRBASE.Syria.Tha_lah` Tha'lah
|
-- * AIRBASE.Syria.HL08
|
||||||
-- * `AIRBASE.Syria.Tiyas` Tiyas
|
-- * AIRBASE.Syria.HL09
|
||||||
-- * `AIRBASE.Syria.Wujah_Al_Hajar` Wujah Al Hajar
|
-- * AIRBASE.Syria.HL10
|
||||||
|
-- * AIRBASE.Syria.HL11
|
||||||
|
-- * AIRBASE.Syria.HL12
|
||||||
|
-- * AIRBASE.Syria.HL13
|
||||||
|
-- * AIRBASE.Syria.HMed00
|
||||||
|
-- * AIRBASE.Syria.HMed01
|
||||||
|
-- * AIRBASE.Syria.HMed02
|
||||||
|
-- * AIRBASE.Syria.HMed03
|
||||||
|
-- * AIRBASE.Syria.HMed04
|
||||||
|
-- * AIRBASE.Syria.HMed05
|
||||||
|
-- * AIRBASE.Syria.HMed06
|
||||||
|
-- * AIRBASE.Syria.HMed07
|
||||||
|
-- * AIRBASE.Syria.HMed08
|
||||||
|
-- * AIRBASE.Syria.HMed09
|
||||||
|
-- * AIRBASE.Syria.HMed10
|
||||||
|
-- * AIRBASE.Syria.HMed11
|
||||||
|
-- * AIRBASE.Syria.HMed12
|
||||||
|
-- * AIRBASE.Syria.HMed13
|
||||||
|
-- * AIRBASE.Syria.HMed14
|
||||||
|
-- * AIRBASE.Syria.HMed15
|
||||||
|
-- * AIRBASE.Syria.HMed16
|
||||||
|
-- * AIRBASE.Syria.HMed17
|
||||||
|
-- * AIRBASE.Syria.HMed18
|
||||||
|
-- * AIRBASE.Syria.HMed19
|
||||||
|
-- * AIRBASE.Syria.HMed20
|
||||||
|
-- * AIRBASE.Syria.HMed21
|
||||||
|
-- * AIRBASE.Syria.HMed22
|
||||||
|
-- * AIRBASE.Syria.HMed23
|
||||||
|
-- * AIRBASE.Syria.HMed24
|
||||||
|
-- * AIRBASE.Syria.HMed25
|
||||||
|
-- * AIRBASE.Syria.HMed26
|
||||||
|
-- * AIRBASE.Syria.HMed27
|
||||||
|
-- * AIRBASE.Syria.HMed28
|
||||||
|
-- * AIRBASE.Syria.HMed29
|
||||||
|
-- * AIRBASE.Syria.HMed30
|
||||||
|
-- * AIRBASE.Syria.HOil01
|
||||||
|
-- * AIRBASE.Syria.HOil02
|
||||||
|
-- * AIRBASE.Syria.HOil03
|
||||||
|
-- * AIRBASE.Syria.HOil04
|
||||||
|
-- * AIRBASE.Syria.HOil05
|
||||||
|
-- * AIRBASE.Syria.HOil06
|
||||||
|
-- * AIRBASE.Syria.HS02
|
||||||
|
-- * AIRBASE.Syria.HS03
|
||||||
|
-- * AIRBASE.Syria.HS04
|
||||||
|
-- * AIRBASE.Syria.HS05
|
||||||
|
-- * AIRBASE.Syria.HS06
|
||||||
|
-- * AIRBASE.Syria.HS07
|
||||||
|
-- * AIRBASE.Syria.HS08
|
||||||
|
-- * AIRBASE.Syria.HS09
|
||||||
|
-- * AIRBASE.Syria.HS10
|
||||||
|
-- * AIRBASE.Syria.HS11
|
||||||
|
-- * AIRBASE.Syria.HS12
|
||||||
|
-- * AIRBASE.Syria.HS13
|
||||||
|
-- * AIRBASE.Syria.HS14
|
||||||
|
-- * AIRBASE.Syria.HS15
|
||||||
|
-- * AIRBASE.Syria.HS16
|
||||||
|
-- * AIRBASE.Syria.HS17
|
||||||
|
-- * AIRBASE.Syria.HS18
|
||||||
|
-- * AIRBASE.Syria.HS19
|
||||||
|
-- * AIRBASE.Syria.HS20
|
||||||
|
-- * AIRBASE.Syria.HS21
|
||||||
|
-- * AIRBASE.Syria.HS22
|
||||||
|
-- * AIRBASE.Syria.HS23
|
||||||
|
-- * AIRBASE.Syria.HS24
|
||||||
|
-- * AIRBASE.Syria.HS25
|
||||||
|
-- * AIRBASE.Syria.HS26
|
||||||
|
-- * AIRBASE.Syria.HS27
|
||||||
|
-- * AIRBASE.Syria.HS28
|
||||||
|
-- * AIRBASE.Syria.HS29
|
||||||
|
-- * AIRBASE.Syria.HS30
|
||||||
|
-- * AIRBASE.Syria.HS31
|
||||||
|
-- * AIRBASE.Syria.HS32
|
||||||
|
-- * AIRBASE.Syria.HS33
|
||||||
|
-- * AIRBASE.Syria.HS34
|
||||||
|
-- * AIRBASE.Syria.HS35
|
||||||
|
-- * AIRBASE.Syria.HS36
|
||||||
|
-- * AIRBASE.Syria.HS37
|
||||||
|
-- * AIRBASE.Syria.HS38
|
||||||
|
-- * AIRBASE.Syria.HS39
|
||||||
|
-- * AIRBASE.Syria.HS40
|
||||||
|
-- * AIRBASE.Syria.HS41
|
||||||
|
-- * AIRBASE.Syria.HS42
|
||||||
|
-- * AIRBASE.Syria.HStad01
|
||||||
|
-- * AIRBASE.Syria.HStad02
|
||||||
|
-- * AIRBASE.Syria.HStad03
|
||||||
|
-- * AIRBASE.Syria.HStad04
|
||||||
|
-- * AIRBASE.Syria.HStad05
|
||||||
|
-- * AIRBASE.Syria.HStad06
|
||||||
|
-- * AIRBASE.Syria.HT01
|
||||||
|
-- * AIRBASE.Syria.HT02
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_01
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_02
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_03
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_04
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_05
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_06
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_07
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_08
|
||||||
|
-- * AIRBASE.Syria.H_med_orig_09
|
||||||
|
-- * AIRBASE.Syria.Haifa
|
||||||
|
-- * AIRBASE.Syria.Hama
|
||||||
|
-- * AIRBASE.Syria.Hatay
|
||||||
|
-- * AIRBASE.Syria.Hatzerim
|
||||||
|
-- * AIRBASE.Syria.Hatzor
|
||||||
|
-- * AIRBASE.Syria.Herzliya
|
||||||
|
-- * AIRBASE.Syria.Incirlik
|
||||||
|
-- * AIRBASE.Syria.Jirah
|
||||||
|
-- * AIRBASE.Syria.Kahramanmaras
|
||||||
|
-- * AIRBASE.Syria.Kedem
|
||||||
|
-- * AIRBASE.Syria.Khalkhalah
|
||||||
|
-- * AIRBASE.Syria.Kharab_Ishk
|
||||||
|
-- * AIRBASE.Syria.King_Abdullah_II
|
||||||
|
-- * AIRBASE.Syria.King_Hussein_Air_College
|
||||||
|
-- * AIRBASE.Syria.Kingsfield
|
||||||
|
-- * AIRBASE.Syria.Kiryat_Shmona
|
||||||
|
-- * AIRBASE.Syria.Konya
|
||||||
|
-- * AIRBASE.Syria.Kuweires
|
||||||
|
-- * AIRBASE.Syria.Lakatamia
|
||||||
|
-- * AIRBASE.Syria.Larnaca
|
||||||
|
-- * AIRBASE.Syria.Marj_Ruhayyil
|
||||||
|
-- * AIRBASE.Syria.Marj_as_Sultan_North
|
||||||
|
-- * AIRBASE.Syria.Marj_as_Sultan_South
|
||||||
|
-- * AIRBASE.Syria.Marka
|
||||||
|
-- * AIRBASE.Syria.Megiddo
|
||||||
|
-- * AIRBASE.Syria.Mezzeh
|
||||||
|
-- * AIRBASE.Syria.Minakh
|
||||||
|
-- * AIRBASE.Syria.Muwaffaq_Salti
|
||||||
|
-- * AIRBASE.Syria.Naqoura
|
||||||
|
-- * AIRBASE.Syria.Nevatim
|
||||||
|
-- * AIRBASE.Syria.Nicosia
|
||||||
|
-- * AIRBASE.Syria.Palmachim
|
||||||
|
-- * AIRBASE.Syria.Palmyra
|
||||||
|
-- * AIRBASE.Syria.Paphos
|
||||||
|
-- * AIRBASE.Syria.Pinarbashi
|
||||||
|
-- * AIRBASE.Syria.Prince_Hassan
|
||||||
|
-- * AIRBASE.Syria.Qabr_as_Sitt
|
||||||
|
-- * AIRBASE.Syria.Ramat_David
|
||||||
|
-- * AIRBASE.Syria.Rayak
|
||||||
|
-- * AIRBASE.Syria.Rene_Mouawad
|
||||||
|
-- * AIRBASE.Syria.Rosh_Pina
|
||||||
|
-- * AIRBASE.Syria.Ruwayshid
|
||||||
|
-- * AIRBASE.Syria.Sanliurfa
|
||||||
|
-- * AIRBASE.Syria.Sanliurfa_Heliport
|
||||||
|
-- * AIRBASE.Syria.Sayqal
|
||||||
|
-- * AIRBASE.Syria.Shayrat
|
||||||
|
-- * AIRBASE.Syria.T2
|
||||||
|
-- * AIRBASE.Syria.T3
|
||||||
|
-- * AIRBASE.Syria.Tabqa
|
||||||
|
-- * AIRBASE.Syria.Taftanaz
|
||||||
|
-- * AIRBASE.Syria.Tal_Siman
|
||||||
|
-- * AIRBASE.Syria.Tel_Nof
|
||||||
|
-- * AIRBASE.Syria.Teyman
|
||||||
|
-- * AIRBASE.Syria.Tha_lah
|
||||||
|
-- * AIRBASE.Syria.Tiyas
|
||||||
|
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
||||||
|
-- * AIRBASE.Syria.Zarqa
|
||||||
|
|
||||||
--
|
--
|
||||||
-- @field Syria
|
-- @field Syria
|
||||||
AIRBASE.Syria = {
|
AIRBASE.Syria = {
|
||||||
["Abu_al_Duhur"] = "Abu al-Duhur",
|
["Abu_al_Duhur"] = "Abu al-Duhur",
|
||||||
["Adana_Sakirpasa"] = "Adana Sakirpasa",
|
["Adana_Sakirpasa"] = "Adana Sakirpasa",
|
||||||
|
["Adiyaman"] = "Adiyaman",
|
||||||
["Akrotiri"] = "Akrotiri",
|
["Akrotiri"] = "Akrotiri",
|
||||||
["Al_Qusayr"] = "Al Qusayr",
|
|
||||||
["Al_Dumayr"] = "Al-Dumayr",
|
["Al_Dumayr"] = "Al-Dumayr",
|
||||||
|
["Al_Qusayr"] = "Al Qusayr",
|
||||||
["Aleppo"] = "Aleppo",
|
["Aleppo"] = "Aleppo",
|
||||||
["An_Nasiriyah"] = "An Nasiriyah",
|
["An_Nasiriyah"] = "An Nasiriyah",
|
||||||
["At_Tanf"] = "At Tanf",
|
["At_Tanf"] = "At Tanf",
|
||||||
["Bassel_Al_Assad"] = "Bassel Al-Assad",
|
["Bassel_Al_Assad"] = "Bassel Al-Assad",
|
||||||
["Beirut_Rafic_Hariri"] = "Beirut-Rafic Hariri",
|
["Beirut_Rafic_Hariri"] = "Beirut-Rafic Hariri",
|
||||||
["Ben_Gurion"] = "Ben Gurion",
|
["Ben_Gurion"] = "Ben Gurion",
|
||||||
|
["Chukurova"] = "Chukurova",
|
||||||
["Damascus"] = "Damascus",
|
["Damascus"] = "Damascus",
|
||||||
["Deir_ez_Zor"] = "Deir ez-Zor",
|
["Deir_ez_Zor"] = "Deir ez-Zor",
|
||||||
|
["Diyarbakir"] = "Diyarbakir",
|
||||||
["Ercan"] = "Ercan",
|
["Ercan"] = "Ercan",
|
||||||
["Eyn_Shemer"] = "Eyn Shemer",
|
["Eyn_Shemer"] = "Eyn Shemer",
|
||||||
["Gaziantep"] = "Gaziantep",
|
["Gaziantep"] = "Gaziantep",
|
||||||
["Gazipasa"] = "Gazipasa",
|
["Gazipasa"] = "Gazipasa",
|
||||||
["Gecitkale"] = "Gecitkale",
|
["Gecitkale"] = "Gecitkale",
|
||||||
["H"] = "H",
|
["Gulechoba"] = "Gulechoba",
|
||||||
["H3"] = "H3",
|
["H3"] = "H3",
|
||||||
["H3_Northwest"] = "H3 Northwest",
|
["H3_Northwest"] = "H3 Northwest",
|
||||||
["H3_Southwest"] = "H3 Southwest",
|
["H3_Southwest"] = "H3 Southwest",
|
||||||
["H4"] = "H4",
|
["H4"] = "H4",
|
||||||
|
["H4_Emergency"] = "H4 Emergency",
|
||||||
|
["HC01"] = "HC01",
|
||||||
|
["HC02"] = "HC02",
|
||||||
|
["HC03"] = "HC03",
|
||||||
|
["HC04"] = "HC04",
|
||||||
|
["HC05"] = "HC05",
|
||||||
|
["HC06"] = "HC06",
|
||||||
|
["HI01"] = "HI01",
|
||||||
|
["HI02"] = "HI02",
|
||||||
|
["HI03"] = "HI03",
|
||||||
|
["HI05"] = "HI05",
|
||||||
|
["HI06"] = "HI06",
|
||||||
|
["HI07"] = "HI07",
|
||||||
|
["HI08"] = "HI08",
|
||||||
|
["HI09"] = "HI09",
|
||||||
|
["HI11"] = "HI11",
|
||||||
|
["HI12"] = "HI12",
|
||||||
|
["HI13"] = "HI13",
|
||||||
|
["HI14"] = "HI14",
|
||||||
|
["HI15"] = "HI15",
|
||||||
|
["HI16"] = "HI16",
|
||||||
|
["HI17"] = "HI17",
|
||||||
|
["HI18"] = "HI18",
|
||||||
|
["HI20"] = "HI20",
|
||||||
|
["HI21"] = "HI21",
|
||||||
|
["HI22"] = "HI22",
|
||||||
|
["HI23"] = "HI23",
|
||||||
|
["HI24"] = "HI24",
|
||||||
|
["HI25"] = "HI25",
|
||||||
|
["HI26"] = "HI26",
|
||||||
|
["HJ01"] = "HJ01",
|
||||||
|
["HJ02"] = "HJ02",
|
||||||
|
["HJ03"] = "HJ03",
|
||||||
|
["HJ04"] = "HJ04",
|
||||||
|
["HL01"] = "HL01",
|
||||||
|
["HL02"] = "HL02",
|
||||||
|
["HL03"] = "HL03",
|
||||||
|
["HL04"] = "HL04",
|
||||||
|
["HL05"] = "HL05",
|
||||||
|
["HL06"] = "HL06",
|
||||||
|
["HL07"] = "HL07",
|
||||||
|
["HL08"] = "HL08",
|
||||||
|
["HL09"] = "HL09",
|
||||||
|
["HL10"] = "HL10",
|
||||||
|
["HL11"] = "HL11",
|
||||||
|
["HL12"] = "HL12",
|
||||||
|
["HL13"] = "HL13",
|
||||||
|
["HMed00"] = "HMed00",
|
||||||
|
["HMed01"] = "HMed01",
|
||||||
|
["HMed02"] = "HMed02",
|
||||||
|
["HMed03"] = "HMed03",
|
||||||
|
["HMed04"] = "HMed04",
|
||||||
|
["HMed05"] = "HMed05",
|
||||||
|
["HMed06"] = "HMed06",
|
||||||
|
["HMed07"] = "HMed07",
|
||||||
|
["HMed08"] = "HMed08",
|
||||||
|
["HMed09"] = "HMed09",
|
||||||
|
["HMed10"] = "HMed10",
|
||||||
|
["HMed11"] = "HMed11",
|
||||||
|
["HMed12"] = "HMed12",
|
||||||
|
["HMed13"] = "HMed13",
|
||||||
|
["HMed14"] = "HMed14",
|
||||||
|
["HMed15"] = "HMed15",
|
||||||
|
["HMed16"] = "HMed16",
|
||||||
|
["HMed17"] = "HMed17",
|
||||||
|
["HMed18"] = "HMed18",
|
||||||
|
["HMed19"] = "HMed19",
|
||||||
|
["HMed20"] = "HMed20",
|
||||||
|
["HMed21"] = "HMed21",
|
||||||
|
["HMed22"] = "HMed22",
|
||||||
|
["HMed23"] = "HMed23",
|
||||||
|
["HMed24"] = "HMed24",
|
||||||
|
["HMed25"] = "HMed25",
|
||||||
|
["HMed26"] = "HMed26",
|
||||||
|
["HMed27"] = "HMed27",
|
||||||
|
["HMed28"] = "HMed28",
|
||||||
|
["HMed29"] = "HMed29",
|
||||||
|
["HMed30"] = "HMed30",
|
||||||
|
["HOil01"] = "HOil01",
|
||||||
|
["HOil02"] = "HOil02",
|
||||||
|
["HOil03"] = "HOil03",
|
||||||
|
["HOil04"] = "HOil04",
|
||||||
|
["HOil05"] = "HOil05",
|
||||||
|
["HOil06"] = "HOil06",
|
||||||
|
["HS02"] = "HS02",
|
||||||
|
["HS03"] = "HS03",
|
||||||
|
["HS04"] = "HS04",
|
||||||
|
["HS05"] = "HS05",
|
||||||
|
["HS06"] = "HS06",
|
||||||
|
["HS07"] = "HS07",
|
||||||
|
["HS08"] = "HS08",
|
||||||
|
["HS09"] = "HS09",
|
||||||
|
["HS10"] = "HS10",
|
||||||
|
["HS11"] = "HS11",
|
||||||
|
["HS12"] = "HS12",
|
||||||
|
["HS13"] = "HS13",
|
||||||
|
["HS14"] = "HS14",
|
||||||
|
["HS15"] = "HS15",
|
||||||
|
["HS16"] = "HS16",
|
||||||
|
["HS17"] = "HS17",
|
||||||
|
["HS18"] = "HS18",
|
||||||
|
["HS19"] = "HS19",
|
||||||
|
["HS20"] = "HS20",
|
||||||
|
["HS21"] = "HS21",
|
||||||
|
["HS22"] = "HS22",
|
||||||
|
["HS23"] = "HS23",
|
||||||
|
["HS24"] = "HS24",
|
||||||
|
["HS25"] = "HS25",
|
||||||
|
["HS26"] = "HS26",
|
||||||
|
["HS27"] = "HS27",
|
||||||
|
["HS28"] = "HS28",
|
||||||
|
["HS29"] = "HS29",
|
||||||
|
["HS30"] = "HS30",
|
||||||
|
["HS31"] = "HS31",
|
||||||
|
["HS32"] = "HS32",
|
||||||
|
["HS33"] = "HS33",
|
||||||
|
["HS34"] = "HS34",
|
||||||
|
["HS35"] = "HS35",
|
||||||
|
["HS36"] = "HS36",
|
||||||
|
["HS37"] = "HS37",
|
||||||
|
["HS38"] = "HS38",
|
||||||
|
["HS39"] = "HS39",
|
||||||
|
["HS40"] = "HS40",
|
||||||
|
["HS41"] = "HS41",
|
||||||
|
["HS42"] = "HS42",
|
||||||
|
["HStad01"] = "HStad01",
|
||||||
|
["HStad02"] = "HStad02",
|
||||||
|
["HStad03"] = "HStad03",
|
||||||
|
["HStad04"] = "HStad04",
|
||||||
|
["HStad05"] = "HStad05",
|
||||||
|
["HStad06"] = "HStad06",
|
||||||
|
["HT01"] = "HT01",
|
||||||
|
["HT02"] = "HT02",
|
||||||
|
["H_med_orig_01"] = "H_med_orig_01",
|
||||||
|
["H_med_orig_02"] = "H_med_orig_02",
|
||||||
|
["H_med_orig_03"] = "H_med_orig_03",
|
||||||
|
["H_med_orig_04"] = "H_med_orig_04",
|
||||||
|
["H_med_orig_05"] = "H_med_orig_05",
|
||||||
|
["H_med_orig_06"] = "H_med_orig_06",
|
||||||
|
["H_med_orig_07"] = "H_med_orig_07",
|
||||||
|
["H_med_orig_08"] = "H_med_orig_08",
|
||||||
|
["H_med_orig_09"] = "H_med_orig_09",
|
||||||
["Haifa"] = "Haifa",
|
["Haifa"] = "Haifa",
|
||||||
["Hama"] = "Hama",
|
["Hama"] = "Hama",
|
||||||
["Hatay"] = "Hatay",
|
["Hatay"] = "Hatay",
|
||||||
|
["Hatzerim"] = "Hatzerim",
|
||||||
["Hatzor"] = "Hatzor",
|
["Hatzor"] = "Hatzor",
|
||||||
["Herzliya"] = "Herzliya",
|
["Herzliya"] = "Herzliya",
|
||||||
["Incirlik"] = "Incirlik",
|
["Incirlik"] = "Incirlik",
|
||||||
["Jirah"] = "Jirah",
|
["Jirah"] = "Jirah",
|
||||||
|
["Kahramanmaras"] = "Kahramanmaras",
|
||||||
|
["Kedem"] = "Kedem",
|
||||||
["Khalkhalah"] = "Khalkhalah",
|
["Khalkhalah"] = "Khalkhalah",
|
||||||
["Kharab_Ishk"] = "Kharab Ishk",
|
["Kharab_Ishk"] = "Kharab Ishk",
|
||||||
["King_Abdullah_II"] = "King Abdullah II",
|
["King_Abdullah_II"] = "King Abdullah II",
|
||||||
["King_Hussein_Air_College"] = "King Hussein Air College",
|
["King_Hussein_Air_College"] = "King Hussein Air College",
|
||||||
["Kingsfield"] = "Kingsfield",
|
["Kingsfield"] = "Kingsfield",
|
||||||
["Kiryat_Shmona"] = "Kiryat Shmona",
|
["Kiryat_Shmona"] = "Kiryat Shmona",
|
||||||
|
["Konya"] = "Konya",
|
||||||
["Kuweires"] = "Kuweires",
|
["Kuweires"] = "Kuweires",
|
||||||
["Lakatamia"] = "Lakatamia",
|
["Lakatamia"] = "Lakatamia",
|
||||||
["Larnaca"] = "Larnaca",
|
["Larnaca"] = "Larnaca",
|
||||||
@@ -579,6 +885,7 @@ AIRBASE.Syria = {
|
|||||||
["Minakh"] = "Minakh",
|
["Minakh"] = "Minakh",
|
||||||
["Muwaffaq_Salti"] = "Muwaffaq Salti",
|
["Muwaffaq_Salti"] = "Muwaffaq Salti",
|
||||||
["Naqoura"] = "Naqoura",
|
["Naqoura"] = "Naqoura",
|
||||||
|
["Nevatim"] = "Nevatim",
|
||||||
["Nicosia"] = "Nicosia",
|
["Nicosia"] = "Nicosia",
|
||||||
["Palmachim"] = "Palmachim",
|
["Palmachim"] = "Palmachim",
|
||||||
["Palmyra"] = "Palmyra",
|
["Palmyra"] = "Palmyra",
|
||||||
@@ -592,15 +899,20 @@ AIRBASE.Syria = {
|
|||||||
["Rosh_Pina"] = "Rosh Pina",
|
["Rosh_Pina"] = "Rosh Pina",
|
||||||
["Ruwayshid"] = "Ruwayshid",
|
["Ruwayshid"] = "Ruwayshid",
|
||||||
["Sanliurfa"] = "Sanliurfa",
|
["Sanliurfa"] = "Sanliurfa",
|
||||||
|
["Sanliurfa_Heliport"] = "Sanliurfa Heliport",
|
||||||
["Sayqal"] = "Sayqal",
|
["Sayqal"] = "Sayqal",
|
||||||
["Shayrat"] = "Shayrat",
|
["Shayrat"] = "Shayrat",
|
||||||
|
["T2"] = "T2",
|
||||||
|
["T3"] = "T3",
|
||||||
["Tabqa"] = "Tabqa",
|
["Tabqa"] = "Tabqa",
|
||||||
["Taftanaz"] = "Taftanaz",
|
["Taftanaz"] = "Taftanaz",
|
||||||
["Tal_Siman"] = "Tal Siman",
|
["Tal_Siman"] = "Tal Siman",
|
||||||
["Tel_Nof"] = "Tel Nof",
|
["Tel_Nof"] = "Tel Nof",
|
||||||
|
["Teyman"] = "Teyman",
|
||||||
["Tha_lah"] = "Tha'lah",
|
["Tha_lah"] = "Tha'lah",
|
||||||
["Tiyas"] = "Tiyas",
|
["Tiyas"] = "Tiyas",
|
||||||
["Wujah_Al_Hajar"] = "Wujah Al Hajar",
|
["Wujah_Al_Hajar"] = "Wujah Al Hajar",
|
||||||
|
["Zarqa"] = "Zarqa",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Mariana Islands map
|
--- Airbases of the Mariana Islands map
|
||||||
@@ -1673,6 +1985,7 @@ elseif self.category==Airbase.Category.SHIP then
|
|||||||
self.category=Airbase.Category.HELIPAD
|
self.category=Airbase.Category.HELIPAD
|
||||||
_DATABASE:AddStatic(AirbaseName)
|
_DATABASE:AddStatic(AirbaseName)
|
||||||
end
|
end
|
||||||
|
if self:GetTypeName() == "Zell" then self.isZell = true end
|
||||||
else
|
else
|
||||||
self:E("ERROR: Unknown airbase category!")
|
self:E("ERROR: Unknown airbase category!")
|
||||||
end
|
end
|
||||||
@@ -2122,6 +2435,13 @@ function AIRBASE:IsShip()
|
|||||||
return self.isShip
|
return self.isShip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if airbase is a ZELL booster.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @return #boolean If true, airbase is a ZELL booster.
|
||||||
|
function AIRBASE:IsZell()
|
||||||
|
return self.isZell
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Parking
|
-- Parking
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ Ops/OpsZone.lua
|
|||||||
Ops/ArmyGroup.lua
|
Ops/ArmyGroup.lua
|
||||||
Ops/OpsTransport.lua
|
Ops/OpsTransport.lua
|
||||||
Ops/Target.lua
|
Ops/Target.lua
|
||||||
|
Ops/TARS.lua
|
||||||
|
|
||||||
Sound/UserSound.lua
|
Sound/UserSound.lua
|
||||||
Sound/SoundOutput.lua
|
Sound/SoundOutput.lua
|
||||||
|
|||||||
Reference in New Issue
Block a user