Compare commits

...

70 Commits
2.7.5 ... 2.7.7

Author SHA1 Message Date
Applevangelist
8b9143d3f1 CTLD - added option to force opening of doors 2021-10-12 08:32:34 +02:00
Applevangelist
0388d47f23 CSAR - Added country options for spawned pilots 2021-10-12 08:31:55 +02:00
Applevangelist
de9b173d9b UTILS - added door check for Hercules 2021-10-07 18:14:29 +02:00
Applevangelist
2cecc526fb ZONE_CAPTURE_COALITION - fixed an issue when monitoring hits and SCENERY was delivered as hit UNIT 2021-10-05 19:10:21 +02:00
Applevangelist
968d178317 Update README.md 2021-10-02 10:18:13 +02:00
Applevangelist
3c477b872a CSAR - hovering rescued parameters added 2021-10-01 14:54:31 +02:00
Applevangelist
77e6088114 UTILS - corrected open door check MI-8 2021-10-01 14:54:16 +02:00
Applevangelist
edd6594953 CTLD: added user-friendly function to inject static cargos: CTLD:InjectStaticFromTemplate(Zone, Template, Mass) 2021-10-01 10:43:17 +02:00
Applevangelist
f8c05c99d0 RADIO - delete frequency check 2021-09-30 08:07:34 +02:00
Applevangelist
50f6d98b49 push for a new build 2021-09-29 09:25:26 +02:00
Frank
147eeb05f6 Merge pull request #1607 from FlightControl-Master/FF/MasterDevel
DATABASE
2021-09-29 09:22:41 +02:00
Frank
d8cb15a577 Update Airbase.lua 2021-09-29 09:01:47 +02:00
Frank
0daac876ea Update Airbase.lua
- Register oil rigs and gas platforms as helipads. DCS bug registers them as ship (Airbase.Category.SHIP instead of Airbase.Category.HELIPAD).
2021-09-29 09:00:53 +02:00
Frank
1832125022 Globals
- Moved _DATABASE:_RegisterAirbases() to Globals.lua
2021-09-29 08:58:46 +02:00
Applevangelist
c311c40b72 GROUP:GetAmmunition() - fix to also return bomb count (#1606)
GROUP:GetAmmunition() - fix to also return bomb count
2021-09-28 16:53:53 +02:00
Applevangelist
db516a2077 Fix "local" error 2021-09-27 15:48:45 +02:00
Applevangelist
ff8766669c Small fix for Airbase Parking Spot Finder 2021-09-26 09:51:14 +02:00
Applevangelist
06dc9a732e bugfix 2021-09-24 18:37:13 +02:00
Applevangelist
50c74d0852 Added option for slingload: enableslingload 2021-09-24 11:08:23 +02:00
Applevangelist
1c97eb6f3c SPAWNSTATIC - bugfix on canCargo, mass could be set but not transported into the template spawn 2021-09-24 11:04:54 +02:00
Applevangelist
69449430d1 CTLD - Added Statics as cargo (#1600)
CTLD - Added Statics as cargo, and the ability to load and save them (alongside your dropped buildable crates).
2021-09-22 15:54:37 +02:00
Applevangelist
663cd34aa3 CTLD - fix when using SAVE or LOAD w/o filename and path 2021-09-21 07:48:09 +02:00
Applevangelist
cfed6f5153 SET - Added SET_CLIENT:CountAlive() 2021-09-21 07:47:43 +02:00
Applevangelist
2b22d5288c CTLD - added persistence 2021-09-20 14:27:45 +02:00
Applevangelist
a64424ecc8 Positionable - Add IsSubmarine, Passenger seats for VAB Mephisto 2021-09-20 14:27:22 +02:00
Applevangelist
fd1b2ecb86 ZONE - Docu bug fix 2021-09-20 14:26:42 +02:00
Applevangelist
6cae3e62cf CTLD - small bug fix on stock removal 2021-09-12 17:37:32 +02:00
Applevangelist
05ce7e4513 CTLD - added alternative crate spawn by @mousepilot. Add menu item to list stock.Injected troops will not lead to cargo type duplication. 2021-09-11 15:20:20 +02:00
Applevangelist
136bd19f19 Bug fixing 2021-09-11 10:03:49 +02:00
Applevangelist
8873504daf CTLD: Align to Dev changes 2021-09-07 19:52:14 +02:00
Applevangelist
a844a5d697 CSAR: Align to Dev changes 2021-09-07 19:52:13 +02:00
Applevangelist
a49f4eaa21 Merge pull request #1597 from Penecruz/Airboss-V/Stol
Airboss v/stol
2021-09-06 07:12:20 +02:00
Penecruz
e6e2651f8c Bug fix to AV-8B grading WIP 2021-09-06 11:08:29 +10:00
Penecruz
b93ba13644 bug fix to V/Stol groove. 2021-09-06 08:20:37 +10:00
Frank
e4a51951b0 Merge pull request #1596 from Penecruz/Airboss-V/Stol
Airboss v/stol
2021-09-04 15:20:03 +02:00
Penecruz
ad56e39942 Docs AV-8B clarifications 2021-09-04 13:40:10 +10:00
Penecruz
ea09dc5a6e Vstol groove timing 2021-09-04 11:25:44 +10:00
Penecruz
8ecfd913a3 Av-8B specific deviation counts adj. 2021-09-04 11:25:25 +10:00
Penecruz
53367c786e AV-8B LIG and Unicorn fix 2021-09-04 11:24:47 +10:00
Applevangelist
db5797bb4e Bug fixing 2021-09-02 18:48:40 +02:00
Applevangelist
5e8fe97752 CTLD Added method to inject troops into the field. 2021-09-01 13:34:13 +02:00
Applevangelist
393fa0bfbb MANTIS - Changes from the dev branch merged 2021-08-28 14:01:37 +02:00
Applevangelist
4f51884b9d SEAD - make padding a variable (radar switch-back-on time) 2021-08-28 14:01:37 +02:00
Frank
4c5c320073 Merge pull request #1594 from Penecruz/Pene-LHA-and-LHD-edits
Pene lha and lhd edits
2021-08-28 10:52:12 +02:00
Penecruz
9098590568 Sound Pack Gabriella add 2021-08-28 14:51:30 +10:00
Penecruz
555bb7e68b Update instructions for AV-8B Harrier 2021-08-28 14:27:32 +10:00
Penecruz
c0a18957f0 AoA for harrier and JC Spot 5 timings 2021-08-28 13:31:16 +10:00
Penecruz
2cf939560e Allow for JC Spot 5 Voice over 2021-08-28 11:02:05 +10:00
Penecruz
9d3a7aae78 Add Landing Spot 5 to JC 2021-08-28 10:26:49 +10:00
Applevangelist
f6ed592f92 CSAR - remove noise 2021-08-27 18:47:13 +02:00
Applevangelist
c98757d13c CTLD - added ENGINEERING 2021-08-27 18:46:59 +02:00
Applevangelist
17378f509e SEAD - Code cleanup and enabled delayed switch off 2021-08-27 18:46:45 +02:00
Applevangelist
7f18ea0e7a UNIT/GROUP - added function to get the skill of a unit. SEAD - added functionality to calculate time-2-impact of HARMS and adjust behaviour accordingly 2021-08-27 14:56:16 +02:00
Frank
5172619cb1 Merge pull request #1593 from Penecruz/Pene-LHA-and-LHD-edits
Pene-LHA-LHD-additions
2021-08-26 08:22:11 +02:00
Penecruz
3962529698 Update Airboss.lua 2021-08-26 09:33:40 +10:00
Penecruz
6481d5d41e Update Airboss.lua 2021-08-25 17:59:33 +10:00
Applevangelist
6cc3d73c04 Changed priority to show bomb target height in ft if no Player Settings 2021-08-22 12:02:52 +02:00
Applevangelist
e541e39403 Bug fixes. Added support for Ships as load zones 2021-08-22 12:02:26 +02:00
Applevangelist
c7ea45e5fd Clean up UTF-8 mess 2021-08-18 18:01:04 +02:00
Applevangelist
20f28b3d2c Fix for SAM pattern matching not working 2021-08-18 15:01:14 +02:00
Applevangelist
f3f63ab8aa Fix for degree sign extra char 2021-08-18 11:52:13 +02:00
Applevangelist
e91090cfff more corrections 2021-08-18 11:47:25 +02:00
Applevangelist
1a7fb3c13e Fix for degree sign extra char 2021-08-18 11:36:33 +02:00
Applevangelist
59857ed79d CSAR - added changes from Development for AFB landings and safer calculation of distances of lost pilots 2021-08-18 09:29:58 +02:00
Frank
4797665939 Update Range.lua
- Added demo by shagrat
2021-08-02 22:02:21 +02:00
Applevangelist
b89749036d Merge pull request #1584 from FlightControl-Master/Applevangelist-patch-3-1
Update Utils.lua
2021-08-02 19:04:57 +02:00
Applevangelist
c6268488de Update Utils.lua 2021-08-02 19:03:07 +02:00
Applevangelist
de04369703 Various for AI_CARGO, CTLD, CSAR, align with dev 2021-07-29 12:46:16 +02:00
Applevangelist
05b6f19a87 Merge pull request #1583 from FlightControl-Master/Applevangelist-patch-2
Update CSAR.lua
2021-07-28 19:47:48 +02:00
Applevangelist
2753df8216 Update CSAR.lua
Fix for CSAR message to all not working, added option to suppress all messaging, make destroys silent to not affect scoring
2021-07-28 19:45:47 +02:00
28 changed files with 2976 additions and 884 deletions

View File

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

View File

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

View File

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

View File

@@ -136,7 +136,7 @@ function DATABASE:New()
self:_RegisterGroupsAndUnits() self:_RegisterGroupsAndUnits()
self:_RegisterClients() self:_RegisterClients()
self:_RegisterStatics() self:_RegisterStatics()
self:_RegisterAirbases() --self:_RegisterAirbases()
--self:_RegisterPlayers() --self:_RegisterPlayers()
self.UNITS_Position = 0 self.UNITS_Position = 0

View File

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

View File

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

View File

@@ -183,12 +183,12 @@ function ZONE_BASE:IsCoordinateInZone( Coordinate )
return InZone return InZone
end end
--- Returns if a PointVec2 is within the zone. --- Returns if a PointVec2 is within the zone. (Name is misleading, actually takes a #COORDINATE)
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test. -- @param Core.Point#COORDINATE PointVec2 The coordinate to test.
-- @return #boolean true if the PointVec2 is within the zone. -- @return #boolean true if the PointVec2 is within the zone.
function ZONE_BASE:IsPointVec2InZone( PointVec2 ) function ZONE_BASE:IsPointVec2InZone( Coordinate )
local InZone = self:IsVec2InZone( PointVec2:GetVec2() ) local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
return InZone return InZone
end end
@@ -498,8 +498,8 @@ end
-- --
-- @field #ZONE_RADIUS -- @field #ZONE_RADIUS
ZONE_RADIUS = { ZONE_RADIUS = {
ClassName="ZONE_RADIUS", ClassName="ZONE_RADIUS",
} }
--- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius. --- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
@@ -510,15 +510,15 @@ ZONE_RADIUS = {
function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
-- Inherit ZONE_BASE. -- Inherit ZONE_BASE.
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
self:F( { ZoneName, Vec2, Radius } ) self:F( { ZoneName, Vec2, Radius } )
self.Radius = Radius self.Radius = Radius
self.Vec2 = Vec2 self.Vec2 = Vec2
--self.Coordinate=COORDINATE:NewFromVec2(Vec2) --self.Coordinate=COORDINATE:NewFromVec2(Vec2)
return self return self
end end
--- Update zone from a 2D vector. --- Update zone from a 2D vector.
@@ -746,11 +746,11 @@ end
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return DCS#Vec2 The location of the zone. -- @return DCS#Vec2 The location of the zone.
function ZONE_RADIUS:GetVec2() function ZONE_RADIUS:GetVec2()
self:F2( self.ZoneName ) self:F2( self.ZoneName )
self:T2( { self.Vec2 } ) self:T2( { self.Vec2 } )
return self.Vec2 return self.Vec2
end end
--- Sets the @{DCS#Vec2} of the zone. --- Sets the @{DCS#Vec2} of the zone.
@@ -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. -- @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. -- @return DCS#Vec2 The random location within the zone.
function ZONE_RADIUS:GetRandomVec2( inner, outer ) function ZONE_RADIUS:GetRandomVec2( inner, outer )
self:F( self.ZoneName, inner, outer ) self:F( self.ZoneName, inner, outer )
local Point = {} local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
local _inner = inner or 0 local _inner = inner or 0
local _outer = outer or self:GetRadius() local _outer = outer or self:GetRadius()
local angle = math.random() * math.pi * 2; local angle = math.random() * math.pi * 2;
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
Point.y = Vec2.y + math.sin( 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 end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.

View File

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

View File

@@ -23,9 +23,9 @@
-- Date: July 2021 -- Date: July 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **MANTIS** class, extends #Core.Base#BASE --- **MANTIS** class, extends Core.Base#BASE
-- @type MANTIS -- @type MANTIS
-- @field #string Classname -- @field #string ClassName
-- @field #string name Name of this Mantis -- @field #string name Name of this Mantis
-- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites -- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites
-- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP -- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP
@@ -195,6 +195,9 @@ MANTIS = {
TimeStamp = 0, TimeStamp = 0,
state2flag = false, state2flag = false,
SamStateTracker = {}, SamStateTracker = {},
DLink = false,
DLTimeStamp = 0,
Padding = 10,
} }
--- Advanced state enumerator --- Advanced state enumerator
@@ -219,7 +222,8 @@ do
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral" --@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral"
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional) --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
--@param #string awacs Group name of your Awacs (optional) --@param #string awacs Group name of your Awacs (optional)
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN --@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional)
--@param #number Padding For #SEAD - Extra number of seconds to add to radar switch-back-on time (optional)
--@return #MANTIS self --@return #MANTIS self
--@usage Start up your MANTIS with a basic setting --@usage Start up your MANTIS with a basic setting
-- --
@@ -241,7 +245,7 @@ do
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mybluemantis:Start()` -- `mybluemantis:Start()`
-- --
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff) function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff, Padding)
-- DONE: Create some user functions for these -- DONE: Create some user functions for these
-- DONE: Make HQ useful -- DONE: Make HQ useful
@@ -278,7 +282,9 @@ do
self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins
self.state2flag = false self.state2flag = false
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
self.DLink = false
self.Padding = Padding or 10
if EmOnOff then if EmOnOff then
if EmOnOff == false then if EmOnOff == false then
self.UseEmOnOff = false self.UseEmOnOff = false
@@ -325,7 +331,7 @@ do
end end
-- @field #string version -- @field #string version
self.version="0.5.2" self.version="0.6.2"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions --- --- FSM Functions ---
@@ -593,7 +599,7 @@ do
-- E.g. `mymantis:SetAdvancedMode(true, 90)` -- E.g. `mymantis:SetAdvancedMode(true, 90)`
function MANTIS:SetAdvancedMode(onoff, ratio) function MANTIS:SetAdvancedMode(onoff, ratio)
self:T(self.lid .. "SetAdvancedMode") self:T(self.lid .. "SetAdvancedMode")
self:T({onoff, ratio}) --self:T({onoff, ratio})
local onoff = onoff or false local onoff = onoff or false
local ratio = ratio or 100 local ratio = ratio or 100
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
@@ -619,6 +625,17 @@ do
return self return self
end 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 --- [Internal] Function to check if HQ is alive
-- @param #MANTIS self -- @param #MANTIS self
-- @return #boolean True if HQ is alive, else false -- @return #boolean True if HQ is alive, else false
@@ -633,10 +650,10 @@ do
local hqgrp = GROUP:FindByName(hq) local hqgrp = GROUP:FindByName(hq)
if hqgrp then if hqgrp then
if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive
self:T(self.lid.." HQ is alive!") --self:T(self.lid.." HQ is alive!")
return true return true
else else
self:T(self.lid.." HQ is dead!") --self:T(self.lid.." HQ is dead!")
return false return false
end end
end end
@@ -650,7 +667,7 @@ do
function MANTIS:_CheckEWRState() function MANTIS:_CheckEWRState()
self:T(self.lid .. "CheckEWRState") self:T(self.lid .. "CheckEWRState")
local text = self.lid.." Checking EWR State" local text = self.lid.." Checking EWR State"
self:T(text) --self:T(text)
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end if self.verbose then self:I(text) end
-- start check -- start check
@@ -666,7 +683,7 @@ do
end end
end end
end end
self:T(self.lid..string.format(" No of EWR alive is %d", nalive)) --self:T(self.lid..string.format(" No of EWR alive is %d", nalive))
if nalive > 0 then if nalive > 0 then
return true return true
else else
@@ -682,10 +699,8 @@ do
-- @return #number Previous state for tracking 0, 1, or 2 -- @return #number Previous state for tracking 0, 1, or 2
function MANTIS:_CalcAdvState() function MANTIS:_CalcAdvState()
self:T(self.lid .. "CalcAdvState") self:T(self.lid .. "CalcAdvState")
local text = self.lid.." Calculating Advanced State" local m=MESSAGE:New(self.lid.." Calculating Advanced State",10,"MANTIS"):ToAllIf(self.debug)
self:T(text) if self.verbose then self:I(self.lid.." Calculating Advanced State") end
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end
-- start check -- start check
local currstate = self.adv_state -- save curr state for comparison later local currstate = self.adv_state -- save curr state for comparison later
local EWR_State = self:_CheckEWRState() local EWR_State = self:_CheckEWRState()
@@ -703,10 +718,12 @@ do
local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8 local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8
ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6 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 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) if self.debug or self.verbose then
self:T(text) local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) --self:T(text)
if self.verbose then self:I(text) end local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end
end
return newinterval, currstate return newinterval, currstate
end end
@@ -716,13 +733,13 @@ do
-- @param #boolean ewr If true, will relocate EWR objects -- @param #boolean ewr If true, will relocate EWR objects
function MANTIS:SetAutoRelocate(hq, ewr) function MANTIS:SetAutoRelocate(hq, ewr)
self:T(self.lid .. "SetAutoRelocate") self:T(self.lid .. "SetAutoRelocate")
self:T({hq, ewr}) --self:T({hq, ewr})
local hqrel = hq or false local hqrel = hq or false
local ewrel = ewr or false local ewrel = ewr or false
if hqrel or ewrel then if hqrel or ewrel then
self.autorelocate = true self.autorelocate = true
self.autorelocateunits = { HQ = hqrel, EWR = ewrel } self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
self:T({self.autorelocate, self.autorelocateunits}) --self:T({self.autorelocate, self.autorelocateunits})
end end
return self return self
end end
@@ -739,7 +756,7 @@ do
local HQGroup = self.HQ_CC local HQGroup = self.HQ_CC
if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists
local _hqgrp = self.HQ_CC local _hqgrp = self.HQ_CC
self:T(self.lid.." Relocating HQ") --self:T(self.lid.." Relocating HQ")
local text = self.lid.." Relocating HQ" local text = self.lid.." Relocating HQ"
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll() --local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true) _hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
@@ -752,7 +769,7 @@ do
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
for _,_grp in pairs (EWR_Grps) do for _,_grp in pairs (EWR_Grps) do
if _grp:IsAlive() and _grp:IsGround() then if _grp:IsAlive() and _grp:IsGround() then
self:T(self.lid.." Relocating EWR ".._grp:GetName()) --self:T(self.lid.." Relocating EWR ".._grp:GetName())
local text = self.lid.." Relocating EWR ".._grp:GetName() local text = self.lid.." Relocating EWR ".._grp:GetName()
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end if self.verbose then self:I(text) end
@@ -778,12 +795,14 @@ do
for _,_coord in pairs (set) do for _,_coord in pairs (set) do
local coord = _coord -- get current coord to check local coord = _coord -- get current coord to check
-- output for cross-check -- output for cross-check
local dectstring = coord:ToStringLLDMS()
local samstring = samcoordinate:ToStringLLDMS()
local targetdistance = samcoordinate:DistanceFromPointVec2(coord) local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring) if self.verbose or self.debug then
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug) local dectstring = coord:ToStringLLDMS()
if self.verbose then self:I(self.lid..text) end 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 -- end output to cross-check
if targetdistance <= radius then if targetdistance <= radius then
return true, targetdistance return true, targetdistance
@@ -888,7 +907,7 @@ do
end end
self.SAM_Table = SAM_Tbl self.SAM_Table = SAM_Tbl
-- make SAMs evasive -- make SAMs evasive
local mysead = SEAD:New( SEAD_Grps ) local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
mysead:SetEngagementRange(engagerange) mysead:SetEngagementRange(engagerange)
self.mysead = mysead self.mysead = mysead
return self return self
@@ -999,9 +1018,11 @@ do
self:__ShoradActivated(1,name, radius, ontime) self:__ShoradActivated(1,name, radius, ontime)
end end
-- debug output -- debug output
local text = string.format("SAM %s switched to alarm state RED!", name) if self.debug or self.verbose then
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local text = string.format("SAM %s switched to alarm state RED!", name)
if self.verbose then self:I(self.lid..text) end 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 alive
else else
if samgroup:IsAlive() then if samgroup:IsAlive() then
@@ -1014,9 +1035,11 @@ do
self:__GreenState(1,samgroup) self:__GreenState(1,samgroup)
self.SamStateTracker[name] = "GREEN" self.SamStateTracker[name] = "GREEN"
end end
local text = string.format("SAM %s switched to alarm state GREEN!", name) if self.debug or self.verbose then
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local text = string.format("SAM %s switched to alarm state GREEN!", name)
if self.verbose then self:I(self.lid..text) end 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 alive
end --end check end --end check
end --for for loop end --for for loop
@@ -1066,6 +1089,20 @@ do
end -- end newstate vs oldstate end -- end newstate vs oldstate
return self return self
end 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 --- [Internal] Function to set start state
-- @param #MANTIS self -- @param #MANTIS self
@@ -1077,11 +1114,13 @@ do
self:T({From, Event, To}) self:T({From, Event, To})
self:T(self.lid.."Starting MANTIS") self:T(self.lid.."Starting MANTIS")
self:SetSAMStartState() self:SetSAMStartState()
self.Detection = self:StartDetection() if not self.DLink then
self.Detection = self:StartDetection()
end
if self.advAwacs then if self.advAwacs then
self.AWACS_Detection = self:StartAwacsDetection() self.AWACS_Detection = self:StartAwacsDetection()
end end
self:__Status(self.detectinterval) self:__Status(-math.random(1,10))
return self return self
end end
@@ -1120,11 +1159,16 @@ do
end end
end end
-- timer for advanced state check -- advanced state check
if self.advanced then if self.advanced then
self:_CheckAdvState() self:_CheckAdvState()
end end
-- check DLink state
if self.DLink then
self:_CheckDLinkState()
end
return self return self
end end
@@ -1136,6 +1180,13 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:onafterStatus(From,Event,To) function MANTIS:onafterStatus(From,Event,To)
self:T({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 local interval = self.detectinterval * -1
self:__Status(interval) self:__Status(interval)
return self return self

View File

@@ -5,7 +5,7 @@
-- ## Features: -- ## Features:
-- --
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. -- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <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 a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs. -- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options. -- * Enable / Disable and Configure the Missile Trainer using the various menu options.

View File

@@ -5371,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
if spawnonground then 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 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())) self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))

View File

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

View File

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

View File

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

View File

@@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION
local UnitHit = EventData.TgtUnit 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. -- 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 if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
-- Update last hit time. -- Update last hit time.
self.HitTimeLast=timer.getTime() self.HitTimeLast=timer.getTime()
-- Only trigger attacked event if not already in state "Attacked". -- Only trigger attacked event if not already in state "Attacked".
if self:GetState()~="Attacked" then if self:GetState()~="Attacked" then
self:F2("Hit ==> Attack") self:F2("Hit ==> Attack")
self:Attack() self:Attack()
end end
end
end end
end end
end end
@@ -890,12 +891,14 @@ do -- ZONE_CAPTURE_COALITION
end end
-- Status text. -- 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) if false then
local NewState = self:GetState() 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)
if NewState~=State then local NewState = self:GetState()
text=text..string.format(" --> %s", NewState) if NewState~=State then
text=text..string.format(" --> %s", NewState)
end
self:I(text)
end end
self:I(text)
end end

View File

@@ -18,6 +18,7 @@ _DATABASE:_RegisterCargos()
--- Register zones. --- Register zones.
_DATABASE:_RegisterZones() _DATABASE:_RegisterZones()
_DATABASE:_RegisterAirbases()
--- Check if os etc is available. --- Check if os etc is available.
BASE:I("Checking de-sanitization of os, io and lfs:") BASE:I("Checking de-sanitization of os, io and lfs:")

View File

@@ -32,6 +32,8 @@
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module] -- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module]
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module] -- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**] -- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**]
-- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**]
-- --
-- **Supported Aircraft:** -- **Supported Aircraft:**
-- --
@@ -48,8 +50,8 @@
-- --
-- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier. -- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier.
-- --
-- The AV-8B Harrier and the USS Tarawa are WIP. Those two can only be used together, i.e. the Tarawa is the only carrier the harrier is supposed to land on and -- The AV-8B Harrier, the USS Tarawa, USS America and Juan Carlos I are WIP. The AV-8B harrier and the LHA's and LHD can only be used together, i.e. these ships are the only carriers the harrier is supposed to land on and
-- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier. -- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier.
-- However, the two Case II/III pattern are very similar so this is not a big drawback. -- However, the two Case II/III pattern are very similar so this is not a big drawback.
-- --
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version. -- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
@@ -108,6 +110,7 @@
-- ### AV-8B Harrier at USS Tarawa -- ### AV-8B Harrier at USS Tarawa
-- --
-- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c) -- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c)
-- * [Harrier Practice pattern USS America](https://youtu.be/99NigITYmcI)
-- --
-- === -- ===
-- --
@@ -295,6 +298,8 @@
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_Case1_Landing.png) -- ![Banner Image](..\Presentations\AIRBOSS\Airboss_Case1_Landing.png)
-- --
-- Once the aircraft reaches the Initial, the landing pattern begins. The important steps of the pattern are shown in the image above. -- Once the aircraft reaches the Initial, the landing pattern begins. The important steps of the pattern are shown in the image above.
-- The AV-8B Harrier pattern is very similar, the only differences are as there is no angled deck there is no wake check. from the ninety you wil fly a straight approach offset 26 ft to port (left) of the tram line.
-- The aim is to arrive abeam the landing spot in a stable hover at 120 ft with forward speed matched to the boat. From there the LSO will call "cleared to land". You then level cross to the tram line at the designated landing spot at land vertcally.
-- --
-- --
-- ## CASE III -- ## CASE III
@@ -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. -- 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 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. -- 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°). -- 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. -- 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 -- ## Glideslope Error
-- --
@@ -876,7 +881,7 @@
-- The graph displays the glideslope error (GSE) as a function of the distance to the carrier. -- 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 -- 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. -- At his point further corrections are necessary.
-- --
-- ## Angle of Attack -- ## Angle of Attack
@@ -919,9 +924,9 @@
-- --
-- ## Sound Packs -- ## Sound Packs
-- --
-- The AIRBOSS currently has two different "sound packs" for both LSO and Marshal radios. These contain voice overs by different actors. -- The AIRBOSS currently has two different "sound packs" for LSO and three different "sound Packs" for Marshal radios. These contain voice overs by different actors.
-- These can be set by @{#AIRBOSS.SetVoiceOversLSOByRaynor}() and @{#AIRBOSS.SetVoiceOversMarshalByRaynor}(). These are the default settings. -- These can be set by @{#AIRBOSS.SetVoiceOversLSOByRaynor}() and @{#AIRBOSS.SetVoiceOversMarshalByRaynor}(). These are the default settings.
-- The other sound files can be set by @{#AIRBOSS.SetVoiceOversLSOByFF}() and @{#AIRBOSS.SetVoiceOversMarshalByFF}(). -- The other sound files can be set by @{#AIRBOSS.SetVoiceOversLSOByFF}(), @{#AIRBOSS.SetVoiceOversMarshalByGabriella}() and @{#AIRBOSS.SetVoiceOversMarshalByFF}().
-- Also combinations can be used, e.g. -- Also combinations can be used, e.g.
-- --
-- airbossStennis:SetVoiceOversLSOByFF() -- airbossStennis:SetVoiceOversLSOByFF()
@@ -1256,7 +1261,7 @@ AIRBOSS = {
--- Aircraft types capable of landing on carrier (human+AI). --- Aircraft types capable of landing on carrier (human+AI).
-- @type AIRBOSS.AircraftCarrier -- @type AIRBOSS.AircraftCarrier
-- @field #string AV8B AV-8B Night Harrier. Works only with the USS Tarawa. -- @field #string AV8B AV-8B Night Harrier. Works only with the USS Tarawa, USS America and Juan Carlos I.
-- @field #string A4EC A-4E Community mod. -- @field #string A4EC A-4E Community mod.
-- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics. -- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
-- @field #string F14A F-14A by Heatblur. -- @field #string F14A F-14A by Heatblur.
@@ -1292,6 +1297,8 @@ AIRBOSS.AircraftCarrier={
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module] -- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete] -- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
-- @field #string TARAWA USS Tarawa (LHA-1) -- @field #string TARAWA USS Tarawa (LHA-1)
-- @field #string AMERICA USS America (LHA-6)
-- @field #string JCARLOS Juan Carlos I (L61)
-- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5) -- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5)
AIRBOSS.CarrierType={ AIRBOSS.CarrierType={
ROOSEVELT="CVN_71", ROOSEVELT="CVN_71",
@@ -1301,6 +1308,8 @@ AIRBOSS.CarrierType={
STENNIS="Stennis", STENNIS="Stennis",
VINSON="VINSON", VINSON="VINSON",
TARAWA="LHA_Tarawa", TARAWA="LHA_Tarawa",
AMERICA="USS America LHA-6",
JCARLOS="L61",
KUZNETSOV="KUZNECOW", KUZNETSOV="KUZNECOW",
} }
@@ -1420,8 +1429,8 @@ AIRBOSS.PatternStep={
-- @field #string IM "IM": In the middle. -- @field #string IM "IM": In the middle.
-- @field #string IC "IC": In close. -- @field #string IC "IC": In close.
-- @field #string AR "AR": At the ramp. -- @field #string AR "AR": At the ramp.
-- @field #string AL "AL": Abeam landing position (Tarawa). -- @field #string AL "AL": Abeam landing position (V/STOL).
-- @field #string LC "LC": Level crossing (Tarawa). -- @field #string LC "LC": Level crossing (V/STOL).
-- @field #string IW "IW": In the wires. -- @field #string IW "IW": In the wires.
AIRBOSS.GroovePos={ AIRBOSS.GroovePos={
X0="X0", X0="X0",
@@ -1486,6 +1495,7 @@ AIRBOSS.GroovePos={
-- @field #AIRBOSS.RadioCall DEPARTANDREENTER "Depart and re-enter" call. -- @field #AIRBOSS.RadioCall DEPARTANDREENTER "Depart and re-enter" call.
-- @field #AIRBOSS.RadioCall EXPECTHEAVYWAVEOFF "Expect heavy wavoff" call. -- @field #AIRBOSS.RadioCall EXPECTHEAVYWAVEOFF "Expect heavy wavoff" call.
-- @field #AIRBOSS.RadioCall EXPECTSPOT75 "Expect spot 7.5" call. -- @field #AIRBOSS.RadioCall EXPECTSPOT75 "Expect spot 7.5" call.
-- @field #AIRBOSS.RadioCall EXPECTSPOT5 "Expect spot 5" call.
-- @field #AIRBOSS.RadioCall FAST "You're fast" call. -- @field #AIRBOSS.RadioCall FAST "You're fast" call.
-- @field #AIRBOSS.RadioCall FOULDECK "Foul Deck" call. -- @field #AIRBOSS.RadioCall FOULDECK "Foul Deck" call.
-- @field #AIRBOSS.RadioCall HIGH "You're high" call. -- @field #AIRBOSS.RadioCall HIGH "You're high" call.
@@ -1970,6 +1980,12 @@ function AIRBOSS:New(carriername, alias)
elseif self.carriertype==AIRBOSS.CarrierType.TARAWA then elseif self.carriertype==AIRBOSS.CarrierType.TARAWA then
-- Tarawa parameters. -- Tarawa parameters.
self:_InitTarawa() self:_InitTarawa()
elseif self.carriertype==AIRBOSS.CarrierType.AMERICA then
-- Use America parameters.
self:_InitAmerica()
elseif self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Use Juan Carlos parameters.
self:_InitJcarlos()
elseif self.carriertype==AIRBOSS.CarrierType.KUZNETSOV then elseif self.carriertype==AIRBOSS.CarrierType.KUZNETSOV then
-- Kusnetsov parameters - maybe... -- Kusnetsov parameters - maybe...
self:_InitStennis() self:_InitStennis()
@@ -2061,7 +2077,7 @@ function AIRBOSS:New(carriername, alias)
-- Carrier specific. -- Carrier specific.
if self.carrier:GetTypeName()~=AIRBOSS.CarrierType.TARAWA then if self.carrier:GetTypeName()~=AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.JCARLOS then
-- Flare wires. -- Flare wires.
local w1=stern:Translate(self.carrierparam.wire1, FB) local w1=stern:Translate(self.carrierparam.wire1, FB)
@@ -2438,7 +2454,7 @@ end
-- @param #number duration Default duration of the recovery in minutes. Default 30 min. -- @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 #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 #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 -- @return #AIRBOSS self
function AIRBOSS:SetMenuRecovery(duration, windondeck, uturn, offset) function AIRBOSS:SetMenuRecovery(duration, windondeck, uturn, offset)
@@ -3878,7 +3894,7 @@ function AIRBOSS:_CheckRecoveryTimes()
-- Check if time is less than 5 minutes. -- Check if time is less than 5 minutes.
if nextwindow.WIND and nextwindow.START-time<self.dTturn and not self.turnintowind then 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 hdg=self:GetHeading()
local wind=self:GetHeadingIntoWind() local wind=self:GetHeadingIntoWind()
local delta=self:_GetDeltaHeading(hdg, wind) local delta=self:_GetDeltaHeading(hdg, wind)
@@ -3896,7 +3912,7 @@ function AIRBOSS:_CheckRecoveryTimes()
end end
--Debug info --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. -- Time into the wind 1 day or if longer recovery time + the 5 min early.
local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn, 60*60*24) local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn, 60*60*24)
@@ -4401,6 +4417,85 @@ function AIRBOSS:_InitTarawa()
end end
--- Init parameters for LHA-6 America carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitAmerica()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist =-125
self.carrierparam.deckheight = 20 --67 ft
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=257
self.carrierparam.totwidthport=11
self.carrierparam.totwidthstarboard=25
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 240
self.carrierparam.rwywidth = 15
-- Wires.
self.carrierparam.wire1=nil
self.carrierparam.wire2=nil
self.carrierparam.wire3=nil
self.carrierparam.wire4=nil
-- Late break.
self.BreakLate.name="Late Break"
self.BreakLate.Xmin=-UTILS.NMToMeters(1) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax= UTILS.NMToMeters(5) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin=-UTILS.NMToMeters(1.6) -- Not more than 1.6 NM port.
self.BreakLate.Zmax= UTILS.NMToMeters(1) -- Not more than 1 NM starboard.
self.BreakLate.LimitXmin= 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax= nil
self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax= nil
end
--- Init parameters for L61 Juan Carlos carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitJcarlos()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist =-125
self.carrierparam.deckheight = 20 --67 ft
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=231
self.carrierparam.totwidthport=10
self.carrierparam.totwidthstarboard=22
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 202
self.carrierparam.rwywidth = 14
-- Wires.
self.carrierparam.wire1=nil
self.carrierparam.wire2=nil
self.carrierparam.wire3=nil
self.carrierparam.wire4=nil
-- Late break.
self.BreakLate.name="Late Break"
self.BreakLate.Xmin=-UTILS.NMToMeters(1) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax= UTILS.NMToMeters(5) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin=-UTILS.NMToMeters(1.6) -- Not more than 1.6 NM port.
self.BreakLate.Zmax= UTILS.NMToMeters(1) -- Not more than 1 NM starboard.
self.BreakLate.LimitXmin= 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax= nil
self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax= nil
end
--- Init parameters for Marshal Voice overs *Gabriella* by HighwaymanEd. --- Init parameters for Marshal Voice overs *Gabriella* by HighwaymanEd.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #string mizfolder (Optional) Folder within miz file where the sound files are located. -- @param #string mizfolder (Optional) Folder within miz file where the sound files are located.
@@ -4555,6 +4650,7 @@ function AIRBOSS:SetVoiceOversLSOByRaynor(mizfolder)
self.LSOCall.DEPARTANDREENTER.duration=1.10 self.LSOCall.DEPARTANDREENTER.duration=1.10
self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.30 self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.30
self.LSOCall.EXPECTSPOT75.duration=1.85 self.LSOCall.EXPECTSPOT75.duration=1.85
self.LSOCall.EXPECTSPOT5.duration=1.3
self.LSOCall.FAST.duration=0.75 self.LSOCall.FAST.duration=0.75
self.LSOCall.FOULDECK.duration=0.75 self.LSOCall.FOULDECK.duration=0.75
self.LSOCall.HIGH.duration=0.65 self.LSOCall.HIGH.duration=0.65
@@ -4613,6 +4709,7 @@ function AIRBOSS:SetVoiceOversLSOByFF(mizfolder)
self.LSOCall.DEPARTANDREENTER.duration=1.10 self.LSOCall.DEPARTANDREENTER.duration=1.10
self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20 self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20
self.LSOCall.EXPECTSPOT75.duration=2.00 self.LSOCall.EXPECTSPOT75.duration=2.00
self.LSOCall.EXPECTSPOT5.duration=1.3
self.LSOCall.FAST.duration=0.70 self.LSOCall.FAST.duration=0.70
self.LSOCall.FOULDECK.duration=0.62 self.LSOCall.FOULDECK.duration=0.62
self.LSOCall.HIGH.duration=0.65 self.LSOCall.HIGH.duration=0.65
@@ -4880,6 +4977,14 @@ function AIRBOSS:_InitVoiceOvers()
subtitle="Expect spot 7.5", subtitle="Expect spot 7.5",
duration=2.0, duration=2.0,
subduration=5, subduration=5,
},
EXPECTSPOT5={
file="LSO-ExpectSpot5",
suffix="ogg",
loud=false,
subtitle="Expect spot 5",
duration=1.3,
subduration=5,
}, },
STABILIZED={ STABILIZED={
file="LSO-Stabilized", file="LSO-Stabilized",
@@ -5540,14 +5645,14 @@ function AIRBOSS:_GetAircraftAoA(playerData)
aoa.Fast = 8.25 --=17.5/2 aoa.Fast = 8.25 --=17.5/2
aoa.FAST = 8.00 --=16.5/2 aoa.FAST = 8.00 --=16.5/2
elseif harrier then elseif harrier then
-- AV-8B Harrier parameters. This might need further tuning. -- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 60 - 73.
aoa.SLOW = 14.0 aoa.SLOW = 14.0
aoa.Slow = 13.0 aoa.Slow = 13.0
aoa.OnSpeedMax = 12.0 aoa.OnSpeedMax = 12.0
aoa.OnSpeed = 11.0 aoa.OnSpeed = 11.0
aoa.OnSpeedMin = 10.0 aoa.OnSpeedMin = 10.0
aoa.Fast = 9.0 aoa.Fast = 8.0
aoa.FAST = 8.0 aoa.FAST = 7.5
end end
return aoa return aoa
@@ -5807,7 +5912,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
alt=UTILS.FeetToMeters(300) --? alt=UTILS.FeetToMeters(300) --?
elseif harrier then elseif harrier then
-- 300-325 ft -- 300-325 ft
alt=UTILS.FeetToMeters(300) alt=UTILS.FeetToMeters(300)-- Need to verify
end end
aoa=aoaac.OnSpeed aoa=aoaac.OnSpeed
@@ -6746,8 +6851,8 @@ function AIRBOSS:_GetMarshalAltitude(stack, case)
-- Second point 1.5 NM ahead. -- Second point 1.5 NM ahead.
p2=Carrier:Translate(UTILS.NMToMeters(1.5), hdg) p2=Carrier:Translate(UTILS.NMToMeters(1.5), hdg)
-- Tarawa Delta pattern. -- Tarawa,LHA,LHD Delta patterns.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Pattern is directly overhead the carrier. -- Pattern is directly overhead the carrier.
p1=Carrier:Translate(UTILS.NMToMeters(1.0), hdg+90) p1=Carrier:Translate(UTILS.NMToMeters(1.0), hdg+90)
@@ -6933,7 +7038,7 @@ function AIRBOSS:_AddMarshalGroup(flight, stack)
-- For case 1 we want the BRC but above routine return FB. -- For case 1 we want the BRC but above routine return FB.
radial=self:GetBRC() radial=self:GetBRC()
end 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, "") self:MessageToPlayer(flight, text, nil, "")
end end
@@ -8592,7 +8697,7 @@ function AIRBOSS:OnEventLand(EventData)
self:T(self.lid..text) self:T(self.lid..text)
-- Check carrier type. -- Check carrier type.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Power "Idle". -- Power "Idle".
self:RadioTransmission(self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true)
@@ -8627,7 +8732,7 @@ function AIRBOSS:OnEventLand(EventData)
-- AI unit landed -- -- AI unit landed --
-------------------- --------------------
if self.carriertype~=AIRBOSS.CarrierType.TARAWA then if self.carriertype~=AIRBOSS.CarrierType.TARAWA or self.carriertype~=AIRBOSS.CarrierType.AMERICA or self.carriertype~=AIRBOSS.CarrierType.JCARLOS then
-- Coordinate at landing event -- Coordinate at landing event
local coord=EventData.IniUnit:GetCoordinate() local coord=EventData.IniUnit:GetCoordinate()
@@ -9346,7 +9451,7 @@ function AIRBOSS:_Initial(playerData)
-- Hook down for students. -- Hook down for students.
if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then 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 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 else
hint=hint.." - Hook down!" hint=hint.." - Hook down!"
end end
@@ -9534,8 +9639,10 @@ function AIRBOSS:_Bullseye(playerData)
-- Hint for player about altitude, AoA etc. -- Hint for player about altitude, AoA etc.
self:_PlayerHint(playerData) self:_PlayerHint(playerData)
-- LSO expect spot 7.5 call -- LSO expect spot 5 or 7.5 call
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and self.carriertype==AIRBOSS.CarrierType.JCARLOS then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT5, nil, nil, nil, true)
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, nil, nil, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, nil, nil, nil, true)
end end
@@ -9671,8 +9778,8 @@ function AIRBOSS:_CheckForLongDownwind(playerData)
-- 1.6 NM from carrier is too far. -- 1.6 NM from carrier is too far.
local limit=UTILS.NMToMeters(-1.6) local limit=UTILS.NMToMeters(-1.6)
-- For the tarawa we give a bit more space. -- For the tarawa, other LHA and LHD we give a bit more space.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
limit=UTILS.NMToMeters(-2.0) limit=UTILS.NMToMeters(-2.0)
end end
@@ -9717,8 +9824,10 @@ function AIRBOSS:_Abeam(playerData)
-- Paddles contact. -- Paddles contact.
self:RadioTransmission(self.LSORadio, self.LSOCall.PADDLESCONTACT, nil, nil, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.PADDLESCONTACT, nil, nil, nil, true)
-- LSO expect spot 7.5 call -- LSO expect spot 5 or 7.5 call
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and self.carriertype==AIRBOSS.CarrierType.JCARLOS then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT5, false, 5, nil, true)
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, false, 5, nil, true) self:RadioTransmission(self.LSORadio, self.LSOCall.EXPECTSPOT75, false, 5, nil, true)
end end
@@ -9755,7 +9864,7 @@ function AIRBOSS:_Ninety(playerData)
self:_PlayerHint(playerData) self:_PlayerHint(playerData)
-- Next step: wake. -- Next step: wake.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Harrier has no wake stop. It stays port of the boat. -- Harrier has no wake stop. It stays port of the boat.
self:_SetPlayerStep(playerData, AIRBOSS.PatternStep.FINAL) self:_SetPlayerStep(playerData, AIRBOSS.PatternStep.FINAL)
else else
@@ -10429,7 +10538,7 @@ function AIRBOSS:_GetSternCoord()
--local stern=self:GetCoordinate() --local stern=self:GetCoordinate()
-- Stern coordinate (sterndist<0). -- Stern coordinate (sterndist<0).
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Tarawa: Translate 8 meters port. -- Tarawa: Translate 8 meters port.
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true) self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
@@ -11172,7 +11281,7 @@ function AIRBOSS:_GetZoneHolding(case, stack)
self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", Post:GetVec2(), self.marshalradius) self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", Post:GetVec2(), self.marshalradius)
-- Delta pattern. -- Delta pattern.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters(5)) self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters(5))
end end
@@ -11225,7 +11334,7 @@ function AIRBOSS:_GetZoneCommence(case, stack)
-- Three position -- Three position
local Three=self:GetCoordinate():Translate(D, hdg+275) local Three=self:GetCoordinate():Translate(D, hdg+275)
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
local Dx=UTILS.NMToMeters(2.25) local Dx=UTILS.NMToMeters(2.25)
@@ -11331,15 +11440,15 @@ function AIRBOSS:_AttitudeMonitor(playerData)
-- Output -- Output
local text=string.format("Pattern step: %s", step) 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 if self.Debug then
-- Velocity vector. -- Velocity vector.
text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s", velo.x, velo.y, velo.z) text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s", velo.x, velo.y, velo.z)
--Wind vector. --Wind vector.
text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s", wind.x, wind.y, wind.z) text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s", wind.x, wind.y, wind.z)
end end
text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw) 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("\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y*196.85)
local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit) local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit)
-- Get player velocity in km/h. -- Get player velocity in km/h.
local vplayer=playerData.unit:GetVelocityKMH() local vplayer=playerData.unit:GetVelocityKMH()
@@ -11360,14 +11469,14 @@ function AIRBOSS:_AttitudeMonitor(playerData)
playerData.step==AIRBOSS.PatternStep.GROOVE_IW then playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
local lue=self:_Lineup(playerData.unit, true) local lue=self:_Lineup(playerData.unit, true)
local gle=self:_Glideslope(playerData.unit) local gle=self:_Glideslope(playerData.unit)
text=text..string.format("\nGamma=%.1f° | Rho=%.1f°", relhead, phi) 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("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units", lue, gle, self:_AoADeg2Units(playerData, aoa))
local grade, points, analysis=self:_LSOgrade(playerData) local grade, points, analysis=self:_LSOgrade(playerData)
text=text..string.format("\nTgroove=%.1f sec", self:_GetTimeInGroove(playerData)) text=text..string.format("\nTgroove=%.1f sec", self:_GetTimeInGroove(playerData))
text=text..string.format("\nGrade: %s %.1f PT - %s", grade, points, analysis) text=text..string.format("\nGrade: %s %.1f PT - %s", grade, points, analysis)
else else
text=text..string.format("\nR=%.2f NM | X=%d Z=%d m", UTILS.MetersToNM(rho), dx, dz) 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 end
MESSAGE:New(text, 1, nil , true):ToClient(playerData.client) MESSAGE:New(text, 1, nil , true):ToClient(playerData.client)
@@ -11516,7 +11625,7 @@ function AIRBOSS:_GetAltCarrier(unit)
return h return h
end end
--- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa we take the abeam landing spot 120 ft abeam the 7.5 position. --- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa and America we take the abeam landing spot 120 ft abeam the 7.5 position, for the Juan Carlos I it is 120 ft and abeam the 5 position.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @return Core.Point#COORDINATE Optimal landing coordinate. -- @return Core.Point#COORDINATE Optimal landing coordinate.
function AIRBOSS:_GetOptLandingCoordinate() function AIRBOSS:_GetOptLandingCoordinate()
@@ -11538,7 +11647,24 @@ function AIRBOSS:_GetOptLandingCoordinate()
-- Alitude 120 ft. -- Alitude 120 ft.
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120)) 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 else
-- Ideally we want to land between 2nd and 3rd wire. -- Ideally we want to land between 2nd and 3rd wire.
@@ -11573,7 +11699,22 @@ function AIRBOSS:_GetLandingSpotCoordinate()
-- Primary landing spot 7.5 -- Primary landing spot 7.5
self.landingspotcoord:Translate(57, hdg, true, true):SetAltitude(self.carrierparam.deckheight) self.landingspotcoord:Translate(57, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
elseif self.carriertype==AIRBOSS.CarrierType.AMERICA then
-- Landing 100 ft abeam, 120 alt.
local hdg=self:GetHeading()
-- Primary landing spot 7.5 a little further forwad on the America
self.landingspotcoord:Translate(59, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
elseif self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-- Landing 100 ft abeam, 120 alt.
local hdg=self:GetHeading()
-- Primary landing spot 5.0 -- TODO voice for different landing Spots.
self.landingspotcoord:Translate(89, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
end end
return self.landingspotcoord return self.landingspotcoord
@@ -12063,8 +12204,13 @@ end
-- * 12-21 seconds: OK (15-18 is ideal) -- * 12-21 seconds: OK (15-18 is ideal)
-- * 22-24 seconds: Fair "(OK) -- * 22-24 seconds: Fair "(OK)
-- * > 24 seconds: No Grade "--" -- * > 24 seconds: No Grade "--"
-- --
-- If you manage to be between 16.4 and and 16.6 seconds, you will even get and okay underline "\_OK\_". -- If you manage to be between 16.4 and and 16.6 seconds, you will even get and okay underline "\_OK\_".
-- No groove time for Harrier on LHA, LHD set to Tgroove Unicorn as starting point to allow possible _OK_ 5.0.
-- If time in the AV-8B
--
-- * < 90 seconds: OK V/STOL
-- * > 91 Seconds: SLOW V/STOL (Early hover stop selection)
-- --
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #AIRBOSS.PlayerData playerData Player data table. -- @param #AIRBOSS.PlayerData playerData Player data table.
@@ -12073,7 +12219,7 @@ function AIRBOSS:_EvalGrooveTime(playerData)
-- Time in groove. -- Time in groove.
local t=playerData.Tgroove local t=playerData.Tgroove
local grade="" local grade=""
if t<9 then if t<9 then
grade="_NESA_" grade="_NESA_"
@@ -12083,14 +12229,26 @@ function AIRBOSS:_EvalGrooveTime(playerData)
grade="OK Groove" grade="OK Groove"
elseif t<=24 then elseif t<=24 then
grade="(LIG)" grade="(LIG)"
-- Time in groove for AV-8B
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and t<55 then -- VSTOL Late Hover stop selection too fast to Abeam LDG Spot AV-8B.
grade="FAST V/STOL Groove"
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and t<90 then -- VSTOL Operations with AV-8B.
grade="OK V/STOL Groove"
elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and t>=91 then -- VSTOL Early Hover stop selection slow to Abeam LDG Spot AV-8B.
grade="SLOW V/STOL Groove"
else else
grade="LIG" grade="LIG"
end end
-- The unicorn! -- The unicorn!
if t>=16.4 and t<=16.6 then if t>=16.4 and t<=16.6 then
grade="_OK_" grade="_OK_"
end end
-- V/STOL Unicorn!
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and (t>=65.0 and t<=75.0) then
grade="_OK_ V/STOL"
end
return grade return grade
end end
@@ -12108,7 +12266,7 @@ function AIRBOSS:_LSOgrade(playerData)
return select(2, string.gsub(base, pattern, "")) return select(2, string.gsub(base, pattern, ""))
end end
-- Analyse flight data and conver to LSO text. -- Analyse flight data and convert to LSO text.
local GXX,nXX=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.XX) local GXX,nXX=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.XX)
local GIM,nIM=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IM) local GIM,nIM=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IM)
local GIC,nIC=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IC) local GIC,nIC=self:_Flightdata2Text(playerData, AIRBOSS.GroovePos.IC)
@@ -12117,25 +12275,37 @@ function AIRBOSS:_LSOgrade(playerData)
-- Put everything together. -- Put everything together.
local G=GXX.." "..GIM.." ".." "..GIC.." "..GAR local G=GXX.." "..GIM.." ".." "..GIC.." "..GAR
-- Count number of minor, normal and major deviations. -- Count number of minor, normal and major deviations. TODO - work on Harrier counts due slower approach speed.
local N=nXX+nIM+nIC+nAR local N=nXX+nIM+nIC+nAR
local nL=count(G, '_')/2 local nL=count(G, '_')/2
local nS=count(G, '%(') local nS=count(G, '%(')
local nN=N-nS-nL local nN=N-nS-nL
-- Groove time 15-18.99 sec for a unicorn. -- Groove time 15-18.99 sec for a unicorn. Or 65-70 for V/STOL unicorn.
local Tgroove=playerData.Tgroove local Tgroove=playerData.Tgroove
local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false
local TgrooveVstolUnicorn=Tgroove and (Tgroove>=65.0 and Tgroove<=70.0)and playerData.actype==AIRBOSS.AircraftCarrier.AV8B or false
local grade local grade
local points local points
if N==0 and TgrooveUnicorn then if N==0 and (TgrooveUnicorn or TgrooveVstolUnicorn ) then
-- No deviations, should be REALLY RARE! -- No deviations, should be REALLY RARE!
grade="_OK_" grade="_OK_"
points=5.0 points=5.0
G="Unicorn" G="Unicorn"
else else
if nL>0 then
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe. (WIP requires feedback)
-- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
if nL>3 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Larger deviations ==> "No grade" 2.0 points.
grade="--"
points=2.0
elseif nN>2 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade="(OK)"
points=3.0
elseif nL>0 then
-- Larger deviations ==> "No grade" 2.0 points. -- Larger deviations ==> "No grade" 2.0 points.
grade="--" grade="--"
points=2.0 points=2.0
@@ -12148,7 +12318,8 @@ function AIRBOSS:_LSOgrade(playerData)
grade="OK" grade="OK"
points=4.0 points=4.0
end end
end
end
-- Replace" )"( and "__" -- Replace" )"( and "__"
G=G:gsub("%)%(", "") G=G:gsub("%)%(", "")
@@ -12444,7 +12615,7 @@ function AIRBOSS:_GS(step, n)
if n==-1 then if n==-1 then
gp=AIRBOSS.GroovePos.IC gp=AIRBOSS.GroovePos.IC
elseif n==1 then elseif n==1 then
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
gp=AIRBOSS.GroovePos.AL gp=AIRBOSS.GroovePos.AL
else else
gp=AIRBOSS.GroovePos.IW gp=AIRBOSS.GroovePos.IW
@@ -12695,7 +12866,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
if self.holdingoffset<0 then if self.holdingoffset<0 then
turn="left" turn="left"
end 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
end end
@@ -12704,7 +12875,7 @@ function AIRBOSS:_PlayerHint(playerData, delay, soundoff)
if playerData.step==AIRBOSS.PatternStep.DIRTYUP then if playerData.step==AIRBOSS.PatternStep.DIRTYUP then
if playerData.difficulty==AIRBOSS.Difficulty.EASY then if playerData.difficulty==AIRBOSS.Difficulty.EASY then
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
hint=hint.."\nFAF! Checks completed. Nozzles 50°." hint=hint.."\nFAF! Checks completed. Nozzles 50°."
else else
--TODO: Tomcat? --TODO: Tomcat?
hint=hint.."\nDirty up! Hook, gear and flaps down." hint=hint.."\nDirty up! Hook, gear and flaps down."
@@ -12773,14 +12944,14 @@ function AIRBOSS:_StepHint(playerData, step)
-- Late break. -- Late break.
if step==AIRBOSS.PatternStep.LATEBREAK then if step==AIRBOSS.PatternStep.LATEBREAK then
if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B 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
end end
-- Abeam. -- Abeam.
if step==AIRBOSS.PatternStep.ABEAM then if step==AIRBOSS.PatternStep.ABEAM then
if playerData.actype==AIRBOSS.AircraftCarrier.AV8B 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 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." hint=hint.."\nSlats/Flaps EXTENDED < 225 KIAS. DLC SELECTED. Auto Throttle IF DESIRED."
else else
@@ -13215,7 +13386,7 @@ function AIRBOSS:_Debrief(playerData)
end end
-- Re-enter message. -- 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) self:MessageToPlayer(playerData, text, "LSO", nil, nil, false, 5)
else else
@@ -13411,9 +13582,9 @@ function AIRBOSS:_CheckCollisionCoord(coordto, coordfrom)
local text="" local text=""
if clear then 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 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 end
self:T2(self.lid..text) self:T2(self.lid..text)
@@ -13473,7 +13644,7 @@ function AIRBOSS:_Pathfinder()
local collision=self:_CheckFreePathToNextWP(fromcoord) local collision=self:_CheckFreePathToNextWP(fromcoord)
-- Debug info. -- 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 path is clear, we start a little detour.
if not collision then if not collision then
@@ -13907,7 +14078,7 @@ function AIRBOSS:_CheckPatternUpdate()
-- Update if carrier moves by more than 2.5 NM. -- Update if carrier moves by more than 2.5 NM.
local Dupdate=UTILS.NMToMeters(2.5) 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 local Hupdate=5
----------------------- -----------------------
@@ -13941,7 +14112,7 @@ function AIRBOSS:_CheckPatternUpdate()
-- Check if orientation changed. -- Check if orientation changed.
local Hchange=false local Hchange=false
if math.abs(deltaHeading)>=Hupdate then 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 Hchange=true
end end
@@ -14334,17 +14505,17 @@ function AIRBOSS:_IsCarrierAircraft(unit)
-- Get aircraft type name -- Get aircraft type name
local aircrafttype=unit:GetTypeName() local aircrafttype=unit:GetTypeName()
-- Special case for Harrier which can only land on Tarawa. -- Special case for Harrier which can only land on Tarawa, LHA and LHD.
if aircrafttype==AIRBOSS.AircraftCarrier.AV8B then if aircrafttype==AIRBOSS.AircraftCarrier.AV8B then
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
return true return true
else else
return false return false
end end
end end
-- Also only Harriers can land on the Tarawa. -- Also only Harriers can land on the Tarawa, LHA and LHD.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
if aircrafttype~=AIRBOSS.AircraftCarrier.AV8B then if aircrafttype~=AIRBOSS.AircraftCarrier.AV8B then
return false return false
end end
@@ -15642,7 +15813,7 @@ end
function AIRBOSS:_MarshalCallNewFinalBearing(FB) function AIRBOSS:_MarshalCallNewFinalBearing(FB)
-- Subtitle. -- Subtitle.
local text=string.format("new final bearing %03d°.", FB) local text=string.format("new final bearing %03d°.", FB)
-- Debug message. -- Debug message.
self:I(self.lid..text) self:I(self.lid..text)
@@ -15665,7 +15836,7 @@ end
function AIRBOSS:_MarshalCallCarrierTurnTo(hdg) function AIRBOSS:_MarshalCallCarrierTurnTo(hdg)
-- Subtitle. -- 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. -- Debug message.
self:I(self.lid..text) self:I(self.lid..text)
@@ -15718,11 +15889,11 @@ function AIRBOSS:_MarshalCallRecoveryStart(case)
-- Debug output. -- Debug output.
local text=string.format("Starting aircraft recovery Case %d ops.", case) local text=string.format("Starting aircraft recovery Case %d ops.", case)
if case==1 then if case==1 then
text=text..string.format(" BRC %03d°.", self:GetBRC()) text=text..string.format(" BRC %03d°.", self:GetBRC())
elseif case==2 then 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 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 end
self:T(self.lid..text) self:T(self.lid..text)
@@ -15767,7 +15938,7 @@ function AIRBOSS:_MarshalCallArrived(modex, case, brc, altitude, charlie, qfe)
local CT=UTILS.Split(clock[1], ":") local CT=UTILS.Split(clock[1], ":")
-- Subtitle text. -- 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. -- Debug message.
self:I(self.lid..text) 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, "60 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 60)
missionCommands.addCommandForGroup(gid, "90 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 90) missionCommands.addCommandForGroup(gid, "90 min", _menusetrtime, self._SkipperRecoveryTime, self, _unitName, 90)
local _menusetrtime=missionCommands.addSubMenuForGroup(gid, "Set Marshal Radial", _skipperPath) local _menusetrtime=missionCommands.addSubMenuForGroup(gid, "Set Marshal Radial", _skipperPath)
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, "+15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 15)
missionCommands.addCommandForGroup(gid, "0°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0) missionCommands.addCommandForGroup(gid, "", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, 0)
missionCommands.addCommandForGroup(gid, "-15°", _menusetrtime, self._SkipperRecoveryOffset, self, _unitName, -15) 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, "U-turn On/Off", _skipperPath, self._SkipperRecoveryUturn, self, _unitName) 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 I", _skipperPath, self._SkipperStartRecovery, self, _unitName, 1)
missionCommands.addCommandForGroup(gid, "Start CASE II", _skipperPath, self._SkipperStartRecovery, self, _unitName, 2) missionCommands.addCommandForGroup(gid, "Start CASE II", _skipperPath, self._SkipperStartRecovery, self, _unitName, 2)
@@ -16005,7 +16176,7 @@ function AIRBOSS:_SkipperStartRecovery(_unitName, case)
-- Inform player. -- 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)) 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 if case>1 then
text=text..string.format(" Marshal radial %d°.", self.skipperOffset) text=text..string.format(" Marshal radial %d°.", self.skipperOffset)
end end
if self:IsRecovering() then if self:IsRecovering() then
text="negative, carrier is already recovering." text="negative, carrier is already recovering."
@@ -16071,7 +16242,7 @@ function AIRBOSS:_SkipperRecoveryOffset(_unitName, offset)
if playerData then if playerData then
-- Inform player. -- 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:MessageToPlayer(playerData, text, "AIRBOSS")
self.skipperOffset=offset self.skipperOffset=offset
@@ -16534,7 +16705,7 @@ function AIRBOSS:_RequestCommence(_unitName)
-- For case 1 we want the BRC but above routine return FB. -- For case 1 we want the BRC but above routine return FB.
radial=self:GetBRC() radial=self:GetBRC()
end 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 end
-- TODO: Inform section members. -- TODO: Inform section members.
@@ -17146,7 +17317,7 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
-- Only include current and future recovery windows. -- Only include current and future recovery windows.
if Tabs<recovery.STOP then if Tabs<recovery.STOP then
-- Output text. -- 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 if recovery.WIND then
recoverytext=recoverytext..string.format(" @ %.1f kts wind", recovery.SPEED) recoverytext=recoverytext..string.format(" @ %.1f kts wind", recovery.SPEED)
end end
@@ -17187,9 +17358,9 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
text=text..string.format("Case %d recovery ops\n", self.case) text=text..string.format("Case %d recovery ops\n", self.case)
else else
local radial=self:GetRadial(self.case, true, true, false) 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 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("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("Tower frequency %.3f MHz\n", self.TowerFreq)
text=text..string.format("Marshal radio %.3f MHz\n", self.MarshalFreq) 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 WodPA=UTILS.MpsToKnots(WodPA)
local WodPP=UTILS.MpsToKnots(WodPP) local WodPP=UTILS.MpsToKnots(WodPP)
local WD=string.format('%03d°', Wd) local WD=string.format('%03d°', Wd)
local Ts=string.format("%d°C",T) 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 tW=string.format("%.1f knots", UTILS.MpsToKnots(Ws))
local tP=string.format("%.2f inHg", UTILS.hPa2inHg(P)) 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 if playerData.step==AIRBOSS.PatternStep.HOLDING and playerData.case>1 then
-- Get inverse magnetic radial potential offset. -- Get inverse magnetic radial potential offset.
local radial=self:GetRadial(playerData.case, true, true, true) 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
end end
@@ -17545,7 +17716,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
local brc=self:GetBRC() local brc=self:GetBRC()
-- Help player to find its way to the initial zone. -- 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 elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
@@ -17560,7 +17731,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
local hdg=self:GetRadial(playerData.case, true, true, true) local hdg=self:GetRadial(playerData.case, true, true, true)
-- Help player to find its way to the initial zone. -- 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 end
@@ -17713,8 +17884,8 @@ function AIRBOSS:_MarkCaseZones(_unitName, flare)
self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green, 45) self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green, 45)
end end
-- Tarawa landing spots. -- Tarawa, LHA and LHD landing spots.
if self.carriertype==AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS then
text=text.."\n* abeam landing stop with RED flares" text=text.."\n* abeam landing stop with RED flares"
-- Abeam landing spot zone. -- Abeam landing spot zone.
local ALSPT=self:_GetZoneAbeamLandingSpot() local ALSPT=self:_GetZoneAbeamLandingSpot()

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -157,7 +157,7 @@ end
--- Set the frequency for the radio transmission. --- 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. -- 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 #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 -- @return #RADIO self
function RADIO:SetFrequency(Frequency) function RADIO:SetFrequency(Frequency)
self:F2(Frequency) self:F2(Frequency)
@@ -165,7 +165,7 @@ function RADIO:SetFrequency(Frequency)
if type(Frequency) == "number" then if type(Frequency) == "number" then
-- If frequency is in range -- 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 -- Convert frequency from MHz to Hz
self.Frequency = Frequency * 1000000 self.Frequency = Frequency * 1000000
@@ -186,10 +186,10 @@ function RADIO:SetFrequency(Frequency)
end end
return self return self
end -- 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 return self
end end

View File

@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
["8000"] = { "8000", 0.92 }, ["8000"] = { "8000", 0.92 },
["9000"] = { "9000", 0.87 }, ["9000"] = { "9000", 0.87 },
["Ñ<EFBFBD>Ñепени"] = { "degrees", 0.5 }, ["градусы"] = { "degrees", 0.5 },
["километров"] = { "kilometers", 0.65 }, ["километры"] = { "kilometers", 0.65 },
["km"] = { "kilometers", 0.65 }, ["km"] = { "kilometers", 0.65 },
["миль"] = { "miles", 0.45 }, ["мили"] = { "miles", 0.45 },
["mi"] = { "miles", 0.45 }, ["mi"] = { "miles", 0.45 },
["метры"] = { "meters", 0.41 }, ["метров"] = { "meters", 0.41 },
["m"] = { "meters", 0.41 }, ["m"] = { "meters", 0.41 },
["ноги"] = { "feet", 0.37 }, ["ноги"] = { "feet", 0.37 },
["br"] = { "br", 1.1 }, ["br"] = { "br", 1.1 },
["bra"] = { "bra", 0.3 }, ["bra"] = { "bra", 0.3 },
["возвращение на базу"] = { "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 }, ["для"] = { "for", 0.27 },
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 }, ["и"] = { "and", 0.17 },
["перехват Ñ<>амолеÑов"] = { "intercepting_bogeys", 1.22 }, ["на сайте"] = { "at", 0.19 },
["поражение наземной цели"] = { "engaging_ground_target", 1.53 }, ["точка"] = { "dot", 0.51 },
["захватывающие Ñ<>амолеÑÑ"] = { "engaging_bogeys", 1.68 }, ["защитник"] = { "defender", 0.45 },
["колеÑ<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 },
} }
--- Create a new RADIOSPEECH object for a given radio frequency/modulation. --- Create a new RADIOSPEECH object for a given radio frequency/modulation.

View File

@@ -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. --- 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. -- * Grimes : Design & Programming of the MIST framework.
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- * FlightControl : Rework to OO framework -- * FlightControl : Rework to OO framework.
-- --
-- @module Utils -- @module Utils
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -18,7 +18,7 @@
-- @field White -- @field White
-- @field Orange -- @field Orange
-- @field Blue -- @field Blue
SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR
--- @type FLARECOLOR --- @type FLARECOLOR
@@ -94,7 +94,7 @@ CALLSIGN={
Texaco=1, Texaco=1,
Arco=2, Arco=2,
Shell=3, Shell=3,
}, },
-- JTAC -- JTAC
JTAC={ JTAC={
Axeman=1, Axeman=1,
@@ -163,31 +163,31 @@ UTILS = {
UTILS.IsInstanceOf = function( object, className ) UTILS.IsInstanceOf = function( object, className )
-- Is className NOT a string ? -- Is className NOT a string ?
if not type( className ) == 'string' then if not type( className ) == 'string' then
-- Is className a Moose class ? -- Is className a Moose class ?
if type( className ) == 'table' and className.IsInstanceOf ~= nil then if type( className ) == 'table' and className.IsInstanceOf ~= nil then
-- Get the name of the Moose class as a string -- Get the name of the Moose class as a string
className = className.ClassName className = className.ClassName
-- className is neither a string nor a Moose class, throw an error -- className is neither a string nor a Moose class, throw an error
else else
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall -- 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 ) local err_str = 'className parameter should be a string; parameter received: '..type( className )
return false return false
-- error( err_str ) -- error( err_str )
end end
end end
-- Is the object a Moose class instance ? -- Is the object a Moose class instance ?
if type( object ) == 'table' and object.IsInstanceOf ~= nil then if type( object ) == 'table' and object.IsInstanceOf ~= nil then
-- Use the IsInstanceOf method of the BASE class -- Use the IsInstanceOf method of the BASE class
return object:IsInstanceOf( className ) return object:IsInstanceOf( className )
else else
-- If the object is not an instance of a Moose class, evaluate against lua basic data types -- 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' } local basicDataTypes = { 'string', 'number', 'function', 'boolean', 'nil', 'table' }
for _, basicDataType in ipairs( basicDataTypes ) do for _, basicDataType in ipairs( basicDataTypes ) do
@@ -196,7 +196,7 @@ UTILS.IsInstanceOf = function( object, className )
end end
end end
end end
-- Check failed -- Check failed
return false return false
end end
@@ -208,7 +208,7 @@ end
UTILS.DeepCopy = function(object) UTILS.DeepCopy = function(object)
local lookup_table = {} local lookup_table = {}
-- Copy function. -- Copy function.
local function _copy(object) local function _copy(object)
if type(object) ~= "table" then if type(object) ~= "table" then
@@ -216,20 +216,20 @@ UTILS.DeepCopy = function(object)
elseif lookup_table[object] then elseif lookup_table[object] then
return lookup_table[object] return lookup_table[object]
end end
local new_table = {} local new_table = {}
lookup_table[object] = new_table lookup_table[object] = new_table
for index, value in pairs(object) do for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value) new_table[_copy(index)] = _copy(value)
end end
return setmetatable(new_table, getmetatable(object)) return setmetatable(new_table, getmetatable(object))
end end
local objectreturn = _copy(object) local objectreturn = _copy(object)
return objectreturn return objectreturn
end 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 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 = {} lookup_table = {}
local function _Serialize( tbl ) local function _Serialize( tbl )
if type(tbl) == 'table' then --function only works for tables! if type(tbl) == 'table' then --function only works for tables!
if lookup_table[tbl] then if lookup_table[tbl] then
return lookup_table[object] return lookup_table[object]
end end
local tbl_str = {} local tbl_str = {}
lookup_table[tbl] = tbl_str lookup_table[tbl] = tbl_str
tbl_str[#tbl_str + 1] = '{' tbl_str[#tbl_str + 1] = '{'
for ind,val in pairs(tbl) do -- serialize its fields 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('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind))
env.info( debug.traceback() ) env.info( debug.traceback() )
end end
end end
tbl_str[#tbl_str + 1] = '}' tbl_str[#tbl_str + 1] = '}'
return table.concat(tbl_str) return table.concat(tbl_str)
@@ -307,7 +307,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s
return tostring(tbl) return tostring(tbl)
end end
end end
local objectreturn = _Serialize(tbl) local objectreturn = _Serialize(tbl)
return objectreturn return objectreturn
end end
@@ -339,18 +339,34 @@ UTILS.MetersToNM = function(meters)
return meters/1852 return meters/1852
end end
UTILS.KiloMetersToNM = function(kilometers)
return kilometers/1852*1000
end
UTILS.MetersToSM = function(meters) UTILS.MetersToSM = function(meters)
return meters/1609.34 return meters/1609.34
end end
UTILS.KiloMetersToSM = function(kilometers)
return kilometers/1609.34*1000
end
UTILS.MetersToFeet = function(meters) UTILS.MetersToFeet = function(meters)
return meters/0.3048 return meters/0.3048
end end
UTILS.KiloMetersToFeet = function(kilometers)
return kilometers/0.3048*1000
end
UTILS.NMToMeters = function(NM) UTILS.NMToMeters = function(NM)
return NM*1852 return NM*1852
end end
UTILS.NMToKiloMeters = function(NM)
return NM*1852/1000
end
UTILS.FeetToMeters = function(feet) UTILS.FeetToMeters = function(feet)
return feet*0.3048 return feet*0.3048
end end
@@ -400,7 +416,7 @@ end
-- @param #number Celcius Temperature in degrees Celsius. -- @param #number Celcius Temperature in degrees Celsius.
-- @return #number Temperature in degrees Farenheit. -- @return #number Temperature in degrees Farenheit.
UTILS.CelciusToFarenheit = function( Celcius ) UTILS.CelciusToFarenheit = function( Celcius )
return Celcius * 9/5 + 32 return Celcius * 9/5 + 32
end end
--- Convert pressure from hecto Pascal (hPa) to inches of mercury (inHg). --- Convert pressure from hecto Pascal (hPa) to inches of mercury (inHg).
@@ -415,7 +431,7 @@ end
-- @param #number altitude Altitude in feet -- @param #number altitude Altitude in feet
-- @return #number Corrected KIAS -- @return #number Corrected KIAS
UTILS.KnotsToAltKIAS = function( knots, altitude ) UTILS.KnotsToAltKIAS = function( knots, altitude )
return (knots * 0.018 * (altitude / 1000)) + knots return (knots * 0.018 * (altitude / 1000)) + knots
end end
--- Convert pressure from hecto Pascal (hPa) to millimeters of mercury (mmHg). --- 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' secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end 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..' ' return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' '
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi .. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
@@ -534,23 +550,23 @@ UTILS.tostringMGRS = function(MGRS, acc) --R2.1
-- Test if Easting/Northing have less than 4 digits. -- Test if Easting/Northing have less than 4 digits.
--MGRS.Easting=123 -- should be 00123 --MGRS.Easting=123 -- should be 00123
--MGRS.Northing=5432 -- should be 05432 --MGRS.Northing=5432 -- should be 05432
-- Truncate rather than round MGRS grid! -- Truncate rather than round MGRS grid!
local Easting=tostring(MGRS.Easting) local Easting=tostring(MGRS.Easting)
local Northing=tostring(MGRS.Northing) 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. -- 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) local nN=5-string.len(Northing)
-- Get leading zeros (if any). -- Get leading zeros (if any).
for i=1,nE do Easting="0"..Easting end for i=1,nE do Easting="0"..Easting end
for i=1,nN do Northing="0"..Northing end for i=1,nN do Northing="0"..Northing end
-- Return MGRS string. -- Return MGRS string.
return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc)) return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc))
end end
end end
@@ -578,7 +594,7 @@ function UTILS.spairs( t, order )
for k in pairs(t) do keys[#keys+1] = k end 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, -- 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 if order then
table.sort(keys, function(a,b) return order(t, a, b) end) table.sort(keys, function(a,b) return order(t, a, b) end)
else 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 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, -- 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 if order then
table.sort(keys, function(a,b) return order(t, a, b) end) table.sort(keys, function(a,b) return order(t, a, b) end)
else 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. -- 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 ) function UTILS.rpairs( t )
-- collect the keys -- collect the keys
local keys = {} local keys = {}
for k in pairs(t) do keys[#keys+1] = k end for k in pairs(t) do keys[#keys+1] = k end
@@ -635,7 +651,7 @@ function UTILS.rpairs( t )
random[i] = keys[k] random[i] = keys[k]
table.remove( keys, k ) table.remove( keys, k )
end end
-- return the iterator function -- return the iterator function
local i = 0 local i = 0
return function() return function()
@@ -751,12 +767,12 @@ end
function UTILS.GetCharacters(str) function UTILS.GetCharacters(str)
local chars={} local chars={}
for i=1,#str do for i=1,#str do
local c=str:sub(i,i) local c=str:sub(i,i)
table.insert(chars, c) table.insert(chars, c)
end end
return chars return chars
end end
@@ -765,15 +781,15 @@ end
-- @param #boolean short (Optional) If true, use short output, i.e. (HH:)MM:SS without day. -- @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). -- @return #string Time in format Hours:Minutes:Seconds+Days (HH:MM:SS+D).
function UTILS.SecondsToClock(seconds, short) function UTILS.SecondsToClock(seconds, short)
-- Nil check. -- Nil check.
if seconds==nil then if seconds==nil then
return nil return nil
end end
-- Seconds -- Seconds
local seconds = tonumber(seconds) local seconds = tonumber(seconds)
-- Seconds of this day. -- Seconds of this day.
local _seconds=seconds%(60*60*24) local _seconds=seconds%(60*60*24)
@@ -803,10 +819,10 @@ function UTILS.SecondsOfToday()
-- Time in seconds. -- Time in seconds.
local time=timer.getAbsTime() local time=timer.getAbsTime()
-- Short format without days since mission start. -- Short format without days since mission start.
local clock=UTILS.SecondsToClock(time, true) local clock=UTILS.SecondsToClock(time, true)
-- Time is now the seconds passed since last midnight. -- Time is now the seconds passed since last midnight.
return UTILS.ClockToSeconds(clock) return UTILS.ClockToSeconds(clock)
end 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. -- @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. -- @return #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
function UTILS.ClockToSeconds(clock) function UTILS.ClockToSeconds(clock)
-- Nil check. -- Nil check.
if clock==nil then if clock==nil then
return nil return nil
end end
-- Seconds init. -- Seconds init.
local seconds=0 local seconds=0
-- Split additional days. -- Split additional days.
local dsplit=UTILS.Split(clock, "+") local dsplit=UTILS.Split(clock, "+")
-- Convert days to seconds. -- Convert days to seconds.
if #dsplit>1 then if #dsplit>1 then
seconds=seconds+tonumber(dsplit[2])*60*60*24 seconds=seconds+tonumber(dsplit[2])*60*60*24
end end
-- Split hours, minutes, seconds -- Split hours, minutes, seconds
local tsplit=UTILS.Split(dsplit[1], ":") local tsplit=UTILS.Split(dsplit[1], ":")
-- Get time in seconds -- Get time in seconds
@@ -856,7 +872,7 @@ function UTILS.ClockToSeconds(clock)
end end
i=i+1 i=i+1
end end
return seconds return seconds
end end
@@ -868,12 +884,12 @@ function UTILS.DisplayMissionTime(duration)
local mission_time=Tnow-timer.getTime0() local mission_time=Tnow-timer.getTime0()
local mission_time_minutes=mission_time/60 local mission_time_minutes=mission_time/60
local mission_time_seconds=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) local text=string.format("Time: %s - %02d:%02d", local_time, mission_time_minutes, mission_time_seconds)
MESSAGE:New(text, duration):ToAll() MESSAGE:New(text, duration):ToAll()
end end
--- Replace illegal characters [<>|/?*:\\] in a string. --- Replace illegal characters [<>|/?*:\\] in a string.
-- @param #string Text Input text. -- @param #string Text Input text.
-- @param #string ReplaceBy Replace illegal characters by this character or string. Default underscore "_". -- @param #string ReplaceBy Replace illegal characters by this character or string. Default underscore "_".
-- @return #string The input text with illegal chars replaced. -- @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. -- Standard deviation. Default 10 if not given.
sigma=sigma or 10 sigma=sigma or 10
-- Max attempts. -- Max attempts.
imax=imax or 100 imax=imax or 100
local r local r
local gotit=false local gotit=false
local i=0 local i=0
while not gotit do while not gotit do
-- Uniform numbers in [0,1). We need two. -- Uniform numbers in [0,1). We need two.
local x1=math.random() local x1=math.random()
local x2=math.random() local x2=math.random()
-- Transform to Gaussian exp(-(x-x0)²/(2*sigma²). -- Transform to Gaussian exp(-(x-x0)°/(2*sigma°).
r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0 r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0
i=i+1 i=i+1
if (r>=xmin and r<=xmax) or i>imax then if (r>=xmin and r<=xmax) or i>imax then
gotit=true gotit=true
end end
end end
return r return r
end end
@@ -940,9 +956,9 @@ function UTILS.Randomize(value, fac, lower, upper)
else else
max=value+value*fac max=value+value*fac
end end
local r=math.random(min, max) local r=math.random(min, max)
return r return r
end end
@@ -961,6 +977,33 @@ function UTILS.VecNorm(a)
return math.sqrt(UTILS.VecDot(a, a)) return math.sqrt(UTILS.VecDot(a, a))
end 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. --- 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 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b 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} 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 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 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b 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. -- @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} return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
end 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 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b 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. -- @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} return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
end 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 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b 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) function UTILS.VecAngle(a, b)
local cosalpha=UTILS.VecDot(a,b)/(UTILS.VecNorm(a)*UTILS.VecNorm(b)) local cosalpha=UTILS.VecDot(a,b)/(UTILS.VecNorm(a)*UTILS.VecNorm(b))
local alpha=0 local alpha=0
if cosalpha>=0.9999999999 then --acos(1) is not defined. if cosalpha>=0.9999999999 then --acos(1) is not defined.
alpha=0 alpha=0
@@ -1000,8 +1043,8 @@ function UTILS.VecAngle(a, b)
alpha=math.pi alpha=math.pi
else else
alpha=math.acos(cosalpha) alpha=math.acos(cosalpha)
end end
return math.deg(alpha) return math.deg(alpha)
end end
@@ -1025,18 +1068,18 @@ function UTILS.HdgDiff(h1, h2)
-- Angle in rad. -- Angle in rad.
local alpha= math.rad(tonumber(h1)) local alpha= math.rad(tonumber(h1))
local beta = math.rad(tonumber(h2)) local beta = math.rad(tonumber(h2))
-- Runway vector. -- Runway vector.
local v1={x=math.cos(alpha), y=0, z=math.sin(alpha)} 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 v2={x=math.cos(beta), y=0, z=math.sin(beta)}
local delta=UTILS.VecAngle(v1, v2) local delta=UTILS.VecAngle(v1, v2)
return math.abs(delta) return math.abs(delta)
end 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 DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number distance The distance to translate. -- @param #number distance The distance to translate.
-- @param #number angle Rotation angle in degrees. -- @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} return {x=TX, y=a.y, z=TY}
end 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 DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number angle Rotation angle in degrees. -- @param #number angle Rotation angle in degrees.
-- @return DCS#Vec3 Vector rotated in the (x,z) plane. -- @return DCS#Vec3 Vector rotated in the (x,z) plane.
function UTILS.Rotate2D(a, angle) function UTILS.Rotate2D(a, angle)
local phi=math.rad(angle) local phi=math.rad(angle)
local x=a.z local x=a.z
local y=a.x local y=a.x
local Z=x*math.cos(phi)-y*math.sin(phi) local Z=x*math.cos(phi)-y*math.sin(phi)
local X=x*math.sin(phi)+y*math.cos(phi) local X=x*math.sin(phi)+y*math.cos(phi)
local Y=a.y local Y=a.y
local A={x=X, y=Y, z=Z} local A={x=X, y=Y, z=Z}
return A return A
@@ -1084,17 +1127,17 @@ function UTILS.TACANToFrequency(TACANChannel, TACANMode)
end end
if TACANMode ~= "X" and TACANMode ~= "Y" then if TACANMode ~= "X" and TACANMode ~= "Y" then
return nil -- error in arguments 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. -- 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 -- I have no idea what it does but it seems to work
local A = 1151 -- 'X', channel >= 64 local A = 1151 -- 'X', channel >= 64
local B = 64 -- channel >= 64 local B = 64 -- channel >= 64
if TACANChannel < 64 then if TACANChannel < 64 then
B = 1 B = 1
end end
if TACANMode == 'Y' then if TACANMode == 'Y' then
A = 1025 A = 1025
if TACANChannel < 64 then if TACANChannel < 64 then
@@ -1105,7 +1148,7 @@ function UTILS.TACANToFrequency(TACANChannel, TACANMode)
A = 962 A = 962
end end
end end
return (A + TACANChannel - B) * 1000000 return (A + TACANChannel - B) * 1000000
end end
@@ -1132,13 +1175,13 @@ end
-- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime(). -- @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. -- @return #number Day of the mission. Mission starts on day 0.
function UTILS.GetMissionDay(Time) function UTILS.GetMissionDay(Time)
Time=Time or timer.getAbsTime() Time=Time or timer.getAbsTime()
local clock=UTILS.SecondsToClock(Time, false) local clock=UTILS.SecondsToClock(Time, false)
local x=tonumber(UTILS.Split(clock, "+")[2]) local x=tonumber(UTILS.Split(clock, "+")[2])
return x return x
end end
@@ -1148,11 +1191,11 @@ end
function UTILS.GetMissionDayOfYear(Time) function UTILS.GetMissionDayOfYear(Time)
local Date, Year, Month, Day=UTILS.GetDCSMissionDate() local Date, Year, Month, Day=UTILS.GetDCSMissionDate()
local d=UTILS.GetMissionDay(Time) local d=UTILS.GetMissionDay(Time)
return UTILS.GetDayOfYear(Year, Month, Day)+d return UTILS.GetDayOfYear(Year, Month, Day)+d
end end
--- Returns the current date. --- Returns the current date.
@@ -1164,20 +1207,20 @@ function UTILS.GetDate()
-- Mission start date -- Mission start date
local date, year, month, day=UTILS.GetDCSMissionDate() local date, year, month, day=UTILS.GetDCSMissionDate()
local time=timer.getAbsTime() local time=timer.getAbsTime()
local clock=UTILS.SecondsToClock(time, false) local clock=UTILS.SecondsToClock(time, false)
local x=tonumber(UTILS.Split(clock, "+")[2]) local x=tonumber(UTILS.Split(clock, "+")[2])
local day=day+x local day=day+x
end end
--- Returns the magnetic declination of the map. --- Returns the magnetic declination of the map.
-- Returned values for the current maps are: -- Returned values for the current maps are:
-- --
-- * Caucasus +6 (East), year ~ 2011 -- * Caucasus +6 (East), year ~ 2011
-- * NTTR +12 (East), year ~ 2011 -- * NTTR +12 (East), year ~ 2011
-- * Normandy -10 (West), year ~ 1944 -- * Normandy -10 (West), year ~ 1944
@@ -1191,7 +1234,7 @@ function UTILS.GetMagneticDeclination(map)
-- Map. -- Map.
map=map or UTILS.GetDCSMap() map=map or UTILS.GetDCSMap()
local declination=0 local declination=0
if map==DCSMAP.Caucasus then if map==DCSMAP.Caucasus then
declination=6 declination=6
@@ -1228,12 +1271,12 @@ function UTILS.FileExists(file)
end end
else else
return nil return nil
end 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. --- 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. -- @param #boolean output If true, print to DCS log file.
-- @return #number Memory usage in kByte. -- @return #number Memory usage in kByte.
function UTILS.CheckMemory(output) function UTILS.CheckMemory(output)
local time=timer.getTime() local time=timer.getTime()
local clock=UTILS.SecondsToClock(time) local clock=UTILS.SecondsToClock(time)
@@ -1263,7 +1306,7 @@ function UTILS.GetCoalitionName(Coalition)
else else
return "Unknown" return "Unknown"
end end
end end
--- Get the modulation name from its numerical value. --- Get the modulation name from its numerical value.
@@ -1282,7 +1325,7 @@ function UTILS.GetModulationName(Modulation)
else else
return "Unknown" return "Unknown"
end end
end end
--- Get the callsign name from its enumerator value --- Get the callsign name from its enumerator value
@@ -1295,7 +1338,7 @@ function UTILS.GetCallsignName(Callsign)
return name return name
end end
end end
for name, value in pairs(CALLSIGN.AWACS) do for name, value in pairs(CALLSIGN.AWACS) do
if value==Callsign then if value==Callsign then
return name return name
@@ -1307,7 +1350,7 @@ function UTILS.GetCallsignName(Callsign)
return name return name
end end
end end
for name, value in pairs(CALLSIGN.Tanker) do for name, value in pairs(CALLSIGN.Tanker) do
if value==Callsign then if value==Callsign then
return name return name
@@ -1336,7 +1379,7 @@ function UTILS.GMTToLocalTimeDifference()
elseif theatre==DCSMAP.Syria then elseif theatre==DCSMAP.Syria then
return 3 -- Damascus is UTC+3 hours return 3 -- Damascus is UTC+3 hours
elseif theatre==DCSMAP.MarianaIslands then elseif theatre==DCSMAP.MarianaIslands then
return 10 -- Guam is UTC+10 hours. return 10 -- Guam is UTC+10 hours.
else else
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre))) BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
return 0 return 0
@@ -1353,11 +1396,11 @@ end
function UTILS.GetDayOfYear(Year, Month, Day) function UTILS.GetDayOfYear(Year, Month, Day)
local floor = math.floor local floor = math.floor
local n1 = floor(275 * Month / 9) local n1 = floor(275 * Month / 9)
local n2 = floor((Month + 9) / 12) local n2 = floor((Month + 9) / 12)
local n3 = (1 + floor((Year - 4 * floor(Year / 4) + 2) / 3)) local n3 = (1 + floor((Year - 4 * floor(Year / 4) + 2) / 3))
return n1 - (n2 * n3) + Day - 30 return n1 - (n2 * n3) + Day - 30
end end
@@ -1370,14 +1413,14 @@ end
-- @return #number Sun rise/set in seconds of the day. -- @return #number Sun rise/set in seconds of the day.
function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal) function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
-- Defaults -- Defaults
local zenith=90.83 local zenith=90.83
local latitude=Latitude local latitude=Latitude
local longitude=Longitude local longitude=Longitude
local rising=Rising local rising=Rising
local n=DayOfYear local n=DayOfYear
Tlocal=Tlocal or 0 Tlocal=Tlocal or 0
-- Short cuts. -- Short cuts.
local rad = math.rad local rad = math.rad
@@ -1404,47 +1447,47 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
return val return val
end end
end end
-- Convert the longitude to hour value and calculate an approximate time -- Convert the longitude to hour value and calculate an approximate time
local lng_hour = longitude / 15 local lng_hour = longitude / 15
local t local t
if rising then -- Rising time is desired if rising then -- Rising time is desired
t = n + ((6 - lng_hour) / 24) t = n + ((6 - lng_hour) / 24)
else -- Setting time is desired else -- Setting time is desired
t = n + ((18 - lng_hour) / 24) t = n + ((18 - lng_hour) / 24)
end end
-- Calculate the Sun's mean anomaly -- Calculate the Sun's mean anomaly
local M = (0.9856 * t) - 3.289 local M = (0.9856 * t) - 3.289
-- Calculate the Sun's true longitude -- 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) 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 -- Calculate the Sun's right ascension
local RA = fit_into_range(atan(0.91764 * tan(L)), 0, 360) local RA = fit_into_range(atan(0.91764 * tan(L)), 0, 360)
-- Right ascension value needs to be in the same quadrant as L -- Right ascension value needs to be in the same quadrant as L
local Lquadrant = floor(L / 90) * 90 local Lquadrant = floor(L / 90) * 90
local RAquadrant = floor(RA / 90) * 90 local RAquadrant = floor(RA / 90) * 90
RA = RA + Lquadrant - RAquadrant RA = RA + Lquadrant - RAquadrant
-- Right ascension value needs to be converted into hours -- Right ascension value needs to be converted into hours
RA = RA / 15 RA = RA / 15
-- Calculate the Sun's declination -- Calculate the Sun's declination
local sinDec = 0.39782 * sin(L) local sinDec = 0.39782 * sin(L)
local cosDec = cos(asin(sinDec)) local cosDec = cos(asin(sinDec))
-- Calculate the Sun's local hour angle -- Calculate the Sun's local hour angle
local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude)) local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
if rising and cosH > 1 then if rising and cosH > 1 then
return "N/R" -- The sun never rises on this location on the specified date return "N/R" -- The sun never rises on this location on the specified date
elseif cosH < -1 then elseif cosH < -1 then
return "N/S" -- The sun never sets on this location on the specified date return "N/S" -- The sun never sets on this location on the specified date
end end
-- Finish calculating H and convert into hours -- Finish calculating H and convert into hours
local H local H
if rising then if rising then
@@ -1453,13 +1496,13 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
H = acos(cosH) H = acos(cosH)
end end
H = H / 15 H = H / 15
-- Calculate local mean time of rising/setting -- Calculate local mean time of rising/setting
local T = H + RA - (0.06571 * t) - 6.622 local T = H + RA - (0.06571 * t) - 6.622
-- Adjust back to UTC -- Adjust back to UTC
local UT = fit_into_range(T - lng_hour +Tlocal, 0, 24) local UT = fit_into_range(T - lng_hour +Tlocal, 0, 24)
return floor(UT)*60*60+frac(UT)*60*60--+Tlocal*60*60 return floor(UT)*60*60+frac(UT)*60*60--+Tlocal*60*60
end end
@@ -1510,7 +1553,7 @@ end
--@return #table --@return #table
function UTILS.ShuffleTable(t) function UTILS.ShuffleTable(t)
if t == nil or type(t) ~= "table" then if t == nil or type(t) ~= "table" then
BASE:I("Error in ShuffleTable: Missing or wrong tyåe of Argument") BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
return return
end end
math.random() math.random()
@@ -1534,17 +1577,17 @@ function UTILS.IsLoadingDoorOpen( unit_name )
local unit = Unit.getByName(unit_name) local unit = Unit.getByName(unit_name)
if unit ~= nil then if unit ~= nil then
local type_name = unit:getTypeName() local type_name = unit:getTypeName()
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0 then
BASE:T(unit_name .. " Cargo doors are open or cargo door not present") BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
ret_val = true ret_val = true
end end
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
BASE:T(unit_name .. " a side door is open") BASE:T(unit_name .. " a side door is open")
ret_val = true ret_val = true
end end
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
BASE:T(unit_name .. " a side door is open ") BASE:T(unit_name .. " a side door is open ")
ret_val = true ret_val = true
@@ -1555,13 +1598,28 @@ function UTILS.IsLoadingDoorOpen( unit_name )
ret_val = true ret_val = true
end 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 if ret_val == false then
BASE:T(unit_name .. " all doors are closed") BASE:T(unit_name .. " all doors are closed")
end end
return ret_val return ret_val
end -- nil end -- nil
return nil return nil
end end
@@ -1588,10 +1646,10 @@ function UTILS.GenerateVHFrequencies()
local _skipFrequencies = { local _skipFrequencies = {
214,274,291.5,295,297.5, 214,274,291.5,295,297.5,
300.5,304,307,309.5,311,312,312.5,316, 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, 342,343,348,351,352,353,358,
363,365,368,372.5,374, 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, 414,420,430,432,435,440,450,455,462,470,485,
507,515,520,525,528,540,550,560,570,577,580, 507,515,520,525,528,540,550,560,570,577,580,
602,625,641,662,670,680,682,690, 602,625,641,662,670,680,682,690,
@@ -1600,13 +1658,13 @@ function UTILS.GenerateVHFrequencies()
905,907,920,935,942,950,995, 905,907,920,935,942,950,995,
1000,1025,1030,1050,1065,1116,1175,1182,1210 1000,1025,1030,1050,1065,1116,1175,1182,1210
} }
local FreeVHFFrequencies = {} local FreeVHFFrequencies = {}
-- first range -- first range
local _start = 200000 local _start = 200000
while _start < 400000 do while _start < 400000 do
-- skip existing NDB frequencies# -- skip existing NDB frequencies#
local _found = false local _found = false
for _, value in pairs(_skipFrequencies) do for _, value in pairs(_skipFrequencies) do
@@ -1620,7 +1678,7 @@ function UTILS.GenerateVHFrequencies()
end end
_start = _start + 10000 _start = _start + 10000
end end
-- second range -- second range
_start = 400000 _start = 400000
while _start < 850000 do while _start < 850000 do
@@ -1637,7 +1695,7 @@ function UTILS.GenerateVHFrequencies()
end end
_start = _start + 10000 _start = _start + 10000
end end
-- third range -- third range
_start = 850000 _start = 850000
while _start <= 999000 do -- adjusted for Gazelle while _start <= 999000 do -- adjusted for Gazelle
@@ -1677,7 +1735,7 @@ end
-- @return #table Laser Codes. -- @return #table Laser Codes.
function UTILS.GenerateLaserCodes() function UTILS.GenerateLaserCodes()
local jtacGeneratedLaserCodes = {} local jtacGeneratedLaserCodes = {}
-- helper function -- helper function
local function ContainsDigit(_number, _numberToFind) local function ContainsDigit(_number, _numberToFind)
local _thisNumber = _number local _thisNumber = _number
@@ -1691,14 +1749,14 @@ function UTILS.GenerateLaserCodes()
end end
return false return false
end end
-- generate list of laser codes -- generate list of laser codes
local _code = 1111 local _code = 1111
local _count = 1 local _count = 1
while _code < 1777 and _count < 30 do while _code < 1777 and _count < 30 do
while true do while true do
_code = _code + 1 _code = _code + 1
if not self:_ContainsDigit(_code, 8) if not ContainsDigit(_code, 8)
and not ContainsDigit(_code, 9) and not ContainsDigit(_code, 9)
and not ContainsDigit(_code, 0) then and not ContainsDigit(_code, 0) then
table.insert(jtacGeneratedLaserCodes, _code) table.insert(jtacGeneratedLaserCodes, _code)

View File

@@ -17,6 +17,7 @@
-- @field #table CategoryName Names of airbase categories. -- @field #table CategoryName Names of airbase categories.
-- @field #string AirbaseName Name of the airbase. -- @field #string AirbaseName Name of the airbase.
-- @field #number AirbaseID Airbase ID. -- @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 #number category Airbase category.
-- @field #table descriptors DCS descriptors. -- @field #table descriptors DCS descriptors.
-- @field #boolean isAirdrome Airbase is an airdrome. -- @field #boolean isAirdrome Airbase is an airdrome.
@@ -526,19 +527,19 @@ function AIRBASE:Register(AirbaseName)
-- Inherit everything from positionable. -- Inherit everything from positionable.
local self=BASE:Inherit(self, POSITIONABLE:New(AirbaseName)) --#AIRBASE local self=BASE:Inherit(self, POSITIONABLE:New(AirbaseName)) --#AIRBASE
-- Set airbase name. -- Set airbase name.
self.AirbaseName=AirbaseName self.AirbaseName=AirbaseName
-- Set airbase ID. -- Set airbase ID.
self.AirbaseID=self:GetID(true) self.AirbaseID=self:GetID(true)
-- Get descriptors. -- Get descriptors.
self.descriptors=self:GetDesc() self.descriptors=self:GetDesc()
-- Category. -- Category.
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
-- Set category. -- Set category.
if self.category==Airbase.Category.AIRDROME then if self.category==Airbase.Category.AIRDROME then
self.isAirdrome=true self.isAirdrome=true
@@ -546,20 +547,33 @@ function AIRBASE:Register(AirbaseName)
self.isHelipad=true self.isHelipad=true
elseif self.category==Airbase.Category.SHIP then elseif self.category==Airbase.Category.SHIP then
self.isShip=true 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 else
self:E("ERROR: Unknown airbase category!") self:E("ERROR: Unknown airbase category!")
end end
self:_InitParkingSpots() self:_InitParkingSpots()
local vec2=self:GetVec2() local vec2=self:GetVec2()
-- Init coordinate. -- Init coordinate.
self:GetCoordinate() self:GetCoordinate()
if vec2 then if vec2 then
-- TODO: For ships we need a moving zone. if self.isShip then
self.AirbaseZone=ZONE_RADIUS:New( AirbaseName, vec2, 2500 ) 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 else
self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName)) self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName))
end end
@@ -693,7 +707,7 @@ function AIRBASE:GetID(unique)
local airbaseID=tonumber(DCSAirbase:getID()) local airbaseID=tonumber(DCSAirbase:getID())
local airbaseCategory=self:GetAirbaseCategory() local airbaseCategory=self:GetAirbaseCategory()
if AirbaseName==self.AirbaseName then if AirbaseName==self.AirbaseName then
if airbaseCategory==Airbase.Category.SHIP or airbaseCategory==Airbase.Category.HELIPAD 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. -- 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. -- Init table.
self.parking={} self.parking={}
self.parkingByID={} self.parkingByID={}
self.NparkingTotal=0 self.NparkingTotal=0
self.NparkingTerminal={} self.NparkingTerminal={}
for _,terminalType in pairs(AIRBASE.TerminalType) do for _,terminalType in pairs(AIRBASE.TerminalType) do
self.NparkingTerminal[terminalType]=0 self.NparkingTerminal[terminalType]=0
end end
-- Put coordinates of parking spots into table. -- Put coordinates of parking spots into table.
for _,spot in pairs(parkingdata) do for _,spot in pairs(parkingdata) do
-- New parking spot. -- New parking spot.
local park={} --#AIRBASE.ParkingSpot local park={} --#AIRBASE.ParkingSpot
park.Vec3=spot.vTerminalPos park.Vec3=spot.vTerminalPos
@@ -952,15 +966,15 @@ function AIRBASE:_InitParkingSpots()
park.TerminalID0=spot.Term_Index_0 park.TerminalID0=spot.Term_Index_0
park.TerminalType=spot.Term_Type park.TerminalType=spot.Term_Type
park.TOAC=spot.TO_AC park.TOAC=spot.TO_AC
self.NparkingTotal=self.NparkingTotal+1 self.NparkingTotal=self.NparkingTotal+1
for _,terminalType in pairs(AIRBASE.TerminalType) do for _,terminalType in pairs(AIRBASE.TerminalType) do
if self._CheckTerminalType(terminalType, park.TerminalType) then if self._CheckTerminalType(terminalType, park.TerminalType) then
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1 self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
end end
end end
self.parkingByID[park.TerminalID]=park self.parkingByID[park.TerminalID]=park
table.insert(self.parking, park) table.insert(self.parking, park)
end end
@@ -984,7 +998,7 @@ function AIRBASE:GetParkingSpotsTable(termtype)
-- Get parking data of all spots (free or occupied) -- Get parking data of all spots (free or occupied)
local parkingdata=self:GetParkingData(false) local parkingdata=self:GetParkingData(false)
-- Get parking data of all free spots. -- Get parking data of all free spots.
local parkingfree=self:GetParkingData(true) local parkingfree=self:GetParkingData(true)
@@ -1001,17 +1015,26 @@ function AIRBASE:GetParkingSpotsTable(termtype)
-- Put coordinates of parking spots into table. -- Put coordinates of parking spots into table.
local spots={} local spots={}
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
local spot=self:_GetParkingSpotByID(_spot.Term_Index) local spot=self:_GetParkingSpotByID(_spot.Term_Index)
spot.Free=_isfree(_spot) -- updated if spot then
spot.TOAC=_spot.TO_AC -- updated
spot.Free=_isfree(_spot) -- updated
table.insert(spots, spot) 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
end end
return spots return spots
@@ -1032,14 +1055,14 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
for _,_spot in pairs(parkingfree) do for _,_spot in pairs(parkingfree) do
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) and _spot.Term_Index>0 then if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) and _spot.Term_Index>0 then
if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then
local spot=self:_GetParkingSpotByID(_spot.Term_Index) local spot=self:_GetParkingSpotByID(_spot.Term_Index)
spot.Free=true -- updated spot.Free=true -- updated
spot.TOAC=_spot.TO_AC -- updated spot.TOAC=_spot.TO_AC -- updated
table.insert(freespots, spot) table.insert(freespots, spot)
end end
end end
end end
@@ -1084,7 +1107,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
-- Get airbase name. -- Get airbase name.
local airbasename=self:GetName() local airbasename=self:GetName()
self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype))) self:E(string.format("Parking spots at %s for terminal type %s:", airbasename, tostring(termtype)))
for _,_spot in pairs(parkingdata) do for _,_spot in pairs(parkingdata) do
@@ -1118,7 +1141,7 @@ end
-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function. -- @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. -- @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) function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata)
-- Init default -- Init default
scanradius=scanradius or 50 scanradius=scanradius or 50
if scanunits==nil then if scanunits==nil then
@@ -1161,14 +1184,25 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype) parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
-- Get the aircraft size, i.e. it's longest side of x,z. -- Get the aircraft size, i.e. it's longest side of x,z.
local aircraft=group:GetUnit(1) local aircraft = nil -- fix local problem below
local _aircraftsize, ax,ay,az=aircraft:GetObjectSize() local _aircraftsize, ax,ay,az
if group and group.ClassName == "GROUP" then
aircraft=group:GetUnit(1)
_aircraftsize, ax,ay,az=aircraft:GetObjectSize()
else
-- SU27 dimensions
_aircraftsize = 23
ax = 23 -- length
ay = 7 -- height
az = 17 -- width
end
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size! -- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
local _nspots=nspots or group:GetSize() local _nspots=nspots or group:GetSize()
-- Debug info. -- Debug info.
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype))) self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
-- Table of valid spots. -- Table of valid spots.
local validspots={} local validspots={}
@@ -1291,6 +1325,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
-- Retrun spots we found, even if there were not enough. -- Retrun spots we found, even if there were not enough.
return validspots return validspots
end end
--- Check black and white lists. --- Check black and white lists.
@@ -1392,7 +1427,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- Get spawn points on runway. These can be used to determine the runway heading. -- Get spawn points on runway. These can be used to determine the runway heading.
local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway) local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
-- Debug: For finding the numbers of the spawn points belonging to each runway. -- Debug: For finding the numbers of the spawn points belonging to each runway.
if false then if false then
for i,_coord in pairs(runwaycoords) do for i,_coord in pairs(runwaycoords) do
@@ -1411,7 +1446,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- Airbase name. -- Airbase name.
local name=self:GetName() local name=self:GetName()
-- Exceptions -- Exceptions
if name==AIRBASE.Nevada.Jean_Airport or if name==AIRBASE.Nevada.Jean_Airport or
@@ -1424,36 +1459,36 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- 1-->4, 2-->3, 3-->2, 4-->1 -- 1-->4, 2-->3, 3-->2, 4-->1
exception=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.Minakh and
name~=AIRBASE.Syria.Damascus and name~=AIRBASE.Syria.Damascus and
name~=AIRBASE.Syria.Khalkhalah and name~=AIRBASE.Syria.Khalkhalah and
name~=AIRBASE.Syria.Marj_Ruhayyil and name~=AIRBASE.Syria.Marj_Ruhayyil and
name~=AIRBASE.Syria.Beirut_Rafic_Hariri then name~=AIRBASE.Syria.Beirut_Rafic_Hariri then
-- 1-->3, 2-->4, 3-->1, 4-->2 -- 1-->3, 2-->4, 3-->1, 4-->2
exception=2 exception=2
end end
--- Function returning the index of the runway coordinate belonding to the given index i. --- Function returning the index of the runway coordinate belonding to the given index i.
local function f(i) local function f(i)
local j local j
if exception==1 then if exception==1 then
j=N-(i-1) -- 1-->4, 2-->3 j=N-(i-1) -- 1-->4, 2-->3
elseif exception==2 then elseif exception==2 then
if i<=N2 then if i<=N2 then
j=i+N2 -- 1-->3, 2-->4 j=i+N2 -- 1-->3, 2-->4
else else
j=i-N2 -- 3-->1, 4-->3 j=i-N2 -- 3-->1, 4-->3
end end
else else
if i%2==0 then if i%2==0 then
@@ -1461,9 +1496,9 @@ function AIRBASE:GetRunwayData(magvar, mark)
else else
j=i+1 -- odd 1-->2, 3-->4 j=i+1 -- odd 1-->2, 3-->4
end end
end end
-- Special case where there is no obvious order. -- Special case where there is no obvious order.
if name==AIRBASE.Syria.Beirut_Rafic_Hariri then if name==AIRBASE.Syria.Beirut_Rafic_Hariri then
if i==1 then if i==1 then
@@ -1496,7 +1531,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
j=2 j=2
end end
end end
return j return j
end end
@@ -1505,7 +1540,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- Get the other spawn point coordinate. -- Get the other spawn point coordinate.
local j=f(i) local j=f(i)
-- Debug info. -- Debug info.
--env.info(string.format("Runway i=%s j=%s (N=%d #runwaycoord=%d)", tostring(i), tostring(j), N, #runwaycoords)) --env.info(string.format("Runway i=%s j=%s (N=%d #runwaycoord=%d)", tostring(i), tostring(j), N, #runwaycoords))

View File

@@ -3779,8 +3779,9 @@ end
-- @param #number radius Radius of the relocation zone, default 500 -- @param #number radius Radius of the relocation zone, default 500
-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true -- @param #boolean onroad If true, route on road (less problems with AI way finding), default true
-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false -- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
-- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road"
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut) function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut, formation)
self:F2( { self.ControllableName } ) self:F2( { self.ControllableName } )
local _coord = self:GetCoordinate() local _coord = self:GetCoordinate()
@@ -3791,14 +3792,14 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc
local _grptsk = {} local _grptsk = {}
local _candoroad = false local _candoroad = false
local _shortcut = shortcut or false local _shortcut = shortcut or false
local _formation = formation or "Off Road"
-- create a DCS Task an push it on the group -- create a DCS Task an push it on the group
-- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments)
if onroad then if onroad then
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut) _grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut)
self:Route(_grptsk,5) self:Route(_grptsk,5)
else else
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation)
end end
return self return self

View File

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

View File

@@ -684,6 +684,27 @@ function POSITIONABLE:IsShip()
end 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. --- Returns true if the POSITIONABLE is in the air.
-- Polymorphic, is overridden in GROUP and UNIT. -- Polymorphic, is overridden in GROUP and UNIT.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
@@ -1513,6 +1534,7 @@ do -- Cargo
["Ural-4320 APA-5D"] = 10, ["Ural-4320 APA-5D"] = 10,
["Ural-4320T"] = 14, ["Ural-4320T"] = 14,
["ZBD04A"] = 7, -- new by kappa ["ZBD04A"] = 7, -- new by kappa
["VAB_Mephisto"] = 8, -- new by Apple
} }
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95

View File

@@ -1426,3 +1426,13 @@ function UNIT:EnableEmission(switch)
return self return self
end end
--- Get skill from Unit.
-- @param #UNIT self
-- @return #string Skill String of skill name.
function UNIT:GetSkill()
self:F2( self.UnitName )
local name = self.UnitName
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
return skill
end

View File

@@ -74,6 +74,6 @@ MOOSE has a living (chat and video) community of users, beta testers and contrib
Kind regards, Kind regards,
FlightControl (FC) The Moose Team