Merge branch 'FF/Ops' into FF/OpsDev

This commit is contained in:
Frank 2021-09-29 09:57:52 +02:00
commit 3e30d15405
27 changed files with 3771 additions and 1816 deletions

View File

@ -3477,7 +3477,7 @@ do -- AI_A2A_DISPATCHER
if Squadron.Language == "EN" and self.SetSendPlayerMessages then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " wheels up.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " wheels up.", DefenderGroup )
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " колеÑ<EFBFBD>а вверх.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " колёса вверх.", DefenderGroup )
end end
--Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit --Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit
Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit
@ -3498,7 +3498,7 @@ do -- AI_A2A_DISPATCHER
if Squadron.Language == "EN" and self.SetSendPlayerMessages then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", intercepting bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", intercepting bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", перехват Ñ<>амолеÑов в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", перехватывая боги в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
elseif Squadron.Language == "DE" and self.SetSendPlayerMessages then elseif Squadron.Language == "DE" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", Eindringlinge abfangen bei" .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", Eindringlinge abfangen bei" .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
end end
@ -3520,7 +3520,7 @@ do -- AI_A2A_DISPATCHER
if Squadron.Language == "EN" and self.SetSendPlayerMessages then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", захватывающие Ñ<>амолеÑÑ Ð² " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", задействуя боги в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
end end
end end
self:GetParent( Fsm ).onafterEngage( self, DefenderGroup, From, Event, To, AttackSetUnit ) self:GetParent( Fsm ).onafterEngage( self, DefenderGroup, From, Event, To, AttackSetUnit )
@ -3538,7 +3538,7 @@ do -- AI_A2A_DISPATCHER
if Squadron.Language == "EN" and self.SetSendPlayerMessages then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup )
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", возвращаÑ<EFBFBD>Ñ<EFBFBD>ÑŒ на базу.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", возвращение на базу.", DefenderGroup )
end end
end end
Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
@ -3569,7 +3569,7 @@ do -- AI_A2A_DISPATCHER
if Squadron.Language == "EN" and self.SetSendPlayerMessages then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " landing at base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " landing at base.", DefenderGroup )
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", захватывающие Ñ<>амолеÑÑ Ð² поÑ<C2BE>адка на базу.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", посадка на базу.", DefenderGroup )
end end
if Action and Action == "Destroy" then if Action and Action == "Destroy" then

View File

@ -47,19 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
self:SetStartState( "Unloaded" ) self:SetStartState( "Unloaded" )
self:AddTransition( "Unloaded", "Pickup", "*" ) -- Board
self:AddTransition( "Loaded", "Deploy", "*" ) self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Reload", "*" )
self:AddTransition( "*", "Board", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
self:AddTransition( "*", "Load", "Boarding" ) -- Unload
self:AddTransition( "Boarding", "Board", "Boarding" ) self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "Loaded", "Board", "Loaded" ) self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "Boarding", "Loaded", "Boarding" ) self:AddTransition( "*", "Unboard", "*" )
self:AddTransition( "Boarding", "PickedUp", "Loaded" ) self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
--- Pickup Handler OnBefore for AI_CARGO --- Pickup Handler OnBefore for AI_CARGO
-- @function [parent=#AI_CARGO] OnBeforePickup -- @function [parent=#AI_CARGO] OnBeforePickup
@ -393,7 +395,7 @@ end
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone ) function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } ) self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
if Carrier and Carrier:IsAlive() and From == "Boarding" then if Carrier and Carrier:IsAlive() then
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } ) self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
self:__Board( -10, Cargo, CarrierUnit, PickupZone ) self:__Board( -10, Cargo, CarrierUnit, PickupZone )
@ -509,7 +511,7 @@ end
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend ) function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } ) self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
if Carrier and Carrier:IsAlive() and From == "Unboarding" then if Carrier and Carrier:IsAlive() then
if not Cargo:IsUnLoaded() then if not Cargo:IsUnLoaded() then
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend ) self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
return return
@ -580,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
end end
end end

View File

@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
self:AddTransition( "*", "Guard", "Unloaded" ) self:AddTransition( "*", "Guard", "Unloaded" )
self:AddTransition( "*", "Home", "*" ) self:AddTransition( "*", "Home", "*" )
self:AddTransition( "*", "Reload", "Boarding" ) self:AddTransition( "*", "Reload", "Boarding" )
self:AddTransition( "*", "Deployed", "*" )
self:AddTransition( "*", "PickedUp", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" ) self:AddTransition( "*", "Destroyed", "Destroyed" )
self:SetCombatRadius( CombatRadius ) self:SetCombatRadius( CombatRadius )

View File

@ -174,8 +174,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZone
self:SetPickupSpeed( 350, 150 ) self:SetPickupSpeed( 350, 150 )
self:SetDeploySpeed( 350, 150 ) self:SetDeploySpeed( 350, 150 )
self:SetPickupRadius( 0, 0 ) self:SetPickupRadius( 40, 12 )
self:SetDeployRadius( 0, 0 ) self:SetDeployRadius( 40, 12 )
self:SetPickupHeight( 500, 200 ) self:SetPickupHeight( 500, 200 )
self:SetDeployHeight( 500, 200 ) self:SetDeployHeight( 500, 200 )
@ -186,6 +186,9 @@ end
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet ) function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet )
return AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) local dispatcher = AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
dispatcher:SetLandingSpeedAndHeight(27, 6)
return dispatcher
end end

View File

@ -64,20 +64,24 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 ) self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
self:SetStartState( "Unloaded" ) self:SetStartState( "Unloaded" )
-- Boarding
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
self:AddTransition( "Unloaded", "Pickup", "*" ) -- Unboarding
self:AddTransition( "Loaded", "Deploy", "*" ) self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Loaded", "Loaded" ) self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "Unboarding", "Pickup", "Unloaded" ) self:AddTransition( "*", "Orbit" , "*" )
self:AddTransition( "Unloaded", "Unboard", "Unloaded" ) self:AddTransition( "*", "Destroyed", "*" )
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" ) self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "PickedUp", "*" ) self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" ) -- RTB
self:AddTransition( "*", "Home" , "*" ) self:AddTransition( "*", "Home" , "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER --- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup -- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
@ -207,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self:SetCarrier( Helicopter ) self:SetCarrier( Helicopter )
self.landingspeed = 15 -- kph
self.landingheight = 5.5 -- meter
return self return self
end end
@ -255,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
return self return self
end end
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
-- @param #AI_CARGO_HELICOPTER self
-- @param #number speed Landing speed in kph(!), e.g. 15
-- @param #number height Landing height in meters(!), e.g. 5.5
-- @return #AI_CARGO_HELICOPTER self
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
--
-- BASE:TraceOn()
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
--
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
-- Adjust if necessary.
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
local _speed = speed or 15
local _height = height or 5.5
self.landingheight = _height
self.landingspeed = _speed
return self
end
--- @param #AI_CARGO_HELICOPTER self --- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter -- @param Wrapper.Group#GROUP Helicopter
@ -271,13 +297,13 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
-- 1 - When the helo lands normally on the ground. -- 1 - When the helo lands normally on the ground.
-- 2 - when the helo is hit and goes RTB or even when it is destroyed. -- 2 - when the helo is hit and goes RTB or even when it is destroyed.
-- For point 2, this is an issue, the infantry may not unload in this case! -- For point 2, this is an issue, the infantry may not unload in this case!
-- So we check if the helo is on the ground, and velocity< 5. -- So we check if the helo is on the ground, and velocity< 15.
-- Only then the infantry can unload (and load too, for consistency)! -- Only then the infantry can unload (and load too, for consistency)!
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } ) self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
if self.RoutePickup == true then if self.RoutePickup == true then
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
--self:Load( Helicopter:GetPointVec2() ) --self:Load( Helicopter:GetPointVec2() )
self:Load( self.PickupZone ) self:Load( self.PickupZone )
self.RoutePickup = false self.RoutePickup = false
@ -285,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
end end
if self.RouteDeploy == true then if self.RouteDeploy == true then
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
self:Unload( self.DeployZone ) self:Unload( self.DeployZone )
self.RouteDeploy = false self.RouteDeploy = false
end end

View File

@ -300,23 +300,23 @@ do -- Zones
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
local ZoneName = ZoneData.name local ZoneName = ZoneData.name
-- Color -- Color
local color=ZoneData.color or {1, 0, 0, 0.15} local color=ZoneData.color or {1, 0, 0, 0.15}
-- Create new Zone -- Create new Zone
local Zone=nil --Core.Zone#ZONE_BASE local Zone=nil --Core.Zone#ZONE_BASE
if ZoneData.type==0 then if ZoneData.type==0 then
--- ---
-- Circular zone -- Circular zone
--- ---
self:I(string.format("Register ZONE: %s (Circular)", ZoneName)) self:I(string.format("Register ZONE: %s (Circular)", ZoneName))
Zone=ZONE:New(ZoneName) Zone=ZONE:New(ZoneName)
else else
--- ---
@ -324,51 +324,51 @@ do -- Zones
--- ---
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName)) self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies) Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies)
--for i,vec2 in pairs(ZoneData.verticies) do --for i,vec2 in pairs(ZoneData.verticies) do
-- local coord=COORDINATE:NewFromVec2(vec2) -- local coord=COORDINATE:NewFromVec2(vec2)
-- coord:MarkToAll(string.format("%s Point %d", ZoneName, i)) -- coord:MarkToAll(string.format("%s Point %d", ZoneName, i))
--end --end
end end
if Zone then if Zone then
-- Store color of zone. -- Store color of zone.
Zone.Color=color Zone.Color=color
-- Store in DB. -- Store in DB.
self.ZONENAMES[ZoneName] = ZoneName self.ZONENAMES[ZoneName] = ZoneName
-- Add zone. -- Add zone.
self:AddZone(ZoneName, Zone) self:AddZone(ZoneName, Zone)
end end
end end
-- Polygon zones defined by late activated groups. -- Polygon zones defined by late activated groups.
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
if ZoneGroupName:match("#ZONE_POLYGON") then if ZoneGroupName:match("#ZONE_POLYGON") then
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON") local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)") local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
local ZoneName = ZoneName1 .. ( ZoneName2 or "" ) local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
-- Debug output -- Debug output
self:I(string.format("Register ZONE: %s (Polygon)", ZoneName)) self:I(string.format("Register ZONE: %s (Polygon)", ZoneName))
-- Create a new polygon zone. -- Create a new polygon zone.
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup ) local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
-- Set color. -- Set color.
Zone_Polygon:SetColor({1, 0, 0}, 0.15) Zone_Polygon:SetColor({1, 0, 0}, 0.15)
-- Store name in DB. -- Store name in DB.
self.ZONENAMES[ZoneName] = ZoneName self.ZONENAMES[ZoneName] = ZoneName
-- Add zone to DB. -- Add zone to DB.
self:AddZone( ZoneName, Zone_Polygon ) self:AddZone( ZoneName, Zone_Polygon )
end end

View File

@ -1881,7 +1881,7 @@ do -- COORDINATE
--- Big smoke and fire at the coordinate. --- Big smoke and fire at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (0=small smoke and fire, 1=medium smoke and fire, 2=large smoke and fire, 3=huge smoke and fire, 4=small smoke, 5=medium smoke, 6=large smoke, 7=huge smoke). -- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke).
-- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5. -- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5.
function COORDINATE:BigSmokeAndFire( preset, density ) function COORDINATE:BigSmokeAndFire( preset, density )
self:F2( { preset=preset, density=density } ) self:F2( { preset=preset, density=density } )

View File

@ -4042,7 +4042,25 @@ do -- SET_CLIENT
return self return self
end end
--- Iterate the SET_CLIENT and count alive units.
-- @param #SET_CLIENT self
-- @return #number count
function SET_CLIENT:CountAlive()
local Set = self:GetSet()
local CountU = 0
for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP
if UnitData and UnitData:IsAlive() then
CountU = CountU + 1
end
end
return CountU
end
--- ---
-- @param #SET_CLIENT self -- @param #SET_CLIENT self
-- @param Wrapper.Client#CLIENT MClient -- @param Wrapper.Client#CLIENT MClient
@ -4790,7 +4808,7 @@ do -- SET_AIRBASE
local airbaseName, airbase=self:FindInDatabase(EventData) local airbaseName, airbase=self:FindInDatabase(EventData)
if airbase and airbase:IsShip() or airbase:IsHelipad() then if airbase and (airbase:IsShip() or airbase:IsHelipad()) then
self:RemoveAirbasesByName(airbaseName) self:RemoveAirbasesByName(airbaseName)
end end

View File

@ -138,24 +138,24 @@ SPAWNSTATIC = {
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID) function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName) local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
if TemplateStatic then if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplateName self.SpawnTemplatePrefix = SpawnTemplateName
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1]) self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
self.CountryID = SpawnCountryID or CountryID self.CountryID = SpawnCountryID or CountryID
self.CategoryID = CategoryID self.CategoryID = CategoryID
self.CoalitionID = CoalitionID self.CoalitionID = CoalitionID
self.SpawnIndex = 0 self.SpawnIndex = 0
else else
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" ) error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
end end
self:SetEventPriority( 5 ) self:SetEventPriority( 5 )
return self return self
end end
--- Creates the main object to spawn a @{Static} given a template table. --- Creates the main object to spawn a @{Static} given a template table.
@ -422,7 +422,11 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
end end
if self.InitCargo~=nil then if self.InitCargo~=nil then
Template.isCargo=self.InitCargo Template.canCargo=self.InitCargo
end
if self.InitCargoMass~=nil then
Template.mass=self.InitCargoMass
end end
if self.InitLinkUnit then if self.InitLinkUnit then

View File

