mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b9143d3f1 | ||
|
|
0388d47f23 | ||
|
|
de9b173d9b | ||
|
|
2cecc526fb | ||
|
|
968d178317 | ||
|
|
3c477b872a | ||
|
|
77e6088114 | ||
|
|
edd6594953 | ||
|
|
f8c05c99d0 | ||
|
|
50f6d98b49 | ||
|
|
147eeb05f6 | ||
|
|
d8cb15a577 | ||
|
|
0daac876ea | ||
|
|
1832125022 | ||
|
|
c311c40b72 | ||
|
|
db516a2077 | ||
|
|
ff8766669c | ||
|
|
06dc9a732e | ||
|
|
50c74d0852 | ||
|
|
1c97eb6f3c | ||
|
|
69449430d1 | ||
|
|
663cd34aa3 | ||
|
|
cfed6f5153 | ||
|
|
2b22d5288c | ||
|
|
a64424ecc8 | ||
|
|
fd1b2ecb86 | ||
|
|
6cae3e62cf | ||
|
|
05ce7e4513 | ||
|
|
136bd19f19 | ||
|
|
8873504daf | ||
|
|
a844a5d697 | ||
|
|
a49f4eaa21 | ||
|
|
e6e2651f8c | ||
|
|
b93ba13644 | ||
|
|
e4a51951b0 | ||
|
|
ad56e39942 | ||
|
|
ea09dc5a6e | ||
|
|
8ecfd913a3 | ||
|
|
53367c786e | ||
|
|
db5797bb4e | ||
|
|
5e8fe97752 | ||
|
|
393fa0bfbb | ||
|
|
4f51884b9d | ||
|
|
4c5c320073 | ||
|
|
9098590568 | ||
|
|
555bb7e68b | ||
|
|
c0a18957f0 | ||
|
|
2cf939560e | ||
|
|
9d3a7aae78 | ||
|
|
f6ed592f92 | ||
|
|
c98757d13c | ||
|
|
17378f509e | ||
|
|
7f18ea0e7a | ||
|
|
5172619cb1 | ||
|
|
3962529698 | ||
|
|
6481d5d41e | ||
|
|
6cc3d73c04 | ||
|
|
e541e39403 | ||
|
|
c7ea45e5fd | ||
|
|
20f28b3d2c | ||
|
|
f3f63ab8aa | ||
|
|
e91090cfff | ||
|
|
1a7fb3c13e | ||
|
|
59857ed79d | ||
|
|
4797665939 | ||
|
|
b89749036d | ||
|
|
c6268488de | ||
|
|
de04369703 | ||
|
|
05b6f19a87 | ||
|
|
2753df8216 |
@@ -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 )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -136,7 +136,7 @@ function DATABASE:New()
|
||||
self:_RegisterGroupsAndUnits()
|
||||
self:_RegisterClients()
|
||||
self:_RegisterStatics()
|
||||
self:_RegisterAirbases()
|
||||
--self:_RegisterAirbases()
|
||||
--self:_RegisterPlayers()
|
||||
|
||||
self.UNITS_Position = 0
|
||||
|
||||
@@ -3998,7 +3998,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
|
||||
@@ -4746,7 +4764,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
|
||||
|
||||
@@ -498,8 +498,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
|
||||
@@ -510,15 +510,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.
|
||||
@@ -746,11 +746,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.
|
||||
@@ -1165,20 +1165,20 @@ end
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return DCS#Vec2 The random location within the zone.
|
||||
function ZONE_RADIUS:GetRandomVec2( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
|
||||
local angle = math.random() * math.pi * 2;
|
||||
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
||||
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
||||
local angle = math.random() * math.pi * 2;
|
||||
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
||||
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
||||
|
||||
self:T( { Point } )
|
||||
self:T( { Point } )
|
||||
|
||||
return Point
|
||||
return Point
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within 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
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
-- 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
|
||||
@@ -195,6 +195,9 @@ MANTIS = {
|
||||
TimeStamp = 0,
|
||||
state2flag = false,
|
||||
SamStateTracker = {},
|
||||
DLink = false,
|
||||
DLTimeStamp = 0,
|
||||
Padding = 10,
|
||||
}
|
||||
|
||||
--- Advanced state enumerator
|
||||
@@ -219,7 +222,8 @@ 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
|
||||
--
|
||||
@@ -241,7 +245,7 @@ do
|
||||
-- `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
|
||||
@@ -278,7 +282,9 @@ do
|
||||
self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
||||
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
|
||||
@@ -325,7 +331,7 @@ do
|
||||
end
|
||||
|
||||
-- @field #string version
|
||||
self.version="0.5.2"
|
||||
self.version="0.6.2"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
--- FSM Functions ---
|
||||
@@ -593,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
|
||||
@@ -619,6 +625,17 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set using an #INTEL_DLINK object instead of #DETECTION. Requires Develop branch of Moose.lua.
|
||||
-- @param #MANTIS self
|
||||
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
|
||||
function MANTIS:SetUsingDLink(DLink)
|
||||
self:T(self.lid .. "SetUsingDLink")
|
||||
self.DLink = true
|
||||
self.Detection = DLink
|
||||
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
|
||||
@@ -633,10 +650,10 @@ 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!")
|
||||
--self:T(self.lid.." HQ is dead!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -650,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
|
||||
@@ -666,7 +683,7 @@ 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
|
||||
@@ -682,10 +699,8 @@ do
|
||||
-- @return #number Previous state for tracking 0, 1, or 2
|
||||
function MANTIS:_CalcAdvState()
|
||||
self:T(self.lid .. "CalcAdvState")
|
||||
local text = self.lid.." Calculating Advanced State"
|
||||
self:T(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
local m=MESSAGE:New(self.lid.." Calculating Advanced State",10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid.." Calculating Advanced State") end
|
||||
-- start check
|
||||
local currstate = self.adv_state -- save curr state for comparison later
|
||||
local EWR_State = self:_CheckEWRState()
|
||||
@@ -703,10 +718,12 @@ do
|
||||
local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8
|
||||
ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6
|
||||
local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78
|
||||
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
|
||||
self:T(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
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)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
end
|
||||
return newinterval, currstate
|
||||
end
|
||||
|
||||
@@ -716,13 +733,13 @@ do
|
||||
-- @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
|
||||
@@ -739,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)
|
||||
@@ -752,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
|
||||
@@ -778,12 +795,14 @@ do
|
||||
for _,_coord in pairs (set) do
|
||||
local coord = _coord -- get current coord to check
|
||||
-- output for cross-check
|
||||
local dectstring = coord:ToStringLLDMS()
|
||||
local samstring = samcoordinate:ToStringLLDMS()
|
||||
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
|
||||
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)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
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)
|
||||
end
|
||||
-- end output to cross-check
|
||||
if targetdistance <= radius then
|
||||
return true, targetdistance
|
||||
@@ -888,7 +907,7 @@ 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
|
||||
@@ -999,9 +1018,11 @@ do
|
||||
self:__ShoradActivated(1,name, radius, ontime)
|
||||
end
|
||||
-- debug output
|
||||
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
if self.debug or self.verbose then
|
||||
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
end
|
||||
end --end alive
|
||||
else
|
||||
if samgroup:IsAlive() then
|
||||
@@ -1014,9 +1035,11 @@ do
|
||||
self:__GreenState(1,samgroup)
|
||||
self.SamStateTracker[name] = "GREEN"
|
||||
end
|
||||
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
|
||||
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
|
||||
end
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for for loop
|
||||
@@ -1066,6 +1089,20 @@ do
|
||||
end -- end newstate vs oldstate
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Check DLink state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_CheckDLinkState()
|
||||
self:T(self.lid .. "_CheckDLinkState")
|
||||
local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
|
||||
local TS = timer.getAbsTime()
|
||||
if not dlink:Is("Running") and (TS - self.DLTimeStamp > 29) then
|
||||
self.DLink = false
|
||||
self.Detection = self:StartDetection() -- fall back
|
||||
self:I(self.lid .. "Intel DLink not running - switching back to single detection!")
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to set start state
|
||||
-- @param #MANTIS self
|
||||
@@ -1077,11 +1114,13 @@ do
|
||||
self:T({From, Event, To})
|
||||
self:T(self.lid.."Starting MANTIS")
|
||||
self:SetSAMStartState()
|
||||
self.Detection = self:StartDetection()
|
||||
if not self.DLink then
|
||||
self.Detection = self:StartDetection()
|
||||
end
|
||||
if self.advAwacs then
|
||||
self.AWACS_Detection = self:StartAwacsDetection()
|
||||
end
|
||||
self:__Status(self.detectinterval)
|
||||
self:__Status(-math.random(1,10))
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1120,11 +1159,16 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
-- timer for advanced state check
|
||||
-- advanced state check
|
||||
if self.advanced then
|
||||
self:_CheckAdvState()
|
||||
end
|
||||
|
||||
-- check DLink state
|
||||
if self.DLink then
|
||||
self:_CheckDLinkState()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1136,6 +1180,13 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterStatus(From,Event,To)
|
||||
self:T({From, Event, To})
|
||||
-- Display some states
|
||||
if self.debug then
|
||||
self:I(self.lid .. "Status Report")
|
||||
for _name,_state in pairs(self.SamStateTracker) do
|
||||
self:I(string.format("Site %s\tStatus %s",_name,_state))
|
||||
end
|
||||
end
|
||||
local interval = self.detectinterval * -1
|
||||
self:__Status(interval)
|
||||
return self
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- ## 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 <EFBFBD>
|
||||
-- * 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.
|
||||
|
||||
@@ -5371,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
|
||||
if spawnonground then
|
||||
|
||||
-- Sh<EFBFBD>ps and FARPS seem to have a build in queue.
|
||||
-- Sh°ps and FARPS seem to have a build in queue.
|
||||
if spawnonship or spawnonfarp or spawnonrunway or automatic then
|
||||
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
--
|
||||
-- ### 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.
|
||||
@@ -48,7 +49,8 @@ SEAD = {
|
||||
},
|
||||
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",
|
||||
@@ -68,17 +70,35 @@ SEAD = {
|
||||
["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.
|
||||
-- @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
|
||||
-- @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 )
|
||||
@@ -91,8 +111,13 @@ function SEAD:New( SEADGroupPrefixes )
|
||||
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,7 +127,7 @@ 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
|
||||
@@ -120,7 +145,7 @@ end
|
||||
-- @param #number range Set the engagement range in percent, e.g. 50
|
||||
-- @return 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,53 +155,100 @@ 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
|
||||
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
|
||||
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( SEADGroupPrefix )
|
||||
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
|
||||
SEADGroupFound = true
|
||||
self:T( '*** SEAD - Group Found' )
|
||||
self:T( '*** SEAD - Group Match Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -185,42 +257,67 @@ function SEAD:HandleEventShot( EventData )
|
||||
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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
if UnitHit.ClassName ~= "SCENERY" then
|
||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -890,12 +891,14 @@ do -- ZONE_CAPTURE_COALITION
|
||||
end
|
||||
|
||||
-- Status text.
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
if false then
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
end
|
||||
self:I(text)
|
||||
end
|
||||
self:I(text)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ _DATABASE:_RegisterCargos()
|
||||
|
||||
--- Register zones.
|
||||
_DATABASE:_RegisterZones()
|
||||
_DATABASE:_RegisterAirbases()
|
||||
|
||||
--- Check if os etc is available.
|
||||
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||
|
||||
@@ -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.
|
||||
@@ -108,6 +110,7 @@
|
||||
-- ### 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
|
||||
@@ -725,7 +730,7 @@
|
||||
--
|
||||
-- The same holds true after the recovery window closes. The carrier will head back to the place where he left its assigned route and resume the path to the next waypoint defined in the mission editor.
|
||||
--
|
||||
-- Note that the carrier will only head into the wind, if the wind direction is different by more than 5° from the current heading of the carrier (the angled runway, if any, fis taken into account here).
|
||||
-- Note that the carrier will only head into the wind, if the wind direction is different by more than 5° from the current heading of the carrier (the angled runway, if any, fis taken into account here).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -864,10 +869,10 @@
|
||||
--
|
||||
-- The graph displays the lineup error (LUE) as a function of the distance to the carrier.
|
||||
--
|
||||
-- The pilot approaches the carrier from the port side, LUE>0°, at a distance of ~1 NM.
|
||||
-- At the beginning of the groove (X), he significantly overshoots to the starboard side (LUE<5°).
|
||||
-- The pilot approaches the carrier from the port side, LUE>0°, at a distance of ~1 NM.
|
||||
-- At the beginning of the groove (X), he significantly overshoots to the starboard side (LUE<5°).
|
||||
-- In the middle (IM), he performs good corrections and smoothly reduces the lineup error.
|
||||
-- Finally, at a distance of ~0.3 NM (IC) he has corrected his lineup with the runway to a reasonable level, |LUE|<0.5°.
|
||||
-- Finally, at a distance of ~0.3 NM (IC) he has corrected his lineup with the runway to a reasonable level, |LUE|<0.5°.
|
||||
--
|
||||
-- ## Glideslope Error
|
||||
--
|
||||
@@ -876,7 +881,7 @@
|
||||
-- The graph displays the glideslope error (GSE) as a function of the distance to the carrier.
|
||||
--
|
||||
-- In this case the pilot already enters the groove (X) below the optimal glideslope. He is not able to correct his height in the IM part and
|
||||
-- stays significantly too low. In close, he performs a harsh correction to gain altitude and ends up even slightly too high (GSE>0.5°).
|
||||
-- stays significantly too low. In close, he performs a harsh correction to gain altitude and ends up even slightly too high (GSE>0.5°).
|
||||
-- At his point further corrections are necessary.
|
||||
--
|
||||
-- ## Angle of Attack
|
||||
@@ -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)
|
||||
@@ -2438,7 +2454,7 @@ end
|
||||
-- @param #number duration Default duration of the recovery in minutes. Default 30 min.
|
||||
-- @param #number windondeck Default wind on deck in knots. Default 25 knots.
|
||||
-- @param #boolean uturn U-turn after recovery window closes on=true or off=false/nil. Default off.
|
||||
-- @param #number offset Relative Marshal radial in degrees for Case II/III recoveries. Default 30°.
|
||||
-- @param #number offset Relative Marshal radial in degrees for Case II/III recoveries. Default 30°.
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetMenuRecovery(duration, windondeck, uturn, offset)
|
||||
|
||||
@@ -3878,7 +3894,7 @@ function AIRBOSS:_CheckRecoveryTimes()
|
||||
-- Check if time is less than 5 minutes.
|
||||
if nextwindow.WIND and nextwindow.START-time<self.dTturn and not self.turnintowind then
|
||||
|
||||
-- Check that wind is blowing from a direction > 5° different from the current heading.
|
||||
-- Check that wind is blowing from a direction > 5° different from the current heading.
|
||||
local hdg=self:GetHeading()
|
||||
local wind=self:GetHeadingIntoWind()
|
||||
local delta=self:_GetDeltaHeading(hdg, wind)
|
||||
@@ -3896,7 +3912,7 @@ function AIRBOSS:_CheckRecoveryTimes()
|
||||
end
|
||||
|
||||
--Debug info
|
||||
self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s", hdg, wind,UTILS.MpsToKnots(vwind), delta, tostring(uturn)))
|
||||
self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s", hdg, wind,UTILS.MpsToKnots(vwind), delta, tostring(uturn)))
|
||||
|
||||
-- Time into the wind 1 day or if longer recovery time + the 5 min early.
|
||||
local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn, 60*60*24)
|
||||
@@ -4401,6 +4417,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.
|
||||
@@ -4555,6 +4650,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
|
||||
@@ -4613,6 +4709,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
|
||||
@@ -4880,6 +4977,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",
|
||||
@@ -5540,14 +5645,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
|
||||
@@ -5807,7 +5912,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
|
||||
@@ -6746,8 +6851,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)
|
||||
@@ -6933,7 +7038,7 @@ function AIRBOSS:_AddMarshalGroup(flight, stack)
|
||||
-- For case 1 we want the BRC but above routine return FB.
|
||||
radial=self:GetBRC()
|
||||
end
|
||||
local text=string.format("Select TACAN %03d°, channel %d%s (%s)", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
|
||||
local text=string.format("Select TACAN %03d°, channel %d%s (%s)", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
|
||||
self:MessageToPlayer(flight, text, nil, "")
|
||||
end
|
||||
|
||||
@@ -8592,7 +8697,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)
|
||||
@@ -8627,7 +8732,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()
|
||||
@@ -9346,7 +9451,7 @@ function AIRBOSS:_Initial(playerData)
|
||||
-- Hook down for students.
|
||||
if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then
|
||||
if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
|
||||
hint=hint.." - Hook down, SAS on, Wing Sweep 68°!"
|
||||
hint=hint.." - Hook down, SAS on, Wing Sweep 68°!"
|
||||
else
|
||||
hint=hint.." - Hook down!"
|
||||
end
|
||||
@@ -9534,8 +9639,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
|
||||
|
||||
@@ -9671,8 +9778,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
|
||||
|
||||
@@ -9717,8 +9824,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
|
||||
|
||||
@@ -9755,7 +9864,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
|
||||
@@ -10429,7 +10538,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
|
||||
@@ -11172,7 +11281,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
|
||||
|
||||
@@ -11225,7 +11334,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)
|
||||
|
||||
@@ -11331,15 +11440,15 @@ function AIRBOSS:_AttitudeMonitor(playerData)
|
||||
|
||||
-- Output
|
||||
local text=string.format("Pattern step: %s", step)
|
||||
text=text..string.format("\nAoA=%.1f° = %.1f Units | |V|=%.1f knots", aoa, self:_AoADeg2Units(playerData, aoa), UTILS.MpsToKnots(vabs))
|
||||
text=text..string.format("\nAoA=%.1f° = %.1f Units | |V|=%.1f knots", aoa, self:_AoADeg2Units(playerData, aoa), UTILS.MpsToKnots(vabs))
|
||||
if self.Debug then
|
||||
-- Velocity vector.
|
||||
text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s", velo.x, velo.y, velo.z)
|
||||
--Wind vector.
|
||||
text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s", wind.x, wind.y, wind.z)
|
||||
end
|
||||
text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw)
|
||||
text=text..string.format("\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y*196.85)
|
||||
text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw)
|
||||
text=text..string.format("\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y*196.85)
|
||||
local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit)
|
||||
-- Get player velocity in km/h.
|
||||
local vplayer=playerData.unit:GetVelocityKMH()
|
||||
@@ -11360,14 +11469,14 @@ function AIRBOSS:_AttitudeMonitor(playerData)
|
||||
playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
|
||||
local lue=self:_Lineup(playerData.unit, true)
|
||||
local gle=self:_Glideslope(playerData.unit)
|
||||
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
|
||||
text=text..string.format("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units", lue, gle, self:_AoADeg2Units(playerData, aoa))
|
||||
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
|
||||
text=text..string.format("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units", lue, gle, self:_AoADeg2Units(playerData, aoa))
|
||||
local grade, points, analysis=self:_LSOgrade(playerData)
|
||||
text=text..string.format("\nTgroove=%.1f sec", self:_GetTimeInGroove(playerData))
|
||||
text=text..string.format("\nGrade: %s %.1f PT - %s", grade, points, analysis)
|
||||
else
|
||||
text=text..string.format("\nR=%.2f NM | X=%d Z=%d m", UTILS.MetersToNM(rho), dx, dz)
|
||||
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
|
||||
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi)
|
||||
end
|
||||
|
||||
MESSAGE:New(text, 1, nil , true):ToClient(playerData.client)
|
||||
@@ -11516,7 +11625,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()
|
||||
@@ -11538,7 +11647,24 @@ function AIRBOSS:_GetOptLandingCoordinate()
|
||||
|
||||
-- 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))
|
||||
|
||||
else
|
||||
|
||||
-- Ideally we want to land between 2nd and 3rd wire.
|
||||
@@ -11573,7 +11699,22 @@ 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
|
||||
|
||||
return self.landingspotcoord
|
||||
@@ -12063,8 +12204,13 @@ end
|
||||
-- * 12-21 seconds: OK (15-18 is ideal)
|
||||
-- * 22-24 seconds: Fair "(OK)
|
||||
-- * > 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.
|
||||
@@ -12073,7 +12219,7 @@ function AIRBOSS:_EvalGrooveTime(playerData)
|
||||
|
||||
-- Time in groove.
|
||||
local t=playerData.Tgroove
|
||||
|
||||
|
||||
local grade=""
|
||||
if t<9 then
|
||||
grade="_NESA_"
|
||||
@@ -12083,14 +12229,26 @@ 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
|
||||
|
||||
|
||||
-- The unicorn!
|
||||
if t>=16.4 and t<=16.6 then
|
||||
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
|
||||
@@ -12108,7 +12266,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)
|
||||
@@ -12117,25 +12275,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
|
||||
@@ -12148,7 +12318,8 @@ function AIRBOSS:_LSOgrade(playerData)
|
||||
grade="OK"
|
||||
points=4.0
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Replace" )"( and "__"
|
||||
G=G:gsub("%)%(", "")
|
||||
@@ -12444,7 +12615,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
|
||||
@@ -12695,7 +12866,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
|
||||
if self.holdingoffset<0 then
|
||||
turn="left"
|
||||
end
|
||||
hint=hint..string.format("\nTurn %s and select TACAN %03d°.", turn, radial)
|
||||
hint=hint..string.format("\nTurn %s and select TACAN %03d°.", turn, radial)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -12704,7 +12875,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
|
||||
if playerData.step==AIRBOSS.PatternStep.DIRTYUP then
|
||||
if playerData.difficulty==AIRBOSS.Difficulty.EASY then
|
||||
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||
hint=hint.."\nFAF! Checks completed. Nozzles 50°."
|
||||
hint=hint.."\nFAF! Checks completed. Nozzles 50°."
|
||||
else
|
||||
--TODO: Tomcat?
|
||||
hint=hint.."\nDirty up! Hook, gear and flaps down."
|
||||
@@ -12773,14 +12944,14 @@ function AIRBOSS:_StepHint(playerData, step)
|
||||
-- Late break.
|
||||
if step==AIRBOSS.PatternStep.LATEBREAK then
|
||||
if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
|
||||
hint=hint.."\nWing Sweep 20°, Gear DOWN < 280 KIAS."
|
||||
hint=hint.."\nWing Sweep 20°, Gear DOWN < 280 KIAS."
|
||||
end
|
||||
end
|
||||
|
||||
-- Abeam.
|
||||
if step==AIRBOSS.PatternStep.ABEAM then
|
||||
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||
hint=hint.."\nNozzles 50°-60°. Antiskid OFF. Lights OFF."
|
||||
hint=hint.."\nNozzles 50°-60°. Antiskid OFF. Lights OFF."
|
||||
elseif playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
|
||||
hint=hint.."\nSlats/Flaps EXTENDED < 225 KIAS. DLC SELECTED. Auto Throttle IF DESIRED."
|
||||
else
|
||||
@@ -13215,7 +13386,7 @@ function AIRBOSS:_Debrief(playerData)
|
||||
end
|
||||
|
||||
-- Re-enter message.
|
||||
local text=string.format("fly heading %03d° for %d NM to re-enter the pattern.", heading, UTILS.MetersToNM(distance))
|
||||
local text=string.format("fly heading %03d° for %d NM to re-enter the pattern.", heading, UTILS.MetersToNM(distance))
|
||||
self:MessageToPlayer(playerData, text, "LSO", nil, nil, false, 5)
|
||||
|
||||
else
|
||||
@@ -13411,9 +13582,9 @@ function AIRBOSS:_CheckCollisionCoord(coordto, coordfrom)
|
||||
|
||||
local text=""
|
||||
if clear then
|
||||
text=string.format("Path into direction %03d° is clear for the next %.1f NM.", direction, UTILS.MetersToNM(d))
|
||||
text=string.format("Path into direction %03d° is clear for the next %.1f NM.", direction, UTILS.MetersToNM(d))
|
||||
else
|
||||
text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.", UTILS.MetersToNM(d), direction)
|
||||
text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.", UTILS.MetersToNM(d), direction)
|
||||
end
|
||||
self:T2(self.lid..text)
|
||||
|
||||
@@ -13473,7 +13644,7 @@ function AIRBOSS:_Pathfinder()
|
||||
local collision=self:_CheckFreePathToNextWP(fromcoord)
|
||||
|
||||
-- Debug info.
|
||||
self:T2(self.lid..string.format("Pathfinder d=%.1f m, direction=%03d°, collision=%s", distance, direction, tostring(collision)))
|
||||
self:T2(self.lid..string.format("Pathfinder d=%.1f m, direction=%03d°, collision=%s", distance, direction, tostring(collision)))
|
||||
|
||||
-- If path is clear, we start a little detour.
|
||||
if not collision then
|
||||
@@ -13907,7 +14078,7 @@ function AIRBOSS:_CheckPatternUpdate()
|
||||
-- Update if carrier moves by more than 2.5 NM.
|
||||
local Dupdate=UTILS.NMToMeters(2.5)
|
||||
|
||||
-- Update if carrier turned by more than 5°.
|
||||
-- Update if carrier turned by more than 5°.
|
||||
local Hupdate=5
|
||||
|
||||
-----------------------
|
||||
@@ -13941,7 +14112,7 @@ function AIRBOSS:_CheckPatternUpdate()
|
||||
-- Check if orientation changed.
|
||||
local Hchange=false
|
||||
if math.abs(deltaHeading)>=Hupdate then
|
||||
self:T(self.lid..string.format("Carrier heading changed by %d°.", deltaHeading))
|
||||
self:T(self.lid..string.format("Carrier heading changed by %d°.", deltaHeading))
|
||||
Hchange=true
|
||||
end
|
||||
|
||||
@@ -14334,17 +14505,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
|
||||
@@ -15642,7 +15813,7 @@ end
|
||||
function AIRBOSS:_MarshalCallNewFinalBearing(FB)
|
||||
|
||||
-- Subtitle.
|
||||
local text=string.format("new final bearing %03d°.", FB)
|
||||
local text=string.format("new final bearing %03d°.", FB)
|
||||
|
||||
-- Debug message.
|
||||
self:I(self.lid..text)
|
||||
@@ -15665,7 +15836,7 @@ end
|
||||
function AIRBOSS:_MarshalCallCarrierTurnTo(hdg)
|
||||
|
||||
-- Subtitle.
|
||||
local text=string.format("carrier is now starting turn to heading %03d°.", hdg)
|
||||
local text=string.format("carrier is now starting turn to heading %03d°.", hdg)
|
||||
|
||||
-- Debug message.
|
||||
self:I(self.lid..text)
|
||||
@@ -15718,11 +15889,11 @@ function AIRBOSS:_MarshalCallRecoveryStart(case)
|
||||
-- Debug output.
|
||||
local text=string.format("Starting aircraft recovery Case %d ops.", case)
|
||||
if case==1 then
|
||||
text=text..string.format(" BRC %03d°.", self:GetBRC())
|
||||
text=text..string.format(" BRC %03d°.", self:GetBRC())
|
||||
elseif case==2 then
|
||||
text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC())
|
||||
text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC())
|
||||
elseif case==3 then
|
||||
text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false))
|
||||
text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false))
|
||||
end
|
||||
self:T(self.lid..text)
|
||||
|
||||
@@ -15767,7 +15938,7 @@ function AIRBOSS:_MarshalCallArrived(modex, case, brc, altitude, charlie, qfe)
|
||||
local CT=UTILS.Split(clock[1], ":")
|
||||
|
||||
-- Subtitle text.
|
||||
local text=string.format("Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.", case, brc, angels, charlie, qfe)
|
||||
local text=string.format("Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.", case, brc, angels, charlie, qfe)
|
||||
|
||||
-- Debug message.
|
||||
self:I(self.lid..text)
|
||||
@@ -15944,11 +16115,11 @@ function AIRBOSS:_AddF10Commands(_unitName)
|
||||
missionCommands.addCommandForGroup(gid, "60 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 60)
|
||||
missionCommands.addCommandForGroup(gid, "90 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 90)
|
||||
local _menusetrtime=missionCommands.addSubMenuForGroup(gid, "Set Marshal Radial", _skipperPath)
|
||||
missionCommands.addCommandForGroup(gid, "+30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 30)
|
||||
missionCommands.addCommandForGroup(gid, "+15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 15)
|
||||
missionCommands.addCommandForGroup(gid, "0°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0)
|
||||
missionCommands.addCommandForGroup(gid, "-15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -15)
|
||||
missionCommands.addCommandForGroup(gid, "-30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -30)
|
||||
missionCommands.addCommandForGroup(gid, "+30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 30)
|
||||
missionCommands.addCommandForGroup(gid, "+15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 15)
|
||||
missionCommands.addCommandForGroup(gid, "0°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0)
|
||||
missionCommands.addCommandForGroup(gid, "-15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -15)
|
||||
missionCommands.addCommandForGroup(gid, "-30°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -30)
|
||||
missionCommands.addCommandForGroup(gid, "U-turn On/Off", _skipperPath, self._SkipperRecoveryUturn, self, _unitName)
|
||||
missionCommands.addCommandForGroup(gid, "Start CASE I", _skipperPath, self._SkipperStartRecovery, self, _unitName, 1)
|
||||
missionCommands.addCommandForGroup(gid, "Start CASE II", _skipperPath, self._SkipperStartRecovery, self, _unitName, 2)
|
||||
@@ -16005,7 +16176,7 @@ function AIRBOSS:_SkipperStartRecovery(_unitName, case)
|
||||
-- Inform player.
|
||||
local text=string.format("affirm, Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.", case, self.skipperTime, self.skipperSpeed, tostring(self.skipperUturn))
|
||||
if case>1 then
|
||||
text=text..string.format(" Marshal radial %d°.", self.skipperOffset)
|
||||
text=text..string.format(" Marshal radial %d°.", self.skipperOffset)
|
||||
end
|
||||
if self:IsRecovering() then
|
||||
text="negative, carrier is already recovering."
|
||||
@@ -16071,7 +16242,7 @@ function AIRBOSS:_SkipperRecoveryOffset(_unitName, offset)
|
||||
if playerData then
|
||||
|
||||
-- Inform player.
|
||||
local text=string.format("roger, relative CASE II/III Marshal radial set to %d°.", offset)
|
||||
local text=string.format("roger, relative CASE II/III Marshal radial set to %d°.", offset)
|
||||
self:MessageToPlayer(playerData, text, "AIRBOSS")
|
||||
|
||||
self.skipperOffset=offset
|
||||
@@ -16534,7 +16705,7 @@ function AIRBOSS:_RequestCommence(_unitName)
|
||||
-- For case 1 we want the BRC but above routine return FB.
|
||||
radial=self:GetBRC()
|
||||
end
|
||||
text=text..string.format("\nSelect TACAN %03d°, Channel %d%s (%s).\n", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
|
||||
text=text..string.format("\nSelect TACAN %03d°, Channel %d%s (%s).\n", radial, self.TACANchannel,self.TACANmode, self.TACANmorse)
|
||||
end
|
||||
|
||||
-- TODO: Inform section members.
|
||||
@@ -17146,7 +17317,7 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
|
||||
-- Only include current and future recovery windows.
|
||||
if Tabs<recovery.STOP then
|
||||
-- Output text.
|
||||
recoverytext=recoverytext..string.format("\n* %s - %s: Case %d (%d°)", UTILS.SecondsToClock(recovery.START), UTILS.SecondsToClock(recovery.STOP), recovery.CASE, recovery.OFFSET)
|
||||
recoverytext=recoverytext..string.format("\n* %s - %s: Case %d (%d°)", UTILS.SecondsToClock(recovery.START), UTILS.SecondsToClock(recovery.STOP), recovery.CASE, recovery.OFFSET)
|
||||
if recovery.WIND then
|
||||
recoverytext=recoverytext..string.format(" @ %.1f kts wind", recovery.SPEED)
|
||||
end
|
||||
@@ -17187,9 +17358,9 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
|
||||
text=text..string.format("Case %d recovery ops\n", self.case)
|
||||
else
|
||||
local radial=self:GetRadial(self.case, true, true, false)
|
||||
text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n", self.case, radial)
|
||||
text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n", self.case, radial)
|
||||
end
|
||||
text=text..string.format("BRC %03d° - FB %03d°\n", self:GetBRC(), self:GetFinalBearing(true))
|
||||
text=text..string.format("BRC %03d° - FB %03d°\n", self:GetBRC(), self:GetFinalBearing(true))
|
||||
text=text..string.format("Speed %.1f kts - Wind on deck %.1f kts\n", carrierspeed, wind)
|
||||
text=text..string.format("Tower frequency %.3f MHz\n", self.TowerFreq)
|
||||
text=text..string.format("Marshal radio %.3f MHz\n", self.MarshalFreq)
|
||||
@@ -17250,10 +17421,10 @@ function AIRBOSS:_DisplayCarrierWeather(_unitname)
|
||||
local WodPA=UTILS.MpsToKnots(WodPA)
|
||||
local WodPP=UTILS.MpsToKnots(WodPP)
|
||||
|
||||
local WD=string.format('%03d°', Wd)
|
||||
local Ts=string.format("%d°C",T)
|
||||
local WD=string.format('%03d°', Wd)
|
||||
local Ts=string.format("%d°C",T)
|
||||
|
||||
local tT=string.format("%d°C",T)
|
||||
local tT=string.format("%d°C",T)
|
||||
local tW=string.format("%.1f knots", UTILS.MpsToKnots(Ws))
|
||||
local tP=string.format("%.2f inHg", UTILS.hPa2inHg(P))
|
||||
|
||||
@@ -17504,7 +17675,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
|
||||
if playerData.step==AIRBOSS.PatternStep.HOLDING and playerData.case>1 then
|
||||
-- Get inverse magnetic radial potential offset.
|
||||
local radial=self:GetRadial(playerData.case, true, true, true)
|
||||
stacktext=stacktext..string.format("Select TACAN %03d°, %d DME\n", radial, angels+15)
|
||||
stacktext=stacktext..string.format("Select TACAN %03d°, %d DME\n", radial, angels+15)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17545,7 +17716,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
|
||||
local brc=self:GetBRC()
|
||||
|
||||
-- Help player to find its way to the initial zone.
|
||||
text=text..string.format("\nTo Initial: Fly heading %03d° for %.1f NM and turn to BRC %03d°", flyhdg, flydist, brc)
|
||||
text=text..string.format("\nTo Initial: Fly heading %03d° for %.1f NM and turn to BRC %03d°", flyhdg, flydist, brc)
|
||||
|
||||
elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
|
||||
|
||||
@@ -17560,7 +17731,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
|
||||
local hdg=self:GetRadial(playerData.case, true, true, true)
|
||||
|
||||
-- Help player to find its way to the initial zone.
|
||||
text=text..string.format("\nTo Platform: Fly heading %03d° for %.1f NM and turn to %03d°", flyhdg, flydist, hdg)
|
||||
text=text..string.format("\nTo Platform: Fly heading %03d° for %.1f NM and turn to %03d°", flyhdg, flydist, hdg)
|
||||
|
||||
end
|
||||
|
||||
@@ -17713,8 +17884,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: Oct 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,16 @@
|
||||
-- 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
|
||||
-- -- (added 0.1.11)
|
||||
-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters
|
||||
-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters
|
||||
-- -- (added 0.1.12)
|
||||
-- -- Country codes for spawned pilots
|
||||
-- self.countryblue= country.id.USA
|
||||
-- self.countryred = country.id.RUSSIA
|
||||
-- self.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
--
|
||||
-- ## 2.1 Experimental Features
|
||||
--
|
||||
@@ -214,26 +224,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 +241,7 @@ CSAR.AircraftType["Mi-24V"] = 8
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="0.1.8r3"
|
||||
CSAR.version="0.1.11r1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -361,12 +351,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 +369,16 @@ 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
|
||||
|
||||
-- added 0.1.11r1
|
||||
self.rescuehoverheight = 20
|
||||
self.rescuehoverdistance = 10
|
||||
|
||||
-- added 0.1.12
|
||||
self.countryblue= country.id.USA
|
||||
self.countryred = country.id.RUSSIA
|
||||
self.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
|
||||
-- WARNING - here\'ll be dragons
|
||||
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
|
||||
@@ -565,6 +566,7 @@ function CSAR:_SpawnPilotInField(country,point,frequency)
|
||||
for i=1,10 do
|
||||
math.random(i,10000)
|
||||
end
|
||||
if point:IsSurfaceTypeWater() then point.y = 0 end
|
||||
local template = self.template
|
||||
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
|
||||
local coalition = self.coalition
|
||||
@@ -642,7 +644,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
|
||||
@@ -703,11 +705,11 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _
|
||||
|
||||
local _country = 0
|
||||
if _coalition == coalition.side.BLUE then
|
||||
_country = country.id.USA
|
||||
_country = self.countryblue
|
||||
elseif _coalition == coalition.side.RED then
|
||||
_country = country.id.RUSSIA
|
||||
_country = self.countryred
|
||||
else
|
||||
_country = country.id.UN_PEACEKEEPERS
|
||||
_country = self.countryneutral
|
||||
end
|
||||
|
||||
self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description, forcedesc)
|
||||
@@ -734,7 +736,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 +807,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
|
||||
@@ -894,11 +894,7 @@ function CSAR:_EventHandler(EventData)
|
||||
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
|
||||
@@ -918,7 +914,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)
|
||||
@@ -934,7 +929,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
|
||||
@@ -1104,7 +1099,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,
|
||||
@@ -1112,7 +1106,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)
|
||||
@@ -1143,39 +1137,7 @@ 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 false
|
||||
return UTILS.IsLoadingDoorOpen(unit_name)
|
||||
end
|
||||
|
||||
--- (Internal) Function to check if heli is close to group.
|
||||
@@ -1206,14 +1168,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]
|
||||
@@ -1257,15 +1217,16 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
end
|
||||
|
||||
if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then
|
||||
|
||||
if _distance < 8.0 then
|
||||
-- TODO - make variable
|
||||
if _distance < self.rescuehoverdistance then
|
||||
|
||||
--check height!
|
||||
local leaderheight = _woundedLeader:GetHeight()
|
||||
if leaderheight < 0 then leaderheight = 0 end
|
||||
local _height = _heliUnit:GetHeight() - leaderheight
|
||||
|
||||
if _height <= 20.0 then
|
||||
|
||||
-- TODO - make variable
|
||||
if _height <= self.rescuehoverheight then
|
||||
|
||||
local _time = self.hoverStatus[_lookupKeyHeli]
|
||||
|
||||
@@ -1314,7 +1275,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)
|
||||
@@ -1337,8 +1299,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)
|
||||
@@ -1347,7 +1309,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
end
|
||||
|
||||
--queue up
|
||||
self:__Returning(-5,heliname,_woundedGroupName)
|
||||
self:__Returning(-5,heliname,_woundedGroupName, isairport)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1363,8 +1325,7 @@ function CSAR:_RescuePilots(_heliUnit)
|
||||
-- Groups already rescued
|
||||
return
|
||||
end
|
||||
|
||||
-- DONE: count saved units?
|
||||
|
||||
local PilotsSaved = self:_PilotsOnboard(_heliName)
|
||||
|
||||
self.inTransitGroups[_heliName] = nil
|
||||
@@ -1403,7 +1364,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)
|
||||
@@ -1458,7 +1421,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)
|
||||
@@ -1501,10 +1463,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
|
||||
|
||||
@@ -1570,12 +1539,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
|
||||
@@ -1747,9 +1715,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
|
||||
@@ -1758,7 +1737,6 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_GenerateVHFrequencies()
|
||||
self:T(self.lid .. " _GenerateVHFrequencies")
|
||||
--local _skipFrequencies = self.SkipFrequencies
|
||||
|
||||
local FreeVHFFrequencies = {}
|
||||
FreeVHFFrequencies = UTILS.GenerateVHFrequencies()
|
||||
@@ -1798,7 +1776,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
|
||||
@@ -1907,6 +1884,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
|
||||
@@ -1915,19 +1893,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
|
||||
|
||||
@@ -2047,9 +2025,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
@@ -157,7 +157,7 @@ end
|
||||
--- Set the frequency for the radio transmission.
|
||||
-- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation.
|
||||
-- @param #RADIO self
|
||||
-- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-87.995 / 108-173.995 / 225-399.975MHz.
|
||||
-- @param #number Frequency Frequency in MHz.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetFrequency(Frequency)
|
||||
self:F2(Frequency)
|
||||
@@ -165,7 +165,7 @@ function RADIO:SetFrequency(Frequency)
|
||||
if type(Frequency) == "number" then
|
||||
|
||||
-- If frequency is in range
|
||||
if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
-- if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
|
||||
-- Convert frequency from MHz to Hz
|
||||
self.Frequency = Frequency * 1000000
|
||||
@@ -186,10 +186,10 @@ function RADIO:SetFrequency(Frequency)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency})
|
||||
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
|
||||
["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 },
|
||||
|
||||
["возвращение на базу"] = { "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 },
|
||||
|
||||
["возвращаÑ<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 },
|
||||
["для"] = { "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.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
--- This module contains derived utilities taken from the MIST framework, which are excellent tools to be reused in an OO environment.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * Grimes : Design & Programming of the MIST framework.
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * FlightControl : Rework to OO framework
|
||||
--
|
||||
--
|
||||
-- * FlightControl : Rework to OO framework.
|
||||
--
|
||||
-- @module Utils
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
-- @field White
|
||||
-- @field Orange
|
||||
-- @field Blue
|
||||
|
||||
|
||||
SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR
|
||||
|
||||
--- @type FLARECOLOR
|
||||
@@ -94,7 +94,7 @@ CALLSIGN={
|
||||
Texaco=1,
|
||||
Arco=2,
|
||||
Shell=3,
|
||||
},
|
||||
},
|
||||
-- JTAC
|
||||
JTAC={
|
||||
Axeman=1,
|
||||
@@ -163,31 +163,31 @@ UTILS = {
|
||||
UTILS.IsInstanceOf = function( object, className )
|
||||
-- Is className NOT a string ?
|
||||
if not type( className ) == 'string' then
|
||||
|
||||
|
||||
-- Is className a Moose class ?
|
||||
if type( className ) == 'table' and className.IsInstanceOf ~= nil then
|
||||
|
||||
|
||||
-- Get the name of the Moose class as a string
|
||||
className = className.ClassName
|
||||
|
||||
|
||||
-- className is neither a string nor a Moose class, throw an error
|
||||
else
|
||||
|
||||
|
||||
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
|
||||
local err_str = 'className parameter should be a string; parameter received: '..type( className )
|
||||
return false
|
||||
-- error( err_str )
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Is the object a Moose class instance ?
|
||||
if type( object ) == 'table' and object.IsInstanceOf ~= nil then
|
||||
|
||||
|
||||
-- Use the IsInstanceOf method of the BASE class
|
||||
return object:IsInstanceOf( className )
|
||||
else
|
||||
|
||||
|
||||
-- If the object is not an instance of a Moose class, evaluate against lua basic data types
|
||||
local basicDataTypes = { 'string', 'number', 'function', 'boolean', 'nil', 'table' }
|
||||
for _, basicDataType in ipairs( basicDataTypes ) do
|
||||
@@ -196,7 +196,7 @@ UTILS.IsInstanceOf = function( object, className )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Check failed
|
||||
return false
|
||||
end
|
||||
@@ -208,7 +208,7 @@ end
|
||||
UTILS.DeepCopy = function(object)
|
||||
|
||||
local lookup_table = {}
|
||||
|
||||
|
||||
-- Copy function.
|
||||
local function _copy(object)
|
||||
if type(object) ~= "table" then
|
||||
@@ -216,20 +216,20 @@ UTILS.DeepCopy = function(object)
|
||||
elseif lookup_table[object] then
|
||||
return lookup_table[object]
|
||||
end
|
||||
|
||||
|
||||
local new_table = {}
|
||||
|
||||
|
||||
lookup_table[object] = new_table
|
||||
|
||||
|
||||
for index, value in pairs(object) do
|
||||
new_table[_copy(index)] = _copy(value)
|
||||
end
|
||||
|
||||
|
||||
return setmetatable(new_table, getmetatable(object))
|
||||
end
|
||||
|
||||
|
||||
local objectreturn = _copy(object)
|
||||
|
||||
|
||||
return objectreturn
|
||||
end
|
||||
|
||||
@@ -239,19 +239,19 @@ end
|
||||
UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
|
||||
|
||||
lookup_table = {}
|
||||
|
||||
|
||||
local function _Serialize( tbl )
|
||||
|
||||
if type(tbl) == 'table' then --function only works for tables!
|
||||
|
||||
|
||||
if lookup_table[tbl] then
|
||||
return lookup_table[object]
|
||||
end
|
||||
|
||||
local tbl_str = {}
|
||||
|
||||
|
||||
lookup_table[tbl] = tbl_str
|
||||
|
||||
|
||||
tbl_str[#tbl_str + 1] = '{'
|
||||
|
||||
for ind,val in pairs(tbl) do -- serialize its fields
|
||||
@@ -299,7 +299,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s
|
||||
env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind))
|
||||
env.info( debug.traceback() )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
tbl_str[#tbl_str + 1] = '}'
|
||||
return table.concat(tbl_str)
|
||||
@@ -307,7 +307,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s
|
||||
return tostring(tbl)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local objectreturn = _Serialize(tbl)
|
||||
return objectreturn
|
||||
end
|
||||
@@ -339,18 +339,34 @@ UTILS.MetersToNM = function(meters)
|
||||
return meters/1852
|
||||
end
|
||||
|
||||
UTILS.KiloMetersToNM = function(kilometers)
|
||||
return kilometers/1852*1000
|
||||
end
|
||||
|
||||
UTILS.MetersToSM = function(meters)
|
||||
return meters/1609.34
|
||||
end
|
||||
|
||||
UTILS.KiloMetersToSM = function(kilometers)
|
||||
return kilometers/1609.34*1000
|
||||
end
|
||||
|
||||
UTILS.MetersToFeet = function(meters)
|
||||
return meters/0.3048
|
||||
end
|
||||
|
||||
UTILS.KiloMetersToFeet = function(kilometers)
|
||||
return kilometers/0.3048*1000
|
||||
end
|
||||
|
||||
UTILS.NMToMeters = function(NM)
|
||||
return NM*1852
|
||||
end
|
||||
|
||||
UTILS.NMToKiloMeters = function(NM)
|
||||
return NM*1852/1000
|
||||
end
|
||||
|
||||
UTILS.FeetToMeters = function(feet)
|
||||
return feet*0.3048
|
||||
end
|
||||
@@ -400,7 +416,7 @@ end
|
||||
-- @param #number Celcius Temperature in degrees Celsius.
|
||||
-- @return #number Temperature in degrees Farenheit.
|
||||
UTILS.CelciusToFarenheit = function( Celcius )
|
||||
return Celcius * 9/5 + 32
|
||||
return Celcius * 9/5 + 32
|
||||
end
|
||||
|
||||
--- Convert pressure from hecto Pascal (hPa) to inches of mercury (inHg).
|
||||
@@ -415,7 +431,7 @@ end
|
||||
-- @param #number altitude Altitude in feet
|
||||
-- @return #number Corrected KIAS
|
||||
UTILS.KnotsToAltKIAS = function( knots, altitude )
|
||||
return (knots * 0.018 * (altitude / 1000)) + knots
|
||||
return (knots * 0.018 * (altitude / 1000)) + knots
|
||||
end
|
||||
|
||||
--- Convert pressure from hecto Pascal (hPa) to millimeters of mercury (mmHg).
|
||||
@@ -491,7 +507,7 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
||||
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
end
|
||||
|
||||
-- 024<EFBFBD> 23' 12"N or 024<EFBFBD> 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
|
||||
|
||||
@@ -534,23 +550,23 @@ UTILS.tostringMGRS = function(MGRS, acc) --R2.1
|
||||
-- Test if Easting/Northing have less than 4 digits.
|
||||
--MGRS.Easting=123 -- should be 00123
|
||||
--MGRS.Northing=5432 -- should be 05432
|
||||
|
||||
|
||||
-- Truncate rather than round MGRS grid!
|
||||
local Easting=tostring(MGRS.Easting)
|
||||
local Northing=tostring(MGRS.Northing)
|
||||
|
||||
|
||||
-- Count number of missing digits. Easting/Northing should have 5 digits. However, it is passed as a number. Therefore, any leading zeros would not be displayed by lua.
|
||||
local nE=5-string.len(Easting)
|
||||
local nE=5-string.len(Easting)
|
||||
local nN=5-string.len(Northing)
|
||||
|
||||
|
||||
-- Get leading zeros (if any).
|
||||
for i=1,nE do Easting="0"..Easting end
|
||||
for i=1,nN do Northing="0"..Northing end
|
||||
|
||||
|
||||
-- Return MGRS string.
|
||||
return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc))
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -578,7 +594,7 @@ function UTILS.spairs( t, order )
|
||||
for k in pairs(t) do keys[#keys+1] = k end
|
||||
|
||||
-- if order function given, sort by it by passing the table and keys a, b,
|
||||
-- otherwise just sort the keys
|
||||
-- otherwise just sort the keys
|
||||
if order then
|
||||
table.sort(keys, function(a,b) return order(t, a, b) end)
|
||||
else
|
||||
@@ -604,7 +620,7 @@ function UTILS.kpairs( t, getkey, order )
|
||||
for k, o in pairs(t) do keys[#keys+1] = k keyso[#keyso+1] = getkey( o ) end
|
||||
|
||||
-- if order function given, sort by it by passing the table and keys a, b,
|
||||
-- otherwise just sort the keys
|
||||
-- otherwise just sort the keys
|
||||
if order then
|
||||
table.sort(keys, function(a,b) return order(t, a, b) end)
|
||||
else
|
||||
@@ -624,7 +640,7 @@ end
|
||||
-- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order.
|
||||
function UTILS.rpairs( t )
|
||||
-- collect the keys
|
||||
|
||||
|
||||
local keys = {}
|
||||
for k in pairs(t) do keys[#keys+1] = k end
|
||||
|
||||
@@ -635,7 +651,7 @@ function UTILS.rpairs( t )
|
||||
random[i] = keys[k]
|
||||
table.remove( keys, k )
|
||||
end
|
||||
|
||||
|
||||
-- return the iterator function
|
||||
local i = 0
|
||||
return function()
|
||||
@@ -751,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
|
||||
|
||||
@@ -765,15 +781,15 @@ end
|
||||
-- @param #boolean short (Optional) If true, use short output, i.e. (HH:)MM:SS without day.
|
||||
-- @return #string Time in format Hours:Minutes:Seconds+Days (HH:MM:SS+D).
|
||||
function UTILS.SecondsToClock(seconds, short)
|
||||
|
||||
|
||||
-- Nil check.
|
||||
if seconds==nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- Seconds
|
||||
local seconds = tonumber(seconds)
|
||||
|
||||
|
||||
-- Seconds of this day.
|
||||
local _seconds=seconds%(60*60*24)
|
||||
|
||||
@@ -803,10 +819,10 @@ function UTILS.SecondsOfToday()
|
||||
|
||||
-- Time in seconds.
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
|
||||
-- Short format without days since mission start.
|
||||
local clock=UTILS.SecondsToClock(time, true)
|
||||
|
||||
|
||||
-- Time is now the seconds passed since last midnight.
|
||||
return UTILS.ClockToSeconds(clock)
|
||||
end
|
||||
@@ -821,24 +837,24 @@ end
|
||||
-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days.
|
||||
-- @return #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
|
||||
function UTILS.ClockToSeconds(clock)
|
||||
|
||||
|
||||
-- Nil check.
|
||||
if clock==nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- Seconds init.
|
||||
local seconds=0
|
||||
|
||||
|
||||
-- Split additional days.
|
||||
local dsplit=UTILS.Split(clock, "+")
|
||||
|
||||
|
||||
-- Convert days to seconds.
|
||||
if #dsplit>1 then
|
||||
seconds=seconds+tonumber(dsplit[2])*60*60*24
|
||||
end
|
||||
|
||||
-- Split hours, minutes, seconds
|
||||
-- Split hours, minutes, seconds
|
||||
local tsplit=UTILS.Split(dsplit[1], ":")
|
||||
|
||||
-- Get time in seconds
|
||||
@@ -856,7 +872,7 @@ function UTILS.ClockToSeconds(clock)
|
||||
end
|
||||
i=i+1
|
||||
end
|
||||
|
||||
|
||||
return seconds
|
||||
end
|
||||
|
||||
@@ -868,12 +884,12 @@ function UTILS.DisplayMissionTime(duration)
|
||||
local mission_time=Tnow-timer.getTime0()
|
||||
local mission_time_minutes=mission_time/60
|
||||
local mission_time_seconds=mission_time%60
|
||||
local local_time=UTILS.SecondsToClock(Tnow)
|
||||
local local_time=UTILS.SecondsToClock(Tnow)
|
||||
local text=string.format("Time: %s - %02d:%02d", local_time, mission_time_minutes, mission_time_seconds)
|
||||
MESSAGE:New(text, duration):ToAll()
|
||||
end
|
||||
|
||||
--- Replace illegal characters [<>|/?*:\\] in a string.
|
||||
--- Replace illegal characters [<>|/?*:\\] in a string.
|
||||
-- @param #string Text Input text.
|
||||
-- @param #string ReplaceBy Replace illegal characters by this character or string. Default underscore "_".
|
||||
-- @return #string The input text with illegal chars replaced.
|
||||
@@ -894,28 +910,28 @@ function UTILS.RandomGaussian(x0, sigma, xmin, xmax, imax)
|
||||
|
||||
-- Standard deviation. Default 10 if not given.
|
||||
sigma=sigma or 10
|
||||
|
||||
|
||||
-- Max attempts.
|
||||
imax=imax or 100
|
||||
|
||||
|
||||
local r
|
||||
local gotit=false
|
||||
local i=0
|
||||
while not gotit do
|
||||
|
||||
|
||||
-- Uniform numbers in [0,1). We need two.
|
||||
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
|
||||
if (r>=xmin and r<=xmax) or i>imax then
|
||||
gotit=true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
@@ -940,9 +956,9 @@ function UTILS.Randomize(value, fac, lower, upper)
|
||||
else
|
||||
max=value+value*fac
|
||||
end
|
||||
|
||||
|
||||
local r=math.random(min, max)
|
||||
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
@@ -961,6 +977,33 @@ function UTILS.VecNorm(a)
|
||||
return math.sqrt(UTILS.VecDot(a, a))
|
||||
end
|
||||
|
||||
--- Calculate the distance between two 2D vectors.
|
||||
-- @param DCS#Vec2 a Vector in 3D with x, y components.
|
||||
-- @param DCS#Vec2 b Vector in 3D with x, y components.
|
||||
-- @return #number Distance between the vectors.
|
||||
function UTILS.VecDist2D(a, b)
|
||||
|
||||
local c={x=b.x-a.x, y=b.y-a.y}
|
||||
|
||||
local d=math.sqrt(c.x*c.x+c.y*c.y)
|
||||
|
||||
return d
|
||||
end
|
||||
|
||||
|
||||
--- Calculate the distance between two 3D vectors.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
-- @return #number Distance between the vectors.
|
||||
function UTILS.VecDist3D(a, b)
|
||||
|
||||
local c={x=b.x-a.x, y=b.y-a.y, z=b.z-a.z}
|
||||
|
||||
local d=math.sqrt(UTILS.VecDot(c, c))
|
||||
|
||||
return d
|
||||
end
|
||||
|
||||
--- Calculate the [cross product](https://en.wikipedia.org/wiki/Cross_product) of two 3D vectors. The result is a 3D vector.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
@@ -969,7 +1012,7 @@ function UTILS.VecCross(a, b)
|
||||
return {x=a.y*b.z - a.z*b.y, y=a.z*b.x - a.x*b.z, z=a.x*b.y - a.y*b.x}
|
||||
end
|
||||
|
||||
--- Calculate the difference between two 3D vectors by substracting the x,y,z components from each other.
|
||||
--- Calculate the difference between two 3D vectors by substracting the x,y,z components from each other.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
-- @return DCS#Vec3 Vector c=a-b with c(i)=a(i)-b(i), i=x,y,z.
|
||||
@@ -977,7 +1020,7 @@ function UTILS.VecSubstract(a, b)
|
||||
return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
|
||||
end
|
||||
|
||||
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
|
||||
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
-- @return DCS#Vec3 Vector c=a+b with c(i)=a(i)+b(i), i=x,y,z.
|
||||
@@ -985,14 +1028,14 @@ function UTILS.VecAdd(a, b)
|
||||
return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
|
||||
end
|
||||
|
||||
--- Calculate the angle between two 3D vectors.
|
||||
--- Calculate the angle between two 3D vectors.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
-- @return #number Angle alpha between and b in degrees. alpha=acos(a*b)/(|a||b|), (* denotes the dot product).
|
||||
-- @return #number Angle alpha between and b in degrees. alpha=acos(a*b)/(|a||b|), (* denotes the dot product).
|
||||
function UTILS.VecAngle(a, b)
|
||||
|
||||
local cosalpha=UTILS.VecDot(a,b)/(UTILS.VecNorm(a)*UTILS.VecNorm(b))
|
||||
|
||||
|
||||
local alpha=0
|
||||
if cosalpha>=0.9999999999 then --acos(1) is not defined.
|
||||
alpha=0
|
||||
@@ -1000,8 +1043,8 @@ function UTILS.VecAngle(a, b)
|
||||
alpha=math.pi
|
||||
else
|
||||
alpha=math.acos(cosalpha)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return math.deg(alpha)
|
||||
end
|
||||
|
||||
@@ -1025,18 +1068,18 @@ function UTILS.HdgDiff(h1, h2)
|
||||
-- Angle in rad.
|
||||
local alpha= math.rad(tonumber(h1))
|
||||
local beta = math.rad(tonumber(h2))
|
||||
|
||||
|
||||
-- Runway vector.
|
||||
local v1={x=math.cos(alpha), y=0, z=math.sin(alpha)}
|
||||
local v2={x=math.cos(beta), y=0, z=math.sin(beta)}
|
||||
|
||||
local delta=UTILS.VecAngle(v1, v2)
|
||||
|
||||
|
||||
return math.abs(delta)
|
||||
end
|
||||
|
||||
|
||||
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param #number distance The distance to translate.
|
||||
-- @param #number angle Rotation angle in degrees.
|
||||
@@ -1052,21 +1095,21 @@ function UTILS.VecTranslate(a, distance, angle)
|
||||
return {x=TX, y=a.y, z=TY}
|
||||
end
|
||||
|
||||
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param #number angle Rotation angle in degrees.
|
||||
-- @return DCS#Vec3 Vector rotated in the (x,z) plane.
|
||||
function UTILS.Rotate2D(a, angle)
|
||||
|
||||
local phi=math.rad(angle)
|
||||
|
||||
|
||||
local x=a.z
|
||||
local y=a.x
|
||||
|
||||
|
||||
local Z=x*math.cos(phi)-y*math.sin(phi)
|
||||
local X=x*math.sin(phi)+y*math.cos(phi)
|
||||
local Y=a.y
|
||||
|
||||
|
||||
local A={x=X, y=Y, z=Z}
|
||||
|
||||
return A
|
||||
@@ -1084,17 +1127,17 @@ function UTILS.TACANToFrequency(TACANChannel, TACANMode)
|
||||
end
|
||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||
return nil -- error in arguments
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
local A = 1151 -- 'X', channel >= 64
|
||||
local B = 64 -- channel >= 64
|
||||
|
||||
|
||||
if TACANChannel < 64 then
|
||||
B = 1
|
||||
end
|
||||
|
||||
|
||||
if TACANMode == 'Y' then
|
||||
A = 1025
|
||||
if TACANChannel < 64 then
|
||||
@@ -1105,7 +1148,7 @@ function UTILS.TACANToFrequency(TACANChannel, TACANMode)
|
||||
A = 962
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return (A + TACANChannel - B) * 1000000
|
||||
end
|
||||
|
||||
@@ -1132,13 +1175,13 @@ end
|
||||
-- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime().
|
||||
-- @return #number Day of the mission. Mission starts on day 0.
|
||||
function UTILS.GetMissionDay(Time)
|
||||
|
||||
|
||||
Time=Time or timer.getAbsTime()
|
||||
|
||||
|
||||
local clock=UTILS.SecondsToClock(Time, false)
|
||||
|
||||
|
||||
local x=tonumber(UTILS.Split(clock, "+")[2])
|
||||
|
||||
|
||||
return x
|
||||
end
|
||||
|
||||
@@ -1148,11 +1191,11 @@ end
|
||||
function UTILS.GetMissionDayOfYear(Time)
|
||||
|
||||
local Date, Year, Month, Day=UTILS.GetDCSMissionDate()
|
||||
|
||||
|
||||
local d=UTILS.GetMissionDay(Time)
|
||||
|
||||
|
||||
return UTILS.GetDayOfYear(Year, Month, Day)+d
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Returns the current date.
|
||||
@@ -1164,20 +1207,20 @@ function UTILS.GetDate()
|
||||
|
||||
-- Mission start date
|
||||
local date, year, month, day=UTILS.GetDCSMissionDate()
|
||||
|
||||
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
|
||||
local clock=UTILS.SecondsToClock(time, false)
|
||||
|
||||
|
||||
local x=tonumber(UTILS.Split(clock, "+")[2])
|
||||
|
||||
|
||||
local day=day+x
|
||||
|
||||
end
|
||||
|
||||
--- Returns the magnetic declination of the map.
|
||||
-- Returned values for the current maps are:
|
||||
--
|
||||
--
|
||||
-- * Caucasus +6 (East), year ~ 2011
|
||||
-- * NTTR +12 (East), year ~ 2011
|
||||
-- * Normandy -10 (West), year ~ 1944
|
||||
@@ -1191,7 +1234,7 @@ function UTILS.GetMagneticDeclination(map)
|
||||
|
||||
-- Map.
|
||||
map=map or UTILS.GetDCSMap()
|
||||
|
||||
|
||||
local declination=0
|
||||
if map==DCSMAP.Caucasus then
|
||||
declination=6
|
||||
@@ -1228,12 +1271,12 @@ function UTILS.FileExists(file)
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Checks the current memory usage collectgarbage("count"). Info is printed to the DCS log file. Time stamp is the current mission runtime.
|
||||
-- @param #boolean output If true, print to DCS log file.
|
||||
-- @return #number Memory usage in kByte.
|
||||
-- @param #boolean output If true, print to DCS log file.
|
||||
-- @return #number Memory usage in kByte.
|
||||
function UTILS.CheckMemory(output)
|
||||
local time=timer.getTime()
|
||||
local clock=UTILS.SecondsToClock(time)
|
||||
@@ -1263,7 +1306,7 @@ function UTILS.GetCoalitionName(Coalition)
|
||||
else
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Get the modulation name from its numerical value.
|
||||
@@ -1282,7 +1325,7 @@ function UTILS.GetModulationName(Modulation)
|
||||
else
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Get the callsign name from its enumerator value
|
||||
@@ -1295,7 +1338,7 @@ function UTILS.GetCallsignName(Callsign)
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for name, value in pairs(CALLSIGN.AWACS) do
|
||||
if value==Callsign then
|
||||
return name
|
||||
@@ -1307,7 +1350,7 @@ function UTILS.GetCallsignName(Callsign)
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for name, value in pairs(CALLSIGN.Tanker) do
|
||||
if value==Callsign then
|
||||
return name
|
||||
@@ -1336,7 +1379,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
|
||||
@@ -1353,11 +1396,11 @@ end
|
||||
function UTILS.GetDayOfYear(Year, Month, Day)
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
|
||||
local n1 = floor(275 * Month / 9)
|
||||
local n2 = floor((Month + 9) / 12)
|
||||
local n3 = (1 + floor((Year - 4 * floor(Year / 4) + 2) / 3))
|
||||
|
||||
|
||||
return n1 - (n2 * n3) + Day - 30
|
||||
end
|
||||
|
||||
@@ -1370,14 +1413,14 @@ end
|
||||
-- @return #number Sun rise/set in seconds of the day.
|
||||
function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
|
||||
|
||||
-- Defaults
|
||||
-- Defaults
|
||||
local zenith=90.83
|
||||
local latitude=Latitude
|
||||
local longitude=Longitude
|
||||
local rising=Rising
|
||||
local n=DayOfYear
|
||||
Tlocal=Tlocal or 0
|
||||
|
||||
|
||||
|
||||
-- Short cuts.
|
||||
local rad = math.rad
|
||||
@@ -1404,47 +1447,47 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Convert the longitude to hour value and calculate an approximate time
|
||||
local lng_hour = longitude / 15
|
||||
|
||||
|
||||
local t
|
||||
if rising then -- Rising time is desired
|
||||
t = n + ((6 - lng_hour) / 24)
|
||||
else -- Setting time is desired
|
||||
t = n + ((18 - lng_hour) / 24)
|
||||
end
|
||||
|
||||
|
||||
-- Calculate the Sun's mean anomaly
|
||||
local M = (0.9856 * t) - 3.289
|
||||
|
||||
|
||||
-- Calculate the Sun's true longitude
|
||||
local L = fit_into_range(M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634, 0, 360)
|
||||
|
||||
|
||||
-- Calculate the Sun's right ascension
|
||||
local RA = fit_into_range(atan(0.91764 * tan(L)), 0, 360)
|
||||
|
||||
|
||||
-- Right ascension value needs to be in the same quadrant as L
|
||||
local Lquadrant = floor(L / 90) * 90
|
||||
local RAquadrant = floor(RA / 90) * 90
|
||||
RA = RA + Lquadrant - RAquadrant
|
||||
|
||||
|
||||
-- Right ascension value needs to be converted into hours
|
||||
RA = RA / 15
|
||||
|
||||
|
||||
-- Calculate the Sun's declination
|
||||
local sinDec = 0.39782 * sin(L)
|
||||
local cosDec = cos(asin(sinDec))
|
||||
|
||||
|
||||
-- Calculate the Sun's local hour angle
|
||||
local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
|
||||
|
||||
|
||||
if rising and cosH > 1 then
|
||||
return "N/R" -- The sun never rises on this location on the specified date
|
||||
elseif cosH < -1 then
|
||||
return "N/S" -- The sun never sets on this location on the specified date
|
||||
end
|
||||
|
||||
|
||||
-- Finish calculating H and convert into hours
|
||||
local H
|
||||
if rising then
|
||||
@@ -1453,13 +1496,13 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
|
||||
H = acos(cosH)
|
||||
end
|
||||
H = H / 15
|
||||
|
||||
|
||||
-- Calculate local mean time of rising/setting
|
||||
local T = H + RA - (0.06571 * t) - 6.622
|
||||
|
||||
-- Adjust back to UTC
|
||||
local UT = fit_into_range(T - lng_hour +Tlocal, 0, 24)
|
||||
|
||||
|
||||
return floor(UT)*60*60+frac(UT)*60*60--+Tlocal*60*60
|
||||
end
|
||||
|
||||
@@ -1510,7 +1553,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()
|
||||
@@ -1534,17 +1577,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) < 0 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
|
||||
@@ -1555,13 +1598,28 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1 then
|
||||
BASE:T(unit_name .. " rear doors are open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then
|
||||
BASE:T(unit_name .. " para doors are open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1217) == 1 then
|
||||
BASE:T(unit_name .. " side door is open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if ret_val == false then
|
||||
BASE:T(unit_name .. " all doors are closed")
|
||||
end
|
||||
return ret_val
|
||||
|
||||
|
||||
end -- nil
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1588,10 +1646,10 @@ function UTILS.GenerateVHFrequencies()
|
||||
local _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,
|
||||
320,324,328,329,330,332,336,337,
|
||||
342,343,348,351,352,353,358,
|
||||
363,365,368,372.5,374,
|
||||
380,381,384,389,395,396,
|
||||
380,381,384,385,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,
|
||||
@@ -1600,13 +1658,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
|
||||
@@ -1620,7 +1678,7 @@ function UTILS.GenerateVHFrequencies()
|
||||
end
|
||||
_start = _start + 10000
|
||||
end
|
||||
|
||||
|
||||
-- second range
|
||||
_start = 400000
|
||||
while _start < 850000 do
|
||||
@@ -1637,7 +1695,7 @@ function UTILS.GenerateVHFrequencies()
|
||||
end
|
||||
_start = _start + 10000
|
||||
end
|
||||
|
||||
|
||||
-- third range
|
||||
_start = 850000
|
||||
while _start <= 999000 do -- adjusted for Gazelle
|
||||
@@ -1677,7 +1735,7 @@ end
|
||||
-- @return #table Laser Codes.
|
||||
function UTILS.GenerateLaserCodes()
|
||||
local jtacGeneratedLaserCodes = {}
|
||||
|
||||
|
||||
-- helper function
|
||||
local function ContainsDigit(_number, _numberToFind)
|
||||
local _thisNumber = _number
|
||||
@@ -1691,14 +1749,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)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
-- @field #table CategoryName Names of airbase categories.
|
||||
-- @field #string AirbaseName Name of the airbase.
|
||||
-- @field #number AirbaseID Airbase ID.
|
||||
-- @field Core.Zone#ZONE AirbaseZone Circular zone around the airbase with a radius of 2500 meters. For ships this is a ZONE_UNIT object.
|
||||
-- @field #number category Airbase category.
|
||||
-- @field #table descriptors DCS descriptors.
|
||||
-- @field #boolean isAirdrome Airbase is an airdrome.
|
||||
@@ -526,19 +527,19 @@ function AIRBASE:Register(AirbaseName)
|
||||
|
||||
-- Inherit everything from positionable.
|
||||
local self=BASE:Inherit(self, POSITIONABLE:New(AirbaseName)) --#AIRBASE
|
||||
|
||||
|
||||
-- Set airbase name.
|
||||
self.AirbaseName=AirbaseName
|
||||
|
||||
|
||||
-- Set airbase ID.
|
||||
self.AirbaseID=self:GetID(true)
|
||||
|
||||
|
||||
-- Get descriptors.
|
||||
self.descriptors=self:GetDesc()
|
||||
|
||||
|
||||
-- Category.
|
||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||
|
||||
|
||||
-- Set category.
|
||||
if self.category==Airbase.Category.AIRDROME then
|
||||
self.isAirdrome=true
|
||||
@@ -546,20 +547,33 @@ function AIRBASE:Register(AirbaseName)
|
||||
self.isHelipad=true
|
||||
elseif self.category==Airbase.Category.SHIP then
|
||||
self.isShip=true
|
||||
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
|
||||
if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then
|
||||
self.isHelipad=true
|
||||
self.isShip=false
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
_DATABASE:AddStatic(AirbaseName)
|
||||
end
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
end
|
||||
|
||||
|
||||
self:_InitParkingSpots()
|
||||
|
||||
|
||||
local vec2=self:GetVec2()
|
||||
|
||||
|
||||
-- Init coordinate.
|
||||
self:GetCoordinate()
|
||||
|
||||
|
||||
if vec2 then
|
||||
-- TODO: For ships we need a moving zone.
|
||||
self.AirbaseZone=ZONE_RADIUS:New( AirbaseName, vec2, 2500 )
|
||||
if self.isShip then
|
||||
local unit=UNIT:FindByName(AirbaseName)
|
||||
if unit then
|
||||
self.AirbaseZone=ZONE_UNIT:New(AirbaseName, unit, 2500)
|
||||
end
|
||||
else
|
||||
self.AirbaseZone=ZONE_RADIUS:New(AirbaseName, vec2, 2500)
|
||||
end
|
||||
else
|
||||
self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName))
|
||||
end
|
||||
@@ -693,7 +707,7 @@ function AIRBASE:GetID(unique)
|
||||
local airbaseID=tonumber(DCSAirbase:getID())
|
||||
|
||||
local airbaseCategory=self:GetAirbaseCategory()
|
||||
|
||||
|
||||
if AirbaseName==self.AirbaseName then
|
||||
if airbaseCategory==Airbase.Category.SHIP or airbaseCategory==Airbase.Category.HELIPAD then
|
||||
-- Ships get a negative sign as their unit number might be the same as the ID of another airbase.
|
||||
@@ -932,16 +946,16 @@ function AIRBASE:_InitParkingSpots()
|
||||
-- Init table.
|
||||
self.parking={}
|
||||
self.parkingByID={}
|
||||
|
||||
|
||||
self.NparkingTotal=0
|
||||
self.NparkingTerminal={}
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
self.NparkingTerminal[terminalType]=0
|
||||
end
|
||||
end
|
||||
|
||||
-- Put coordinates of parking spots into table.
|
||||
for _,spot in pairs(parkingdata) do
|
||||
|
||||
|
||||
-- New parking spot.
|
||||
local park={} --#AIRBASE.ParkingSpot
|
||||
park.Vec3=spot.vTerminalPos
|
||||
@@ -952,15 +966,15 @@ function AIRBASE:_InitParkingSpots()
|
||||
park.TerminalID0=spot.Term_Index_0
|
||||
park.TerminalType=spot.Term_Type
|
||||
park.TOAC=spot.TO_AC
|
||||
|
||||
|
||||
self.NparkingTotal=self.NparkingTotal+1
|
||||
|
||||
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
||||
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.parkingByID[park.TerminalID]=park
|
||||
table.insert(self.parking, park)
|
||||
end
|
||||
@@ -984,7 +998,7 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
|
||||
-- Get parking data of all spots (free or occupied)
|
||||
local parkingdata=self:GetParkingData(false)
|
||||
|
||||
|
||||
-- Get parking data of all free spots.
|
||||
local parkingfree=self:GetParkingData(true)
|
||||
|
||||
@@ -1001,17 +1015,26 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
-- Put coordinates of parking spots into table.
|
||||
local spots={}
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
|
||||
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
|
||||
|
||||
|
||||
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
||||
|
||||
spot.Free=_isfree(_spot) -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
|
||||
table.insert(spots, spot)
|
||||
|
||||
if spot then
|
||||
|
||||
spot.Free=_isfree(_spot) -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
|
||||
table.insert(spots, spot)
|
||||
|
||||
else
|
||||
|
||||
self:E(string.format("ERROR: Parking spot %s is nil!", tostring(_spot.Term_Index)))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return spots
|
||||
@@ -1032,14 +1055,14 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
for _,_spot in pairs(parkingfree) do
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) and _spot.Term_Index>0 then
|
||||
if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then
|
||||
|
||||
|
||||
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
||||
|
||||
spot.Free=true -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
|
||||
|
||||
table.insert(freespots, spot)
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1084,7 +1107,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
|
||||
|
||||
@@ -1118,7 +1141,7 @@ end
|
||||
-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function.
|
||||
-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID.
|
||||
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata)
|
||||
|
||||
|
||||
-- Init default
|
||||
scanradius=scanradius or 50
|
||||
if scanunits==nil then
|
||||
@@ -1161,14 +1184,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 -- fix local problem below
|
||||
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={}
|
||||
@@ -1291,6 +1325,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.
|
||||
@@ -1392,7 +1427,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Get spawn points on runway. These can be used to determine the runway heading.
|
||||
local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
|
||||
|
||||
|
||||
-- Debug: For finding the numbers of the spawn points belonging to each runway.
|
||||
if false then
|
||||
for i,_coord in pairs(runwaycoords) do
|
||||
@@ -1411,7 +1446,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Airbase name.
|
||||
local name=self:GetName()
|
||||
|
||||
|
||||
|
||||
-- Exceptions
|
||||
if name==AIRBASE.Nevada.Jean_Airport or
|
||||
@@ -1424,36 +1459,36 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||
exception=1
|
||||
|
||||
elseif UTILS.GetDCSMap()==DCSMAP.Syria and N>=2 and
|
||||
|
||||
elseif UTILS.GetDCSMap()==DCSMAP.Syria and N>=2 and
|
||||
name~=AIRBASE.Syria.Minakh and
|
||||
name~=AIRBASE.Syria.Damascus and
|
||||
name~=AIRBASE.Syria.Khalkhalah and
|
||||
name~=AIRBASE.Syria.Marj_Ruhayyil and
|
||||
name~=AIRBASE.Syria.Beirut_Rafic_Hariri then
|
||||
|
||||
|
||||
-- 1-->3, 2-->4, 3-->1, 4-->2
|
||||
exception=2
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Function returning the index of the runway coordinate belonding to the given index i.
|
||||
local function f(i)
|
||||
|
||||
local j
|
||||
|
||||
|
||||
if exception==1 then
|
||||
|
||||
|
||||
j=N-(i-1) -- 1-->4, 2-->3
|
||||
|
||||
|
||||
elseif exception==2 then
|
||||
|
||||
|
||||
if i<=N2 then
|
||||
j=i+N2 -- 1-->3, 2-->4
|
||||
else
|
||||
j=i-N2 -- 3-->1, 4-->3
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
|
||||
if i%2==0 then
|
||||
@@ -1461,9 +1496,9 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
else
|
||||
j=i+1 -- odd 1-->2, 3-->4
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Special case where there is no obvious order.
|
||||
if name==AIRBASE.Syria.Beirut_Rafic_Hariri then
|
||||
if i==1 then
|
||||
@@ -1496,7 +1531,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
j=2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return j
|
||||
end
|
||||
|
||||
@@ -1505,7 +1540,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Get the other spawn point coordinate.
|
||||
local j=f(i)
|
||||
|
||||
|
||||
-- Debug info.
|
||||
--env.info(string.format("Runway i=%s j=%s (N=%d #runwaycoord=%d)", tostring(i), tostring(j), N, #runwaycoords))
|
||||
|
||||
|
||||
@@ -3779,8 +3779,9 @@ end
|
||||
-- @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()
|
||||
@@ -3791,14 +3792,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
|
||||
|
||||
@@ -1137,7 +1137,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 )
|
||||
|
||||
@@ -1147,6 +1147,7 @@ function GROUP:GetAmmunition()
|
||||
local Nshells=0
|
||||
local Nrockets=0
|
||||
local Nmissiles=0
|
||||
local Nbombs=0
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
@@ -1155,18 +1156,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
|
||||
|
||||
|
||||
@@ -2594,6 +2596,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.
|
||||
|
||||
@@ -684,6 +684,27 @@ function POSITIONABLE:IsShip()
|
||||
end
|
||||
|
||||
|
||||
--- Returns if the unit is a submarine.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return #boolean Submarines attributes result.
|
||||
function POSITIONABLE:IsSubmarine()
|
||||
self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitDescriptor = DCSUnit:getDesc()
|
||||
if UnitDescriptor.attributes["Submarines"] == true then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns true if the POSITIONABLE is in the air.
|
||||
-- Polymorphic, is overridden in GROUP and UNIT.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
@@ -1513,6 +1534,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
|
||||
|
||||
@@ -1426,3 +1426,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
|
||||
|
||||
Reference in New Issue
Block a user