mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Merge branch 'FF/Ops' into FF/OpsDev
This commit is contained in:
commit
3e30d15405
@ -3477,7 +3477,7 @@ do -- AI_A2A_DISPATCHER
|
||||
if Squadron.Language == "EN" and self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " wheels up.", DefenderGroup )
|
||||
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " колеÑ<EFBFBD>а вверх.", DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " колёса вверх.", DefenderGroup )
|
||||
end
|
||||
--Fsm:__Engage( 2, 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
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", intercepting bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
|
||||
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
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", Eindringlinge abfangen bei" .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
|
||||
end
|
||||
@ -3520,7 +3520,7 @@ do -- AI_A2A_DISPATCHER
|
||||
if Squadron.Language == "EN" and self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
|
||||
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
|
||||
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
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup )
|
||||
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", возвращаÑ<EFBFBD>Ñ<EFBFBD>ÑŒ на базу.", DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", возвращение на базу.", DefenderGroup )
|
||||
end
|
||||
end
|
||||
Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
|
||||
@ -3569,7 +3569,7 @@ do -- AI_A2A_DISPATCHER
|
||||
if Squadron.Language == "EN" and self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " landing at base.", DefenderGroup )
|
||||
elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", захватывающие Ñ<>амолеты в поÑ<C2BE>адка на базу.", DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", посадка на базу.", DefenderGroup )
|
||||
end
|
||||
|
||||
if Action and Action == "Destroy" then
|
||||
|
||||
@ -47,19 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
-- Board
|
||||
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" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Loaded", "Board", "Loaded" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
||||
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
||||
-- Unload
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unboard", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnBeforePickup
|
||||
@ -393,7 +395,7 @@ end
|
||||
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
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() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
|
||||
@ -509,7 +511,7 @@ end
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, 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
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
return
|
||||
@ -580,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
|
||||
self:AddTransition( "*", "Guard", "Unloaded" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
self:AddTransition( "*", "Reload", "Boarding" )
|
||||
|
||||
self:AddTransition( "*", "Deployed", "*" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
self:SetCombatRadius( CombatRadius )
|
||||
|
||||
@ -174,8 +174,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZone
|
||||
self:SetPickupSpeed( 350, 150 )
|
||||
self:SetDeploySpeed( 350, 150 )
|
||||
|
||||
self:SetPickupRadius( 0, 0 )
|
||||
self:SetDeployRadius( 0, 0 )
|
||||
self:SetPickupRadius( 40, 12 )
|
||||
self:SetDeployRadius( 40, 12 )
|
||||
|
||||
self:SetPickupHeight( 500, 200 )
|
||||
self:SetDeployHeight( 500, 200 )
|
||||
@ -186,6 +186,9 @@ end
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -64,20 +64,24 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
||||
|
||||
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", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
-- Unboarding
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
self:AddTransition( "*", "Destroyed", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
-- RTB
|
||||
self:AddTransition( "*", "Home" , "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
||||
@ -207,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
self:SetCarrier( Helicopter )
|
||||
|
||||
self.landingspeed = 15 -- kph
|
||||
self.landingheight = 5.5 -- meter
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -255,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
|
||||
return self
|
||||
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 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.
|
||||
-- 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!
|
||||
-- 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)!
|
||||
|
||||
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 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( self.PickupZone )
|
||||
self.RoutePickup = false
|
||||
@ -285,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
end
|
||||
|
||||
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.RouteDeploy = false
|
||||
end
|
||||
|
||||
@ -300,23 +300,23 @@ do -- Zones
|
||||
|
||||
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
|
||||
local ZoneName = ZoneData.name
|
||||
|
||||
|
||||
-- Color
|
||||
local color=ZoneData.color or {1, 0, 0, 0.15}
|
||||
|
||||
|
||||
-- Create new Zone
|
||||
local Zone=nil --Core.Zone#ZONE_BASE
|
||||
|
||||
|
||||
if ZoneData.type==0 then
|
||||
|
||||
|
||||
---
|
||||
-- Circular zone
|
||||
---
|
||||
|
||||
|
||||
self:I(string.format("Register ZONE: %s (Circular)", ZoneName))
|
||||
|
||||
|
||||
Zone=ZONE:New(ZoneName)
|
||||
|
||||
|
||||
else
|
||||
|
||||
---
|
||||
@ -324,51 +324,51 @@ do -- Zones
|
||||
---
|
||||
|
||||
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
|
||||
|
||||
|
||||
Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies)
|
||||
|
||||
|
||||
--for i,vec2 in pairs(ZoneData.verticies) do
|
||||
-- local coord=COORDINATE:NewFromVec2(vec2)
|
||||
-- coord:MarkToAll(string.format("%s Point %d", ZoneName, i))
|
||||
--end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
if Zone then
|
||||
|
||||
-- Store color of zone.
|
||||
-- Store color of zone.
|
||||
Zone.Color=color
|
||||
|
||||
|
||||
-- Store in DB.
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
|
||||
|
||||
-- Add zone.
|
||||
self:AddZone(ZoneName, Zone)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- Polygon zones defined by late activated groups.
|
||||
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
||||
if ZoneGroupName:match("#ZONE_POLYGON") then
|
||||
|
||||
|
||||
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
||||
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
||||
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Register ZONE: %s (Polygon)", ZoneName))
|
||||
|
||||
|
||||
-- Create a new polygon zone.
|
||||
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||
|
||||
|
||||
-- Set color.
|
||||
Zone_Polygon:SetColor({1, 0, 0}, 0.15)
|
||||
|
||||
|
||||
-- Store name in DB.
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
|
||||
|
||||
-- Add zone to DB.
|
||||
self:AddZone( ZoneName, Zone_Polygon )
|
||||
end
|
||||
|
||||
@ -1881,7 +1881,7 @@ do -- COORDINATE
|
||||
|
||||
--- Big smoke and fire at the coordinate.
|
||||
-- @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.
|
||||
function COORDINATE:BigSmokeAndFire( preset, density )
|
||||
self:F2( { preset=preset, density=density } )
|
||||
|
||||
@ -4042,7 +4042,25 @@ do -- SET_CLIENT
|
||||
|
||||
return self
|
||||
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 Wrapper.Client#CLIENT MClient
|
||||
@ -4790,7 +4808,7 @@ do -- SET_AIRBASE
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
@ -138,24 +138,24 @@ SPAWNSTATIC = {
|
||||
-- @return #SPAWNSTATIC self
|
||||
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)
|
||||
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates the main object to spawn a @{Static} given a template table.
|
||||
@ -422,7 +422,11 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
end
|
||||
|
||||
if self.InitCargo~=nil then
|
||||
Template.isCargo=self.InitCargo
|
||||
Template.canCargo=self.InitCargo
|
||||
end
|
||||
|
||||
if self.InitCargoMass~=nil then
|
||||
Template.mass=self.InitCargoMass
|
||||
end
|
||||
|
||||
if self.InitLinkUnit then
|
||||
|
||||
@ -183,12 +183,12 @@ function ZONE_BASE:IsCoordinateInZone( Coordinate )
|
||||
return InZone
|
||||
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 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.
|
||||
function ZONE_BASE:IsPointVec2InZone( PointVec2 )
|
||||
local InZone = self:IsVec2InZone( PointVec2:GetVec2() )
|
||||
function ZONE_BASE:IsPointVec2InZone( Coordinate )
|
||||
local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
|
||||
return InZone
|
||||
end
|
||||
|
||||
@ -515,8 +515,8 @@ end
|
||||
--
|
||||
-- @field #ZONE_RADIUS
|
||||
ZONE_RADIUS = {
|
||||
ClassName="ZONE_RADIUS",
|
||||
}
|
||||
ClassName="ZONE_RADIUS",
|
||||
}
|
||||
|
||||
--- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius.
|
||||
-- @param #ZONE_RADIUS self
|
||||
@ -527,15 +527,15 @@ ZONE_RADIUS = {
|
||||
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
||||
|
||||
-- Inherit ZONE_BASE.
|
||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
|
||||
self:F( { ZoneName, Vec2, Radius } )
|
||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
|
||||
self:F( { ZoneName, Vec2, Radius } )
|
||||
|
||||
self.Radius = Radius
|
||||
self.Vec2 = Vec2
|
||||
self.Radius = Radius
|
||||
self.Vec2 = Vec2
|
||||
|
||||
--self.Coordinate=COORDINATE:NewFromVec2(Vec2)
|
||||
--self.Coordinate=COORDINATE:NewFromVec2(Vec2)
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update zone from a 2D vector.
|
||||
@ -763,11 +763,11 @@ end
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return DCS#Vec2 The location of the zone.
|
||||
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
|
||||
|
||||
--- Sets the @{DCS#Vec2} of the zone.
|
||||
|
||||
@ -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.
|
||||
-- @param #FOX self
|
||||
-- @param DCS#Weapon weapon The weapon.
|
||||
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD>
|
||||
-- @return #number Notching heading left, 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°.
|
||||
function FOX:_GetNotchingHeadings(weapon)
|
||||
|
||||
if weapon then
|
||||
|
||||
@ -1,31 +1,31 @@
|
||||
--- **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
|
||||
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy
|
||||
-- Leverage evasiveness from SEAD
|
||||
-- Leverage attack range setup added by DCS in 11/20
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## 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)
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author : **applevangelist **
|
||||
--
|
||||
--
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
|
||||
-- Date: July 2021
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends #Core.Base#BASE
|
||||
--- **MANTIS** class, extends Core.Base#BASE
|
||||
-- @type MANTIS
|
||||
-- @field #string Classname
|
||||
-- @field #string ClassName
|
||||
-- @field #string name Name of this Mantis
|
||||
-- @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
|
||||
@ -59,10 +59,10 @@
|
||||
-- @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
|
||||
--
|
||||
--
|
||||
-- #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.
|
||||
@ -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 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.
|
||||
--
|
||||
--
|
||||
-- # 1. Basic tactical considerations when setting up your SAM sites
|
||||
--
|
||||
--
|
||||
-- ## 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.
|
||||
-- **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
|
||||
-- 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
|
||||
--
|
||||
-- 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,
|
||||
--
|
||||
-- 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,
|
||||
-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage.
|
||||
--
|
||||
--
|
||||
-- ## 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
|
||||
--
|
||||
-- * bad placement of radar units,
|
||||
-- * 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.
|
||||
--
|
||||
--
|
||||
-- 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,
|
||||
-- * 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.
|
||||
--
|
||||
-- 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**.
|
||||
--
|
||||
--
|
||||
-- # 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()`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
-- * `MANTIS:SetDetectInterval(interval)`
|
||||
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
--
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
-- `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()`
|
||||
--
|
||||
-- # 3. Default settings
|
||||
--
|
||||
--
|
||||
-- By default, the following settings are active:
|
||||
--
|
||||
-- * 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
|
||||
-- * 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)`
|
||||
-- * 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)`
|
||||
-- * 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)`
|
||||
@ -135,28 +135,28 @@
|
||||
-- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)`
|
||||
--
|
||||
-- # 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.
|
||||
--
|
||||
-- E.g. `mymantis:SetAdvancedMode( true, 90 )`
|
||||
--
|
||||
-- Use this option if you want to make use of or allow advanced SEAD tactics.
|
||||
--
|
||||
--
|
||||
-- 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 )`
|
||||
--
|
||||
-- Use this option if you want to make use of or allow advanced SEAD tactics.
|
||||
--
|
||||
-- # 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
|
||||
-- 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()`
|
||||
-- `myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")`
|
||||
-- `-- now set up MANTIS`
|
||||
-- `mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
|
||||
-- `mymantis:AddShorad(myshorad,720)`
|
||||
-- `mymantis:Start()`
|
||||
--
|
||||
--
|
||||
-- and (optionally) remove the link later on with
|
||||
--
|
||||
-- `mymantis:RemoveShorad()`
|
||||
--
|
||||
-- `mymantis:RemoveShorad()`
|
||||
--
|
||||
-- @field #MANTIS
|
||||
MANTIS = {
|
||||
@ -197,6 +197,7 @@ MANTIS = {
|
||||
SamStateTracker = {},
|
||||
DLink = false,
|
||||
DLTimeStamp = 0,
|
||||
Padding = 10,
|
||||
}
|
||||
|
||||
--- Advanced state enumerator
|
||||
@ -221,30 +222,31 @@ do
|
||||
--@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 #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
|
||||
--@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()`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
-- * `MANTIS:SetDetectInterval(interval)`
|
||||
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
--
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
-- `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()`
|
||||
--
|
||||
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: Make HQ useful
|
||||
-- DONE: Set SAMs to auto if EWR dies
|
||||
@ -267,7 +269,7 @@ do
|
||||
self.autorelocateunits = { HQ = false, EWR = false}
|
||||
self.advanced = false
|
||||
self.adv_ratio = 100
|
||||
self.adv_state = 0
|
||||
self.adv_state = 0
|
||||
self.verbose = false
|
||||
self.Adv_EWR_Group = nil
|
||||
self.AWACS_Prefix = awacs or nil
|
||||
@ -281,27 +283,28 @@ do
|
||||
self.state2flag = false
|
||||
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
|
||||
self.DLink = false
|
||||
|
||||
self.Padding = Padding or 10
|
||||
|
||||
if EmOnOff then
|
||||
if EmOnOff == false then
|
||||
self.UseEmOnOff = false
|
||||
else
|
||||
self.UseEmOnOff = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if type(awacs) == "string" then
|
||||
self.advAwacs = true
|
||||
else
|
||||
self.advAwacs = false
|
||||
end
|
||||
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
local self = BASE:Inherit(self, FSM:New()) -- #MANTIS
|
||||
|
||||
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("MANTIS %s | ", self.name)
|
||||
|
||||
|
||||
-- Debug trace.
|
||||
if self.debug then
|
||||
BASE:TraceOnOff(true)
|
||||
@ -309,7 +312,7 @@ do
|
||||
--BASE:TraceClass("SEAD")
|
||||
BASE:TraceLevel(1)
|
||||
end
|
||||
|
||||
|
||||
if self.dynamic then
|
||||
-- Set SAM SET_GROUP
|
||||
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
||||
@ -321,18 +324,18 @@ do
|
||||
-- Set EWR SET_GROUP
|
||||
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
end
|
||||
|
||||
|
||||
-- set up CC
|
||||
if self.HQ_Template_CC then
|
||||
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
|
||||
end
|
||||
|
||||
|
||||
-- @field #string version
|
||||
self.version="0.6.2"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
|
||||
--- FSM Functions ---
|
||||
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Stopped")
|
||||
|
||||
@ -346,11 +349,11 @@ do
|
||||
self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change.
|
||||
self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
------------------------
|
||||
|
||||
|
||||
--- Triggers the FSM event "Start". Starts the MANTIS. Initializes parameters and starts event handlers.
|
||||
-- @function [parent=#MANTIS] Start
|
||||
-- @param #MANTIS self
|
||||
@ -376,7 +379,7 @@ do
|
||||
-- @function [parent=#MANTIS] __Status
|
||||
-- @param #MANTIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- On After "Relocating" event. HQ and/or EWR moved.
|
||||
-- @function [parent=#MANTIS] OnAfterRelocating
|
||||
-- @param #MANTIS self
|
||||
@ -384,7 +387,7 @@ do
|
||||
-- @param #string Event The Event
|
||||
-- @param #string To The To State
|
||||
-- @return #MANTIS self
|
||||
|
||||
|
||||
--- On After "GreenState" event. A SAM group was switched to GREEN alert.
|
||||
-- @function [parent=#MANTIS] OnAfterGreenState
|
||||
-- @param #MANTIS self
|
||||
@ -393,7 +396,7 @@ do
|
||||
-- @param #string To The To State
|
||||
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
||||
-- @return #MANTIS self
|
||||
|
||||
|
||||
--- On After "RedState" event. A SAM group was switched to RED alert.
|
||||
-- @function [parent=#MANTIS] OnAfterRedState
|
||||
-- @param #MANTIS self
|
||||
@ -402,7 +405,7 @@ do
|
||||
-- @param #string To The To State
|
||||
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
||||
-- @return #MANTIS self
|
||||
|
||||
|
||||
--- On After "AdvStateChange" event. Advanced state changed, influencing detection speed.
|
||||
-- @function [parent=#MANTIS] OnAfterAdvStateChange
|
||||
-- @param #MANTIS self
|
||||
@ -413,7 +416,7 @@ do
|
||||
-- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red
|
||||
-- @param #number Interval Calculated detection interval based on state and advanced feature setting
|
||||
-- @return #MANTIS self
|
||||
|
||||
|
||||
--- On After "ShoradActivated" event. Mantis has activated a SHORAD.
|
||||
-- @function [parent=#MANTIS] OnAfterShoradActivated
|
||||
-- @param #MANTIS self
|
||||
@ -424,21 +427,21 @@ do
|
||||
-- @param #number Radius Radius around the named group to find SHORAD groups
|
||||
-- @param #number Ontime Seconds the SHORAD will stay active
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS helper functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- [Internal] Function to get the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #table table
|
||||
-- @return #table table
|
||||
function MANTIS:_GetSAMTable()
|
||||
self:T(self.lid .. "GetSAMTable")
|
||||
return self.SAM_Table
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to set the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
@ -447,7 +450,7 @@ do
|
||||
self.SAM_Table = table
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to set the grouping radius of the detection in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius upon which detected objects will be grouped
|
||||
@ -467,17 +470,17 @@ do
|
||||
self.acceptrange = radius
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to set switch-on/off zone for the SAM sites in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius of the firing zone
|
||||
-- @param #number radius Radius of the firing zone
|
||||
function MANTIS:SetSAMRadius(radius)
|
||||
self:T(self.lid .. "SetSAMRadius")
|
||||
local radius = radius or 25000
|
||||
self.checkradius = radius
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Percent of the max fire range
|
||||
@ -490,7 +493,7 @@ do
|
||||
self.engagerange = range
|
||||
return self
|
||||
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
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Percent of the max fire range
|
||||
@ -505,7 +508,7 @@ do
|
||||
self.mysead.EngagementRange = range
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to set switch-on/off the debug state
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean onoff Set true to switch on
|
||||
@ -523,7 +526,7 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to get the HQ object for further use
|
||||
-- @param #MANTIS self
|
||||
-- @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
|
||||
return self.HQ_CC
|
||||
else
|
||||
return nil
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Function to set separate AWACS detection instance
|
||||
-- @param #MANTIS self
|
||||
-- @param #string prefix Name of the AWACS group in the mission editor
|
||||
@ -559,7 +562,7 @@ do
|
||||
self.awacsrange = range
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to set the HQ object for further use
|
||||
-- @param #MANTIS self
|
||||
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
|
||||
@ -577,7 +580,7 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to set the detection interval
|
||||
-- @param #MANTIS self
|
||||
-- @param #number interval The interval in seconds
|
||||
@ -586,8 +589,8 @@ do
|
||||
local interval = interval or 30
|
||||
self.detectinterval = interval
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Function to set Advanded Mode
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean onoff If true, will activate Advanced Mode
|
||||
@ -596,7 +599,7 @@ do
|
||||
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
|
||||
function MANTIS:SetAdvancedMode(onoff, ratio)
|
||||
self:T(self.lid .. "SetAdvancedMode")
|
||||
--self.T({onoff, ratio})
|
||||
--self:T({onoff, ratio})
|
||||
local onoff = onoff or false
|
||||
local ratio = ratio or 100
|
||||
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
|
||||
@ -612,7 +615,7 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set using Emissions on/off instead of changing alarm state
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean switch Decide if we are changing alarm state or Emission state
|
||||
@ -621,7 +624,7 @@ do
|
||||
self.UseEmOnOff = switch or false
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set using an #INTEL_DLINK object instead of #DETECTION
|
||||
-- @param #MANTIS self
|
||||
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
|
||||
@ -632,7 +635,7 @@ do
|
||||
self.DLTimeStamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to check if HQ is alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean True if HQ is alive, else false
|
||||
@ -647,15 +650,15 @@ do
|
||||
local hqgrp = GROUP:FindByName(hq)
|
||||
if hqgrp then
|
||||
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
|
||||
else
|
||||
--self.T(self.lid.." HQ is dead!")
|
||||
return false
|
||||
--self:T(self.lid.." HQ is dead!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Function to check if EWR is (at least partially) alive
|
||||
@ -664,7 +667,7 @@ do
|
||||
function MANTIS:_CheckEWRState()
|
||||
self:T(self.lid .. "CheckEWRState")
|
||||
local text = self.lid.." Checking EWR State"
|
||||
--self.T(text)
|
||||
--self:T(text)
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
-- start check
|
||||
@ -680,14 +683,14 @@ do
|
||||
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
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- [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
|
||||
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)
|
||||
--self.T(text)
|
||||
--self:T(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
end
|
||||
return newinterval, currstate
|
||||
end
|
||||
|
||||
|
||||
--- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS!
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean hq If true, will relocate HQ object
|
||||
-- @param #boolean ewr If true, will relocate EWR objects
|
||||
function MANTIS:SetAutoRelocate(hq, ewr)
|
||||
self:T(self.lid .. "SetAutoRelocate")
|
||||
--self.T({hq, ewr})
|
||||
--self:T({hq, ewr})
|
||||
local hqrel = hq or false
|
||||
local ewrel = ewr or false
|
||||
if hqrel or ewrel then
|
||||
self.autorelocate = true
|
||||
self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
|
||||
--self.T({self.autorelocate, self.autorelocateunits})
|
||||
--self:T({self.autorelocate, self.autorelocateunits})
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- [Internal] Function to execute the relocation
|
||||
-- @param #MANTIS self
|
||||
function MANTIS:_RelocateGroups()
|
||||
@ -753,7 +756,7 @@ do
|
||||
local HQGroup = self.HQ_CC
|
||||
if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists
|
||||
local _hqgrp = self.HQ_CC
|
||||
--self.T(self.lid.." Relocating HQ")
|
||||
--self:T(self.lid.." Relocating HQ")
|
||||
local text = self.lid.." Relocating HQ"
|
||||
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
|
||||
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||
@ -766,7 +769,7 @@ do
|
||||
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
|
||||
for _,_grp in pairs (EWR_Grps) do
|
||||
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 m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
@ -777,7 +780,7 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to check if any object is in the given SAM zone
|
||||
-- @param #MANTIS self
|
||||
-- @param #table dectset Table of coordinates of detected items
|
||||
@ -793,12 +796,12 @@ do
|
||||
local coord = _coord -- get current coord to check
|
||||
-- output for cross-check
|
||||
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
|
||||
if self.verbose or self.debug then
|
||||
if self.verbose or self.debug then
|
||||
local dectstring = coord:ToStringLLDMS()
|
||||
local samstring = samcoordinate:ToStringLLDMS()
|
||||
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)
|
||||
self:I(self.lid..text)
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
-- end output to cross-check
|
||||
if targetdistance <= radius then
|
||||
@ -813,20 +816,20 @@ do
|
||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||
function MANTIS:StartDetection()
|
||||
self:T(self.lid.."Starting Detection")
|
||||
|
||||
|
||||
-- start detection
|
||||
local groupset = self.EWR_Group
|
||||
local grouping = self.grouping or 5000
|
||||
local acceptrange = self.acceptrange or 80000
|
||||
local interval = self.detectinterval or 60
|
||||
|
||||
|
||||
--@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
|
||||
MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||
MANTISdetection:SetAcceptRange(acceptrange)
|
||||
MANTISdetection:SetRefreshTimeInterval(interval)
|
||||
MANTISdetection:Start()
|
||||
|
||||
|
||||
function MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||
--BASE:I( { From, Event, To, DetectedItem })
|
||||
local debug = false
|
||||
@ -835,30 +838,30 @@ do
|
||||
local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS()
|
||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
end
|
||||
end
|
||||
end
|
||||
return MANTISdetection
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to start the detection via AWACS if defined as separate
|
||||
-- @param #MANTIS self
|
||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||
function MANTIS:StartAwacsDetection()
|
||||
self:T(self.lid.."Starting Awacs Detection")
|
||||
|
||||
|
||||
-- start detection
|
||||
local group = self.AWACS_Prefix
|
||||
local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart()
|
||||
local grouping = self.grouping or 5000
|
||||
--local acceptrange = self.acceptrange or 80000
|
||||
local interval = self.detectinterval or 60
|
||||
|
||||
|
||||
--@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
|
||||
MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||
MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
|
||||
MANTISAwacs:SetRefreshTimeInterval(interval)
|
||||
MANTISAwacs:Start()
|
||||
|
||||
|
||||
function MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||
--BASE:I( { From, Event, To, DetectedItem })
|
||||
local debug = false
|
||||
@ -867,10 +870,10 @@ do
|
||||
local text = "Awacs Detection at "..Coordinate:ToStringLLDMS()
|
||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
end
|
||||
end
|
||||
end
|
||||
return MANTISAwacs
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to set the SAM start state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
@ -904,12 +907,12 @@ do
|
||||
end
|
||||
self.SAM_Table = SAM_Tbl
|
||||
-- make SAMs evasive
|
||||
local mysead = SEAD:New( SEAD_Grps )
|
||||
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
|
||||
mysead:SetEngagementRange(engagerange)
|
||||
self.mysead = mysead
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to update SAM table and SEAD state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
@ -941,7 +944,7 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to link up #MANTIS with a #SHORAD installation
|
||||
-- @param #MANTIS self
|
||||
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
|
||||
@ -958,7 +961,7 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Function to unlink #MANTIS from a #SHORAD installation
|
||||
-- @param #MANTIS self
|
||||
function MANTIS:RemoveShorad()
|
||||
@ -966,11 +969,11 @@ do
|
||||
self.ShoradLink = false
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS main functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- [Internal] Check detection function
|
||||
-- @param #MANTIS self
|
||||
-- @param Functional.Detection#DETECTION_AREAS detection Detection object
|
||||
@ -1021,7 +1024,7 @@ do
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
end
|
||||
end --end alive
|
||||
else
|
||||
else
|
||||
if samgroup:IsAlive() then
|
||||
-- switch off SAM
|
||||
if self.UseEmOnOff then
|
||||
@ -1032,7 +1035,7 @@ do
|
||||
self:__GreenState(1,samgroup)
|
||||
self.SamStateTracker[name] = "GREEN"
|
||||
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 m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
@ -1041,8 +1044,8 @@ do
|
||||
end --end check
|
||||
end --for for loop
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- [Internal] Relocation relay function
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
@ -1051,7 +1054,7 @@ do
|
||||
self:_RelocateGroups()
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Check advanced state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
@ -1086,7 +1089,7 @@ do
|
||||
end -- end newstate vs oldstate
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Check DLink state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
@ -1100,7 +1103,7 @@ do
|
||||
self:I(self.lid .. "Intel DLink not running - switching back to single detection!")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to set start state
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1117,10 +1120,10 @@ do
|
||||
if self.advAwacs then
|
||||
self.AWACS_Detection = self:StartAwacsDetection()
|
||||
end
|
||||
self:__Status(-math.random(1,10))
|
||||
self:__Status(-math.random(1,10))
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Before status function for MANTIS
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1138,7 +1141,7 @@ do
|
||||
if self.advAwacs and not self.state2flag then
|
||||
self:_Check(self.AWACS_Detection)
|
||||
end
|
||||
|
||||
|
||||
-- relocate HQ and EWR
|
||||
if self.autorelocate then
|
||||
local relointerval = self.relointerval
|
||||
@ -1146,26 +1149,26 @@ do
|
||||
local timepassed = thistime - self.TimeStamp
|
||||
|
||||
local halfintv = math.floor(timepassed / relointerval)
|
||||
|
||||
|
||||
--self:T({timepassed=timepassed, halfintv=halfintv})
|
||||
|
||||
|
||||
if halfintv >= 1 then
|
||||
self.TimeStamp = timer.getAbsTime()
|
||||
self:_Relocate()
|
||||
self:__Relocating(1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- advanced state check
|
||||
if self.advanced then
|
||||
self:_CheckAdvState()
|
||||
end
|
||||
|
||||
|
||||
-- check DLink state
|
||||
if self.DLink then
|
||||
self:_CheckDLinkState()
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -1188,7 +1191,7 @@ do
|
||||
self:__Status(interval)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to stop MANTIS
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1197,9 +1200,9 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterStop(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function triggered by Event Relocating
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1208,9 +1211,9 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterRelocating(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function triggered by Event GreenState
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1220,9 +1223,9 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterGreenState(From, Event, To, Group)
|
||||
self:T({From, Event, To, Group})
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function triggered by Event RedState
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1232,9 +1235,9 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterRedState(From, Event, To, Group)
|
||||
self:T({From, Event, To, Group})
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function triggered by Event AdvStateChange
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1246,9 +1249,9 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval)
|
||||
self:T({From, Event, To, Oldstate, Newstate, Interval})
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function triggered by Event ShoradActivated
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@ -1259,7 +1262,7 @@ do
|
||||
-- @param #number Ontime Seconds the SHORAD will stay active
|
||||
function MANTIS:onafterShoradActivated(From, Event, To, Name, Radius, Ontime)
|
||||
self:T({From, Event, To, Name, Radius, Ontime})
|
||||
return self
|
||||
return self
|
||||
end
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
@ -1,28 +1,28 @@
|
||||
--- **Functional** -- Train missile defence and deflection.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * 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 the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
--
|
||||
-- [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,
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- * **Messages**: Menu to configure all messages.
|
||||
-- * **Messages On**: Show all messages.
|
||||
-- * **Messages Off**: Disable all messages.
|
||||
@ -45,23 +45,23 @@
|
||||
-- * **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 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.
|
||||
-- * **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.
|
||||
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **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.
|
||||
--
|
||||
-- * **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.
|
||||
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
|
||||
-- * **132nd Squadron**: Testing and optimizing the logic.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.MissileTrainer
|
||||
@ -76,7 +76,7 @@
|
||||
---
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
@ -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.
|
||||
--
|
||||
-- # Initialization:
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
@ -97,8 +97,8 @@
|
||||
-- * @{#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.InitMenusOnOff}: Allows to configure the options through the radio menu.
|
||||
--
|
||||
-- @field #MISSILETRAINER
|
||||
--
|
||||
-- @field #MISSILETRAINER
|
||||
MISSILETRAINER = {
|
||||
ClassName = "MISSILETRAINER",
|
||||
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.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @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
|
||||
function MISSILETRAINER:New( Distance, Briefing )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
@ -194,8 +194,8 @@ function MISSILETRAINER:New( Distance, Briefing )
|
||||
-- self:F( "ForEach:" .. Client.UnitName )
|
||||
-- Client:Alive( self._Alive, self )
|
||||
-- end
|
||||
--
|
||||
self.DBClients:ForEachClient(
|
||||
--
|
||||
self.DBClients:ForEachClient(
|
||||
function( Client )
|
||||
self:F( "ForEach:" .. Client.UnitName )
|
||||
Client:Alive( self._Alive, self )
|
||||
@ -207,9 +207,9 @@ function MISSILETRAINER:New( Distance, Briefing )
|
||||
-- self.DB:ForEachClient(
|
||||
-- --- @param Wrapper.Client#CLIENT Client
|
||||
-- function( Client )
|
||||
--
|
||||
--
|
||||
-- ... actions ...
|
||||
--
|
||||
--
|
||||
-- end
|
||||
-- )
|
||||
|
||||
@ -225,7 +225,7 @@ function MISSILETRAINER:New( Distance, Briefing )
|
||||
|
||||
self.DetailsRangeOnOff = true
|
||||
self.DetailsBearingOnOff = true
|
||||
|
||||
|
||||
self.MenusOnOff = true
|
||||
|
||||
self.TrackingMissiles = {}
|
||||
@ -293,7 +293,7 @@ end
|
||||
--- 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.
|
||||
-- @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
|
||||
function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency )
|
||||
self:F( TrackingFrequency )
|
||||
@ -478,30 +478,30 @@ function MISSILETRAINER:OnEventShot( EVentData )
|
||||
if TrainerTargetDCSUnit then
|
||||
local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit )
|
||||
local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
|
||||
|
||||
|
||||
self:T(TrainerTargetDCSUnitName )
|
||||
|
||||
|
||||
local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName )
|
||||
if Client then
|
||||
|
||||
|
||||
local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit )
|
||||
local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit )
|
||||
|
||||
|
||||
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
||||
|
||||
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched a %s",
|
||||
TrainerSourceUnit:GetTypeName(),
|
||||
TrainerWeaponName
|
||||
) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" )
|
||||
|
||||
|
||||
if self.AlertsToAll then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local ClientID = Client:GetID()
|
||||
self:T( ClientID )
|
||||
local MissileData = {}
|
||||
@ -579,52 +579,52 @@ function MISSILETRAINER:_TrackMissiles()
|
||||
end
|
||||
|
||||
-- ALERTS PART
|
||||
|
||||
|
||||
-- Loop for all Player Clients to check the alerts and deletion of missiles.
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
local Client = ClientData.Client
|
||||
|
||||
|
||||
if Client and Client:IsAlive() then
|
||||
|
||||
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
|
||||
self:T3( MissileDataID )
|
||||
|
||||
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||
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
|
||||
local PositionMissile = TrainerWeapon:getPosition().p
|
||||
local TargetVec3 = Client:GetVec3()
|
||||
|
||||
|
||||
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
|
||||
( PositionMissile.y - TargetVec3.y )^2 +
|
||||
( PositionMissile.z - TargetVec3.z )^2
|
||||
) ^ 0.5 / 1000
|
||||
|
||||
|
||||
if Distance <= self.Distance then
|
||||
-- Hit alert
|
||||
TrainerWeapon:destroy()
|
||||
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
|
||||
|
||||
|
||||
self:T( "killed" )
|
||||
|
||||
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s killed %s",
|
||||
TrainerWeapon:getTypeName(),
|
||||
TrainerSourceUnit:GetTypeName(),
|
||||
TrainerTargetUnit:GetPlayerName()
|
||||
), 15, "Hit Alert" )
|
||||
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
|
||||
|
||||
MissileData = nil
|
||||
table.remove( ClientData.MissileData, MissileDataID )
|
||||
self:T(ClientData.MissileData)
|
||||
@ -639,7 +639,7 @@ function MISSILETRAINER:_TrackMissiles()
|
||||
TrainerWeaponTypeName,
|
||||
TrainerSourceUnit:GetTypeName()
|
||||
), 5, "Tracking" )
|
||||
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
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.
|
||||
|
||||
-- TRACKING PART
|
||||
|
||||
|
||||
-- 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.
|
||||
-- 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
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
|
||||
local Client = ClientData.Client
|
||||
--self:T2( { Client:GetName() } )
|
||||
|
||||
|
||||
|
||||
|
||||
ClientData.MessageToClient = ""
|
||||
ClientData.MessageToAll = ""
|
||||
|
||||
|
||||
-- Other Players Client loop
|
||||
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
|
||||
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
|
||||
--self:T3( MissileDataID )
|
||||
|
||||
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||
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 ShowMessages == true then
|
||||
local TrackingTo
|
||||
TrackingTo = string.format( " -> %s",
|
||||
TrainerWeaponTypeName
|
||||
)
|
||||
|
||||
|
||||
if ClientDataID == TrackingDataID then
|
||||
if ClientData.MessageToClient == "" then
|
||||
ClientData.MessageToClient = "Missiles to You:\n"
|
||||
@ -712,7 +712,7 @@ function MISSILETRAINER:_TrackMissiles()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
|
||||
if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then
|
||||
local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
local elevation=coord:GetLandHeight()
|
||||
local eltxt=string.format("%d m", elevation)
|
||||
if _settings:IsImperial() then
|
||||
if not _settings:IsMetric() then
|
||||
elevation=UTILS.MetersToFeet(elevation)
|
||||
eltxt=string.format("%d ft", elevation)
|
||||
end
|
||||
@ -2829,6 +2831,7 @@ function RANGE:_CheckInZone(_unitName)
|
||||
local accur=0
|
||||
if shots>0 then
|
||||
accur=_result.hits/shots*100
|
||||
if accur > 100 then accur = 100 end
|
||||
end
|
||||
|
||||
-- Message text.
|
||||
|
||||
@ -1,54 +1,56 @@
|
||||
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## 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 defensive action by shutting down their radars.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
--
|
||||
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: July 2021
|
||||
--
|
||||
--
|
||||
-- Last Update: Aug 2021
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Functional.Sead
|
||||
-- @image SEAD.JPG
|
||||
|
||||
--- @type SEAD
|
||||
---
|
||||
-- @type SEAD
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- 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.
|
||||
--
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
--
|
||||
-- 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' } )
|
||||
--
|
||||
--
|
||||
-- @field #SEAD
|
||||
SEAD = {
|
||||
ClassName = "SEAD",
|
||||
ClassName = "SEAD",
|
||||
TargetSkill = {
|
||||
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||
},
|
||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||
},
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||
Padding = 10,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@ -59,7 +61,7 @@ SEAD = {
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
@ -67,22 +69,40 @@ SEAD = {
|
||||
["X_31"] = "X_31",
|
||||
["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.
|
||||
-- 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.
|
||||
-- @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.
|
||||
-- @return SEAD
|
||||
-- @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.
|
||||
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
|
||||
-- @return #SEAD self
|
||||
-- @usage
|
||||
-- -- CCCP SEAD Defenses
|
||||
-- -- 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' } )
|
||||
function SEAD:New( SEADGroupPrefixes )
|
||||
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
@ -90,9 +110,14 @@ function SEAD:New( SEADGroupPrefixes )
|
||||
else
|
||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
|
||||
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
|
||||
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||
self:I("*** SEAD - Started Version 0.2.8")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.3.1")
|
||||
return self
|
||||
end
|
||||
|
||||
@ -102,8 +127,8 @@ end
|
||||
-- @return #SEAD self
|
||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
self:T( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
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
|
||||
-- @param #SEAD self
|
||||
-- @param #number range Set the engagement range in percent, e.g. 50
|
||||
-- @return self
|
||||
-- @return #SEAD self
|
||||
function SEAD:SetEngagementRange(range)
|
||||
self:F( { range } )
|
||||
self:T( { range } )
|
||||
range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
@ -130,100 +155,175 @@ function SEAD:SetEngagementRange(range)
|
||||
return self
|
||||
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
|
||||
-- @param #SEAD self
|
||||
-- @param #string WeaponName
|
||||
-- @return #boolean Returns true for a match
|
||||
-- @return #string name Name of hit in table
|
||||
function SEAD:_CheckHarms(WeaponName)
|
||||
self:F( { WeaponName } )
|
||||
self:T( { WeaponName } )
|
||||
local hit = false
|
||||
local name = ""
|
||||
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
|
||||
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
|
||||
|
||||
--- 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
|
||||
-- @param #SEAD
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @return #SEAD self
|
||||
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 SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
self:T({ SEADWeapon })
|
||||
|
||||
--self:T({ SEADWeapon })
|
||||
|
||||
if self:_CheckHarms(SEADWeaponName) then
|
||||
self:T( '*** SEAD - Weapon Match' )
|
||||
local _targetskill = "Random"
|
||||
local _targetMimgroupName = "none"
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
||||
local _targetgroupname = "none"
|
||||
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
||||
local _targetgroup = nil -- Wrapper.Group#GROUP
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
local _targetMimgroup = _targetUnit:GetGroup()
|
||||
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
||||
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
||||
self:T( self.SEADGroupPrefixes )
|
||||
self:T( _targetMimgroupName )
|
||||
_targetgroup = _targetUnit:GetGroup()
|
||||
_targetgroupname = _targetgroup:GetName() -- group name
|
||||
local _targetUnitName = _targetUnit:GetName()
|
||||
_targetUnit:GetSkill()
|
||||
_targetskill = _targetUnit:GetSkill()
|
||||
end
|
||||
-- see if we are shot at
|
||||
local SEADGroupFound = false
|
||||
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
|
||||
self:T( '*** SEAD - Group Found' )
|
||||
self:T( '*** SEAD - Group Match Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||
_targetskill = Skills[ math.random(1,4) ]
|
||||
end
|
||||
self:T( _targetskill )
|
||||
--self:T( _targetskill )
|
||||
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
|
||||
|
||||
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
--tracker ID table to switch groups off and on again
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
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
|
||||
self:T("*** SEAD - Evading")
|
||||
-- calculate distance of attacker
|
||||
local _targetpos = _targetgroup:GetCoordinate()
|
||||
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
|
||||
-- weapon speed
|
||||
local hit, data = self:_CheckHarms(SEADWeaponName)
|
||||
local wpnspeed = 666 -- ;)
|
||||
local reach = 10
|
||||
if hit then
|
||||
local wpndata = SEAD.HarmData[data]
|
||||
reach = wpndata[1] * 1,1
|
||||
local mach = wpndata[2]
|
||||
wpnspeed = math.floor(mach * 340.29)
|
||||
end
|
||||
-- randomize switch-on time
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
local SuppressionEndTime = timer.getTime() + delay
|
||||
--create entry
|
||||
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
||||
self.SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = delay
|
||||
}
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
--_targetMimgroup:enableEmission(false)
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
-- time to impact
|
||||
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
|
||||
if _distance > 0 then
|
||||
_distance = math.floor(_distance / 1000) -- km
|
||||
else
|
||||
_distance = 0
|
||||
end
|
||||
|
||||
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
|
||||
|
||||
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
|
||||
return self
|
||||
end
|
||||
|
||||
@ -113,7 +113,7 @@ do
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
|
||||
@ -32,6 +32,8 @@
|
||||
-- * [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 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:**
|
||||
--
|
||||
@ -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.
|
||||
--
|
||||
-- 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 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.
|
||||
-- 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
|
||||
-- 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.
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
-- * [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)
|
||||
--
|
||||
-- ### AV-8B Harrier at USS Tarawa
|
||||
--
|
||||
-- * [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 @@
|
||||
-- 
|
||||
--
|
||||
-- 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
|
||||
@ -919,9 +924,9 @@
|
||||
--
|
||||
-- ## 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.
|
||||
-- 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.
|
||||
--
|
||||
-- airbossStennis:SetVoiceOversLSOByFF()
|
||||
@ -1256,7 +1261,7 @@ AIRBOSS = {
|
||||
|
||||
--- Aircraft types capable of landing on carrier (human+AI).
|
||||
-- @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 HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
|
||||
-- @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 VINSON USS Carl Vinson (CVN-70) [Obsolete]
|
||||
-- @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)
|
||||
AIRBOSS.CarrierType={
|
||||
ROOSEVELT="CVN_71",
|
||||
@ -1301,6 +1308,8 @@ AIRBOSS.CarrierType={
|
||||
STENNIS="Stennis",
|
||||
VINSON="VINSON",
|
||||
TARAWA="LHA_Tarawa",
|
||||
AMERICA="USS America LHA-6",
|
||||
JCARLOS="L61",
|
||||
KUZNETSOV="KUZNECOW",
|
||||
}
|
||||
|
||||
@ -1420,8 +1429,8 @@ AIRBOSS.PatternStep={
|
||||
-- @field #string IM "IM": In the middle.
|
||||
-- @field #string IC "IC": In close.
|
||||
-- @field #string AR "AR": At the ramp.
|
||||
-- @field #string AL "AL": Abeam landing position (Tarawa).
|
||||
-- @field #string LC "LC": Level crossing (Tarawa).
|
||||
-- @field #string AL "AL": Abeam landing position (V/STOL).
|
||||
-- @field #string LC "LC": Level crossing (V/STOL).
|
||||
-- @field #string IW "IW": In the wires.
|
||||
AIRBOSS.GroovePos={
|
||||
X0="X0",
|
||||
@ -1486,6 +1495,7 @@ AIRBOSS.GroovePos={
|
||||
-- @field #AIRBOSS.RadioCall DEPARTANDREENTER "Depart and re-enter" call.
|
||||
-- @field #AIRBOSS.RadioCall EXPECTHEAVYWAVEOFF "Expect heavy wavoff" 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 FOULDECK "Foul Deck" 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
|
||||
-- Tarawa parameters.
|
||||
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
|
||||
-- Kusnetsov parameters - maybe...
|
||||
self:_InitStennis()
|
||||
@ -2061,7 +2077,7 @@ function AIRBOSS:New(carriername, alias)
|
||||
|
||||
|
||||
-- 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.
|
||||
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.LEFT=LEFT or -3.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
|
||||
return self
|
||||
end
|
||||
@ -4404,6 +4420,85 @@ function AIRBOSS:_InitTarawa()
|
||||
|
||||
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.
|
||||
-- @param #AIRBOSS self
|
||||
-- @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.EXPECTHEAVYWAVEOFF.duration=1.30
|
||||
self.LSOCall.EXPECTSPOT75.duration=1.85
|
||||
self.LSOCall.EXPECTSPOT5.duration=1.3
|
||||
self.LSOCall.FAST.duration=0.75
|
||||
self.LSOCall.FOULDECK.duration=0.75
|
||||
self.LSOCall.HIGH.duration=0.65
|
||||
@ -4616,6 +4712,7 @@ function AIRBOSS:SetVoiceOversLSOByFF(mizfolder)
|
||||
self.LSOCall.DEPARTANDREENTER.duration=1.10
|
||||
self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20
|
||||
self.LSOCall.EXPECTSPOT75.duration=2.00
|
||||
self.LSOCall.EXPECTSPOT5.duration=1.3
|
||||
self.LSOCall.FAST.duration=0.70
|
||||
self.LSOCall.FOULDECK.duration=0.62
|
||||
self.LSOCall.HIGH.duration=0.65
|
||||
@ -4883,6 +4980,14 @@ function AIRBOSS:_InitVoiceOvers()
|
||||
subtitle="Expect spot 7.5",
|
||||
duration=2.0,
|
||||
subduration=5,
|
||||
},
|
||||
EXPECTSPOT5={
|
||||
file="LSO-ExpectSpot5",
|
||||
suffix="ogg",
|
||||
loud=false,
|
||||
subtitle="Expect spot 5",
|
||||
duration=1.3,
|
||||
subduration=5,
|
||||
},
|
||||
STABILIZED={
|
||||
file="LSO-Stabilized",
|
||||
@ -5543,14 +5648,14 @@ function AIRBOSS:_GetAircraftAoA(playerData)
|
||||
aoa.Fast = 8.25 --=17.5/2
|
||||
aoa.FAST = 8.00 --=16.5/2
|
||||
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 = 13.0
|
||||
aoa.OnSpeedMax = 12.0
|
||||
aoa.OnSpeed = 11.0
|
||||
aoa.OnSpeedMin = 10.0
|
||||
aoa.Fast = 9.0
|
||||
aoa.FAST = 8.0
|
||||
aoa.Fast = 8.0
|
||||
aoa.FAST = 7.5
|
||||
end
|
||||
|
||||
return aoa
|
||||
@ -5810,7 +5915,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
||||
alt=UTILS.FeetToMeters(300) --?
|
||||
elseif harrier then
|
||||
-- 300-325 ft
|
||||
alt=UTILS.FeetToMeters(300)
|
||||
alt=UTILS.FeetToMeters(300)-- Need to verify
|
||||
end
|
||||
|
||||
aoa=aoaac.OnSpeed
|
||||
@ -6751,8 +6856,8 @@ function AIRBOSS:_GetMarshalAltitude(stack, case)
|
||||
-- Second point 1.5 NM ahead.
|
||||
p2=Carrier:Translate(UTILS.NMToMeters(1.5), hdg)
|
||||
|
||||
-- Tarawa Delta pattern.
|
||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||
-- Tarawa,LHA,LHD Delta patterns.
|
||||
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.
|
||||
p1=Carrier:Translate(UTILS.NMToMeters(1.0), hdg+90)
|
||||
@ -8597,7 +8702,7 @@ function AIRBOSS:OnEventLand(EventData)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- 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".
|
||||
self:RadioTransmission(self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true)
|
||||
@ -8632,7 +8737,7 @@ function AIRBOSS:OnEventLand(EventData)
|
||||
-- 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
|
||||
local coord=EventData.IniUnit:GetCoordinate()
|
||||
@ -9539,8 +9644,10 @@ function AIRBOSS:_Bullseye(playerData)
|
||||
-- Hint for player about altitude, AoA etc.
|
||||
self:_PlayerHint(playerData)
|
||||
|
||||
-- LSO expect spot 7.5 call
|
||||
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||
-- LSO expect spot 5 or 7.5 call
|
||||
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)
|
||||
end
|
||||
|
||||
@ -9676,8 +9783,8 @@ function AIRBOSS:_CheckForLongDownwind(playerData)
|
||||
-- 1.6 NM from carrier is too far.
|
||||
local limit=UTILS.NMToMeters(-1.6)
|
||||
|
||||
-- For the tarawa we give a bit more space.
|
||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||
-- For the tarawa, other LHA and LHD we give a bit more space.
|
||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
|
||||
limit=UTILS.NMToMeters(-2.0)
|
||||
end
|
||||
|
||||
@ -9722,8 +9829,10 @@ function AIRBOSS:_Abeam(playerData)
|
||||
-- Paddles contact.
|
||||
self:RadioTransmission(self.LSORadio, self.LSOCall.PADDLESCONTACT, nil, nil, nil, true)
|
||||
|
||||
-- LSO expect spot 7.5 call
|
||||
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||
-- LSO expect spot 5 or 7.5 call
|
||||
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)
|
||||
end
|
||||
|
||||
@ -9760,7 +9869,7 @@ function AIRBOSS:_Ninety(playerData)
|
||||
self:_PlayerHint(playerData)
|
||||
|
||||
-- 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.
|
||||
self:_SetPlayerStep(playerData, AIRBOSS.PatternStep.FINAL)
|
||||
else
|
||||
@ -10113,7 +10222,7 @@ function AIRBOSS:_Groove(playerData)
|
||||
|
||||
-- Drift on lineup.
|
||||
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-"
|
||||
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))
|
||||
@ -10128,7 +10237,7 @@ function AIRBOSS:_Groove(playerData)
|
||||
elseif gd.LUE<-0.13 and lineupError>0.14 then
|
||||
env.info" Little Drift Left across centre ==> (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
|
||||
|
||||
@ -10434,7 +10543,7 @@ function AIRBOSS:_GetSternCoord()
|
||||
--local stern=self:GetCoordinate()
|
||||
|
||||
-- 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.
|
||||
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
|
||||
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.
|
||||
local d=Ldist-dc
|
||||
|
||||
|
||||
-- Multiplayer wire correction.
|
||||
if self.mpWireCorrection then
|
||||
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)
|
||||
|
||||
-- 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))
|
||||
end
|
||||
|
||||
@ -11230,7 +11339,7 @@ function AIRBOSS:_GetZoneCommence(case, stack)
|
||||
-- Three position
|
||||
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)
|
||||
|
||||
@ -11521,7 +11630,7 @@ function AIRBOSS:_GetAltCarrier(unit)
|
||||
return h
|
||||
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
|
||||
-- @return Core.Point#COORDINATE Optimal landing coordinate.
|
||||
function AIRBOSS:_GetOptLandingCoordinate()
|
||||
@ -11541,6 +11650,23 @@ function AIRBOSS:_GetOptLandingCoordinate()
|
||||
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.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.
|
||||
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
|
||||
|
||||
@ -11578,6 +11704,21 @@ function AIRBOSS:_GetLandingSpotCoordinate()
|
||||
|
||||
-- Primary landing spot 7.5
|
||||
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
|
||||
|
||||
@ -12070,6 +12211,11 @@ end
|
||||
-- * > 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\_".
|
||||
-- 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.PlayerData playerData Player data table.
|
||||
@ -12088,6 +12234,13 @@ function AIRBOSS:_EvalGrooveTime(playerData)
|
||||
grade="OK Groove"
|
||||
elseif t<=24 then
|
||||
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
|
||||
grade="LIG"
|
||||
end
|
||||
@ -12097,6 +12250,11 @@ function AIRBOSS:_EvalGrooveTime(playerData)
|
||||
grade="_OK_"
|
||||
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
|
||||
end
|
||||
|
||||
@ -12113,7 +12271,7 @@ function AIRBOSS:_LSOgrade(playerData)
|
||||
return select(2, string.gsub(base, pattern, ""))
|
||||
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 GIM,nIM=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IM)
|
||||
local GIC,nIC=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IC)
|
||||
@ -12122,25 +12280,37 @@ function AIRBOSS:_LSOgrade(playerData)
|
||||
-- Put everything together.
|
||||
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 nL=count(G, '_')/2
|
||||
local nS=count(G, '%(')
|
||||
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 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 points
|
||||
if N==0 and TgrooveUnicorn then
|
||||
if N==0 and (TgrooveUnicorn or TgrooveVstolUnicorn ) then
|
||||
-- No deviations, should be REALLY RARE!
|
||||
grade="_OK_"
|
||||
points=5.0
|
||||
G="Unicorn"
|
||||
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.
|
||||
grade="--"
|
||||
points=2.0
|
||||
@ -12153,7 +12323,8 @@ function AIRBOSS:_LSOgrade(playerData)
|
||||
grade="OK"
|
||||
points=4.0
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Replace" )"( and "__"
|
||||
G=G:gsub("%)%(", "")
|
||||
@ -12263,35 +12434,35 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
||||
|
||||
-- Aircraft specific AoA values.
|
||||
local acaoa=self:_GetAircraftAoA(playerData)
|
||||
|
||||
|
||||
--Angled Approach.
|
||||
local P=nil
|
||||
if step==AIRBOSS.PatternStep.GROOVE_XX and ROL<=4.0 and playerData.case<3 then
|
||||
if LUE>self.lue.RIGHT then
|
||||
P=underline("AA")
|
||||
elseif
|
||||
elseif
|
||||
LUE>self.lue.RightMed then
|
||||
P="AA "
|
||||
elseif
|
||||
elseif
|
||||
LUE>self.lue.Right then
|
||||
P=little("AA")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--Overshoot Start.
|
||||
local O=nil
|
||||
if step==AIRBOSS.PatternStep.GROOVE_XX then
|
||||
if LUE<self.lue.LEFT then
|
||||
if LUE<self.lue.LEFT then
|
||||
O=underline("OS")
|
||||
elseif
|
||||
elseif
|
||||
LUE<self.lue.Left then
|
||||
O="OS"
|
||||
elseif
|
||||
elseif
|
||||
LUE<self.lue._min then
|
||||
O=little("OS")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Speed via AoA. Depends on aircraft type.
|
||||
local S=nil
|
||||
if AOA>acaoa.SLOW then
|
||||
@ -12361,7 +12532,7 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
||||
if P then
|
||||
G=G..P
|
||||
n=n
|
||||
end
|
||||
end
|
||||
-- Speed.
|
||||
if S then
|
||||
G=G..S
|
||||
@ -12387,7 +12558,7 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
||||
G=G..O
|
||||
n=n+1
|
||||
end
|
||||
|
||||
|
||||
-- Add current step.
|
||||
local step=self:_GS(step)
|
||||
step=step:gsub("XX","X")
|
||||
@ -12449,7 +12620,7 @@ function AIRBOSS:_GS(step, n)
|
||||
if n==-1 then
|
||||
gp=AIRBOSS.GroovePos.IC
|
||||
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
|
||||
else
|
||||
gp=AIRBOSS.GroovePos.IW
|
||||
@ -14339,17 +14510,17 @@ function AIRBOSS:_IsCarrierAircraft(unit)
|
||||
-- Get aircraft type name
|
||||
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 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
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Also only Harriers can land on the Tarawa.
|
||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||
-- Also only Harriers can land on the Tarawa, LHA and LHD.
|
||||
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
|
||||
return false
|
||||
end
|
||||
@ -17718,8 +17889,8 @@ function AIRBOSS:_MarkCaseZones(_unitName, flare)
|
||||
self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green, 45)
|
||||
end
|
||||
|
||||
-- Tarawa landing spots.
|
||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||
-- Tarawa, LHA and LHD landing spots.
|
||||
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"
|
||||
-- Abeam landing spot zone.
|
||||
local ALSPT=self:_GetZoneAbeamLandingSpot()
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
--- **Ops** -- Combat Search and Rescue.
|
||||
--
|
||||
-- ===
|
||||
@ -23,7 +22,7 @@
|
||||
-- @module Ops.CSAR
|
||||
-- @image OPS_CSAR.jpg
|
||||
|
||||
-- Date: July 2021
|
||||
-- Date: Sep 2021
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **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.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.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.
|
||||
@ -95,6 +95,8 @@
|
||||
-- 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.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
|
||||
--
|
||||
@ -214,26 +216,6 @@ CSAR = {
|
||||
-- @field Wrapper.Group#GROUP group Spawned group object.
|
||||
-- @field #number timestamp Timestamp for approach process
|
||||
-- @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
|
||||
-- @type CSAR.AircraftType
|
||||
@ -251,7 +233,7 @@ CSAR.AircraftType["Mi-24V"] = 8
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="0.1.8r3"
|
||||
CSAR.version="0.1.10r5"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@ -361,12 +343,13 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
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.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.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.template = Template or "generic" -- template for downed pilot
|
||||
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.autosmokedistance = 2000 -- distance for autosmoke
|
||||
-- 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_near = 3000 -- switch to 5 sec interval approach mode, meters
|
||||
self.pilotmustopendoors = false -- switch to true to enable check on open doors
|
||||
self.suppressmessages = false
|
||||
|
||||
-- WARNING - here\'ll be dragons
|
||||
-- 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"
|
||||
|
||||
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
|
||||
|
||||
if _freq then
|
||||
@ -734,7 +718,6 @@ function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessa
|
||||
return self
|
||||
end
|
||||
|
||||
-- TODO: Split in functions per Event type
|
||||
--- (Internal) Event handler.
|
||||
-- @param #CSAR self
|
||||
function CSAR:_EventHandler(EventData)
|
||||
@ -806,8 +789,7 @@ function CSAR:_EventHandler(EventData)
|
||||
if self:_DoubleEjection(_unitname) then
|
||||
return
|
||||
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
|
||||
self:T(self.lid .. " Pilot has not taken off, ignore")
|
||||
end
|
||||
@ -886,13 +868,15 @@ function CSAR:_EventHandler(EventData)
|
||||
self:T(self.lid .. " Landing Place Nil")
|
||||
return -- error!
|
||||
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 self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then
|
||||
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
|
||||
else
|
||||
self:_RescuePilots(_unit)
|
||||
end
|
||||
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
|
||||
else
|
||||
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
|
||||
end
|
||||
@ -912,7 +896,6 @@ end
|
||||
function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
|
||||
self:T(self.lid .. " _InitSARForPilot")
|
||||
local _leader = _downedGroup:GetUnit(1)
|
||||
--local _groupName = _downedGroup:GetName()
|
||||
local _groupName = _GroupName
|
||||
local _freqk = _freq / 1000
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_downedGroup)
|
||||
@ -928,7 +911,7 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
|
||||
end
|
||||
|
||||
-- trigger FSM event
|
||||
self:__PilotDown(2,_downedGroup, _freqk, _leadername, _coordinatesText)
|
||||
self:__PilotDown(2,_downedGroup, _freqk, _groupName, _coordinatesText)
|
||||
|
||||
return self
|
||||
end
|
||||
@ -1098,7 +1081,6 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
|
||||
local grouptable = downedgrouptable --#CSAR.DownedPilot
|
||||
self.inTransitGroups[_heliName][_woundedGroupName] =
|
||||
{
|
||||
-- DONE: Fix with #CSAR.DownedPilot
|
||||
originalUnit = grouptable.originalUnit,
|
||||
woundedGroup = _woundedGroupName,
|
||||
side = self.coalition,
|
||||
@ -1106,7 +1088,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
|
||||
player = grouptable.player,
|
||||
}
|
||||
|
||||
_woundedGroup:Destroy()
|
||||
_woundedGroup:Destroy(false)
|
||||
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)
|
||||
@ -1137,39 +1119,8 @@ end
|
||||
-- @return #boolean outcome The outcome.
|
||||
function CSAR:_IsLoadingDoorOpen( unit_name )
|
||||
self:T(self.lid .. " _IsLoadingDoorOpen")
|
||||
local ret_val = false
|
||||
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 UTILS.IsLoadingDoorOpen(unit_name)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- (Internal) Function to check if heli is close to group.
|
||||
@ -1200,14 +1151,12 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
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)
|
||||
end
|
||||
--mark as shown for THIS heli and THIS group
|
||||
self.heliCloseMessage[_lookupKeyHeli] = true
|
||||
end
|
||||
|
||||
-- have we landed close enough?
|
||||
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 (_distance < self.extractDistance) then
|
||||
local _time = self.landedStatus[_lookupKeyHeli]
|
||||
@ -1308,7 +1257,8 @@ end
|
||||
-- @param #CSAR self
|
||||
-- @param #string heliname Heli 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({heliname,groupname})
|
||||
local _heliUnit = self:_GetSARHeli(heliname)
|
||||
@ -1331,8 +1281,8 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
return
|
||||
end
|
||||
|
||||
if _dist < 200 and _heliUnit:InAir() == false then
|
||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then
|
||||
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
|
||||
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
|
||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
|
||||
else
|
||||
self:_RescuePilots(_heliUnit)
|
||||
@ -1341,7 +1291,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
end
|
||||
|
||||
--queue up
|
||||
self:__Returning(-5,heliname,_woundedGroupName)
|
||||
self:__Returning(-5,heliname,_woundedGroupName, isairport)
|
||||
return self
|
||||
end
|
||||
|
||||
@ -1357,8 +1307,7 @@ function CSAR:_RescuePilots(_heliUnit)
|
||||
-- Groups already rescued
|
||||
return
|
||||
end
|
||||
|
||||
-- DONE: count saved units?
|
||||
|
||||
local PilotsSaved = self:_PilotsOnboard(_heliName)
|
||||
|
||||
self.inTransitGroups[_heliName] = nil
|
||||
@ -1397,7 +1346,9 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
|
||||
local group = _unit:GetGroup()
|
||||
local _clear = _clear or nil
|
||||
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
|
||||
if _speak and self.useSRS then
|
||||
local srstext = SOUNDTEXT:New(_text)
|
||||
@ -1452,7 +1403,6 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
local _groupName = _value.name
|
||||
self:T(string.format("Display Active Pilot: %s", tostring(_groupName)))
|
||||
self:T({Table=_value})
|
||||
--local _woundedGroup = GROUP:FindByName(_groupName)
|
||||
local _woundedGroup = _value.group
|
||||
if _woundedGroup and _value.alive then
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
|
||||
@ -1495,10 +1445,17 @@ function CSAR:_GetClosestDownedPilot(_heli)
|
||||
local _shortestDistance = -1
|
||||
local _distance = 0
|
||||
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
|
||||
for _, _groupInfo in pairs(DownedPilotsTable) do
|
||||
|
||||
for _, _groupInfo in UTILS.spairs(DownedPilotsTable) do
|
||||
--for _, _groupInfo in pairs(DownedPilotsTable) do
|
||||
local _woundedName = _groupInfo.name
|
||||
local _tempWounded = _groupInfo.group
|
||||
|
||||
@ -1564,12 +1521,11 @@ end
|
||||
-- @param #number _messagetime How long to show.
|
||||
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
|
||||
self:T(self.lid .. " _DisplayToAllSAR")
|
||||
local messagetime = _messagetime or self.messageTime
|
||||
for _, _unitName in pairs(self.csarUnits) do
|
||||
local _unit = self:_GetSARHeli(_unitName)
|
||||
if _unit then
|
||||
if not _messagetime then
|
||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||
end
|
||||
if _unit and not self.suppressmessages then
|
||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||
end
|
||||
end
|
||||
return self
|
||||
@ -1741,9 +1697,20 @@ end
|
||||
function CSAR:_GetDistance(_point1, _point2)
|
||||
self:T(self.lid .. " _GetDistance")
|
||||
if _point1 and _point2 then
|
||||
local distance = _point1:DistanceFromPointVec2(_point2)
|
||||
return distance
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
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
|
||||
self:E("******Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
end
|
||||
@ -1752,7 +1719,6 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_GenerateVHFrequencies()
|
||||
self:T(self.lid .. " _GenerateVHFrequencies")
|
||||
--local _skipFrequencies = self.SkipFrequencies
|
||||
|
||||
local FreeVHFFrequencies = {}
|
||||
FreeVHFFrequencies = UTILS.GenerateVHFrequencies()
|
||||
@ -1792,7 +1758,6 @@ function CSAR:_GetClockDirection(_heli, _group)
|
||||
if _heading then
|
||||
local Aspect = Angle - _heading
|
||||
if Aspect == 0 then Aspect = 360 end
|
||||
--clock = math.floor(Aspect / 30)
|
||||
clock = math.abs(UTILS.Round((Aspect / 30),0))
|
||||
if clock == 0 then clock = 12 end
|
||||
end
|
||||
@ -1901,6 +1866,7 @@ function CSAR:onafterStart(From, Event, To)
|
||||
else
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
|
||||
end
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
|
||||
self:__Status(-10)
|
||||
return self
|
||||
end
|
||||
@ -1909,19 +1875,19 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_CheckDownedPilotTable()
|
||||
local pilots = self.downedPilots
|
||||
for _,_entry in pairs (pilots) do
|
||||
self:T("Checking for " .. _entry.name)
|
||||
self:T({entry=_entry})
|
||||
local group = _entry.group
|
||||
if not group:IsAlive() then
|
||||
self:T("Group is dead")
|
||||
if _entry.alive == true then
|
||||
self:T("Switching .alive to false")
|
||||
local npilots = {}
|
||||
|
||||
for _ind,_entry in pairs(pilots) do
|
||||
local _group = _entry.group
|
||||
if _group:IsAlive() then
|
||||
npilots[_ind] = _entry
|
||||
else
|
||||
if _entry.alive then
|
||||
self:__KIA(1,_entry.desc)
|
||||
self:_RemoveNameFromDownedPilots(_entry.name,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.downedPilots = npilots
|
||||
return self
|
||||
end
|
||||
|
||||
@ -2041,9 +2007,10 @@ end
|
||||
-- @param #string To To state.
|
||||
-- @param #string Heliname Name of the helicopter 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:_ScheduledSARFlight(Heliname,Woundedgroupname)
|
||||
self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,9 @@
|
||||
--- **Core** - Makes the radio talk.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Send text strings using a vocabulary that is converted in spoken language.
|
||||
-- * Possiblity to implement multiple language.
|
||||
--
|
||||
@ -15,10 +15,10 @@
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
--- Makes the radio speak.
|
||||
--
|
||||
--
|
||||
-- # RADIOSPEECH usage
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @type RADIOSPEECH
|
||||
-- @extends Core.RadioQueue#RADIOQUEUE
|
||||
RADIOSPEECH = {
|
||||
@ -59,24 +59,24 @@ RADIOSPEECH.Vocabulary.EN = {
|
||||
["70"] = { "70", 0.48 },
|
||||
["80"] = { "80", 0.26 },
|
||||
["90"] = { "90", 0.36 },
|
||||
["100"] = { "100", 0.55 },
|
||||
["200"] = { "200", 0.55 },
|
||||
["300"] = { "300", 0.61 },
|
||||
["400"] = { "400", 0.60 },
|
||||
["500"] = { "500", 0.61 },
|
||||
["600"] = { "600", 0.65 },
|
||||
["700"] = { "700", 0.70 },
|
||||
["800"] = { "800", 0.54 },
|
||||
["900"] = { "900", 0.60 },
|
||||
["1000"] = { "1000", 0.60 },
|
||||
["2000"] = { "2000", 0.61 },
|
||||
["3000"] = { "3000", 0.64 },
|
||||
["4000"] = { "4000", 0.62 },
|
||||
["5000"] = { "5000", 0.69 },
|
||||
["6000"] = { "6000", 0.69 },
|
||||
["7000"] = { "7000", 0.75 },
|
||||
["8000"] = { "8000", 0.59 },
|
||||
["9000"] = { "9000", 0.65 },
|
||||
["100"] = { "100", 0.55 },
|
||||
["200"] = { "200", 0.55 },
|
||||
["300"] = { "300", 0.61 },
|
||||
["400"] = { "400", 0.60 },
|
||||
["500"] = { "500", 0.61 },
|
||||
["600"] = { "600", 0.65 },
|
||||
["700"] = { "700", 0.70 },
|
||||
["800"] = { "800", 0.54 },
|
||||
["900"] = { "900", 0.60 },
|
||||
["1000"] = { "1000", 0.60 },
|
||||
["2000"] = { "2000", 0.61 },
|
||||
["3000"] = { "3000", 0.64 },
|
||||
["4000"] = { "4000", 0.62 },
|
||||
["5000"] = { "5000", 0.69 },
|
||||
["6000"] = { "6000", 0.69 },
|
||||
["7000"] = { "7000", 0.75 },
|
||||
["8000"] = { "8000", 0.59 },
|
||||
["9000"] = { "9000", 0.65 },
|
||||
|
||||
["chevy"] = { "chevy", 0.35 },
|
||||
["colt"] = { "colt", 0.35 },
|
||||
@ -94,10 +94,10 @@ RADIOSPEECH.Vocabulary.EN = {
|
||||
["meters"] = { "meters", 0.41 },
|
||||
["mi"] = { "miles", 0.45 },
|
||||
["feet"] = { "feet", 0.29 },
|
||||
|
||||
|
||||
["br"] = { "br", 1.1 },
|
||||
["bra"] = { "bra", 0.3 },
|
||||
|
||||
|
||||
|
||||
["returning to base"] = { "returning_to_base", 0.85 },
|
||||
["on route to ground target"] = { "on_route_to_ground_target", 1.05 },
|
||||
@ -143,52 +143,52 @@ RADIOSPEECH.Vocabulary.RU = {
|
||||
["70"] = { "70", 0.68 },
|
||||
["80"] = { "80", 0.84 },
|
||||
["90"] = { "90", 0.71 },
|
||||
["100"] = { "100", 0.35 },
|
||||
["200"] = { "200", 0.59 },
|
||||
["300"] = { "300", 0.53 },
|
||||
["400"] = { "400", 0.70 },
|
||||
["500"] = { "500", 0.50 },
|
||||
["600"] = { "600", 0.58 },
|
||||
["700"] = { "700", 0.64 },
|
||||
["800"] = { "800", 0.77 },
|
||||
["900"] = { "900", 0.75 },
|
||||
["1000"] = { "1000", 0.87 },
|
||||
["2000"] = { "2000", 0.83 },
|
||||
["3000"] = { "3000", 0.84 },
|
||||
["4000"] = { "4000", 1.00 },
|
||||
["5000"] = { "5000", 0.77 },
|
||||
["6000"] = { "6000", 0.90 },
|
||||
["7000"] = { "7000", 0.77 },
|
||||
["8000"] = { "8000", 0.92 },
|
||||
["9000"] = { "9000", 0.87 },
|
||||
["100"] = { "100", 0.35 },
|
||||
["200"] = { "200", 0.59 },
|
||||
["300"] = { "300", 0.53 },
|
||||
["400"] = { "400", 0.70 },
|
||||
["500"] = { "500", 0.50 },
|
||||
["600"] = { "600", 0.58 },
|
||||
["700"] = { "700", 0.64 },
|
||||
["800"] = { "800", 0.77 },
|
||||
["900"] = { "900", 0.75 },
|
||||
["1000"] = { "1000", 0.87 },
|
||||
["2000"] = { "2000", 0.83 },
|
||||
["3000"] = { "3000", 0.84 },
|
||||
["4000"] = { "4000", 1.00 },
|
||||
["5000"] = { "5000", 0.77 },
|
||||
["6000"] = { "6000", 0.90 },
|
||||
["7000"] = { "7000", 0.77 },
|
||||
["8000"] = { "8000", 0.92 },
|
||||
["9000"] = { "9000", 0.87 },
|
||||
|
||||
["Ñ<EFBFBD>тепени"] = { "degrees", 0.5 },
|
||||
["километров"] = { "kilometers", 0.65 },
|
||||
["градусы"] = { "degrees", 0.5 },
|
||||
["километры"] = { "kilometers", 0.65 },
|
||||
["km"] = { "kilometers", 0.65 },
|
||||
["миль"] = { "miles", 0.45 },
|
||||
["мили"] = { "miles", 0.45 },
|
||||
["mi"] = { "miles", 0.45 },
|
||||
["метры"] = { "meters", 0.41 },
|
||||
["метров"] = { "meters", 0.41 },
|
||||
["m"] = { "meters", 0.41 },
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
|
||||
["br"] = { "br", 1.1 },
|
||||
["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 },
|
||||
["и"] = { "and", 0.17 },
|
||||
["в"] = { "at", 0.19 },
|
||||
["dot"] = { "dot", 0.51 },
|
||||
["defender"] = { "defender", 0.45 },
|
||||
["возвращение на базу"] = { "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 },
|
||||
["колёса вверх..."] = { "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.
|
||||
@ -200,11 +200,11 @@ function RADIOSPEECH:New(frequency, modulation)
|
||||
|
||||
-- Inherit base
|
||||
local self = BASE:Inherit( self, RADIOQUEUE:New( frequency, modulation ) ) -- #RADIOSPEECH
|
||||
|
||||
|
||||
self.Language = "EN"
|
||||
|
||||
|
||||
self:BuildTree()
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -262,7 +262,7 @@ end
|
||||
function RADIOSPEECH:BuildTree()
|
||||
|
||||
self.Speech = {}
|
||||
|
||||
|
||||
for Language, Sentences in pairs( self.Vocabulary ) do
|
||||
self:I( { Language = Language, Sentences = Sentences })
|
||||
self.Speech[Language] = {}
|
||||
@ -271,7 +271,7 @@ function RADIOSPEECH:BuildTree()
|
||||
self:AddSentenceToSpeech( Sentence, self.Speech[Language], Sentence, Data )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:I( { Speech = self.Speech } )
|
||||
|
||||
return self
|
||||
@ -290,7 +290,7 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
||||
local Word, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" )
|
||||
|
||||
self:I( { Word = Word, Speech = Speech[Word], RemainderSentence = RemainderSentence } )
|
||||
|
||||
|
||||
|
||||
if Word then
|
||||
if Word ~= "" and tonumber(Word) == nil then
|
||||
@ -302,7 +302,7 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
||||
if Speech[Word].Next == nil then
|
||||
self:I( { Sentence = Speech[Word].Sentence, Data = Speech[Word].Data } )
|
||||
self:NewTransmission( Speech[Word].Data[1] .. ".wav", Speech[Word].Data[2], Language .. "/" )
|
||||
else
|
||||
else
|
||||
if RemainderSentence and RemainderSentence ~= "" then
|
||||
return self:SpeakWords( RemainderSentence, Speech[Word].Next, Language )
|
||||
end
|
||||
@ -310,11 +310,11 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
||||
end
|
||||
return RemainderSentence
|
||||
end
|
||||
return OriginalSentence
|
||||
return OriginalSentence
|
||||
else
|
||||
return ""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Speak a sentence.
|
||||
@ -333,7 +333,7 @@ function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
|
||||
|
||||
if Digits then
|
||||
if Digits ~= "" and tonumber( Digits ) ~= nil then
|
||||
|
||||
|
||||
-- Construct numbers
|
||||
local Number = tonumber( Digits )
|
||||
local Multiple = nil
|
||||
@ -357,7 +357,7 @@ function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
|
||||
end
|
||||
return RemainderSentence
|
||||
end
|
||||
return OriginalSentence
|
||||
return OriginalSentence
|
||||
else
|
||||
return ""
|
||||
end
|
||||
@ -374,26 +374,26 @@ function RADIOSPEECH:Speak( Sentence, Language )
|
||||
self:I( { Sentence, Language } )
|
||||
|
||||
local Language = Language or "EN"
|
||||
|
||||
|
||||
self:I( { Language = Language } )
|
||||
|
||||
|
||||
-- If there is no node for Speech, then we start at the first nodes of the language.
|
||||
local Speech = self.Speech[Language]
|
||||
|
||||
|
||||
self:I( { Speech = Speech, Language = Language } )
|
||||
|
||||
|
||||
self:NewTransmission( "_In.wav", 0.52, Language .. "/" )
|
||||
|
||||
|
||||
repeat
|
||||
|
||||
Sentence = self:SpeakWords( Sentence, Speech, Language )
|
||||
|
||||
|
||||
self:I( { Sentence = Sentence } )
|
||||
|
||||
Sentence = self:SpeakDigits( Sentence, Speech, Language )
|
||||
|
||||
self:I( { Sentence = Sentence } )
|
||||
|
||||
|
||||
-- Sentence = self:SpeakSymbols( Sentence, Speech )
|
||||
--
|
||||
-- self:I( { Sentence = Sentence } )
|
||||
|
||||
@ -507,7 +507,7 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
||||
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
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..' '
|
||||
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
|
||||
|
||||
@ -767,12 +767,12 @@ end
|
||||
function UTILS.GetCharacters(str)
|
||||
|
||||
local chars={}
|
||||
|
||||
|
||||
for i=1,#str do
|
||||
local c=str:sub(i,i)
|
||||
table.insert(chars, c)
|
||||
end
|
||||
|
||||
|
||||
return chars
|
||||
end
|
||||
|
||||
@ -923,7 +923,7 @@ function UTILS.RandomGaussian(x0, sigma, xmin, xmax, imax)
|
||||
local x1=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
|
||||
|
||||
i=i+1
|
||||
@ -1441,7 +1441,7 @@ function UTILS.GMTToLocalTimeDifference()
|
||||
elseif theatre==DCSMAP.Syria then
|
||||
return 3 -- Damascus is UTC+3 hours
|
||||
elseif theatre==DCSMAP.MarianaIslands then
|
||||
return 10 -- Guam is UTC+10 hours.
|
||||
return 10 -- Guam is UTC+10 hours.
|
||||
else
|
||||
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
|
||||
return 0
|
||||
@ -1615,7 +1615,7 @@ end
|
||||
--@return #table
|
||||
function UTILS.ShuffleTable(t)
|
||||
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
|
||||
end
|
||||
math.random()
|
||||
@ -1639,17 +1639,17 @@ function 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
|
||||
|
||||
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")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
|
||||
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")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
|
||||
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 ")
|
||||
ret_val = true
|
||||
@ -1664,9 +1664,9 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
BASE:T(unit_name .. " all doors are closed")
|
||||
end
|
||||
return ret_val
|
||||
|
||||
|
||||
end -- nil
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@ -1705,13 +1705,13 @@ function UTILS.GenerateVHFrequencies()
|
||||
905,907,920,935,942,950,995,
|
||||
1000,1025,1030,1050,1065,1116,1175,1182,1210
|
||||
}
|
||||
|
||||
|
||||
local FreeVHFFrequencies = {}
|
||||
|
||||
|
||||
-- first range
|
||||
local _start = 200000
|
||||
while _start < 400000 do
|
||||
|
||||
|
||||
-- skip existing NDB frequencies#
|
||||
local _found = false
|
||||
for _, value in pairs(_skipFrequencies) do
|
||||
@ -1725,7 +1725,7 @@ function UTILS.GenerateVHFrequencies()
|
||||
end
|
||||
_start = _start + 10000
|
||||
end
|
||||
|
||||
|
||||
-- second range
|
||||
_start = 400000
|
||||
while _start < 850000 do
|
||||
@ -1742,7 +1742,7 @@ function UTILS.GenerateVHFrequencies()
|
||||
end
|
||||
_start = _start + 10000
|
||||
end
|
||||
|
||||
|
||||
-- third range
|
||||
_start = 850000
|
||||
while _start <= 999000 do -- adjusted for Gazelle
|
||||
@ -1782,7 +1782,7 @@ end
|
||||
-- @return #table Laser Codes.
|
||||
function UTILS.GenerateLaserCodes()
|
||||
local jtacGeneratedLaserCodes = {}
|
||||
|
||||
|
||||
-- helper function
|
||||
local function ContainsDigit(_number, _numberToFind)
|
||||
local _thisNumber = _number
|
||||
@ -1796,14 +1796,14 @@ function UTILS.GenerateLaserCodes()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- generate list of laser codes
|
||||
local _code = 1111
|
||||
local _count = 1
|
||||
while _code < 1777 and _count < 30 do
|
||||
while true do
|
||||
_code = _code + 1
|
||||
if not self:_ContainsDigit(_code, 8)
|
||||
if not ContainsDigit(_code, 8)
|
||||
and not ContainsDigit(_code, 9)
|
||||
and not ContainsDigit(_code, 0) then
|
||||
table.insert(jtacGeneratedLaserCodes, _code)
|
||||
@ -1813,4 +1813,4 @@ function UTILS.GenerateLaserCodes()
|
||||
_count = _count + 1
|
||||
end
|
||||
return jtacGeneratedLaserCodes
|
||||
end
|
||||
end
|
||||
|
||||
@ -142,7 +142,7 @@ AIRBASE.Caucasus = {
|
||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||
--
|
||||
--
|
||||
-- @field Nevada
|
||||
AIRBASE.Nevada = {
|
||||
["Creech_AFB"] = "Creech AFB",
|
||||
@ -197,7 +197,7 @@ AIRBASE.Nevada = {
|
||||
-- * AIRBASE.Normandy.Funtington
|
||||
-- * AIRBASE.Normandy.Tangmere
|
||||
-- * AIRBASE.Normandy.Ford_AF
|
||||
--
|
||||
--
|
||||
-- @field Normandy
|
||||
AIRBASE.Normandy = {
|
||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||
@ -271,7 +271,7 @@ AIRBASE.Normandy = {
|
||||
-- * AIRBASE.PersianGulf.Sirri_Island
|
||||
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
|
||||
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
||||
--
|
||||
--
|
||||
-- @field PersianGulf
|
||||
AIRBASE.PersianGulf = {
|
||||
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
|
||||
@ -554,7 +554,7 @@ function AIRBASE:Register(AirbaseName)
|
||||
|
||||
-- Get descriptors.
|
||||
self.descriptors=self:GetDesc()
|
||||
|
||||
|
||||
-- Debug info.
|
||||
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
|
||||
|
||||
@ -1134,7 +1134,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
|
||||
|
||||
-- Get airbase name.
|
||||
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
|
||||
|
||||
@ -1211,14 +1211,25 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
|
||||
|
||||
-- Get the aircraft size, i.e. it's longest side of x,z.
|
||||
local aircraft=group:GetUnit(1)
|
||||
local _aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
||||
local aircraft = nil
|
||||
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!
|
||||
local _nspots=nspots or group:GetSize()
|
||||
|
||||
-- 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.
|
||||
local validspots={}
|
||||
@ -1341,6 +1352,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
|
||||
-- Retrun spots we found, even if there were not enough.
|
||||
return validspots
|
||||
|
||||
end
|
||||
|
||||
--- Check black and white lists.
|
||||
@ -1359,7 +1371,7 @@ function AIRBASE:_CheckParkingLists(TerminalID)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Check if a whitelist was defined.
|
||||
if self.parkingWhitelist and #self.parkingWhitelist>0 then
|
||||
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.Dubai_Intl 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
|
||||
|
||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||
|
||||
@ -1851,27 +1851,27 @@ do -- Patrol methods
|
||||
|
||||
-- Calculate the new Route.
|
||||
local FromCoord = PatrolGroup:GetCoordinate()
|
||||
|
||||
|
||||
-- test for submarine
|
||||
local depth = 0
|
||||
local IsSub = false
|
||||
if PatrolGroup:IsShip() then
|
||||
local navalvec3 = FromCoord:GetVec3()
|
||||
if navalvec3.y < 0 then
|
||||
if navalvec3.y < 0 then
|
||||
depth = navalvec3.y
|
||||
IsSub = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local Waypoint = Waypoints[1]
|
||||
local Speed = Waypoint.speed or (20 / 3.6)
|
||||
local From = FromCoord:WaypointGround( Speed )
|
||||
|
||||
if IsSub then
|
||||
|
||||
if IsSub then
|
||||
From = FromCoord:WaypointNaval( Speed, Waypoint.alt )
|
||||
end
|
||||
|
||||
|
||||
table.insert( Waypoints, 1, From )
|
||||
|
||||
local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" )
|
||||
@ -1916,7 +1916,7 @@ do -- Patrol methods
|
||||
local IsSub = false
|
||||
if PatrolGroup:IsShip() then
|
||||
local navalvec3 = FromCoord:GetVec3()
|
||||
if navalvec3.y < 0 then
|
||||
if navalvec3.y < 0 then
|
||||
depth = navalvec3.y
|
||||
IsSub = true
|
||||
end
|
||||
@ -1982,16 +1982,16 @@ do -- Patrol methods
|
||||
self:F( { PatrolGroup = PatrolGroup:GetName() } )
|
||||
|
||||
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
|
||||
|
||||
|
||||
-- Calculate the new Route.
|
||||
local FromCoord = PatrolGroup:GetCoordinate()
|
||||
|
||||
|
||||
-- test for submarine
|
||||
local depth = 0
|
||||
local IsSub = false
|
||||
if PatrolGroup:IsShip() then
|
||||
local navalvec3 = FromCoord:GetVec3()
|
||||
if navalvec3.y < 0 then
|
||||
if navalvec3.y < 0 then
|
||||
depth = navalvec3.y
|
||||
IsSub = true
|
||||
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.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number speed Speed of the controllable, default 20
|
||||
-- @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 shortcut If true and onroad is set, take a shorter route - if available - off road, default false
|
||||
-- @param #number speed Speed of the controllable, default 20
|
||||
-- @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 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
|
||||
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut)
|
||||
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut, formation)
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local _coord = self:GetCoordinate()
|
||||
@ -3789,14 +3790,14 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc
|
||||
local _grptsk = {}
|
||||
local _candoroad = false
|
||||
local _shortcut = shortcut or false
|
||||
local _formation = formation or "Off Road"
|
||||
|
||||
-- create a DCS Task an push it on the group
|
||||
-- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments)
|
||||
if onroad then
|
||||
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut)
|
||||
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut)
|
||||
self:Route(_grptsk,5)
|
||||
else
|
||||
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road")
|
||||
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation)
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@ -1132,7 +1132,7 @@ end
|
||||
-- @return #number Number of shells left.
|
||||
-- @return #number Number of rockets left.
|
||||
-- @return #number Number of bombs left.
|
||||
-- @return #number Number of missiles left.
|
||||
-- @return #number Number of missiles left.
|
||||
function GROUP:GetAmmunition()
|
||||
self:F( self.ControllableName )
|
||||
|
||||
@ -1142,6 +1142,7 @@ function GROUP:GetAmmunition()
|
||||
local Nshells=0
|
||||
local Nrockets=0
|
||||
local Nmissiles=0
|
||||
local Nbombs=0
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
@ -1150,18 +1151,19 @@ function GROUP:GetAmmunition()
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
-- Get ammo of the unit
|
||||
local ntot, nshells, nrockets, nmissiles = Unit:GetAmmunition()
|
||||
local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition()
|
||||
|
||||
Ntot=Ntot+ntot
|
||||
Nshells=Nshells+nshells
|
||||
Nrockets=Nrockets+nrockets
|
||||
Nmissiles=Nmissiles+nmissiles
|
||||
Nmissiles=Nmissiles+nmissiles
|
||||
Nbombs=Nbombs+nbombs
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Ntot, Nshells, Nrockets, Nmissiles
|
||||
return Ntot, Nshells, Nrockets, Nbombs, Nmissiles
|
||||
end
|
||||
|
||||
|
||||
@ -2589,6 +2591,17 @@ function GROUP:SetCommandImmortal(switch)
|
||||
return self
|
||||
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
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
|
||||
@ -1517,6 +1517,7 @@ do -- Cargo
|
||||
["Ural-4320 APA-5D"] = 10,
|
||||
["Ural-4320T"] = 14,
|
||||
["ZBD04A"] = 7, -- new by kappa
|
||||
["VAB_Mephisto"] = 8, -- new by Apple
|
||||
}
|
||||
|
||||
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95
|
||||
|
||||
@ -637,6 +637,18 @@ function UNIT:GetAmmo()
|
||||
return nil
|
||||
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.
|
||||
-- @param #UNIT self
|
||||
-- @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
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user