@ -183,12 +183,12 @@ function ZONE_BASE:IsCoordinateInZone( Coordinate )
return InZone return InZone
end end
--- Returns if a PointVec2 is within the zone. --- Returns if a PointVec2 is within the zone. (Name is misleading, actually takes a #COORDINATE)
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test. -- @param Core.Point#COORDINATE PointVec2 The coordinate to test.
-- @return #boolean true if the PointVec2 is within the zone. -- @return #boolean true if the PointVec2 is within the zone.
function ZONE_BASE:IsPointVec2InZone( PointVec2 ) function ZONE_BASE:IsPointVec2InZone( Coordinate )
local InZone = self:IsVec2InZone( PointVec2:GetVec2() ) local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
return InZone return InZone
end end
@ -515,8 +515,8 @@ end
-- --
-- @field #ZONE_RADIUS -- @field #ZONE_RADIUS
ZONE_RADIUS = { ZONE_RADIUS = {
ClassName="ZONE_RADIUS", ClassName="ZONE_RADIUS",
} }
--- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius. --- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
@ -527,15 +527,15 @@ ZONE_RADIUS = {
function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
-- Inherit ZONE_BASE. -- Inherit ZONE_BASE.
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
self:F( { ZoneName, Vec2, Radius } ) self:F( { ZoneName, Vec2, Radius } )
self.Radius = Radius self.Radius = Radius
self.Vec2 = Vec2 self.Vec2 = Vec2
--self.Coordinate=COORDINATE:NewFromVec2(Vec2) --self.Coordinate=COORDINATE:NewFromVec2(Vec2)
return self return self
end end
--- Update zone from a 2D vector. --- Update zone from a 2D vector.
@ -763,11 +763,11 @@ end
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return DCS#Vec2 The location of the zone. -- @return DCS#Vec2 The location of the zone.
function ZONE_RADIUS:GetVec2() function ZONE_RADIUS:GetVec2()
self:F2( self.ZoneName ) self:F2( self.ZoneName )
self:T2( { self.Vec2 } ) self:T2( { self.Vec2 } )
return self.Vec2 return self.Vec2
end end
--- Sets the @{DCS#Vec2} of the zone. --- Sets the @{DCS#Vec2} of the zone.

View File

@ -1707,8 +1707,8 @@ end
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned. --- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
-- @param #FOX self -- @param #FOX self
-- @param DCS#Weapon weapon The weapon. -- @param DCS#Weapon weapon The weapon.
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD> -- @return #number Notching heading right, i.e. missile heading +90°.
-- @return #number Notching heading left, i.e. missile heading -90<EFBFBD>. -- @return #number Notching heading left, i.e. missile heading -90°.
function FOX:_GetNotchingHeadings(weapon) function FOX:_GetNotchingHeadings(weapon)
if weapon then if weapon then

View File

@ -1,31 +1,31 @@
--- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses --- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses
-- --
-- === -- ===
-- --
-- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System -- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy -- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy
-- Leverage evasiveness from SEAD -- Leverage evasiveness from SEAD
-- Leverage attack range setup added by DCS in 11/20 -- Leverage attack range setup added by DCS in 11/20
-- --
-- === -- ===
-- --
-- ## Missions: -- ## Missions:
-- --
-- ### [MANTIS - Modular, Automatic and Network capable Targeting and Interception System](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MTS%20-%20Mantis/MTS-010%20-%20Basic%20Mantis%20Demo) -- ### [MANTIS - Modular, Automatic and Network capable Targeting and Interception System](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MTS%20-%20Mantis/MTS-010%20-%20Basic%20Mantis%20Demo)
-- --
-- === -- ===
-- --
-- ### Author : **applevangelist ** -- ### Author : **applevangelist **
-- --
-- @module Functional.Mantis -- @module Functional.Mantis
-- @image Functional.Mantis.jpg -- @image Functional.Mantis.jpg
-- Date: July 2021 -- Date: July 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **MANTIS** class, extends #Core.Base#BASE --- **MANTIS** class, extends Core.Base#BASE
-- @type MANTIS -- @type MANTIS
-- @field #string Classname -- @field #string ClassName
-- @field #string name Name of this Mantis -- @field #string name Name of this Mantis
-- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites -- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites
-- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP -- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP
@ -59,10 +59,10 @@
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- *The worst thing that can happen to a good cause is, not to be skillfully attacked, but to be ineptly defended.* - Frédéric Bastiat --- *The worst thing that can happen to a good cause is, not to be skillfully attacked, but to be ineptly defended.* - Frédéric Bastiat
-- --
-- Simple Class for a more intelligent Air Defense System -- Simple Class for a more intelligent Air Defense System
-- --
-- #MANTIS -- #MANTIS
-- Moose derived Modular, Automatic and Network capable Targeting and Interception System. -- Moose derived Modular, Automatic and Network capable Targeting and Interception System.
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy. -- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy.
@ -72,62 +72,62 @@
-- Set up your SAM sites in the mission editor. Name the groups with common prefix like "Red SAM". -- Set up your SAM sites in the mission editor. Name the groups with common prefix like "Red SAM".
-- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc. -- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
-- [optional] Set up your HQ. Can be any group, e.g. a command vehicle. -- [optional] Set up your HQ. Can be any group, e.g. a command vehicle.
-- --
-- # 1. Basic tactical considerations when setting up your SAM sites -- # 1. Basic tactical considerations when setting up your SAM sites
-- --
-- ## 1.1 Radar systems and AWACS -- ## 1.1 Radar systems and AWACS
-- --
-- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track. -- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track.
-- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system -- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system
-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units -- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do. -- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
-- --
-- ## 1.2 SAM sites -- ## 1.2 SAM sites
-- --
-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like -- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like
-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral, -- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral,
-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage. -- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage.
-- --
-- ## 1.3 Typical problems -- ## 1.3 Typical problems
-- --
-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are -- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are
-- --
-- * bad placement of radar units, -- * bad placement of radar units,
-- * overestimation how far units can "see" and -- * overestimation how far units can "see" and
-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing. -- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing.
-- --
-- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles -- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles
-- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**. -- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**.
-- --
-- # 2. Start up your MANTIS with a basic setting -- # 2. Start up your MANTIS with a basic setting
-- --
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
-- `myredmantis:Start()` -- `myredmantis:Start()`
-- --
-- [optional] Use -- [optional] Use
-- --
-- * `MANTIS:SetEWRGrouping(radius)` -- * `MANTIS:SetEWRGrouping(radius)`
-- * `MANTIS:SetEWRRange(radius)` -- * `MANTIS:SetEWRRange(radius)`
-- * `MANTIS:SetSAMRadius(radius)` -- * `MANTIS:SetSAMRadius(radius)`
-- * `MANTIS:SetDetectInterval(interval)` -- * `MANTIS:SetDetectInterval(interval)`
-- * `MANTIS:SetAutoRelocate(hq, ewr)` -- * `MANTIS:SetAutoRelocate(hq, ewr)`
-- --
-- before starting #MANTIS to fine-tune your setup. -- before starting #MANTIS to fine-tune your setup.
-- --
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: -- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
-- --
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mybluemantis:Start()` -- `mybluemantis:Start()`
-- --
-- # 3. Default settings -- # 3. Default settings
-- --
-- By default, the following settings are active: -- By default, the following settings are active:
-- --
-- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM" -- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM"
-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit -- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit
-- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)` -- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)`
-- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)` -- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)`
-- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)` -- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)`
-- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)` -- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)`
-- * engagerange = 85 (percent) - SAMs will only fire if flights are inside of a 85% radius of their max firerange - `MANTIS:SetSAMRange(range)` -- * engagerange = 85 (percent) - SAMs will only fire if flights are inside of a 85% radius of their max firerange - `MANTIS:SetSAMRange(range)`
-- * dynamic = false - Group filtering is set to once, i.e. newly added groups will not be part of the setup by default - `MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic)` -- * dynamic = false - Group filtering is set to once, i.e. newly added groups will not be part of the setup by default - `MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic)`
@ -135,28 +135,28 @@
-- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)` -- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)`
-- --
-- # 4. Advanced Mode -- # 4. Advanced Mode
-- --
-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option. -- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
-- --
-- E.g. `mymantis:SetAdvancedMode( true, 90 )` -- E.g. `mymantis:SetAdvancedMode( true, 90 )`
-- --
-- Use this option if you want to make use of or allow advanced SEAD tactics. -- Use this option if you want to make use of or allow advanced SEAD tactics.
-- --
-- # 5. Integrate SHORAD -- # 5. Integrate SHORAD
-- --
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in -- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so: -- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
-- --
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()` -- `local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()`
-- `myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")` -- `myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")`
-- `-- now set up MANTIS` -- `-- now set up MANTIS`
-- `mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- `mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mymantis:AddShorad(myshorad,720)` -- `mymantis:AddShorad(myshorad,720)`
-- `mymantis:Start()` -- `mymantis:Start()`
-- --
-- and (optionally) remove the link later on with -- and (optionally) remove the link later on with
-- --
-- `mymantis:RemoveShorad()` -- `mymantis:RemoveShorad()`
-- --
-- @field #MANTIS -- @field #MANTIS
MANTIS = { MANTIS = {
@ -197,6 +197,7 @@ MANTIS = {
SamStateTracker = {}, SamStateTracker = {},
DLink = false, DLink = false,
DLTimeStamp = 0, DLTimeStamp = 0,
Padding = 10,
} }
--- Advanced state enumerator --- Advanced state enumerator
@ -221,30 +222,31 @@ do
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral" --@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral"
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional) --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
--@param #string awacs Group name of your Awacs (optional) --@param #string awacs Group name of your Awacs (optional)
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN --@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional)
--@param #number Padding For #SEAD - Extra number of seconds to add to radar switch-back-on time (optional)
--@return #MANTIS self --@return #MANTIS self
--@usage Start up your MANTIS with a basic setting --@usage Start up your MANTIS with a basic setting
-- --
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
-- `myredmantis:Start()` -- `myredmantis:Start()`
-- --
-- [optional] Use -- [optional] Use
-- --
-- * `MANTIS:SetEWRGrouping(radius)` -- * `MANTIS:SetEWRGrouping(radius)`
-- * `MANTIS:SetEWRRange(radius)` -- * `MANTIS:SetEWRRange(radius)`
-- * `MANTIS:SetSAMRadius(radius)` -- * `MANTIS:SetSAMRadius(radius)`
-- * `MANTIS:SetDetectInterval(interval)` -- * `MANTIS:SetDetectInterval(interval)`
-- * `MANTIS:SetAutoRelocate(hq, ewr)` -- * `MANTIS:SetAutoRelocate(hq, ewr)`
-- --
-- before starting #MANTIS to fine-tune your setup. -- before starting #MANTIS to fine-tune your setup.
-- --
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: -- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
-- --
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mybluemantis:Start()` -- `mybluemantis:Start()`
-- --
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff) function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff, Padding)
-- DONE: Create some user functions for these -- DONE: Create some user functions for these
-- DONE: Make HQ useful -- DONE: Make HQ useful
-- DONE: Set SAMs to auto if EWR dies -- DONE: Set SAMs to auto if EWR dies
@ -267,7 +269,7 @@ do
self.autorelocateunits = { HQ = false, EWR = false} self.autorelocateunits = { HQ = false, EWR = false}
self.advanced = false self.advanced = false
self.adv_ratio = 100 self.adv_ratio = 100
self.adv_state = 0 self.adv_state = 0
self.verbose = false self.verbose = false
self.Adv_EWR_Group = nil self.Adv_EWR_Group = nil
self.AWACS_Prefix = awacs or nil self.AWACS_Prefix = awacs or nil
@ -281,27 +283,28 @@ do
self.state2flag = false self.state2flag = false
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
self.DLink = false self.DLink = false
self.Padding = Padding or 10
if EmOnOff then if EmOnOff then
if EmOnOff == false then if EmOnOff == false then
self.UseEmOnOff = false self.UseEmOnOff = false
else else
self.UseEmOnOff = true self.UseEmOnOff = true
end end
end end
if type(awacs) == "string" then if type(awacs) == "string" then
self.advAwacs = true self.advAwacs = true
else else
self.advAwacs = false self.advAwacs = false
end end
-- Inherit everything from BASE class. -- Inherit everything from BASE class.
local self = BASE:Inherit(self, FSM:New()) -- #MANTIS local self = BASE:Inherit(self, FSM:New()) -- #MANTIS
-- Set the string id for output to DCS.log file. -- Set the string id for output to DCS.log file.
self.lid=string.format("MANTIS %s | ", self.name) self.lid=string.format("MANTIS %s | ", self.name)
-- Debug trace. -- Debug trace.
if self.debug then if self.debug then
BASE:TraceOnOff(true) BASE:TraceOnOff(true)
@ -309,7 +312,7 @@ do
--BASE:TraceClass("SEAD") --BASE:TraceClass("SEAD")
BASE:TraceLevel(1) BASE:TraceLevel(1)
end end
if self.dynamic then if self.dynamic then
-- Set SAM SET_GROUP -- Set SAM SET_GROUP
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart() self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
@ -321,18 +324,18 @@ do
-- Set EWR SET_GROUP -- Set EWR SET_GROUP
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce() self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce()
end end
-- set up CC -- set up CC
if self.HQ_Template_CC then if self.HQ_Template_CC then
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC) self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
end end
-- @field #string version -- @field #string version
self.version="0.6.2" self.version="0.6.2"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions --- --- FSM Functions ---
-- Start State. -- Start State.
self:SetStartState("Stopped") self:SetStartState("Stopped")
@ -346,11 +349,11 @@ do
self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change. self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change.
self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD. self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
------------------------ ------------------------
--- Triggers the FSM event "Start". Starts the MANTIS. Initializes parameters and starts event handlers. --- Triggers the FSM event "Start". Starts the MANTIS. Initializes parameters and starts event handlers.
-- @function [parent=#MANTIS] Start -- @function [parent=#MANTIS] Start
-- @param #MANTIS self -- @param #MANTIS self
@ -376,7 +379,7 @@ do
-- @function [parent=#MANTIS] __Status -- @function [parent=#MANTIS] __Status
-- @param #MANTIS self -- @param #MANTIS self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- On After "Relocating" event. HQ and/or EWR moved. --- On After "Relocating" event. HQ and/or EWR moved.
-- @function [parent=#MANTIS] OnAfterRelocating -- @function [parent=#MANTIS] OnAfterRelocating
-- @param #MANTIS self -- @param #MANTIS self
@ -384,7 +387,7 @@ do
-- @param #string Event The Event -- @param #string Event The Event
-- @param #string To The To State -- @param #string To The To State
-- @return #MANTIS self -- @return #MANTIS self
--- On After "GreenState" event. A SAM group was switched to GREEN alert. --- On After "GreenState" event. A SAM group was switched to GREEN alert.
-- @function [parent=#MANTIS] OnAfterGreenState -- @function [parent=#MANTIS] OnAfterGreenState
-- @param #MANTIS self -- @param #MANTIS self
@ -393,7 +396,7 @@ do
-- @param #string To The To State -- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed -- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
-- @return #MANTIS self -- @return #MANTIS self
--- On After "RedState" event. A SAM group was switched to RED alert. --- On After "RedState" event. A SAM group was switched to RED alert.
-- @function [parent=#MANTIS] OnAfterRedState -- @function [parent=#MANTIS] OnAfterRedState
-- @param #MANTIS self -- @param #MANTIS self
@ -402,7 +405,7 @@ do
-- @param #string To The To State -- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed -- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
-- @return #MANTIS self -- @return #MANTIS self
--- On After "AdvStateChange" event. Advanced state changed, influencing detection speed. --- On After "AdvStateChange" event. Advanced state changed, influencing detection speed.
-- @function [parent=#MANTIS] OnAfterAdvStateChange -- @function [parent=#MANTIS] OnAfterAdvStateChange
-- @param #MANTIS self -- @param #MANTIS self
@ -413,7 +416,7 @@ do
-- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red -- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red
-- @param #number Interval Calculated detection interval based on state and advanced feature setting -- @param #number Interval Calculated detection interval based on state and advanced feature setting
-- @return #MANTIS self -- @return #MANTIS self
--- On After "ShoradActivated" event. Mantis has activated a SHORAD. --- On After "ShoradActivated" event. Mantis has activated a SHORAD.
-- @function [parent=#MANTIS] OnAfterShoradActivated -- @function [parent=#MANTIS] OnAfterShoradActivated
-- @param #MANTIS self -- @param #MANTIS self
@ -424,21 +427,21 @@ do
-- @param #number Radius Radius around the named group to find SHORAD groups -- @param #number Radius Radius around the named group to find SHORAD groups
-- @param #number Ontime Seconds the SHORAD will stay active -- @param #number Ontime Seconds the SHORAD will stay active
return self return self
end end
----------------------------------------------------------------------- -----------------------------------------------------------------------
-- MANTIS helper functions -- MANTIS helper functions
----------------------------------------------------------------------- -----------------------------------------------------------------------
--- [Internal] Function to get the self.SAM_Table --- [Internal] Function to get the self.SAM_Table
-- @param #MANTIS self -- @param #MANTIS self
-- @return #table table -- @return #table table
function MANTIS:_GetSAMTable() function MANTIS:_GetSAMTable()
self:T(self.lid .. "GetSAMTable") self:T(self.lid .. "GetSAMTable")
return self.SAM_Table return self.SAM_Table
end end
--- [Internal] Function to set the self.SAM_Table --- [Internal] Function to set the self.SAM_Table
-- @param #MANTIS self -- @param #MANTIS self
-- @return #MANTIS self -- @return #MANTIS self
@ -447,7 +450,7 @@ do
self.SAM_Table = table self.SAM_Table = table
return self return self
end end
--- Function to set the grouping radius of the detection in meters --- Function to set the grouping radius of the detection in meters
-- @param #MANTIS self -- @param #MANTIS self
-- @param #number radius Radius upon which detected objects will be grouped -- @param #number radius Radius upon which detected objects will be grouped
@ -467,17 +470,17 @@ do
self.acceptrange = radius self.acceptrange = radius
return self return self
end end
--- Function to set switch-on/off zone for the SAM sites in meters --- Function to set switch-on/off zone for the SAM sites in meters
-- @param #MANTIS self -- @param #MANTIS self
-- @param #number radius Radius of the firing zone -- @param #number radius Radius of the firing zone
function MANTIS:SetSAMRadius(radius) function MANTIS:SetSAMRadius(radius)
self:T(self.lid .. "SetSAMRadius") self:T(self.lid .. "SetSAMRadius")
local radius = radius or 25000 local radius = radius or 25000
self.checkradius = radius self.checkradius = radius
return self return self
end end
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75 --- Function to set SAM firing engage range, 0-100 percent, e.g. 75
-- @param #MANTIS self -- @param #MANTIS self
-- @param #number range Percent of the max fire range -- @param #number range Percent of the max fire range
@ -490,7 +493,7 @@ do
self.engagerange = range self.engagerange = range
return self return self
end end
--- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night --- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night
-- @param #MANTIS self -- @param #MANTIS self
-- @param #number range Percent of the max fire range -- @param #number range Percent of the max fire range
@ -505,7 +508,7 @@ do
self.mysead.EngagementRange = range self.mysead.EngagementRange = range
return self return self
end end
--- Function to set switch-on/off the debug state --- Function to set switch-on/off the debug state
-- @param #MANTIS self -- @param #MANTIS self
-- @param #boolean onoff Set true to switch on -- @param #boolean onoff Set true to switch on
@ -523,7 +526,7 @@ do
end end
return self return self
end end
--- Function to get the HQ object for further use --- Function to get the HQ object for further use
-- @param #MANTIS self -- @param #MANTIS self
-- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist -- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist
@ -532,10 +535,10 @@ do
if self.HQ_CC then if self.HQ_CC then
return self.HQ_CC return self.HQ_CC
else else
return nil return nil
end end
end end
--- Function to set separate AWACS detection instance --- Function to set separate AWACS detection instance
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string prefix Name of the AWACS group in the mission editor -- @param #string prefix Name of the AWACS group in the mission editor
@ -559,7 +562,7 @@ do
self.awacsrange = range self.awacsrange = range
return self return self
end end
--- Function to set the HQ object for further use --- Function to set the HQ object for further use
-- @param #MANTIS self -- @param #MANTIS self
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ -- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
@ -577,7 +580,7 @@ do
end end
return self return self
end end
--- Function to set the detection interval --- Function to set the detection interval
-- @param #MANTIS self -- @param #MANTIS self
-- @param #number interval The interval in seconds -- @param #number interval The interval in seconds
@ -586,8 +589,8 @@ do
local interval = interval or 30 local interval = interval or 30
self.detectinterval = interval self.detectinterval = interval
return self return self
end end
--- Function to set Advanded Mode --- Function to set Advanded Mode
-- @param #MANTIS self -- @param #MANTIS self
-- @param #boolean onoff If true, will activate Advanced Mode -- @param #boolean onoff If true, will activate Advanced Mode
@ -596,7 +599,7 @@ do
-- E.g. `mymantis:SetAdvancedMode(true, 90)` -- E.g. `mymantis:SetAdvancedMode(true, 90)`
function MANTIS:SetAdvancedMode(onoff, ratio) function MANTIS:SetAdvancedMode(onoff, ratio)
self:T(self.lid .. "SetAdvancedMode") self:T(self.lid .. "SetAdvancedMode")
--self.T({onoff, ratio}) --self:T({onoff, ratio})
local onoff = onoff or false local onoff = onoff or false
local ratio = ratio or 100 local ratio = ratio or 100
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
@ -612,7 +615,7 @@ do
end end
return self return self
end end
--- Set using Emissions on/off instead of changing alarm state --- Set using Emissions on/off instead of changing alarm state
-- @param #MANTIS self -- @param #MANTIS self
-- @param #boolean switch Decide if we are changing alarm state or Emission state -- @param #boolean switch Decide if we are changing alarm state or Emission state
@ -621,7 +624,7 @@ do
self.UseEmOnOff = switch or false self.UseEmOnOff = switch or false
return self return self
end end
--- Set using an #INTEL_DLINK object instead of #DETECTION --- Set using an #INTEL_DLINK object instead of #DETECTION
-- @param #MANTIS self -- @param #MANTIS self
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used. -- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
@ -632,7 +635,7 @@ do
self.DLTimeStamp = timer.getAbsTime() self.DLTimeStamp = timer.getAbsTime()
return self return self
end end
--- [Internal] Function to check if HQ is alive --- [Internal] Function to check if HQ is alive
-- @param #MANTIS self -- @param #MANTIS self
-- @return #boolean True if HQ is alive, else false -- @return #boolean True if HQ is alive, else false
@ -647,15 +650,15 @@ do
local hqgrp = GROUP:FindByName(hq) local hqgrp = GROUP:FindByName(hq)
if hqgrp then if hqgrp then
if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive
--self.T(self.lid.." HQ is alive!") --self:T(self.lid.." HQ is alive!")
return true return true
else else
--self.T(self.lid.." HQ is dead!") --self:T(self.lid.." HQ is dead!")
return false return false
end end
end end
end end
return self return self
end end
--- [Internal] Function to check if EWR is (at least partially) alive --- [Internal] Function to check if EWR is (at least partially) alive
@ -664,7 +667,7 @@ do
function MANTIS:_CheckEWRState() function MANTIS:_CheckEWRState()
self:T(self.lid .. "CheckEWRState") self:T(self.lid .. "CheckEWRState")
local text = self.lid.." Checking EWR State" local text = self.lid.." Checking EWR State"
--self.T(text) --self:T(text)
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end if self.verbose then self:I(text) end
-- start check -- start check
@ -680,14 +683,14 @@ do
end end
end end
end end
--self.T(self.lid..string.format(" No of EWR alive is %d", nalive)) --self:T(self.lid..string.format(" No of EWR alive is %d", nalive))
if nalive > 0 then if nalive > 0 then
return true return true
else else
return false return false
end end
end end
return self return self
end end
--- [Internal] Function to determine state of the advanced mode --- [Internal] Function to determine state of the advanced mode
@ -717,30 +720,30 @@ do
local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78 local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78
if self.debug or self.verbose then if self.debug or self.verbose then
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval) local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
--self.T(text) --self:T(text)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end if self.verbose then self:I(text) end
end end
return newinterval, currstate return newinterval, currstate
end end
--- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS! --- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS!
-- @param #MANTIS self -- @param #MANTIS self
-- @param #boolean hq If true, will relocate HQ object -- @param #boolean hq If true, will relocate HQ object
-- @param #boolean ewr If true, will relocate EWR objects -- @param #boolean ewr If true, will relocate EWR objects
function MANTIS:SetAutoRelocate(hq, ewr) function MANTIS:SetAutoRelocate(hq, ewr)
self:T(self.lid .. "SetAutoRelocate") self:T(self.lid .. "SetAutoRelocate")
--self.T({hq, ewr}) --self:T({hq, ewr})
local hqrel = hq or false local hqrel = hq or false
local ewrel = ewr or false local ewrel = ewr or false
if hqrel or ewrel then if hqrel or ewrel then
self.autorelocate = true self.autorelocate = true
self.autorelocateunits = { HQ = hqrel, EWR = ewrel } self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
--self.T({self.autorelocate, self.autorelocateunits}) --self:T({self.autorelocate, self.autorelocateunits})
end end
return self return self
end end
--- [Internal] Function to execute the relocation --- [Internal] Function to execute the relocation
-- @param #MANTIS self -- @param #MANTIS self
function MANTIS:_RelocateGroups() function MANTIS:_RelocateGroups()
@ -753,7 +756,7 @@ do
local HQGroup = self.HQ_CC local HQGroup = self.HQ_CC
if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists
local _hqgrp = self.HQ_CC local _hqgrp = self.HQ_CC
--self.T(self.lid.." Relocating HQ") --self:T(self.lid.." Relocating HQ")
local text = self.lid.." Relocating HQ" local text = self.lid.." Relocating HQ"
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll() --local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true) _hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
@ -766,7 +769,7 @@ do
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
for _,_grp in pairs (EWR_Grps) do for _,_grp in pairs (EWR_Grps) do
if _grp:IsAlive() and _grp:IsGround() then if _grp:IsAlive() and _grp:IsGround() then
--self.T(self.lid.." Relocating EWR ".._grp:GetName()) --self:T(self.lid.." Relocating EWR ".._grp:GetName())
local text = self.lid.." Relocating EWR ".._grp:GetName() local text = self.lid.." Relocating EWR ".._grp:GetName()
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end if self.verbose then self:I(text) end
@ -777,7 +780,7 @@ do
end end
return self return self
end end
--- [Internal] Function to check if any object is in the given SAM zone --- [Internal] Function to check if any object is in the given SAM zone
-- @param #MANTIS self -- @param #MANTIS self
-- @param #table dectset Table of coordinates of detected items -- @param #table dectset Table of coordinates of detected items
@ -793,12 +796,12 @@ do
local coord = _coord -- get current coord to check local coord = _coord -- get current coord to check
-- output for cross-check -- output for cross-check
local targetdistance = samcoordinate:DistanceFromPointVec2(coord) local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
if self.verbose or self.debug then if self.verbose or self.debug then
local dectstring = coord:ToStringLLDMS() local dectstring = coord:ToStringLLDMS()
local samstring = samcoordinate:ToStringLLDMS() local samstring = samcoordinate:ToStringLLDMS()
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring) local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug) local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
self:I(self.lid..text) self:I(self.lid..text)
end end
-- end output to cross-check -- end output to cross-check
if targetdistance <= radius then if targetdistance <= radius then
@ -813,20 +816,20 @@ do
-- @return Functional.Detection #DETECTION_AREAS The running detection set -- @return Functional.Detection #DETECTION_AREAS The running detection set
function MANTIS:StartDetection() function MANTIS:StartDetection()
self:T(self.lid.."Starting Detection") self:T(self.lid.."Starting Detection")
-- start detection -- start detection
local groupset = self.EWR_Group local groupset = self.EWR_Group
local grouping = self.grouping or 5000 local grouping = self.grouping or 5000
local acceptrange = self.acceptrange or 80000 local acceptrange = self.acceptrange or 80000
local interval = self.detectinterval or 60 local interval = self.detectinterval or 60
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object
local MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones local MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones
MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER }) MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
MANTISdetection:SetAcceptRange(acceptrange) MANTISdetection:SetAcceptRange(acceptrange)
MANTISdetection:SetRefreshTimeInterval(interval) MANTISdetection:SetRefreshTimeInterval(interval)
MANTISdetection:Start() MANTISdetection:Start()
function MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem) function MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
--BASE:I( { From, Event, To, DetectedItem }) --BASE:I( { From, Event, To, DetectedItem })
local debug = false local debug = false
@ -835,30 +838,30 @@ do
local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS() local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS()
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
end end
end end
return MANTISdetection return MANTISdetection
end end
--- [Internal] Function to start the detection via AWACS if defined as separate --- [Internal] Function to start the detection via AWACS if defined as separate
-- @param #MANTIS self -- @param #MANTIS self
-- @return Functional.Detection #DETECTION_AREAS The running detection set -- @return Functional.Detection #DETECTION_AREAS The running detection set
function MANTIS:StartAwacsDetection() function MANTIS:StartAwacsDetection()
self:T(self.lid.."Starting Awacs Detection") self:T(self.lid.."Starting Awacs Detection")
-- start detection -- start detection
local group = self.AWACS_Prefix local group = self.AWACS_Prefix
local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart() local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart()
local grouping = self.grouping or 5000 local grouping = self.grouping or 5000
--local acceptrange = self.acceptrange or 80000 --local acceptrange = self.acceptrange or 80000
local interval = self.detectinterval or 60 local interval = self.detectinterval or 60
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object
local MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones local MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones
MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER }) MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
MANTISAwacs:SetAcceptRange(self.awacsrange) --250km MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
MANTISAwacs:SetRefreshTimeInterval(interval) MANTISAwacs:SetRefreshTimeInterval(interval)
MANTISAwacs:Start() MANTISAwacs:Start()
function MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem) function MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
--BASE:I( { From, Event, To, DetectedItem }) --BASE:I( { From, Event, To, DetectedItem })
local debug = false local debug = false
@ -867,10 +870,10 @@ do
local text = "Awacs Detection at "..Coordinate:ToStringLLDMS() local text = "Awacs Detection at "..Coordinate:ToStringLLDMS()
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
end end
end end
return MANTISAwacs return MANTISAwacs
end end
--- [Internal] Function to set the SAM start state --- [Internal] Function to set the SAM start state
-- @param #MANTIS self -- @param #MANTIS self
-- @return #MANTIS self -- @return #MANTIS self
@ -904,12 +907,12 @@ do
end end
self.SAM_Table = SAM_Tbl self.SAM_Table = SAM_Tbl
-- make SAMs evasive -- make SAMs evasive
local mysead = SEAD:New( SEAD_Grps ) local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
mysead:SetEngagementRange(engagerange) mysead:SetEngagementRange(engagerange)
self.mysead = mysead self.mysead = mysead
return self return self
end end
--- [Internal] Function to update SAM table and SEAD state --- [Internal] Function to update SAM table and SEAD state
-- @param #MANTIS self -- @param #MANTIS self
-- @return #MANTIS self -- @return #MANTIS self
@ -941,7 +944,7 @@ do
end end
return self return self
end end
--- Function to link up #MANTIS with a #SHORAD installation --- Function to link up #MANTIS with a #SHORAD installation
-- @param #MANTIS self -- @param #MANTIS self
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object -- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
@ -958,7 +961,7 @@ do
end end
return self return self
end end
--- Function to unlink #MANTIS from a #SHORAD installation --- Function to unlink #MANTIS from a #SHORAD installation
-- @param #MANTIS self -- @param #MANTIS self
function MANTIS:RemoveShorad() function MANTIS:RemoveShorad()
@ -966,11 +969,11 @@ do
self.ShoradLink = false self.ShoradLink = false
return self return self
end end
----------------------------------------------------------------------- -----------------------------------------------------------------------
-- MANTIS main functions -- MANTIS main functions
----------------------------------------------------------------------- -----------------------------------------------------------------------
--- [Internal] Check detection function --- [Internal] Check detection function
-- @param #MANTIS self -- @param #MANTIS self
-- @param Functional.Detection#DETECTION_AREAS detection Detection object -- @param Functional.Detection#DETECTION_AREAS detection Detection object
@ -1021,7 +1024,7 @@ do
if self.verbose then self:I(self.lid..text) end if self.verbose then self:I(self.lid..text) end
end end
end --end alive end --end alive
else else
if samgroup:IsAlive() then if samgroup:IsAlive() then
-- switch off SAM -- switch off SAM
if self.UseEmOnOff then if self.UseEmOnOff then
@ -1032,7 +1035,7 @@ do
self:__GreenState(1,samgroup) self:__GreenState(1,samgroup)
self.SamStateTracker[name] = "GREEN" self.SamStateTracker[name] = "GREEN"
end end
if self.debug or self.verbose then if self.debug or self.verbose then
local text = string.format("SAM %s switched to alarm state GREEN!", name) local text = string.format("SAM %s switched to alarm state GREEN!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(self.lid..text) end if self.verbose then self:I(self.lid..text) end
@ -1041,8 +1044,8 @@ do
end --end check end --end check
end --for for loop end --for for loop
return self return self
end end
--- [Internal] Relocation relay function --- [Internal] Relocation relay function
-- @param #MANTIS self -- @param #MANTIS self
-- @return #MANTIS self -- @return #MANTIS self
@ -1051,7 +1054,7 @@ do
self:_RelocateGroups() self:_RelocateGroups()
return self return self
end end
--- [Internal] Check advanced state --- [Internal] Check advanced state
-- @param #MANTIS self -- @param #MANTIS self
-- @return #MANTIS self -- @return #MANTIS self
@ -1086,7 +1089,7 @@ do
end -- end newstate vs oldstate end -- end newstate vs oldstate
return self return self
end end
--- [Internal] Check DLink state --- [Internal] Check DLink state
-- @param #MANTIS self -- @param #MANTIS self
-- @return #MANTIS self -- @return #MANTIS self
@ -1100,7 +1103,7 @@ do
self:I(self.lid .. "Intel DLink not running - switching back to single detection!") self:I(self.lid .. "Intel DLink not running - switching back to single detection!")
end end
end end
--- [Internal] Function to set start state --- [Internal] Function to set start state
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1117,10 +1120,10 @@ do
if self.advAwacs then if self.advAwacs then
self.AWACS_Detection = self:StartAwacsDetection() self.AWACS_Detection = self:StartAwacsDetection()
end end
self:__Status(-math.random(1,10)) self:__Status(-math.random(1,10))
return self return self
end end
--- [Internal] Before status function for MANTIS --- [Internal] Before status function for MANTIS
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1138,7 +1141,7 @@ do
if self.advAwacs and not self.state2flag then if self.advAwacs and not self.state2flag then
self:_Check(self.AWACS_Detection) self:_Check(self.AWACS_Detection)
end end
-- relocate HQ and EWR -- relocate HQ and EWR
if self.autorelocate then if self.autorelocate then
local relointerval = self.relointerval local relointerval = self.relointerval
@ -1146,26 +1149,26 @@ do
local timepassed = thistime - self.TimeStamp local timepassed = thistime - self.TimeStamp
local halfintv = math.floor(timepassed / relointerval) local halfintv = math.floor(timepassed / relointerval)
--self:T({timepassed=timepassed, halfintv=halfintv}) --self:T({timepassed=timepassed, halfintv=halfintv})
if halfintv >= 1 then if halfintv >= 1 then
self.TimeStamp = timer.getAbsTime() self.TimeStamp = timer.getAbsTime()
self:_Relocate() self:_Relocate()
self:__Relocating(1) self:__Relocating(1)
end end
end end
-- advanced state check -- advanced state check
if self.advanced then if self.advanced then
self:_CheckAdvState() self:_CheckAdvState()
end end
-- check DLink state -- check DLink state
if self.DLink then if self.DLink then
self:_CheckDLinkState() self:_CheckDLinkState()
end end
return self return self
end end
@ -1188,7 +1191,7 @@ do
self:__Status(interval) self:__Status(interval)
return self return self
end end
--- [Internal] Function to stop MANTIS --- [Internal] Function to stop MANTIS
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1197,9 +1200,9 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:onafterStop(From, Event, To) function MANTIS:onafterStop(From, Event, To)
self:T({From, Event, To}) self:T({From, Event, To})
return self return self
end end
--- [Internal] Function triggered by Event Relocating --- [Internal] Function triggered by Event Relocating
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1208,9 +1211,9 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:onafterRelocating(From, Event, To) function MANTIS:onafterRelocating(From, Event, To)
self:T({From, Event, To}) self:T({From, Event, To})
return self return self
end end
--- [Internal] Function triggered by Event GreenState --- [Internal] Function triggered by Event GreenState
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1220,9 +1223,9 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:onafterGreenState(From, Event, To, Group) function MANTIS:onafterGreenState(From, Event, To, Group)
self:T({From, Event, To, Group}) self:T({From, Event, To, Group})
return self return self
end end
--- [Internal] Function triggered by Event RedState --- [Internal] Function triggered by Event RedState
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1232,9 +1235,9 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:onafterRedState(From, Event, To, Group) function MANTIS:onafterRedState(From, Event, To, Group)
self:T({From, Event, To, Group}) self:T({From, Event, To, Group})
return self return self
end end
--- [Internal] Function triggered by Event AdvStateChange --- [Internal] Function triggered by Event AdvStateChange
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1246,9 +1249,9 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval) function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval)
self:T({From, Event, To, Oldstate, Newstate, Interval}) self:T({From, Event, To, Oldstate, Newstate, Interval})
return self return self
end end
--- [Internal] Function triggered by Event ShoradActivated --- [Internal] Function triggered by Event ShoradActivated
-- @param #MANTIS self -- @param #MANTIS self
-- @param #string From The From State -- @param #string From The From State
@ -1259,7 +1262,7 @@ do
-- @param #number Ontime Seconds the SHORAD will stay active -- @param #number Ontime Seconds the SHORAD will stay active
function MANTIS:onafterShoradActivated(From, Event, To, Name, Radius, Ontime) function MANTIS:onafterShoradActivated(From, Event, To, Name, Radius, Ontime)
self:T({From, Event, To, Name, Radius, Ontime}) self:T({From, Event, To, Name, Radius, Ontime})
return self return self
end end
end end
----------------------------------------------------------------------- -----------------------------------------------------------------------

View File

@ -1,28 +1,28 @@
--- **Functional** -- Train missile defence and deflection. --- **Functional** -- Train missile defence and deflection.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. -- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <20> -- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range
-- * Provide alerts when a missile would have killed your aircraft. -- * Provide alerts when a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs. -- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options. -- * Enable / Disable and Configure the Missile Trainer using the various menu options.
-- --
-- === -- ===
-- --
-- ## Missions: -- ## Missions:
-- --
-- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MIT%20-%20Missile%20Trainer) -- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MIT%20-%20Missile%20Trainer)
-- --
-- === -- ===
-- --
-- Uses the MOOSE messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft, -- Uses the MOOSE messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft. -- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
-- --
-- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players: -- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
-- --
-- * **Messages**: Menu to configure all messages. -- * **Messages**: Menu to configure all messages.
-- * **Messages On**: Show all messages. -- * **Messages On**: Show all messages.
-- * **Messages Off**: Disable all messages. -- * **Messages Off**: Disable all messages.
@ -45,23 +45,23 @@
-- * **Range Off**: Disable range information when a missile is fired to a target. -- * **Range Off**: Disable range information when a missile is fired to a target.
-- * **Bearing On**: Shows bearing information when a missile is fired to a target. -- * **Bearing On**: Shows bearing information when a missile is fired to a target.
-- * **Bearing Off**: Disable bearing information when a missile is fired to a target. -- * **Bearing Off**: Disable bearing information when a missile is fired to a target.
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured. -- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
-- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter. -- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter.
-- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter. -- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter.
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter. -- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter. -- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
-- --
-- === -- ===
-- --
-- ### Authors: **FlightControl** -- ### Authors: **FlightControl**
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class. -- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class.
-- Danny has shared his ideas and together we made a design. -- Danny has shared his ideas and together we made a design.
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback! -- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
-- * **132nd Squadron**: Testing and optimizing the logic. -- * **132nd Squadron**: Testing and optimizing the logic.
-- --
-- === -- ===
-- --
-- @module Functional.MissileTrainer -- @module Functional.MissileTrainer
@ -76,7 +76,7 @@
--- ---
-- --
-- # Constructor: -- # Constructor:
-- --
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method: -- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
-- --
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed. -- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
@ -84,7 +84,7 @@
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those. -- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
-- --
-- # Initialization: -- # Initialization:
-- --
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods: -- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
-- --
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF. -- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
@ -97,8 +97,8 @@
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF. -- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF. -- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu. -- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
-- --
-- @field #MISSILETRAINER -- @field #MISSILETRAINER
MISSILETRAINER = { MISSILETRAINER = {
ClassName = "MISSILETRAINER", ClassName = "MISSILETRAINER",
TrackingMissiles = {}, TrackingMissiles = {},
@ -167,7 +167,7 @@ end
-- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed. -- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed.
-- @param #MISSILETRAINER self -- @param #MISSILETRAINER self
-- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player. -- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player.
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes. -- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
-- @return #MISSILETRAINER -- @return #MISSILETRAINER
function MISSILETRAINER:New( Distance, Briefing ) function MISSILETRAINER:New( Distance, Briefing )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
@ -194,8 +194,8 @@ function MISSILETRAINER:New( Distance, Briefing )
-- self:F( "ForEach:" .. Client.UnitName ) -- self:F( "ForEach:" .. Client.UnitName )
-- Client:Alive( self._Alive, self ) -- Client:Alive( self._Alive, self )
-- end -- end
-- --
self.DBClients:ForEachClient( self.DBClients:ForEachClient(
function( Client ) function( Client )
self:F( "ForEach:" .. Client.UnitName ) self:F( "ForEach:" .. Client.UnitName )
Client:Alive( self._Alive, self ) Client:Alive( self._Alive, self )
@ -207,9 +207,9 @@ function MISSILETRAINER:New( Distance, Briefing )
-- self.DB:ForEachClient( -- self.DB:ForEachClient(
-- --- @param Wrapper.Client#CLIENT Client -- --- @param Wrapper.Client#CLIENT Client
-- function( Client ) -- function( Client )
-- --
-- ... actions ... -- ... actions ...
-- --
-- end -- end
-- ) -- )
@ -225,7 +225,7 @@ function MISSILETRAINER:New( Distance, Briefing )
self.DetailsRangeOnOff = true self.DetailsRangeOnOff = true
self.DetailsBearingOnOff = true self.DetailsBearingOnOff = true
self.MenusOnOff = true self.MenusOnOff = true
self.TrackingMissiles = {} self.TrackingMissiles = {}
@ -293,7 +293,7 @@ end
--- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds. --- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
-- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update. -- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update.
-- @param #MISSILETRAINER self -- @param #MISSILETRAINER self
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency. -- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
-- @return #MISSILETRAINER self -- @return #MISSILETRAINER self
function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency ) function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency )
self:F( TrackingFrequency ) self:F( TrackingFrequency )
@ -478,30 +478,30 @@ function MISSILETRAINER:OnEventShot( EVentData )
if TrainerTargetDCSUnit then if TrainerTargetDCSUnit then
local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit ) local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit )
local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
self:T(TrainerTargetDCSUnitName ) self:T(TrainerTargetDCSUnitName )
local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName ) local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName )
if Client then if Client then
local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit ) local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit )
local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit ) local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit )
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
local Message = MESSAGE:New( local Message = MESSAGE:New(
string.format( "%s launched a %s", string.format( "%s launched a %s",
TrainerSourceUnit:GetTypeName(), TrainerSourceUnit:GetTypeName(),
TrainerWeaponName TrainerWeaponName
) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" ) ) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" )
if self.AlertsToAll then if self.AlertsToAll then
Message:ToAll() Message:ToAll()
else else
Message:ToClient( Client ) Message:ToClient( Client )
end end
end end
local ClientID = Client:GetID() local ClientID = Client:GetID()
self:T( ClientID ) self:T( ClientID )
local MissileData = {} local MissileData = {}
@ -579,52 +579,52 @@ function MISSILETRAINER:_TrackMissiles()
end end
-- ALERTS PART -- ALERTS PART
-- Loop for all Player Clients to check the alerts and deletion of missiles. -- Loop for all Player Clients to check the alerts and deletion of missiles.
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client local Client = ClientData.Client
if Client and Client:IsAlive() then if Client and Client:IsAlive() then
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
self:T3( MissileDataID ) self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
local PositionMissile = TrainerWeapon:getPosition().p local PositionMissile = TrainerWeapon:getPosition().p
local TargetVec3 = Client:GetVec3() local TargetVec3 = Client:GetVec3()
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 + local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
( PositionMissile.y - TargetVec3.y )^2 + ( PositionMissile.y - TargetVec3.y )^2 +
( PositionMissile.z - TargetVec3.z )^2 ( PositionMissile.z - TargetVec3.z )^2
) ^ 0.5 / 1000 ) ^ 0.5 / 1000
if Distance <= self.Distance then if Distance <= self.Distance then
-- Hit alert -- Hit alert
TrainerWeapon:destroy() TrainerWeapon:destroy()
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
self:T( "killed" ) self:T( "killed" )
local Message = MESSAGE:New( local Message = MESSAGE:New(
string.format( "%s launched by %s killed %s", string.format( "%s launched by %s killed %s",
TrainerWeapon:getTypeName(), TrainerWeapon:getTypeName(),
TrainerSourceUnit:GetTypeName(), TrainerSourceUnit:GetTypeName(),
TrainerTargetUnit:GetPlayerName() TrainerTargetUnit:GetPlayerName()
), 15, "Hit Alert" ) ), 15, "Hit Alert" )
if self.AlertsToAll == true then if self.AlertsToAll == true then
Message:ToAll() Message:ToAll()
else else
Message:ToClient( Client ) Message:ToClient( Client )
end end
MissileData = nil MissileData = nil
table.remove( ClientData.MissileData, MissileDataID ) table.remove( ClientData.MissileData, MissileDataID )
self:T(ClientData.MissileData) self:T(ClientData.MissileData)
@ -639,7 +639,7 @@ function MISSILETRAINER:_TrackMissiles()
TrainerWeaponTypeName, TrainerWeaponTypeName,
TrainerSourceUnit:GetTypeName() TrainerSourceUnit:GetTypeName()
), 5, "Tracking" ) ), 5, "Tracking" )
if self.AlertsToAll == true then if self.AlertsToAll == true then
Message:ToAll() Message:ToAll()
else else
@ -660,41 +660,41 @@ function MISSILETRAINER:_TrackMissiles()
if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed. if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed.
-- TRACKING PART -- TRACKING PART
-- For the current client, the missile range and bearing details are displayed To the Player Client. -- For the current client, the missile range and bearing details are displayed To the Player Client.
-- For the other clients, the missile range and bearing details are displayed To the other Player Clients. -- For the other clients, the missile range and bearing details are displayed To the other Player Clients.
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information. -- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
-- Main Player Client loop -- Main Player Client loop
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client local Client = ClientData.Client
--self:T2( { Client:GetName() } ) --self:T2( { Client:GetName() } )
ClientData.MessageToClient = "" ClientData.MessageToClient = ""
ClientData.MessageToAll = "" ClientData.MessageToAll = ""
-- Other Players Client loop -- Other Players Client loop
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
--self:T3( MissileDataID ) --self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
if ShowMessages == true then if ShowMessages == true then
local TrackingTo local TrackingTo
TrackingTo = string.format( " -> %s", TrackingTo = string.format( " -> %s",
TrainerWeaponTypeName TrainerWeaponTypeName
) )
if ClientDataID == TrackingDataID then if ClientDataID == TrackingDataID then
if ClientData.MessageToClient == "" then if ClientData.MessageToClient == "" then
ClientData.MessageToClient = "Missiles to You:\n" ClientData.MessageToClient = "Missiles to You:\n"
@ -712,7 +712,7 @@ function MISSILETRAINER:_TrackMissiles()
end end
end end
end end
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them. -- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then
local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client ) local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client )

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,9 @@
-- --
-- === -- ===
-- --
-- ## Missions: Example missions will be added later. -- ## Missions:
--
-- * [MAR - On the Range - MOOSE - SC](https://www.digitalcombatsimulator.com/en/files/3317765/) by shagrat
-- --
-- === -- ===
-- --
@ -2558,7 +2560,7 @@ function RANGE:_DisplayBombTargets(_unitname)
-- Get elevation -- Get elevation
local elevation=coord:GetLandHeight() local elevation=coord:GetLandHeight()
local eltxt=string.format("%d m", elevation) local eltxt=string.format("%d m", elevation)
if _settings:IsImperial() then if not _settings:IsMetric() then
elevation=UTILS.MetersToFeet(elevation) elevation=UTILS.MetersToFeet(elevation)
eltxt=string.format("%d ft", elevation) eltxt=string.format("%d ft", elevation)
end end
@ -2829,6 +2831,7 @@ function RANGE:_CheckInZone(_unitName)
local accur=0 local accur=0
if shots>0 then if shots>0 then
accur=_result.hits/shots*100 accur=_result.hits/shots*100
if accur > 100 then accur = 100 end
end end
-- Message text. -- Message text.

View File

@ -1,54 +1,56 @@
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible. -- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars. -- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
-- --
-- === -- ===
-- --
-- ## Missions: -- ## Missions:
-- --
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion) -- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
-- --
-- === -- ===
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: July 2021 -- Last Update: Aug 2021
-- --
-- === -- ===
-- --
-- @module Functional.Sead -- @module Functional.Sead
-- @image SEAD.JPG -- @image SEAD.JPG
--- @type SEAD ---
-- @type SEAD
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
-- --
-- # Constructor: -- # Constructor:
-- --
-- Use the @{#SEAD.New}() constructor to create a new SEAD object. -- Use the @{#SEAD.New}() constructor to create a new SEAD object.
-- --
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
-- --
-- @field #SEAD -- @field #SEAD
SEAD = { SEAD = {
ClassName = "SEAD", ClassName = "SEAD",
TargetSkill = { TargetSkill = {
Average = { Evade = 30, DelayOn = { 40, 60 } } , Average = { Evade = 30, DelayOn = { 40, 60 } } ,
Good = { Evade = 20, DelayOn = { 30, 50 } } , Good = { Evade = 20, DelayOn = { 30, 50 } } ,
High = { Evade = 15, DelayOn = { 20, 40 } } , High = { Evade = 15, DelayOn = { 20, 40 } } ,
Excellent = { Evade = 10, DelayOn = { 10, 30 } } Excellent = { Evade = 10, DelayOn = { 10, 30 } }
}, },
SEADGroupPrefixes = {}, SEADGroupPrefixes = {},
SuppressedGroups = {}, SuppressedGroups = {},
EngagementRange = 75 -- default 75% engagement range Feature Request #1355 EngagementRange = 75, -- default 75% engagement range Feature Request #1355
Padding = 10,
} }
--- Missile enumerators --- Missile enumerators
@ -59,7 +61,7 @@ SEAD = {
["AGM_122"] = "AGM_122", ["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84", ["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45", ["AGM_45"] = "AGM_45",
["ALARN"] = "ALARM", ["ALARM"] = "ALARM",
["LD-10"] = "LD-10", ["LD-10"] = "LD-10",
["X_58"] = "X_58", ["X_58"] = "X_58",
["X_28"] = "X_28", ["X_28"] = "X_28",
@ -67,22 +69,40 @@ SEAD = {
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
} }
--- Missile enumerators - from DCS ME and Wikipedia
-- @field HarmData
SEAD.HarmData = {
-- km and mach
["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.85},
["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4},
["X_58"] = { 70, 4},
["X_28"] = { 80, 2.5},
["X_25"] = { 25, 0.76},
["X_31"] = {150, 3},
["Kh25"] = {25, 0.8},
}
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions... -- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
-- Chances are big that the missile will miss. -- Chances are big that the missile will miss.
-- @param #SEAD self -- @param #SEAD self
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken. -- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
-- @return SEAD -- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
-- @return #SEAD self
-- @usage -- @usage
-- -- CCCP SEAD Defenses -- -- CCCP SEAD Defenses
-- -- Defends the Russian SA installations from SEAD attacks. -- -- Defends the Russian SA installations from SEAD attacks.
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
function SEAD:New( SEADGroupPrefixes ) function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( SEADGroupPrefixes ) self:F( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@ -90,9 +110,14 @@ function SEAD:New( SEADGroupPrefixes )
else else
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
end end
local padding = Padding or 10
if padding < 10 then padding = 10 end
self.Padding = padding
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.2.8")
self:I("*** SEAD - Started Version 0.3.1")
return self return self
end end
@ -102,8 +127,8 @@ end
-- @return #SEAD self -- @return #SEAD self
function SEAD:UpdateSet( SEADGroupPrefixes ) function SEAD:UpdateSet( SEADGroupPrefixes )
self:F( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@ -118,9 +143,9 @@ end
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355 --- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
-- @param #SEAD self -- @param #SEAD self
-- @param #number range Set the engagement range in percent, e.g. 50 -- @param #number range Set the engagement range in percent, e.g. 50
-- @return self -- @return #SEAD self
function SEAD:SetEngagementRange(range) function SEAD:SetEngagementRange(range)
self:F( { range } ) self:T( { range } )
range = range or 75 range = range or 75
if range < 0 or range > 100 then if range < 0 or range > 100 then
range = 75 range = 75
@ -130,100 +155,175 @@ function SEAD:SetEngagementRange(range)
return self return self
end end
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
-- @param #SEAD self
-- @param #number Padding Extra number of seconds to add for the switch-on
-- @return #SEAD self
function SEAD:SetPadding(Padding)
self:T( { Padding } )
local padding = Padding or 10
if padding < 10 then padding = 10 end
self.Padding = padding
return self
end
--- Check if a known HARM was fired --- Check if a known HARM was fired
-- @param #SEAD self -- @param #SEAD self
-- @param #string WeaponName -- @param #string WeaponName
-- @return #boolean Returns true for a match -- @return #boolean Returns true for a match
-- @return #string name Name of hit in table
function SEAD:_CheckHarms(WeaponName) function SEAD:_CheckHarms(WeaponName)
self:F( { WeaponName } ) self:T( { WeaponName } )
local hit = false local hit = false
local name = ""
for _,_name in pairs (SEAD.Harms) do for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end if string.find(WeaponName,_name,1) then
hit = true
name = _name
break
end
end end
return hit return hit, name
end
--- (Internal) Return distance in meters between two coordinates or -1 on error.
-- @param #SEAD self
-- @param Core.Point#COORDINATE _point1 Coordinate one
-- @param Core.Point#COORDINATE _point2 Coordinate two
-- @return #number Distance in meters
function SEAD:_GetDistance(_point1, _point2)
self:T("_GetDistance")
if _point1 and _point2 then
local distance1 = _point1:Get2DDistance(_point2)
local distance2 = _point1:DistanceFromPointVec2(_point2)
--self:T({dist1=distance1, dist2=distance2})
if distance1 and type(distance1) == "number" then
return distance1
elseif distance2 and type(distance2) == "number" then
return distance2
else
self:E("*****Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
end end
--- Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @see SEAD -- @see SEAD
-- @param #SEAD -- @param #SEAD
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
-- @return #SEAD self
function SEAD:HandleEventShot( EventData ) function SEAD:HandleEventShot( EventData )
self:T( { EventData } ) self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type local SEADWeaponName = EventData.WeaponName -- return weapon type
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
self:T({ SEADWeapon }) --self:T({ SEADWeapon })
if self:_CheckHarms(SEADWeaponName) then if self:_CheckHarms(SEADWeaponName) then
self:T( '*** SEAD - Weapon Match' )
local _targetskill = "Random" local _targetskill = "Random"
local _targetMimgroupName = "none" local _targetgroupname = "none"
local _evade = math.random (1,100) -- random number for chance of evading action local _target = EventData.Weapon:getTarget() -- Identify target
local _targetMim = EventData.Weapon:getTarget() -- Identify target local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object local _targetgroup = nil -- Wrapper.Group#GROUP
if _targetUnit and _targetUnit:IsAlive() then if _targetUnit and _targetUnit:IsAlive() then
local _targetMimgroup = _targetUnit:GetGroup() _targetgroup = _targetUnit:GetGroup()
local _targetMimgroupName = _targetMimgroup:GetName() -- group name _targetgroupname = _targetgroup:GetName() -- group name
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill local _targetUnitName = _targetUnit:GetName()
self:T( self.SEADGroupPrefixes ) _targetUnit:GetSkill()
self:T( _targetMimgroupName ) _targetskill = _targetUnit:GetSkill()
end end
-- see if we are shot at -- see if we are shot at
local SEADGroupFound = false local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then self:T( _targetgroupname, SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix ) then
SEADGroupFound = true SEADGroupFound = true
self:T( '*** SEAD - Group Found' ) self:T( '*** SEAD - Group Match Found' )
break break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" } local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ] _targetskill = Skills[ math.random(1,4) ]
end end
self:T( _targetskill ) --self:T( _targetskill )
if self.TargetSkill[_targetskill] then if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
if (_evade > self.TargetSkill[_targetskill].Evade) then if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T("*** SEAD - Evading")
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) ) -- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
local _targetMimcont= _targetMimgroup:getController() -- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly local wpnspeed = 666 -- ;)
local reach = 10
--tracker ID table to switch groups off and on again if hit then
local id = { local wpndata = SEAD.HarmData[data]
groupName = _targetMimgroup, reach = wpndata[1] * 1,1
ctrl = _targetMimcont local mach = wpndata[2]
} wpnspeed = math.floor(mach * 340.29)
local function SuppressionEnd(id) --switch group back on
local range = self.EngagementRange -- Feature Request #1355
self:T(string.format("*** SEAD - Engagement Range is %d", range))
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
--id.groupName:enableEmission(true)
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
end end
-- randomize switch-on time -- time to impact
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
local SuppressionEndTime = timer.getTime() + delay if _distance > 0 then
--create entry _distance = math.floor(_distance / 1000) -- km
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet else
self.SuppressedGroups[id.groupName] = { _distance = 0
SuppressionEndTime = delay end
}
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
--_targetMimgroup:enableEmission(false)
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateGreen()
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateRed()
grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[args[2]] = false
end
-- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if delay > _tti then delay = delay / 2 end -- speed up
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
end
end end
end end
end end
end end
end end
return self
end end

View File

@ -113,7 +113,7 @@ do
["AGM_122"] = "AGM_122", ["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84", ["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45", ["AGM_45"] = "AGM_45",
["ALARN"] = "ALARM", ["ALARM"] = "ALARM",
["LD-10"] = "LD-10", ["LD-10"] = "LD-10",
["X_58"] = "X_58", ["X_58"] = "X_58",
["X_28"] = "X_28", ["X_28"] = "X_28",

View File

@ -32,6 +32,8 @@
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module] -- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module]
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module] -- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**] -- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**]
-- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**]
-- --
-- **Supported Aircraft:** -- **Supported Aircraft:**
-- --
@ -48,8 +50,8 @@
-- --
-- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier. -- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier.
-- --
-- The AV-8B Harrier and the USS Tarawa are WIP. Those two can only be used together, i.e. the Tarawa is the only carrier the harrier is supposed to land on and -- The AV-8B Harrier, the USS Tarawa, USS America and Juan Carlos I are WIP. The AV-8B harrier and the LHA's and LHD can only be used together, i.e. these ships are the only carriers the harrier is supposed to land on and
-- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier. -- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier.
-- However, the two Case II/III pattern are very similar so this is not a big drawback. -- However, the two Case II/III pattern are very similar so this is not a big drawback.
-- --
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version. -- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
@ -102,12 +104,13 @@
-- ### Wags DCS Hornet Videos: -- ### Wags DCS Hornet Videos:
-- --
-- * [DCS: F/A-18C Hornet - Episode 9: CASE I Carrier Landing](https://www.youtube.com/watch?v=TuigBLhtAH8) -- * [DCS: F/A-18C Hornet - Episode 9: CASE I Carrier Landing](https://www.youtube.com/watch?v=TuigBLhtAH8)
-- * [DCS: F/A-18C Hornet Episode 16: CASE III Introduction](https://www.youtube.com/watch?v=DvlMHnLjbDQ) -- * [DCS: F/A-18C Hornet – Episode 16: CASE III Introduction](https://www.youtube.com/watch?v=DvlMHnLjbDQ)
-- * [DCS: F/A-18C Hornet Case I Carrier Landing Training Lesson Recording](https://www.youtube.com/watch?v=D33uM9q4xgA) -- * [DCS: F/A-18C Hornet Case I Carrier Landing Training Lesson Recording](https://www.youtube.com/watch?v=D33uM9q4xgA)
-- --
-- ### AV-8B Harrier at USS Tarawa -- ### AV-8B Harrier at USS Tarawa
-- --
-- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c) -- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c)
-- * [Harrier Practice pattern USS America](https://youtu.be/99NigITYmcI)
-- --
-- === -- ===
-- --
@ -295,6 +298,8 @@
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_Case1_Landing.png) -- ![Banner Image](..\Presentations\AIRBOSS\Airboss_Case1_Landing.png)
-- --
-- Once the aircraft reaches the Initial, the landing pattern begins. The important steps of the pattern are shown in the image above. -- Once the aircraft reaches the Initial, the landing pattern begins. The important steps of the pattern are shown in the image above.
-- The AV-8B Harrier pattern is very similar, the only differences are as there is no angled deck there is no wake check. from the ninety you wil fly a straight approach offset 26 ft to port (left) of the tram line.
-- The aim is to arrive abeam the landing spot in a stable hover at 120 ft with forward speed matched to the boat. From there the LSO will call "cleared to land". You then level cross to the tram line at the designated landing spot at land vertcally.
-- --
-- --
-- ## CASE III -- ## CASE III
@ -919,9 +924,9 @@
-- --
-- ## Sound Packs -- ## Sound Packs
-- --
-- The AIRBOSS currently has two different "sound packs" for both LSO and Marshal radios. These contain voice overs by different actors. -- The AIRBOSS currently has two different "sound packs" for LSO and three different "sound Packs" for Marshal radios. These contain voice overs by different actors.
-- These can be set by @{#AIRBOSS.SetVoiceOversLSOByRaynor}() and @{#AIRBOSS.SetVoiceOversMarshalByRaynor}(). These are the default settings. -- These can be set by @{#AIRBOSS.SetVoiceOversLSOByRaynor}() and @{#AIRBOSS.SetVoiceOversMarshalByRaynor}(). These are the default settings.
-- The other sound files can be set by @{#AIRBOSS.SetVoiceOversLSOByFF}() and @{#AIRBOSS.SetVoiceOversMarshalByFF}(). -- The other sound files can be set by @{#AIRBOSS.SetVoiceOversLSOByFF}(), @{#AIRBOSS.SetVoiceOversMarshalByGabriella}() and @{#AIRBOSS.SetVoiceOversMarshalByFF}().
-- Also combinations can be used, e.g. -- Also combinations can be used, e.g.
-- --
-- airbossStennis:SetVoiceOversLSOByFF() -- airbossStennis:SetVoiceOversLSOByFF()
@ -1256,7 +1261,7 @@ AIRBOSS = {
--- Aircraft types capable of landing on carrier (human+AI). --- Aircraft types capable of landing on carrier (human+AI).
-- @type AIRBOSS.AircraftCarrier -- @type AIRBOSS.AircraftCarrier
-- @field #string AV8B AV-8B Night Harrier. Works only with the USS Tarawa. -- @field #string AV8B AV-8B Night Harrier. Works only with the USS Tarawa, USS America and Juan Carlos I.
-- @field #string A4EC A-4E Community mod. -- @field #string A4EC A-4E Community mod.
-- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics. -- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
-- @field #string F14A F-14A by Heatblur. -- @field #string F14A F-14A by Heatblur.
@ -1292,6 +1297,8 @@ AIRBOSS.AircraftCarrier={
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module] -- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete] -- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
-- @field #string TARAWA USS Tarawa (LHA-1) -- @field #string TARAWA USS Tarawa (LHA-1)
-- @field #string AMERICA USS America (LHA-6)
-- @field #string JCARLOS Juan Carlos I (L61)
-- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5) -- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5)
AIRBOSS.CarrierType={ AIRBOSS.CarrierType={
ROOSEVELT="CVN_71", ROOSEVELT="CVN_71",
@ -1301,6 +1308,8 @@ AIRBOSS.CarrierType={
STENNIS="Stennis", STENNIS="Stennis",
VINSON="VINSON", VINSON="VINSON",
TARAWA="LHA_Tarawa", TARAWA="LHA_Tarawa",
AMERICA="USS America LHA-6",
JCARLOS="L61",
KUZNETSOV="KUZNECOW", KUZNETSOV="KUZNECOW",
} }
@ -1420,8 +1429,8 @@ AIRBOSS.PatternStep={
-- @field #string IM "IM": In the middle. -- @field #string IM "IM": In the middle.
-- @field #string IC "IC": In close. -- @field #string IC "IC": In close.
-- @field #string AR "AR": At the ramp. -- @field #string AR "AR": At the ramp.
-- @field #string AL "AL": Abeam landing position (Tarawa). -- @field #string AL "AL": Abeam landing position (V/STOL).
-- @field #string LC "LC": Level crossing (Tarawa). -- @field #string LC "LC": Level crossing (V/STOL).
-- @field #string IW "IW": In the wires. -- @field #string IW "IW": In the wires.
AIRBOSS.GroovePos={ AIRBOSS.GroovePos={
X0="X0", X0="X0",
@ -1486,6 +1495,7 @@ AIRBOSS.GroovePos={
-- @field #AIRBOSS.RadioCall DEPARTANDREENTER "Depart and re-enter" call. -- @field #AIRBOSS.RadioCall DEPARTANDREENTER "Depart and re-enter" call.
-- @field #AIRBOSS.RadioCall EXPECTHEAVYWAVEOFF "Expect heavy wavoff" call. -- @field #AIRBOSS.RadioCall EXPECTHEAVYWAVEOFF "Expect heavy wavoff" call.
-- @field #AIRBOSS.RadioCall EXPECTSPOT75 "Expect spot 7.5" call. -- @field #AIRBOSS.RadioCall EXPECTSPOT75 "Expect spot 7.5" call.
-- @field #AIRBOSS.RadioCall EXPECTSPOT5 "Expect spot 5" call.
-- @field #AIRBOSS.RadioCall FAST "You're fast" call. -- @field #AIRBOSS.RadioCall FAST "You're fast" call.
-- @field #AIRBOSS.RadioCall FOULDECK "Foul Deck" call. -- @field #AIRBOSS.RadioCall FOULDECK "Foul Deck" call.
-- @field #AIRBOSS.RadioCall HIGH "You're high" call. -- @field #AIRBOSS.RadioCall HIGH "You're high" call.
@ -1970,6 +1980,12 @@ function AIRBOSS:New(carriername, alias)
elseif self.carriertype==AIRBOSS.CarrierType.TARAWA then elseif self.carriertype==AIRBOSS.CarrierType.TARAWA then
-- Tarawa parameters. -- Tarawa parameters.
self:_InitTarawa() self:_InitTarawa()
elseif self.carriertype==AIRBOSS.CarrierType.AMERICA then
-- Use America parameters.
self:_InitAmerica()
elseif self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Use Juan Carlos parameters.
self:_InitJcarlos()
elseif self.carriertype==AIRBOSS.CarrierType.KUZNETSOV then elseif self.carriertype==AIRBOSS.CarrierType.KUZNETSOV then
-- Kusnetsov parameters - maybe... -- Kusnetsov parameters - maybe...
self:_InitStennis() self:_InitStennis()
@ -2061,7 +2077,7 @@ function AIRBOSS:New(carriername, alias)
-- Carrier specific. -- Carrier specific.
if self.carrier:GetTypeName()~=AIRBOSS.CarrierType.TARAWA then if self.carrier:GetTypeName()~=AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.JCARLOS then
-- Flare wires. -- Flare wires.
local w1=stern:Translate(self.carrierparam.wire1, FB) local w1=stern:Translate(self.carrierparam.wire1, FB)
@ -2834,7 +2850,7 @@ function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LeftMed, LEFT, Right,
self.lue.LeftMed=LeftMed or -2.0 self.lue.LeftMed=LeftMed or -2.0
self.lue.LEFT=LEFT or -3.0 self.lue.LEFT=LEFT or -3.0
self.lue.Right=Right or 1.0 self.lue.Right=Right or 1.0
self.lue.RightMed=RightMed or 2.0 self.lue.RightMed=RightMed or 2.0
self.lue.RIGHT=RIGHT or 3.0 self.lue.RIGHT=RIGHT or 3.0
return self return self
end end
@ -4404,6 +4420,85 @@ function AIRBOSS:_InitTarawa()
end end
--- Init parameters for LHA-6 America carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitAmerica()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist =-125
self.carrierparam.deckheight = 20 --67 ft
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=257
self.carrierparam.totwidthport=11
self.carrierparam.totwidthstarboard=25
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 240
self.carrierparam.rwywidth = 15
-- Wires.
self.carrierparam.wire1=nil
self.carrierparam.wire2=nil
self.carrierparam.wire3=nil
self.carrierparam.wire4=nil
-- Late break.
self.BreakLate.name="Late Break"
self.BreakLate.Xmin=-UTILS.NMToMeters(1) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax= UTILS.NMToMeters(5) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin=-UTILS.NMToMeters(1.6) -- Not more than 1.6 NM port.
self.BreakLate.Zmax= UTILS.NMToMeters(1) -- Not more than 1 NM starboard.
self.BreakLate.LimitXmin= 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax= nil
self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax= nil
end
--- Init parameters for L61 Juan Carlos carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitJcarlos()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist =-125
self.carrierparam.deckheight = 20 --67 ft
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=231
self.carrierparam.totwidthport=10
self.carrierparam.totwidthstarboard=22
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 202
self.carrierparam.rwywidth = 14
-- Wires.
self.carrierparam.wire1=nil
self.carrierparam.wire2=nil
self.carrierparam.wire3=nil
self.carrierparam.wire4=nil
-- Late break.
self.BreakLate.name="Late Break"
self.BreakLate.Xmin=-UTILS.NMToMeters(1) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax= UTILS.NMToMeters(5) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin=-UTILS.NMToMeters(1.6) -- Not more than 1.6 NM port.
self.BreakLate.Zmax= UTILS.NMToMeters(1) -- Not more than 1 NM starboard.
self.BreakLate.LimitXmin= 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax= nil
self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax= nil
end
--- Init parameters for Marshal Voice overs *Gabriella* by HighwaymanEd. --- Init parameters for Marshal Voice overs *Gabriella* by HighwaymanEd.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #string mizfolder (Optional) Folder within miz file where the sound files are located. -- @param #string mizfolder (Optional) Folder within miz file where the sound files are located.
@ -4558,6 +4653,7 @@ function AIRBOSS:SetVoiceOversLSOByRaynor(mizfolder)
self.LSOCall.DEPARTANDREENTER.duration=1.10 self.LSOCall.DEPARTANDREENTER.duration=1.10
self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.30 self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.30
self.LSOCall.EXPECTSPOT75.duration=1.85 self.LSOCall.EXPECTSPOT75.duration=1.85
self.LSOCall.EXPECTSPOT5.duration=1.3
self.LSOCall.FAST.duration=0.75 self.LSOCall.FAST.duration=0.75
self.LSOCall.FOULDECK.duration=0.75 self.LSOCall.FOULDECK.duration=0.75
self.LSOCall.HIGH.duration=0.65 self.LSOCall.HIGH.duration=0.65
@ -4616,6 +4712,7 @@ function AIRBOSS:SetVoiceOversLSOByFF(mizfolder)
self.LSOCall.DEPARTANDREENTER.duration=1.10 self.LSOCall.DEPARTANDREENTER.duration=1.10
self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20 self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20
self.LSOCall.EXPECTSPOT75.duration=2.00 self.LSOCall.EXPECTSPOT75.duration=2.00
self.LSOCall.EXPECTSPOT5.duration=1.3
self.LSOCall.FAST.duration=0.70 self.LSOCall.FAST.duration=0.70
self.LSOCall.FOULDECK.duration=0.62 self.LSOCall.FOULDECK.duration=0.62
self.LSOCall.HIGH.duration=0.65 self.LSOCall.HIGH.duration=0.65
@ -4883,6 +4980,14 @@ function AIRBOSS:_InitVoiceOvers()
subtitle="Expect spot 7.5", subtitle="Expect spot 7.5",
duration=2.0, duration=2.0,
subduration=5, subduration=5,
},
EXPECTSPOT5={
file="LSO-ExpectSpot5",
suffix="ogg",
loud=false,
subtitle="Expect spot 5",
duration=1.3,
subduration=5,
}, },
STABILIZED={ STABILIZED={
file="LSO-Stabilized", file="LSO-Stabilized",
@ -5543,14 +5648,14 @@ function AIRBOSS:_GetAircraftAoA(playerData)
aoa.Fast = 8.25 --=17.5/2 aoa.Fast = 8.25 --=17.5/2
aoa.FAST = 8.00 --=16.5/2 aoa.FAST = 8.00 --=16.5/2
elseif harrier then elseif harrier then
-- AV-8B Harrier parameters. This might need further tuning. -- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 60 - 73.
aoa.SLOW = 14.0 aoa.SLOW = 14.0
aoa.Slow = 13.0 aoa.Slow = 13.0
aoa.OnSpeedMax = 12.0 aoa.OnSpeedMax = 12.0
aoa.OnSpeed = 11.0 aoa.OnSpeed = 11.0
aoa.OnSpeedMin = 10.0 aoa.OnSpeedMin = 10.0
aoa.Fast = 9.0 aoa.Fast = 8.0
aoa.FAST = 8.0 aoa.FAST = 7.5
end end
return aoa return aoa
@ -5810,7 +5915,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
alt=UTILS.FeetToMeters(300) --? alt=UTILS.FeetToMeters(300) --?
elseif harrier then elseif harrier then
-- 300-325 ft -- 300-325 ft
alt=UTILS.FeetToMeters(300) alt=UTILS.FeetToMeters(300)-- Need to verify
end end
aoa=aoaac.OnSpeed aoa=aoaac.OnSpeed
@ -6751,8 +6856,8 @@ function AIRBOSS:_GetMarshalAltitude(stack, case)
-- Second point 1.5 NM ahead. -- Second point 1.5 NM ahead.
p2=Carrier:Translate(UTILS.NMToMeters(1.5), hdg) p2=Carrier:Translate(UTILS.NMToMeters(1.5), hdg)
-- Tarawa Delta pattern. -- Tarawa,LHA,LHD Delta patterns.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Pattern is directly overhead the carrier. -- Pattern is directly overhead the carrier.
p1=Carrier:Translate(UTILS.NMToMeters(1.0), hdg+90) p1=Carrier:Translate(UTILS.NMToMeters(1.0), hdg+90)
@ -8597,7 +8702,7 @@ function AIRBOSS:OnEventLand(EventData)
self:T(self.lid..text) self:T(self.lid..text)
-- Check carrier type. -- Check carrier type.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Power "Idle". -- Power "Idle".
self:RadioTransmission(self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true)
@ -8632,7 +8737,7 @@ function AIRBOSS:OnEventLand(EventData)
-- AI unit landed -- -- AI unit landed --
-------------------- --------------------
if self.carriertype~=AIRBOSS.CarrierType.TARAWA then if self.carriertype~=AIRBOSS.CarrierType.TARAWA or self.carriertype~=AIRBOSS.CarrierType.AMERICA or self.carriertype~=AIRBOSS.CarrierType.JCARLOS then
-- Coordinate at landing event -- Coordinate at landing event
local coord=EventData.IniUnit:GetCoordinate() local coord=EventData.IniUnit:GetCoordinate()
@ -9539,8 +9644,10 @@ function AIRBOSS:_Bullseye(playerData)
-- Hint for player about altitude, AoA etc. -- Hint for player about altitude, AoA etc.
self:_PlayerHint(playerData) self:_PlayerHint(playerData)
-- LSO expect spot 7.5 call -- LSO expect spot 5 or 7.5 call
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and self.carriertype==AIRBOSS.CarrierType.JCARLOS then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT5, nil, nil, nil, true)
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, nil, nil, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, nil, nil, nil, true)
end end
@ -9676,8 +9783,8 @@ function AIRBOSS:_CheckForLongDownwind(playerData)
-- 1.6 NM from carrier is too far. -- 1.6 NM from carrier is too far.
local limit=UTILS.NMToMeters(-1.6) local limit=UTILS.NMToMeters(-1.6)
-- For the tarawa we give a bit more space. -- For the tarawa, other LHA and LHD we give a bit more space.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
limit=UTILS.NMToMeters(-2.0) limit=UTILS.NMToMeters(-2.0)
end end
@ -9722,8 +9829,10 @@ function AIRBOSS:_Abeam(playerData)
-- Paddles contact. -- Paddles contact.
self:RadioTransmission(self.LSORadio, self.LSOCall.PADDLESCONTACT, nil, nil, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.PADDLESCONTACT, nil, nil, nil, true)
-- LSO expect spot 7.5 call -- LSO expect spot 5 or 7.5 call
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and self.carriertype==AIRBOSS.CarrierType.JCARLOS then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT5, false, 5, nil, true)
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, false, 5, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, false, 5, nil, true)
end end
@ -9760,7 +9869,7 @@ function AIRBOSS:_Ninety(playerData)
self:_PlayerHint(playerData) self:_PlayerHint(playerData)
-- Next step: wake. -- Next step: wake.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Harrier has no wake stop. It stays port of the boat. -- Harrier has no wake stop. It stays port of the boat.
self:_SetPlayerStep(playerData, AIRBOSS.PatternStep.FINAL) self:_SetPlayerStep(playerData, AIRBOSS.PatternStep.FINAL)
else else
@ -10113,7 +10222,7 @@ function AIRBOSS:_Groove(playerData)
-- Drift on lineup. -- Drift on lineup.
if rho>=RAR and rho<=RIM then if rho>=RAR and rho<=RIM then
if gd.LUE>0.22 and lineupError<-0.22 then if gd.LUE>0.22 and lineupError<-0.22 then
env.info" Drift Right across centre ==> DR-" env.info" Drift Right across centre ==> DR-"
gd.Drift=" DR" gd.Drift=" DR"
self:T(self.lid..string.format("Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError)) self:T(self.lid..string.format("Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
@ -10128,7 +10237,7 @@ function AIRBOSS:_Groove(playerData)
elseif gd.LUE<-0.13 and lineupError>0.14 then elseif gd.LUE<-0.13 and lineupError>0.14 then
env.info" Little Drift Left across centre ==> (DL-)" env.info" Little Drift Left across centre ==> (DL-)"
gd.Drift=" (DL)" gd.Drift=" (DL)"
self:E(self.lid..string.format("Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError)) self:E(self.lid..string.format("Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
end end
end end
@ -10434,7 +10543,7 @@ function AIRBOSS:_GetSternCoord()
--local stern=self:GetCoordinate() --local stern=self:GetCoordinate()
-- Stern coordinate (sterndist<0). -- Stern coordinate (sterndist<0).
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Tarawa: Translate 8 meters port. -- Tarawa: Translate 8 meters port.
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true) self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
@ -10472,7 +10581,7 @@ function AIRBOSS:_GetWire(Lcoord, dc)
-- Corrected landing distance wrt to stern. Landing distance needs to be reduced due to delayed landing event for human players. -- Corrected landing distance wrt to stern. Landing distance needs to be reduced due to delayed landing event for human players.
local d=Ldist-dc local d=Ldist-dc
-- Multiplayer wire correction. -- Multiplayer wire correction.
if self.mpWireCorrection then if self.mpWireCorrection then
d=d-self.mpWireCorrection d=d-self.mpWireCorrection
@ -11177,7 +11286,7 @@ function AIRBOSS:_GetZoneHolding(case, stack)
self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", Post:GetVec2(), self.marshalradius) self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", Post:GetVec2(), self.marshalradius)
-- Delta pattern. -- Delta pattern.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters(5)) self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters(5))
end end
@ -11230,7 +11339,7 @@ function AIRBOSS:_GetZoneCommence(case, stack)
-- Three position -- Three position
local Three=self:GetCoordinate():Translate(D, hdg+275) local Three=self:GetCoordinate():Translate(D, hdg+275)
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
local Dx=UTILS.NMToMeters(2.25) local Dx=UTILS.NMToMeters(2.25)
@ -11521,7 +11630,7 @@ function AIRBOSS:_GetAltCarrier(unit)
return h return h
end end
--- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa we take the abeam landing spot 120 ft abeam the 7.5 position. --- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa and America we take the abeam landing spot 120 ft abeam the 7.5 position, for the Juan Carlos I it is 120 ft and abeam the 5 position.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @return Core.Point#COORDINATE Optimal landing coordinate. -- @return Core.Point#COORDINATE Optimal landing coordinate.
function AIRBOSS:_GetOptLandingCoordinate() function AIRBOSS:_GetOptLandingCoordinate()
@ -11541,6 +11650,23 @@ function AIRBOSS:_GetOptLandingCoordinate()
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true) self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true)
--stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90) --stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
elseif self.carriertype==AIRBOSS.CarrierType.AMERICA then
-- Landing 100 ft abeam, 120 ft alt. To allow adjustments to match different deck configurations.
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true)
--stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
elseif self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-100, true, true)
--stern=self:_GetLandingSpotCoordinate():Translate(35, FB-100)
-- Alitude 120 ft. -- Alitude 120 ft.
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120)) self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
@ -11578,6 +11704,21 @@ function AIRBOSS:_GetLandingSpotCoordinate()
-- Primary landing spot 7.5 -- Primary landing spot 7.5
self.landingspotcoord:Translate(57, hdg, true, true):SetAltitude(self.carrierparam.deckheight) self.landingspotcoord:Translate(57, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
elseif self.carriertype==AIRBOSS.CarrierType.AMERICA then
-- Landing 100 ft abeam, 120 alt.
local hdg=self:GetHeading()
-- Primary landing spot 7.5 a little further forwad on the America
self.landingspotcoord:Translate(59, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
elseif self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Landing 100 ft abeam, 120 alt.
local hdg=self:GetHeading()
-- Primary landing spot 5.0 -- TODO voice for different landing Spots.
self.landingspotcoord:Translate(89, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
end end
@ -12070,6 +12211,11 @@ end
-- * > 24 seconds: No Grade "--" -- * > 24 seconds: No Grade "--"
-- --
-- If you manage to be between 16.4 and and 16.6 seconds, you will even get and okay underline "\_OK\_". -- If you manage to be between 16.4 and and 16.6 seconds, you will even get and okay underline "\_OK\_".
-- No groove time for Harrier on LHA, LHD set to Tgroove Unicorn as starting point to allow possible _OK_ 5.0.
-- If time in the AV-8B
--
-- * < 90 seconds: OK V/STOL
-- * > 91 Seconds: SLOW V/STOL (Early hover stop selection)
-- --
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #AIRBOSS.PlayerData playerData Player data table. -- @param #AIRBOSS.PlayerData playerData Player data table.
@ -12088,6 +12234,13 @@ function AIRBOSS:_EvalGrooveTime(playerData)
grade="OK Groove" grade="OK Groove"
elseif t<=24 then elseif t<=24 then
grade="(LIG)" grade="(LIG)"
-- Time in groove for AV-8B
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and t<55 then -- VSTOL Late Hover stop selection too fast to Abeam LDG Spot AV-8B.
grade="FAST V/STOL Groove"
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and t<90 then -- VSTOL Operations with AV-8B.
grade="OK V/STOL Groove"
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and t>=91 then -- VSTOL Early Hover stop selection slow to Abeam LDG Spot AV-8B.
grade="SLOW V/STOL Groove"
else else
grade="LIG" grade="LIG"
end end
@ -12097,6 +12250,11 @@ function AIRBOSS:_EvalGrooveTime(playerData)
grade="_OK_" grade="_OK_"
end end
-- V/STOL Unicorn!
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and (t>=65.0 and t<=75.0) then
grade="_OK_ V/STOL"
end
return grade return grade
end end
@ -12113,7 +12271,7 @@ function AIRBOSS:_LSOgrade(playerData)
return select(2, string.gsub(base, pattern, "")) return select(2, string.gsub(base, pattern, ""))
end end
-- Analyse flight data and conver to LSO text. -- Analyse flight data and convert to LSO text.
local GXX,nXX=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.XX) local GXX,nXX=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.XX)
local GIM,nIM=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IM) local GIM,nIM=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IM)
local GIC,nIC=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IC) local GIC,nIC=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IC)
@ -12122,25 +12280,37 @@ function AIRBOSS:_LSOgrade(playerData)
-- Put everything together. -- Put everything together.
local G=GXX.." "..GIM.." ".." "..GIC.." "..GAR local G=GXX.." "..GIM.." ".." "..GIC.." "..GAR
-- Count number of minor, normal and major deviations. -- Count number of minor, normal and major deviations. TODO - work on Harrier counts due slower approach speed.
local N=nXX+nIM+nIC+nAR local N=nXX+nIM+nIC+nAR
local nL=count(G, '_')/2 local nL=count(G, '_')/2
local nS=count(G, '%(') local nS=count(G, '%(')
local nN=N-nS-nL local nN=N-nS-nL
-- Groove time 15-18.99 sec for a unicorn. -- Groove time 15-18.99 sec for a unicorn. Or 65-70 for V/STOL unicorn.
local Tgroove=playerData.Tgroove local Tgroove=playerData.Tgroove
local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false
local TgrooveVstolUnicorn=Tgroove and (Tgroove>=65.0 and Tgroove<=70.0)and playerData.actype==AIRBOSS.AircraftCarrier.AV8B or false
local grade local grade
local points local points
if N==0 and TgrooveUnicorn then if N==0 and (TgrooveUnicorn or TgrooveVstolUnicorn ) then
-- No deviations, should be REALLY RARE! -- No deviations, should be REALLY RARE!
grade="_OK_" grade="_OK_"
points=5.0 points=5.0
G="Unicorn" G="Unicorn"
else else
if nL>0 then
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe. (WIP requires feedback)
-- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
if nL>3 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Larger deviations ==> "No grade" 2.0 points.
grade="--"
points=2.0
elseif nN>2 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade="(OK)"
points=3.0
elseif nL>0 then
-- Larger deviations ==> "No grade" 2.0 points. -- Larger deviations ==> "No grade" 2.0 points.
grade="--" grade="--"
points=2.0 points=2.0
@ -12153,7 +12323,8 @@ function AIRBOSS:_LSOgrade(playerData)
grade="OK" grade="OK"
points=4.0 points=4.0
end end
end
end
-- Replace" )"( and "__" -- Replace" )"( and "__"
G=G:gsub("%)%(", "") G=G:gsub("%)%(", "")
@ -12263,35 +12434,35 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
-- Aircraft specific AoA values. -- Aircraft specific AoA values.
local acaoa=self:_GetAircraftAoA(playerData) local acaoa=self:_GetAircraftAoA(playerData)
--Angled Approach. --Angled Approach.
local P=nil local P=nil
if step==AIRBOSS.PatternStep.GROOVE_XX and ROL<=4.0 and playerData.case<3 then if step==AIRBOSS.PatternStep.GROOVE_XX and ROL<=4.0 and playerData.case<3 then
if LUE>self.lue.RIGHT then if LUE>self.lue.RIGHT then
P=underline("AA") P=underline("AA")
elseif elseif
LUE>self.lue.RightMed then LUE>self.lue.RightMed then
P="AA " P="AA "
elseif elseif
LUE>self.lue.Right then LUE>self.lue.Right then
P=little("AA") P=little("AA")
end end
end end
--Overshoot Start. --Overshoot Start.
local O=nil local O=nil
if step==AIRBOSS.PatternStep.GROOVE_XX then if step==AIRBOSS.PatternStep.GROOVE_XX then
if LUE<self.lue.LEFT then if LUE<self.lue.LEFT then
O=underline("OS") O=underline("OS")
elseif elseif
LUE<self.lue.Left then LUE<self.lue.Left then
O="OS" O="OS"
elseif elseif
LUE<self.lue._min then LUE<self.lue._min then
O=little("OS") O=little("OS")
end end
end end
-- Speed via AoA. Depends on aircraft type. -- Speed via AoA. Depends on aircraft type.
local S=nil local S=nil
if AOA>acaoa.SLOW then if AOA>acaoa.SLOW then
@ -12361,7 +12532,7 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
if P then if P then
G=G..P G=G..P
n=n n=n
end end
-- Speed. -- Speed.
if S then if S then
G=G..S G=G..S
@ -12387,7 +12558,7 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
G=G..O G=G..O
n=n+1 n=n+1
end end
-- Add current step. -- Add current step.
local step=self:_GS(step) local step=self:_GS(step)
step=step:gsub("XX","X") step=step:gsub("XX","X")
@ -12449,7 +12620,7 @@ function AIRBOSS:_GS(step, n)
if n==-1 then if n==-1 then
gp=AIRBOSS.GroovePos.IC gp=AIRBOSS.GroovePos.IC
elseif n==1 then elseif n==1 then
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
gp=AIRBOSS.GroovePos.AL gp=AIRBOSS.GroovePos.AL
else else
gp=AIRBOSS.GroovePos.IW gp=AIRBOSS.GroovePos.IW
@ -14339,17 +14510,17 @@ function AIRBOSS:_IsCarrierAircraft(unit)
-- Get aircraft type name -- Get aircraft type name
local aircrafttype=unit:GetTypeName() local aircrafttype=unit:GetTypeName()
-- Special case for Harrier which can only land on Tarawa. -- Special case for Harrier which can only land on Tarawa, LHA and LHD.
if aircrafttype==AIRBOSS.AircraftCarrier.AV8B then if aircrafttype==AIRBOSS.AircraftCarrier.AV8B then
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
return true return true
else else
return false return false
end end
end end
-- Also only Harriers can land on the Tarawa. -- Also only Harriers can land on the Tarawa, LHA and LHD.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
if aircrafttype~=AIRBOSS.AircraftCarrier.AV8B then if aircrafttype~=AIRBOSS.AircraftCarrier.AV8B then
return false return false
end end
@ -17718,8 +17889,8 @@ function AIRBOSS:_MarkCaseZones(_unitName, flare)
self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green, 45) self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green, 45)
end end
-- Tarawa landing spots. -- Tarawa, LHA and LHD landing spots.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
text=text.."\n* abeam landing stop with RED flares" text=text.."\n* abeam landing stop with RED flares"
-- Abeam landing spot zone. -- Abeam landing spot zone.
local ALSPT=self:_GetZoneAbeamLandingSpot() local ALSPT=self:_GetZoneAbeamLandingSpot()

View File

@ -1,4 +1,3 @@
--- **Ops** -- Combat Search and Rescue. --- **Ops** -- Combat Search and Rescue.
-- --
-- === -- ===
@ -23,7 +22,7 @@
-- @module Ops.CSAR -- @module Ops.CSAR
-- @image OPS_CSAR.jpg -- @image OPS_CSAR.jpg
-- Date: July 2021 -- Date: Sep 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@ -70,6 +69,7 @@
-- --
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms. -- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms.
-- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only! -- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only!
-- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
-- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near. -- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
-- self.autosmokedistance = 1000 -- distance for autosmoke -- self.autosmokedistance = 1000 -- distance for autosmoke
-- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. -- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
@ -95,6 +95,8 @@
-- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters -- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
-- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters -- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
-- self.pilotmustopendoors = false -- switch to true to enable check of open doors -- self.pilotmustopendoors = false -- switch to true to enable check of open doors
-- -- (added 0.1.9)
-- self.suppressmessages = false -- switch off all messaging if you want to do your own
-- --
-- ## 2.1 Experimental Features -- ## 2.1 Experimental Features
-- --
@ -214,26 +216,6 @@ CSAR = {
-- @field Wrapper.Group#GROUP group Spawned group object. -- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process -- @field #number timestamp Timestamp for approach process
-- @field #boolean alive Group is alive or dead/rescued -- @field #boolean alive Group is alive or dead/rescued
--
--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps.
--[[ Moved to Utils
-- @field #CSAR.SkipFrequencies
CSAR.SkipFrequencies = {
214,274,291.5,295,297.5,
300.5,304,307,309.5,311,312,312.5,316,
320,324,328,329,330,336,337,
342,343,348,351,352,353,358,
363,365,368,372.5,374,
380,381,384,389,395,396,
414,420,430,432,435,440,450,455,462,470,485,
507,515,520,525,528,540,550,560,570,577,580,602,625,641,662,670,680,682,690,
705,720,722,730,735,740,745,750,770,795,
822,830,862,866,
905,907,920,935,942,950,995,
1000,1025,1030,1050,1065,1116,1175,1182,1210
}
--]]
--- All slot / Limit settings --- All slot / Limit settings
-- @type CSAR.AircraftType -- @type CSAR.AircraftType
@ -251,7 +233,7 @@ CSAR.AircraftType["Mi-24V"] = 8
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="0.1.8r3" CSAR.version="0.1.10r5"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@ -361,12 +343,13 @@ function CSAR:New(Coalition, Template, Alias)
self.loadtimemax = 135 -- seconds self.loadtimemax = 135 -- seconds
self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK! self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
self.max_units = 6 --max number of pilots that can be carried self.max_units = 6 --max number of pilots that can be carried
self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below
self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names! self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names!
self.template = Template or "generic" -- template for downed pilot self.template = Template or "generic" -- template for downed pilot
self.mashprefix = {"MASH"} -- prefixes used to find MASHes self.mashprefix = {"MASH"} -- prefixes used to find MASHes
self.mash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also?
self.autosmoke = false -- automatically smoke location when heli is near self.autosmoke = false -- automatically smoke location when heli is near
self.autosmokedistance = 2000 -- distance for autosmoke self.autosmokedistance = 2000 -- distance for autosmoke
-- added 0.1.4 -- added 0.1.4
@ -378,6 +361,7 @@ function CSAR:New(Coalition, Template, Alias)
self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
self.pilotmustopendoors = false -- switch to true to enable check on open doors self.pilotmustopendoors = false -- switch to true to enable check on open doors
self.suppressmessages = false
-- WARNING - here\'ll be dragons -- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua -- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
@ -642,7 +626,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
local _typeName = _typeName or "Pilot" local _typeName = _typeName or "Pilot"
if not noMessage then if not noMessage then
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10) self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, self.messageTime)
end end
if _freq then if _freq then
@ -734,7 +718,6 @@ function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessa
return self return self
end end
-- TODO: Split in functions per Event type
--- (Internal) Event handler. --- (Internal) Event handler.
-- @param #CSAR self -- @param #CSAR self
function CSAR:_EventHandler(EventData) function CSAR:_EventHandler(EventData)
@ -806,8 +789,7 @@ function CSAR:_EventHandler(EventData)
if self:_DoubleEjection(_unitname) then if self:_DoubleEjection(_unitname) then
return return
end end
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, 10)
--local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition)
else else
self:T(self.lid .. " Pilot has not taken off, ignore") self:T(self.lid .. " Pilot has not taken off, ignore")
end end
@ -886,13 +868,15 @@ function CSAR:_EventHandler(EventData)
self:T(self.lid .. " Landing Place Nil") self:T(self.lid .. " Landing Place Nil")
return -- error! return -- error!
end end
-- anyone on board?
if self.inTransitGroups[_event.IniUnitName] == nil then
-- ignore
return
end
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
else
self:_RescuePilots(_unit)
end
else else
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition())) self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
end end
@ -912,7 +896,6 @@ end
function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage) function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
self:T(self.lid .. " _InitSARForPilot") self:T(self.lid .. " _InitSARForPilot")
local _leader = _downedGroup:GetUnit(1) local _leader = _downedGroup:GetUnit(1)
--local _groupName = _downedGroup:GetName()
local _groupName = _GroupName local _groupName = _GroupName
local _freqk = _freq / 1000 local _freqk = _freq / 1000
local _coordinatesText = self:_GetPositionOfWounded(_downedGroup) local _coordinatesText = self:_GetPositionOfWounded(_downedGroup)
@ -928,7 +911,7 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
end end
-- trigger FSM event -- trigger FSM event
self:__PilotDown(2,_downedGroup, _freqk, _leadername, _coordinatesText) self:__PilotDown(2,_downedGroup, _freqk, _groupName, _coordinatesText)
return self return self
end end
@ -1098,7 +1081,6 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
local grouptable = downedgrouptable --#CSAR.DownedPilot local grouptable = downedgrouptable --#CSAR.DownedPilot
self.inTransitGroups[_heliName][_woundedGroupName] = self.inTransitGroups[_heliName][_woundedGroupName] =
{ {
-- DONE: Fix with #CSAR.DownedPilot
originalUnit = grouptable.originalUnit, originalUnit = grouptable.originalUnit,
woundedGroup = _woundedGroupName, woundedGroup = _woundedGroupName,
side = self.coalition, side = self.coalition,
@ -1106,7 +1088,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
player = grouptable.player, player = grouptable.player,
} }
_woundedGroup:Destroy() _woundedGroup:Destroy(false)
self:_RemoveNameFromDownedPilots(_woundedGroupName,true) self:_RemoveNameFromDownedPilots(_woundedGroupName,true)
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true) self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true)
@ -1137,39 +1119,8 @@ end
-- @return #boolean outcome The outcome. -- @return #boolean outcome The outcome.
function CSAR:_IsLoadingDoorOpen( unit_name ) function CSAR:_IsLoadingDoorOpen( unit_name )
self:T(self.lid .. " _IsLoadingDoorOpen") self:T(self.lid .. " _IsLoadingDoorOpen")
local ret_val = false return UTILS.IsLoadingDoorOpen(unit_name)
local unit = Unit.getByName(unit_name)
if unit ~= nil then
local type_name = unit:getTypeName()
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
self:T(unit_name .. " Cargo doors are open or cargo door not present")
ret_val = true
end
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
self:T(unit_name .. " a side door is open")
ret_val = true
end
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
self:T(unit_name .. " a side door is open ")
ret_val = true
end
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
self:T(unit_name .. " front door(s) are open")
ret_val = true
end
if ret_val == false then
self:T(unit_name .. " all doors are closed")
end
return ret_val
end -- nil
return false
end end
--- (Internal) Function to check if heli is close to group. --- (Internal) Function to check if heli is close to group.
@ -1200,14 +1151,12 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
else else
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,false,true) self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,false,true)
end end
--mark as shown for THIS heli and THIS group
self.heliCloseMessage[_lookupKeyHeli] = true self.heliCloseMessage[_lookupKeyHeli] = true
end end
-- have we landed close enough? -- have we landed close enough?
if not _heliUnit:InAir() then if not _heliUnit:InAir() then
-- if you land on them, doesnt matter if they were heading to someone else as you\'re closer, you win! :)
if self.pilotRuntoExtractPoint == true then if self.pilotRuntoExtractPoint == true then
if (_distance < self.extractDistance) then if (_distance < self.extractDistance) then
local _time = self.landedStatus[_lookupKeyHeli] local _time = self.landedStatus[_lookupKeyHeli]
@ -1308,7 +1257,8 @@ end
-- @param #CSAR self -- @param #CSAR self
-- @param #string heliname Heli name -- @param #string heliname Heli name
-- @param #string groupname Group name -- @param #string groupname Group name
function CSAR:_ScheduledSARFlight(heliname,groupname) -- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport)
self:T(self.lid .. " _ScheduledSARFlight") self:T(self.lid .. " _ScheduledSARFlight")
self:T({heliname,groupname}) self:T({heliname,groupname})
local _heliUnit = self:_GetSARHeli(heliname) local _heliUnit = self:_GetSARHeli(heliname)
@ -1331,8 +1281,8 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
return return
end end
if _dist < 200 and _heliUnit:InAir() == false then if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
else else
self:_RescuePilots(_heliUnit) self:_RescuePilots(_heliUnit)
@ -1341,7 +1291,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
end end
--queue up --queue up
self:__Returning(-5,heliname,_woundedGroupName) self:__Returning(-5,heliname,_woundedGroupName, isairport)
return self return self
end end
@ -1357,8 +1307,7 @@ function CSAR:_RescuePilots(_heliUnit)
-- Groups already rescued -- Groups already rescued
return return
end end
-- DONE: count saved units?
local PilotsSaved = self:_PilotsOnboard(_heliName) local PilotsSaved = self:_PilotsOnboard(_heliName)
self.inTransitGroups[_heliName] = nil self.inTransitGroups[_heliName] = nil
@ -1397,7 +1346,9 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
local group = _unit:GetGroup() local group = _unit:GetGroup()
local _clear = _clear or nil local _clear = _clear or nil
local _time = _time or self.messageTime local _time = _time or self.messageTime
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group) if not self.suppressmessages then
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
end
-- integrate SRS -- integrate SRS
if _speak and self.useSRS then if _speak and self.useSRS then
local srstext = SOUNDTEXT:New(_text) local srstext = SOUNDTEXT:New(_text)
@ -1452,7 +1403,6 @@ function CSAR:_DisplayActiveSAR(_unitName)
local _groupName = _value.name local _groupName = _value.name
self:T(string.format("Display Active Pilot: %s", tostring(_groupName))) self:T(string.format("Display Active Pilot: %s", tostring(_groupName)))
self:T({Table=_value}) self:T({Table=_value})
--local _woundedGroup = GROUP:FindByName(_groupName)
local _woundedGroup = _value.group local _woundedGroup = _value.group
if _woundedGroup and _value.alive then if _woundedGroup and _value.alive then
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup) local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
@ -1495,10 +1445,17 @@ function CSAR:_GetClosestDownedPilot(_heli)
local _shortestDistance = -1 local _shortestDistance = -1
local _distance = 0 local _distance = 0
local _closestGroupInfo = nil local _closestGroupInfo = nil
local _heliCoord = _heli:GetCoordinate() local _heliCoord = _heli:GetCoordinate() or _heli:GetCoordinate()
if _heliCoord == nil then
self:E("****Error obtaining coordinate!")
return nil
end
local DownedPilotsTable = self.downedPilots local DownedPilotsTable = self.downedPilots
for _, _groupInfo in pairs(DownedPilotsTable) do
for _, _groupInfo in UTILS.spairs(DownedPilotsTable) do
--for _, _groupInfo in pairs(DownedPilotsTable) do
local _woundedName = _groupInfo.name local _woundedName = _groupInfo.name
local _tempWounded = _groupInfo.group local _tempWounded = _groupInfo.group
@ -1564,12 +1521,11 @@ end
-- @param #number _messagetime How long to show. -- @param #number _messagetime How long to show.
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
self:T(self.lid .. " _DisplayToAllSAR") self:T(self.lid .. " _DisplayToAllSAR")
local messagetime = _messagetime or self.messageTime
for _, _unitName in pairs(self.csarUnits) do for _, _unitName in pairs(self.csarUnits) do
local _unit = self:_GetSARHeli(_unitName) local _unit = self:_GetSARHeli(_unitName)
if _unit then if _unit and not self.suppressmessages then
if not _messagetime then self:_DisplayMessageToSAR(_unit, _message, _messagetime)
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
end
end end
end end
return self return self
@ -1741,9 +1697,20 @@ end
function CSAR:_GetDistance(_point1, _point2) function CSAR:_GetDistance(_point1, _point2)
self:T(self.lid .. " _GetDistance") self:T(self.lid .. " _GetDistance")
if _point1 and _point2 then if _point1 and _point2 then
local distance = _point1:DistanceFromPointVec2(_point2) local distance1 = _point1:Get2DDistance(_point2)
return distance local distance2 = _point1:DistanceFromPointVec2(_point2)
if distance1 and type(distance1) == "number" then
return distance1
elseif distance2 and type(distance2) == "number" then
return distance2
else
self:E("*****Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
else else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1 return -1
end end
end end
@ -1752,7 +1719,6 @@ end
-- @param #CSAR self -- @param #CSAR self
function CSAR:_GenerateVHFrequencies() function CSAR:_GenerateVHFrequencies()
self:T(self.lid .. " _GenerateVHFrequencies") self:T(self.lid .. " _GenerateVHFrequencies")
--local _skipFrequencies = self.SkipFrequencies
local FreeVHFFrequencies = {} local FreeVHFFrequencies = {}
FreeVHFFrequencies = UTILS.GenerateVHFrequencies() FreeVHFFrequencies = UTILS.GenerateVHFrequencies()
@ -1792,7 +1758,6 @@ function CSAR:_GetClockDirection(_heli, _group)
if _heading then if _heading then
local Aspect = Angle - _heading local Aspect = Angle - _heading
if Aspect == 0 then Aspect = 360 end if Aspect == 0 then Aspect = 360 end
--clock = math.floor(Aspect / 30)
clock = math.abs(UTILS.Round((Aspect / 30),0)) clock = math.abs(UTILS.Round((Aspect / 30),0))
if clock == 0 then clock = 12 end if clock == 0 then clock = 12 end
end end
@ -1901,6 +1866,7 @@ function CSAR:onafterStart(From, Event, To)
else else
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end end
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
self:__Status(-10) self:__Status(-10)
return self return self
end end
@ -1909,19 +1875,19 @@ end
-- @param #CSAR self -- @param #CSAR self
function CSAR:_CheckDownedPilotTable() function CSAR:_CheckDownedPilotTable()
local pilots = self.downedPilots local pilots = self.downedPilots
for _,_entry in pairs (pilots) do local npilots = {}
self:T("Checking for " .. _entry.name)
self:T({entry=_entry}) for _ind,_entry in pairs(pilots) do
local group = _entry.group local _group = _entry.group
if not group:IsAlive() then if _group:IsAlive() then
self:T("Group is dead") npilots[_ind] = _entry
if _entry.alive == true then else
self:T("Switching .alive to false") if _entry.alive then
self:__KIA(1,_entry.desc) self:__KIA(1,_entry.desc)
self:_RemoveNameFromDownedPilots(_entry.name,true)
end end
end end
end end
self.downedPilots = npilots
return self return self
end end
@ -2041,9 +2007,10 @@ end
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot\'s group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname) -- @param #boolean IsAirport True if heli has landed on an AFB (from event land).
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort)
self:T({From, Event, To, Heliname, Woundedgroupname}) self:T({From, Event, To, Heliname, Woundedgroupname})
self:_ScheduledSARFlight(Heliname,Woundedgroupname) self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort)
return self return self
end end

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
--- **Core** - Makes the radio talk. --- **Core** - Makes the radio talk.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Send text strings using a vocabulary that is converted in spoken language. -- * Send text strings using a vocabulary that is converted in spoken language.
-- * Possiblity to implement multiple language. -- * Possiblity to implement multiple language.
-- --
@ -15,10 +15,10 @@
-- @image Core_Radio.JPG -- @image Core_Radio.JPG
--- Makes the radio speak. --- Makes the radio speak.
-- --
-- # RADIOSPEECH usage -- # RADIOSPEECH usage
-- --
-- --
-- @type RADIOSPEECH -- @type RADIOSPEECH
-- @extends Core.RadioQueue#RADIOQUEUE -- @extends Core.RadioQueue#RADIOQUEUE
RADIOSPEECH = { RADIOSPEECH = {
@ -59,24 +59,24 @@ RADIOSPEECH.Vocabulary.EN = {
["70"] = { "70", 0.48 }, ["70"] = { "70", 0.48 },
["80"] = { "80", 0.26 }, ["80"] = { "80", 0.26 },
["90"] = { "90", 0.36 }, ["90"] = { "90", 0.36 },
["100"] = { "100", 0.55 }, ["100"] = { "100", 0.55 },
["200"] = { "200", 0.55 }, ["200"] = { "200", 0.55 },
["300"] = { "300", 0.61 }, ["300"] = { "300", 0.61 },
["400"] = { "400", 0.60 }, ["400"] = { "400", 0.60 },
["500"] = { "500", 0.61 }, ["500"] = { "500", 0.61 },
["600"] = { "600", 0.65 }, ["600"] = { "600", 0.65 },
["700"] = { "700", 0.70 }, ["700"] = { "700", 0.70 },
["800"] = { "800", 0.54 }, ["800"] = { "800", 0.54 },
["900"] = { "900", 0.60 }, ["900"] = { "900", 0.60 },
["1000"] = { "1000", 0.60 }, ["1000"] = { "1000", 0.60 },
["2000"] = { "2000", 0.61 }, ["2000"] = { "2000", 0.61 },
["3000"] = { "3000", 0.64 }, ["3000"] = { "3000", 0.64 },
["4000"] = { "4000", 0.62 }, ["4000"] = { "4000", 0.62 },
["5000"] = { "5000", 0.69 }, ["5000"] = { "5000", 0.69 },
["6000"] = { "6000", 0.69 }, ["6000"] = { "6000", 0.69 },
["7000"] = { "7000", 0.75 }, ["7000"] = { "7000", 0.75 },
["8000"] = { "8000", 0.59 }, ["8000"] = { "8000", 0.59 },
["9000"] = { "9000", 0.65 }, ["9000"] = { "9000", 0.65 },
["chevy"] = { "chevy", 0.35 }, ["chevy"] = { "chevy", 0.35 },
["colt"] = { "colt", 0.35 }, ["colt"] = { "colt", 0.35 },
@ -94,10 +94,10 @@ RADIOSPEECH.Vocabulary.EN = {
["meters"] = { "meters", 0.41 }, ["meters"] = { "meters", 0.41 },
["mi"] = { "miles", 0.45 }, ["mi"] = { "miles", 0.45 },
["feet"] = { "feet", 0.29 }, ["feet"] = { "feet", 0.29 },
["br"] = { "br", 1.1 }, ["br"] = { "br", 1.1 },
["bra"] = { "bra", 0.3 }, ["bra"] = { "bra", 0.3 },
["returning to base"] = { "returning_to_base", 0.85 }, ["returning to base"] = { "returning_to_base", 0.85 },
["on route to ground target"] = { "on_route_to_ground_target", 1.05 }, ["on route to ground target"] = { "on_route_to_ground_target", 1.05 },
@ -143,52 +143,52 @@ RADIOSPEECH.Vocabulary.RU = {
["70"] = { "70", 0.68 }, ["70"] = { "70", 0.68 },
["80"] = { "80", 0.84 }, ["80"] = { "80", 0.84 },
["90"] = { "90", 0.71 }, ["90"] = { "90", 0.71 },
["100"] = { "100", 0.35 }, ["100"] = { "100", 0.35 },
["200"] = { "200", 0.59 }, ["200"] = { "200", 0.59 },
["300"] = { "300", 0.53 }, ["300"] = { "300", 0.53 },
["400"] = { "400", 0.70 }, ["400"] = { "400", 0.70 },
["500"] = { "500", 0.50 }, ["500"] = { "500", 0.50 },
["600"] = { "600", 0.58 }, ["600"] = { "600", 0.58 },
["700"] = { "700", 0.64 }, ["700"] = { "700", 0.64 },
["800"] = { "800", 0.77 }, ["800"] = { "800", 0.77 },
["900"] = { "900", 0.75 }, ["900"] = { "900", 0.75 },
["1000"] = { "1000", 0.87 }, ["1000"] = { "1000", 0.87 },
["2000"] = { "2000", 0.83 }, ["2000"] = { "2000", 0.83 },
["3000"] = { "3000", 0.84 }, ["3000"] = { "3000", 0.84 },
["4000"] = { "4000", 1.00 }, ["4000"] = { "4000", 1.00 },
["5000"] = { "5000", 0.77 }, ["5000"] = { "5000", 0.77 },
["6000"] = { "6000", 0.90 }, ["6000"] = { "6000", 0.90 },
["7000"] = { "7000", 0.77 }, ["7000"] = { "7000", 0.77 },
["8000"] = { "8000", 0.92 }, ["8000"] = { "8000", 0.92 },
["9000"] = { "9000", 0.87 }, ["9000"] = { "9000", 0.87 },
["Ñ<EFBFBD>Ñепени"] = { "degrees", 0.5 }, ["градусы"] = { "degrees", 0.5 },
["километров"] = { "kilometers", 0.65 }, ["километры"] = { "kilometers", 0.65 },
["km"] = { "kilometers", 0.65 }, ["km"] = { "kilometers", 0.65 },
["миль"] = { "miles", 0.45 }, ["мили"] = { "miles", 0.45 },
["mi"] = { "miles", 0.45 }, ["mi"] = { "miles", 0.45 },
["метры"] = { "meters", 0.41 }, ["метров"] = { "meters", 0.41 },
["m"] = { "meters", 0.41 }, ["m"] = { "meters", 0.41 },
["ноги"] = { "feet", 0.37 }, ["ноги"] = { "feet", 0.37 },
["br"] = { "br", 1.1 }, ["br"] = { "br", 1.1 },
["bra"] = { "bra", 0.3 }, ["bra"] = { "bra", 0.3 },
["возвращаÑ<EFBFBD>Ñ<EFBFBD>ÑŒ на базу"] = { "returning_to_base", 1.40 },
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
["перехват Ñ<>амолеÑов"] = { "intercepting_bogeys", 1.22 },
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
["захватывающие Ñ<>амолеÑÑ"] = { "engaging_bogeys", 1.68 },
["колеÑ<EFBFBD>а вверх"] = { "wheels_up", 0.92 },
["поÑ<EFBFBD>адка на базу"] = { "landing at base", 1.04 },
["патрулирующий"] = { "patrolling", 0.96 },
["за"] = { "for", 0.27 }, ["возвращение на базу"] = { "returning_to_base", 1.40 },
["и"] = { "and", 0.17 }, ["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
["в"] = { "at", 0.19 }, ["перехват боги"] = { "intercepting_bogeys", 1.22 },
["dot"] = { "dot", 0.51 }, ["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
["defender"] = { "defender", 0.45 }, ["привлечение болотных птиц"] = { "engaging_bogeys", 1.68 },
["колёса вверх..."] = { "wheels_up", 0.92 },
["посадка на базу"] = { "landing at base", 1.04 },
["патрулирование"] = { "patrolling", 0.96 },
["для"] = { "for", 0.27 },
["и"] = { "and", 0.17 },
["на сайте"] = { "at", 0.19 },
["точка"] = { "dot", 0.51 },
["защитник"] = { "defender", 0.45 },
} }
--- Create a new RADIOSPEECH object for a given radio frequency/modulation. --- Create a new RADIOSPEECH object for a given radio frequency/modulation.
@ -200,11 +200,11 @@ function RADIOSPEECH:New(frequency, modulation)
-- Inherit base -- Inherit base
local self = BASE:Inherit( self, RADIOQUEUE:New( frequency, modulation ) ) -- #RADIOSPEECH local self = BASE:Inherit( self, RADIOQUEUE:New( frequency, modulation ) ) -- #RADIOSPEECH
self.Language = "EN" self.Language = "EN"
self:BuildTree() self:BuildTree()
return self return self
end end
@ -262,7 +262,7 @@ end
function RADIOSPEECH:BuildTree() function RADIOSPEECH:BuildTree()
self.Speech = {} self.Speech = {}
for Language, Sentences in pairs( self.Vocabulary ) do for Language, Sentences in pairs( self.Vocabulary ) do
self:I( { Language = Language, Sentences = Sentences }) self:I( { Language = Language, Sentences = Sentences })
self.Speech[Language] = {} self.Speech[Language] = {}
@ -271,7 +271,7 @@ function RADIOSPEECH:BuildTree()
self:AddSentenceToSpeech( Sentence, self.Speech[Language], Sentence, Data ) self:AddSentenceToSpeech( Sentence, self.Speech[Language], Sentence, Data )
end end
end end
self:I( { Speech = self.Speech } ) self:I( { Speech = self.Speech } )
return self return self
@ -290,7 +290,7 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
local Word, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" ) local Word, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" )
self:I( { Word = Word, Speech = Speech[Word], RemainderSentence = RemainderSentence } ) self:I( { Word = Word, Speech = Speech[Word], RemainderSentence = RemainderSentence } )
if Word then if Word then
if Word ~= "" and tonumber(Word) == nil then if Word ~= "" and tonumber(Word) == nil then
@ -302,7 +302,7 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
if Speech[Word].Next == nil then if Speech[Word].Next == nil then
self:I( { Sentence = Speech[Word].Sentence, Data = Speech[Word].Data } ) self:I( { Sentence = Speech[Word].Sentence, Data = Speech[Word].Data } )
self:NewTransmission( Speech[Word].Data[1] .. ".wav", Speech[Word].Data[2], Language .. "/" ) self:NewTransmission( Speech[Word].Data[1] .. ".wav", Speech[Word].Data[2], Language .. "/" )
else else
if RemainderSentence and RemainderSentence ~= "" then if RemainderSentence and RemainderSentence ~= "" then
return self:SpeakWords( RemainderSentence, Speech[Word].Next, Language ) return self:SpeakWords( RemainderSentence, Speech[Word].Next, Language )
end end
@ -310,11 +310,11 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
end end
return RemainderSentence return RemainderSentence
end end
return OriginalSentence return OriginalSentence
else else
return "" return ""
end end
end end
--- Speak a sentence. --- Speak a sentence.
@ -333,7 +333,7 @@ function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
if Digits then if Digits then
if Digits ~= "" and tonumber( Digits ) ~= nil then if Digits ~= "" and tonumber( Digits ) ~= nil then
-- Construct numbers -- Construct numbers
local Number = tonumber( Digits ) local Number = tonumber( Digits )
local Multiple = nil local Multiple = nil
@ -357,7 +357,7 @@ function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
end end
return RemainderSentence return RemainderSentence
end end
return OriginalSentence return OriginalSentence
else else
return "" return ""
end end
@ -374,26 +374,26 @@ function RADIOSPEECH:Speak( Sentence, Language )
self:I( { Sentence, Language } ) self:I( { Sentence, Language } )
local Language = Language or "EN" local Language = Language or "EN"
self:I( { Language = Language } ) self:I( { Language = Language } )
-- If there is no node for Speech, then we start at the first nodes of the language. -- If there is no node for Speech, then we start at the first nodes of the language.
local Speech = self.Speech[Language] local Speech = self.Speech[Language]
self:I( { Speech = Speech, Language = Language } ) self:I( { Speech = Speech, Language = Language } )
self:NewTransmission( "_In.wav", 0.52, Language .. "/" ) self:NewTransmission( "_In.wav", 0.52, Language .. "/" )
repeat repeat
Sentence = self:SpeakWords( Sentence, Speech, Language ) Sentence = self:SpeakWords( Sentence, Speech, Language )
self:I( { Sentence = Sentence } ) self:I( { Sentence = Sentence } )
Sentence = self:SpeakDigits( Sentence, Speech, Language ) Sentence = self:SpeakDigits( Sentence, Speech, Language )
self:I( { Sentence = Sentence } ) self:I( { Sentence = Sentence } )
-- Sentence = self:SpeakSymbols( Sentence, Speech ) -- Sentence = self:SpeakSymbols( Sentence, Speech )
-- --
-- self:I( { Sentence = Sentence } ) -- self:I( { Sentence = Sentence } )

View File

@ -507,7 +507,7 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end end
-- 024<EFBFBD> 23' 12"N or 024<32> 23' 12.03"N -- 024° 23' 12"N or 024° 23' 12.03"N
return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' ' return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' '
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi .. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
@ -767,12 +767,12 @@ end
function UTILS.GetCharacters(str) function UTILS.GetCharacters(str)
local chars={} local chars={}
for i=1,#str do for i=1,#str do
local c=str:sub(i,i) local c=str:sub(i,i)
table.insert(chars, c) table.insert(chars, c)
end end
return chars return chars
end end
@ -923,7 +923,7 @@ function UTILS.RandomGaussian(x0, sigma, xmin, xmax, imax)
local x1=math.random() local x1=math.random()
local x2=math.random() local x2=math.random()
-- Transform to Gaussian exp(-(x-x0)²/(2*sigma²). -- Transform to Gaussian exp(-(x-x0)°/(2*sigma°).
r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0 r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0
i=i+1 i=i+1
@ -1441,7 +1441,7 @@ function UTILS.GMTToLocalTimeDifference()
elseif theatre==DCSMAP.Syria then elseif theatre==DCSMAP.Syria then
return 3 -- Damascus is UTC+3 hours return 3 -- Damascus is UTC+3 hours
elseif theatre==DCSMAP.MarianaIslands then elseif theatre==DCSMAP.MarianaIslands then
return 10 -- Guam is UTC+10 hours. return 10 -- Guam is UTC+10 hours.
else else
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre))) BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
return 0 return 0
@ -1615,7 +1615,7 @@ end
--@return #table --@return #table
function UTILS.ShuffleTable(t) function UTILS.ShuffleTable(t)
if t == nil or type(t) ~= "table" then if t == nil or type(t) ~= "table" then
BASE:I("Error in ShuffleTable: Missing or wrong tyåe of Argument") BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
return return
end end
math.random() math.random()
@ -1639,17 +1639,17 @@ function UTILS.IsLoadingDoorOpen( unit_name )
local unit = Unit.getByName(unit_name) local unit = Unit.getByName(unit_name)
if unit ~= nil then if unit ~= nil then
local type_name = unit:getTypeName() local type_name = unit:getTypeName()
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
BASE:T(unit_name .. " Cargo doors are open or cargo door not present") BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
ret_val = true ret_val = true
end end
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
BASE:T(unit_name .. " a side door is open") BASE:T(unit_name .. " a side door is open")
ret_val = true ret_val = true
end end
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
BASE:T(unit_name .. " a side door is open ") BASE:T(unit_name .. " a side door is open ")
ret_val = true ret_val = true
@ -1664,9 +1664,9 @@ function UTILS.IsLoadingDoorOpen( unit_name )
BASE:T(unit_name .. " all doors are closed") BASE:T(unit_name .. " all doors are closed")
end end
return ret_val return ret_val
end -- nil end -- nil
return nil return nil
end end
@ -1705,13 +1705,13 @@ function UTILS.GenerateVHFrequencies()
905,907,920,935,942,950,995, 905,907,920,935,942,950,995,
1000,1025,1030,1050,1065,1116,1175,1182,1210 1000,1025,1030,1050,1065,1116,1175,1182,1210
} }
local FreeVHFFrequencies = {} local FreeVHFFrequencies = {}
-- first range -- first range
local _start = 200000 local _start = 200000
while _start < 400000 do while _start < 400000 do
-- skip existing NDB frequencies# -- skip existing NDB frequencies#
local _found = false local _found = false
for _, value in pairs(_skipFrequencies) do for _, value in pairs(_skipFrequencies) do
@ -1725,7 +1725,7 @@ function UTILS.GenerateVHFrequencies()
end end
_start = _start + 10000 _start = _start + 10000
end end
-- second range -- second range
_start = 400000 _start = 400000
while _start < 850000 do while _start < 850000 do
@ -1742,7 +1742,7 @@ function UTILS.GenerateVHFrequencies()
end end
_start = _start + 10000 _start = _start + 10000
end end
-- third range -- third range
_start = 850000 _start = 850000
while _start <= 999000 do -- adjusted for Gazelle while _start <= 999000 do -- adjusted for Gazelle
@ -1782,7 +1782,7 @@ end
-- @return #table Laser Codes. -- @return #table Laser Codes.
function UTILS.GenerateLaserCodes() function UTILS.GenerateLaserCodes()
local jtacGeneratedLaserCodes = {} local jtacGeneratedLaserCodes = {}
-- helper function -- helper function
local function ContainsDigit(_number, _numberToFind) local function ContainsDigit(_number, _numberToFind)
local _thisNumber = _number local _thisNumber = _number
@ -1796,14 +1796,14 @@ function UTILS.GenerateLaserCodes()
end end
return false return false
end end
-- generate list of laser codes -- generate list of laser codes
local _code = 1111 local _code = 1111
local _count = 1 local _count = 1
while _code < 1777 and _count < 30 do while _code < 1777 and _count < 30 do
while true do while true do
_code = _code + 1 _code = _code + 1
if not self:_ContainsDigit(_code, 8) if not ContainsDigit(_code, 8)
and not ContainsDigit(_code, 9) and not ContainsDigit(_code, 9)
and not ContainsDigit(_code, 0) then and not ContainsDigit(_code, 0) then
table.insert(jtacGeneratedLaserCodes, _code) table.insert(jtacGeneratedLaserCodes, _code)
@ -1813,4 +1813,4 @@ function UTILS.GenerateLaserCodes()
_count = _count + 1 _count = _count + 1
end end
return jtacGeneratedLaserCodes return jtacGeneratedLaserCodes
end end

View File

@ -142,7 +142,7 @@ AIRBASE.Caucasus = {
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip -- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
-- * AIRBASE.Nevada.Tonopah_Airport -- * AIRBASE.Nevada.Tonopah_Airport
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield -- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
-- --
-- @field Nevada -- @field Nevada
AIRBASE.Nevada = { AIRBASE.Nevada = {
["Creech_AFB"] = "Creech AFB", ["Creech_AFB"] = "Creech AFB",
@ -197,7 +197,7 @@ AIRBASE.Nevada = {
-- * AIRBASE.Normandy.Funtington -- * AIRBASE.Normandy.Funtington
-- * AIRBASE.Normandy.Tangmere -- * AIRBASE.Normandy.Tangmere
-- * AIRBASE.Normandy.Ford_AF -- * AIRBASE.Normandy.Ford_AF
-- --
-- @field Normandy -- @field Normandy
AIRBASE.Normandy = { AIRBASE.Normandy = {
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont", ["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
@ -271,7 +271,7 @@ AIRBASE.Normandy = {
-- * AIRBASE.PersianGulf.Sirri_Island -- * AIRBASE.PersianGulf.Sirri_Island
-- * AIRBASE.PersianGulf.Tunb_Island_AFB -- * AIRBASE.PersianGulf.Tunb_Island_AFB
-- * AIRBASE.PersianGulf.Tunb_Kochak -- * AIRBASE.PersianGulf.Tunb_Kochak
-- --
-- @field PersianGulf -- @field PersianGulf
AIRBASE.PersianGulf = { AIRBASE.PersianGulf = {
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl", ["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
@ -554,7 +554,7 @@ function AIRBASE:Register(AirbaseName)
-- Get descriptors. -- Get descriptors.
self.descriptors=self:GetDesc() self.descriptors=self:GetDesc()
-- Debug info. -- Debug info.
--self:I({airbase=AirbaseName, descriptors=self.descriptors}) --self:I({airbase=AirbaseName, descriptors=self.descriptors})
@ -1134,7 +1134,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
-- Get airbase name. -- Get airbase name.
local airbasename=self:GetName() local airbasename=self:GetName()
self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype))) self:E(string.format("Parking spots at %s for terminal type %s:", airbasename, tostring(termtype)))
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
@ -1211,14 +1211,25 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype) parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
-- Get the aircraft size, i.e. it's longest side of x,z. -- Get the aircraft size, i.e. it's longest side of x,z.
local aircraft=group:GetUnit(1) local aircraft = nil
local _aircraftsize, ax,ay,az=aircraft:GetObjectSize() local _aircraftsize, ax,ay,az
if group and group.ClassName == "GROUP" then
aircraft=group:GetUnit(1)
_aircraftsize, ax,ay,az=aircraft:GetObjectSize()
else
-- SU27 dimensions
_aircraftsize = 23
ax = 23 -- length
ay = 7 -- height
az = 17 -- width
end
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size! -- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
local _nspots=nspots or group:GetSize() local _nspots=nspots or group:GetSize()
-- Debug info. -- Debug info.
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype))) self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
-- Table of valid spots. -- Table of valid spots.
local validspots={} local validspots={}
@ -1341,6 +1352,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
-- Retrun spots we found, even if there were not enough. -- Retrun spots we found, even if there were not enough.
return validspots return validspots
end end
--- Check black and white lists. --- Check black and white lists.
@ -1359,7 +1371,7 @@ function AIRBASE:_CheckParkingLists(TerminalID)
end end
end end
-- Check if a whitelist was defined. -- Check if a whitelist was defined.
if self.parkingWhitelist and #self.parkingWhitelist>0 then if self.parkingWhitelist and #self.parkingWhitelist>0 then
for _,terminalID in pairs(self.parkingWhitelist or {}) do for _,terminalID in pairs(self.parkingWhitelist or {}) do
@ -1469,7 +1481,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
name==AIRBASE.PersianGulf.Dubai_Intl or name==AIRBASE.PersianGulf.Dubai_Intl or
name==AIRBASE.PersianGulf.Shiraz_International_Airport or name==AIRBASE.PersianGulf.Shiraz_International_Airport or
name==AIRBASE.PersianGulf.Kish_International_Airport or name==AIRBASE.PersianGulf.Kish_International_Airport or
name==AIRBASE.MarianaIslands.Andersen_AFB then name==AIRBASE.MarianaIslands.Andersen_AFB then
-- 1-->4, 2-->3, 3-->2, 4-->1 -- 1-->4, 2-->3, 3-->2, 4-->1

View File

@ -1851,27 +1851,27 @@ do -- Patrol methods
-- Calculate the new Route. -- Calculate the new Route.
local FromCoord = PatrolGroup:GetCoordinate() local FromCoord = PatrolGroup:GetCoordinate()
-- test for submarine -- test for submarine
local depth = 0 local depth = 0
local IsSub = false local IsSub = false
if PatrolGroup:IsShip() then if PatrolGroup:IsShip() then
local navalvec3 = FromCoord:GetVec3() local navalvec3 = FromCoord:GetVec3()
if navalvec3.y < 0 then if navalvec3.y < 0 then
depth = navalvec3.y depth = navalvec3.y
IsSub = true IsSub = true
end end
end end
local Waypoint = Waypoints[1] local Waypoint = Waypoints[1]
local Speed = Waypoint.speed or (20 / 3.6) local Speed = Waypoint.speed or (20 / 3.6)
local From = FromCoord:WaypointGround( Speed ) local From = FromCoord:WaypointGround( Speed )
if IsSub then if IsSub then
From = FromCoord:WaypointNaval( Speed, Waypoint.alt ) From = FromCoord:WaypointNaval( Speed, Waypoint.alt )
end end
table.insert( Waypoints, 1, From ) table.insert( Waypoints, 1, From )
local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" ) local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" )
@ -1916,7 +1916,7 @@ do -- Patrol methods
local IsSub = false local IsSub = false
if PatrolGroup:IsShip() then if PatrolGroup:IsShip() then
local navalvec3 = FromCoord:GetVec3() local navalvec3 = FromCoord:GetVec3()
if navalvec3.y < 0 then if navalvec3.y < 0 then
depth = navalvec3.y depth = navalvec3.y
IsSub = true IsSub = true
end end
@ -1982,16 +1982,16 @@ do -- Patrol methods
self:F( { PatrolGroup = PatrolGroup:GetName() } ) self:F( { PatrolGroup = PatrolGroup:GetName() } )
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
-- Calculate the new Route. -- Calculate the new Route.
local FromCoord = PatrolGroup:GetCoordinate() local FromCoord = PatrolGroup:GetCoordinate()
-- test for submarine -- test for submarine
local depth = 0 local depth = 0
local IsSub = false local IsSub = false
if PatrolGroup:IsShip() then if PatrolGroup:IsShip() then
local navalvec3 = FromCoord:GetVec3() local navalvec3 = FromCoord:GetVec3()
if navalvec3.y < 0 then if navalvec3.y < 0 then
depth = navalvec3.y depth = navalvec3.y
IsSub = true IsSub = true
end end
@ -3773,12 +3773,13 @@ end
--- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving. --- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number speed Speed of the controllable, default 20 -- @param #number speed Speed of the controllable, default 20
-- @param #number radius Radius of the relocation zone, default 500 -- @param #number radius Radius of the relocation zone, default 500
-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true -- @param #boolean onroad If true, route on road (less problems with AI way finding), default true
-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false -- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
-- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road"
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut) function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut, formation)
self:F2( { self.ControllableName } ) self:F2( { self.ControllableName } )
local _coord = self:GetCoordinate() local _coord = self:GetCoordinate()
@ -3789,14 +3790,14 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc
local _grptsk = {} local _grptsk = {}
local _candoroad = false local _candoroad = false
local _shortcut = shortcut or false local _shortcut = shortcut or false
local _formation = formation or "Off Road"
-- create a DCS Task an push it on the group -- create a DCS Task an push it on the group
-- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments)
if onroad then if onroad then
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut) _grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut)
self:Route(_grptsk,5) self:Route(_grptsk,5)
else else
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation)
end end
return self return self

View File

@ -1132,7 +1132,7 @@ end
-- @return #number Number of shells left. -- @return #number Number of shells left.
-- @return #number Number of rockets left. -- @return #number Number of rockets left.
-- @return #number Number of bombs left. -- @return #number Number of bombs left.
-- @return #number Number of missiles left. -- @return #number Number of missiles left.
function GROUP:GetAmmunition() function GROUP:GetAmmunition()
self:F( self.ControllableName ) self:F( self.ControllableName )
@ -1142,6 +1142,7 @@ function GROUP:GetAmmunition()
local Nshells=0 local Nshells=0
local Nrockets=0 local Nrockets=0
local Nmissiles=0 local Nmissiles=0
local Nbombs=0
if DCSControllable then if DCSControllable then
@ -1150,18 +1151,19 @@ function GROUP:GetAmmunition()
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
-- Get ammo of the unit -- Get ammo of the unit
local ntot, nshells, nrockets, nmissiles = Unit:GetAmmunition() local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition()
Ntot=Ntot+ntot Ntot=Ntot+ntot
Nshells=Nshells+nshells Nshells=Nshells+nshells
Nrockets=Nrockets+nrockets Nrockets=Nrockets+nrockets
Nmissiles=Nmissiles+nmissiles Nmissiles=Nmissiles+nmissiles
Nbombs=Nbombs+nbombs
end end
end end
return Ntot, Nshells, Nrockets, Nmissiles return Ntot, Nshells, Nrockets, Nbombs, Nmissiles
end end
@ -2589,6 +2591,17 @@ function GROUP:SetCommandImmortal(switch)
return self return self
end end
--- Get skill from Group. Effectively gets the skill from Unit 1 as the group holds no skill value.
-- @param #GROUP self
-- @return #string Skill String of skill name.
function GROUP:GetSkill()
self:F2( self.GroupName )
local unit = self:GetUnit(1)
local name = unit:GetName()
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
return skill
end
--do -- Smoke --do -- Smoke
-- --
----- Signal a flare at the position of the GROUP. ----- Signal a flare at the position of the GROUP.

View File

@ -1517,6 +1517,7 @@ do -- Cargo
["Ural-4320 APA-5D"] = 10, ["Ural-4320 APA-5D"] = 10,
["Ural-4320T"] = 14, ["Ural-4320T"] = 14,
["ZBD04A"] = 7, -- new by kappa ["ZBD04A"] = 7, -- new by kappa
["VAB_Mephisto"] = 8, -- new by Apple
} }
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95

View File

@ -637,6 +637,18 @@ function UNIT:GetAmmo()
return nil return nil
end end
--- Sets the Unit's Internal Cargo Mass, in kg
-- @param #UNIT self
-- @param #number mass to set cargo to
-- @return #UNIT self
function UNIT:SetUnitInternalCargo(mass)
local DCSUnit = self:GetDCSObject()
if DCSUnit then
trigger.action.setUnitInternalCargo(DCSUnit:getName(), mass)
end
return self
end
--- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has. --- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has.
-- @param #UNIT self -- @param #UNIT self
-- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles. -- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles.
@ -1471,3 +1483,13 @@ function UNIT:EnableEmission(switch)
return self return self
end end
--- Get skill from Unit.
-- @param #UNIT self
-- @return #string Skill String of skill name.
function UNIT:GetSkill()
self:F2( self.UnitName )
local name = self.UnitName
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
return skill
end