mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cc3d73c04 | ||
|
|
e541e39403 | ||
|
|
c7ea45e5fd | ||
|
|
20f28b3d2c | ||
|
|
f3f63ab8aa | ||
|
|
e91090cfff | ||
|
|
1a7fb3c13e | ||
|
|
59857ed79d | ||
|
|
4797665939 | ||
|
|
b89749036d | ||
|
|
c6268488de | ||
|
|
de04369703 | ||
|
|
05b6f19a87 | ||
|
|
2753df8216 | ||
|
|
33d761503d | ||
|
|
b52272e18e | ||
|
|
70fa1cf19f | ||
|
|
7ca7caea75 | ||
|
|
277c26821e | ||
|
|
22826b4cd1 | ||
|
|
8cc1c24b64 | ||
|
|
0db35a0e9f | ||
|
|
b4707bb3eb | ||
|
|
bbfc2e9ed7 | ||
|
|
2fb0ab1aed | ||
|
|
6fbf050b72 | ||
|
|
29210f670c | ||
|
|
18b45c9621 | ||
|
|
9356d67d74 | ||
|
|
5bb2b05f71 | ||
|
|
89308f7d06 | ||
|
|
353d6dfec0 | ||
|
|
3f5e322948 | ||
|
|
0e8732fd44 | ||
|
|
77a3c7369d | ||
|
|
8d8070bbd7 | ||
|
|
72df687ce7 | ||
|
|
246935622c | ||
|
|
078573629d | ||
|
|
973b8323c9 | ||
|
|
6d61c5ee94 | ||
|
|
3e8db6a1fa | ||
|
|
832d6b1c08 | ||
|
|
e7936950f4 | ||
|
|
831e986ee8 | ||
|
|
8a44fae3d4 | ||
|
|
0c9390914a | ||
|
|
4fa525a4ae | ||
|
|
858b00336b | ||
|
|
82d78c98bb | ||
|
|
06c3f7998b | ||
|
|
e03e87f501 | ||
|
|
cf83abfe90 | ||
|
|
5a00f461e9 | ||
|
|
bb43b08190 | ||
|
|
05f95796f6 | ||
|
|
cfcd7d7588 | ||
|
|
4c3d44a63b | ||
|
|
fcd75a34eb | ||
|
|
4827b73bb1 | ||
|
|
2d7e7d55a9 | ||
|
|
3129c8c8ea | ||
|
|
6e37300d9b | ||
|
|
6b747e924b | ||
|
|
85fef96d00 | ||
|
|
e55bb21401 | ||
|
|
7a3fb95851 | ||
|
|
d39074bf3e | ||
|
|
47d814e409 | ||
|
|
41b01a508d | ||
|
|
0410ae6877 | ||
|
|
1ae41319fa | ||
|
|
0265152c12 | ||
|
|
a893d46cb9 | ||
|
|
b68f271fb7 | ||
|
|
057e231a9d | ||
|
|
b6fedbd97d | ||
|
|
7cd29501a9 | ||
|
|
59e4f48726 | ||
|
|
20fe2ee505 | ||
|
|
ebb4623bb5 | ||
|
|
d1f3e3f4bb | ||
|
|
45f578b5a3 | ||
|
|
2bce305451 | ||
|
|
2e66a854b1 |
@@ -47,19 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
|
|||||||
|
|
||||||
self:SetStartState( "Unloaded" )
|
self:SetStartState( "Unloaded" )
|
||||||
|
|
||||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
-- Board
|
||||||
|
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||||
|
self:AddTransition( "*", "Load", "*" )
|
||||||
|
self:AddTransition( "*", "Reload", "*" )
|
||||||
|
self:AddTransition( "*", "Board", "*" )
|
||||||
|
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||||
|
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||||
|
|
||||||
|
-- Unload
|
||||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||||
|
self:AddTransition( "*", "Unload", "*" )
|
||||||
|
self:AddTransition( "*", "Unboard", "*" )
|
||||||
|
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||||
|
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||||
|
|
||||||
self:AddTransition( "*", "Load", "Boarding" )
|
|
||||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
|
||||||
self:AddTransition( "Loaded", "Board", "Loaded" )
|
|
||||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
|
||||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
|
||||||
|
|
||||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
|
||||||
|
|
||||||
--- 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
|
||||||
|
|
||||||
|
|||||||
@@ -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 )
|
||||||
|
|||||||
@@ -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", "*" )
|
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
|
||||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
|
||||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
|
||||||
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
|
|
||||||
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
|
|
||||||
self:AddTransition( "*", "PickedUp", "*" )
|
|
||||||
self:AddTransition( "*", "Landed", "*" )
|
self:AddTransition( "*", "Landed", "*" )
|
||||||
|
self:AddTransition( "*", "Load", "*" )
|
||||||
|
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||||
|
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||||
|
|
||||||
|
-- Unboarding
|
||||||
|
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||||
self:AddTransition( "*", "Queue", "*" )
|
self:AddTransition( "*", "Queue", "*" )
|
||||||
self:AddTransition( "*", "Orbit" , "*" )
|
self:AddTransition( "*", "Orbit" , "*" )
|
||||||
self:AddTransition( "*", "Home" , "*" )
|
self:AddTransition( "*", "Destroyed", "*" )
|
||||||
|
self:AddTransition( "*", "Unload", "*" )
|
||||||
|
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||||
|
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||||
|
|
||||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
-- RTB
|
||||||
|
self:AddTransition( "*", "Home" , "*" )
|
||||||
|
|
||||||
--- 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
|
||||||
|
|||||||
442
Moose Development/Moose/Core/Beacon.lua
Normal file
442
Moose Development/Moose/Core/Beacon.lua
Normal file
@@ -0,0 +1,442 @@
|
|||||||
|
--- **Core** - TACAN and other beacons.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Features:
|
||||||
|
--
|
||||||
|
-- * Provide beacon functionality to assist pilots.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
||||||
|
--
|
||||||
|
-- @module Core.Beacon
|
||||||
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
|
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
|
||||||
|
--
|
||||||
|
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
||||||
|
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
||||||
|
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
||||||
|
-- attach to a cargo crate, for exemple.
|
||||||
|
--
|
||||||
|
-- ## AA TACAN Beacon usage
|
||||||
|
--
|
||||||
|
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
||||||
|
-- Use @#BEACON:StopAATACAN}() to stop it.
|
||||||
|
--
|
||||||
|
-- ## General Purpose Radio Beacon usage
|
||||||
|
--
|
||||||
|
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
||||||
|
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
||||||
|
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
||||||
|
--
|
||||||
|
-- @type BEACON
|
||||||
|
-- @field #string ClassName Name of the class "BEACON".
|
||||||
|
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
BEACON = {
|
||||||
|
ClassName = "BEACON",
|
||||||
|
Positionable = nil,
|
||||||
|
name = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Beacon types supported by DCS.
|
||||||
|
-- @type BEACON.Type
|
||||||
|
-- @field #number NULL
|
||||||
|
-- @field #number VOR
|
||||||
|
-- @field #number DME
|
||||||
|
-- @field #number VOR_DME
|
||||||
|
-- @field #number TACAN TACtical Air Navigation system.
|
||||||
|
-- @field #number VORTAC
|
||||||
|
-- @field #number RSBN
|
||||||
|
-- @field #number BROADCAST_STATION
|
||||||
|
-- @field #number HOMER
|
||||||
|
-- @field #number AIRPORT_HOMER
|
||||||
|
-- @field #number AIRPORT_HOMER_WITH_MARKER
|
||||||
|
-- @field #number ILS_FAR_HOMER
|
||||||
|
-- @field #number ILS_NEAR_HOMER
|
||||||
|
-- @field #number ILS_LOCALIZER
|
||||||
|
-- @field #number ILS_GLIDESLOPE
|
||||||
|
-- @field #number PRMG_LOCALIZER
|
||||||
|
-- @field #number PRMG_GLIDESLOPE
|
||||||
|
-- @field #number ICLS Same as ICLS glideslope.
|
||||||
|
-- @field #number ICLS_LOCALIZER
|
||||||
|
-- @field #number ICLS_GLIDESLOPE
|
||||||
|
-- @field #number NAUTICAL_HOMER
|
||||||
|
BEACON.Type={
|
||||||
|
NULL = 0,
|
||||||
|
VOR = 1,
|
||||||
|
DME = 2,
|
||||||
|
VOR_DME = 3,
|
||||||
|
TACAN = 4,
|
||||||
|
VORTAC = 5,
|
||||||
|
RSBN = 128,
|
||||||
|
BROADCAST_STATION = 1024,
|
||||||
|
HOMER = 8,
|
||||||
|
AIRPORT_HOMER = 4104,
|
||||||
|
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||||
|
ILS_FAR_HOMER = 16408,
|
||||||
|
ILS_NEAR_HOMER = 16424,
|
||||||
|
ILS_LOCALIZER = 16640,
|
||||||
|
ILS_GLIDESLOPE = 16896,
|
||||||
|
PRMG_LOCALIZER = 33024,
|
||||||
|
PRMG_GLIDESLOPE = 33280,
|
||||||
|
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
|
||||||
|
ICLS_LOCALIZER = 131328,
|
||||||
|
ICLS_GLIDESLOPE = 131584,
|
||||||
|
NAUTICAL_HOMER = 65536,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
|
||||||
|
-- @type BEACON.System
|
||||||
|
-- @field #number PAR_10 ?
|
||||||
|
-- @field #number RSBN_5 Russian VOR/DME system.
|
||||||
|
-- @field #number TACAN TACtical Air Navigation system on ground.
|
||||||
|
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
||||||
|
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
||||||
|
-- @field #number VOR Very High Frequency Omni-Directional Range
|
||||||
|
-- @field #number ILS_LOCALIZER ILS localizer
|
||||||
|
-- @field #number ILS_GLIDESLOPE ILS glideslope.
|
||||||
|
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
||||||
|
-- @field #number PRGM_GLIDESLOPE PRGM glideslope.
|
||||||
|
-- @field #number BROADCAST_STATION Broadcast station.
|
||||||
|
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||||
|
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
||||||
|
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
||||||
|
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
||||||
|
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
||||||
|
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
||||||
|
BEACON.System={
|
||||||
|
PAR_10 = 1,
|
||||||
|
RSBN_5 = 2,
|
||||||
|
TACAN = 3,
|
||||||
|
TACAN_TANKER_X = 4,
|
||||||
|
TACAN_TANKER_Y = 5,
|
||||||
|
VOR = 6,
|
||||||
|
ILS_LOCALIZER = 7,
|
||||||
|
ILS_GLIDESLOPE = 8,
|
||||||
|
PRMG_LOCALIZER = 9,
|
||||||
|
PRMG_GLIDESLOPE = 10,
|
||||||
|
BROADCAST_STATION = 11,
|
||||||
|
VORTAC = 12,
|
||||||
|
TACAN_AA_MODE_X = 13,
|
||||||
|
TACAN_AA_MODE_Y = 14,
|
||||||
|
VORDME = 15,
|
||||||
|
ICLS_LOCALIZER = 16,
|
||||||
|
ICLS_GLIDESLOPE = 17,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.
|
||||||
|
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||||
|
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
|
||||||
|
function BEACON:New(Positionable)
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#BEACON
|
||||||
|
|
||||||
|
-- Debug.
|
||||||
|
self:F(Positionable)
|
||||||
|
|
||||||
|
-- Set positionable.
|
||||||
|
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||||
|
self.Positionable = Positionable
|
||||||
|
self.name=Positionable:GetName()
|
||||||
|
self:I(string.format("New BEACON %s", tostring(self.name)))
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Activates a TACAN BEACON.
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
|
||||||
|
-- @param #string Mode TACAN mode, i.e. the "Y" part in "10Y".
|
||||||
|
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||||
|
-- @param #boolean Bearing If true, beacon provides bearing information. If false (or nil), only distance information is available.
|
||||||
|
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
-- @usage
|
||||||
|
-- -- Let's create a TACAN Beacon for a tanker
|
||||||
|
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||||
|
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||||
|
--
|
||||||
|
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
||||||
|
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
||||||
|
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
||||||
|
|
||||||
|
-- Get frequency.
|
||||||
|
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
||||||
|
|
||||||
|
-- Check.
|
||||||
|
if not Frequency then
|
||||||
|
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Beacon type.
|
||||||
|
local Type=BEACON.Type.TACAN
|
||||||
|
|
||||||
|
-- Beacon system.
|
||||||
|
local System=BEACON.System.TACAN
|
||||||
|
|
||||||
|
-- Check if unit is an aircraft and set system accordingly.
|
||||||
|
local AA=self.Positionable:IsAir()
|
||||||
|
if AA then
|
||||||
|
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
||||||
|
-- Check if "Y" mode is selected for aircraft.
|
||||||
|
if Mode~="Y" then
|
||||||
|
self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.", self.Positionable})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Attached unit.
|
||||||
|
local UnitID=self.Positionable:GetID()
|
||||||
|
|
||||||
|
-- Debug.
|
||||||
|
self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
|
||||||
|
|
||||||
|
-- Start beacon.
|
||||||
|
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
||||||
|
|
||||||
|
-- Stop sheduler.
|
||||||
|
if Duration then
|
||||||
|
self.Positionable:DeactivateBeacon(Duration)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Activates an ICLS BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #number Channel ICLS channel.
|
||||||
|
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||||
|
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
||||||
|
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
|
||||||
|
|
||||||
|
-- Attached unit.
|
||||||
|
local UnitID=self.Positionable:GetID()
|
||||||
|
|
||||||
|
-- Debug
|
||||||
|
self:T2({"ICLS BEACON started!"})
|
||||||
|
|
||||||
|
-- Start beacon.
|
||||||
|
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
||||||
|
|
||||||
|
-- Stop sheduler
|
||||||
|
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
|
self.Positionable:DeactivateBeacon(Duration)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Activates a TACAN BEACON on an Aircraft.
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
||||||
|
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
||||||
|
-- @param #boolean Bearing Can the BEACON be homed on ?
|
||||||
|
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
-- @usage
|
||||||
|
-- -- Let's create a TACAN Beacon for a tanker
|
||||||
|
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||||
|
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||||
|
--
|
||||||
|
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
|
||||||
|
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
||||||
|
self:F({TACANChannel, Message, Bearing, BeaconDuration})
|
||||||
|
|
||||||
|
local IsValid = true
|
||||||
|
|
||||||
|
if not self.Positionable:IsAir() then
|
||||||
|
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
|
||||||
|
if not Frequency then
|
||||||
|
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
||||||
|
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
||||||
|
local System
|
||||||
|
if Bearing then
|
||||||
|
System = 5
|
||||||
|
else
|
||||||
|
System = 14
|
||||||
|
end
|
||||||
|
|
||||||
|
if IsValid then -- Starts the BEACON
|
||||||
|
self:T2({"AA TACAN BEACON started !"})
|
||||||
|
self.Positionable:SetCommand({
|
||||||
|
id = "ActivateBeacon",
|
||||||
|
params = {
|
||||||
|
type = 4,
|
||||||
|
system = System,
|
||||||
|
callsign = Message,
|
||||||
|
frequency = Frequency,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
|
SCHEDULER:New(nil,
|
||||||
|
function()
|
||||||
|
self:StopAATACAN()
|
||||||
|
end, {}, BeaconDuration)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the AA TACAN BEACON
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @return #BEACON self
|
||||||
|
function BEACON:StopAATACAN()
|
||||||
|
self:F()
|
||||||
|
if not self.Positionable then
|
||||||
|
self:E({"Start the beacon first before stoping it !"})
|
||||||
|
else
|
||||||
|
self.Positionable:SetCommand({
|
||||||
|
id = 'DeactivateBeacon',
|
||||||
|
params = {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Activates a general pupose Radio Beacon
|
||||||
|
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
||||||
|
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
||||||
|
-- They can home in on these specific frequencies :
|
||||||
|
-- * **Mi8**
|
||||||
|
-- * R-828 -> 20-60MHz
|
||||||
|
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
||||||
|
-- * ARK9 -> 150-1300KHz
|
||||||
|
-- * **Huey**
|
||||||
|
-- * AN/ARC-131 -> 30-76 Mhz FM
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #string FileName The name of the audio file
|
||||||
|
-- @param #number Frequency in MHz
|
||||||
|
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
||||||
|
-- @param #number Power in W
|
||||||
|
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
-- @usage
|
||||||
|
-- -- Let's create a beacon for a unit in distress.
|
||||||
|
-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131)
|
||||||
|
-- -- The beacon they use is battery-powered, and only lasts for 5 min
|
||||||
|
-- local UnitInDistress = UNIT:FindByName("Unit1")
|
||||||
|
-- local UnitBeacon = UnitInDistress:GetBeacon()
|
||||||
|
--
|
||||||
|
-- -- Set the beacon and start it
|
||||||
|
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
|
||||||
|
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
||||||
|
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
||||||
|
local IsValid = false
|
||||||
|
|
||||||
|
-- Check the filename
|
||||||
|
if type(FileName) == "string" then
|
||||||
|
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||||
|
if not FileName:find("l10n/DEFAULT/") then
|
||||||
|
FileName = "l10n/DEFAULT/" .. FileName
|
||||||
|
end
|
||||||
|
IsValid = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not IsValid then
|
||||||
|
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check the Frequency
|
||||||
|
if type(Frequency) ~= "number" and IsValid then
|
||||||
|
self:E({"Frequency invalid. ", Frequency})
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
Frequency = Frequency * 1000000 -- Conversion to Hz
|
||||||
|
|
||||||
|
-- Check the modulation
|
||||||
|
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
||||||
|
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check the Power
|
||||||
|
if type(Power) ~= "number" and IsValid then
|
||||||
|
self:E({"Power is invalid. ", Power})
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
||||||
|
|
||||||
|
if IsValid then
|
||||||
|
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
||||||
|
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
||||||
|
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
|
||||||
|
|
||||||
|
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
|
SCHEDULER:New( nil,
|
||||||
|
function()
|
||||||
|
self:StopRadioBeacon()
|
||||||
|
end, {}, BeaconDuration)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the AA TACAN BEACON
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @return #BEACON self
|
||||||
|
function BEACON:StopRadioBeacon()
|
||||||
|
self:F()
|
||||||
|
-- The unique name of the transmission is the class ID
|
||||||
|
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #number TACANChannel
|
||||||
|
-- @param #string TACANMode
|
||||||
|
-- @return #number Frequecy
|
||||||
|
-- @return #nil if parameters are invalid
|
||||||
|
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||||
|
self:F3({TACANChannel, TACANMode})
|
||||||
|
|
||||||
|
if type(TACANChannel) ~= "number" then
|
||||||
|
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||||
|
return nil -- error in arguments
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||||
|
-- I have no idea what it does but it seems to work
|
||||||
|
local A = 1151 -- 'X', channel >= 64
|
||||||
|
local B = 64 -- channel >= 64
|
||||||
|
|
||||||
|
if TACANChannel < 64 then
|
||||||
|
B = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if TACANMode == 'Y' then
|
||||||
|
A = 1025
|
||||||
|
if TACANChannel < 64 then
|
||||||
|
A = 1088
|
||||||
|
end
|
||||||
|
else -- 'X'
|
||||||
|
if TACANChannel < 64 then
|
||||||
|
A = 962
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return (A + TACANChannel - B) * 1000000
|
||||||
|
end
|
||||||
@@ -301,21 +301,75 @@ do -- Zones
|
|||||||
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
|
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
|
||||||
local ZoneName = ZoneData.name
|
local ZoneName = ZoneData.name
|
||||||
|
|
||||||
self:I( { "Register ZONE:", Name = ZoneName } )
|
-- Color
|
||||||
local Zone = ZONE:New( ZoneName )
|
local color=ZoneData.color or {1, 0, 0, 0.15}
|
||||||
self.ZONENAMES[ZoneName] = ZoneName
|
|
||||||
self:AddZone( ZoneName, Zone )
|
-- Create new Zone
|
||||||
|
local Zone=nil --Core.Zone#ZONE_BASE
|
||||||
|
|
||||||
|
if ZoneData.type==0 then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Circular zone
|
||||||
|
---
|
||||||
|
|
||||||
|
self:I(string.format("Register ZONE: %s (Circular)", ZoneName))
|
||||||
|
|
||||||
|
Zone=ZONE:New(ZoneName)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Quad-point zone
|
||||||
|
---
|
||||||
|
|
||||||
|
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
|
||||||
|
|
||||||
|
Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies)
|
||||||
|
|
||||||
|
--for i,vec2 in pairs(ZoneData.verticies) do
|
||||||
|
-- local coord=COORDINATE:NewFromVec2(vec2)
|
||||||
|
-- coord:MarkToAll(string.format("%s Point %d", ZoneName, i))
|
||||||
|
--end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if Zone then
|
||||||
|
|
||||||
|
-- Store color of zone.
|
||||||
|
Zone.Color=color
|
||||||
|
|
||||||
|
-- Store in DB.
|
||||||
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
|
-- Add zone.
|
||||||
|
self:AddZone(ZoneName, Zone)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Polygon zones defined by late activated groups.
|
||||||
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
||||||
if ZoneGroupName:match("#ZONE_POLYGON") then
|
if ZoneGroupName:match("#ZONE_POLYGON") then
|
||||||
|
|
||||||
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
||||||
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
||||||
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
||||||
|
|
||||||
self:I( { "Register ZONE_POLYGON:", Name = ZoneName } )
|
-- Debug output
|
||||||
|
self:I(string.format("Register ZONE: %s (Polygon)", ZoneName))
|
||||||
|
|
||||||
|
-- Create a new polygon zone.
|
||||||
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
Zone_Polygon:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
|
-- Store name in DB.
|
||||||
self.ZONENAMES[ZoneName] = ZoneName
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
|
-- Add zone to DB.
|
||||||
self:AddZone( ZoneName, Zone_Polygon )
|
self:AddZone( ZoneName, Zone_Polygon )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1000,11 +1000,7 @@ function EVENT:onEvent( Event )
|
|||||||
-- Check if this is a known event?
|
-- Check if this is a known event?
|
||||||
if EventMeta then
|
if EventMeta then
|
||||||
|
|
||||||
if self and
|
if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
|
||||||
self.Events and
|
|
||||||
self.Events[Event.id] and
|
|
||||||
self.MissionEnd == false and
|
|
||||||
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
|
|
||||||
|
|
||||||
if Event.id and Event.id == EVENTS.MissionEnd then
|
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||||
self.MissionEnd = true
|
self.MissionEnd = true
|
||||||
@@ -1039,9 +1035,10 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||||
|
|
||||||
if Event.id==31 then
|
if Event.id==31 then
|
||||||
--env.info("FF event 31")
|
|
||||||
-- Event.initiator is a Static object representing the pilot. But getName() error due to DCS bug.
|
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
local ID=Event.initiator.id_
|
local ID=Event.initiator.id_
|
||||||
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
||||||
@@ -1123,7 +1120,6 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||||
BASE:T({StaticTgtEvent = Event.id})
|
|
||||||
-- get base data
|
-- get base data
|
||||||
Event.TgtDCSUnit = Event.target
|
Event.TgtDCSUnit = Event.target
|
||||||
if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object
|
if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ do -- FSM
|
|||||||
|
|
||||||
--- Returns a table with the scores defined.
|
--- Returns a table with the scores defined.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #table Scores.
|
-- @return #table Scores.
|
||||||
function FSM:GetScores()
|
function FSM:GetScores()
|
||||||
return self._Scores or {}
|
return self._Scores or {}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ do -- COORDINATE
|
|||||||
--
|
--
|
||||||
-- * @{#COORDINATE.WaypointAir}(): Build an air route point.
|
-- * @{#COORDINATE.WaypointAir}(): Build an air route point.
|
||||||
-- * @{#COORDINATE.WaypointGround}(): Build a ground route point.
|
-- * @{#COORDINATE.WaypointGround}(): Build a ground route point.
|
||||||
|
-- * @{#COORDINATE.WaypointNaval}(): Build a naval route point.
|
||||||
--
|
--
|
||||||
-- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class.
|
-- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class.
|
||||||
--
|
--
|
||||||
@@ -183,10 +184,18 @@ do -- COORDINATE
|
|||||||
--
|
--
|
||||||
-- ## 9) Coordinate text generation
|
-- ## 9) Coordinate text generation
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance.
|
-- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance.
|
||||||
-- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text.
|
-- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text.
|
||||||
--
|
--
|
||||||
|
-- ## 10) Drawings on F10 map
|
||||||
|
--
|
||||||
|
-- * @{#COORDINATE.CircleToAll}(): Draw a circle on the F10 map.
|
||||||
|
-- * @{#COORDINATE.LineToAll}(): Draw a line on the F10 map.
|
||||||
|
-- * @{#COORDINATE.RectToAll}(): Draw a rectangle on the F10 map.
|
||||||
|
-- * @{#COORDINATE.QuadToAll}(): Draw a shape with four points on the F10 map.
|
||||||
|
-- * @{#COORDINATE.TextToAll}(): Write some text on the F10 map.
|
||||||
|
-- * @{#COORDINATE.ArrowToAll}(): Draw an arrow on the F10 map.
|
||||||
|
--
|
||||||
-- @field #COORDINATE
|
-- @field #COORDINATE
|
||||||
COORDINATE = {
|
COORDINATE = {
|
||||||
ClassName = "COORDINATE",
|
ClassName = "COORDINATE",
|
||||||
@@ -625,9 +634,9 @@ do -- COORDINATE
|
|||||||
|
|
||||||
--- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
|
--- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#Distance OuterRadius
|
-- @param DCS#Distance OuterRadius Outer radius in meters.
|
||||||
-- @param DCS#Distance InnerRadius
|
-- @param DCS#Distance InnerRadius Inner radius in meters.
|
||||||
-- @return #COORDINATE
|
-- @return #COORDINATE self
|
||||||
function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius )
|
function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius )
|
||||||
self:F2( { OuterRadius, InnerRadius } )
|
self:F2( { OuterRadius, InnerRadius } )
|
||||||
|
|
||||||
@@ -1984,13 +1993,13 @@ do -- COORDINATE
|
|||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param #COORDINATE Endpoint COORDIANTE to where the line is drawn.
|
-- @param #COORDINATE Endpoint COORDIANTE to where the line is drawn.
|
||||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
|
||||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||||
-- @return #number The resulting Mark ID which is a number.
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
function COORDINATE:LineToAll(Endpoint, Coalition, LineType, Color, Alpha, ReadOnly, Text)
|
function COORDINATE:LineToAll(Endpoint, Coalition, Color, Alpha, LineType, ReadOnly, Text)
|
||||||
local MarkID = UTILS.GetMarkID()
|
local MarkID = UTILS.GetMarkID()
|
||||||
if ReadOnly==nil then
|
if ReadOnly==nil then
|
||||||
ReadOnly=false
|
ReadOnly=false
|
||||||
@@ -2007,18 +2016,17 @@ do -- COORDINATE
|
|||||||
--- Circle to all.
|
--- Circle to all.
|
||||||
-- Creates a circle on the map with a given radius, color, fill color, and outline.
|
-- Creates a circle on the map with a given radius, color, fill color, and outline.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param #COORDINATE Center COORDIANTE of the center of the circle.
|
|
||||||
-- @param #numberr Radius Radius in meters. Default 1000 m.
|
-- @param #numberr Radius Radius in meters. Default 1000 m.
|
||||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
|
||||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.5.
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||||
-- @return #number The resulting Mark ID which is a number.
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
function COORDINATE:CircleToAll(Radius, Coalition, LineType, Color, Alpha, FillColor, FillAlpha, ReadOnly, Text)
|
function COORDINATE:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||||
local MarkID = UTILS.GetMarkID()
|
local MarkID = UTILS.GetMarkID()
|
||||||
if ReadOnly==nil then
|
if ReadOnly==nil then
|
||||||
ReadOnly=false
|
ReadOnly=false
|
||||||
@@ -2029,14 +2037,193 @@ do -- COORDINATE
|
|||||||
Color=Color or {1,0,0}
|
Color=Color or {1,0,0}
|
||||||
Color[4]=Alpha or 1.0
|
Color[4]=Alpha or 1.0
|
||||||
LineType=LineType or 1
|
LineType=LineType or 1
|
||||||
FillColor=FillColor or {1,0,0}
|
FillColor=FillColor or Color
|
||||||
FillColor[4]=FillAlpha or 0.5
|
FillColor[4]=FillAlpha or 0.15
|
||||||
trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "")
|
trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
return MarkID
|
return MarkID
|
||||||
end
|
end
|
||||||
|
|
||||||
end -- Markings
|
end -- Markings
|
||||||
|
|
||||||
|
--- Rectangle to all. Creates a rectangle on the map from the COORDINATE in one corner to the end COORDINATE in the opposite corner.
|
||||||
|
-- Creates a line on the F10 map from one point to another.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #COORDINATE Endpoint COORDIANTE in the opposite corner.
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||||
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
|
function COORDINATE:RectToAll(Endpoint, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||||
|
local MarkID = UTILS.GetMarkID()
|
||||||
|
if ReadOnly==nil then
|
||||||
|
ReadOnly=false
|
||||||
|
end
|
||||||
|
local vec3=Endpoint:GetVec3()
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
Color=Color or {1,0,0}
|
||||||
|
Color[4]=Alpha or 1.0
|
||||||
|
LineType=LineType or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillColor[4]=FillAlpha or 0.15
|
||||||
|
trigger.action.rectToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
return MarkID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a shape defined by 4 points on the F10 map. The first point is the current COORDINATE. The remaining three points need to be specified.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #COORDINATE Coord2 Second COORDIANTE of the quad shape.
|
||||||
|
-- @param #COORDINATE Coord3 Third COORDIANTE of the quad shape.
|
||||||
|
-- @param #COORDINATE Coord4 Fourth COORDIANTE of the quad shape.
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||||
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
|
function COORDINATE:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||||
|
local MarkID = UTILS.GetMarkID()
|
||||||
|
if ReadOnly==nil then
|
||||||
|
ReadOnly=false
|
||||||
|
end
|
||||||
|
local point1=self:GetVec3()
|
||||||
|
local point2=Coord2:GetVec3()
|
||||||
|
local point3=Coord3:GetVec3()
|
||||||
|
local point4=Coord4:GetVec3()
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
Color=Color or {1,0,0}
|
||||||
|
Color[4]=Alpha or 1.0
|
||||||
|
LineType=LineType or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillColor[4]=FillAlpha or 0.15
|
||||||
|
trigger.action.quadToAll(Coalition, MarkID, self:GetVec3(), point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
return MarkID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified.
|
||||||
|
-- **NOTE**: A free form polygon must have **at least three points** in total and currently only **up to 10 points** in total are supported.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #table Coordinates Table of coordinates of the remaining points of the shape.
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||||
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
|
function COORDINATE:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||||
|
|
||||||
|
local MarkID = UTILS.GetMarkID()
|
||||||
|
if ReadOnly==nil then
|
||||||
|
ReadOnly=false
|
||||||
|
end
|
||||||
|
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
|
||||||
|
Color=Color or {1,0,0}
|
||||||
|
Color[4]=Alpha or 1.0
|
||||||
|
|
||||||
|
LineType=LineType or 1
|
||||||
|
|
||||||
|
FillColor=FillColor or UTILS.DeepCopy(Color)
|
||||||
|
FillColor[4]=FillAlpha or 0.15
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
vecs[1]=self:GetVec3()
|
||||||
|
for i,coord in ipairs(Coordinates) do
|
||||||
|
vecs[i+1]=coord:GetVec3()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #vecs<3 then
|
||||||
|
self:E("ERROR: A free form polygon needs at least three points!")
|
||||||
|
elseif #vecs==3 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
elseif #vecs==4 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
elseif #vecs==5 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
elseif #vecs==6 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], Color, FillColor, LineType, Text or "")
|
||||||
|
elseif #vecs==7 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
elseif #vecs==8 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
elseif #vecs==9 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], vecs[9], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
elseif #vecs==10 then
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], vecs[9], vecs[10], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
else
|
||||||
|
self:E("ERROR: Currently a free form polygon can only have 10 points in total!")
|
||||||
|
-- Unfortunately, unpack(vecs) does not work! So no idea how to generalize this :(
|
||||||
|
trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
end
|
||||||
|
|
||||||
|
return MarkID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Text to all. Creates a text imposed on the map at the COORDINATE. Text scales with the map.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #string Text Text displayed on the F10 map.
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.3.
|
||||||
|
-- @param #number FontSize Font size. Default 14.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
|
function COORDINATE:TextToAll(Text, Coalition, Color, Alpha, FillColor, FillAlpha, FontSize, ReadOnly)
|
||||||
|
local MarkID = UTILS.GetMarkID()
|
||||||
|
if ReadOnly==nil then
|
||||||
|
ReadOnly=false
|
||||||
|
end
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
Color=Color or {1,0,0}
|
||||||
|
Color[4]=Alpha or 1.0
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillColor[4]=FillAlpha or 0.3
|
||||||
|
FontSize=FontSize or 14
|
||||||
|
trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World")
|
||||||
|
return MarkID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Arrow to all. Creates an arrow from the COORDINATE to the endpoint COORDINATE on the F10 map. There is no control over other dimensions of the arrow.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #COORDINATE Endpoint COORDINATE where the tip of the arrow is pointing at.
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||||
|
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||||
|
function COORDINATE:ArrowToAll(Endpoint, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||||
|
local MarkID = UTILS.GetMarkID()
|
||||||
|
if ReadOnly==nil then
|
||||||
|
ReadOnly=false
|
||||||
|
end
|
||||||
|
local vec3=Endpoint:GetVec3()
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
Color=Color or {1,0,0}
|
||||||
|
Color[4]=Alpha or 1.0
|
||||||
|
LineType=LineType or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillColor[4]=FillAlpha or 0.15
|
||||||
|
--trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World")
|
||||||
|
trigger.action.arrowToAll(Coalition, MarkID, vec3, self:GetVec3(), Color, FillColor, LineType, ReadOnly, Text or "")
|
||||||
|
return MarkID
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
|
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ do -- SETTINGS
|
|||||||
|
|
||||||
--- SETTINGS constructor.
|
--- SETTINGS constructor.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
|
-- @param #string PlayerName (Optional) Set settings for this player.
|
||||||
-- @return #SETTINGS
|
-- @return #SETTINGS
|
||||||
function SETTINGS:Set( PlayerName )
|
function SETTINGS:Set( PlayerName )
|
||||||
|
|
||||||
|
|||||||
@@ -3408,8 +3408,8 @@ function SPAWN:_SpawnCleanUpScheduler()
|
|||||||
if Stamp.Vec2 then
|
if Stamp.Vec2 then
|
||||||
if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then
|
if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then
|
||||||
local NewVec2 = SpawnUnit:GetVec2()
|
local NewVec2 = SpawnUnit:GetVec2()
|
||||||
if Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y then
|
if (Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y) or (SpawnUnit:GetLife() <= 1) then
|
||||||
-- If the plane is not moving, and is on the ground, assign it with a timestamp...
|
-- If the plane is not moving or dead , and is on the ground, assign it with a timestamp...
|
||||||
if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then
|
if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then
|
||||||
self:T( { "CleanUp Scheduler:", "ReSpawning:", SpawnGroup:GetName() } )
|
self:T( { "CleanUp Scheduler:", "ReSpawning:", SpawnGroup:GetName() } )
|
||||||
self:ReSpawn( SpawnCursor )
|
self:ReSpawn( SpawnCursor )
|
||||||
@@ -3427,7 +3427,7 @@ function SPAWN:_SpawnCleanUpScheduler()
|
|||||||
else
|
else
|
||||||
if SpawnUnit:InAir() == false then
|
if SpawnUnit:InAir() == false then
|
||||||
Stamp.Vec2 = SpawnUnit:GetVec2()
|
Stamp.Vec2 = SpawnUnit:GetVec2()
|
||||||
if SpawnUnit:GetVelocityKMH() < 1 then
|
if (SpawnUnit:GetVelocityKMH() < 1) then
|
||||||
Stamp.Time = timer.getTime()
|
Stamp.Time = timer.getTime()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
-- * Get zone properties.
|
-- * Get zone properties.
|
||||||
-- * Get zone bounding box.
|
-- * Get zone bounding box.
|
||||||
-- * Set/get zone name.
|
-- * Set/get zone name.
|
||||||
|
-- * Draw zones (circular and polygon) on the F10 map.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- There are essentially two core functions that zones accomodate:
|
-- There are essentially two core functions that zones accomodate:
|
||||||
@@ -56,6 +57,8 @@
|
|||||||
--- @type ZONE_BASE
|
--- @type ZONE_BASE
|
||||||
-- @field #string ZoneName Name of the zone.
|
-- @field #string ZoneName Name of the zone.
|
||||||
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
||||||
|
-- @field #number DrawID Unique ID of the drawn zone on the F10 map.
|
||||||
|
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -104,6 +107,8 @@ ZONE_BASE = {
|
|||||||
ClassName = "ZONE_BASE",
|
ClassName = "ZONE_BASE",
|
||||||
ZoneName = "",
|
ZoneName = "",
|
||||||
ZoneProbability = 1,
|
ZoneProbability = 1,
|
||||||
|
DrawID=nil,
|
||||||
|
Color={}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -221,22 +226,6 @@ function ZONE_BASE:GetPointVec2()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns a @{Core.Point#COORDINATE} of the zone.
|
|
||||||
-- @param #ZONE_BASE self
|
|
||||||
-- @return Core.Point#COORDINATE The Coordinate of the zone.
|
|
||||||
function ZONE_BASE:GetCoordinate()
|
|
||||||
self:F2( self.ZoneName )
|
|
||||||
|
|
||||||
local Vec2 = self:GetVec2()
|
|
||||||
|
|
||||||
local Coordinate = COORDINATE:NewFromVec2( Vec2 )
|
|
||||||
|
|
||||||
self:T2( { Coordinate } )
|
|
||||||
|
|
||||||
return Coordinate
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns the @{DCS#Vec3} of the zone.
|
--- Returns the @{DCS#Vec3} of the zone.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||||
@@ -280,11 +269,23 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
|
|||||||
|
|
||||||
local Vec3 = self:GetVec3( Height )
|
local Vec3 = self:GetVec3( Height )
|
||||||
|
|
||||||
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
|
if self.Coordinate then
|
||||||
|
|
||||||
self:T2( { PointVec3 } )
|
-- Update coordinates.
|
||||||
|
self.Coordinate.x=Vec3.x
|
||||||
|
self.Coordinate.y=Vec3.y
|
||||||
|
self.Coordinate.z=Vec3.z
|
||||||
|
|
||||||
return PointVec3
|
--env.info("FF GetCoordinate NEW for ZONE_BASE "..tostring(self.ZoneName))
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Create a new coordinate object.
|
||||||
|
self.Coordinate=COORDINATE:NewFromVec3(Vec3)
|
||||||
|
|
||||||
|
--env.info("FF GetCoordinate NEW for ZONE_BASE "..tostring(self.ZoneName))
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.Coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -324,6 +325,76 @@ function ZONE_BASE:BoundZone()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set color of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
||||||
|
-- @param #number Alpha Transparacy between 0 and 1. Default 0.15.
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:SetColor(RGBcolor, Alpha)
|
||||||
|
|
||||||
|
RGBcolor=RGBcolor or {1, 0, 0}
|
||||||
|
Alpha=Alpha or 0.15
|
||||||
|
|
||||||
|
self.Color={}
|
||||||
|
self.Color[1]=RGBcolor[1]
|
||||||
|
self.Color[2]=RGBcolor[2]
|
||||||
|
self.Color[3]=RGBcolor[3]
|
||||||
|
self.Color[4]=Alpha
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get color table of the zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
|
function ZONE_BASE:GetColor()
|
||||||
|
return self.Color
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get RGB color of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code.
|
||||||
|
function ZONE_BASE:GetColorRGB()
|
||||||
|
local rgb={}
|
||||||
|
rgb[1]=self.Color[1]
|
||||||
|
rgb[2]=self.Color[2]
|
||||||
|
rgb[3]=self.Color[3]
|
||||||
|
return rgb
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get transperency Alpha value of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #number Alpha value.
|
||||||
|
function ZONE_BASE:GetColorAlpha()
|
||||||
|
local alpha=self.Color[4]
|
||||||
|
return alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the drawing of the zone from the F10 map.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #number Delay (Optional) Delay before the drawing is removed.
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:UndrawZone(Delay)
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self)
|
||||||
|
else
|
||||||
|
if self.DrawID then
|
||||||
|
UTILS.RemoveMark(self.DrawID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get ID of the zone object drawn on the F10 map.
|
||||||
|
-- The ID can be used to remove the drawn object from the F10 map view via `UTILS.RemoveMark(MarkID)`.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #number Unique ID of the
|
||||||
|
function ZONE_BASE:GetDrawID()
|
||||||
|
return self.DrawID
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Smokes the zone boundaries in a color.
|
--- Smokes the zone boundaries in a color.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||||
@@ -421,6 +492,10 @@ end
|
|||||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
|
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
|
||||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||||
--
|
--
|
||||||
|
-- ## Draw zone
|
||||||
|
--
|
||||||
|
-- * @{#ZONE_RADIUS.DrawZone}(): Draws the zone on the F10 map.
|
||||||
|
--
|
||||||
-- @field #ZONE_RADIUS
|
-- @field #ZONE_RADIUS
|
||||||
ZONE_RADIUS = {
|
ZONE_RADIUS = {
|
||||||
ClassName="ZONE_RADIUS",
|
ClassName="ZONE_RADIUS",
|
||||||
@@ -433,12 +508,51 @@ ZONE_RADIUS = {
|
|||||||
-- @param DCS#Distance Radius The radius of the zone.
|
-- @param DCS#Distance Radius The radius of the zone.
|
||||||
-- @return #ZONE_RADIUS self
|
-- @return #ZONE_RADIUS self
|
||||||
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update zone from a 2D vector.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param DCS#Vec2 Vec2 The location of the zone.
|
||||||
|
-- @param DCS#Distance Radius The radius of the zone.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:UpdateFromVec2(Vec2, Radius)
|
||||||
|
|
||||||
|
-- New center of the zone.
|
||||||
|
self.Vec2=Vec2
|
||||||
|
|
||||||
|
if Radius then
|
||||||
|
self.Radius=Radius
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update zone from a 2D vector.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param DCS#Vec3 Vec3 The location of the zone.
|
||||||
|
-- @param DCS#Distance Radius The radius of the zone.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:UpdateFromVec3(Vec3, Radius)
|
||||||
|
|
||||||
|
-- New center of the zone.
|
||||||
|
self.Vec2.x=Vec3.x
|
||||||
|
self.Vec2.y=Vec3.z
|
||||||
|
|
||||||
|
if Radius then
|
||||||
|
self.Radius=Radius
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -469,6 +583,32 @@ function ZONE_RADIUS:MarkZone(Points)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Draw the zone circle on the F10 map.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
local coordinate=self:GetCoordinate()
|
||||||
|
|
||||||
|
local Radius=self:GetRadius()
|
||||||
|
|
||||||
|
Color=Color or self:GetColorRGB()
|
||||||
|
Alpha=Alpha or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillAlpha=FillAlpha or self:GetColorAlpha()
|
||||||
|
|
||||||
|
self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Bounds the zone with tires.
|
--- Bounds the zone with tires.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
||||||
@@ -781,6 +921,32 @@ function ZONE_RADIUS:GetScannedSetUnit()
|
|||||||
return SetUnit
|
return SetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a set of scanned units.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @return Core.Set#SET_GROUP Set of groups.
|
||||||
|
function ZONE_RADIUS:GetScannedSetGroup()
|
||||||
|
|
||||||
|
self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() --Core.Set#SET_GROUP
|
||||||
|
|
||||||
|
self.ScanSetGroup.Set={}
|
||||||
|
|
||||||
|
if self.ScanData then
|
||||||
|
for ObjectID, UnitObject in pairs( self.ScanData.Units ) do
|
||||||
|
local UnitObject = UnitObject -- DCS#Unit
|
||||||
|
if UnitObject:isExist() then
|
||||||
|
|
||||||
|
local FoundUnit=UNIT:FindByName(UnitObject:getName())
|
||||||
|
if FoundUnit then
|
||||||
|
local group=FoundUnit:GetGroup()
|
||||||
|
self.ScanSetGroup:AddGroup(group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.ScanSetGroup
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Count the number of different coalitions inside the zone.
|
--- Count the number of different coalitions inside the zone.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
@@ -1116,22 +1282,37 @@ ZONE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Constructor of ZONE, taking the zone name.
|
--- Constructor of ZONE taking the zone name.
|
||||||
-- @param #ZONE self
|
-- @param #ZONE self
|
||||||
-- @param #string ZoneName The name of the zone as defined within the mission editor.
|
-- @param #string ZoneName The name of the zone as defined within the mission editor.
|
||||||
-- @return #ZONE
|
-- @return #ZONE self
|
||||||
function ZONE:New( ZoneName )
|
function ZONE:New( ZoneName )
|
||||||
|
|
||||||
|
-- First try to find the zone in the DB.
|
||||||
|
local zone=_DATABASE:FindZone(ZoneName)
|
||||||
|
|
||||||
|
if zone then
|
||||||
|
--env.info("FF found zone in DB")
|
||||||
|
return zone
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get zone from DCS trigger function.
|
||||||
local Zone = trigger.misc.getZone( ZoneName )
|
local Zone = trigger.misc.getZone( ZoneName )
|
||||||
|
|
||||||
|
-- Error!
|
||||||
if not Zone then
|
if not Zone then
|
||||||
error( "Zone " .. ZoneName .. " does not exist." )
|
error( "Zone " .. ZoneName .. " does not exist." )
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Create a new ZONE_RADIUS.
|
||||||
local self=BASE:Inherit( self, ZONE_RADIUS:New(ZoneName, {x=Zone.point.x, y=Zone.point.z}, Zone.radius))
|
local self=BASE:Inherit( self, ZONE_RADIUS:New(ZoneName, {x=Zone.point.x, y=Zone.point.z}, Zone.radius))
|
||||||
self:F(ZoneName)
|
self:F(ZoneName)
|
||||||
|
|
||||||
|
-- Color of zone.
|
||||||
|
self.Color={1, 0, 0, 0.15}
|
||||||
|
|
||||||
|
-- DCS zone.
|
||||||
self.Zone = Zone
|
self.Zone = Zone
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -1392,26 +1573,38 @@ end
|
|||||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
|
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
|
||||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
||||||
--
|
--
|
||||||
|
-- ## Draw zone
|
||||||
|
--
|
||||||
|
-- * @{#ZONE_POLYGON_BASE.DrawZone}(): Draws the zone on the F10 map.
|
||||||
|
-- * @{#ZONE_POLYGON_BASE.Boundary}(): Draw a frontier on the F10 map with small filled circles.
|
||||||
|
--
|
||||||
|
--
|
||||||
-- @field #ZONE_POLYGON_BASE
|
-- @field #ZONE_POLYGON_BASE
|
||||||
ZONE_POLYGON_BASE = {
|
ZONE_POLYGON_BASE = {
|
||||||
ClassName="ZONE_POLYGON_BASE",
|
ClassName="ZONE_POLYGON_BASE",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- A points array.
|
--- A 2D points array.
|
||||||
-- @type ZONE_POLYGON_BASE.ListVec2
|
-- @type ZONE_POLYGON_BASE.ListVec2
|
||||||
-- @list <DCS#Vec2>
|
-- @list <DCS#Vec2> Table of 2D vectors.
|
||||||
|
|
||||||
|
--- A 3D points array.
|
||||||
|
-- @type ZONE_POLYGON_BASE.ListVec3
|
||||||
|
-- @list <DCS#Vec3> Table of 3D vectors.
|
||||||
|
|
||||||
--- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCS#Vec2}, forming a polygon.
|
--- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCS#Vec2}, forming a polygon.
|
||||||
-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected.
|
-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #string ZoneName Name of the zone.
|
-- @param #string ZoneName Name of the zone.
|
||||||
-- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCS#Vec2}, forming a polygon..
|
-- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCS#Vec2}, forming a polygon.
|
||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
||||||
|
|
||||||
|
-- Inherit ZONE_BASE.
|
||||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) )
|
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) )
|
||||||
self:F( { ZoneName, PointsArray } )
|
self:F( { ZoneName, PointsArray } )
|
||||||
|
|
||||||
local i = 0
|
if PointsArray then
|
||||||
|
|
||||||
self._.Polygon = {}
|
self._.Polygon = {}
|
||||||
|
|
||||||
@@ -1421,11 +1614,47 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
|||||||
self._.Polygon[i].y = PointsArray[i].y
|
self._.Polygon[i].y = PointsArray[i].y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update polygon points with an array of @{DCS#Vec2}.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #ZONE_POLYGON_BASE.ListVec2 Vec2Array An array of @{DCS#Vec2}, forming a polygon.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array)
|
||||||
|
|
||||||
|
self._.Polygon = {}
|
||||||
|
|
||||||
|
for i=1,#Vec2Array do
|
||||||
|
self._.Polygon[i] = {}
|
||||||
|
self._.Polygon[i].x=Vec2Array[i].x
|
||||||
|
self._.Polygon[i].y=Vec2Array[i].y
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update polygon points with an array of @{DCS#Vec3}.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #ZONE_POLYGON_BASE.ListVec3 Vec2Array An array of @{DCS#Vec3}, forming a polygon.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array)
|
||||||
|
|
||||||
|
self._.Polygon = {}
|
||||||
|
|
||||||
|
for i=1,#Vec3Array do
|
||||||
|
self._.Polygon[i] = {}
|
||||||
|
self._.Polygon[i].x=Vec3Array[i].x
|
||||||
|
self._.Polygon[i].y=Vec3Array[i].z
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the center location of the polygon.
|
--- Returns the center location of the polygon.
|
||||||
-- @param #ZONE_GROUP self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
||||||
function ZONE_POLYGON_BASE:GetVec2()
|
function ZONE_POLYGON_BASE:GetVec2()
|
||||||
self:F( self.ZoneName )
|
self:F( self.ZoneName )
|
||||||
@@ -1435,6 +1664,78 @@ function ZONE_POLYGON_BASE:GetVec2()
|
|||||||
return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 }
|
return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a vertex of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Index Index of the vertex. Default 1.
|
||||||
|
-- @return DCS#Vec2 Vertex of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVertexVec2(Index)
|
||||||
|
return self._.Polygon[Index or 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a vertex of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Index Index of the vertex. Default 1.
|
||||||
|
-- @return DCS#Vec3 Vertex of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVertexVec3(Index)
|
||||||
|
local vec2=self:GetVertexVec2(Index)
|
||||||
|
if vec2 then
|
||||||
|
local vec3={x=vec2.x, y=land.getHeight(vec2), z=vec2.y}
|
||||||
|
return vec3
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a vertex of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Index Index of the vertex. Default 1.
|
||||||
|
-- @return Core.Point#COORDINATE Vertex of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVertexCoordinate(Index)
|
||||||
|
local vec2=self:GetVertexVec2(Index)
|
||||||
|
if vec2 then
|
||||||
|
local coord=COORDINATE:NewFromVec2(vec2)
|
||||||
|
return coord
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get a list of verticies of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return <DCS#Vec2> List of DCS#Vec2 verticies defining the edges of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVerticiesVec2()
|
||||||
|
return self._.Polygon
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a list of verticies of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #table List of DCS#Vec3 verticies defining the edges of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVerticiesVec3()
|
||||||
|
|
||||||
|
local coords={}
|
||||||
|
|
||||||
|
for i,vec2 in ipairs(self._.Polygon) do
|
||||||
|
local vec3={x=vec2.x, y=land.getHeight(vec2), z=vec2.y}
|
||||||
|
table.insert(coords, vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
return coords
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a list of verticies of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #table List of COORDINATES verticies defining the edges of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVerticiesCoordinates()
|
||||||
|
|
||||||
|
local coords={}
|
||||||
|
|
||||||
|
for i,vec2 in ipairs(self._.Polygon) do
|
||||||
|
local coord=COORDINATE:NewFromVec2(vec2)
|
||||||
|
table.insert(coords, coord)
|
||||||
|
end
|
||||||
|
|
||||||
|
return coords
|
||||||
|
end
|
||||||
|
|
||||||
--- Flush polygon coordinates as a table in DCS.log.
|
--- Flush polygon coordinates as a table in DCS.log.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
@@ -1494,6 +1795,46 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Draw the zone on the F10 map. **NOTE** Currently, only polygons with **exactly four points** are supported!
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
||||||
|
|
||||||
|
Color=Color or self:GetColorRGB()
|
||||||
|
Alpha=Alpha or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillAlpha=FillAlpha or self:GetColorAlpha()
|
||||||
|
|
||||||
|
|
||||||
|
if #self._.Polygon==4 then
|
||||||
|
|
||||||
|
local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
|
||||||
|
local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3])
|
||||||
|
local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4])
|
||||||
|
|
||||||
|
self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
local Coordinates=self:GetVerticiesCoordinates()
|
||||||
|
table.remove(Coordinates, 1)
|
||||||
|
|
||||||
|
self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Smokes the zone boundaries in a color.
|
--- Smokes the zone boundaries in a color.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
@@ -1685,6 +2026,45 @@ function ZONE_POLYGON_BASE:GetBoundingSquare()
|
|||||||
return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 }
|
return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Draw a frontier on the F10 map with small filled circles.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1= All.
|
||||||
|
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1, 0, 0} for red. Default {1, 1, 1}= White.
|
||||||
|
-- @param #number Radius (Optional) Radius of the circles in meters. Default 1000.
|
||||||
|
-- @param #number Alpha (Optional) Alpha transparency [0,1]. Default 1.
|
||||||
|
-- @param #number Segments (Optional) Number of segments within boundary line. Default 10.
|
||||||
|
-- @param #boolean Closed (Optional) Link the last point with the first one to obtain a closed boundary. Default false
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, Closed)
|
||||||
|
Coalition = Coalition or -1
|
||||||
|
Color = Color or {1, 1, 1}
|
||||||
|
Radius = Radius or 1000
|
||||||
|
Alpha = Alpha or 1
|
||||||
|
Segments = Segments or 10
|
||||||
|
Closed = Closed or false
|
||||||
|
local i = 1
|
||||||
|
local j = #self._.Polygon
|
||||||
|
if (Closed) then
|
||||||
|
Limit = #self._.Polygon + 1
|
||||||
|
else
|
||||||
|
Limit = #self._.Polygon
|
||||||
|
end
|
||||||
|
while i <= #self._.Polygon do
|
||||||
|
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||||
|
if j ~= Limit then
|
||||||
|
local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x
|
||||||
|
local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y
|
||||||
|
for Segment = 0, Segments do
|
||||||
|
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||||
|
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||||
|
ZONE_RADIUS:New( "Zone", {x = PointX, y = PointY}, Radius ):DrawZone(Coalition, Color, 1, Color, Alpha, nil, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
j = i
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- @type ZONE_POLYGON
|
--- @type ZONE_POLYGON
|
||||||
-- @extends #ZONE_POLYGON_BASE
|
-- @extends #ZONE_POLYGON_BASE
|
||||||
@@ -1711,7 +2091,7 @@ end
|
|||||||
-- This is especially handy if you want to quickly setup a SET_ZONE...
|
-- This is especially handy if you want to quickly setup a SET_ZONE...
|
||||||
-- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`,
|
-- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`,
|
||||||
-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection,
|
-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection,
|
||||||
-- without much scripting overhead!!!
|
-- without much scripting overhead!
|
||||||
--
|
--
|
||||||
-- @field #ZONE_POLYGON
|
-- @field #ZONE_POLYGON
|
||||||
ZONE_POLYGON = {
|
ZONE_POLYGON = {
|
||||||
|
|||||||
@@ -295,6 +295,17 @@ do -- country
|
|||||||
-- @field QATAR
|
-- @field QATAR
|
||||||
-- @field OMAN
|
-- @field OMAN
|
||||||
-- @field UNITED_ARAB_EMIRATES
|
-- @field UNITED_ARAB_EMIRATES
|
||||||
|
-- @field SOUTH_AFRICA
|
||||||
|
-- @field CUBA
|
||||||
|
-- @field PORTUGAL
|
||||||
|
-- @field GDR
|
||||||
|
-- @field LEBANON
|
||||||
|
-- @field CJTF_BLUE
|
||||||
|
-- @field CJTF_RED
|
||||||
|
-- @field UN_PEACEKEEPERS
|
||||||
|
-- @field Argentinia
|
||||||
|
-- @field Cyprus
|
||||||
|
-- @field Slovenia
|
||||||
|
|
||||||
country = {} --#country
|
country = {} --#country
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
-- @module Functional.Mantis
|
-- @module Functional.Mantis
|
||||||
-- @image Functional.Mantis.jpg
|
-- @image Functional.Mantis.jpg
|
||||||
|
|
||||||
-- Date: Apr 2021
|
-- Date: July 2021
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **MANTIS** class, extends #Core.Base#BASE
|
--- **MANTIS** class, extends #Core.Base#BASE
|
||||||
@@ -191,7 +191,18 @@ MANTIS = {
|
|||||||
ShoradLink = false,
|
ShoradLink = false,
|
||||||
ShoradTime = 600,
|
ShoradTime = 600,
|
||||||
ShoradActDistance = 15000,
|
ShoradActDistance = 15000,
|
||||||
UseEmOnOff = true,
|
UseEmOnOff = false,
|
||||||
|
TimeStamp = 0,
|
||||||
|
state2flag = false,
|
||||||
|
SamStateTracker = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Advanced state enumerator
|
||||||
|
-- @type MANTIS.AdvancedState
|
||||||
|
MANTIS.AdvancedState = {
|
||||||
|
GREEN = 0,
|
||||||
|
AMBER = 1,
|
||||||
|
RED = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
@@ -208,7 +219,7 @@ 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 (optional, deault true)
|
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN
|
||||||
--@return #MANTIS self
|
--@return #MANTIS self
|
||||||
--@usage Start up your MANTIS with a basic setting
|
--@usage Start up your MANTIS with a basic setting
|
||||||
--
|
--
|
||||||
@@ -263,10 +274,16 @@ do
|
|||||||
self.ShoradLink = false
|
self.ShoradLink = false
|
||||||
self.ShoradTime = 600
|
self.ShoradTime = 600
|
||||||
self.ShoradActDistance = 15000
|
self.ShoradActDistance = 15000
|
||||||
-- TODO: add emissions on/off when available .... in 2 weeks
|
self.TimeStamp = timer.getAbsTime()
|
||||||
|
self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
||||||
|
self.state2flag = false
|
||||||
|
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
|
||||||
|
|
||||||
if EmOnOff then
|
if EmOnOff then
|
||||||
if EmOnOff == false then
|
if EmOnOff == false then
|
||||||
self.UseEmOnOff = false
|
self.UseEmOnOff = false
|
||||||
|
else
|
||||||
|
self.UseEmOnOff = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -277,7 +294,7 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Inherit everything from BASE class.
|
-- Inherit everything from BASE class.
|
||||||
local self = BASE:Inherit(self, BASE:New()) -- #MANTIS
|
local self = BASE:Inherit(self, FSM:New()) -- #MANTIS
|
||||||
|
|
||||||
-- Set the string id for output to DCS.log file.
|
-- Set the string id for output to DCS.log file.
|
||||||
self.lid=string.format("MANTIS %s | ", self.name)
|
self.lid=string.format("MANTIS %s | ", self.name)
|
||||||
@@ -308,9 +325,102 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
self.version="0.4.1"
|
self.version="0.5.2"
|
||||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||||
|
|
||||||
|
--- FSM Functions ---
|
||||||
|
|
||||||
|
-- Start State.
|
||||||
|
self:SetStartState("Stopped")
|
||||||
|
|
||||||
|
-- Add FSM transitions.
|
||||||
|
-- From State --> Event --> To State
|
||||||
|
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
||||||
|
self:AddTransition("*", "Status", "*") -- MANTIS status update.
|
||||||
|
self:AddTransition("*", "Relocating", "*") -- MANTIS HQ and EWR are relocating.
|
||||||
|
self:AddTransition("*", "GreenState", "*") -- MANTIS A SAM switching to GREEN state.
|
||||||
|
self:AddTransition("*", "RedState", "*") -- MANTIS A SAM switching to RED state.
|
||||||
|
self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change.
|
||||||
|
self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD.
|
||||||
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
--- Pseudo Functions ---
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Start". Starts the MANTIS. Initializes parameters and starts event handlers.
|
||||||
|
-- @function [parent=#MANTIS] Start
|
||||||
|
-- @param #MANTIS self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Start" after a delay. Starts the MANTIS. Initializes parameters and starts event handlers.
|
||||||
|
-- @function [parent=#MANTIS] __Start
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Stop". Stops the MANTIS and all its event handlers.
|
||||||
|
-- @param #MANTIS self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Stop" after a delay. Stops the MANTIS and all its event handlers.
|
||||||
|
-- @function [parent=#MANTIS] __Stop
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Status".
|
||||||
|
-- @function [parent=#MANTIS] Status
|
||||||
|
-- @param #MANTIS self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Status" after a delay.
|
||||||
|
-- @function [parent=#MANTIS] __Status
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- On After "Relocating" event. HQ and/or EWR moved.
|
||||||
|
-- @function [parent=#MANTIS] OnAfterRelocating
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @return #MANTIS self
|
||||||
|
|
||||||
|
--- On After "GreenState" event. A SAM group was switched to GREEN alert.
|
||||||
|
-- @function [parent=#MANTIS] OnAfterGreenState
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
||||||
|
-- @return #MANTIS self
|
||||||
|
|
||||||
|
--- On After "RedState" event. A SAM group was switched to RED alert.
|
||||||
|
-- @function [parent=#MANTIS] OnAfterRedState
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
||||||
|
-- @return #MANTIS self
|
||||||
|
|
||||||
|
--- On After "AdvStateChange" event. Advanced state changed, influencing detection speed.
|
||||||
|
-- @function [parent=#MANTIS] OnAfterAdvStateChange
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param #number Oldstate Old state - 0 = green, 1 = amber, 2 = red
|
||||||
|
-- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red
|
||||||
|
-- @param #number Interval Calculated detection interval based on state and advanced feature setting
|
||||||
|
-- @return #MANTIS self
|
||||||
|
|
||||||
|
--- On After "ShoradActivated" event. Mantis has activated a SHORAD.
|
||||||
|
-- @function [parent=#MANTIS] OnAfterShoradActivated
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param #string Name Name of the GROUP which SHORAD shall protect
|
||||||
|
-- @param #number Radius Radius around the named group to find SHORAD groups
|
||||||
|
-- @param #number Ontime Seconds the SHORAD will stay active
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -318,17 +428,19 @@ do
|
|||||||
-- MANTIS helper functions
|
-- MANTIS helper functions
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
--- [internal] Function to get the self.SAM_Table
|
--- [Internal] Function to get the self.SAM_Table
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #table table
|
-- @return #table table
|
||||||
function MANTIS:_GetSAMTable()
|
function MANTIS:_GetSAMTable()
|
||||||
|
self:T(self.lid .. "GetSAMTable")
|
||||||
return self.SAM_Table
|
return self.SAM_Table
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [internal] Function to set the self.SAM_Table
|
--- [Internal] Function to set the self.SAM_Table
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #MANTIS self
|
-- @return #MANTIS self
|
||||||
function MANTIS:_SetSAMTable(table)
|
function MANTIS:_SetSAMTable(table)
|
||||||
|
self:T(self.lid .. "SetSAMTable")
|
||||||
self.SAM_Table = table
|
self.SAM_Table = table
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -337,41 +449,50 @@ do
|
|||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number radius Radius upon which detected objects will be grouped
|
-- @param #number radius Radius upon which detected objects will be grouped
|
||||||
function MANTIS:SetEWRGrouping(radius)
|
function MANTIS:SetEWRGrouping(radius)
|
||||||
|
self:T(self.lid .. "SetEWRGrouping")
|
||||||
local radius = radius or 5000
|
local radius = radius or 5000
|
||||||
self.grouping = radius
|
self.grouping = radius
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set the detection radius of the EWR in meters
|
--- Function to set the detection radius of the EWR in meters
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number radius Radius of the EWR detection zone
|
-- @param #number radius Radius of the EWR detection zone
|
||||||
function MANTIS:SetEWRRange(radius)
|
function MANTIS:SetEWRRange(radius)
|
||||||
|
self:T(self.lid .. "SetEWRRange")
|
||||||
local radius = radius or 80000
|
local radius = radius or 80000
|
||||||
self.acceptrange = radius
|
self.acceptrange = radius
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set switch-on/off zone for the SAM sites in meters
|
--- Function to set switch-on/off zone for the SAM sites in meters
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number radius Radius of the firing zone
|
-- @param #number radius Radius of the firing zone
|
||||||
function MANTIS:SetSAMRadius(radius)
|
function MANTIS:SetSAMRadius(radius)
|
||||||
|
self:T(self.lid .. "SetSAMRadius")
|
||||||
local radius = radius or 25000
|
local radius = radius or 25000
|
||||||
self.checkradius = radius
|
self.checkradius = radius
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75
|
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number range Percent of the max fire range
|
-- @param #number range Percent of the max fire range
|
||||||
function MANTIS:SetSAMRange(range)
|
function MANTIS:SetSAMRange(range)
|
||||||
|
self:T(self.lid .. "SetSAMRange")
|
||||||
local range = range or 75
|
local range = range or 75
|
||||||
if range < 0 or range > 100 then
|
if range < 0 or range > 100 then
|
||||||
range = 75
|
range = 75
|
||||||
end
|
end
|
||||||
self.engagerange = range
|
self.engagerange = range
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night
|
--- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number range Percent of the max fire range
|
-- @param #number range Percent of the max fire range
|
||||||
function MANTIS:SetNewSAMRangeWhileRunning(range)
|
function MANTIS:SetNewSAMRangeWhileRunning(range)
|
||||||
|
self:T(self.lid .. "SetNewSAMRangeWhileRunning")
|
||||||
local range = range or 75
|
local range = range or 75
|
||||||
if range < 0 or range > 100 then
|
if range < 0 or range > 100 then
|
||||||
range = 75
|
range = 75
|
||||||
@@ -379,20 +500,32 @@ do
|
|||||||
self.engagerange = range
|
self.engagerange = range
|
||||||
self:_RefreshSAMTable()
|
self:_RefreshSAMTable()
|
||||||
self.mysead.EngagementRange = range
|
self.mysead.EngagementRange = range
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set switch-on/off the debug state
|
--- Function to set switch-on/off the debug state
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #boolean onoff Set true to switch on
|
-- @param #boolean onoff Set true to switch on
|
||||||
function MANTIS:Debug(onoff)
|
function MANTIS:Debug(onoff)
|
||||||
|
self:T(self.lid .. "SetDebug")
|
||||||
local onoff = onoff or false
|
local onoff = onoff or false
|
||||||
self.debug = onoff
|
self.debug = onoff
|
||||||
|
if onoff then
|
||||||
|
-- Debug trace.
|
||||||
|
BASE:TraceOn()
|
||||||
|
BASE:TraceClass("MANTIS")
|
||||||
|
BASE:TraceLevel(1)
|
||||||
|
else
|
||||||
|
BASE:TraceOff()
|
||||||
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to get the HQ object for further use
|
--- Function to get the HQ object for further use
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist
|
-- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist
|
||||||
function MANTIS:GetCommandCenter()
|
function MANTIS:GetCommandCenter()
|
||||||
|
self:T(self.lid .. "GetCommandCenter")
|
||||||
if self.HQ_CC then
|
if self.HQ_CC then
|
||||||
return self.HQ_CC
|
return self.HQ_CC
|
||||||
else
|
else
|
||||||
@@ -404,26 +537,31 @@ do
|
|||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #string prefix Name of the AWACS group in the mission editor
|
-- @param #string prefix Name of the AWACS group in the mission editor
|
||||||
function MANTIS:SetAwacs(prefix)
|
function MANTIS:SetAwacs(prefix)
|
||||||
|
self:T(self.lid .. "SetAwacs")
|
||||||
if prefix ~= nil then
|
if prefix ~= nil then
|
||||||
if type(prefix) == "string" then
|
if type(prefix) == "string" then
|
||||||
self.AWACS_Prefix = prefix
|
self.AWACS_Prefix = prefix
|
||||||
self.advAwacs = true
|
self.advAwacs = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set AWACS detection range. Defaults to 250.000m (250km) - use **before** starting your Mantis!
|
--- Function to set AWACS detection range. Defaults to 250.000m (250km) - use **before** starting your Mantis!
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number range Detection range of the AWACS group
|
-- @param #number range Detection range of the AWACS group
|
||||||
function MANTIS:SetAwacsRange(range)
|
function MANTIS:SetAwacsRange(range)
|
||||||
|
self:T(self.lid .. "SetAwacsRange")
|
||||||
local range = range or 250000
|
local range = range or 250000
|
||||||
self.awacsrange = range
|
self.awacsrange = range
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set the HQ object for further use
|
--- Function to set the HQ object for further use
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
|
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
|
||||||
function MANTIS:SetCommandCenter(group)
|
function MANTIS:SetCommandCenter(group)
|
||||||
|
self:T(self.lid .. "SetCommandCenter")
|
||||||
local group = group or nil
|
local group = group or nil
|
||||||
if group ~= nil then
|
if group ~= nil then
|
||||||
if type(group) == "string" then
|
if type(group) == "string" then
|
||||||
@@ -434,14 +572,17 @@ do
|
|||||||
self.HQ_Template_CC = group:GetName()
|
self.HQ_Template_CC = group:GetName()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set the detection interval
|
--- Function to set the detection interval
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #number interval The interval in seconds
|
-- @param #number interval The interval in seconds
|
||||||
function MANTIS:SetDetectInterval(interval)
|
function MANTIS:SetDetectInterval(interval)
|
||||||
|
self:T(self.lid .. "SetDetectInterval")
|
||||||
local interval = interval or 30
|
local interval = interval or 30
|
||||||
self.detectinterval = interval
|
self.detectinterval = interval
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to set Advanded Mode
|
--- Function to set Advanded Mode
|
||||||
@@ -451,7 +592,8 @@ do
|
|||||||
-- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
|
-- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
|
||||||
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
|
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
|
||||||
function MANTIS:SetAdvancedMode(onoff, ratio)
|
function MANTIS:SetAdvancedMode(onoff, ratio)
|
||||||
self:F({onoff, ratio})
|
self:T(self.lid .. "SetAdvancedMode")
|
||||||
|
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
|
||||||
@@ -459,53 +601,58 @@ do
|
|||||||
self.advanced = true
|
self.advanced = true
|
||||||
self.adv_state = 0
|
self.adv_state = 0
|
||||||
self.Adv_EWR_Group = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
self.Adv_EWR_Group = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
||||||
env.info(string.format("***** Starting Advanced Mode MANTIS Version %s *****", self.version))
|
self:I(string.format("***** Starting Advanced Mode MANTIS Version %s *****", self.version))
|
||||||
else
|
else
|
||||||
local text = self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both."
|
local text = self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both."
|
||||||
local m= MESSAGE:New(text,10,"MANTIS",true):ToAll()
|
local m= MESSAGE:New(text,10,"MANTIS",true):ToAll()
|
||||||
BASE:E(text)
|
self:E(text)
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set using Emissions on/off instead of changing alarm state
|
--- Set using Emissions on/off instead of changing alarm state
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #boolean switch Decide if we are changing alarm state or Emission state
|
-- @param #boolean switch Decide if we are changing alarm state or Emission state
|
||||||
function MANTIS:SetUsingEmOnOff(switch)
|
function MANTIS:SetUsingEmOnOff(switch)
|
||||||
|
self:T(self.lid .. "SetUsingEmOnOff")
|
||||||
self.UseEmOnOff = switch or false
|
self.UseEmOnOff = switch or false
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [Internal] Function to check if HQ is alive
|
--- [Internal] Function to check if HQ is alive
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #boolean True if HQ is alive, else false
|
-- @return #boolean True if HQ is alive, else false
|
||||||
function MANTIS:_CheckHQState()
|
function MANTIS:_CheckHQState()
|
||||||
|
self:T(self.lid .. "CheckHQState")
|
||||||
local text = self.lid.." Checking HQ State"
|
local text = self.lid.." Checking HQ State"
|
||||||
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 env.info(text) end
|
if self.verbose then self:I(text) end
|
||||||
-- start check
|
-- start check
|
||||||
if self.advanced then
|
if self.advanced then
|
||||||
local hq = self.HQ_Template_CC
|
local hq = self.HQ_Template_CC
|
||||||
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
|
||||||
env.info(self.lid.." HQ is alive!")
|
self:T(self.lid.." HQ is alive!")
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
env.info(self.lid.." HQ is dead!")
|
self:T(self.lid.." HQ is dead!")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [Internal] Function to check if EWR is (at least partially) alive
|
--- [Internal] Function to check if EWR is (at least partially) alive
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #boolean True if EWR is alive, else false
|
-- @return #boolean True if EWR is alive, else false
|
||||||
function MANTIS:_CheckEWRState()
|
function MANTIS:_CheckEWRState()
|
||||||
|
self:T(self.lid .. "CheckEWRState")
|
||||||
local text = self.lid.." Checking EWR State"
|
local text = self.lid.." Checking EWR State"
|
||||||
self:F(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 env.info(text) end
|
if self.verbose then self:I(text) end
|
||||||
-- start check
|
-- start check
|
||||||
if self.advanced then
|
if self.advanced then
|
||||||
local EWR_Group = self.Adv_EWR_Group
|
local EWR_Group = self.Adv_EWR_Group
|
||||||
@@ -519,24 +666,26 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
env.info(self.lid..string.format(" No of EWR alive is %d", nalive))
|
self:T(self.lid..string.format(" No of EWR alive is %d", nalive))
|
||||||
if nalive > 0 then
|
if nalive > 0 then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [Internal] Function to determine state of the advanced mode
|
--- [Internal] Function to determine state of the advanced mode
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #number Newly calculated interval
|
-- @return #number Newly calculated interval
|
||||||
-- @return #number Previous state for tracking 0, 1, or 2
|
-- @return #number Previous state for tracking 0, 1, or 2
|
||||||
function MANTIS:_CheckAdvState()
|
function MANTIS:_CalcAdvState()
|
||||||
local text = self.lid.." Checking Advanced State"
|
self:T(self.lid .. "CalcAdvState")
|
||||||
self:F(text)
|
local text = self.lid.." Calculating Advanced State"
|
||||||
|
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 env.info(text) end
|
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()
|
||||||
@@ -555,9 +704,9 @@ do
|
|||||||
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)
|
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
|
||||||
self:F(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 env.info(text) end
|
if self.verbose then self:I(text) end
|
||||||
return newinterval, currstate
|
return newinterval, currstate
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -566,7 +715,8 @@ do
|
|||||||
-- @param #boolean hq If true, will relocate HQ object
|
-- @param #boolean hq If true, will relocate HQ object
|
||||||
-- @param #boolean ewr If true, will relocate EWR objects
|
-- @param #boolean ewr If true, will relocate EWR objects
|
||||||
function MANTIS:SetAutoRelocate(hq, ewr)
|
function MANTIS:SetAutoRelocate(hq, ewr)
|
||||||
self:F({hq, ewr})
|
self:T(self.lid .. "SetAutoRelocate")
|
||||||
|
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
|
||||||
@@ -574,22 +724,24 @@ do
|
|||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [Internal] Function to execute the relocation
|
--- [Internal] Function to execute the relocation
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
function MANTIS:_RelocateGroups()
|
function MANTIS:_RelocateGroups()
|
||||||
self:T(self.lid.." Relocating Groups")
|
self:T(self.lid .. "RelocateGroups")
|
||||||
local text = self.lid.." Relocating Groups"
|
local text = self.lid.." Relocating Groups"
|
||||||
local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug)
|
local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug)
|
||||||
if self.verbose then env.info(text) end
|
if self.verbose then self:I(text) end
|
||||||
if self.autorelocate then
|
if self.autorelocate then
|
||||||
-- relocate HQ
|
-- relocate HQ
|
||||||
if self.autorelocateunits.HQ and self.HQ_CC then --only relocate if HQ exists
|
local HQGroup = self.HQ_CC
|
||||||
|
if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists
|
||||||
local _hqgrp = self.HQ_CC
|
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)
|
||||||
end
|
end
|
||||||
--relocate EWR
|
--relocate EWR
|
||||||
@@ -599,26 +751,27 @@ do
|
|||||||
local EWR_GRP = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
local EWR_GRP = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
||||||
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: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 env.info(text) end
|
if self.verbose then self:I(text) end
|
||||||
_grp:RelocateGroundRandomInRadius(20,500,true,true)
|
_grp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Function to check if any object is in the given SAM zone
|
--- [Internal] Function to check if any object is in the given SAM zone
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @param #table dectset Table of coordinates of detected items
|
-- @param #table dectset Table of coordinates of detected items
|
||||||
-- @param samcoordinate Core.Point#COORDINATE Coordinate object.
|
-- @param Core.Point#COORDINATE samcoordinate Coordinate object.
|
||||||
-- @return #boolean True if in any zone, else false
|
-- @return #boolean True if in any zone, else false
|
||||||
-- @return #number Distance Target distance in meters or zero when no object is in zone
|
-- @return #number Distance Target distance in meters or zero when no object is in zone
|
||||||
function MANTIS:CheckObjectInZone(dectset, samcoordinate)
|
function MANTIS:CheckObjectInZone(dectset, samcoordinate)
|
||||||
self:F(self.lid.."CheckObjectInZone Called")
|
self:T(self.lid.."CheckObjectInZone")
|
||||||
-- check if non of the coordinate is in the given defense zone
|
-- check if non of the coordinate is in the given defense zone
|
||||||
local radius = self.checkradius
|
local radius = self.checkradius
|
||||||
local set = dectset
|
local set = dectset
|
||||||
@@ -630,7 +783,7 @@ do
|
|||||||
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)
|
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
|
||||||
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
||||||
if self.verbose then env.info(self.lid..text) end
|
if self.verbose then 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
|
||||||
@@ -639,11 +792,11 @@ do
|
|||||||
return false, 0
|
return false, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Function to start the detection via EWR groups
|
--- [Internal] Function to start the detection via EWR groups
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||||
function MANTIS:StartDetection()
|
function MANTIS:StartDetection()
|
||||||
self:F(self.lid.."Starting Detection")
|
self:T(self.lid.."Starting Detection")
|
||||||
|
|
||||||
-- start detection
|
-- start detection
|
||||||
local groupset = self.EWR_Group
|
local groupset = self.EWR_Group
|
||||||
@@ -651,14 +804,14 @@ do
|
|||||||
local acceptrange = self.acceptrange or 80000
|
local acceptrange = self.acceptrange or 80000
|
||||||
local interval = self.detectinterval or 60
|
local interval = self.detectinterval or 60
|
||||||
|
|
||||||
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object
|
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object
|
||||||
_MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones
|
local MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones
|
||||||
_MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||||
_MANTISdetection:SetAcceptRange(acceptrange)
|
MANTISdetection:SetAcceptRange(acceptrange)
|
||||||
_MANTISdetection:SetRefreshTimeInterval(interval)
|
MANTISdetection:SetRefreshTimeInterval(interval)
|
||||||
_MANTISdetection:Start()
|
MANTISdetection:Start()
|
||||||
|
|
||||||
function _MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
function MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||||
--BASE:I( { From, Event, To, DetectedItem })
|
--BASE:I( { From, Event, To, DetectedItem })
|
||||||
local debug = false
|
local debug = false
|
||||||
if DetectedItem.IsDetected and debug then
|
if DetectedItem.IsDetected and debug then
|
||||||
@@ -667,14 +820,14 @@ do
|
|||||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return _MANTISdetection
|
return MANTISdetection
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Function to start the detection via AWACS if defined as separate
|
--- [Internal] Function to start the detection via AWACS if defined as separate
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||||
function MANTIS:StartAwacsDetection()
|
function MANTIS:StartAwacsDetection()
|
||||||
self:F(self.lid.."Starting Awacs Detection")
|
self:T(self.lid.."Starting Awacs Detection")
|
||||||
|
|
||||||
-- start detection
|
-- start detection
|
||||||
local group = self.AWACS_Prefix
|
local group = self.AWACS_Prefix
|
||||||
@@ -683,14 +836,14 @@ do
|
|||||||
--local acceptrange = self.acceptrange or 80000
|
--local acceptrange = self.acceptrange or 80000
|
||||||
local interval = self.detectinterval or 60
|
local interval = self.detectinterval or 60
|
||||||
|
|
||||||
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object
|
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object
|
||||||
_MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones
|
local MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones
|
||||||
_MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||||
_MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
|
MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
|
||||||
_MANTISAwacs:SetRefreshTimeInterval(interval)
|
MANTISAwacs:SetRefreshTimeInterval(interval)
|
||||||
_MANTISAwacs:Start()
|
MANTISAwacs:Start()
|
||||||
|
|
||||||
function _MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
function MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||||
--BASE:I( { From, Event, To, DetectedItem })
|
--BASE:I( { From, Event, To, DetectedItem })
|
||||||
local debug = false
|
local debug = false
|
||||||
if DetectedItem.IsDetected and debug then
|
if DetectedItem.IsDetected and debug then
|
||||||
@@ -699,15 +852,15 @@ do
|
|||||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return _MANTISAwacs
|
return MANTISAwacs
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Function to set the SAM start state
|
--- [Internal] Function to set the SAM start state
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #MANTIS self
|
-- @return #MANTIS self
|
||||||
function MANTIS:SetSAMStartState()
|
function MANTIS:SetSAMStartState()
|
||||||
-- DONE: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406
|
-- DONE: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406
|
||||||
self:F(self.lid.."Setting SAM Start States")
|
self:T(self.lid.."Setting SAM Start States")
|
||||||
-- get SAM Group
|
-- get SAM Group
|
||||||
local SAM_SET = self.SAM_Group
|
local SAM_SET = self.SAM_Group
|
||||||
local SAM_Grps = SAM_SET.Set --table of objects
|
local SAM_Grps = SAM_SET.Set --table of objects
|
||||||
@@ -716,7 +869,7 @@ do
|
|||||||
local engagerange = self.engagerange -- firing range in % of max
|
local engagerange = self.engagerange -- firing range in % of max
|
||||||
--cycle through groups and set alarm state etc
|
--cycle through groups and set alarm state etc
|
||||||
for _i,_group in pairs (SAM_Grps) do
|
for _i,_group in pairs (SAM_Grps) do
|
||||||
local group = _group
|
local group = _group -- Wrapper.Group#GROUP
|
||||||
-- TODO: add emissions on/off
|
-- TODO: add emissions on/off
|
||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
group:EnableEmission(false)
|
group:EnableEmission(false)
|
||||||
@@ -725,11 +878,12 @@ do
|
|||||||
group:OptionAlarmStateGreen() -- AI off
|
group:OptionAlarmStateGreen() -- AI off
|
||||||
end
|
end
|
||||||
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range
|
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range
|
||||||
if group:IsGround() then
|
if group:IsGround() and group:IsAlive() then
|
||||||
local grpname = group:GetName()
|
local grpname = group:GetName()
|
||||||
local grpcoord = group:GetCoordinate()
|
local grpcoord = group:GetCoordinate()
|
||||||
table.insert( SAM_Tbl, {grpname, grpcoord})
|
table.insert( SAM_Tbl, {grpname, grpcoord})
|
||||||
table.insert( SEAD_Grps, grpname )
|
table.insert( SEAD_Grps, grpname )
|
||||||
|
self.SamStateTracker[grpname] = "GREEN"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.SAM_Table = SAM_Tbl
|
self.SAM_Table = SAM_Tbl
|
||||||
@@ -740,11 +894,11 @@ do
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Function to update SAM table and SEAD state
|
--- [Internal] Function to update SAM table and SEAD state
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
-- @return #MANTIS self
|
-- @return #MANTIS self
|
||||||
function MANTIS:_RefreshSAMTable()
|
function MANTIS:_RefreshSAMTable()
|
||||||
self:F(self.lid.."Setting SAM Start States")
|
self:T(self.lid.."RefreshSAMTable")
|
||||||
-- Requires SEAD 0.2.2 or better
|
-- Requires SEAD 0.2.2 or better
|
||||||
-- get SAM Group
|
-- get SAM Group
|
||||||
local SAM_SET = self.SAM_Group
|
local SAM_SET = self.SAM_Group
|
||||||
@@ -756,7 +910,7 @@ do
|
|||||||
for _i,_group in pairs (SAM_Grps) do
|
for _i,_group in pairs (SAM_Grps) do
|
||||||
local group = _group
|
local group = _group
|
||||||
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range
|
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range
|
||||||
if group:IsGround() then
|
if group:IsGround() and group:IsAlive() then
|
||||||
local grpname = group:GetName()
|
local grpname = group:GetName()
|
||||||
local grpcoord = group:GetCoordinate()
|
local grpcoord = group:GetCoordinate()
|
||||||
table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here
|
table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here
|
||||||
@@ -777,6 +931,7 @@ do
|
|||||||
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
|
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
|
||||||
-- @param #number Shoradtime Number of seconds #SHORAD stays active post wake-up
|
-- @param #number Shoradtime Number of seconds #SHORAD stays active post wake-up
|
||||||
function MANTIS:AddShorad(Shorad,Shoradtime)
|
function MANTIS:AddShorad(Shorad,Shoradtime)
|
||||||
|
self:T(self.lid.."AddShorad")
|
||||||
local Shorad = Shorad or nil
|
local Shorad = Shorad or nil
|
||||||
local ShoradTime = Shoradtime or 600
|
local ShoradTime = Shoradtime or 600
|
||||||
local ShoradLink = true
|
local ShoradLink = true
|
||||||
@@ -785,33 +940,30 @@ do
|
|||||||
self.Shorad = Shorad --#SHORAD
|
self.Shorad = Shorad --#SHORAD
|
||||||
self.ShoradTime = Shoradtime -- #number
|
self.ShoradTime = Shoradtime -- #number
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to unlink #MANTIS from a #SHORAD installation
|
--- Function to unlink #MANTIS from a #SHORAD installation
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
function MANTIS:RemoveShorad()
|
function MANTIS:RemoveShorad()
|
||||||
|
self:T(self.lid.."RemoveShorad")
|
||||||
self.ShoradLink = false
|
self.ShoradLink = false
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
-- MANTIS main functions
|
-- MANTIS main functions
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
--- Function to set the SAM start state
|
--- [Internal] Check detection function
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
|
-- @param Functional.Detection#DETECTION_AREAS detection Detection object
|
||||||
-- @return #MANTIS self
|
-- @return #MANTIS self
|
||||||
function MANTIS:Start()
|
function MANTIS:_Check(detection)
|
||||||
self:F(self.lid.."Starting MANTIS")
|
self:T(self.lid .. "Check")
|
||||||
self:SetSAMStartState()
|
|
||||||
self.Detection = self:StartDetection()
|
|
||||||
if self.advAwacs then
|
|
||||||
self.AWACS_Detection = self:StartAwacsDetection()
|
|
||||||
end
|
|
||||||
-- detection function
|
|
||||||
local function check(detection)
|
|
||||||
--get detected set
|
--get detected set
|
||||||
local detset = detection:GetDetectedItemCoordinates()
|
local detset = detection:GetDetectedItemCoordinates()
|
||||||
self:F("Check:", {detset})
|
self:T("Check:", {detset})
|
||||||
-- randomly update SAM Table
|
-- randomly update SAM Table
|
||||||
local rand = math.random(1,100)
|
local rand = math.random(1,100)
|
||||||
if rand > 65 then -- 1/3 of cases
|
if rand > 65 then -- 1/3 of cases
|
||||||
@@ -833,6 +985,10 @@ do
|
|||||||
samgroup:EnableEmission(true)
|
samgroup:EnableEmission(true)
|
||||||
end
|
end
|
||||||
samgroup:OptionAlarmStateRed()
|
samgroup:OptionAlarmStateRed()
|
||||||
|
if self.SamStateTracker[name] ~= "RED" then
|
||||||
|
self:__RedState(1,samgroup)
|
||||||
|
self.SamStateTracker[name] = "RED"
|
||||||
|
end
|
||||||
-- link in to SHORAD if available
|
-- link in to SHORAD if available
|
||||||
-- DONE: Test integration fully
|
-- DONE: Test integration fully
|
||||||
if self.ShoradLink and Distance < self.ShoradActDistance then -- don't give SHORAD position away too early
|
if self.ShoradLink and Distance < self.ShoradActDistance then -- don't give SHORAD position away too early
|
||||||
@@ -840,51 +996,55 @@ do
|
|||||||
local radius = self.checkradius
|
local radius = self.checkradius
|
||||||
local ontime = self.ShoradTime
|
local ontime = self.ShoradTime
|
||||||
Shorad:WakeUpShorad(name, radius, ontime)
|
Shorad:WakeUpShorad(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)
|
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||||
if self.verbose then env.info(self.lid..text) end
|
if self.verbose then self:I(self.lid..text) end
|
||||||
end --end alive
|
end --end alive
|
||||||
else
|
else
|
||||||
if samgroup:IsAlive() then
|
if samgroup:IsAlive() then
|
||||||
-- switch off SAM
|
-- switch off SAM
|
||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
-- TODO: add emissions on/off
|
|
||||||
samgroup:EnableEmission(false)
|
samgroup:EnableEmission(false)
|
||||||
--samgroup:SetAIOff()
|
|
||||||
else
|
|
||||||
samgroup:OptionAlarmStateGreen()
|
|
||||||
end
|
end
|
||||||
--samgroup:OptionROEWeaponFree()
|
samgroup:OptionAlarmStateGreen()
|
||||||
--samgroup:SetAIOn()
|
if self.SamStateTracker[name] ~= "GREEN" then
|
||||||
|
self:__GreenState(1,samgroup)
|
||||||
|
self.SamStateTracker[name] = "GREEN"
|
||||||
|
end
|
||||||
local text = string.format("SAM %s switched to alarm state GREEN!", name)
|
local text = string.format("SAM %s switched to alarm state GREEN!", name)
|
||||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||||
if self.verbose then env.info(self.lid..text) end
|
if self.verbose then self:I(self.lid..text) end
|
||||||
end --end alive
|
end --end alive
|
||||||
end --end check
|
end --end check
|
||||||
end --for for loop
|
end --for for loop
|
||||||
end --end function
|
return self
|
||||||
-- relocation relay function
|
|
||||||
local function relocate()
|
|
||||||
self:_RelocateGroups()
|
|
||||||
end
|
end
|
||||||
-- check advanced state
|
|
||||||
local function checkadvstate()
|
--- [Internal] Relocation relay function
|
||||||
local interval, oldstate = self:_CheckAdvState()
|
-- @param #MANTIS self
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:_Relocate()
|
||||||
|
self:T(self.lid .. "Relocate")
|
||||||
|
self:_RelocateGroups()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Check advanced state
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:_CheckAdvState()
|
||||||
|
self:T(self.lid .. "CheckAdvSate")
|
||||||
|
local interval, oldstate = self:_CalcAdvState()
|
||||||
local newstate = self.adv_state
|
local newstate = self.adv_state
|
||||||
if newstate ~= oldstate then
|
if newstate ~= oldstate then
|
||||||
-- deal with new state
|
-- deal with new state
|
||||||
|
self:__AdvStateChange(1,oldstate,newstate,interval)
|
||||||
if newstate == 2 then
|
if newstate == 2 then
|
||||||
-- switch alarm state RED
|
-- switch alarm state RED
|
||||||
if self.MantisTimer.isrunning then
|
self.state2flag = true
|
||||||
self.MantisTimer:Stop()
|
|
||||||
self.MantisTimer.isrunning = false
|
|
||||||
end -- stop Awacs timer
|
|
||||||
if self.MantisATimer.isrunning then
|
|
||||||
self.MantisATimer:Stop()
|
|
||||||
self.MantisATimer.isrunning = false
|
|
||||||
end -- stop timer
|
|
||||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
||||||
for _,_data in pairs (samset) do
|
for _,_data in pairs (samset) do
|
||||||
local name = _data[1]
|
local name = _data[1]
|
||||||
@@ -900,69 +1060,159 @@ do
|
|||||||
end -- end for loop
|
end -- end for loop
|
||||||
elseif newstate <= 1 then
|
elseif newstate <= 1 then
|
||||||
-- change MantisTimer to slow down or speed up
|
-- change MantisTimer to slow down or speed up
|
||||||
if self.MantisTimer.isrunning then
|
self.detectinterval = interval
|
||||||
self.MantisTimer:Stop()
|
self.state2flag = false
|
||||||
self.MantisTimer.isrunning = false
|
|
||||||
end
|
|
||||||
if self.MantisATimer.isrunning then
|
|
||||||
self.MantisATimer:Stop()
|
|
||||||
self.MantisATimer.isrunning = false
|
|
||||||
end
|
|
||||||
self.MantisTimer = TIMER:New(check,self.Detection)
|
|
||||||
self.MantisTimer:Start(5,interval,nil)
|
|
||||||
self.MantisTimer.isrunning = true
|
|
||||||
if self.advAwacs then
|
|
||||||
self.MantisATimer = TIMER:New(check,self.AWACS_Detection)
|
|
||||||
self.MantisATimer:Start(15,interval,nil)
|
|
||||||
self.MantisATimer.isrunning = true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end -- end newstate vs oldstate
|
end -- end newstate vs oldstate
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
-- timers to run the system
|
|
||||||
local interval = self.detectinterval
|
--- [Internal] Function to set start state
|
||||||
self.MantisTimer = TIMER:New(check,self.Detection)
|
-- @param #MANTIS self
|
||||||
self.MantisTimer:Start(5,interval,nil)
|
-- @param #string From The From State
|
||||||
self.MantisTimer.isrunning = true
|
-- @param #string Event The Event
|
||||||
-- Awacs timer
|
-- @param #string To The To State
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onafterStart(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
self:T(self.lid.."Starting MANTIS")
|
||||||
|
self:SetSAMStartState()
|
||||||
|
self.Detection = self:StartDetection()
|
||||||
if self.advAwacs then
|
if self.advAwacs then
|
||||||
self.MantisATimer = TIMER:New(check,self.AWACS_Detection)
|
self.AWACS_Detection = self:StartAwacsDetection()
|
||||||
self.MantisATimer:Start(15,interval,nil)
|
|
||||||
self.MantisATimer.isrunning = true
|
|
||||||
end
|
end
|
||||||
-- timer to relocate HQ and EWR
|
self:__Status(self.detectinterval)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Before status function for MANTIS
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onbeforeStatus(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
-- check detection
|
||||||
|
if not self.state2flag then
|
||||||
|
self:_Check(self.Detection)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check Awacs
|
||||||
|
if self.advAwacs and not self.state2flag then
|
||||||
|
self:_Check(self.AWACS_Detection)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- relocate HQ and EWR
|
||||||
if self.autorelocate then
|
if self.autorelocate then
|
||||||
local relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
local relointerval = self.relointerval
|
||||||
self.MantisReloTimer = TIMER:New(relocate)
|
local thistime = timer.getAbsTime()
|
||||||
self.MantisReloTimer:Start(relointerval,relointerval,nil)
|
local timepassed = thistime - self.TimeStamp
|
||||||
|
|
||||||
|
local halfintv = math.floor(timepassed / relointerval)
|
||||||
|
|
||||||
|
--self:T({timepassed=timepassed, halfintv=halfintv})
|
||||||
|
|
||||||
|
if halfintv >= 1 then
|
||||||
|
self.TimeStamp = timer.getAbsTime()
|
||||||
|
self:_Relocate()
|
||||||
|
self:__Relocating(1)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- timer for advanced state check
|
-- timer for advanced state check
|
||||||
if self.advanced then
|
if self.advanced then
|
||||||
self.MantisAdvTimer = TIMER:New(checkadvstate)
|
self:_CheckAdvState()
|
||||||
self.MantisAdvTimer:Start(30,interval*5,nil)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to stop MANTIS
|
--- [Internal] Status function for MANTIS
|
||||||
-- @param #MANTIS self
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
-- @return #MANTIS self
|
-- @return #MANTIS self
|
||||||
function MANTIS:Stop()
|
function MANTIS:onafterStatus(From,Event,To)
|
||||||
if self.MantisTimer.isrunning then
|
self:T({From, Event, To})
|
||||||
self.MantisTimer:Stop()
|
local interval = self.detectinterval * -1
|
||||||
end
|
self:__Status(interval)
|
||||||
if self.MantisATimer.isrunning then
|
|
||||||
self.MantisATimer:Stop()
|
|
||||||
end
|
|
||||||
if self.autorelocate then
|
|
||||||
self.MantisReloTimer:Stop()
|
|
||||||
end
|
|
||||||
if self.advanced then
|
|
||||||
self.MantisAdvTimer:Stop()
|
|
||||||
end
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function to stop MANTIS
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onafterStop(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function triggered by Event Relocating
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onafterRelocating(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function triggered by Event GreenState
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onafterGreenState(From, Event, To, Group)
|
||||||
|
self:T({From, Event, To, Group})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function triggered by Event RedState
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onafterRedState(From, Event, To, Group)
|
||||||
|
self:T({From, Event, To, Group})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function triggered by Event AdvStateChange
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param #number Oldstate Old state - 0 = green, 1 = amber, 2 = red
|
||||||
|
-- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red
|
||||||
|
-- @param #number Interval Calculated detection interval based on state and advanced feature setting
|
||||||
|
-- @return #MANTIS self
|
||||||
|
function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval)
|
||||||
|
self:T({From, Event, To, Oldstate, Newstate, Interval})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function triggered by Event ShoradActivated
|
||||||
|
-- @param #MANTIS self
|
||||||
|
-- @param #string From The From State
|
||||||
|
-- @param #string Event The Event
|
||||||
|
-- @param #string To The To State
|
||||||
|
-- @param #string Name Name of the GROUP which SHORAD shall protect
|
||||||
|
-- @param #number Radius Radius around the named group to find SHORAD groups
|
||||||
|
-- @param #number Ontime Seconds the SHORAD will stay active
|
||||||
|
function MANTIS:onafterShoradActivated(From, Event, To, Name, Radius, Ontime)
|
||||||
|
self:T({From, Event, To, Name, Radius, Ontime})
|
||||||
|
return self
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
-- MANTIS end
|
-- MANTIS end
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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()))
|
||||||
|
|
||||||
@@ -5786,6 +5786,8 @@ end
|
|||||||
-- If desired, the @{#RATMANAGER} can be stopped by the @{#RATMANAGER.Stop}(stoptime) function. The parameter "stoptime" specifies the time delay in seconds after which the manager stops.
|
-- If desired, the @{#RATMANAGER} can be stopped by the @{#RATMANAGER.Stop}(stoptime) function. The parameter "stoptime" specifies the time delay in seconds after which the manager stops.
|
||||||
-- When this happens, no new aircraft will be spawned and the population will eventually decrease to zero.
|
-- When this happens, no new aircraft will be spawned and the population will eventually decrease to zero.
|
||||||
--
|
--
|
||||||
|
-- When you are using a time intervall like @{#RATMANAGER.dTspawn}(delay), @{#RATMANAGER} will ignore the amount set with @{#RATMANAGER.New}(). @{#RATMANAGER.dTspawn}(delay) will spawn infinite groups.
|
||||||
|
--
|
||||||
-- ## Example
|
-- ## Example
|
||||||
-- In this example, three different @{#RAT} objects are created (but not spawned manually). The @{#RATMANAGER} takes care that at least five aircraft of each type are alive and that the total number of aircraft
|
-- In this example, three different @{#RAT} objects are created (but not spawned manually). The @{#RATMANAGER} takes care that at least five aircraft of each type are alive and that the total number of aircraft
|
||||||
-- spawned is 25. The @{#RATMANAGER} is started after 30 seconds and stopped after two hours.
|
-- spawned is 25. The @{#RATMANAGER} is started after 30 seconds and stopped after two hours.
|
||||||
|
|||||||
@@ -32,11 +32,13 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Missions: Example missions will be added later.
|
-- ## Missions:
|
||||||
|
--
|
||||||
|
-- * [MAR - On the Range - MOOSE - SC](https://www.digitalcombatsimulator.com/en/files/3317765/) by shagrat
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Sound files: Check out the pinned messages in the Moose discord *#func-range* channel.
|
-- ## Sound files: [MOOSE Sound Files](https://github.com/FlightControl-Master/MOOSE_SOUND/releases)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -91,9 +93,9 @@
|
|||||||
-- @field #boolean defaultsmokebomb If true, initialize player settings to smoke bomb.
|
-- @field #boolean defaultsmokebomb If true, initialize player settings to smoke bomb.
|
||||||
-- @field #boolean autosave If true, automatically save results every X seconds.
|
-- @field #boolean autosave If true, automatically save results every X seconds.
|
||||||
-- @field #number instructorfreq Frequency on which the range control transmitts.
|
-- @field #number instructorfreq Frequency on which the range control transmitts.
|
||||||
-- @field Core.RadioQueue#RADIOQUEUE instructor Instructor radio queue.
|
-- @field Sound.RadioQueue#RADIOQUEUE instructor Instructor radio queue.
|
||||||
-- @field #number rangecontrolfreq Frequency on which the range control transmitts.
|
-- @field #number rangecontrolfreq Frequency on which the range control transmitts.
|
||||||
-- @field Core.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue.
|
-- @field Sound.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue.
|
||||||
-- @field #string rangecontrolrelayname Name of relay unit.
|
-- @field #string rangecontrolrelayname Name of relay unit.
|
||||||
-- @field #string instructorrelayname Name of relay unit.
|
-- @field #string instructorrelayname Name of relay unit.
|
||||||
-- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
|
-- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Authors: **FlightControl**, **applevangelist**
|
-- ### Authors: **FlightControl**, **applevangelist**
|
||||||
--
|
--
|
||||||
-- Last Update: April 2021
|
-- Last Update: July 2021
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -51,26 +51,9 @@ SEAD = {
|
|||||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||||
}
|
}
|
||||||
|
|
||||||
-- TODO Complete list?
|
|
||||||
--- Missile enumerators
|
--- Missile enumerators
|
||||||
-- @field Harms
|
-- @field Harms
|
||||||
SEAD.Harms = {
|
SEAD.Harms = {
|
||||||
--[[
|
|
||||||
["X58"] = "weapons.missiles.X_58", --Kh-58X anti-radiation missiles fired
|
|
||||||
["Kh25"] = "weapons.missiles.Kh25MP_PRGS1VP", --Kh-25MP anti-radiation missiles fired
|
|
||||||
["X25"] = "weapons.missiles.X_25MP", --Kh-25MPU anti-radiation missiles fired
|
|
||||||
["X28"] = "weapons.missiles.X_28", --Kh-28 anti-radiation missiles fired
|
|
||||||
["X31"] = "weapons.missiles.X_31P", --Kh-31P anti-radiation missiles fired
|
|
||||||
["AGM45A"] = "weapons.missiles.AGM_45A", --AGM-45A anti-radiation missiles fired
|
|
||||||
["AGM45"] = "weapons.missiles.AGM_45", --AGM-45B anti-radiation missiles fired
|
|
||||||
["AGM88"] = "weapons.missiles.AGM_88", --AGM-88C anti-radiation missiles fired
|
|
||||||
["AGM122"] = "weapons.missiles.AGM_122", --AGM-122 Sidearm anti-radiation missiles fired
|
|
||||||
["LD10"] = "weapons.missiles.LD-10", --LD-10 anti-radiation missiles fired
|
|
||||||
["ALARM"] = "weapons.missiles.ALARM", --ALARM anti-radiation missiles fired
|
|
||||||
["AGM84E"] = "weapons.missiles.AGM_84E", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84A"] = "weapons.missiles.AGM_84A", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84H"] = "weapons.missiles.AGM_84H", --AGM84 anti-radiation missiles fired
|
|
||||||
--]]
|
|
||||||
["AGM_88"] = "AGM_88",
|
["AGM_88"] = "AGM_88",
|
||||||
["AGM_45"] = "AGM_45",
|
["AGM_45"] = "AGM_45",
|
||||||
["AGM_122"] = "AGM_122",
|
["AGM_122"] = "AGM_122",
|
||||||
@@ -108,8 +91,8 @@ function SEAD:New( SEADGroupPrefixes )
|
|||||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||||
end
|
end
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Shot )
|
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||||
self:I("*** SEAD - Started Version 0.2.7")
|
self:I("*** SEAD - Started Version 0.2.9")
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -119,7 +102,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
|
||||||
@@ -137,7 +120,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
|
||||||
@@ -152,7 +135,7 @@ end
|
|||||||
-- @param #string WeaponName
|
-- @param #string WeaponName
|
||||||
-- @return #boolean Returns true for a match
|
-- @return #boolean Returns true for a match
|
||||||
function SEAD:_CheckHarms(WeaponName)
|
function SEAD:_CheckHarms(WeaponName)
|
||||||
self:F( { WeaponName } )
|
self:T( { WeaponName } )
|
||||||
local hit = false
|
local hit = false
|
||||||
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 end
|
||||||
@@ -164,7 +147,7 @@ end
|
|||||||
-- @see SEAD
|
-- @see SEAD
|
||||||
-- @param #SEAD
|
-- @param #SEAD
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function SEAD:OnEventShot( EventData )
|
function SEAD:HandleEventShot( EventData )
|
||||||
self:T( { EventData } )
|
self:T( { EventData } )
|
||||||
|
|
||||||
local SEADUnit = EventData.IniDCSUnit
|
local SEADUnit = EventData.IniDCSUnit
|
||||||
@@ -175,35 +158,6 @@ function SEAD:OnEventShot( EventData )
|
|||||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||||
self:T({ SEADWeapon })
|
self:T({ SEADWeapon })
|
||||||
|
|
||||||
--[[check for SEAD missiles
|
|
||||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.X_28" --Kh-28 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.X_31P" --Kh-31P anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_45A" --AGM-45A anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_45" --AGM-45B anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_88" --AGM-88C anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.LD-10" --LD-10 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_84E" --AGM84 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_84A" --AGM84 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired
|
|
||||||
--]]
|
|
||||||
if self:_CheckHarms(SEADWeaponName) then
|
if self:_CheckHarms(SEADWeaponName) then
|
||||||
local _targetskill = "Random"
|
local _targetskill = "Random"
|
||||||
local _targetMimgroupName = "none"
|
local _targetMimgroupName = "none"
|
||||||
@@ -212,7 +166,7 @@ function SEAD:OnEventShot( EventData )
|
|||||||
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
||||||
if _targetUnit and _targetUnit:IsAlive() then
|
if _targetUnit and _targetUnit:IsAlive() then
|
||||||
local _targetMimgroup = _targetUnit:GetGroup()
|
local _targetMimgroup = _targetUnit:GetGroup()
|
||||||
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
_targetMimgroupName = _targetMimgroup:GetName() -- group name
|
||||||
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
||||||
self:T( self.SEADGroupPrefixes )
|
self:T( self.SEADGroupPrefixes )
|
||||||
self:T( _targetMimgroupName )
|
self:T( _targetMimgroupName )
|
||||||
@@ -220,6 +174,7 @@ function SEAD:OnEventShot( EventData )
|
|||||||
-- 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
|
||||||
|
self:T( SEADGroupPrefix )
|
||||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
||||||
SEADGroupFound = true
|
SEADGroupFound = true
|
||||||
self:T( '*** SEAD - Group Found' )
|
self:T( '*** SEAD - Group Found' )
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
-- @module Functional.Shorad
|
-- @module Functional.Shorad
|
||||||
-- @image Functional.Shorad.jpg
|
-- @image Functional.Shorad.jpg
|
||||||
--
|
--
|
||||||
-- Date: May 2021
|
-- Date: July 2021
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **SHORAD** class, extends Core.Base#BASE
|
--- **SHORAD** class, extends Core.Base#BASE
|
||||||
@@ -108,22 +108,6 @@ do
|
|||||||
--- Missile enumerators
|
--- Missile enumerators
|
||||||
-- @field Harms
|
-- @field Harms
|
||||||
SHORAD.Harms = {
|
SHORAD.Harms = {
|
||||||
--[[
|
|
||||||
["X58"] = "weapons.missiles.X_58", --Kh-58X anti-radiation missiles fired
|
|
||||||
["Kh25"] = "weapons.missiles.Kh25MP_PRGS1VP", --Kh-25MP anti-radiation missiles fired
|
|
||||||
["X25"] = "weapons.missiles.X_25MP", --Kh-25MPU anti-radiation missiles fired
|
|
||||||
["X28"] = "weapons.missiles.X_28", --Kh-28 anti-radiation missiles fired
|
|
||||||
["X31"] = "weapons.missiles.X_31P", --Kh-31P anti-radiation missiles fired
|
|
||||||
["AGM45A"] = "weapons.missiles.AGM_45A", --AGM-45A anti-radiation missiles fired
|
|
||||||
["AGM45"] = "weapons.missiles.AGM_45", --AGM-45B anti-radiation missiles fired
|
|
||||||
["AGM88"] = "weapons.missiles.AGM_88", --AGM-88C anti-radiation missiles fired
|
|
||||||
["AGM122"] = "weapons.missiles.AGM_122", --AGM-122 Sidearm anti-radiation missiles fired
|
|
||||||
["LD10"] = "weapons.missiles.LD-10", --LD-10 anti-radiation missiles fired
|
|
||||||
["ALARM"] = "weapons.missiles.ALARM", --ALARM anti-radiation missiles fired
|
|
||||||
["AGM84E"] = "weapons.missiles.AGM_84E", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84A"] = "weapons.missiles.AGM_84A", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84H"] = "weapons.missiles.AGM_84H", --AGM84 anti-radiation missiles fired
|
|
||||||
--]]
|
|
||||||
["AGM_88"] = "AGM_88",
|
["AGM_88"] = "AGM_88",
|
||||||
["AGM_45"] = "AGM_45",
|
["AGM_45"] = "AGM_45",
|
||||||
["AGM_122"] = "AGM_122",
|
["AGM_122"] = "AGM_122",
|
||||||
@@ -157,7 +141,9 @@ do
|
|||||||
-- @param #number Radius Defense radius in meters, used to switch on groups
|
-- @param #number Radius Defense radius in meters, used to switch on groups
|
||||||
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||||
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
||||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition)
|
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
||||||
|
-- @retunr #SHORAD self
|
||||||
|
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
||||||
|
|
||||||
@@ -171,22 +157,23 @@ do
|
|||||||
self.ActiveTimer = ActiveTimer or 600
|
self.ActiveTimer = ActiveTimer or 600
|
||||||
self.ActiveGroups = {}
|
self.ActiveGroups = {}
|
||||||
self.Groupset = GroupSet
|
self.Groupset = GroupSet
|
||||||
self:HandleEvent( EVENTS.Shot )
|
|
||||||
self.DefendHarms = true
|
self.DefendHarms = true
|
||||||
self.DefendMavs = true
|
self.DefendMavs = true
|
||||||
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
||||||
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
||||||
self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||||
self:I("*** SHORAD - Started Version 0.2.5")
|
self:I("*** SHORAD - Started Version 0.2.8")
|
||||||
-- Set the string id for output to DCS.log file.
|
-- Set the string id for output to DCS.log file.
|
||||||
self.lid=string.format("SHORAD %s | ", self.name)
|
self.lid=string.format("SHORAD %s | ", self.name)
|
||||||
self:_InitState()
|
self:_InitState()
|
||||||
|
self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Initially set all groups to alarm state GREEN
|
--- Initially set all groups to alarm state GREEN
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
function SHORAD:_InitState()
|
function SHORAD:_InitState()
|
||||||
|
self:T(self.lid .. " _InitState")
|
||||||
local table = {}
|
local table = {}
|
||||||
local set = self.Groupset
|
local set = self.Groupset
|
||||||
self:T({set = set})
|
self:T({set = set})
|
||||||
@@ -195,31 +182,48 @@ do
|
|||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
--_group:SetAIOff()
|
--_group:SetAIOff()
|
||||||
_group:EnableEmission(false)
|
_group:EnableEmission(false)
|
||||||
|
_group:OptionAlarmStateRed() --Wrapper.Group#GROUP
|
||||||
else
|
else
|
||||||
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
|
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
|
||||||
end
|
end
|
||||||
|
_group:OptionDisperseOnAttack(30)
|
||||||
end
|
end
|
||||||
-- gather entropy
|
-- gather entropy
|
||||||
for i=1,10 do
|
for i=1,100 do
|
||||||
math.random()
|
math.random()
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Switch debug state
|
--- Switch debug state on
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #boolean debug Switch debug on (true) or off (false)
|
-- @param #boolean debug Switch debug on (true) or off (false)
|
||||||
function SHORAD:SwitchDebug(debug)
|
function SHORAD:SwitchDebug(onoff)
|
||||||
self:T( { debug } )
|
self:T( { onoff } )
|
||||||
local onoff = debug or false
|
if onoff then
|
||||||
if debug then
|
self:SwitchDebugOn()
|
||||||
|
else
|
||||||
|
self.SwitchDebugOff()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch debug state on
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:SwitchDebugOn()
|
||||||
self.debug = true
|
self.debug = true
|
||||||
--tracing
|
--tracing
|
||||||
BASE:TraceOn()
|
BASE:TraceOn()
|
||||||
BASE:TraceClass("SHORAD")
|
BASE:TraceClass("SHORAD")
|
||||||
else
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch debug state off
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:SwitchDebugOff()
|
||||||
self.debug = false
|
self.debug = false
|
||||||
BASE:TraceOff()
|
BASE:TraceOff()
|
||||||
end
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Switch defense for HARMs
|
--- Switch defense for HARMs
|
||||||
@@ -229,6 +233,7 @@ do
|
|||||||
self:T( { onoff } )
|
self:T( { onoff } )
|
||||||
local onoff = onoff or true
|
local onoff = onoff or true
|
||||||
self.DefendHarms = onoff
|
self.DefendHarms = onoff
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Switch defense for AGMs
|
--- Switch defense for AGMs
|
||||||
@@ -238,6 +243,7 @@ do
|
|||||||
self:T( { onoff } )
|
self:T( { onoff } )
|
||||||
local onoff = onoff or true
|
local onoff = onoff or true
|
||||||
self.DefendMavs = onoff
|
self.DefendMavs = onoff
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set defense probability limits
|
--- Set defense probability limits
|
||||||
@@ -256,35 +262,42 @@ do
|
|||||||
end
|
end
|
||||||
self.DefenseLowProb = low
|
self.DefenseLowProb = low
|
||||||
self.DefenseHighProb = high
|
self.DefenseHighProb = high
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the number of seconds a SHORAD site will stay active
|
--- Set the number of seconds a SHORAD site will stay active
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #number seconds Number of seconds systems stay active
|
-- @param #number seconds Number of seconds systems stay active
|
||||||
function SHORAD:SetActiveTimer(seconds)
|
function SHORAD:SetActiveTimer(seconds)
|
||||||
|
self:T(self.lid .. " SetActiveTimer")
|
||||||
local timer = seconds or 600
|
local timer = seconds or 600
|
||||||
if timer < 0 then
|
if timer < 0 then
|
||||||
timer = 600
|
timer = 600
|
||||||
end
|
end
|
||||||
self.ActiveTimer = timer
|
self.ActiveTimer = timer
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the number of meters for the SHORAD defense zone
|
--- Set the number of meters for the SHORAD defense zone
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
|
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
|
||||||
function SHORAD:SetDefenseRadius(meters)
|
function SHORAD:SetDefenseRadius(meters)
|
||||||
|
self:T(self.lid .. " SetDefenseRadius")
|
||||||
local radius = meters or 20000
|
local radius = meters or 20000
|
||||||
if radius < 0 then
|
if radius < 0 then
|
||||||
radius = 20000
|
radius = 20000
|
||||||
end
|
end
|
||||||
self.Radius = radius
|
self.Radius = radius
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set using Emission on/off instead of changing alarm state
|
--- Set using Emission on/off instead of changing alarm state
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #boolean switch Decide if we are changing alarm state or AI state
|
-- @param #boolean switch Decide if we are changing alarm state or AI state
|
||||||
function SHORAD:SetUsingEmOnOff(switch)
|
function SHORAD:SetUsingEmOnOff(switch)
|
||||||
|
self:T(self.lid .. " SetUsingEmOnOff")
|
||||||
self.UseEmOnOff = switch or false
|
self.UseEmOnOff = switch or false
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if a HARM was fired
|
--- Check if a HARM was fired
|
||||||
@@ -292,6 +305,7 @@ do
|
|||||||
-- @param #string WeaponName
|
-- @param #string WeaponName
|
||||||
-- @return #boolean Returns true for a match
|
-- @return #boolean Returns true for a match
|
||||||
function SHORAD:_CheckHarms(WeaponName)
|
function SHORAD:_CheckHarms(WeaponName)
|
||||||
|
self:T(self.lid .. " _CheckHarms")
|
||||||
self:T( { WeaponName } )
|
self:T( { WeaponName } )
|
||||||
local hit = false
|
local hit = false
|
||||||
if self.DefendHarms then
|
if self.DefendHarms then
|
||||||
@@ -307,6 +321,7 @@ do
|
|||||||
-- @param #string WeaponName
|
-- @param #string WeaponName
|
||||||
-- @return #boolean Returns true for a match
|
-- @return #boolean Returns true for a match
|
||||||
function SHORAD:_CheckMavs(WeaponName)
|
function SHORAD:_CheckMavs(WeaponName)
|
||||||
|
self:T(self.lid .. " _CheckMavs")
|
||||||
self:T( { WeaponName } )
|
self:T( { WeaponName } )
|
||||||
local hit = false
|
local hit = false
|
||||||
if self.DefendMavs then
|
if self.DefendMavs then
|
||||||
@@ -322,6 +337,7 @@ do
|
|||||||
-- @param #string Coalition name
|
-- @param #string Coalition name
|
||||||
-- @return #boolean Returns false for a match
|
-- @return #boolean Returns false for a match
|
||||||
function SHORAD:_CheckCoalition(Coalition)
|
function SHORAD:_CheckCoalition(Coalition)
|
||||||
|
self:T(self.lid .. " _CheckCoalition")
|
||||||
local owncoalition = self.Coalition
|
local owncoalition = self.Coalition
|
||||||
local othercoalition = ""
|
local othercoalition = ""
|
||||||
if Coalition == 0 then
|
if Coalition == 0 then
|
||||||
@@ -344,6 +360,7 @@ do
|
|||||||
-- @param #string TargetGroupName Name of the target group
|
-- @param #string TargetGroupName Name of the target group
|
||||||
-- @return #boolean Returns true for a match, else false
|
-- @return #boolean Returns true for a match, else false
|
||||||
function SHORAD:_CheckShotAtShorad(TargetGroupName)
|
function SHORAD:_CheckShotAtShorad(TargetGroupName)
|
||||||
|
self:T(self.lid .. " _CheckShotAtShorad")
|
||||||
local tgtgrp = TargetGroupName
|
local tgtgrp = TargetGroupName
|
||||||
local shorad = self.Groupset
|
local shorad = self.Groupset
|
||||||
local shoradset = shorad:GetAliveSet() --#table
|
local shoradset = shorad:GetAliveSet() --#table
|
||||||
@@ -352,7 +369,7 @@ do
|
|||||||
local groupname = _groups:GetName()
|
local groupname = _groups:GetName()
|
||||||
if string.find(groupname, tgtgrp, 1) then
|
if string.find(groupname, tgtgrp, 1) then
|
||||||
returnname = true
|
returnname = true
|
||||||
_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return returnname
|
return returnname
|
||||||
@@ -363,6 +380,7 @@ do
|
|||||||
-- @param #string TargetGroupName Name of the target group
|
-- @param #string TargetGroupName Name of the target group
|
||||||
-- @return #boolean Returns true for a match, else false
|
-- @return #boolean Returns true for a match, else false
|
||||||
function SHORAD:_CheckShotAtSams(TargetGroupName)
|
function SHORAD:_CheckShotAtSams(TargetGroupName)
|
||||||
|
self:T(self.lid .. " _CheckShotAtSams")
|
||||||
local tgtgrp = TargetGroupName
|
local tgtgrp = TargetGroupName
|
||||||
local shorad = self.Samset
|
local shorad = self.Samset
|
||||||
--local shoradset = shorad:GetAliveSet() --#table
|
--local shoradset = shorad:GetAliveSet() --#table
|
||||||
@@ -381,6 +399,7 @@ do
|
|||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @return #boolean Returns true for a detection, else false
|
-- @return #boolean Returns true for a detection, else false
|
||||||
function SHORAD:_ShotIsDetected()
|
function SHORAD:_ShotIsDetected()
|
||||||
|
self:T(self.lid .. " _ShotIsDetected")
|
||||||
local IsDetected = false
|
local IsDetected = false
|
||||||
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
||||||
local ActualDetection = math.random(1,100) -- value for this shot
|
local ActualDetection = math.random(1,100) -- value for this shot
|
||||||
@@ -405,6 +424,7 @@ do
|
|||||||
-- mymantis:AddShorad(myshorad,720)
|
-- mymantis:AddShorad(myshorad,720)
|
||||||
-- mymantis:Start()
|
-- mymantis:Start()
|
||||||
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
|
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
|
||||||
|
self:T(self.lid .. " WakeUpShorad")
|
||||||
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||||
local targetcat = TargetCat or Object.Category.UNIT
|
local targetcat = TargetCat or Object.Category.UNIT
|
||||||
local targetgroup = TargetGroup
|
local targetgroup = TargetGroup
|
||||||
@@ -442,7 +462,7 @@ do
|
|||||||
self:T(text)
|
self:T(text)
|
||||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
_group:SetAIOn()
|
--_group:SetAIOn()
|
||||||
_group:EnableEmission(true)
|
_group:EnableEmission(true)
|
||||||
end
|
end
|
||||||
_group:OptionAlarmStateRed()
|
_group:OptionAlarmStateRed()
|
||||||
@@ -454,14 +474,15 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Main function - work on the EventData
|
--- Main function - work on the EventData
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||||
function SHORAD:OnEventShot( EventData )
|
function SHORAD:HandleEventShot( EventData )
|
||||||
self:T( { EventData } )
|
self:T( { EventData } )
|
||||||
|
self:T(self.lid .. " HandleEventShot")
|
||||||
--local ShootingUnit = EventData.IniDCSUnit
|
--local ShootingUnit = EventData.IniDCSUnit
|
||||||
--local ShootingUnitName = EventData.IniDCSUnitName
|
--local ShootingUnitName = EventData.IniDCSUnitName
|
||||||
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- The order of the declarations is important here. Don't touch it.
|
--- GLOBALS: The order of the declarations is important here. Don't touch it.
|
||||||
|
|
||||||
|
|
||||||
--- Declare the event dispatcher based on the EVENT class
|
--- Declare the event dispatcher based on the EVENT class
|
||||||
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
|
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
|
||||||
@@ -10,9 +9,37 @@ _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDU
|
|||||||
--- Declare the main database object, which is used internally by the MOOSE classes.
|
--- Declare the main database object, which is used internally by the MOOSE classes.
|
||||||
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
||||||
|
|
||||||
|
--- Settings
|
||||||
_SETTINGS = SETTINGS:Set()
|
_SETTINGS = SETTINGS:Set()
|
||||||
_SETTINGS:SetPlayerMenuOn()
|
_SETTINGS:SetPlayerMenuOn()
|
||||||
|
|
||||||
|
--- Register cargos.
|
||||||
_DATABASE:_RegisterCargos()
|
_DATABASE:_RegisterCargos()
|
||||||
|
|
||||||
|
--- Register zones.
|
||||||
_DATABASE:_RegisterZones()
|
_DATABASE:_RegisterZones()
|
||||||
|
|
||||||
|
--- Check if os etc is available.
|
||||||
|
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||||
|
local __na=false
|
||||||
|
if os then
|
||||||
|
BASE:I("- os available")
|
||||||
|
else
|
||||||
|
BASE:I("- os NOT available! Some functions may not work.")
|
||||||
|
__na=true
|
||||||
|
end
|
||||||
|
if io then
|
||||||
|
BASE:I("- io available")
|
||||||
|
else
|
||||||
|
BASE:I("- io NOT available! Some functions may not work.")
|
||||||
|
__na=true
|
||||||
|
end
|
||||||
|
if lfs then
|
||||||
|
BASE:I("- lfs available")
|
||||||
|
else
|
||||||
|
BASE:I("- lfs NOT available! Some functions may not work.")
|
||||||
|
__na=true
|
||||||
|
end
|
||||||
|
if __na then
|
||||||
|
BASE:I("Check <DCS install folder>/Scripts/MissionScripting.lua and comment out the lines with sanitizeModule(''). Use at your own risk!)")
|
||||||
|
end
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ __Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/UserSound.lua' )
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Report.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Report.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/ScheduleDispatcher.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/ScheduleDispatcher.lua' )
|
||||||
@@ -20,9 +22,6 @@ __Moose.Include( 'Scripts/Moose/Core/Point.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Radio.lua' )
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/RadioQueue.lua' )
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/RadioSpeech.lua' )
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
||||||
@@ -74,6 +73,8 @@ __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Ops/ATIS.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/ATIS.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
|
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
|
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
|
||||||
@@ -112,6 +113,13 @@ __Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' )
|
__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' )
|
__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' )
|
||||||
|
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/UserSound.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/SoundOutput.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/Radio.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/RadioQueue.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/RadioSpeech.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/SRS.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' )
|
__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' )
|
__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' )
|
__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' )
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
-- * Option to present information in imperial or metric units
|
-- * Option to present information in imperial or metric units
|
||||||
-- * Runway length and airfield elevation (optional)
|
-- * Runway length and airfield elevation (optional)
|
||||||
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional)
|
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional)
|
||||||
|
-- * SRS Simple-Text-To-Speech (STTS) integration (no sound files necessary)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Sound files: Check out the pinned messages in the Moose discord #ops-atis channel.
|
-- ## Sound files: [MOOSE Sound Files](https://github.com/FlightControl-Master/MOOSE_SOUND/releases)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
-- @field #number frequency Radio frequency in MHz.
|
-- @field #number frequency Radio frequency in MHz.
|
||||||
-- @field #number modulation Radio modulation 0=AM or 1=FM.
|
-- @field #number modulation Radio modulation 0=AM or 1=FM.
|
||||||
-- @field #number power Radio power in Watts. Default 100 W.
|
-- @field #number power Radio power in Watts. Default 100 W.
|
||||||
-- @field Core.RadioQueue#RADIOQUEUE radioqueue Radio queue for broadcasing messages.
|
-- @field Sound.RadioQueue#RADIOQUEUE radioqueue Radio queue for broadcasing messages.
|
||||||
-- @field #string soundpath Path to sound files.
|
-- @field #string soundpath Path to sound files.
|
||||||
-- @field #string relayunitname Name of the radio relay unit.
|
-- @field #string relayunitname Name of the radio relay unit.
|
||||||
-- @field #table towerfrequency Table with tower frequencies.
|
-- @field #table towerfrequency Table with tower frequencies.
|
||||||
@@ -88,6 +89,9 @@
|
|||||||
-- @field #boolean usemarker Use mark on the F10 map.
|
-- @field #boolean usemarker Use mark on the F10 map.
|
||||||
-- @field #number markerid Numerical ID of the F10 map mark point.
|
-- @field #number markerid Numerical ID of the F10 map mark point.
|
||||||
-- @field #number relHumidity Relative humidity (used to approximately calculate the dew point).
|
-- @field #number relHumidity Relative humidity (used to approximately calculate the dew point).
|
||||||
|
-- @field #boolean useSRS If true, use SRS for transmission.
|
||||||
|
-- @field Sound.SRS#MSRS msrs Moose SRS object.
|
||||||
|
-- @field #number dTQueueCheck Time interval to check the radio queue. Default 5 sec or 90 sec if SRS is used.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||||
@@ -253,6 +257,16 @@
|
|||||||
--
|
--
|
||||||
-- You can place marks on the F10 map via the @{#ATIS.SetMapMarks}() function. These will contain info about the ATIS frequency, the currently active runway and some basic info about the weather (wind, pressure and temperature).
|
-- You can place marks on the F10 map via the @{#ATIS.SetMapMarks}() function. These will contain info about the ATIS frequency, the currently active runway and some basic info about the weather (wind, pressure and temperature).
|
||||||
--
|
--
|
||||||
|
-- # Text-To-Speech
|
||||||
|
--
|
||||||
|
-- You can enable text-to-speech ATIS information with the @{#ATIS.SetSRS}() function. This uses [SRS](http://dcssimpleradio.com/) (Version >= 1.9.6.0) for broadcasing.
|
||||||
|
-- Advantages are that **no sound files** or radio relay units are necessary. Also the issue that FC3 aircraft hear all transmissions will be circumvented.
|
||||||
|
--
|
||||||
|
-- The @{#ATIS.SetSRS}() requires you to specify the path to the SRS install directory or more specifically the path to the DCS-SR-ExternalAudio.exe file.
|
||||||
|
--
|
||||||
|
-- Unfortunately, it is not possible to determine the duration of the complete transmission. So once the transmission is finished, there might be some radio silence before
|
||||||
|
-- the next iteration begins. You can fine tune the time interval between transmissions with the @{#ATIS.SetQueueUpdateTime}() function. The default interval is 90 seconds.
|
||||||
|
--
|
||||||
-- # Examples
|
-- # Examples
|
||||||
--
|
--
|
||||||
-- ## Caucasus: Batumi
|
-- ## Caucasus: Batumi
|
||||||
@@ -284,6 +298,13 @@
|
|||||||
-- atisAbuDhabi:SetVOR(114.25)
|
-- atisAbuDhabi:SetVOR(114.25)
|
||||||
-- atisAbuDhabi:Start()
|
-- atisAbuDhabi:Start()
|
||||||
--
|
--
|
||||||
|
-- ## SRS
|
||||||
|
--
|
||||||
|
-- atis=ATIS:New("Batumi", 305, radio.modulation.AM)
|
||||||
|
-- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US")
|
||||||
|
-- atis:Start()
|
||||||
|
--
|
||||||
|
-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux).
|
||||||
--
|
--
|
||||||
-- @field #ATIS
|
-- @field #ATIS
|
||||||
ATIS = {
|
ATIS = {
|
||||||
@@ -368,6 +389,7 @@ ATIS.Alphabet = {
|
|||||||
-- @field #number PersianGulf +2° (East).
|
-- @field #number PersianGulf +2° (East).
|
||||||
-- @field #number TheChannel -10° (West).
|
-- @field #number TheChannel -10° (West).
|
||||||
-- @field #number Syria +5° (East).
|
-- @field #number Syria +5° (East).
|
||||||
|
-- @field #number MarianaIslands +2° (East).
|
||||||
ATIS.RunwayM2T={
|
ATIS.RunwayM2T={
|
||||||
Caucasus=0,
|
Caucasus=0,
|
||||||
Nevada=12,
|
Nevada=12,
|
||||||
@@ -375,6 +397,7 @@ ATIS.RunwayM2T={
|
|||||||
PersianGulf=2,
|
PersianGulf=2,
|
||||||
TheChannel=-10,
|
TheChannel=-10,
|
||||||
Syria=5,
|
Syria=5,
|
||||||
|
MarianaIslands=2,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Whether ICAO phraseology is used for ATIS broadcasts.
|
--- Whether ICAO phraseology is used for ATIS broadcasts.
|
||||||
@@ -385,6 +408,7 @@ ATIS.RunwayM2T={
|
|||||||
-- @field #boolean PersianGulf true.
|
-- @field #boolean PersianGulf true.
|
||||||
-- @field #boolean TheChannel true.
|
-- @field #boolean TheChannel true.
|
||||||
-- @field #boolean Syria true.
|
-- @field #boolean Syria true.
|
||||||
|
-- @field #boolean MarianaIslands true.
|
||||||
ATIS.ICAOPhraseology={
|
ATIS.ICAOPhraseology={
|
||||||
Caucasus=true,
|
Caucasus=true,
|
||||||
Nevada=false,
|
Nevada=false,
|
||||||
@@ -392,6 +416,7 @@ ATIS.ICAOPhraseology={
|
|||||||
PersianGulf=true,
|
PersianGulf=true,
|
||||||
TheChannel=true,
|
TheChannel=true,
|
||||||
Syria=true,
|
Syria=true,
|
||||||
|
MarianaIslands=true,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Nav point data.
|
--- Nav point data.
|
||||||
@@ -564,7 +589,7 @@ _ATIS={}
|
|||||||
|
|
||||||
--- ATIS class version.
|
--- ATIS class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ATIS.version="0.9.1"
|
ATIS.version="0.9.6"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -628,6 +653,7 @@ function ATIS:New(airbasename, frequency, modulation)
|
|||||||
self:SetAltimeterQNH(true)
|
self:SetAltimeterQNH(true)
|
||||||
self:SetMapMarks(false)
|
self:SetMapMarks(false)
|
||||||
self:SetRelativeHumidity()
|
self:SetRelativeHumidity()
|
||||||
|
self:SetQueueUpdateTime()
|
||||||
|
|
||||||
-- Start State.
|
-- Start State.
|
||||||
self:SetStartState("Stopped")
|
self:SetStartState("Stopped")
|
||||||
@@ -955,7 +981,9 @@ end
|
|||||||
-- * 170° on the Normany map
|
-- * 170° on the Normany map
|
||||||
-- * 182° on the Persian Gulf map
|
-- * 182° on the Persian Gulf map
|
||||||
--
|
--
|
||||||
-- Likewise, to convert *magnetic* into *true* heading, one has to substract easterly and add westerly variation.
|
-- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation.
|
||||||
|
--
|
||||||
|
-- Or you make your life simple and just include the sign so you don't have to bother about East/West.
|
||||||
--
|
--
|
||||||
-- @param #ATIS self
|
-- @param #ATIS self
|
||||||
-- @param #number magvar Magnetic variation in degrees. Positive for easterly and negative for westerly variation. Default is magnatic declinaton of the used map, c.f. @{Utilities.UTils#UTILS.GetMagneticDeclination}.
|
-- @param #number magvar Magnetic variation in degrees. Positive for easterly and negative for westerly variation. Default is magnatic declinaton of the used map, c.f. @{Utilities.UTils#UTILS.GetMagneticDeclination}.
|
||||||
@@ -1100,6 +1128,44 @@ function ATIS:MarkRunways(markall)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.
|
||||||
|
-- @param #ATIS self
|
||||||
|
-- @param #string PathToSRS Path to SRS directory.
|
||||||
|
-- @param #string Gender Gender: "male" or "female" (default).
|
||||||
|
-- @param #string Culture Culture, e.g. "en-GB" (default).
|
||||||
|
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
|
||||||
|
-- @param #number Port SRS port. Default 5002.
|
||||||
|
-- @return #ATIS self
|
||||||
|
function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port)
|
||||||
|
self.useSRS=true
|
||||||
|
self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation)
|
||||||
|
self.msrs:SetGender(Gender)
|
||||||
|
self.msrs:SetCulture(Culture)
|
||||||
|
self.msrs:SetVoice(Voice)
|
||||||
|
self.msrs:SetPort(Port)
|
||||||
|
self.msrs:SetCoalition(self:GetCoalition())
|
||||||
|
if self.dTQueueCheck<=10 then
|
||||||
|
self:SetQueueUpdateTime(90)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the time interval between radio queue updates.
|
||||||
|
-- @param #ATIS self
|
||||||
|
-- @param #number TimeInterval Interval in seconds. Default 5 sec.
|
||||||
|
-- @return #ATIS self
|
||||||
|
function ATIS:SetQueueUpdateTime(TimeInterval)
|
||||||
|
self.dTQueueCheck=TimeInterval or 5
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the coalition of the associated airbase.
|
||||||
|
-- @param #ATIS self
|
||||||
|
-- @return #number Coalition of the associcated airbase.
|
||||||
|
function ATIS:GetCoalition()
|
||||||
|
local coal=self.airbase and self.airbase:GetCoalition() or nil
|
||||||
|
return coal
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Start & Status
|
-- Start & Status
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -1147,6 +1213,10 @@ function ATIS:onafterStart(From, Event, To)
|
|||||||
-- Start radio queue.
|
-- Start radio queue.
|
||||||
self.radioqueue:Start(1, 0.1)
|
self.radioqueue:Start(1, 0.1)
|
||||||
|
|
||||||
|
-- Handle airbase capture
|
||||||
|
-- Handle events.
|
||||||
|
self:HandleEvent(EVENTS.BaseCaptured)
|
||||||
|
|
||||||
-- Init status updates.
|
-- Init status updates.
|
||||||
self:__Status(-2)
|
self:__Status(-2)
|
||||||
self:__CheckQueue(-3)
|
self:__CheckQueue(-3)
|
||||||
@@ -1171,7 +1241,13 @@ function ATIS:onafterStatus(From, Event, To)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Info text.
|
-- Info text.
|
||||||
local text=string.format("State %s: Freq=%.3f MHz %s, Relay unit=%s (alive=%s)", fsmstate, self.frequency, UTILS.GetModulationName(self.modulation), tostring(self.relayunitname), relayunitstatus)
|
local text=string.format("State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName(self.modulation))
|
||||||
|
if self.useSRS then
|
||||||
|
text=text..string.format(", SRS path=%s (%s), gender=%s, culture=%s, voice=%s",
|
||||||
|
tostring(self.msrs.path), tostring(self.msrs.port), tostring(self.msrs.gender), tostring(self.msrs.culture), tostring(self.msrs.voice))
|
||||||
|
else
|
||||||
|
text=text..string.format(", Relay unit=%s (alive=%s)", tostring(self.relayunitname), relayunitstatus)
|
||||||
|
end
|
||||||
self:I(self.lid..text)
|
self:I(self.lid..text)
|
||||||
|
|
||||||
self:__Status(-60)
|
self:__Status(-60)
|
||||||
@@ -1188,6 +1264,12 @@ end
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
function ATIS:onafterCheckQueue(From, Event, To)
|
function ATIS:onafterCheckQueue(From, Event, To)
|
||||||
|
|
||||||
|
if self.useSRS then
|
||||||
|
|
||||||
|
self:Broadcast()
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
if #self.radioqueue.queue==0 then
|
if #self.radioqueue.queue==0 then
|
||||||
self:T(self.lid..string.format("Radio queue empty. Repeating message."))
|
self:T(self.lid..string.format("Radio queue empty. Repeating message."))
|
||||||
self:Broadcast()
|
self:Broadcast()
|
||||||
@@ -1195,8 +1277,12 @@ function ATIS:onafterCheckQueue(From, Event, To)
|
|||||||
self:T2(self.lid..string.format("Radio queue %d transmissions queued.", #self.radioqueue.queue))
|
self:T2(self.lid..string.format("Radio queue %d transmissions queued.", #self.radioqueue.queue))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
-- Check back in 5 seconds.
|
-- Check back in 5 seconds.
|
||||||
self:__CheckQueue(-5)
|
self:__CheckQueue(-math.abs(self.dTQueueCheck))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Broadcast ATIS radio message.
|
--- Broadcast ATIS radio message.
|
||||||
@@ -1327,6 +1413,9 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
local clock=UTILS.SecondsToClock(time)
|
local clock=UTILS.SecondsToClock(time)
|
||||||
local zulu=UTILS.Split(clock, ":")
|
local zulu=UTILS.Split(clock, ":")
|
||||||
local ZULU=string.format("%s%s", zulu[1], zulu[2])
|
local ZULU=string.format("%s%s", zulu[1], zulu[2])
|
||||||
|
if self.useSRS then
|
||||||
|
ZULU=string.format("%s hours", zulu[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc.
|
-- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc.
|
||||||
@@ -1346,10 +1435,17 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
local sunrise=coord:GetSunrise()
|
local sunrise=coord:GetSunrise()
|
||||||
sunrise=UTILS.Split(sunrise, ":")
|
sunrise=UTILS.Split(sunrise, ":")
|
||||||
local SUNRISE=string.format("%s%s", sunrise[1], sunrise[2])
|
local SUNRISE=string.format("%s%s", sunrise[1], sunrise[2])
|
||||||
|
if self.useSRS then
|
||||||
|
SUNRISE=string.format("%s %s hours", sunrise[1], sunrise[2])
|
||||||
|
end
|
||||||
|
|
||||||
local sunset=coord:GetSunset()
|
local sunset=coord:GetSunset()
|
||||||
sunset=UTILS.Split(sunset, ":")
|
sunset=UTILS.Split(sunset, ":")
|
||||||
local SUNSET=string.format("%s%s", sunset[1], sunset[2])
|
local SUNSET=string.format("%s%s", sunset[1], sunset[2])
|
||||||
|
if self.useSRS then
|
||||||
|
SUNSET=string.format("%s %s hours", sunset[1], sunset[2])
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
---------------------------------
|
---------------------------------
|
||||||
--- Temperature and Dew Point ---
|
--- Temperature and Dew Point ---
|
||||||
@@ -1528,6 +1624,30 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
precepitation=3 -- snow
|
precepitation=3 -- snow
|
||||||
end
|
end
|
||||||
|
elseif cloudspreset:find("RainyPreset1") then
|
||||||
|
-- Overcast + Rain
|
||||||
|
clouddens=9
|
||||||
|
if temperature>5 then
|
||||||
|
precepitation=1 -- rain
|
||||||
|
else
|
||||||
|
precepitation=3 -- snow
|
||||||
|
end
|
||||||
|
elseif cloudspreset:find("RainyPreset2") then
|
||||||
|
-- Overcast + Rain
|
||||||
|
clouddens=9
|
||||||
|
if temperature>5 then
|
||||||
|
precepitation=1 -- rain
|
||||||
|
else
|
||||||
|
precepitation=3 -- snow
|
||||||
|
end
|
||||||
|
elseif cloudspreset:find("RainyPreset3") then
|
||||||
|
-- Overcast + Rain
|
||||||
|
clouddens=9
|
||||||
|
if temperature>5 then
|
||||||
|
precepitation=1 -- rain
|
||||||
|
else
|
||||||
|
precepitation=3 -- snow
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local CLOUDBASE=string.format("%d", UTILS.MetersToFeet(cloudbase))
|
local CLOUDBASE=string.format("%d", UTILS.MetersToFeet(cloudbase))
|
||||||
@@ -1591,36 +1711,46 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
if self.airbasename:find("AFB")==nil and self.airbasename:find("Airport")==nil and self.airbasename:find("Airstrip")==nil and self.airbasename:find("airfield")==nil and self.airbasename:find("AB")==nil then
|
if self.airbasename:find("AFB")==nil and self.airbasename:find("Airport")==nil and self.airbasename:find("Airstrip")==nil and self.airbasename:find("airfield")==nil and self.airbasename:find("AB")==nil then
|
||||||
subtitle=subtitle.." Airport"
|
subtitle=subtitle.." Airport"
|
||||||
end
|
end
|
||||||
|
if not self.useSRS then
|
||||||
self.radioqueue:NewTransmission(string.format("%s/%s.ogg", self.theatre, self.airbasename), 3.0, self.soundpath, nil, nil, subtitle, self.subduration)
|
self.radioqueue:NewTransmission(string.format("%s/%s.ogg", self.theatre, self.airbasename), 3.0, self.soundpath, nil, nil, subtitle, self.subduration)
|
||||||
|
end
|
||||||
local alltext=subtitle
|
local alltext=subtitle
|
||||||
|
|
||||||
-- Information tag
|
-- Information tag
|
||||||
subtitle=string.format("Information %s", NATO)
|
subtitle=string.format("Information %s", NATO)
|
||||||
local _INFORMATION=subtitle
|
local _INFORMATION=subtitle
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.Information, 0.5, subtitle)
|
self:Transmission(ATIS.Sound.Information, 0.5, subtitle)
|
||||||
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Zulu Time
|
-- Zulu Time
|
||||||
subtitle=string.format("%s Zulu", ZULU)
|
subtitle=string.format("%s Zulu", ZULU)
|
||||||
|
if not self.useSRS then
|
||||||
self.radioqueue:Number2Transmission(ZULU, nil, 0.5)
|
self.radioqueue:Number2Transmission(ZULU, nil, 0.5)
|
||||||
self:Transmission(ATIS.Sound.Zulu, 0.2, subtitle)
|
self:Transmission(ATIS.Sound.Zulu, 0.2, subtitle)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
if not self.zulutimeonly then
|
if not self.zulutimeonly then
|
||||||
|
|
||||||
-- Sunrise Time
|
-- Sunrise Time
|
||||||
subtitle=string.format("Sunrise at %s local time", SUNRISE)
|
subtitle=string.format("Sunrise at %s local time", SUNRISE)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.SunriseAt, 0.5, subtitle)
|
self:Transmission(ATIS.Sound.SunriseAt, 0.5, subtitle)
|
||||||
self.radioqueue:Number2Transmission(SUNRISE, nil, 0.2)
|
self.radioqueue:Number2Transmission(SUNRISE, nil, 0.2)
|
||||||
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
|
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Sunset Time
|
-- Sunset Time
|
||||||
subtitle=string.format("Sunset at %s local time", SUNSET)
|
subtitle=string.format("Sunset at %s local time", SUNSET)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.SunsetAt, 0.5, subtitle)
|
self:Transmission(ATIS.Sound.SunsetAt, 0.5, subtitle)
|
||||||
self.radioqueue:Number2Transmission(SUNSET, nil, 0.5)
|
self.radioqueue:Number2Transmission(SUNSET, nil, 0.5)
|
||||||
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
|
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1634,6 +1764,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
subtitle=subtitle..", gusting"
|
subtitle=subtitle..", gusting"
|
||||||
end
|
end
|
||||||
local _WIND=subtitle
|
local _WIND=subtitle
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.WindFrom, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.WindFrom, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(WINDFROM)
|
self.radioqueue:Number2Transmission(WINDFROM)
|
||||||
self:Transmission(ATIS.Sound.At, 0.2)
|
self:Transmission(ATIS.Sound.At, 0.2)
|
||||||
@@ -1646,6 +1777,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
if turbulence>0 then
|
if turbulence>0 then
|
||||||
self:Transmission(ATIS.Sound.Gusting, 0.2)
|
self:Transmission(ATIS.Sound.Gusting, 0.2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Visibility
|
-- Visibility
|
||||||
@@ -1654,6 +1786,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
subtitle=string.format("Visibility %s SM", VISIBILITY)
|
subtitle=string.format("Visibility %s SM", VISIBILITY)
|
||||||
end
|
end
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.Visibilty, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.Visibilty, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(VISIBILITY)
|
self.radioqueue:Number2Transmission(VISIBILITY)
|
||||||
if self.metric then
|
if self.metric then
|
||||||
@@ -1661,6 +1794,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
self:Transmission(ATIS.Sound.StatuteMiles, 0.2)
|
self:Transmission(ATIS.Sound.StatuteMiles, 0.2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Weather phenomena
|
-- Weather phenomena
|
||||||
@@ -1699,6 +1833,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
-- Actual output
|
-- Actual output
|
||||||
if wp then
|
if wp then
|
||||||
subtitle=string.format("Weather phenomena:%s", wpsub)
|
subtitle=string.format("Weather phenomena:%s", wpsub)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.WeatherPhenomena, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.WeatherPhenomena, 1.0, subtitle)
|
||||||
if precepitation==1 then
|
if precepitation==1 then
|
||||||
self:Transmission(ATIS.Sound.Rain, 0.5)
|
self:Transmission(ATIS.Sound.Rain, 0.5)
|
||||||
@@ -1715,18 +1850,26 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
if dust then
|
if dust then
|
||||||
self:Transmission(ATIS.Sound.Dust, 0.5)
|
self:Transmission(ATIS.Sound.Dust, 0.5)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cloud base
|
-- Cloud base
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(CloudCover, 1.0, CLOUDSsub)
|
self:Transmission(CloudCover, 1.0, CLOUDSsub)
|
||||||
|
end
|
||||||
if CLOUDBASE and static then
|
if CLOUDBASE and static then
|
||||||
-- Base
|
-- Base
|
||||||
|
local cbase=tostring(tonumber(CLOUDBASE1000)*1000+tonumber(CLOUDBASE0100)*100)
|
||||||
|
local cceil=tostring(tonumber(CLOUDCEIL1000)*1000+tonumber(CLOUDCEIL0100)*100)
|
||||||
if self.metric then
|
if self.metric then
|
||||||
subtitle=string.format("Cloudbase %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
|
--subtitle=string.format("Cloud base %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
|
||||||
|
subtitle=string.format("Cloud base %s, ceiling %s meters", cbase, cceil)
|
||||||
else
|
else
|
||||||
subtitle=string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL)
|
--subtitle=string.format("Cloud base %s, ceiling %s feet", CLOUDBASE, CLOUDCEIL)
|
||||||
|
subtitle=string.format("Cloud base %s, ceiling %s feet", cbase, cceil)
|
||||||
end
|
end
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.CloudBase, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.CloudBase, 1.0, subtitle)
|
||||||
if tonumber(CLOUDBASE1000)>0 then
|
if tonumber(CLOUDBASE1000)>0 then
|
||||||
self.radioqueue:Number2Transmission(CLOUDBASE1000)
|
self.radioqueue:Number2Transmission(CLOUDBASE1000)
|
||||||
@@ -1752,6 +1895,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Temperature
|
-- Temperature
|
||||||
@@ -1769,6 +1913,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
local _TEMPERATURE=subtitle
|
local _TEMPERATURE=subtitle
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.Temperature, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.Temperature, 1.0, subtitle)
|
||||||
if temperature<0 then
|
if temperature<0 then
|
||||||
self:Transmission(ATIS.Sound.Minus, 0.2)
|
self:Transmission(ATIS.Sound.Minus, 0.2)
|
||||||
@@ -1779,6 +1924,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Dew point
|
-- Dew point
|
||||||
@@ -1796,6 +1942,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
local _DEWPOINT=subtitle
|
local _DEWPOINT=subtitle
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.DewPoint, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.DewPoint, 1.0, subtitle)
|
||||||
if dewpoint<0 then
|
if dewpoint<0 then
|
||||||
self:Transmission(ATIS.Sound.Minus, 0.2)
|
self:Transmission(ATIS.Sound.Minus, 0.2)
|
||||||
@@ -1806,6 +1953,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Altimeter QNH/QFE.
|
-- Altimeter QNH/QFE.
|
||||||
@@ -1813,24 +1961,25 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
if self.qnhonly then
|
if self.qnhonly then
|
||||||
subtitle=string.format("Altimeter %s.%s mmHg", QNH[1], QNH[2])
|
subtitle=string.format("Altimeter %s.%s mmHg", QNH[1], QNH[2])
|
||||||
else
|
else
|
||||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
subtitle=string.format("Altimeter: QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if self.metric then
|
if self.metric then
|
||||||
if self.qnhonly then
|
if self.qnhonly then
|
||||||
subtitle=string.format("Altimeter %s.%s hPa", QNH[1], QNH[2])
|
subtitle=string.format("Altimeter %s.%s hPa", QNH[1], QNH[2])
|
||||||
else
|
else
|
||||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
|
subtitle=string.format("Altimeter: QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if self.qnhonly then
|
if self.qnhonly then
|
||||||
subtitle=string.format("Altimeter %s.%s inHg", QNH[1], QNH[2])
|
subtitle=string.format("Altimeter %s.%s inHg", QNH[1], QNH[2])
|
||||||
else
|
else
|
||||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
subtitle=string.format("Altimeter: QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local _ALTIMETER=subtitle
|
local _ALTIMETER=subtitle
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.Altimeter, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.Altimeter, 1.0, subtitle)
|
||||||
if not self.qnhonly then
|
if not self.qnhonly then
|
||||||
self:Transmission(ATIS.Sound.QNH, 0.5)
|
self:Transmission(ATIS.Sound.QNH, 0.5)
|
||||||
@@ -1860,6 +2009,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
|
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Active runway.
|
-- Active runway.
|
||||||
@@ -1870,6 +2020,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
subtitle=subtitle.." Right"
|
subtitle=subtitle.." Right"
|
||||||
end
|
end
|
||||||
local _RUNACT=subtitle
|
local _RUNACT=subtitle
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(runway)
|
self.radioqueue:Number2Transmission(runway)
|
||||||
if rwyLeft==true then
|
if rwyLeft==true then
|
||||||
@@ -1877,6 +2028,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
elseif rwyLeft==false then
|
elseif rwyLeft==false then
|
||||||
self:Transmission(ATIS.Sound.Right, 0.2)
|
self:Transmission(ATIS.Sound.Right, 0.2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Runway length.
|
-- Runway length.
|
||||||
@@ -1900,6 +2052,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Transmit.
|
-- Transmit.
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.RunwayLength, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.RunwayLength, 1.0, subtitle)
|
||||||
if tonumber(L1000)>0 then
|
if tonumber(L1000)>0 then
|
||||||
self.radioqueue:Number2Transmission(L1000)
|
self.radioqueue:Number2Transmission(L1000)
|
||||||
@@ -1914,7 +2067,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1937,7 +2090,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
subtitle=subtitle.." feet"
|
subtitle=subtitle.." feet"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Transmitt.
|
-- Transmit.
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.Elevation, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.Elevation, 1.0, subtitle)
|
||||||
if tonumber(L1000)>0 then
|
if tonumber(L1000)>0 then
|
||||||
self.radioqueue:Number2Transmission(L1000)
|
self.radioqueue:Number2Transmission(L1000)
|
||||||
@@ -1952,7 +2106,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
else
|
else
|
||||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1966,6 +2120,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
subtitle=string.format("Tower frequency %s", freqs)
|
subtitle=string.format("Tower frequency %s", freqs)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.TowerFrequency, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.TowerFrequency, 1.0, subtitle)
|
||||||
for _,freq in pairs(self.towerfrequency) do
|
for _,freq in pairs(self.towerfrequency) do
|
||||||
local f=string.format("%.3f", freq)
|
local f=string.format("%.3f", freq)
|
||||||
@@ -1977,7 +2132,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
end
|
end
|
||||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1985,6 +2140,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
local ils=self:GetNavPoint(self.ils, runway, rwyLeft)
|
local ils=self:GetNavPoint(self.ils, runway, rwyLeft)
|
||||||
if ils then
|
if ils then
|
||||||
subtitle=string.format("ILS frequency %.2f MHz", ils.frequency)
|
subtitle=string.format("ILS frequency %.2f MHz", ils.frequency)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.ILSFrequency, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.ILSFrequency, 1.0, subtitle)
|
||||||
local f=string.format("%.2f", ils.frequency)
|
local f=string.format("%.2f", ils.frequency)
|
||||||
f=UTILS.Split(f, ".")
|
f=UTILS.Split(f, ".")
|
||||||
@@ -1994,7 +2150,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
self.radioqueue:Number2Transmission(f[2])
|
self.radioqueue:Number2Transmission(f[2])
|
||||||
end
|
end
|
||||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2002,6 +2158,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
local ndb=self:GetNavPoint(self.ndbouter, runway, rwyLeft)
|
local ndb=self:GetNavPoint(self.ndbouter, runway, rwyLeft)
|
||||||
if ndb then
|
if ndb then
|
||||||
subtitle=string.format("Outer NDB frequency %.2f MHz", ndb.frequency)
|
subtitle=string.format("Outer NDB frequency %.2f MHz", ndb.frequency)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.OuterNDBFrequency, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.OuterNDBFrequency, 1.0, subtitle)
|
||||||
local f=string.format("%.2f", ndb.frequency)
|
local f=string.format("%.2f", ndb.frequency)
|
||||||
f=UTILS.Split(f, ".")
|
f=UTILS.Split(f, ".")
|
||||||
@@ -2011,7 +2168,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
self.radioqueue:Number2Transmission(f[2])
|
self.radioqueue:Number2Transmission(f[2])
|
||||||
end
|
end
|
||||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2019,6 +2176,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
local ndb=self:GetNavPoint(self.ndbinner, runway, rwyLeft)
|
local ndb=self:GetNavPoint(self.ndbinner, runway, rwyLeft)
|
||||||
if ndb then
|
if ndb then
|
||||||
subtitle=string.format("Inner NDB frequency %.2f MHz", ndb.frequency)
|
subtitle=string.format("Inner NDB frequency %.2f MHz", ndb.frequency)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.InnerNDBFrequency, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.InnerNDBFrequency, 1.0, subtitle)
|
||||||
local f=string.format("%.2f", ndb.frequency)
|
local f=string.format("%.2f", ndb.frequency)
|
||||||
f=UTILS.Split(f, ".")
|
f=UTILS.Split(f, ".")
|
||||||
@@ -2028,13 +2186,17 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
self.radioqueue:Number2Transmission(f[2])
|
self.radioqueue:Number2Transmission(f[2])
|
||||||
end
|
end
|
||||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
-- VOR
|
-- VOR
|
||||||
if self.vor then
|
if self.vor then
|
||||||
subtitle=string.format("VOR frequency %.2f MHz", self.vor)
|
subtitle=string.format("VOR frequency %.2f MHz", self.vor)
|
||||||
|
if self.useSRS then
|
||||||
|
subtitle=string.format("V O R frequency %.2f MHz", self.vor)
|
||||||
|
end
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.VORFrequency, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.VORFrequency, 1.0, subtitle)
|
||||||
local f=string.format("%.2f", self.vor)
|
local f=string.format("%.2f", self.vor)
|
||||||
f=UTILS.Split(f, ".")
|
f=UTILS.Split(f, ".")
|
||||||
@@ -2044,26 +2206,28 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
self.radioqueue:Number2Transmission(f[2])
|
self.radioqueue:Number2Transmission(f[2])
|
||||||
end
|
end
|
||||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TACAN
|
-- TACAN
|
||||||
if self.tacan then
|
if self.tacan then
|
||||||
subtitle=string.format("TACAN channel %dX", self.tacan)
|
subtitle=string.format("TACAN channel %dX", self.tacan)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.TACANChannel, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.TACANChannel, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(tostring(self.tacan), nil, 0.2)
|
self.radioqueue:Number2Transmission(tostring(self.tacan), nil, 0.2)
|
||||||
self.radioqueue:NewTransmission("NATO Alphabet/Xray.ogg", 0.75, self.soundpath, nil, 0.2)
|
self.radioqueue:NewTransmission("NATO Alphabet/Xray.ogg", 0.75, self.soundpath, nil, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RSBN
|
-- RSBN
|
||||||
if self.rsbn then
|
if self.rsbn then
|
||||||
subtitle=string.format("RSBN channel %d", self.rsbn)
|
subtitle=string.format("RSBN channel %d", self.rsbn)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.RSBNChannel, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.RSBNChannel, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(tostring(self.rsbn), nil, 0.2)
|
self.radioqueue:Number2Transmission(tostring(self.rsbn), nil, 0.2)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2071,17 +2235,19 @@ function ATIS:onafterBroadcast(From, Event, To)
|
|||||||
local ndb=self:GetNavPoint(self.prmg, runway, rwyLeft)
|
local ndb=self:GetNavPoint(self.prmg, runway, rwyLeft)
|
||||||
if ndb then
|
if ndb then
|
||||||
subtitle=string.format("PRMG channel %d", ndb.frequency)
|
subtitle=string.format("PRMG channel %d", ndb.frequency)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.PRMGChannel, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.PRMGChannel, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(tostring(ndb.frequency), nil, 0.5)
|
self.radioqueue:Number2Transmission(tostring(ndb.frequency), nil, 0.5)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Advice on initial...
|
-- Advice on initial...
|
||||||
subtitle=string.format("Advise on initial contact, you have information %s", NATO)
|
subtitle=string.format("Advise on initial contact, you have information %s", NATO)
|
||||||
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.AdviceOnInitial, 0.5, subtitle)
|
self:Transmission(ATIS.Sound.AdviceOnInitial, 0.5, subtitle)
|
||||||
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
||||||
|
end
|
||||||
alltext=alltext..";\n"..subtitle
|
alltext=alltext..";\n"..subtitle
|
||||||
|
|
||||||
-- Report ATIS text.
|
-- Report ATIS text.
|
||||||
@@ -2102,6 +2268,62 @@ end
|
|||||||
-- @param #string Text Report text.
|
-- @param #string Text Report text.
|
||||||
function ATIS:onafterReport(From, Event, To, Text)
|
function ATIS:onafterReport(From, Event, To, Text)
|
||||||
self:T(self.lid..string.format("Report:\n%s", Text))
|
self:T(self.lid..string.format("Report:\n%s", Text))
|
||||||
|
|
||||||
|
if self.useSRS and self.msrs then
|
||||||
|
|
||||||
|
-- Remove line breaks
|
||||||
|
local text=string.gsub(Text, "[\r\n]", "")
|
||||||
|
|
||||||
|
-- Replace other stuff.
|
||||||
|
local text=string.gsub(text, "SM", "statute miles")
|
||||||
|
local text=string.gsub(text, "°C", "degrees Celsius")
|
||||||
|
local text=string.gsub(text, "°F", "degrees Fahrenheit")
|
||||||
|
local text=string.gsub(text, "inHg", "inches of Mercury")
|
||||||
|
local text=string.gsub(text, "mmHg", "millimeters of Mercury")
|
||||||
|
local text=string.gsub(text, "hPa", "hecto Pascals")
|
||||||
|
local text=string.gsub(text, "m/s", "meters per second")
|
||||||
|
|
||||||
|
-- Replace ";" by "."
|
||||||
|
local text=string.gsub(text, ";", " . ")
|
||||||
|
|
||||||
|
--Debug output.
|
||||||
|
self:T("SRS TTS: "..text)
|
||||||
|
|
||||||
|
-- Play text-to-speech report.
|
||||||
|
self.msrs:PlayText(text)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Event Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Base captured
|
||||||
|
-- @param #ATIS self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData Event data.
|
||||||
|
function ATIS:OnEventBaseCaptured(EventData)
|
||||||
|
|
||||||
|
if EventData and EventData.Place then
|
||||||
|
|
||||||
|
-- Place is the airbase that was captured.
|
||||||
|
local airbase=EventData.Place --Wrapper.Airbase#AIRBASE
|
||||||
|
|
||||||
|
-- Check that this airbase belongs or did belong to this warehouse.
|
||||||
|
if EventData.PlaceName==self.airbasename then
|
||||||
|
|
||||||
|
-- New coalition of airbase after it was captured.
|
||||||
|
local NewCoalitionAirbase=airbase:GetCoalition()
|
||||||
|
|
||||||
|
if self.useSRS and self.msrs and self.msrs.coalition~=NewCoalitionAirbase then
|
||||||
|
self.msrs:SetCoalition(NewCoalitionAirbase)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
-- * [F-14A/B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
|
-- * [F-14A/B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
|
||||||
-- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI)
|
-- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI)
|
||||||
-- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI) [**WIP**]
|
-- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI) [**WIP**]
|
||||||
|
-- * [T-45C Goshawk](https://www.vnao-cvw-7.com/t-45-goshawk) (VNAO)(Player & AI) [**WIP**]
|
||||||
-- * F/A-18C Hornet (AI)
|
-- * F/A-18C Hornet (AI)
|
||||||
-- * F-14A Tomcat (AI)
|
-- * F-14A Tomcat (AI)
|
||||||
-- * E-2D Hawkeye (AI)
|
-- * E-2D Hawkeye (AI)
|
||||||
@@ -101,7 +102,7 @@
|
|||||||
-- ### Wags DCS Hornet Videos:
|
-- ### Wags DCS Hornet Videos:
|
||||||
--
|
--
|
||||||
-- * [DCS: F/A-18C Hornet - Episode 9: CASE I Carrier Landing](https://www.youtube.com/watch?v=TuigBLhtAH8)
|
-- * [DCS: F/A-18C Hornet - Episode 9: CASE I Carrier Landing](https://www.youtube.com/watch?v=TuigBLhtAH8)
|
||||||
-- * [DCS: F/A-18C Hornet – Episode 16: CASE III Introduction](https://www.youtube.com/watch?v=DvlMHnLjbDQ)
|
-- * [DCS: F/A-18C Hornet – Episode 16: CASE III Introduction](https://www.youtube.com/watch?v=DvlMHnLjbDQ)
|
||||||
-- * [DCS: F/A-18C Hornet Case I Carrier Landing Training Lesson Recording](https://www.youtube.com/watch?v=D33uM9q4xgA)
|
-- * [DCS: F/A-18C Hornet Case I Carrier Landing Training Lesson Recording](https://www.youtube.com/watch?v=D33uM9q4xgA)
|
||||||
--
|
--
|
||||||
-- ### AV-8B Harrier at USS Tarawa
|
-- ### AV-8B Harrier at USS Tarawa
|
||||||
@@ -266,7 +267,7 @@
|
|||||||
--
|
--
|
||||||
-- That being said, this script allows you to use any of the three cases to be used at any time. Or, in other words, *you* need to specify when which case is safe and appropriate.
|
-- That being said, this script allows you to use any of the three cases to be used at any time. Or, in other words, *you* need to specify when which case is safe and appropriate.
|
||||||
--
|
--
|
||||||
-- This is a lot of responsability. *You* are the boss, but *you* need to make the right decisions or things will go terribly wrong!
|
-- This is a lot of responsibility. *You* are the boss, but *you* need to make the right decisions or things will go terribly wrong!
|
||||||
--
|
--
|
||||||
-- Recovery windows can be set up via the @{#AIRBOSS.AddRecoveryWindow} function as explained below. With this it is possible to seamlessly (within reason!) switch recovery cases in the same mission.
|
-- Recovery windows can be set up via the @{#AIRBOSS.AddRecoveryWindow} function as explained below. With this it is possible to seamlessly (within reason!) switch recovery cases in the same mission.
|
||||||
--
|
--
|
||||||
@@ -293,14 +294,14 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Once the aircraft reaches the Inital, 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.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ## CASE III
|
-- ## CASE III
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- A Case III recovery is conducted during nighttime. The holding position and the landing pattern are rather different from a Case I recovery as can be seen in the image above.
|
-- A Case III recovery is conducted during nighttime or when the visibility is below CASE II minima during the day. The holding position and the landing pattern are rather different from a Case I recovery as can be seen in the image above.
|
||||||
--
|
--
|
||||||
-- The first holding zone starts 21 NM astern the carrier at angels 6. The separation between the stacks is 1000 ft just like in Case I. However, the distance to the boat
|
-- The first holding zone starts 21 NM astern the carrier at angels 6. The separation between the stacks is 1000 ft just like in Case I. However, the distance to the boat
|
||||||
-- increases by 1 NM with each stack. The general form can be written as D=15+6+(N-1), where D is the distance to the boat in NM and N the number of the stack starting at N=1.
|
-- increases by 1 NM with each stack. The general form can be written as D=15+6+(N-1), where D is the distance to the boat in NM and N the number of the stack starting at N=1.
|
||||||
@@ -572,7 +573,10 @@
|
|||||||
-- * **L**ined **U**p **L**eft or **R**ight: LUL, LUR
|
-- * **L**ined **U**p **L**eft or **R**ight: LUL, LUR
|
||||||
-- * Too **H**igh or too **LO**w: H, LO
|
-- * Too **H**igh or too **LO**w: H, LO
|
||||||
-- * Too **F**ast or too **SLO**w: F, SLO
|
-- * Too **F**ast or too **SLO**w: F, SLO
|
||||||
-- * **Fly through** glideslope **down** or **up**: \\ , /
|
-- * **O**ver**S**hoot: OS, only referenced during **X**
|
||||||
|
-- * **Fly through** glideslope **down** or **up**: \\ , /, advisory only
|
||||||
|
-- * **D**rift **L**eft or **R**ight:DL, DR, advisory only
|
||||||
|
-- * **A**ngled **A**pproach: Angled approach (wings level and LUL): AA, advisory only
|
||||||
--
|
--
|
||||||
-- Each grading, x, is subdivided by
|
-- Each grading, x, is subdivided by
|
||||||
--
|
--
|
||||||
@@ -633,7 +637,7 @@
|
|||||||
--
|
--
|
||||||
-- ## Foul Deck Waveoff
|
-- ## Foul Deck Waveoff
|
||||||
--
|
--
|
||||||
-- A foul deck waveoff is called by the LSO if an aircraft is detected within the landing area when an approaching aircraft is crossing the ship's wake during Case I/II operations,
|
-- A foul deck waveoff is called by the LSO if an aircraft is detected within the landing area when an approaching aircraft is at position IM-IC during Case I/II operations,
|
||||||
-- or with an aircraft approaching the 3/4 NM during Case III operations.
|
-- or with an aircraft approaching the 3/4 NM during Case III operations.
|
||||||
--
|
--
|
||||||
-- The approaching aircraft will be notified via LSO radio comms and is supposed to overfly the landing area to enter the Bolter pattern. **This pass is not graded**.
|
-- The approaching aircraft will be notified via LSO radio comms and is supposed to overfly the landing area to enter the Bolter pattern. **This pass is not graded**.
|
||||||
@@ -1263,6 +1267,7 @@ AIRBOSS = {
|
|||||||
-- @field #string S3BTANKER Lockheed S-3B Viking tanker.
|
-- @field #string S3BTANKER Lockheed S-3B Viking tanker.
|
||||||
-- @field #string E2D Grumman E-2D Hawkeye AWACS.
|
-- @field #string E2D Grumman E-2D Hawkeye AWACS.
|
||||||
-- @field #string C2A Grumman C-2A Greyhound from Military Aircraft Mod.
|
-- @field #string C2A Grumman C-2A Greyhound from Military Aircraft Mod.
|
||||||
|
-- @field #string T45C T-45C by VNAO
|
||||||
AIRBOSS.AircraftCarrier={
|
AIRBOSS.AircraftCarrier={
|
||||||
AV8B="AV8BNA",
|
AV8B="AV8BNA",
|
||||||
HORNET="FA-18C_hornet",
|
HORNET="FA-18C_hornet",
|
||||||
@@ -1271,6 +1276,7 @@ AIRBOSS.AircraftCarrier={
|
|||||||
F14B="F-14B",
|
F14B="F-14B",
|
||||||
F14A_AI="F-14A",
|
F14A_AI="F-14A",
|
||||||
FA18C="F/A-18C",
|
FA18C="F/A-18C",
|
||||||
|
T45C="T-45",
|
||||||
S3B="S-3B",
|
S3B="S-3B",
|
||||||
S3BTANKER="S-3B Tanker",
|
S3BTANKER="S-3B Tanker",
|
||||||
E2D="E-2C",
|
E2D="E-2C",
|
||||||
@@ -1338,6 +1344,8 @@ AIRBOSS.CarrierType={
|
|||||||
-- @field #number _min Min _OK_ value. Default -0.5 deg.
|
-- @field #number _min Min _OK_ value. Default -0.5 deg.
|
||||||
-- @field #number Left (LUR) threshold. Default -1.0 deg.
|
-- @field #number Left (LUR) threshold. Default -1.0 deg.
|
||||||
-- @field #number Right (LUL) threshold. Default 1.0 deg.
|
-- @field #number Right (LUL) threshold. Default 1.0 deg.
|
||||||
|
-- @field #number LeftMed threshold for AA/OS measuring. Default -2.0 deg.
|
||||||
|
-- @field #number RightMed threshold for AA/OS measuring. Default 2.0 deg.
|
||||||
-- @field #number LEFT LUR threshold. Default -3.0 deg.
|
-- @field #number LEFT LUR threshold. Default -3.0 deg.
|
||||||
-- @field #number RIGHT LUL threshold. Default 3.0 deg.
|
-- @field #number RIGHT LUL threshold. Default 3.0 deg.
|
||||||
|
|
||||||
@@ -1940,6 +1948,11 @@ function AIRBOSS:New(carriername, alias)
|
|||||||
-- Welcome players.
|
-- Welcome players.
|
||||||
self:SetWelcomePlayers(true)
|
self:SetWelcomePlayers(true)
|
||||||
|
|
||||||
|
-- Coordinates
|
||||||
|
self.landingcoord=COORDINATE:New(0,0,0) --Core.Point#COORDINATE
|
||||||
|
self.sterncoord=COORDINATE:New(0, 0, 0) --Core.Point#COORDINATE
|
||||||
|
self.landingspotcoord=COORDINATE:New(0,0,0) --Core.Point#COORDINATE
|
||||||
|
|
||||||
-- Init carrier parameters.
|
-- Init carrier parameters.
|
||||||
if self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
if self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
||||||
self:_InitStennis()
|
self:_InitStennis()
|
||||||
@@ -2643,10 +2656,10 @@ end
|
|||||||
|
|
||||||
--- Set multiplayer environment wire correction.
|
--- Set multiplayer environment wire correction.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param #number Dcorr Correction distance in meters. Default 8.7 m.
|
-- @param #number Dcorr Correction distance in meters. Default 12 m.
|
||||||
-- @return #AIRBOSS self
|
-- @return #AIRBOSS self
|
||||||
function AIRBOSS:SetMPWireCorrection(Dcorr)
|
function AIRBOSS:SetMPWireCorrection(Dcorr)
|
||||||
self.mpWireCorrection=Dcorr or 8.7
|
self.mpWireCorrection=Dcorr or 12
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2808,16 +2821,20 @@ end
|
|||||||
-- @param #number _max
|
-- @param #number _max
|
||||||
-- @param #number _min
|
-- @param #number _min
|
||||||
-- @param #number Left
|
-- @param #number Left
|
||||||
|
-- @param #number LeftMed
|
||||||
-- @param #number LEFT
|
-- @param #number LEFT
|
||||||
-- @param #number Right
|
-- @param #number Right
|
||||||
|
-- @param #number RightMed
|
||||||
-- @param #number RIGHT
|
-- @param #number RIGHT
|
||||||
-- @return #AIRBOSS self
|
-- @return #AIRBOSS self
|
||||||
function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LEFT, Right, RIGHT)
|
function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LeftMed, LEFT, Right, RightMed, RIGHT)
|
||||||
self.lue._max=_max or 0.5
|
self.lue._max=_max or 0.5
|
||||||
self.lue._min=_min or -0.5
|
self.lue._min=_min or -0.5
|
||||||
self.lue.Left=Left or -1.0
|
self.lue.Left=Left or -1.0
|
||||||
|
self.lue.LeftMed=LeftMed or -2.0
|
||||||
self.lue.LEFT=LEFT or -3.0
|
self.lue.LEFT=LEFT or -3.0
|
||||||
self.lue.Right=Right or 1.0
|
self.lue.Right=Right or 1.0
|
||||||
|
self.lue.RightMed=RightMed or 2.0
|
||||||
self.lue.RIGHT=RIGHT or 3.0
|
self.lue.RIGHT=RIGHT or 3.0
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -3398,6 +3415,12 @@ function AIRBOSS:onafterStart(From, Event, To)
|
|||||||
self:HandleEvent(EVENTS.Ejection)
|
self:HandleEvent(EVENTS.Ejection)
|
||||||
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft)
|
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft)
|
||||||
self:HandleEvent(EVENTS.MissionEnd)
|
self:HandleEvent(EVENTS.MissionEnd)
|
||||||
|
self:HandleEvent(EVENTS.RemoveUnit)
|
||||||
|
|
||||||
|
--self.StatusScheduler=SCHEDULER:New(self)
|
||||||
|
--self.StatusScheduler:Schedule(self, self._Status, {}, 1, 0.5)
|
||||||
|
|
||||||
|
self.StatusTimer=TIMER:New(self._Status, self):Start(2, 0.5)
|
||||||
|
|
||||||
-- Start status check in 1 second.
|
-- Start status check in 1 second.
|
||||||
self:__Status(1)
|
self:__Status(1)
|
||||||
@@ -3410,19 +3433,12 @@ end
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
function AIRBOSS:onafterStatus(From, Event, To)
|
function AIRBOSS:onafterStatus(From, Event, To)
|
||||||
|
|
||||||
if true then
|
|
||||||
--env.info("FF Status ==> return")
|
|
||||||
--return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get current time.
|
-- Get current time.
|
||||||
local time=timer.getTime()
|
local time=timer.getTime()
|
||||||
|
|
||||||
-- Update marshal and pattern queue every 30 seconds.
|
-- Update marshal and pattern queue every 30 seconds.
|
||||||
if time-self.Tqueue>self.dTqueue then
|
if time-self.Tqueue>self.dTqueue then
|
||||||
|
|
||||||
--collectgarbage()
|
|
||||||
|
|
||||||
-- Get time.
|
-- Get time.
|
||||||
local clock=UTILS.SecondsToClock(timer.getAbsTime())
|
local clock=UTILS.SecondsToClock(timer.getAbsTime())
|
||||||
local eta=UTILS.SecondsToClock(self:_GetETAatNextWP())
|
local eta=UTILS.SecondsToClock(self:_GetETAatNextWP())
|
||||||
@@ -3433,7 +3449,7 @@ function AIRBOSS:onafterStatus(From, Event, To)
|
|||||||
local speed=self.carrier:GetVelocityKNOTS()
|
local speed=self.carrier:GetVelocityKNOTS()
|
||||||
|
|
||||||
-- Check water is ahead.
|
-- Check water is ahead.
|
||||||
local collision=self:_CheckCollisionCoord(pos:Translate(self.collisiondist, hdg))
|
local collision=false --self:_CheckCollisionCoord(pos:Translate(self.collisiondist, hdg))
|
||||||
|
|
||||||
local holdtime=0
|
local holdtime=0
|
||||||
if self.holdtimestamp then
|
if self.holdtimestamp then
|
||||||
@@ -3490,15 +3506,9 @@ function AIRBOSS:onafterStatus(From, Event, To)
|
|||||||
self.recoverywindow.WIND=false
|
self.recoverywindow.WIND=false
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Find path around the obstacle.
|
|
||||||
if not self.detour then
|
|
||||||
--self:_Pathfinder()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Check recovery times and start/stop recovery mode if necessary.
|
-- Check recovery times and start/stop recovery mode if necessary.
|
||||||
@@ -3528,14 +3538,21 @@ function AIRBOSS:onafterStatus(From, Event, To)
|
|||||||
self:_ActivateBeacons()
|
self:_ActivateBeacons()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Call status every ~0.5 seconds.
|
||||||
|
self:__Status(-30)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check AI status. Pattern queue AI in the groove? Marshal queue AI arrived in holding zone?
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
function AIRBOSS:_Status()
|
||||||
|
|
||||||
-- Check player status.
|
-- Check player status.
|
||||||
self:_CheckPlayerStatus()
|
self:_CheckPlayerStatus()
|
||||||
|
|
||||||
-- Check AI landing pattern status
|
-- Check AI landing pattern status
|
||||||
self:_CheckAIStatus()
|
self:_CheckAIStatus()
|
||||||
|
|
||||||
-- Call status every ~0.5 seconds.
|
|
||||||
self:__Status(-self.dTstatus)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check AI status. Pattern queue AI in the groove? Marshal queue AI arrived in holding zone?
|
--- Check AI status. Pattern queue AI in the groove? Marshal queue AI arrived in holding zone?
|
||||||
@@ -3587,11 +3604,15 @@ function AIRBOSS:_CheckAIStatus()
|
|||||||
-- Get lineup and distance to carrier.
|
-- Get lineup and distance to carrier.
|
||||||
local lineup=self:_Lineup(unit, true)
|
local lineup=self:_Lineup(unit, true)
|
||||||
|
|
||||||
|
local unitcoord=unit:GetCoord()
|
||||||
|
|
||||||
|
local dist=unitcoord:Get2DDistance(self:GetCoord())
|
||||||
|
|
||||||
-- Distance in NM.
|
-- Distance in NM.
|
||||||
local distance=UTILS.MetersToNM(unit:GetCoordinate():Get2DDistance(self:GetCoordinate()))
|
local distance=UTILS.MetersToNM(dist)
|
||||||
|
|
||||||
-- Altitude in ft.
|
-- Altitude in ft.
|
||||||
local alt=UTILS.MetersToFeet(unit:GetAltitude())
|
local alt=UTILS.MetersToFeet(unitcoord.y)
|
||||||
|
|
||||||
-- Check if parameters are right and flight is in the groove.
|
-- Check if parameters are right and flight is in the groove.
|
||||||
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
|
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
|
||||||
@@ -5471,6 +5492,7 @@ function AIRBOSS:_GetAircraftAoA(playerData)
|
|||||||
|
|
||||||
-- Get AC type.
|
-- Get AC type.
|
||||||
local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET
|
local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET
|
||||||
|
local goshawk=playerData.actype==AIRBOSS.AircraftCarrier.T45C
|
||||||
local skyhawk=playerData.actype==AIRBOSS.AircraftCarrier.A4EC
|
local skyhawk=playerData.actype==AIRBOSS.AircraftCarrier.A4EC
|
||||||
local harrier=playerData.actype==AIRBOSS.AircraftCarrier.AV8B
|
local harrier=playerData.actype==AIRBOSS.AircraftCarrier.AV8B
|
||||||
local tomcat=playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B
|
local tomcat=playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B
|
||||||
@@ -5497,6 +5519,15 @@ function AIRBOSS:_GetAircraftAoA(playerData)
|
|||||||
aoa.OnSpeedMin = self:_AoAUnit2Deg(playerData, 14.5) --14.17 --14.5 units
|
aoa.OnSpeedMin = self:_AoAUnit2Deg(playerData, 14.5) --14.17 --14.5 units
|
||||||
aoa.Fast = self:_AoAUnit2Deg(playerData, 14.0) --13.33 --14.0 units
|
aoa.Fast = self:_AoAUnit2Deg(playerData, 14.0) --13.33 --14.0 units
|
||||||
aoa.FAST = self:_AoAUnit2Deg(playerData, 13.0) --11.67 --13.0 units
|
aoa.FAST = self:_AoAUnit2Deg(playerData, 13.0) --11.67 --13.0 units
|
||||||
|
elseif goshawk then
|
||||||
|
-- T-45C Goshawk parameters.
|
||||||
|
aoa.SLOW = 8.00 --19
|
||||||
|
aoa.Slow = 7.75 --18
|
||||||
|
aoa.OnSpeedMax = 7.25 --17.5
|
||||||
|
aoa.OnSpeed = 7.00 --17
|
||||||
|
aoa.OnSpeedMin = 6.75 --16.5
|
||||||
|
aoa.Fast = 6.25 --16
|
||||||
|
aoa.FAST = 6.00 --15
|
||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
-- A-4E-C Skyhawk parameters from https://forums.eagle.ru/showpost.php?p=3703467&postcount=390
|
-- A-4E-C Skyhawk parameters from https://forums.eagle.ru/showpost.php?p=3703467&postcount=390
|
||||||
-- Note that these are arbitrary UNITS and not degrees. We need a conversion formula!
|
-- Note that these are arbitrary UNITS and not degrees. We need a conversion formula!
|
||||||
@@ -5681,6 +5712,9 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
alt=UTILS.FeetToMeters(600)
|
alt=UTILS.FeetToMeters(600)
|
||||||
speed=UTILS.KnotsToMps(250)
|
speed=UTILS.KnotsToMps(250)
|
||||||
|
elseif goshawk then
|
||||||
|
alt=UTILS.FeetToMeters(800)
|
||||||
|
speed=UTILS.KnotsToMps(300)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.BREAKENTRY then
|
elseif step==AIRBOSS.PatternStep.BREAKENTRY then
|
||||||
@@ -5691,11 +5725,14 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
alt=UTILS.FeetToMeters(600)
|
alt=UTILS.FeetToMeters(600)
|
||||||
speed=UTILS.KnotsToMps(250)
|
speed=UTILS.KnotsToMps(250)
|
||||||
|
elseif goshawk then
|
||||||
|
alt=UTILS.FeetToMeters(800)
|
||||||
|
speed=UTILS.KnotsToMps(300)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.EARLYBREAK then
|
elseif step==AIRBOSS.PatternStep.EARLYBREAK then
|
||||||
|
|
||||||
if hornet or tomcat or harrier then
|
if hornet or tomcat or harrier or goshawk then
|
||||||
alt=UTILS.FeetToMeters(800)
|
alt=UTILS.FeetToMeters(800)
|
||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
alt=UTILS.FeetToMeters(600)
|
alt=UTILS.FeetToMeters(600)
|
||||||
@@ -5703,7 +5740,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.LATEBREAK then
|
elseif step==AIRBOSS.PatternStep.LATEBREAK then
|
||||||
|
|
||||||
if hornet or tomcat or harrier then
|
if hornet or tomcat or harrier or goshawk then
|
||||||
alt=UTILS.FeetToMeters(800)
|
alt=UTILS.FeetToMeters(800)
|
||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
alt=UTILS.FeetToMeters(600)
|
alt=UTILS.FeetToMeters(600)
|
||||||
@@ -5711,7 +5748,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.ABEAM then
|
elseif step==AIRBOSS.PatternStep.ABEAM then
|
||||||
|
|
||||||
if hornet or tomcat or harrier then
|
if hornet or tomcat or harrier or goshawk then
|
||||||
alt=UTILS.FeetToMeters(600)
|
alt=UTILS.FeetToMeters(600)
|
||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
alt=UTILS.FeetToMeters(500)
|
alt=UTILS.FeetToMeters(500)
|
||||||
@@ -5726,10 +5763,19 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
dist=UTILS.NMToMeters(1.2)
|
dist=UTILS.NMToMeters(1.2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if goshawk then
|
||||||
|
-- 0.9 to 1.1 NM per natops ch.4 page 48
|
||||||
|
dist=UTILS.NMToMeters(0.9)
|
||||||
|
else
|
||||||
|
dist=UTILS.NMToMeters(1.1)
|
||||||
|
end
|
||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.NINETY then
|
elseif step==AIRBOSS.PatternStep.NINETY then
|
||||||
|
|
||||||
if hornet or tomcat then
|
if hornet or tomcat then
|
||||||
alt=UTILS.FeetToMeters(500)
|
alt=UTILS.FeetToMeters(500)
|
||||||
|
elseif goshawk then
|
||||||
|
alt=UTILS.FeetToMeters(450)
|
||||||
elseif skyhawk then
|
elseif skyhawk then
|
||||||
alt=UTILS.FeetToMeters(500)
|
alt=UTILS.FeetToMeters(500)
|
||||||
elseif harrier then
|
elseif harrier then
|
||||||
@@ -5740,7 +5786,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.WAKE then
|
elseif step==AIRBOSS.PatternStep.WAKE then
|
||||||
|
|
||||||
if hornet then
|
if hornet or goshawk then
|
||||||
alt=UTILS.FeetToMeters(370)
|
alt=UTILS.FeetToMeters(370)
|
||||||
elseif tomcat then
|
elseif tomcat then
|
||||||
alt=UTILS.FeetToMeters(430) -- Tomcat should be a bit higher as it intercepts the GS a bit higher.
|
alt=UTILS.FeetToMeters(430) -- Tomcat should be a bit higher as it intercepts the GS a bit higher.
|
||||||
@@ -5753,7 +5799,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
|
|||||||
|
|
||||||
elseif step==AIRBOSS.PatternStep.FINAL then
|
elseif step==AIRBOSS.PatternStep.FINAL then
|
||||||
|
|
||||||
if hornet then
|
if hornet or goshawk then
|
||||||
alt=UTILS.FeetToMeters(300)
|
alt=UTILS.FeetToMeters(300)
|
||||||
elseif tomcat then
|
elseif tomcat then
|
||||||
alt=UTILS.FeetToMeters(360)
|
alt=UTILS.FeetToMeters(360)
|
||||||
@@ -6079,66 +6125,32 @@ function AIRBOSS:_ScanCarrierZone()
|
|||||||
-- Create a new flight group
|
-- Create a new flight group
|
||||||
if knownflight then
|
if knownflight then
|
||||||
|
|
||||||
-- Debug output.
|
|
||||||
self:T2(self.lid..string.format("Known flight group %s of type %s in CCA.", groupname, actype))
|
|
||||||
|
|
||||||
-- Check if flight is AI and if we want to handle it at all.
|
-- Check if flight is AI and if we want to handle it at all.
|
||||||
if knownflight.ai and self.handleai then
|
if knownflight.ai and knownflight.flag==-100 and self.handleai then
|
||||||
|
|
||||||
-- Defines if AI group should be handled by the airboss.
|
local putintomarshal=false
|
||||||
local iscarriersquad=true
|
|
||||||
|
|
||||||
-- Check if AI group is part of the group set if a set was defined.
|
-- Get flight group.
|
||||||
if self.squadsetAI then
|
local flight=_DATABASE:GetFlightGroup(groupname)
|
||||||
local group=self.squadsetAI:FindGroup(groupname)
|
|
||||||
if group then
|
if flight and flight:IsInbound() and flight.destbase:GetName()==self.carrier:GetName() then
|
||||||
iscarriersquad=true
|
if flight.ishelo then
|
||||||
else
|
else
|
||||||
iscarriersquad=false
|
putintomarshal=true
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if group was explicitly excluded.
|
|
||||||
if self.excludesetAI then
|
|
||||||
local group=self.excludesetAI:FindGroup(groupname)
|
|
||||||
if group then
|
|
||||||
iscarriersquad=false
|
|
||||||
end
|
end
|
||||||
|
flight.airboss=self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Get distance to carrier.
|
|
||||||
local dist=knownflight.group:GetCoordinate():Get2DDistance(self:GetCoordinate())
|
|
||||||
|
|
||||||
-- Close in distance. Is >0 if AC comes closer wrt to first detected distance d0.
|
-- Send AI flight to marshal stack.
|
||||||
local closein=knownflight.dist0-dist
|
if putintomarshal then
|
||||||
|
|
||||||
-- Debug info.
|
|
||||||
self:T3(self.lid..string.format("Known AI flight group %s closed in by %.1f NM", knownflight.groupname, UTILS.MetersToNM(closein)))
|
|
||||||
|
|
||||||
-- Is this group the tanker?
|
|
||||||
local istanker=self.tanker and self.tanker.tanker:GetName()==groupname
|
|
||||||
|
|
||||||
-- Is this group the AWACS?
|
|
||||||
local isawacs=self.awacs and self.awacs.tanker:GetName()==groupname
|
|
||||||
|
|
||||||
-- Send tanker to marshal stack?
|
|
||||||
local tanker2marshal = istanker and self.tanker:IsReturning() and self.tanker.airbase:GetName()==self.airbase:GetName() and knownflight.flag==-100 and self.tanker.recovery==true
|
|
||||||
|
|
||||||
-- Send AWACS to marhsal stack?
|
|
||||||
local awacs2marshal = isawacs and self.awacs:IsReturning() and self.awacs.airbase:GetName()==self.airbase:GetName() and knownflight.flag==-100 and self.awacs.recovery==true
|
|
||||||
|
|
||||||
-- Put flight into Marshal.
|
|
||||||
local putintomarshal=closein>UTILS.NMToMeters(5) and knownflight.flag==-100 and iscarriersquad and (not istanker) and (not isawacs)
|
|
||||||
|
|
||||||
-- Send AI flight to marshal stack if group closes in more than 5 and has initial flag value.
|
|
||||||
if putintomarshal or tanker2marshal or awacs2marshal then
|
|
||||||
|
|
||||||
-- Get the next free stack for current recovery case.
|
-- Get the next free stack for current recovery case.
|
||||||
local stack=self:_GetFreeStack(knownflight.ai)
|
local stack=self:_GetFreeStack(knownflight.ai)
|
||||||
|
|
||||||
-- Repawn.
|
-- Repawn.
|
||||||
local respawn=self.respawnAI --or tanker2marshal
|
local respawn=self.respawnAI
|
||||||
|
|
||||||
if stack then
|
if stack then
|
||||||
|
|
||||||
@@ -6158,7 +6170,8 @@ function AIRBOSS:_ScanCarrierZone()
|
|||||||
break
|
break
|
||||||
|
|
||||||
end -- Closed in or tanker/AWACS
|
end -- Closed in or tanker/AWACS
|
||||||
end -- AI
|
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
@@ -8877,6 +8890,58 @@ function AIRBOSS:OnEventEjection(EventData)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Airboss event handler for event REMOVEUNIT.
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function AIRBOSS:OnEventRemoveUnit(EventData)
|
||||||
|
self:F3({eventland = EventData})
|
||||||
|
|
||||||
|
-- Nil checks.
|
||||||
|
if EventData==nil then
|
||||||
|
self:E(self.lid.."ERROR: EventData=nil in event REMOVEUNIT!")
|
||||||
|
self:E(EventData)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if EventData.IniUnit==nil then
|
||||||
|
self:E(self.lid.."ERROR: EventData.IniUnit=nil in event REMOVEUNIT!")
|
||||||
|
self:E(EventData)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local _unitName=EventData.IniUnitName
|
||||||
|
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
||||||
|
|
||||||
|
self:T3(self.lid.."EJECT: unit = "..tostring(EventData.IniUnitName))
|
||||||
|
self:T3(self.lid.."EJECT: group = "..tostring(EventData.IniGroupName))
|
||||||
|
self:T3(self.lid.."EJECT: player = "..tostring(_playername))
|
||||||
|
|
||||||
|
if _unit and _playername then
|
||||||
|
self:T(self.lid..string.format("Player %s removed!",_playername))
|
||||||
|
|
||||||
|
-- Get player flight.
|
||||||
|
local flight=self.players[_playername]
|
||||||
|
|
||||||
|
-- Remove flight completely from all queues and collapse marshal if necessary.
|
||||||
|
if flight then
|
||||||
|
self:_RemoveFlight(flight, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Debug message.
|
||||||
|
self:T(self.lid..string.format("AI unit %s removed!", EventData.IniUnitName))
|
||||||
|
|
||||||
|
-- Remove element/unit from flight group and from all queues if no elements alive.
|
||||||
|
self:_RemoveUnitFromFlight(EventData.IniUnit)
|
||||||
|
|
||||||
|
-- What could happen is, that another element has landed (recovered) already and this one crashes.
|
||||||
|
-- This would mean that the flight would not be deleted from the queue ==> Check if section recovered.
|
||||||
|
local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup, self.flights)
|
||||||
|
self:_CheckSectionRecovered(flight)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Airboss event handler for event player leave unit.
|
--- Airboss event handler for event player leave unit.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
@@ -10041,6 +10106,27 @@ function AIRBOSS:_Groove(playerData)
|
|||||||
-- Distance in NM.
|
-- Distance in NM.
|
||||||
local d=UTILS.MetersToNM(rho)
|
local d=UTILS.MetersToNM(rho)
|
||||||
|
|
||||||
|
-- Drift on lineup.
|
||||||
|
if rho>=RAR and rho<=RIM then
|
||||||
|
if gd.LUE>0.22 and lineupError<-0.22 then
|
||||||
|
env.info" Drift Right across centre ==> DR-"
|
||||||
|
gd.Drift=" DR"
|
||||||
|
self:T(self.lid..string.format("Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
|
||||||
|
elseif gd.LUE<-0.22 and lineupError>0.22 then
|
||||||
|
env.info" Drift Left ==> DL-"
|
||||||
|
gd.Drift=" DL"
|
||||||
|
self:T(self.lid..string.format("Got Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
|
||||||
|
elseif gd.LUE>0.13 and lineupError<-0.14 then
|
||||||
|
env.info" Little Drift Right across centre ==> (DR-)"
|
||||||
|
gd.Drift=" (DR)"
|
||||||
|
self:T(self.lid..string.format("Got Little Drift Right across centre at step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
|
||||||
|
elseif gd.LUE<-0.13 and lineupError>0.14 then
|
||||||
|
env.info" Little Drift Left across centre ==> (DL-)"
|
||||||
|
gd.Drift=" (DL)"
|
||||||
|
self:E(self.lid..string.format("Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Update max deviation of line up error.
|
-- Update max deviation of line up error.
|
||||||
if math.abs(lineupError)>math.abs(gd.LUE) then
|
if math.abs(lineupError)>math.abs(gd.LUE) then
|
||||||
self:T(self.lid..string.format("Got bigger LUE at step %s, d=%.3f: LUE %.3f>%.3f", gs, d, lineupError, gd.LUE))
|
self:T(self.lid..string.format("Got bigger LUE at step %s, d=%.3f: LUE %.3f>%.3f", gs, d, lineupError, gd.LUE))
|
||||||
@@ -10339,24 +10425,25 @@ function AIRBOSS:_GetSternCoord()
|
|||||||
local FB=self:GetFinalBearing()
|
local FB=self:GetFinalBearing()
|
||||||
|
|
||||||
-- Stern coordinate (sterndist<0). Also translate 10 meters starboard wrt Final bearing.
|
-- Stern coordinate (sterndist<0). Also translate 10 meters starboard wrt Final bearing.
|
||||||
local stern=self:GetCoordinate()
|
self.sterncoord:UpdateFromCoordinate(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 then
|
||||||
-- Tarawa: Translate 8 meters port.
|
-- Tarawa: Translate 8 meters port.
|
||||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(8, FB-90)
|
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
|
||||||
-- Stennis: translate 7 meters starboard wrt Final bearing.
|
-- Stennis: translate 7 meters starboard wrt Final bearing.
|
||||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(7, FB+90)
|
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(7, FB+90, true, true)
|
||||||
else
|
else
|
||||||
-- Nimitz SC: translate 8 meters starboard wrt Final bearing.
|
-- Nimitz SC: translate 8 meters starboard wrt Final bearing.
|
||||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(9.5, FB+90)
|
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(9.5, FB+90, true, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set altitude.
|
-- Set altitude.
|
||||||
stern:SetAltitude(self.carrierparam.deckheight)
|
self.sterncoord:SetAltitude(self.carrierparam.deckheight)
|
||||||
|
|
||||||
return stern
|
return self.sterncoord
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get wire from landing position.
|
--- Get wire from landing position.
|
||||||
@@ -10475,6 +10562,9 @@ function AIRBOSS:_Trapped(playerData)
|
|||||||
dcorr=100
|
dcorr=100
|
||||||
elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
|
elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
|
||||||
-- A-4E gets slowed down much faster the the F/A-18C!
|
-- A-4E gets slowed down much faster the the F/A-18C!
|
||||||
|
dcorr=56
|
||||||
|
elseif playerData.actype==AIRBOSS.AircraftCarrier.T45C then
|
||||||
|
-- T-45 also gets slowed down much faster the the F/A-18C.
|
||||||
dcorr=56
|
dcorr=56
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -10561,6 +10651,8 @@ end
|
|||||||
-- @return Core.Zone#ZONE_POLYGON_BASE Initial zone.
|
-- @return Core.Zone#ZONE_POLYGON_BASE Initial zone.
|
||||||
function AIRBOSS:_GetZoneInitial(case)
|
function AIRBOSS:_GetZoneInitial(case)
|
||||||
|
|
||||||
|
self.zoneInitial=self.zoneInitial or ZONE_POLYGON_BASE:New("Zone CASE I/II Initial")
|
||||||
|
|
||||||
-- Get radial, i.e. inverse of BRC.
|
-- Get radial, i.e. inverse of BRC.
|
||||||
local radial=self:GetRadial(2, false, false)
|
local radial=self:GetRadial(2, false, false)
|
||||||
|
|
||||||
@@ -10568,7 +10660,7 @@ function AIRBOSS:_GetZoneInitial(case)
|
|||||||
local cv=self:GetCoordinate()
|
local cv=self:GetCoordinate()
|
||||||
|
|
||||||
-- Vec2 array.
|
-- Vec2 array.
|
||||||
local vec2
|
local vec2={}
|
||||||
|
|
||||||
if case==1 then
|
if case==1 then
|
||||||
-- Case I
|
-- Case I
|
||||||
@@ -10599,9 +10691,12 @@ function AIRBOSS:_GetZoneInitial(case)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Polygon zone.
|
-- Polygon zone.
|
||||||
local zone=ZONE_POLYGON_BASE:New("Zone CASE I/II Initial", vec2)
|
--local zone=ZONE_POLYGON_BASE:New("Zone CASE I/II Initial", vec2)
|
||||||
|
|
||||||
return zone
|
self.zoneInitial:UpdateFromVec2(vec2)
|
||||||
|
|
||||||
|
--return zone
|
||||||
|
return self.zoneInitial
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get lineup groove zone.
|
--- Get lineup groove zone.
|
||||||
@@ -10609,6 +10704,8 @@ end
|
|||||||
-- @return Core.Zone#ZONE_POLYGON_BASE Lineup zone.
|
-- @return Core.Zone#ZONE_POLYGON_BASE Lineup zone.
|
||||||
function AIRBOSS:_GetZoneLineup()
|
function AIRBOSS:_GetZoneLineup()
|
||||||
|
|
||||||
|
self.zoneLineup=self.zoneLineup or ZONE_POLYGON_BASE:New("Zone Lineup")
|
||||||
|
|
||||||
-- Get radial, i.e. inverse of BRC.
|
-- Get radial, i.e. inverse of BRC.
|
||||||
local fbi=self:GetRadial(1, false, false)
|
local fbi=self:GetRadial(1, false, false)
|
||||||
|
|
||||||
@@ -10625,10 +10722,13 @@ function AIRBOSS:_GetZoneLineup()
|
|||||||
-- Vec2 array.
|
-- Vec2 array.
|
||||||
local vec2={c1:GetVec2(), c2:GetVec2(), c3:GetVec2(), c4:GetVec2(), c5:GetVec2()}
|
local vec2={c1:GetVec2(), c2:GetVec2(), c3:GetVec2(), c4:GetVec2(), c5:GetVec2()}
|
||||||
|
|
||||||
-- Polygon zone.
|
self.zoneLineup:UpdateFromVec2(vec2)
|
||||||
local zone=ZONE_POLYGON_BASE:New("Zone Lineup", vec2)
|
|
||||||
|
|
||||||
return zone
|
-- Polygon zone.
|
||||||
|
--local zone=ZONE_POLYGON_BASE:New("Zone Lineup", vec2)
|
||||||
|
--return zone
|
||||||
|
|
||||||
|
return self.zoneLineup
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -10640,6 +10740,8 @@ end
|
|||||||
-- @return Core.Zone#ZONE_POLYGON_BASE Groove zone.
|
-- @return Core.Zone#ZONE_POLYGON_BASE Groove zone.
|
||||||
function AIRBOSS:_GetZoneGroove(l, w, b)
|
function AIRBOSS:_GetZoneGroove(l, w, b)
|
||||||
|
|
||||||
|
self.zoneGroove=self.zoneGroove or ZONE_POLYGON_BASE:New("Zone Groove")
|
||||||
|
|
||||||
l=l or 1.50
|
l=l or 1.50
|
||||||
w=w or 0.25
|
w=w or 0.25
|
||||||
b=b or 0.10
|
b=b or 0.10
|
||||||
@@ -10661,10 +10763,13 @@ function AIRBOSS:_GetZoneGroove(l, w, b)
|
|||||||
-- Vec2 array.
|
-- Vec2 array.
|
||||||
local vec2={c1:GetVec2(), c2:GetVec2(), c3:GetVec2(), c4:GetVec2(), c5:GetVec2(), c6:GetVec2()}
|
local vec2={c1:GetVec2(), c2:GetVec2(), c3:GetVec2(), c4:GetVec2(), c5:GetVec2(), c6:GetVec2()}
|
||||||
|
|
||||||
-- Polygon zone.
|
self.zoneGroove:UpdateFromVec2(vec2)
|
||||||
local zone=ZONE_POLYGON_BASE:New("Zone Groove", vec2)
|
|
||||||
|
|
||||||
return zone
|
-- Polygon zone.
|
||||||
|
--local zone=ZONE_POLYGON_BASE:New("Zone Groove", vec2)
|
||||||
|
--return zone
|
||||||
|
|
||||||
|
return self.zoneGroove
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get Bullseye zone with radius 1 NM and DME 3 NM from the carrier. Radial depends on recovery case.
|
--- Get Bullseye zone with radius 1 NM and DME 3 NM from the carrier. Radial depends on recovery case.
|
||||||
@@ -10688,8 +10793,9 @@ function AIRBOSS:_GetZoneBullseye(case)
|
|||||||
|
|
||||||
-- Create zone.
|
-- Create zone.
|
||||||
local zone=ZONE_RADIUS:New("Zone Bullseye", vec2, radius)
|
local zone=ZONE_RADIUS:New("Zone Bullseye", vec2, radius)
|
||||||
|
|
||||||
return zone
|
return zone
|
||||||
|
|
||||||
|
--self.zoneBullseye=self.zoneBullseye or ZONE_RADIUS:New("Zone Bullseye", vec2, radius)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get dirty up zone with radius 1 NM and DME 9 NM from the carrier. Radial depends on recovery case.
|
--- Get dirty up zone with radius 1 NM and DME 9 NM from the carrier. Radial depends on recovery case.
|
||||||
@@ -10885,6 +10991,8 @@ end
|
|||||||
-- @return Core.Zone#ZONE Zone surrounding the carrier.
|
-- @return Core.Zone#ZONE Zone surrounding the carrier.
|
||||||
function AIRBOSS:_GetZoneCarrierBox()
|
function AIRBOSS:_GetZoneCarrierBox()
|
||||||
|
|
||||||
|
self.zoneCarrierbox=self.zoneCarrierbox or ZONE_POLYGON_BASE:New("Carrier Box Zone")
|
||||||
|
|
||||||
-- Stern coordinate.
|
-- Stern coordinate.
|
||||||
local S=self:_GetSternCoord()
|
local S=self:_GetSternCoord()
|
||||||
|
|
||||||
@@ -10913,9 +11021,12 @@ function AIRBOSS:_GetZoneCarrierBox()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Create polygon zone.
|
-- Create polygon zone.
|
||||||
local zone=ZONE_POLYGON_BASE:New("Carrier Box Zone", vec2)
|
--local zone=ZONE_POLYGON_BASE:New("Carrier Box Zone", vec2)
|
||||||
|
--return zone
|
||||||
|
|
||||||
return zone
|
self.zoneCarrierbox:UpdateFromVec2(vec2)
|
||||||
|
|
||||||
|
return self.zoneCarrierbox
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get zone of landing runway.
|
--- Get zone of landing runway.
|
||||||
@@ -10923,6 +11034,8 @@ end
|
|||||||
-- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway.
|
-- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway.
|
||||||
function AIRBOSS:_GetZoneRunwayBox()
|
function AIRBOSS:_GetZoneRunwayBox()
|
||||||
|
|
||||||
|
self.zoneRunwaybox=self.zoneRunwaybox or ZONE_POLYGON_BASE:New("Landing Runway Zone")
|
||||||
|
|
||||||
-- Stern coordinate.
|
-- Stern coordinate.
|
||||||
local S=self:_GetSternCoord()
|
local S=self:_GetSternCoord()
|
||||||
|
|
||||||
@@ -10945,9 +11058,12 @@ function AIRBOSS:_GetZoneRunwayBox()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Create polygon zone.
|
-- Create polygon zone.
|
||||||
local zone=ZONE_POLYGON_BASE:New("Landing Runway Zone", vec2)
|
--local zone=ZONE_POLYGON_BASE:New("Landing Runway Zone", vec2)
|
||||||
|
--return zone
|
||||||
|
|
||||||
return zone
|
self.zoneRunwaybox:UpdateFromVec2(vec2)
|
||||||
|
|
||||||
|
return self.zoneRunwaybox
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -11050,12 +11166,14 @@ function AIRBOSS:_GetZoneHolding(case, stack)
|
|||||||
-- Post 2.5 NM port of carrier.
|
-- Post 2.5 NM port of carrier.
|
||||||
local Post=self:GetCoordinate():Translate(D, hdg+270)
|
local Post=self:GetCoordinate():Translate(D, hdg+270)
|
||||||
|
|
||||||
|
--TODO: update zone not creating a new one.
|
||||||
|
|
||||||
-- Create holding zone.
|
-- Create holding zone.
|
||||||
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 then
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@@ -11074,10 +11192,12 @@ function AIRBOSS:_GetZoneHolding(case, stack)
|
|||||||
|
|
||||||
-- Square zone length=7NM width=6 NM behind the carrier starting at angels+15 NM behind the carrier.
|
-- Square zone length=7NM width=6 NM behind the carrier starting at angels+15 NM behind the carrier.
|
||||||
-- So stay 0-5 NM (+1 NM error margin) port of carrier.
|
-- So stay 0-5 NM (+1 NM error margin) port of carrier.
|
||||||
zoneHolding=ZONE_POLYGON_BASE:New("CASE II/III Holding Zone", p)
|
self.zoneHolding=self.zoneHolding or ZONE_POLYGON_BASE:New("CASE II/III Holding Zone")
|
||||||
|
|
||||||
|
self.zoneHolding:UpdateFromVec2(p)
|
||||||
end
|
end
|
||||||
|
|
||||||
return zoneHolding
|
return self.zoneHolding
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get zone where player are automatically commence when enter.
|
--- Get zone where player are automatically commence when enter.
|
||||||
@@ -11118,7 +11238,9 @@ function AIRBOSS:_GetZoneCommence(case, stack)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Create holding zone.
|
-- Create holding zone.
|
||||||
zone=ZONE_RADIUS:New("CASE I Commence Zone", Three:GetVec2(), R)
|
self.zoneCommence=self.zoneCommence or ZONE_RADIUS:New("CASE I Commence Zone")
|
||||||
|
|
||||||
|
self.zoneCommence:UpdateFromVec2(Three:GetVec2(), R)
|
||||||
|
|
||||||
else
|
else
|
||||||
-- Case II/III
|
-- Case II/III
|
||||||
@@ -11149,11 +11271,13 @@ function AIRBOSS:_GetZoneCommence(case, stack)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Zone polygon.
|
-- Zone polygon.
|
||||||
zone=ZONE_POLYGON_BASE:New("CASE II/III Commence Zone", p)
|
self.zoneCommence=self.zoneCommence or ZONE_POLYGON_BASE:New("CASE II/III Commence Zone")
|
||||||
|
|
||||||
|
self.zoneCommence:UpdateFromVec2(p)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return zone
|
return self.zoneCommence
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -11397,8 +11521,11 @@ end
|
|||||||
-- @return Core.Point#COORDINATE Optimal landing coordinate.
|
-- @return Core.Point#COORDINATE Optimal landing coordinate.
|
||||||
function AIRBOSS:_GetOptLandingCoordinate()
|
function AIRBOSS:_GetOptLandingCoordinate()
|
||||||
|
|
||||||
|
-- Start with stern coordiante.
|
||||||
|
self.landingcoord:UpdateFromCoordinate(self:_GetSternCoord())
|
||||||
|
|
||||||
-- Stern coordinate.
|
-- Stern coordinate.
|
||||||
local stern=self:_GetSternCoord()
|
--local stern=self:_GetSternCoord()
|
||||||
|
|
||||||
-- Final bearing.
|
-- Final bearing.
|
||||||
local FB=self:GetFinalBearing(false)
|
local FB=self:GetFinalBearing(false)
|
||||||
@@ -11406,10 +11533,11 @@ function AIRBOSS:_GetOptLandingCoordinate()
|
|||||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||||
|
|
||||||
-- Landing 100 ft abeam, 120 ft alt.
|
-- Landing 100 ft abeam, 120 ft alt.
|
||||||
stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
|
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true)
|
||||||
|
--stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
|
||||||
|
|
||||||
-- Alitude 120 ft.
|
-- Alitude 120 ft.
|
||||||
stern:SetAltitude(UTILS.FeetToMeters(120))
|
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
@@ -11417,15 +11545,15 @@ function AIRBOSS:_GetOptLandingCoordinate()
|
|||||||
if self.carrierparam.wire3 then
|
if self.carrierparam.wire3 then
|
||||||
-- We take the position of the 3rd wire to approximately account for the length of the aircraft.
|
-- We take the position of the 3rd wire to approximately account for the length of the aircraft.
|
||||||
local w3=self.carrierparam.wire3
|
local w3=self.carrierparam.wire3
|
||||||
stern=stern:Translate(w3, FB, true)
|
self.landingcoord:Translate(w3, FB, true, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add 2 meters to account for aircraft height.
|
-- Add 2 meters to account for aircraft height.
|
||||||
stern.y=stern.y+2
|
self.landingcoord.y=self.landingcoord.y+2
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return stern
|
return self.landingcoord
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get landing spot on Tarawa.
|
--- Get landing spot on Tarawa.
|
||||||
@@ -11433,8 +11561,10 @@ end
|
|||||||
-- @return Core.Point#COORDINATE Primary landing spot coordinate.
|
-- @return Core.Point#COORDINATE Primary landing spot coordinate.
|
||||||
function AIRBOSS:_GetLandingSpotCoordinate()
|
function AIRBOSS:_GetLandingSpotCoordinate()
|
||||||
|
|
||||||
|
self.landingspotcoord:UpdateFromCoordinate(self:_GetSternCoord())
|
||||||
|
|
||||||
-- Stern coordinate.
|
-- Stern coordinate.
|
||||||
local stern=self:_GetSternCoord()
|
--local stern=self:_GetSternCoord()
|
||||||
|
|
||||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||||
|
|
||||||
@@ -11442,11 +11572,11 @@ function AIRBOSS:_GetLandingSpotCoordinate()
|
|||||||
local hdg=self:GetHeading()
|
local hdg=self:GetHeading()
|
||||||
|
|
||||||
-- Primary landing spot 7.5
|
-- Primary landing spot 7.5
|
||||||
stern=stern:Translate(57, hdg):SetAltitude(self.carrierparam.deckheight)
|
self.landingspotcoord:Translate(57, hdg, true, true):SetAltitude(self.carrierparam.deckheight)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return stern
|
return self.landingspotcoord
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get true (or magnetic) heading of carrier.
|
--- Get true (or magnetic) heading of carrier.
|
||||||
@@ -11509,7 +11639,7 @@ end
|
|||||||
|
|
||||||
--- Get wind speed on carrier deck parallel and perpendicular to runway.
|
--- Get wind speed on carrier deck parallel and perpendicular to runway.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param #number alt Altitude in meters. Default 50 m.
|
-- @param #number alt Altitude in meters. Default 15 m. (change made from 50m from Discord discussion from Sickdog)
|
||||||
-- @return #number Wind component parallel to runway im m/s.
|
-- @return #number Wind component parallel to runway im m/s.
|
||||||
-- @return #number Wind component perpendicular to runway in m/s.
|
-- @return #number Wind component perpendicular to runway in m/s.
|
||||||
-- @return #number Total wind strength in m/s.
|
-- @return #number Total wind strength in m/s.
|
||||||
@@ -11532,7 +11662,7 @@ function AIRBOSS:GetWindOnDeck(alt)
|
|||||||
zc=UTILS.Rotate2D(zc, -self.carrierparam.rwyangle)
|
zc=UTILS.Rotate2D(zc, -self.carrierparam.rwyangle)
|
||||||
|
|
||||||
-- Wind (from) vector
|
-- Wind (from) vector
|
||||||
local vw=cv:GetWindWithTurbulenceVec3(alt or 50)
|
local vw=cv:GetWindWithTurbulenceVec3(alt or 15)
|
||||||
|
|
||||||
-- Total wind velocity vector.
|
-- Total wind velocity vector.
|
||||||
-- Carrier velocity has to be negative. If carrier drives in the direction the wind is blowing from, we have less wind in total.
|
-- Carrier velocity has to be negative. If carrier drives in the direction the wind is blowing from, we have less wind in total.
|
||||||
@@ -11946,15 +12076,15 @@ function AIRBOSS:_EvalGrooveTime(playerData)
|
|||||||
|
|
||||||
local grade=""
|
local grade=""
|
||||||
if t<9 then
|
if t<9 then
|
||||||
grade="--"
|
grade="_NESA_"
|
||||||
elseif t<12 then
|
elseif t<15 then
|
||||||
grade="(OK)"
|
grade="NESA"
|
||||||
elseif t<22 then
|
elseif t<19 then
|
||||||
grade="OK"
|
grade="OK Groove"
|
||||||
elseif t<=24 then
|
elseif t<=24 then
|
||||||
grade="(OK)"
|
grade="(LIG)"
|
||||||
else
|
else
|
||||||
grade="--"
|
grade="LIG"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The unicorn!
|
-- The unicorn!
|
||||||
@@ -11993,9 +12123,9 @@ function AIRBOSS:_LSOgrade(playerData)
|
|||||||
local nS=count(G, '%(')
|
local nS=count(G, '%(')
|
||||||
local nN=N-nS-nL
|
local nN=N-nS-nL
|
||||||
|
|
||||||
-- Groove time 16-18 sec for a unicorn.
|
-- Groove time 15-18.99 sec for a unicorn.
|
||||||
local Tgroove=playerData.Tgroove
|
local Tgroove=playerData.Tgroove
|
||||||
local TgrooveUnicorn=Tgroove and (Tgroove>=16.0 and Tgroove<=18.0) or false
|
local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false
|
||||||
|
|
||||||
local grade
|
local grade
|
||||||
local points
|
local points
|
||||||
@@ -12129,6 +12259,34 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
|||||||
-- Aircraft specific AoA values.
|
-- Aircraft specific AoA values.
|
||||||
local acaoa=self:_GetAircraftAoA(playerData)
|
local acaoa=self:_GetAircraftAoA(playerData)
|
||||||
|
|
||||||
|
--Angled Approach.
|
||||||
|
local P=nil
|
||||||
|
if step==AIRBOSS.PatternStep.GROOVE_XX and ROL<=4.0 and playerData.case<3 then
|
||||||
|
if LUE>self.lue.RIGHT then
|
||||||
|
P=underline("AA")
|
||||||
|
elseif
|
||||||
|
LUE>self.lue.RightMed then
|
||||||
|
P="AA "
|
||||||
|
elseif
|
||||||
|
LUE>self.lue.Right then
|
||||||
|
P=little("AA")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--Overshoot Start.
|
||||||
|
local O=nil
|
||||||
|
if step==AIRBOSS.PatternStep.GROOVE_XX then
|
||||||
|
if LUE<self.lue.LEFT then
|
||||||
|
O=underline("OS")
|
||||||
|
elseif
|
||||||
|
LUE<self.lue.Left then
|
||||||
|
O="OS"
|
||||||
|
elseif
|
||||||
|
LUE<self.lue._min then
|
||||||
|
O=little("OS")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Speed via AoA. Depends on aircraft type.
|
-- Speed via AoA. Depends on aircraft type.
|
||||||
local S=nil
|
local S=nil
|
||||||
if AOA>acaoa.SLOW then
|
if AOA>acaoa.SLOW then
|
||||||
@@ -12161,7 +12319,7 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
|||||||
A=little("LO")
|
A=little("LO")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Line up. Good [-0.5, 0.5]
|
-- Line up. XX Step replaced by Overshoot start (OS). Good [-0.5, 0.5]
|
||||||
local D=nil
|
local D=nil
|
||||||
if LUE>self.lue.RIGHT then
|
if LUE>self.lue.RIGHT then
|
||||||
D=underline("LUL")
|
D=underline("LUL")
|
||||||
@@ -12169,13 +12327,23 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
|||||||
D="LUL"
|
D="LUL"
|
||||||
elseif LUE>self.lue._max then
|
elseif LUE>self.lue._max then
|
||||||
D=little("LUL")
|
D=little("LUL")
|
||||||
elseif LUE<self.lue.LEFT then
|
elseif playerData.case<3 then
|
||||||
|
if LUE<self.lue.LEFT and step~=AIRBOSS.PatternStep.GROOVE_XX then
|
||||||
|
D=underline("LUR")
|
||||||
|
elseif LUE<self.lue.Left and step~=AIRBOSS.PatternStep.GROOVE_XX then
|
||||||
|
D="LUR"
|
||||||
|
elseif LUE<self.lue._min and step~=AIRBOSS.PatternStep.GROOVE_XX then
|
||||||
|
D=little("LUR")
|
||||||
|
end
|
||||||
|
elseif playerData.case==3 then
|
||||||
|
if LUE<self.lue.LEFT then
|
||||||
D=underline("LUR")
|
D=underline("LUR")
|
||||||
elseif LUE<self.lue.Left then
|
elseif LUE<self.lue.Left then
|
||||||
D="LUR"
|
D="LUR"
|
||||||
elseif LUE<self.lue._min then
|
elseif LUE<self.lue._min then
|
||||||
D=little("LUR")
|
D=little("LUR")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Compile.
|
-- Compile.
|
||||||
local G=""
|
local G=""
|
||||||
@@ -12184,6 +12352,11 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
|||||||
if fdata.FlyThrough then
|
if fdata.FlyThrough then
|
||||||
G=G..fdata.FlyThrough
|
G=G..fdata.FlyThrough
|
||||||
end
|
end
|
||||||
|
-- Angled Approach - doesn't affect score, advisory only.
|
||||||
|
if P then
|
||||||
|
G=G..P
|
||||||
|
n=n
|
||||||
|
end
|
||||||
-- Speed.
|
-- Speed.
|
||||||
if S then
|
if S then
|
||||||
G=G..S
|
G=G..S
|
||||||
@@ -12199,6 +12372,16 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
|
|||||||
G=G..D
|
G=G..D
|
||||||
n=n+1
|
n=n+1
|
||||||
end
|
end
|
||||||
|
--Drift in Lineup
|
||||||
|
if fdata.Drift then
|
||||||
|
G=G..fdata.Drift
|
||||||
|
n=n -- Drift doesn't affect score, advisory only.
|
||||||
|
end
|
||||||
|
-- Overshoot.
|
||||||
|
if O then
|
||||||
|
G=G..O
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
|
||||||
-- Add current step.
|
-- Add current step.
|
||||||
local step=self:_GS(step)
|
local step=self:_GS(step)
|
||||||
@@ -14025,6 +14208,8 @@ function AIRBOSS:_GetACNickname(actype)
|
|||||||
local nickname="unknown"
|
local nickname="unknown"
|
||||||
if actype==AIRBOSS.AircraftCarrier.A4EC then
|
if actype==AIRBOSS.AircraftCarrier.A4EC then
|
||||||
nickname="Skyhawk"
|
nickname="Skyhawk"
|
||||||
|
elseif actype==AIRBOSS.AircraftCarrier.T45C then
|
||||||
|
nickname="Goshawk"
|
||||||
elseif actype==AIRBOSS.AircraftCarrier.AV8B then
|
elseif actype==AIRBOSS.AircraftCarrier.AV8B then
|
||||||
nickname="Harrier"
|
nickname="Harrier"
|
||||||
elseif actype==AIRBOSS.AircraftCarrier.E2D then
|
elseif actype==AIRBOSS.AircraftCarrier.E2D then
|
||||||
@@ -14390,9 +14575,15 @@ end
|
|||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @return Core.Point#COORDINATE Carrier coordinate.
|
-- @return Core.Point#COORDINATE Carrier coordinate.
|
||||||
function AIRBOSS:GetCoordinate()
|
function AIRBOSS:GetCoordinate()
|
||||||
return self.carrier:GetCoordinate()
|
return self.carrier:GetCoord()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get carrier coordinate.
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
-- @return Core.Point#COORDINATE Carrier coordinate.
|
||||||
|
function AIRBOSS:GetCoord()
|
||||||
|
return self.carrier:GetCoord()
|
||||||
|
end
|
||||||
|
|
||||||
--- Get static weather of this mission from env.mission.weather.
|
--- Get static weather of this mission from env.mission.weather.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
@@ -15526,8 +15717,12 @@ 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(" Marshal radial %03d°.", radial)
|
text=text..string.format(" BRC %03d°.", self:GetBRC())
|
||||||
|
elseif case==2 then
|
||||||
|
text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC())
|
||||||
|
elseif case==3 then
|
||||||
|
text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false))
|
||||||
end
|
end
|
||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
|||||||
2097
Moose Development/Moose/Ops/CSAR.lua
Normal file
2097
Moose Development/Moose/Ops/CSAR.lua
Normal file
File diff suppressed because it is too large
Load Diff
2948
Moose Development/Moose/Ops/CTLD.lua
Normal file
2948
Moose Development/Moose/Ops/CTLD.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,10 @@
|
|||||||
--- **Core** - Is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions.
|
--- **Sound** - Radio transmissions.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Provide radio functionality to broadcast radio transmissions.
|
-- * Provide radio functionality to broadcast radio transmissions.
|
||||||
-- * Provide beacon functionality to assist pilots.
|
|
||||||
--
|
|
||||||
-- The Radio contains 2 classes : RADIO and BEACON
|
|
||||||
--
|
--
|
||||||
-- What are radio communications in DCS?
|
-- What are radio communications in DCS?
|
||||||
--
|
--
|
||||||
@@ -35,13 +32,13 @@
|
|||||||
--
|
--
|
||||||
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
||||||
--
|
--
|
||||||
-- @module Core.Radio
|
-- @module Sound.Radio
|
||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
|
|
||||||
--- Models the radio capability.
|
--- *It's not true I had nothing on, I had the radio on.* -- Marilyn Monroe
|
||||||
--
|
--
|
||||||
-- ## RADIO usage
|
-- # RADIO usage
|
||||||
--
|
--
|
||||||
-- There are 3 steps to a successful radio transmission.
|
-- There are 3 steps to a successful radio transmission.
|
||||||
--
|
--
|
||||||
@@ -395,438 +392,3 @@ function RADIO:StopBroadcast()
|
|||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
|
||||||
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
|
||||||
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
|
||||||
-- attach to a cargo crate, for exemple.
|
|
||||||
--
|
|
||||||
-- ## AA TACAN Beacon usage
|
|
||||||
--
|
|
||||||
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
|
||||||
-- Use @#BEACON:StopAATACAN}() to stop it.
|
|
||||||
--
|
|
||||||
-- ## General Purpose Radio Beacon usage
|
|
||||||
--
|
|
||||||
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
|
||||||
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
|
||||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
|
||||||
--
|
|
||||||
-- @type BEACON
|
|
||||||
-- @field #string ClassName Name of the class "BEACON".
|
|
||||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
|
|
||||||
-- @extends Core.Base#BASE
|
|
||||||
BEACON = {
|
|
||||||
ClassName = "BEACON",
|
|
||||||
Positionable = nil,
|
|
||||||
name=nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Beacon types supported by DCS.
|
|
||||||
-- @type BEACON.Type
|
|
||||||
-- @field #number NULL
|
|
||||||
-- @field #number VOR
|
|
||||||
-- @field #number DME
|
|
||||||
-- @field #number VOR_DME
|
|
||||||
-- @field #number TACAN TACtical Air Navigation system.
|
|
||||||
-- @field #number VORTAC
|
|
||||||
-- @field #number RSBN
|
|
||||||
-- @field #number BROADCAST_STATION
|
|
||||||
-- @field #number HOMER
|
|
||||||
-- @field #number AIRPORT_HOMER
|
|
||||||
-- @field #number AIRPORT_HOMER_WITH_MARKER
|
|
||||||
-- @field #number ILS_FAR_HOMER
|
|
||||||
-- @field #number ILS_NEAR_HOMER
|
|
||||||
-- @field #number ILS_LOCALIZER
|
|
||||||
-- @field #number ILS_GLIDESLOPE
|
|
||||||
-- @field #number PRMG_LOCALIZER
|
|
||||||
-- @field #number PRMG_GLIDESLOPE
|
|
||||||
-- @field #number ICLS Same as ICLS glideslope.
|
|
||||||
-- @field #number ICLS_LOCALIZER
|
|
||||||
-- @field #number ICLS_GLIDESLOPE
|
|
||||||
-- @field #number NAUTICAL_HOMER
|
|
||||||
BEACON.Type={
|
|
||||||
NULL = 0,
|
|
||||||
VOR = 1,
|
|
||||||
DME = 2,
|
|
||||||
VOR_DME = 3,
|
|
||||||
TACAN = 4,
|
|
||||||
VORTAC = 5,
|
|
||||||
RSBN = 128,
|
|
||||||
BROADCAST_STATION = 1024,
|
|
||||||
HOMER = 8,
|
|
||||||
AIRPORT_HOMER = 4104,
|
|
||||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
|
||||||
ILS_FAR_HOMER = 16408,
|
|
||||||
ILS_NEAR_HOMER = 16424,
|
|
||||||
ILS_LOCALIZER = 16640,
|
|
||||||
ILS_GLIDESLOPE = 16896,
|
|
||||||
PRMG_LOCALIZER = 33024,
|
|
||||||
PRMG_GLIDESLOPE = 33280,
|
|
||||||
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
|
|
||||||
ICLS_LOCALIZER = 131328,
|
|
||||||
ICLS_GLIDESLOPE = 131584,
|
|
||||||
NAUTICAL_HOMER = 65536,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
|
|
||||||
-- @type BEACON.System
|
|
||||||
-- @field #number PAR_10 ?
|
|
||||||
-- @field #number RSBN_5 Russian VOR/DME system.
|
|
||||||
-- @field #number TACAN TACtical Air Navigation system on ground.
|
|
||||||
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
|
||||||
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
|
||||||
-- @field #number VOR Very High Frequency Omni-Directional Range
|
|
||||||
-- @field #number ILS_LOCALIZER ILS localizer
|
|
||||||
-- @field #number ILS_GLIDESLOPE ILS glideslope.
|
|
||||||
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
|
||||||
-- @field #number PRGM_GLIDESLOPE PRGM glideslope.
|
|
||||||
-- @field #number BROADCAST_STATION Broadcast station.
|
|
||||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
|
||||||
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
|
||||||
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
|
||||||
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
|
||||||
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
|
||||||
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
|
||||||
BEACON.System={
|
|
||||||
PAR_10 = 1,
|
|
||||||
RSBN_5 = 2,
|
|
||||||
TACAN = 3,
|
|
||||||
TACAN_TANKER_X = 4,
|
|
||||||
TACAN_TANKER_Y = 5,
|
|
||||||
VOR = 6,
|
|
||||||
ILS_LOCALIZER = 7,
|
|
||||||
ILS_GLIDESLOPE = 8,
|
|
||||||
PRMG_LOCALIZER = 9,
|
|
||||||
PRMG_GLIDESLOPE = 10,
|
|
||||||
BROADCAST_STATION = 11,
|
|
||||||
VORTAC = 12,
|
|
||||||
TACAN_AA_MODE_X = 13,
|
|
||||||
TACAN_AA_MODE_Y = 14,
|
|
||||||
VORDME = 15,
|
|
||||||
ICLS_LOCALIZER = 16,
|
|
||||||
ICLS_GLIDESLOPE = 17,
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.
|
|
||||||
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
|
||||||
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
|
|
||||||
function BEACON:New(Positionable)
|
|
||||||
|
|
||||||
-- Inherit BASE.
|
|
||||||
local self=BASE:Inherit(self, BASE:New()) --#BEACON
|
|
||||||
|
|
||||||
-- Debug.
|
|
||||||
self:F(Positionable)
|
|
||||||
|
|
||||||
-- Set positionable.
|
|
||||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
|
||||||
self.Positionable = Positionable
|
|
||||||
self.name=Positionable:GetName()
|
|
||||||
self:I(string.format("New BEACON %s", tostring(self.name)))
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Activates a TACAN BEACON.
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
|
|
||||||
-- @param #string Mode TACAN mode, i.e. the "Y" part in "10Y".
|
|
||||||
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon.
|
|
||||||
-- @param #boolean Bearing If true, beacon provides bearing information. If false (or nil), only distance information is available.
|
|
||||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
|
||||||
-- @return #BEACON self
|
|
||||||
-- @usage
|
|
||||||
-- -- Let's create a TACAN Beacon for a tanker
|
|
||||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
|
||||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
|
||||||
--
|
|
||||||
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
|
||||||
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
|
||||||
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
|
||||||
|
|
||||||
-- Get frequency.
|
|
||||||
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
|
||||||
|
|
||||||
-- Check.
|
|
||||||
if not Frequency then
|
|
||||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Beacon type.
|
|
||||||
local Type=BEACON.Type.TACAN
|
|
||||||
|
|
||||||
-- Beacon system.
|
|
||||||
local System=BEACON.System.TACAN
|
|
||||||
|
|
||||||
-- Check if unit is an aircraft and set system accordingly.
|
|
||||||
local AA=self.Positionable:IsAir()
|
|
||||||
if AA then
|
|
||||||
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
|
||||||
-- Check if "Y" mode is selected for aircraft.
|
|
||||||
if Mode~="Y" then
|
|
||||||
self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.", self.Positionable})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Attached unit.
|
|
||||||
local UnitID=self.Positionable:GetID()
|
|
||||||
|
|
||||||
-- Debug.
|
|
||||||
self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
|
|
||||||
|
|
||||||
-- Start beacon.
|
|
||||||
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
|
||||||
|
|
||||||
-- Stop sheduler.
|
|
||||||
if Duration then
|
|
||||||
self.Positionable:DeactivateBeacon(Duration)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Activates an ICLS BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param #number Channel ICLS channel.
|
|
||||||
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon.
|
|
||||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
|
||||||
-- @return #BEACON self
|
|
||||||
function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
|
||||||
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
|
|
||||||
|
|
||||||
-- Attached unit.
|
|
||||||
local UnitID=self.Positionable:GetID()
|
|
||||||
|
|
||||||
-- Debug
|
|
||||||
self:T2({"ICLS BEACON started!"})
|
|
||||||
|
|
||||||
-- Start beacon.
|
|
||||||
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
|
||||||
|
|
||||||
-- Stop sheduler
|
|
||||||
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
|
||||||
self.Positionable:DeactivateBeacon(Duration)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Activates a TACAN BEACON on an Aircraft.
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
|
||||||
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
|
||||||
-- @param #boolean Bearing Can the BEACON be homed on ?
|
|
||||||
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
|
||||||
-- @return #BEACON self
|
|
||||||
-- @usage
|
|
||||||
-- -- Let's create a TACAN Beacon for a tanker
|
|
||||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
|
||||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
|
||||||
--
|
|
||||||
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
|
|
||||||
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
|
||||||
self:F({TACANChannel, Message, Bearing, BeaconDuration})
|
|
||||||
|
|
||||||
local IsValid = true
|
|
||||||
|
|
||||||
if not self.Positionable:IsAir() then
|
|
||||||
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
|
|
||||||
if not Frequency then
|
|
||||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
|
||||||
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
|
||||||
local System
|
|
||||||
if Bearing then
|
|
||||||
System = 5
|
|
||||||
else
|
|
||||||
System = 14
|
|
||||||
end
|
|
||||||
|
|
||||||
if IsValid then -- Starts the BEACON
|
|
||||||
self:T2({"AA TACAN BEACON started !"})
|
|
||||||
self.Positionable:SetCommand({
|
|
||||||
id = "ActivateBeacon",
|
|
||||||
params = {
|
|
||||||
type = 4,
|
|
||||||
system = System,
|
|
||||||
callsign = Message,
|
|
||||||
frequency = Frequency,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
|
||||||
SCHEDULER:New(nil,
|
|
||||||
function()
|
|
||||||
self:StopAATACAN()
|
|
||||||
end, {}, BeaconDuration)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Stops the AA TACAN BEACON
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @return #BEACON self
|
|
||||||
function BEACON:StopAATACAN()
|
|
||||||
self:F()
|
|
||||||
if not self.Positionable then
|
|
||||||
self:E({"Start the beacon first before stoping it !"})
|
|
||||||
else
|
|
||||||
self.Positionable:SetCommand({
|
|
||||||
id = 'DeactivateBeacon',
|
|
||||||
params = {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Activates a general pupose Radio Beacon
|
|
||||||
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
|
||||||
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
|
||||||
-- They can home in on these specific frequencies :
|
|
||||||
-- * **Mi8**
|
|
||||||
-- * R-828 -> 20-60MHz
|
|
||||||
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
|
||||||
-- * ARK9 -> 150-1300KHz
|
|
||||||
-- * **Huey**
|
|
||||||
-- * AN/ARC-131 -> 30-76 Mhz FM
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param #string FileName The name of the audio file
|
|
||||||
-- @param #number Frequency in MHz
|
|
||||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
|
||||||
-- @param #number Power in W
|
|
||||||
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
|
||||||
-- @return #BEACON self
|
|
||||||
-- @usage
|
|
||||||
-- -- Let's create a beacon for a unit in distress.
|
|
||||||
-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131)
|
|
||||||
-- -- The beacon they use is battery-powered, and only lasts for 5 min
|
|
||||||
-- local UnitInDistress = UNIT:FindByName("Unit1")
|
|
||||||
-- local UnitBeacon = UnitInDistress:GetBeacon()
|
|
||||||
--
|
|
||||||
-- -- Set the beacon and start it
|
|
||||||
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
|
|
||||||
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
|
||||||
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
|
||||||
local IsValid = false
|
|
||||||
|
|
||||||
-- Check the filename
|
|
||||||
if type(FileName) == "string" then
|
|
||||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
|
||||||
if not FileName:find("l10n/DEFAULT/") then
|
|
||||||
FileName = "l10n/DEFAULT/" .. FileName
|
|
||||||
end
|
|
||||||
IsValid = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not IsValid then
|
|
||||||
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check the Frequency
|
|
||||||
if type(Frequency) ~= "number" and IsValid then
|
|
||||||
self:E({"Frequency invalid. ", Frequency})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
Frequency = Frequency * 1000000 -- Conversion to Hz
|
|
||||||
|
|
||||||
-- Check the modulation
|
|
||||||
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
|
||||||
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check the Power
|
|
||||||
if type(Power) ~= "number" and IsValid then
|
|
||||||
self:E({"Power is invalid. ", Power})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
|
||||||
|
|
||||||
if IsValid then
|
|
||||||
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
|
||||||
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
|
||||||
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
|
|
||||||
|
|
||||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
|
||||||
SCHEDULER:New( nil,
|
|
||||||
function()
|
|
||||||
self:StopRadioBeacon()
|
|
||||||
end, {}, BeaconDuration)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Stops the AA TACAN BEACON
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @return #BEACON self
|
|
||||||
function BEACON:StopRadioBeacon()
|
|
||||||
self:F()
|
|
||||||
-- The unique name of the transmission is the class ID
|
|
||||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param #number TACANChannel
|
|
||||||
-- @param #string TACANMode
|
|
||||||
-- @return #number Frequecy
|
|
||||||
-- @return #nil if parameters are invalid
|
|
||||||
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
|
||||||
self:F3({TACANChannel, TACANMode})
|
|
||||||
|
|
||||||
if type(TACANChannel) ~= "number" then
|
|
||||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
|
||||||
return nil -- error in arguments
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
|
||||||
-- I have no idea what it does but it seems to work
|
|
||||||
local A = 1151 -- 'X', channel >= 64
|
|
||||||
local B = 64 -- channel >= 64
|
|
||||||
|
|
||||||
if TACANChannel < 64 then
|
|
||||||
B = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if TACANMode == 'Y' then
|
|
||||||
A = 1025
|
|
||||||
if TACANChannel < 64 then
|
|
||||||
A = 1088
|
|
||||||
end
|
|
||||||
else -- 'X'
|
|
||||||
if TACANChannel < 64 then
|
|
||||||
A = 962
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return (A + TACANChannel - B) * 1000000
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
--- **Core** - Queues Radio Transmissions.
|
--- **Sound** - Queues Radio Transmissions.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Managed Radio Transmissions.
|
-- * Manage Radio Transmissions
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: funkyfranky
|
-- ### Authors: funkyfranky
|
||||||
--
|
--
|
||||||
-- @module Core.RadioQueue
|
-- @module Sound.RadioQueue
|
||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
--- Manages radio transmissions.
|
--- Manages radio transmissions.
|
||||||
--
|
--
|
||||||
|
-- The main goal of the RADIOQUEUE class is to string together multiple sound files to play a complete sentence.
|
||||||
|
-- The underlying problem is that radio transmissions in DCS are not queued but played "on top" of each other.
|
||||||
|
-- Therefore, to achive the goal, it is vital to know the precise duration how long it takes to play the sound file.
|
||||||
|
--
|
||||||
-- @type RADIOQUEUE
|
-- @type RADIOQUEUE
|
||||||
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
||||||
-- @field #boolean Debugmode Debug mode. More info.
|
-- @field #boolean Debugmode Debug mode. More info.
|
||||||
@@ -35,6 +39,7 @@
|
|||||||
-- @field #table numbers Table of number transmission parameters.
|
-- @field #table numbers Table of number transmission parameters.
|
||||||
-- @field #boolean checking Scheduler is checking the radio queue.
|
-- @field #boolean checking Scheduler is checking the radio queue.
|
||||||
-- @field #boolean schedonce Call ScheduleOnce instead of normal scheduler.
|
-- @field #boolean schedonce Call ScheduleOnce instead of normal scheduler.
|
||||||
|
-- @field Sound.SRS#MSRS msrs Moose SRS class.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
RADIOQUEUE = {
|
RADIOQUEUE = {
|
||||||
ClassName = "RADIOQUEUE",
|
ClassName = "RADIOQUEUE",
|
||||||
@@ -69,12 +74,14 @@ RADIOQUEUE = {
|
|||||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||||
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||||
-- @field #number interval Interval in seconds before next transmission.
|
-- @field #number interval Interval in seconds before next transmission.
|
||||||
|
-- @field Sound.SoundOutput#SOUNDFILE soundfile Sound file object to play via SRS.
|
||||||
|
-- @field Sound.SoundOutput#SOUNDTEXT soundtext Sound TTS object to play via SRS.
|
||||||
|
|
||||||
|
|
||||||
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #number frequency The radio frequency in MHz.
|
-- @param #number frequency The radio frequency in MHz.
|
||||||
-- @param #number modulation (Optional) The radio modulation. Default radio.modulation.AM.
|
-- @param #number modulation (Optional) The radio modulation. Default `radio.modulation.AM` (=0).
|
||||||
-- @param #string alias (Optional) Name of the radio queue.
|
-- @param #string alias (Optional) Name of the radio queue.
|
||||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||||
function RADIOQUEUE:New(frequency, modulation, alias)
|
function RADIOQUEUE:New(frequency, modulation, alias)
|
||||||
@@ -125,9 +132,9 @@ function RADIOQUEUE:Start(delay, dt)
|
|||||||
|
|
||||||
-- Start Scheduler.
|
-- Start Scheduler.
|
||||||
if self.schedonce then
|
if self.schedonce then
|
||||||
self:_CheckRadioQueueDelayed(delay)
|
self:_CheckRadioQueueDelayed(self.delay)
|
||||||
else
|
else
|
||||||
self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, delay, dt)
|
self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, self.delay, self.dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -170,6 +177,17 @@ function RADIOQUEUE:SetRadioPower(power)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set SRS.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param #string PathToSRS Path to SRS.
|
||||||
|
-- @param #number Port SRS port. Default 5002.
|
||||||
|
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||||
|
function RADIOQUEUE:SetSRS(PathToSRS, Port)
|
||||||
|
self.msrs=MSRS:New(PathToSRS, self.frequency/1000000, self.modulation)
|
||||||
|
self.msrs:SetPort(Port)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set parameters of a digit.
|
--- Set parameters of a digit.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #number digit The digit 0-9.
|
-- @param #number digit The digit 0-9.
|
||||||
@@ -202,7 +220,7 @@ end
|
|||||||
--- Add a transmission to the radio queue.
|
--- Add a transmission to the radio queue.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
||||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
-- @return #RADIOQUEUE self
|
||||||
function RADIOQUEUE:AddTransmission(transmission)
|
function RADIOQUEUE:AddTransmission(transmission)
|
||||||
self:F({transmission=transmission})
|
self:F({transmission=transmission})
|
||||||
|
|
||||||
@@ -221,7 +239,7 @@ function RADIOQUEUE:AddTransmission(transmission)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a transmission to the radio queue.
|
--- Create a new transmission and add it to the radio queue.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #string filename Name of the sound file. Usually an ogg or wav file type.
|
-- @param #string filename Name of the sound file. Usually an ogg or wav file type.
|
||||||
-- @param #number duration Duration in seconds the file lasts.
|
-- @param #number duration Duration in seconds the file lasts.
|
||||||
@@ -230,7 +248,7 @@ end
|
|||||||
-- @param #number interval Interval in seconds after the last transmission finished.
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
-- @param #string subtitle Subtitle of the transmission.
|
-- @param #string subtitle Subtitle of the transmission.
|
||||||
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
||||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
-- @return #RADIOQUEUE.Transmission Radio transmission table.
|
||||||
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval, subtitle, subduration)
|
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval, subtitle, subduration)
|
||||||
|
|
||||||
-- Sanity checks.
|
-- Sanity checks.
|
||||||
@@ -269,9 +287,36 @@ function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval,
|
|||||||
-- Add transmission to queue.
|
-- Add transmission to queue.
|
||||||
self:AddTransmission(transmission)
|
self:AddTransmission(transmission)
|
||||||
|
|
||||||
|
return transmission
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a SOUNDFILE to the radio queue.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param Sound.SoundOutput#SOUNDFILE soundfile Sound file object to be added.
|
||||||
|
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||||
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
|
-- @return #RADIOQUEUE self
|
||||||
|
function RADIOQUEUE:AddSoundFile(soundfile, tstart, interval)
|
||||||
|
--env.info(string.format("FF add soundfile: name=%s%s", soundfile:GetPath(), soundfile:GetFileName()))
|
||||||
|
local transmission=self:NewTransmission(soundfile:GetFileName(), soundfile.duration, soundfile:GetPath(), tstart, interval, soundfile.subtitle, soundfile.subduration)
|
||||||
|
transmission.soundfile=soundfile
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Add a SOUNDTEXT to the radio queue.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param Sound.SoundOutput#SOUNDTEXT soundtext Text-to-speech text.
|
||||||
|
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||||
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
|
-- @return #RADIOQUEUE self
|
||||||
|
function RADIOQUEUE:AddSoundText(soundtext, tstart, interval)
|
||||||
|
|
||||||
|
local transmission=self:NewTransmission("SoundText.ogg", soundtext.duration, nil, tstart, interval, soundtext.subtitle, soundtext.subduration)
|
||||||
|
transmission.soundtext=soundtext
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Convert a number (as string) into a radio transmission.
|
--- Convert a number (as string) into a radio transmission.
|
||||||
-- E.g. for board number or headings.
|
-- E.g. for board number or headings.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
@@ -281,18 +326,8 @@ end
|
|||||||
-- @return #number Duration of the call in seconds.
|
-- @return #number Duration of the call in seconds.
|
||||||
function RADIOQUEUE:Number2Transmission(number, delay, interval)
|
function RADIOQUEUE:Number2Transmission(number, delay, interval)
|
||||||
|
|
||||||
--- Split string into characters.
|
|
||||||
local function _split(str)
|
|
||||||
local chars={}
|
|
||||||
for i=1,#str do
|
|
||||||
local c=str:sub(i,i)
|
|
||||||
table.insert(chars, c)
|
|
||||||
end
|
|
||||||
return chars
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Split string into characters.
|
-- Split string into characters.
|
||||||
local numbers=_split(number)
|
local numbers=UTILS.GetCharacters(number)
|
||||||
|
|
||||||
local wait=0
|
local wait=0
|
||||||
for i=1,#numbers do
|
for i=1,#numbers do
|
||||||
@@ -325,6 +360,11 @@ end
|
|||||||
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||||
function RADIOQUEUE:Broadcast(transmission)
|
function RADIOQUEUE:Broadcast(transmission)
|
||||||
|
|
||||||
|
if ((transmission.soundfile and transmission.soundfile.useSRS) or transmission.soundtext) and self.msrs then
|
||||||
|
self:_BroadcastSRS(transmission)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Get unit sending the transmission.
|
-- Get unit sending the transmission.
|
||||||
local sender=self:_GetRadioSender()
|
local sender=self:_GetRadioSender()
|
||||||
|
|
||||||
@@ -416,6 +456,19 @@ function RADIOQUEUE:Broadcast(transmission)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Broadcast radio message.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||||
|
function RADIOQUEUE:_BroadcastSRS(transmission)
|
||||||
|
|
||||||
|
if transmission.soundfile and transmission.soundfile.useSRS then
|
||||||
|
self.msrs:PlaySoundFile(transmission.soundfile)
|
||||||
|
elseif transmission.soundtext then
|
||||||
|
self.msrs:PlaySoundText(transmission.soundtext)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Start checking the radio queue.
|
--- Start checking the radio queue.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #number delay Delay in seconds before checking.
|
-- @param #number delay Delay in seconds before checking.
|
||||||
@@ -547,7 +600,7 @@ function RADIOQUEUE:_GetRadioSender()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
--- Get unit from which we want to transmit a radio message. This has to be an aircraft or ground unit for subtitles to work.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @return DCS#Vec3 Vector 3D.
|
-- @return DCS#Vec3 Vector 3D.
|
||||||
function RADIOQUEUE:_GetRadioSenderCoord()
|
function RADIOQUEUE:_GetRadioSenderCoord()
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Authors: FlightControl
|
-- ### Authors: FlightControl
|
||||||
--
|
--
|
||||||
-- @module Core.RadioSpeech
|
-- @module Sound.RadioSpeech
|
||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
--- Makes the radio speak.
|
--- Makes the radio speak.
|
||||||
@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
|
|||||||
["8000"] = { "8000", 0.92 },
|
["8000"] = { "8000", 0.92 },
|
||||||
["9000"] = { "9000", 0.87 },
|
["9000"] = { "9000", 0.87 },
|
||||||
|
|
||||||
["степени"] = { "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 },
|
||||||
["возвращаясь на базу"] = { "returning_to_base", 1.40 },
|
|
||||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||||
["перехват самолетов"] = { "intercepting_bogeys", 1.22 },
|
["перехват боги"] = { "intercepting_bogeys", 1.22 },
|
||||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||||
["захватывающие самолеты"] = { "engaging_bogeys", 1.68 },
|
["привлечение болотных птиц"] = { "engaging_bogeys", 1.68 },
|
||||||
["колеса вверх"] = { "wheels_up", 0.92 },
|
["колёса вверх..."] = { "wheels_up", 0.92 },
|
||||||
["посадка на базу"] = { "landing at base", 1.04 },
|
["посадка на базу"] = { "landing at base", 1.04 },
|
||||||
["патрулирующий"] = { "patrolling", 0.96 },
|
["патрулирование"] = { "patrolling", 0.96 },
|
||||||
|
|
||||||
["за"] = { "for", 0.27 },
|
["для"] = { "for", 0.27 },
|
||||||
["и"] = { "and", 0.17 },
|
["и"] = { "and", 0.17 },
|
||||||
["в"] = { "at", 0.19 },
|
["на сайте"] = { "at", 0.19 },
|
||||||
["dot"] = { "dot", 0.51 },
|
["точка"] = { "dot", 0.51 },
|
||||||
["defender"] = { "defender", 0.45 },
|
["защитник"] = { "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.
|
||||||
692
Moose Development/Moose/Sound/SRS.lua
Normal file
692
Moose Development/Moose/Sound/SRS.lua
Normal file
@@ -0,0 +1,692 @@
|
|||||||
|
--- **Sound** - Simple Radio Standalone (SRS) Integration.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Play sound files via SRS
|
||||||
|
-- * Play text-to-speach via SRS
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Youtube Videos: None yet
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Missions: None yet
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Sound files: [MOOSE Sound Files](https://github.com/FlightControl-Master/MOOSE_SOUND/releases)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- The goal of the [SRS](https://github.com/ciribob/DCS-SimpleRadioStandalone) project is to bring VoIP communication into DCS and to make communication as frictionless as possible.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
-- @module Sound.MSRS
|
||||||
|
-- @image Sound_MSRS.png
|
||||||
|
|
||||||
|
--- MSRS class.
|
||||||
|
-- @type MSRS
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #table frequencies Frequencies used in the transmissions.
|
||||||
|
-- @field #table modulations Modulations used in the transmissions.
|
||||||
|
-- @field #number coalition Coalition of the transmission.
|
||||||
|
-- @field #number port Port. Default 5002.
|
||||||
|
-- @field #string name Name. Default "DCS-STTS".
|
||||||
|
-- @field #number volume Volume between 0 (min) and 1 (max). Default 1.
|
||||||
|
-- @field #string culture Culture. Default "en-GB".
|
||||||
|
-- @field #string gender Gender. Default "female".
|
||||||
|
-- @field #string voice Specifc voce.
|
||||||
|
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
|
||||||
|
-- @field #string path Path to the SRS exe. This includes the final slash "/".
|
||||||
|
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- 
|
||||||
|
--
|
||||||
|
-- # The MSRS Concept
|
||||||
|
--
|
||||||
|
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
|
||||||
|
--
|
||||||
|
-- ## Prerequisites
|
||||||
|
--
|
||||||
|
-- This script needs SRS version >= 1.9.6.
|
||||||
|
--
|
||||||
|
-- # Play Sound Files
|
||||||
|
--
|
||||||
|
-- local soundfile=SOUNDFILE:New("My Soundfile.ogg", "D:\\Sounds For DCS")
|
||||||
|
-- local msrs=MSRS:New("C:\\Path To SRS", 251, radio.modulation.AM)
|
||||||
|
-- msrs:PlaySoundFile(soundfile)
|
||||||
|
--
|
||||||
|
-- # Play Text-To-Speech
|
||||||
|
--
|
||||||
|
-- Basic example:
|
||||||
|
--
|
||||||
|
-- -- Create a SOUNDTEXT object.
|
||||||
|
-- local text=SOUNDTEXT:New("All Enemies destroyed")
|
||||||
|
--
|
||||||
|
-- -- MOOSE SRS
|
||||||
|
-- local msrs=MSRS:New("D:\\DCS\\_SRS\\", 305, radio.modulation.AM)
|
||||||
|
--
|
||||||
|
-- -- Text-to speech with default voice after 2 seconds.
|
||||||
|
-- msrs:PlaySoundText(text, 2)
|
||||||
|
--
|
||||||
|
-- ## Set Gender
|
||||||
|
--
|
||||||
|
-- Use a specific gender with the @{#MSRS.SetGender} function, e.g. `SetGender("male")` or `:SetGender("female")`.
|
||||||
|
--
|
||||||
|
-- ## Set Culture
|
||||||
|
--
|
||||||
|
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
|
||||||
|
--
|
||||||
|
-- ## Set Voice
|
||||||
|
--
|
||||||
|
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||||
|
-- Note that this must be installed on your windows system.
|
||||||
|
--
|
||||||
|
-- ## Set Coordinate
|
||||||
|
--
|
||||||
|
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
|
||||||
|
--
|
||||||
|
-- @field #MSRS
|
||||||
|
MSRS = {
|
||||||
|
ClassName = "MSRS",
|
||||||
|
lid = nil,
|
||||||
|
port = 5002,
|
||||||
|
name = "MSRS",
|
||||||
|
frequencies = {},
|
||||||
|
modulations = {},
|
||||||
|
coalition = 0,
|
||||||
|
gender = "female",
|
||||||
|
culture = nil,
|
||||||
|
voice = nil,
|
||||||
|
volume = 1,
|
||||||
|
speed = 1,
|
||||||
|
coordinate = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- MSRS class version.
|
||||||
|
-- @field #string version
|
||||||
|
MSRS.version="0.0.3"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: Add functions to add/remove freqs and modulations.
|
||||||
|
-- DONE: Add coordinate.
|
||||||
|
-- DONE: Add google.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new MSRS object.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string PathToSRS Path to the directory, where SRS is located.
|
||||||
|
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
|
||||||
|
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:New(PathToSRS, Frequency, Modulation)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
Frequency =Frequency or 143
|
||||||
|
Modulation= Modulation or radio.modulation.AM
|
||||||
|
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #MSRS
|
||||||
|
|
||||||
|
self:SetPath(PathToSRS)
|
||||||
|
self:SetPort()
|
||||||
|
self:SetFrequencies(Frequency)
|
||||||
|
self:SetModulations(Modulation)
|
||||||
|
self:SetGender()
|
||||||
|
self:SetCoalition()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set path to SRS install directory. More precisely, path to where the DCS-
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Path Path to the directory, where the sound file is located. This does **not** contain a final backslash or slash.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetPath(Path)
|
||||||
|
|
||||||
|
if Path==nil then
|
||||||
|
self:E("ERROR: No path to SRS directory specified!")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set path.
|
||||||
|
self.path=Path
|
||||||
|
|
||||||
|
-- Remove (back)slashes.
|
||||||
|
local n=1 ; local nmax=1000
|
||||||
|
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
||||||
|
self.path=self.path:sub(1,#self.path-1)
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T(string.format("SRS path=%s", self:GetPath()))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get path to SRS directory.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #string Path to the directory. This includes the final slash "/".
|
||||||
|
function MSRS:GetPath()
|
||||||
|
return self.path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set port.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number Port Port. Default 5002.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetPort(Port)
|
||||||
|
self.port=Port or 5002
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get port.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #number Port.
|
||||||
|
function MSRS:GetPort()
|
||||||
|
return self.port
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set coalition.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number Coalition Coalition. Default 0.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetCoalition(Coalition)
|
||||||
|
self.coalition=Coalition or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get coalition.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #number Coalition.
|
||||||
|
function MSRS:GetCoalition()
|
||||||
|
return self.coalition
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set frequencies.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetFrequencies(Frequencies)
|
||||||
|
|
||||||
|
-- Ensure table.
|
||||||
|
if type(Frequencies)~="table" then
|
||||||
|
Frequencies={Frequencies}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.frequencies=Frequencies
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get frequencies.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Frequencies in MHz.
|
||||||
|
function MSRS:GetFrequencies()
|
||||||
|
return self.frequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set modulations.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetModulations(Modulations)
|
||||||
|
|
||||||
|
-- Ensure table.
|
||||||
|
if type(Modulations)~="table" then
|
||||||
|
Modulations={Modulations}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.modulations=Modulations
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get modulations.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Modulations.
|
||||||
|
function MSRS:GetModulations()
|
||||||
|
return self.modulations
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set gender.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Gender Gender: "male" or "female" (default).
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetGender(Gender)
|
||||||
|
|
||||||
|
Gender=Gender or "female"
|
||||||
|
|
||||||
|
self.gender=Gender:lower()
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("Setting gender to "..tostring(self.gender))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set culture.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Culture Culture, e.g. "en-GB" (default).
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetCulture(Culture)
|
||||||
|
|
||||||
|
self.culture=Culture
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set to use a specific voice. Will override gender and culture settings.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Voice Voice.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetVoice(Voice)
|
||||||
|
|
||||||
|
self.voice=Voice
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the coordinate from which the transmissions will be broadcasted.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Origin of the transmission.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetCoordinate(Coordinate)
|
||||||
|
|
||||||
|
self.coordinate=Coordinate
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Use google text-to-speech.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetGoogle(PathToCredentials)
|
||||||
|
|
||||||
|
self.google=PathToCredentials
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Print SRS STTS help to DCS log file.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:Help()
|
||||||
|
|
||||||
|
-- Path and exe.
|
||||||
|
local path=self:GetPath() or STTS.DIRECTORY
|
||||||
|
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
||||||
|
|
||||||
|
-- Text file for output.
|
||||||
|
local filename = os.getenv('TMP') .. "\\MSRS-help-"..STTS.uuid()..".txt"
|
||||||
|
|
||||||
|
-- Print help.
|
||||||
|
local command=string.format("%s/%s --help > %s", path, exe, filename)
|
||||||
|
os.execute(command)
|
||||||
|
|
||||||
|
local f=assert(io.open(filename, "rb"))
|
||||||
|
local data=f:read("*all")
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
-- Print to log file.
|
||||||
|
env.info("SRS STTS help output:")
|
||||||
|
env.info("======================================================================")
|
||||||
|
env.info(data)
|
||||||
|
env.info("======================================================================")
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Transmission Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Play sound file (ogg or mp3) via SRS.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Sound.SoundFile#SOUNDFILE Soundfile Sound file to play.
|
||||||
|
-- @param #number Delay Delay in seconds, before the sound file is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlaySoundFile(Soundfile, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlaySoundFile, self, Soundfile, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Sound file name.
|
||||||
|
local soundfile=Soundfile:GetName()
|
||||||
|
|
||||||
|
-- Get command.
|
||||||
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
|
-- Append file.
|
||||||
|
command=command.." --file="..tostring(soundfile)
|
||||||
|
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
command=command.." > bla.txt"
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
||||||
|
|
||||||
|
-- Execute SRS command.
|
||||||
|
local x=os.execute(command)
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Play a SOUNDTEXT text-to-speech object.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Sound.SoundFile#SOUNDTEXT SoundText Sound text.
|
||||||
|
-- @param #number Delay Delay in seconds, before the sound file is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlaySoundText(SoundText, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Get command.
|
||||||
|
local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed)
|
||||||
|
|
||||||
|
-- Append text.
|
||||||
|
command=command..string.format(" --text=\"%s\"", tostring(SoundText.text))
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
command=command.." > bla.txt"
|
||||||
|
|
||||||
|
-- Debug putput.
|
||||||
|
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
||||||
|
|
||||||
|
-- Execute SRS command.
|
||||||
|
local x=os.execute(command)
|
||||||
|
]]
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Play text message via STTS.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Text Text message.
|
||||||
|
-- @param #number Delay Delay in seconds, before the message is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlayText(Text, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Get command line.
|
||||||
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
|
-- Append text.
|
||||||
|
command=command..string.format(" --text=\"%s\"", tostring(Text))
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
-- Check that length of command is max 255 chars or os.execute() will not work!
|
||||||
|
if string.len(command)>255 then
|
||||||
|
|
||||||
|
-- Create a tmp file.
|
||||||
|
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat"
|
||||||
|
|
||||||
|
local script = io.open(filename, "w+")
|
||||||
|
script:write(command.." && exit")
|
||||||
|
script:close()
|
||||||
|
|
||||||
|
-- Play command.
|
||||||
|
command=string.format("\"%s\"", filename)
|
||||||
|
|
||||||
|
-- Play file in 0.05 seconds
|
||||||
|
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
|
||||||
|
|
||||||
|
-- Remove file in 1 second.
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:I(string.format("MSRS Text command=%s", command))
|
||||||
|
|
||||||
|
-- Execute SRS command.
|
||||||
|
local x=os.execute(command)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
]]
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Play text file via STTS.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string TextFile Full path to the file.
|
||||||
|
-- @param #number Delay Delay in seconds, before the message is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlayTextFile(TextFile, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlayTextFile, self, TextFile, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- First check if text file exists!
|
||||||
|
local exists=UTILS.FileExists(TextFile)
|
||||||
|
if not exists then
|
||||||
|
self:E("ERROR: MSRS Text file does not exist! File="..tostring(TextFile))
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get command line.
|
||||||
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
|
-- Append text file.
|
||||||
|
command=command..string.format(" --textFile=\"%s\"", tostring(TextFile))
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T(string.format("MSRS TextFile command=%s", command))
|
||||||
|
|
||||||
|
-- Count length of command.
|
||||||
|
local l=string.len(command)
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Misc Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Execute SRS command to play sound using the `DCS-SR-ExternalAudio.exe`.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string command Command to executer
|
||||||
|
-- @return #number Return value of os.execute() command.
|
||||||
|
function MSRS:_ExecCommand(command)
|
||||||
|
|
||||||
|
-- Create a tmp file.
|
||||||
|
local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat"
|
||||||
|
|
||||||
|
local script=io.open(filename, "w+")
|
||||||
|
script:write(command.." && exit")
|
||||||
|
script:close()
|
||||||
|
|
||||||
|
-- Play command.
|
||||||
|
command=string.format('start /b "" "%s"', filename)
|
||||||
|
|
||||||
|
local res=nil
|
||||||
|
if true then
|
||||||
|
|
||||||
|
-- Create a tmp file.
|
||||||
|
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".vbs"
|
||||||
|
|
||||||
|
-- VBS script
|
||||||
|
local script = io.open(filenvbs, "w+")
|
||||||
|
script:write(string.format('Dim WinScriptHost\n'))
|
||||||
|
script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n'))
|
||||||
|
script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename))
|
||||||
|
script:write(string.format('Set WinScriptHost = Nothing'))
|
||||||
|
script:close()
|
||||||
|
|
||||||
|
-- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus.
|
||||||
|
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("MSRS execute command="..command)
|
||||||
|
self:T("MSRS execute VBS command="..runvbs)
|
||||||
|
|
||||||
|
-- Play file in 0.01 seconds
|
||||||
|
res=os.execute(runvbs)
|
||||||
|
|
||||||
|
-- Remove file in 1 second.
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||||
|
timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1)
|
||||||
|
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("MSRS execute command="..command)
|
||||||
|
|
||||||
|
-- Execute command
|
||||||
|
res=os.execute(command)
|
||||||
|
|
||||||
|
-- Remove file in 1 second.
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get lat, long and alt from coordinate.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3.
|
||||||
|
-- @return #number Latitude.
|
||||||
|
-- @return #number Longitude.
|
||||||
|
-- @return #number Altitude.
|
||||||
|
function MSRS:_GetLatLongAlt(Coordinate)
|
||||||
|
|
||||||
|
local lat, lon, alt=coord.LOtoLL(Coordinate)
|
||||||
|
|
||||||
|
return lat, lon, math.floor(alt)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table freqs Frequencies in MHz.
|
||||||
|
-- @param #table modus Modulations.
|
||||||
|
-- @param #number coal Coalition.
|
||||||
|
-- @param #string gender Gender.
|
||||||
|
-- @param #string voice Voice.
|
||||||
|
-- @param #string culture Culture.
|
||||||
|
-- @param #number volume Volume.
|
||||||
|
-- @param #number speed Speed.
|
||||||
|
-- @param #number port Port.
|
||||||
|
-- @return #string Command.
|
||||||
|
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port)
|
||||||
|
|
||||||
|
local path=self:GetPath() or STTS.DIRECTORY
|
||||||
|
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
||||||
|
freqs=table.concat(freqs or self.frequencies, ",")
|
||||||
|
modus=table.concat(modus or self.modulations, ",")
|
||||||
|
coal=coal or self.coalition
|
||||||
|
gender=gender or self.gender
|
||||||
|
voice=voice or self.voice
|
||||||
|
culture=culture or self.culture
|
||||||
|
volume=volume or self.volume
|
||||||
|
speed=speed or self.speed
|
||||||
|
port=port or self.port
|
||||||
|
|
||||||
|
-- Replace modulation
|
||||||
|
modus=modus:gsub("0", "AM")
|
||||||
|
modus=modus:gsub("1", "FM")
|
||||||
|
|
||||||
|
-- This did not work well. Stopped if the transmission was a bit longer with no apparent error.
|
||||||
|
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
|
||||||
|
|
||||||
|
-- Command from orig STTS script. Works better for some unknown reason!
|
||||||
|
local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
|
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
|
-- Command.
|
||||||
|
local command=string.format('%s/%s -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
|
-- Set voice or gender/culture.
|
||||||
|
if voice then
|
||||||
|
-- Use a specific voice (no need for gender and/or culture.
|
||||||
|
command=command..string.format(" --voice=\"%s\"", tostring(voice))
|
||||||
|
else
|
||||||
|
-- Add gender.
|
||||||
|
if gender and gender~="female" then
|
||||||
|
command=command..string.format(" --gender=%s", tostring(gender))
|
||||||
|
end
|
||||||
|
-- Add culture.
|
||||||
|
if culture and culture~="en-GB" then
|
||||||
|
command=command..string.format(" -l %s", tostring(culture))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set coordinate.
|
||||||
|
if self.coordinate then
|
||||||
|
local lat,lon,alt=self:_GetLatLongAlt(self.coordinate)
|
||||||
|
command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set google.
|
||||||
|
if self.google then
|
||||||
|
command=command..string.format(' -G "%s"', self.google)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("MSRS command="..command)
|
||||||
|
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
408
Moose Development/Moose/Sound/SoundOutput.lua
Normal file
408
Moose Development/Moose/Sound/SoundOutput.lua
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
--- **Sound** - Sound output classes.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Features:
|
||||||
|
--
|
||||||
|
-- * Create a SOUNDFILE object (mp3 or ogg) to be played via DCS or SRS transmissions
|
||||||
|
-- * Create a SOUNDTEXT object for text-to-speech output vis SRS Simple-Text-To-Speech (STTS)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- There are two classes, SOUNDFILE and SOUNDTEXT, defined in this section that deal with playing
|
||||||
|
-- sound files or arbitrary text (via SRS Simple-Text-To-Speech), respectively.
|
||||||
|
--
|
||||||
|
-- The SOUNDFILE and SOUNDTEXT objects can be defined and used in other MOOSE classes.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @module Sound.SoundOutput
|
||||||
|
-- @image Sound_SoundOutput.png
|
||||||
|
|
||||||
|
do -- Sound Base
|
||||||
|
|
||||||
|
--- @type SOUNDBASE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
|
--- Basic sound output inherited by other classes suche as SOUNDFILE and SOUNDTEXT.
|
||||||
|
--
|
||||||
|
-- This class is **not** meant to be used by "ordinary" users.
|
||||||
|
--
|
||||||
|
-- @field #SOUNDBASE
|
||||||
|
SOUNDBASE={
|
||||||
|
ClassName = "SOUNDBASE",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a new SOUNDBASE object.
|
||||||
|
-- @param #SOUNDBASE self
|
||||||
|
-- @return #SOUNDBASE self
|
||||||
|
function SOUNDBASE:New()
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDBASE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function returns estimated speech time in seconds.
|
||||||
|
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word so
|
||||||
|
--
|
||||||
|
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
||||||
|
--
|
||||||
|
-- So lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
||||||
|
--
|
||||||
|
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||||
|
--
|
||||||
|
-- @param #string Text The text string to analyze.
|
||||||
|
-- @param #number Speed Speed factor. Default 1.
|
||||||
|
-- @param #boolean isGoogle If true, google text-to-speech is used.
|
||||||
|
function SOUNDBASE:GetSpeechTime(length,speed,isGoogle)
|
||||||
|
|
||||||
|
local maxRateRatio = 3
|
||||||
|
|
||||||
|
speed = speed or 1.0
|
||||||
|
isGoogle = isGoogle or false
|
||||||
|
|
||||||
|
local speedFactor = 1.0
|
||||||
|
if isGoogle then
|
||||||
|
speedFactor = speed
|
||||||
|
else
|
||||||
|
if speed ~= 0 then
|
||||||
|
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1
|
||||||
|
end
|
||||||
|
if speed < 0 then
|
||||||
|
speedFactor = 1/speedFactor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Words per minute.
|
||||||
|
local wpm = math.ceil(100 * speedFactor)
|
||||||
|
|
||||||
|
-- Characters per second.
|
||||||
|
local cps = math.floor((wpm * 5)/60)
|
||||||
|
|
||||||
|
if type(length) == "string" then
|
||||||
|
length = string.len(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
return math.ceil(length/cps)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do -- Sound File
|
||||||
|
|
||||||
|
--- @type SOUNDFILE
|
||||||
|
-- @field #string ClassName Name of the class
|
||||||
|
-- @field #string filename Name of the flag.
|
||||||
|
-- @field #string path Directory path, where the sound file is located. This includes the final slash "/".
|
||||||
|
-- @field #string duration Duration of the sound file in seconds.
|
||||||
|
-- @field #string subtitle Subtitle of the transmission.
|
||||||
|
-- @field #number subduration Duration in seconds how long the subtitle is displayed.
|
||||||
|
-- @field #boolean useSRS If true, sound file is played via SRS. Sound file needs to be on local disk not inside the miz file!
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
|
--- Sound files used by other classes.
|
||||||
|
--
|
||||||
|
-- # The SOUNDFILE Concept
|
||||||
|
--
|
||||||
|
-- A SOUNDFILE object hold the important properties that are necessary to play the sound file, e.g. its file name, path, duration.
|
||||||
|
--
|
||||||
|
-- It can be created with the @{#SOUNDFILE.New}(*FileName*, *Path*, *Duration*) function:
|
||||||
|
--
|
||||||
|
-- local soundfile=SOUNDFILE:New("My Soundfile.ogg", "Sound File/", 3.5)
|
||||||
|
--
|
||||||
|
-- ## SRS
|
||||||
|
--
|
||||||
|
-- If sound files are supposed to be played via SRS, you need to use the @{#SOUNDFILE.SetPlayWithSRS}() function.
|
||||||
|
--
|
||||||
|
-- # Location/Path
|
||||||
|
--
|
||||||
|
-- ## DCS
|
||||||
|
--
|
||||||
|
-- DCS can only play sound files that are located inside the mission (.miz) file. In particular, DCS cannot make use of files that are stored on
|
||||||
|
-- your hard drive.
|
||||||
|
--
|
||||||
|
-- The default location where sound files are stored in DCS is the directory "l10n/DEFAULT/". This is where sound files are placed, if they are
|
||||||
|
-- added via the mission editor (TRIGGERS-->ACTIONS-->SOUND TO ALL). Note however, that sound files which are not added with a trigger command,
|
||||||
|
-- will be deleted each time the mission is saved! Therefore, this directory is not ideal to be used especially if many sound files are to
|
||||||
|
-- be included since for each file a trigger action needs to be created. Which is cumbersome, to say the least.
|
||||||
|
--
|
||||||
|
-- The recommended way is to create a new folder inside the mission (.miz) file (a miz file is essentially zip file and can be opened, e.g., with 7-Zip)
|
||||||
|
-- and to place the sound files in there. Sound files in these folders are not wiped out by DCS on the next save.
|
||||||
|
--
|
||||||
|
-- ## SRS
|
||||||
|
--
|
||||||
|
-- SRS sound files need to be located on your local drive (not inside the miz). Therefore, you need to specify the full path.
|
||||||
|
--
|
||||||
|
-- @field #SOUNDFILE
|
||||||
|
SOUNDFILE={
|
||||||
|
ClassName = "SOUNDFILE",
|
||||||
|
filename = nil,
|
||||||
|
path = "l10n/DEFAULT/",
|
||||||
|
duration = 3,
|
||||||
|
subtitle = nil,
|
||||||
|
subduration = 0,
|
||||||
|
useSRS = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a new SOUNDFILE object.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string FileName The name of the sound file, e.g. "Hello World.ogg".
|
||||||
|
-- @param #string Path The path of the directory, where the sound file is located. Default is "l10n/DEFAULT/" within the miz file.
|
||||||
|
-- @param #number Duration Duration in seconds, how long it takes to play the sound file. Default is 3 seconds.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:New(FileName, Path, Duration)
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDFILE
|
||||||
|
|
||||||
|
-- Set file name.
|
||||||
|
self:SetFileName(FileName)
|
||||||
|
|
||||||
|
-- Set path.
|
||||||
|
self:SetPath(Path)
|
||||||
|
|
||||||
|
-- Set duration.
|
||||||
|
self:SetDuration(Duration)
|
||||||
|
|
||||||
|
-- Debug info:
|
||||||
|
self:T(string.format("New SOUNDFILE: file name=%s, path=%s", self.filename, self.path))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set path, where the sound file is located.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string Path Path to the directory, where the sound file is located.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetPath(Path)
|
||||||
|
|
||||||
|
-- Init path.
|
||||||
|
self.path=Path or "l10n/DEFAULT/"
|
||||||
|
|
||||||
|
-- Remove (back)slashes.
|
||||||
|
local nmax=1000 ; local n=1
|
||||||
|
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
||||||
|
self.path=self.path:sub(1,#self.path-1)
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Append slash.
|
||||||
|
self.path=self.path.."/"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get path of the directory, where the sound file is located.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #string Path.
|
||||||
|
function SOUNDFILE:GetPath()
|
||||||
|
local path=self.path or "l10n/DEFAULT/"
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set sound file name. This must be a .ogg or .mp3 file!
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string FileName Name of the file. Default is "Hello World.mp3".
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetFileName(FileName)
|
||||||
|
--TODO: check that sound file is really .ogg or .mp3
|
||||||
|
self.filename=FileName or "Hello World.mp3"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the sound file name.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #string Name of the soud file. This does *not* include its path.
|
||||||
|
function SOUNDFILE:GetFileName()
|
||||||
|
return self.filename
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set duration how long it takes to play the sound file.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string Duration Duration in seconds. Default 3 seconds.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetDuration(Duration)
|
||||||
|
self.duration=Duration or 3
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get duration how long the sound file takes to play.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #number Duration in seconds.
|
||||||
|
function SOUNDFILE:GetDuration()
|
||||||
|
return self.duration or 3
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the complete sound file name inlcuding its path.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #string Name of the sound file.
|
||||||
|
function SOUNDFILE:GetName()
|
||||||
|
local path=self:GetPath()
|
||||||
|
local filename=self:GetFileName()
|
||||||
|
local name=string.format("%s%s", path, filename)
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set whether sound files should be played via SRS.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #boolean Switch If true or nil, use SRS. If false, use DCS transmission.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetPlayWithSRS(Switch)
|
||||||
|
if Switch==true or Switch==nil then
|
||||||
|
self.useSRS=true
|
||||||
|
else
|
||||||
|
self.useSRS=false
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- Text-To-Speech
|
||||||
|
|
||||||
|
--- @type SOUNDTEXT
|
||||||
|
-- @field #string ClassName Name of the class
|
||||||
|
-- @field #string text Text to speak.
|
||||||
|
-- @field #number duration Duration in seconds.
|
||||||
|
-- @field #string gender Gender: "male", "female".
|
||||||
|
-- @field #string culture Culture, e.g. "en-GB".
|
||||||
|
-- @field #string voice Specific voice to use. Overrules `gender` and `culture` settings.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
|
--- Text-to-speech objects for other classes.
|
||||||
|
--
|
||||||
|
-- # The SOUNDTEXT Concept
|
||||||
|
--
|
||||||
|
-- A SOUNDTEXT object holds all necessary information to play a general text via SRS Simple-Text-To-Speech.
|
||||||
|
--
|
||||||
|
-- It can be created with the @{#SOUNDTEXT.New}(*Text*, *Duration*) function.
|
||||||
|
--
|
||||||
|
-- * @{#SOUNDTEXT.New}(*Text, Duration*): Creates a new SOUNDTEXT object.
|
||||||
|
--
|
||||||
|
-- # Options
|
||||||
|
--
|
||||||
|
-- ## Gender
|
||||||
|
--
|
||||||
|
-- You can choose a gender ("male" or "femal") with the @{#SOUNDTEXT.SetGender}(*Gender*) function.
|
||||||
|
-- Note that the gender voice needs to be installed on your windows machine for the used culture (see below).
|
||||||
|
--
|
||||||
|
-- ## Culture
|
||||||
|
--
|
||||||
|
-- You can choose a "culture" (accent) with the @{#SOUNDTEXT.SetCulture}(*Culture*) function, where the default (SRS) culture is "en-GB".
|
||||||
|
--
|
||||||
|
-- Other examples for culture are: "en-US" (US accent), "de-DE" (German), "it-IT" (Italian), "ru-RU" (Russian), "zh-CN" (Chinese).
|
||||||
|
--
|
||||||
|
-- Note that the chosen culture needs to be installed on your windows machine.
|
||||||
|
--
|
||||||
|
-- ## Specific Voice
|
||||||
|
--
|
||||||
|
-- You can use a specific voice for the transmission with the @{SOUNDTEXT.SetVoice}(*VoiceName*) function. Here are some examples
|
||||||
|
--
|
||||||
|
-- * Name: Microsoft Hazel Desktop, Culture: en-GB, Gender: Female, Age: Adult, Desc: Microsoft Hazel Desktop - English (Great Britain)
|
||||||
|
-- * Name: Microsoft David Desktop, Culture: en-US, Gender: Male, Age: Adult, Desc: Microsoft David Desktop - English (United States)
|
||||||
|
-- * Name: Microsoft Zira Desktop, Culture: en-US, Gender: Female, Age: Adult, Desc: Microsoft Zira Desktop - English (United States)
|
||||||
|
-- * Name: Microsoft Hedda Desktop, Culture: de-DE, Gender: Female, Age: Adult, Desc: Microsoft Hedda Desktop - German
|
||||||
|
-- * Name: Microsoft Helena Desktop, Culture: es-ES, Gender: Female, Age: Adult, Desc: Microsoft Helena Desktop - Spanish (Spain)
|
||||||
|
-- * Name: Microsoft Hortense Desktop, Culture: fr-FR, Gender: Female, Age: Adult, Desc: Microsoft Hortense Desktop - French
|
||||||
|
-- * Name: Microsoft Elsa Desktop, Culture: it-IT, Gender: Female, Age: Adult, Desc: Microsoft Elsa Desktop - Italian (Italy)
|
||||||
|
-- * Name: Microsoft Irina Desktop, Culture: ru-RU, Gender: Female, Age: Adult, Desc: Microsoft Irina Desktop - Russian
|
||||||
|
-- * Name: Microsoft Huihui Desktop, Culture: zh-CN, Gender: Female, Age: Adult, Desc: Microsoft Huihui Desktop - Chinese (Simplified)
|
||||||
|
--
|
||||||
|
-- Note that this must be installed on your windos machine. Also note that this overrides any culture and gender settings.
|
||||||
|
--
|
||||||
|
-- @field #SOUNDTEXT
|
||||||
|
SOUNDTEXT={
|
||||||
|
ClassName = "SOUNDTEXT",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a new SOUNDTEXT object.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Text The text to speak.
|
||||||
|
-- @param #number Duration Duration in seconds, how long it takes to play the text. Default is 3 seconds.
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:New(Text, Duration)
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDTEXT
|
||||||
|
|
||||||
|
self:SetText(Text)
|
||||||
|
self:SetDuration(Duration or STTS.getSpeechTime(Text))
|
||||||
|
--self:SetGender()
|
||||||
|
--self:SetCulture()
|
||||||
|
|
||||||
|
-- Debug info:
|
||||||
|
self:T(string.format("New SOUNDTEXT: text=%s, duration=%.1f sec", self.text, self.duration))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set text.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Text Text to speak. Default "Hello World!".
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetText(Text)
|
||||||
|
|
||||||
|
self.text=Text or "Hello World!"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set duration, how long it takes to speak the text.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #number Duration Duration in seconds. Default 3 seconds.
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetDuration(Duration)
|
||||||
|
|
||||||
|
self.duration=Duration or 3
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set gender.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Gender Gender: "male" or "female" (default).
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetGender(Gender)
|
||||||
|
|
||||||
|
self.gender=Gender or "female"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set TTS culture - local for the voice.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Culture TTS culture. Default "en-GB".
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetCulture(Culture)
|
||||||
|
|
||||||
|
self.culture=Culture or "en-GB"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set to use a specific voice name.
|
||||||
|
-- See the list from `DCS-SR-ExternalAudio.exe --help` or if using google see [google voices](https://cloud.google.com/text-to-speech/docs/voices).
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string VoiceName Voice name. Note that this will overrule `Gender` and `Culture`.
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetVoice(VoiceName)
|
||||||
|
|
||||||
|
self.voice=VoiceName
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **Core** - Manage user sound.
|
--- **Sound** - Manage user sound.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Core.UserSound
|
-- @module Sound.UserSound
|
||||||
-- @image Core_Usersound.JPG
|
-- @image Core_Usersound.JPG
|
||||||
|
|
||||||
do -- UserSound
|
do -- UserSound
|
||||||
@@ -202,6 +202,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
|||||||
self:SetAutoAcceptTasks( true )
|
self:SetAutoAcceptTasks( true )
|
||||||
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance )
|
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance )
|
||||||
self:SetFlashStatus( false )
|
self:SetFlashStatus( false )
|
||||||
|
self:SetMessageDuration(10)
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Birth,
|
self:HandleEvent( EVENTS.Birth,
|
||||||
--- @param #COMMANDCENTER self
|
--- @param #COMMANDCENTER self
|
||||||
@@ -682,7 +683,7 @@ end
|
|||||||
-- @param #string Message The message text.
|
-- @param #string Message The message text.
|
||||||
function COMMANDCENTER:MessageToAll( Message )
|
function COMMANDCENTER:MessageToAll( Message )
|
||||||
|
|
||||||
self:GetPositionable():MessageToAll( Message, 20, self:GetName() )
|
self:GetPositionable():MessageToAll( Message, self.MessageDuration, self:GetName() )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -692,7 +693,7 @@ end
|
|||||||
-- @param Wrapper.Group#GROUP MessageGroup The group to receive the message.
|
-- @param Wrapper.Group#GROUP MessageGroup The group to receive the message.
|
||||||
function COMMANDCENTER:MessageToGroup( Message, MessageGroup )
|
function COMMANDCENTER:MessageToGroup( Message, MessageGroup )
|
||||||
|
|
||||||
self:GetPositionable():MessageToGroup( Message, 15, MessageGroup, self:GetShortText() )
|
self:GetPositionable():MessageToGroup( Message, self.MessageDuration, MessageGroup, self:GetShortText() )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -715,7 +716,7 @@ function COMMANDCENTER:MessageToCoalition( Message )
|
|||||||
local CCCoalition = self:GetPositionable():GetCoalition()
|
local CCCoalition = self:GetPositionable():GetCoalition()
|
||||||
--TODO: Fix coalition bug!
|
--TODO: Fix coalition bug!
|
||||||
|
|
||||||
self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition, self:GetShortText() )
|
self:GetPositionable():MessageToCoalition( Message, self.MessageDuration, CCCoalition, self:GetShortText() )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -795,9 +796,18 @@ end
|
|||||||
|
|
||||||
--- Let the command center flash a report of the status of the subscribed task to a group.
|
--- Let the command center flash a report of the status of the subscribed task to a group.
|
||||||
-- @param #COMMANDCENTER self
|
-- @param #COMMANDCENTER self
|
||||||
|
-- @param Flash #boolean
|
||||||
function COMMANDCENTER:SetFlashStatus( Flash )
|
function COMMANDCENTER:SetFlashStatus( Flash )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
self.FlashStatus = Flash or true
|
self.FlashStatus = Flash and true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Duration a command center message is shown.
|
||||||
|
-- @param #COMMANDCENTER self
|
||||||
|
-- @param seconds #number
|
||||||
|
function COMMANDCENTER:SetMessageDuration(seconds)
|
||||||
|
self:F()
|
||||||
|
|
||||||
|
self.MessageDuration = 10 or seconds
|
||||||
end
|
end
|
||||||
|
|||||||
256
Moose Development/Moose/Utilities/STTS.lua
Normal file
256
Moose Development/Moose/Utilities/STTS.lua
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
--- **Utilities** DCS Simple Text-To-Speech (STTS).
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @module Utils.STTS
|
||||||
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
--- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||||
|
-- @type STTS
|
||||||
|
-- @field #string DIRECTORY Path of the SRS directory.
|
||||||
|
|
||||||
|
--- Simple Text-To-Speech
|
||||||
|
--
|
||||||
|
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
|
||||||
|
--
|
||||||
|
-- # DCS Modification Required
|
||||||
|
--
|
||||||
|
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitisation.
|
||||||
|
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
|
||||||
|
-- Do this without DCS running to allow mission scripts to use os functions.
|
||||||
|
--
|
||||||
|
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
|
||||||
|
--
|
||||||
|
-- # USAGE:
|
||||||
|
--
|
||||||
|
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialise it
|
||||||
|
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
|
||||||
|
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
|
||||||
|
--
|
||||||
|
-- Example calls:
|
||||||
|
--
|
||||||
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
|
||||||
|
--
|
||||||
|
-- Arguments in order are:
|
||||||
|
--
|
||||||
|
-- * Message to say, make sure not to use a newline (\n) !
|
||||||
|
-- * Frequency in MHz
|
||||||
|
-- * Modulation - AM/FM
|
||||||
|
-- * Volume - 1.0 max, 0.5 half
|
||||||
|
-- * Name of the transmitter - ATC, RockFM etc
|
||||||
|
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||||
|
-- * OPTIONAL - Vec3 Point i.e Unit.getByName("A UNIT"):getPoint() - needs Vec3 for Height! OR null if not needed
|
||||||
|
-- * OPTIONAL - Speed -10 to +10
|
||||||
|
-- * OPTIONAL - Gender male, female or neuter
|
||||||
|
-- * OPTIONAL - Culture - en-US, en-GB etc
|
||||||
|
-- * OPTIONAL - Voice - a specfic voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
|
||||||
|
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- ## Example
|
||||||
|
--
|
||||||
|
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
|
||||||
|
--
|
||||||
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
|
||||||
|
--
|
||||||
|
-- ## Example
|
||||||
|
--
|
||||||
|
--This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
||||||
|
--
|
||||||
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
|
||||||
|
--
|
||||||
|
-- Arguments in order are:
|
||||||
|
--
|
||||||
|
-- * FULL path to the MP3 OR OGG to play
|
||||||
|
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
|
||||||
|
-- * Modulation - AM/FM - to use multiple
|
||||||
|
-- * Volume - 1.0 max, 0.5 half
|
||||||
|
-- * Name of the transmitter - ATC, RockFM etc
|
||||||
|
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||||
|
--
|
||||||
|
-- ## Example
|
||||||
|
--
|
||||||
|
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
|
||||||
|
--
|
||||||
|
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
|
||||||
|
--
|
||||||
|
-- @field #STTS
|
||||||
|
STTS={
|
||||||
|
ClassName="STTS",
|
||||||
|
DIRECTORY="",
|
||||||
|
SRS_PORT=5002,
|
||||||
|
GOOGLE_CREDENTIALS="C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
||||||
|
EXECUTABLE="DCS-SR-ExternalAudio.exe",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
|
||||||
|
STTS.DIRECTORY = "D:/DCS/_SRS"
|
||||||
|
|
||||||
|
--- LOCAL SRS PORT - DEFAULT IS 5002
|
||||||
|
STTS.SRS_PORT = 5002
|
||||||
|
|
||||||
|
--- Google credentials file
|
||||||
|
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
|
||||||
|
|
||||||
|
--- DONT CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
|
||||||
|
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||||
|
|
||||||
|
|
||||||
|
--- Function for UUID.
|
||||||
|
function STTS.uuid()
|
||||||
|
local random = math.random
|
||||||
|
local template ='yxxx-xxxxxxxxxxxx'
|
||||||
|
return string.gsub(template, '[xy]', function (c)
|
||||||
|
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
|
||||||
|
return string.format('%x', v)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Round a number.
|
||||||
|
-- @param #number x Number.
|
||||||
|
-- @param #number n Precision.
|
||||||
|
function STTS.round(x, n)
|
||||||
|
n = math.pow(10, n or 0)
|
||||||
|
x = x * n
|
||||||
|
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
|
||||||
|
return x / n
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function returns estimated speech time in seconds.
|
||||||
|
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word so
|
||||||
|
--
|
||||||
|
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
||||||
|
--
|
||||||
|
-- So lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
||||||
|
--
|
||||||
|
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||||
|
--
|
||||||
|
function STTS.getSpeechTime(length,speed,isGoogle)
|
||||||
|
|
||||||
|
local maxRateRatio = 3
|
||||||
|
|
||||||
|
speed = speed or 1.0
|
||||||
|
isGoogle = isGoogle or false
|
||||||
|
|
||||||
|
local speedFactor = 1.0
|
||||||
|
if isGoogle then
|
||||||
|
speedFactor = speed
|
||||||
|
else
|
||||||
|
if speed ~= 0 then
|
||||||
|
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1
|
||||||
|
end
|
||||||
|
if speed < 0 then
|
||||||
|
speedFactor = 1/speedFactor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local wpm = math.ceil(100 * speedFactor)
|
||||||
|
local cps = math.floor((wpm * 5)/60)
|
||||||
|
|
||||||
|
if type(length) == "string" then
|
||||||
|
length = string.len(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
return math.ceil(length/cps)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Text to speech function.
|
||||||
|
function STTS.TextToSpeech(message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS)
|
||||||
|
if os == nil or io == nil then
|
||||||
|
env.info("[DCS-STTS] LUA modules os or io are sanitized. skipping. ")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
speed = speed or 1
|
||||||
|
gender = gender or "female"
|
||||||
|
culture = culture or ""
|
||||||
|
voice = voice or ""
|
||||||
|
coalition=coalition or "0"
|
||||||
|
name=name or "ROBOT"
|
||||||
|
volume=1
|
||||||
|
speed=1
|
||||||
|
|
||||||
|
|
||||||
|
message = message:gsub("\"","\\\"")
|
||||||
|
|
||||||
|
local cmd = string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name)
|
||||||
|
|
||||||
|
if voice ~= "" then
|
||||||
|
cmd = cmd .. string.format(" -V \"%s\"",voice)
|
||||||
|
else
|
||||||
|
|
||||||
|
if culture ~= "" then
|
||||||
|
cmd = cmd .. string.format(" -l %s",culture)
|
||||||
|
end
|
||||||
|
|
||||||
|
if gender ~= "" then
|
||||||
|
cmd = cmd .. string.format(" -g %s",gender)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if googleTTS == true then
|
||||||
|
cmd = cmd .. string.format(" -G \"%s\"",STTS.GOOGLE_CREDENTIALS)
|
||||||
|
end
|
||||||
|
|
||||||
|
if speed ~= 1 then
|
||||||
|
cmd = cmd .. string.format(" -s %s",speed)
|
||||||
|
end
|
||||||
|
|
||||||
|
if volume ~= 1.0 then
|
||||||
|
cmd = cmd .. string.format(" -v %s",volume)
|
||||||
|
end
|
||||||
|
|
||||||
|
if point and type(point) == "table" and point.x then
|
||||||
|
local lat, lon, alt = coord.LOtoLL(point)
|
||||||
|
|
||||||
|
lat = STTS.round(lat,4)
|
||||||
|
lon = STTS.round(lon,4)
|
||||||
|
alt = math.floor(alt)
|
||||||
|
|
||||||
|
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd = cmd ..string.format(" -t \"%s\"",message)
|
||||||
|
|
||||||
|
if string.len(cmd) > 255 then
|
||||||
|
local filename = os.getenv('TMP') .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
||||||
|
local script = io.open(filename,"w+")
|
||||||
|
script:write(cmd .. " && exit" )
|
||||||
|
script:close()
|
||||||
|
cmd = string.format("\"%s\"",filename)
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime() + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.len(cmd) > 255 then
|
||||||
|
env.info("[DCS-STTS] - cmd string too long")
|
||||||
|
env.info("[DCS-STTS] TextToSpeech Command :\n" .. cmd.."\n")
|
||||||
|
end
|
||||||
|
os.execute(cmd)
|
||||||
|
|
||||||
|
return STTS.getSpeechTime(message,speed,googleTTS)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Play mp3 function.
|
||||||
|
-- @param #string pathToMP3 Path to the sound file.
|
||||||
|
-- @param #string freqs Frequencies, e.g. "305, 256".
|
||||||
|
-- @param #string modulations Modulations, e.g. "AM, FM".
|
||||||
|
-- @param #string volume Volume, e.g. "0.5".
|
||||||
|
function STTS.PlayMP3(pathToMP3, freqs, modulations, volume, name, coalition, point)
|
||||||
|
|
||||||
|
local cmd = string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h",
|
||||||
|
STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1")
|
||||||
|
|
||||||
|
if point and type(point) == "table" and point.x then
|
||||||
|
local lat, lon, alt = coord.LOtoLL(point)
|
||||||
|
|
||||||
|
lat = STTS.round(lat,4)
|
||||||
|
lon = STTS.round(lon,4)
|
||||||
|
alt = math.floor(alt)
|
||||||
|
|
||||||
|
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("[DCS-STTS] MP3/OGG Command :\n" .. cmd.."\n")
|
||||||
|
os.execute(cmd)
|
||||||
|
|
||||||
|
end
|
||||||
612
Moose Development/Moose/Utilities/Templates.lua
Normal file
612
Moose Development/Moose/Utilities/Templates.lua
Normal file
@@ -0,0 +1,612 @@
|
|||||||
|
--- **Utils** Templates
|
||||||
|
--
|
||||||
|
-- DCS unit templates
|
||||||
|
--
|
||||||
|
-- @module Utilities.Templates
|
||||||
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
--- TEMPLATE class.
|
||||||
|
-- @type TEMPLATE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
|
||||||
|
--- *Templates*
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- 
|
||||||
|
--
|
||||||
|
-- Get DCS templates from thin air.
|
||||||
|
--
|
||||||
|
-- # Ground Units
|
||||||
|
--
|
||||||
|
-- Ground units.
|
||||||
|
--
|
||||||
|
-- # Naval Units
|
||||||
|
--
|
||||||
|
-- Ships are not implemented yet.
|
||||||
|
--
|
||||||
|
-- # Aircraft
|
||||||
|
--
|
||||||
|
-- ## Airplanes
|
||||||
|
--
|
||||||
|
-- Airplanes are not implemented yet.
|
||||||
|
--
|
||||||
|
-- ## Helicopters
|
||||||
|
--
|
||||||
|
-- Helicopters are not implemented yet.
|
||||||
|
--
|
||||||
|
-- @field #TEMPLATE
|
||||||
|
TEMPLATE = {
|
||||||
|
ClassName = "TEMPLATE",
|
||||||
|
Ground = {},
|
||||||
|
Naval = {},
|
||||||
|
Airplane = {},
|
||||||
|
Helicopter = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Ground unit type names.
|
||||||
|
-- @type TEMPLATE.TypeGround
|
||||||
|
-- @param #string InfantryAK
|
||||||
|
TEMPLATE.TypeGround={
|
||||||
|
InfantryAK="Infantry AK",
|
||||||
|
ParatrooperAKS74="Paratrooper AKS-74",
|
||||||
|
ParatrooperRPG16="Paratrooper RPG-16",
|
||||||
|
SoldierWWIIUS="soldier_wwii_us",
|
||||||
|
InfantryM248="Infantry M249",
|
||||||
|
SoldierM4="Soldier M4",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Naval unit type names.
|
||||||
|
-- @type TEMPLATE.TypeNaval
|
||||||
|
-- @param #string Ticonderoga
|
||||||
|
TEMPLATE.TypeNaval={
|
||||||
|
Ticonderoga="TICONDEROG",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Rotary wing unit type names.
|
||||||
|
-- @type TEMPLATE.TypeAirplane
|
||||||
|
-- @param #string A10C
|
||||||
|
TEMPLATE.TypeAirplane={
|
||||||
|
A10C="A-10C",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Rotary wing unit type names.
|
||||||
|
-- @type TEMPLATE.TypeHelicopter
|
||||||
|
-- @param #string AH1W
|
||||||
|
TEMPLATE.TypeHelicopter={
|
||||||
|
AH1W="AH-1W",
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Ground Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get template for ground units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 50 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetGround(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeGround.SoldierM4
|
||||||
|
GroupName=GroupName or "Ground-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 50
|
||||||
|
|
||||||
|
|
||||||
|
-- Get generic template.
|
||||||
|
local template=UTILS.DeepCopy(TEMPLATE.GenericGround)
|
||||||
|
|
||||||
|
-- Set group name.
|
||||||
|
template.name=GroupName
|
||||||
|
|
||||||
|
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||||
|
template.CountryID=CountryID
|
||||||
|
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||||
|
template.CategoryID=Unit.Category.GROUND_UNIT
|
||||||
|
|
||||||
|
-- Set first unit.
|
||||||
|
template.units[1].type=TypeName
|
||||||
|
template.units[1].name=GroupName.."-1"
|
||||||
|
|
||||||
|
if Vec3 then
|
||||||
|
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Naval Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get template for ground units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetNaval(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeNaval.Ticonderoga
|
||||||
|
GroupName=GroupName or "Naval-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 500
|
||||||
|
|
||||||
|
|
||||||
|
-- Get generic template.
|
||||||
|
local template=UTILS.DeepCopy(TEMPLATE.GenericNaval)
|
||||||
|
|
||||||
|
-- Set group name.
|
||||||
|
template.name=GroupName
|
||||||
|
|
||||||
|
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||||
|
template.CountryID=CountryID
|
||||||
|
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||||
|
template.CategoryID=Unit.Category.SHIP
|
||||||
|
|
||||||
|
-- Set first unit.
|
||||||
|
template.units[1].type=TypeName
|
||||||
|
template.units[1].name=GroupName.."-1"
|
||||||
|
|
||||||
|
if Vec3 then
|
||||||
|
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Aircraft Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get template for fixed wing units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetAirplane(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeAirplane.A10C
|
||||||
|
GroupName=GroupName or "Airplane-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=1000, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 100
|
||||||
|
|
||||||
|
local template=TEMPLATE._GetAircraft(true, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get template for fixed wing units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetHelicopter(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeHelicopter.AH1W
|
||||||
|
GroupName=GroupName or "Helicopter-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=500, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 100
|
||||||
|
|
||||||
|
-- Limit unis to 4.
|
||||||
|
Nunits=math.min(Nunits, 4)
|
||||||
|
|
||||||
|
local template=TEMPLATE._GetAircraft(false, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get template for aircraft units.
|
||||||
|
-- @param #boolean Airplane If true, this is a fixed wing. Else, rotary wing.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE._GetAircraft(Airplane, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName
|
||||||
|
GroupName=GroupName or "Aircraft-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 100
|
||||||
|
|
||||||
|
-- Get generic template.
|
||||||
|
local template=UTILS.DeepCopy(TEMPLATE.GenericAircraft)
|
||||||
|
|
||||||
|
-- Set group name.
|
||||||
|
template.name=GroupName
|
||||||
|
|
||||||
|
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||||
|
template.CountryID=CountryID
|
||||||
|
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||||
|
if Airplane then
|
||||||
|
template.CategoryID=Unit.Category.AIRPLANE
|
||||||
|
else
|
||||||
|
template.CategoryID=Unit.Category.HELICOPTER
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set first unit.
|
||||||
|
template.units[1].type=TypeName
|
||||||
|
template.units[1].name=GroupName.."-1"
|
||||||
|
|
||||||
|
-- Set position.
|
||||||
|
if Vec3 then
|
||||||
|
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set number of units.
|
||||||
|
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Misc Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param DCS#Vec2 Vec2 2D Position vector with x and y components of the group.
|
||||||
|
function TEMPLATE.SetPositionFromVec2(Template, Vec2)
|
||||||
|
|
||||||
|
Template.x=Vec2.x
|
||||||
|
Template.y=Vec2.y
|
||||||
|
|
||||||
|
for _,unit in pairs(Template.units) do
|
||||||
|
unit.x=Vec2.x
|
||||||
|
unit.y=Vec2.y
|
||||||
|
end
|
||||||
|
|
||||||
|
Template.route.points[1].x=Vec2.x
|
||||||
|
Template.route.points[1].y=Vec2.y
|
||||||
|
Template.route.points[1].alt=0 --TODO: Use land height.
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position vector of the group.
|
||||||
|
function TEMPLATE.SetPositionFromVec3(Template, Vec3)
|
||||||
|
|
||||||
|
local Vec2={x=Vec3.x, y=Vec3.z}
|
||||||
|
|
||||||
|
TEMPLATE.SetPositionFromVec2(Template, Vec2)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param #number N Total number of units in the group.
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Position of the first unit.
|
||||||
|
-- @param #number Radius Radius in meters to randomly place the additional units.
|
||||||
|
function TEMPLATE.SetUnits(Template, N, Coordinate, Radius)
|
||||||
|
|
||||||
|
local units=Template.units
|
||||||
|
|
||||||
|
local unit1=units[1]
|
||||||
|
|
||||||
|
local Vec3=Coordinate:GetVec3()
|
||||||
|
|
||||||
|
unit1.x=Vec3.x
|
||||||
|
unit1.y=Vec3.z
|
||||||
|
unit1.alt=Vec3.y
|
||||||
|
|
||||||
|
for i=2,N do
|
||||||
|
units[i]=UTILS.DeepCopy(unit1)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,N do
|
||||||
|
local unit=units[i]
|
||||||
|
unit.name=string.format("%s-%d", Template.name, i)
|
||||||
|
if i>1 then
|
||||||
|
local vec2=Coordinate:GetRandomCoordinateInRadius(Radius, 5):GetVec2()
|
||||||
|
unit.x=vec2.x
|
||||||
|
unit.y=vec2.y
|
||||||
|
unit.alt=unit1.alt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE AirBase The airbase where the aircraft are spawned.
|
||||||
|
-- @param #table ParkingSpots List of parking spot IDs. Every unit needs one!
|
||||||
|
-- @param #boolean EngineOn If true, aircraft are spawned hot.
|
||||||
|
function TEMPLATE.SetAirbase(Template, AirBase, ParkingSpots, EngineOn)
|
||||||
|
|
||||||
|
-- Airbase ID.
|
||||||
|
local AirbaseID=AirBase:GetID()
|
||||||
|
|
||||||
|
-- Spawn point.
|
||||||
|
local point=Template.route.points[1]
|
||||||
|
|
||||||
|
-- Set ID.
|
||||||
|
if AirBase:IsAirdrome() then
|
||||||
|
point.airdromeId=AirbaseID
|
||||||
|
else
|
||||||
|
point.helipadId=AirbaseID
|
||||||
|
point.linkUnit=AirbaseID
|
||||||
|
end
|
||||||
|
|
||||||
|
if EngineOn then
|
||||||
|
point.action=COORDINATE.WaypointAction.FromParkingAreaHot
|
||||||
|
point.type=COORDINATE.WaypointType.TakeOffParkingHot
|
||||||
|
else
|
||||||
|
point.action=COORDINATE.WaypointAction.FromParkingArea
|
||||||
|
point.type=COORDINATE.WaypointType.TakeOffParking
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,unit in ipairs(Template.units) do
|
||||||
|
unit.parking_id=ParkingSpots[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a waypoint.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param #table Waypoint Waypoint table.
|
||||||
|
function TEMPLATE.AddWaypoint(Template, Waypoint)
|
||||||
|
|
||||||
|
table.insert(Template.route.points, Waypoint)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Generic Ground Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEMPLATE.GenericGround=
|
||||||
|
{
|
||||||
|
["visible"] = false,
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
["uncontrollable"] = false,
|
||||||
|
["task"] = "Ground Nothing",
|
||||||
|
["route"] =
|
||||||
|
{
|
||||||
|
["spans"] = {}, -- end of ["spans"]
|
||||||
|
["points"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["alt"] = 0,
|
||||||
|
["type"] = "Turning Point",
|
||||||
|
["ETA"] = 0,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["formation_template"] = "",
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["ETA_locked"] = true,
|
||||||
|
["speed"] = 0,
|
||||||
|
["action"] = "Off Road",
|
||||||
|
["task"] =
|
||||||
|
{
|
||||||
|
["id"] = "ComboTask",
|
||||||
|
["params"] =
|
||||||
|
{
|
||||||
|
["tasks"] =
|
||||||
|
{
|
||||||
|
}, -- end of ["tasks"]
|
||||||
|
}, -- end of ["params"]
|
||||||
|
}, -- end of ["task"]
|
||||||
|
["speed_locked"] = true,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["points"]
|
||||||
|
}, -- end of ["route"]
|
||||||
|
["groupId"] = nil,
|
||||||
|
["hidden"] = false,
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["transportable"] =
|
||||||
|
{
|
||||||
|
["randomTransportable"] = false,
|
||||||
|
}, -- end of ["transportable"]
|
||||||
|
["skill"] = "Average",
|
||||||
|
["type"] = "Infantry AK",
|
||||||
|
["unitId"] = nil,
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Infantry AK-47 Rus",
|
||||||
|
["heading"] = 0,
|
||||||
|
["playerCanDrive"] = false,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Infantry AK-47 Rus",
|
||||||
|
["start_time"] = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Generic Ship Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEMPLATE.GenericNaval=
|
||||||
|
{
|
||||||
|
["visible"] = false,
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
["uncontrollable"] = false,
|
||||||
|
["route"] =
|
||||||
|
{
|
||||||
|
["points"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["alt"] = 0,
|
||||||
|
["type"] = "Turning Point",
|
||||||
|
["ETA"] = 0,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["formation_template"] = "",
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["ETA_locked"] = true,
|
||||||
|
["speed"] = 0,
|
||||||
|
["action"] = "Turning Point",
|
||||||
|
["task"] =
|
||||||
|
{
|
||||||
|
["id"] = "ComboTask",
|
||||||
|
["params"] =
|
||||||
|
{
|
||||||
|
["tasks"] =
|
||||||
|
{
|
||||||
|
}, -- end of ["tasks"]
|
||||||
|
}, -- end of ["params"]
|
||||||
|
}, -- end of ["task"]
|
||||||
|
["speed_locked"] = true,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["points"]
|
||||||
|
}, -- end of ["route"]
|
||||||
|
["groupId"] = nil,
|
||||||
|
["hidden"] = false,
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["transportable"] =
|
||||||
|
{
|
||||||
|
["randomTransportable"] = false,
|
||||||
|
}, -- end of ["transportable"]
|
||||||
|
["skill"] = "Average",
|
||||||
|
["type"] = "TICONDEROG",
|
||||||
|
["unitId"] = nil,
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Naval-1-1",
|
||||||
|
["heading"] = 0,
|
||||||
|
["modulation"] = 0,
|
||||||
|
["frequency"] = 127500000,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Naval-1",
|
||||||
|
["start_time"] = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Generic Aircraft Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEMPLATE.GenericAircraft=
|
||||||
|
{
|
||||||
|
["groupId"] = nil,
|
||||||
|
["name"] = "Rotary-1",
|
||||||
|
["uncontrolled"] = false,
|
||||||
|
["hidden"] = false,
|
||||||
|
["task"] = "Nothing",
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["start_time"] = 0,
|
||||||
|
["communication"] = true,
|
||||||
|
["radioSet"] = false,
|
||||||
|
["frequency"] = 127.5,
|
||||||
|
["modulation"] = 0,
|
||||||
|
["taskSelected"] = true,
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
["route"] =
|
||||||
|
{
|
||||||
|
["points"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["alt"] = 1000,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["action"] = "Turning Point",
|
||||||
|
["type"] = "Turning Point",
|
||||||
|
["airdromeId"] = nil,
|
||||||
|
["task"] =
|
||||||
|
{
|
||||||
|
["id"] = "ComboTask",
|
||||||
|
["params"] =
|
||||||
|
{
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
}, -- end of ["params"]
|
||||||
|
}, -- end of ["task"]
|
||||||
|
["ETA"] = 0,
|
||||||
|
["ETA_locked"] = true,
|
||||||
|
["speed"] = 100,
|
||||||
|
["speed_locked"] = true,
|
||||||
|
["formation_template"] = "",
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["points"]
|
||||||
|
}, -- end of ["route"]
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["name"] = "Rotary-1-1",
|
||||||
|
["unitId"] = nil,
|
||||||
|
["type"] = "AH-1W",
|
||||||
|
["onboard_num"] = "050",
|
||||||
|
["livery_id"] = "USA X Black",
|
||||||
|
["skill"] = "High",
|
||||||
|
["ropeLength"] = 15,
|
||||||
|
["speed"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["y"] = 0,
|
||||||
|
["alt"] = 10,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["heading"] = 0,
|
||||||
|
["psi"] = 0,
|
||||||
|
["parking"] = nil,
|
||||||
|
["parking_id"] = nil,
|
||||||
|
["payload"] =
|
||||||
|
{
|
||||||
|
["pylons"] = {}, -- end of ["pylons"]
|
||||||
|
["fuel"] = "1250.0",
|
||||||
|
["flare"] = 30,
|
||||||
|
["chaff"] = 30,
|
||||||
|
["gun"] = 100,
|
||||||
|
}, -- end of ["payload"]
|
||||||
|
["callsign"] =
|
||||||
|
{
|
||||||
|
[1] = 2,
|
||||||
|
[2] = 1,
|
||||||
|
[3] = 1,
|
||||||
|
["name"] = "Springfield11",
|
||||||
|
}, -- end of ["callsign"]
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -50,6 +50,7 @@ BIGSMOKEPRESET = {
|
|||||||
-- @field #string PersianGulf Persian Gulf map.
|
-- @field #string PersianGulf Persian Gulf map.
|
||||||
-- @field #string TheChannel The Channel map.
|
-- @field #string TheChannel The Channel map.
|
||||||
-- @field #string Syria Syria map.
|
-- @field #string Syria Syria map.
|
||||||
|
-- @field #string MarianaIslands Mariana Islands map.
|
||||||
DCSMAP = {
|
DCSMAP = {
|
||||||
Caucasus="Caucasus",
|
Caucasus="Caucasus",
|
||||||
NTTR="Nevada",
|
NTTR="Nevada",
|
||||||
@@ -57,6 +58,7 @@ DCSMAP = {
|
|||||||
PersianGulf="PersianGulf",
|
PersianGulf="PersianGulf",
|
||||||
TheChannel="TheChannel",
|
TheChannel="TheChannel",
|
||||||
Syria="Syria",
|
Syria="Syria",
|
||||||
|
MarianaIslands="MarianaIslands"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -489,7 +491,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
|
||||||
|
|
||||||
@@ -652,6 +654,17 @@ function UTILS.GetMarkID()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Remove an object (marker, circle, arrow, text, quad, ...) on the F10 map.
|
||||||
|
-- @param #number MarkID Unique ID of the object.
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the mark is removed.
|
||||||
|
function UTILS.RemoveMark(MarkID, Delay)
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
TIMER:New(UTILS.RemoveMark, MarkID):Start(Delay)
|
||||||
|
else
|
||||||
|
trigger.action.removeMark(MarkID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Test if a Vec2 is in a radius of another Vec2
|
-- Test if a Vec2 is in a radius of another Vec2
|
||||||
function UTILS.IsInRadius( InVec2, Vec2, Radius )
|
function UTILS.IsInRadius( InVec2, Vec2, Radius )
|
||||||
@@ -669,7 +682,10 @@ function UTILS.IsInSphere( InVec3, Vec3, Radius )
|
|||||||
return InSphere
|
return InSphere
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Beaufort scale: returns Beaufort number and wind description as a function of wind speed in m/s.
|
--- Beaufort scale: returns Beaufort number and wind description as a function of wind speed in m/s.
|
||||||
|
-- @param #number speed Wind speed in m/s.
|
||||||
|
-- @return #number Beaufort number.
|
||||||
|
-- @return #string Beauford wind description.
|
||||||
function UTILS.BeaufortScale(speed)
|
function UTILS.BeaufortScale(speed)
|
||||||
local bn=nil
|
local bn=nil
|
||||||
local bd=nil
|
local bd=nil
|
||||||
@@ -729,6 +745,21 @@ function UTILS.Split(str, sep)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a table of all characters in a string.
|
||||||
|
-- @param #string str Sting.
|
||||||
|
-- @return #table Individual characters.
|
||||||
|
function UTILS.GetCharacters(str)
|
||||||
|
|
||||||
|
local chars={}
|
||||||
|
|
||||||
|
for i=1,#str do
|
||||||
|
local c=str:sub(i,i)
|
||||||
|
table.insert(chars, c)
|
||||||
|
end
|
||||||
|
|
||||||
|
return chars
|
||||||
|
end
|
||||||
|
|
||||||
--- Convert time in seconds to hours, minutes and seconds.
|
--- Convert time in seconds to hours, minutes and seconds.
|
||||||
-- @param #number seconds Time in seconds, e.g. from timer.getAbsTime() function.
|
-- @param #number seconds Time in seconds, e.g. from timer.getAbsTime() function.
|
||||||
-- @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.
|
||||||
@@ -1151,6 +1182,9 @@ end
|
|||||||
-- * NTTR +12 (East), year ~ 2011
|
-- * NTTR +12 (East), year ~ 2011
|
||||||
-- * Normandy -10 (West), year ~ 1944
|
-- * Normandy -10 (West), year ~ 1944
|
||||||
-- * Persian Gulf +2 (East), year ~ 2011
|
-- * Persian Gulf +2 (East), year ~ 2011
|
||||||
|
-- * The Cannel Map -10 (West)
|
||||||
|
-- * Syria +5 (East)
|
||||||
|
-- * Mariana Islands +2 (East)
|
||||||
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
||||||
-- @return #number Declination in degrees.
|
-- @return #number Declination in degrees.
|
||||||
function UTILS.GetMagneticDeclination(map)
|
function UTILS.GetMagneticDeclination(map)
|
||||||
@@ -1171,6 +1205,8 @@ function UTILS.GetMagneticDeclination(map)
|
|||||||
declination=-10
|
declination=-10
|
||||||
elseif map==DCSMAP.Syria then
|
elseif map==DCSMAP.Syria then
|
||||||
declination=5
|
declination=5
|
||||||
|
elseif map==DCSMAP.MarianaIslands then
|
||||||
|
declination=2
|
||||||
else
|
else
|
||||||
declination=0
|
declination=0
|
||||||
end
|
end
|
||||||
@@ -1299,6 +1335,8 @@ function UTILS.GMTToLocalTimeDifference()
|
|||||||
return 2 -- This map currently needs +2
|
return 2 -- This map currently needs +2
|
||||||
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
|
||||||
|
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
|
||||||
@@ -1466,3 +1504,208 @@ function UTILS.GetOSTime()
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Shuffle a table accoring to Fisher Yeates algorithm
|
||||||
|
--@param #table table to be shuffled
|
||||||
|
--@return #table
|
||||||
|
function UTILS.ShuffleTable(t)
|
||||||
|
if t == nil or type(t) ~= "table" then
|
||||||
|
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
local TempTable = {}
|
||||||
|
for i = 1, #t do
|
||||||
|
local r = math.random(1,#t)
|
||||||
|
TempTable[i] = t[r]
|
||||||
|
table.remove(t,r)
|
||||||
|
end
|
||||||
|
return TempTable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Helicopter) Check if one loading door is open.
|
||||||
|
--@param #string unit_name Unit name to be checked
|
||||||
|
--@return #boolean Outcome - true if a (loading door) is open, false if not, nil if none exists.
|
||||||
|
function UTILS.IsLoadingDoorOpen( unit_name )
|
||||||
|
|
||||||
|
local ret_val = false
|
||||||
|
local unit = Unit.getByName(unit_name)
|
||||||
|
if unit ~= nil then
|
||||||
|
local type_name = unit:getTypeName()
|
||||||
|
|
||||||
|
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
|
||||||
|
BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
|
||||||
|
ret_val = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
|
||||||
|
BASE:T(unit_name .. " a side door is open")
|
||||||
|
ret_val = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
|
||||||
|
BASE:T(unit_name .. " a side door is open ")
|
||||||
|
ret_val = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
|
||||||
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
|
ret_val = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if ret_val == false then
|
||||||
|
BASE:T(unit_name .. " all doors are closed")
|
||||||
|
end
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
end -- nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid FM frequencies in mHz for radio beacons (FM).
|
||||||
|
-- @return #table Table of frequencies.
|
||||||
|
function UTILS.GenerateFMFrequencies()
|
||||||
|
local FreeFMFrequencies = {}
|
||||||
|
for _first = 3, 7 do
|
||||||
|
for _second = 0, 5 do
|
||||||
|
for _third = 0, 9 do
|
||||||
|
local _frequency = ((100 * _first) + (10 * _second) + _third) * 100000 --extra 0 because we didnt bother with 4th digit
|
||||||
|
table.insert(FreeFMFrequencies, _frequency)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return FreeFMFrequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid VHF frequencies in kHz for radio beacons (FM).
|
||||||
|
-- @return #table VHFrequencies
|
||||||
|
function UTILS.GenerateVHFrequencies()
|
||||||
|
|
||||||
|
-- known and sorted map-wise NDBs in kHz
|
||||||
|
local _skipFrequencies = {
|
||||||
|
214,274,291.5,295,297.5,
|
||||||
|
300.5,304,307,309.5,311,312,312.5,316,
|
||||||
|
320,324,328,329,330,336,337,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
local FreeVHFFrequencies = {}
|
||||||
|
|
||||||
|
-- first range
|
||||||
|
local _start = 200000
|
||||||
|
while _start < 400000 do
|
||||||
|
|
||||||
|
-- skip existing NDB frequencies#
|
||||||
|
local _found = false
|
||||||
|
for _, value in pairs(_skipFrequencies) do
|
||||||
|
if value * 1000 == _start then
|
||||||
|
_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _found == false then
|
||||||
|
table.insert(FreeVHFFrequencies, _start)
|
||||||
|
end
|
||||||
|
_start = _start + 10000
|
||||||
|
end
|
||||||
|
|
||||||
|
-- second range
|
||||||
|
_start = 400000
|
||||||
|
while _start < 850000 do
|
||||||
|
-- skip existing NDB frequencies
|
||||||
|
local _found = false
|
||||||
|
for _, value in pairs(_skipFrequencies) do
|
||||||
|
if value * 1000 == _start then
|
||||||
|
_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _found == false then
|
||||||
|
table.insert(FreeVHFFrequencies, _start)
|
||||||
|
end
|
||||||
|
_start = _start + 10000
|
||||||
|
end
|
||||||
|
|
||||||
|
-- third range
|
||||||
|
_start = 850000
|
||||||
|
while _start <= 999000 do -- adjusted for Gazelle
|
||||||
|
-- skip existing NDB frequencies
|
||||||
|
local _found = false
|
||||||
|
for _, value in pairs(_skipFrequencies) do
|
||||||
|
if value * 1000 == _start then
|
||||||
|
_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _found == false then
|
||||||
|
table.insert(FreeVHFFrequencies, _start)
|
||||||
|
end
|
||||||
|
_start = _start + 50000
|
||||||
|
end
|
||||||
|
|
||||||
|
return FreeVHFFrequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid UHF Frequencies in mHz (AM).
|
||||||
|
-- @return #table UHF Frequencies
|
||||||
|
function UTILS.GenerateUHFrequencies()
|
||||||
|
|
||||||
|
local FreeUHFFrequencies = {}
|
||||||
|
local _start = 220000000
|
||||||
|
|
||||||
|
while _start < 399000000 do
|
||||||
|
table.insert(FreeUHFFrequencies, _start)
|
||||||
|
_start = _start + 500000
|
||||||
|
end
|
||||||
|
|
||||||
|
return FreeUHFFrequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid laser codes for JTAC.
|
||||||
|
-- @return #table Laser Codes.
|
||||||
|
function UTILS.GenerateLaserCodes()
|
||||||
|
local jtacGeneratedLaserCodes = {}
|
||||||
|
|
||||||
|
-- helper function
|
||||||
|
local function ContainsDigit(_number, _numberToFind)
|
||||||
|
local _thisNumber = _number
|
||||||
|
local _thisDigit = 0
|
||||||
|
while _thisNumber ~= 0 do
|
||||||
|
_thisDigit = _thisNumber % 10
|
||||||
|
_thisNumber = math.floor(_thisNumber / 10)
|
||||||
|
if _thisDigit == _numberToFind then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate list of laser codes
|
||||||
|
local _code = 1111
|
||||||
|
local _count = 1
|
||||||
|
while _code < 1777 and _count < 30 do
|
||||||
|
while true do
|
||||||
|
_code = _code + 1
|
||||||
|
if not self:_ContainsDigit(_code, 8)
|
||||||
|
and not ContainsDigit(_code, 9)
|
||||||
|
and not ContainsDigit(_code, 0) then
|
||||||
|
table.insert(jtacGeneratedLaserCodes, _code)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_count = _count + 1
|
||||||
|
end
|
||||||
|
return jtacGeneratedLaserCodes
|
||||||
|
end
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ AIRBASE = {
|
|||||||
|
|
||||||
--- Enumeration to identify the airbases in the Caucasus region.
|
--- Enumeration to identify the airbases in the Caucasus region.
|
||||||
--
|
--
|
||||||
-- These are all airbases of Caucasus:
|
-- Airbases of the Caucasus map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Caucasus.Gelendzhik
|
-- * AIRBASE.Caucasus.Gelendzhik
|
||||||
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
|
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
|
||||||
@@ -122,7 +122,7 @@ AIRBASE.Caucasus = {
|
|||||||
["Beslan"] = "Beslan",
|
["Beslan"] = "Beslan",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of Nevada:
|
--- Airbases of the Nevada map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Nevada.Creech_AFB
|
-- * AIRBASE.Nevada.Creech_AFB
|
||||||
-- * AIRBASE.Nevada.Groom_Lake_AFB
|
-- * AIRBASE.Nevada.Groom_Lake_AFB
|
||||||
@@ -141,6 +141,7 @@ AIRBASE.Caucasus = {
|
|||||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||||
|
--
|
||||||
-- @field Nevada
|
-- @field Nevada
|
||||||
AIRBASE.Nevada = {
|
AIRBASE.Nevada = {
|
||||||
["Creech_AFB"] = "Creech AFB",
|
["Creech_AFB"] = "Creech AFB",
|
||||||
@@ -162,7 +163,7 @@ AIRBASE.Nevada = {
|
|||||||
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
|
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of Normandy:
|
--- Airbases of the Normandy map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
|
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
|
||||||
-- * AIRBASE.Normandy.Lignerolles
|
-- * AIRBASE.Normandy.Lignerolles
|
||||||
@@ -195,6 +196,7 @@ AIRBASE.Nevada = {
|
|||||||
-- * AIRBASE.Normandy.Funtington
|
-- * AIRBASE.Normandy.Funtington
|
||||||
-- * AIRBASE.Normandy.Tangmere
|
-- * AIRBASE.Normandy.Tangmere
|
||||||
-- * AIRBASE.Normandy.Ford_AF
|
-- * AIRBASE.Normandy.Ford_AF
|
||||||
|
--
|
||||||
-- @field Normandy
|
-- @field Normandy
|
||||||
AIRBASE.Normandy = {
|
AIRBASE.Normandy = {
|
||||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||||
@@ -237,7 +239,7 @@ AIRBASE.Normandy = {
|
|||||||
["Conches"] = "Conches",
|
["Conches"] = "Conches",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of the Persion Gulf Map:
|
--- Airbases of the Persion Gulf Map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.PersianGulf.Abu_Dhabi_International_Airport
|
-- * AIRBASE.PersianGulf.Abu_Dhabi_International_Airport
|
||||||
-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport
|
-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport
|
||||||
@@ -268,6 +270,7 @@ AIRBASE.Normandy = {
|
|||||||
-- * AIRBASE.PersianGulf.Sirri_Island
|
-- * AIRBASE.PersianGulf.Sirri_Island
|
||||||
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
|
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
|
||||||
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
||||||
|
--
|
||||||
-- @field PersianGulf
|
-- @field PersianGulf
|
||||||
AIRBASE.PersianGulf = {
|
AIRBASE.PersianGulf = {
|
||||||
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
|
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
|
||||||
@@ -301,7 +304,7 @@ AIRBASE.PersianGulf = {
|
|||||||
["Tunb_Kochak"] = "Tunb Kochak",
|
["Tunb_Kochak"] = "Tunb Kochak",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of the The Channel Map:
|
--- Airbases of The Channel Map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.TheChannel.Abbeville_Drucat
|
-- * AIRBASE.TheChannel.Abbeville_Drucat
|
||||||
-- * AIRBASE.TheChannel.Merville_Calonne
|
-- * AIRBASE.TheChannel.Merville_Calonne
|
||||||
@@ -326,7 +329,7 @@ AIRBASE.TheChannel = {
|
|||||||
["High_Halden"] = "High Halden",
|
["High_Halden"] = "High Halden",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of Syria
|
--- Airbases of the Syria map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Syria.Kuweires
|
-- * AIRBASE.Syria.Kuweires
|
||||||
-- * AIRBASE.Syria.Marj_Ruhayyil
|
-- * AIRBASE.Syria.Marj_Ruhayyil
|
||||||
@@ -336,39 +339,51 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Incirlik
|
-- * AIRBASE.Syria.Incirlik
|
||||||
-- * AIRBASE.Syria.Damascus
|
-- * AIRBASE.Syria.Damascus
|
||||||
-- * AIRBASE.Syria.Bassel_Al_Assad
|
-- * AIRBASE.Syria.Bassel_Al_Assad
|
||||||
|
-- * AIRBASE.Syria.Rosh_Pina
|
||||||
-- * AIRBASE.Syria.Aleppo
|
-- * AIRBASE.Syria.Aleppo
|
||||||
-- * AIRBASE.Syria.Qabr_as_Sitt
|
-- * AIRBASE.Syria.Al_Qusayr
|
||||||
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
||||||
-- * AIRBASE.Syria.Al_Dumayr
|
-- * AIRBASE.Syria.Al_Dumayr
|
||||||
|
-- * AIRBASE.Syria.Gazipasa
|
||||||
|
-- * AIRBASE.Syria.Ru_Convoy_4
|
||||||
-- * AIRBASE.Syria.Hatay
|
-- * AIRBASE.Syria.Hatay
|
||||||
|
-- * AIRBASE.Syria.Nicosia
|
||||||
|
-- * AIRBASE.Syria.Pinarbashi
|
||||||
|
-- * AIRBASE.Syria.Paphos
|
||||||
|
-- * AIRBASE.Syria.Kingsfield
|
||||||
|
-- * AIRBASE.Syria.Thalah
|
||||||
-- * AIRBASE.Syria.Haifa
|
-- * AIRBASE.Syria.Haifa
|
||||||
-- * AIRBASE.Syria.Khalkhalah
|
-- * AIRBASE.Syria.Khalkhalah
|
||||||
-- * AIRBASE.Syria.Megiddo
|
-- * AIRBASE.Syria.Megiddo
|
||||||
|
-- * AIRBASE.Syria.Lakatamia
|
||||||
-- * AIRBASE.Syria.Rayak
|
-- * AIRBASE.Syria.Rayak
|
||||||
|
-- * AIRBASE.Syria.Larnaca
|
||||||
-- * AIRBASE.Syria.Mezzeh
|
-- * AIRBASE.Syria.Mezzeh
|
||||||
-- * AIRBASE.Syria.King_Hussein_Air_College
|
-- * AIRBASE.Syria.Gecitkale
|
||||||
-- * AIRBASE.Syria.Jirah
|
-- * AIRBASE.Syria.Akrotiri
|
||||||
|
-- * AIRBASE.Syria.Naqoura
|
||||||
|
-- * AIRBASE.Syria.Gaziantep
|
||||||
|
-- * AIRBASE.Syria.CVN_71
|
||||||
|
-- * AIRBASE.Syria.Sayqal
|
||||||
|
-- * AIRBASE.Syria.Tiyas
|
||||||
|
-- * AIRBASE.Syria.Shayrat
|
||||||
-- * AIRBASE.Syria.Taftanaz
|
-- * AIRBASE.Syria.Taftanaz
|
||||||
|
-- * AIRBASE.Syria.H4
|
||||||
|
-- * AIRBASE.Syria.King_Hussein_Air_College
|
||||||
-- * AIRBASE.Syria.Rene_Mouawad
|
-- * AIRBASE.Syria.Rene_Mouawad
|
||||||
|
-- * AIRBASE.Syria.Jirah
|
||||||
-- * AIRBASE.Syria.Ramat_David
|
-- * AIRBASE.Syria.Ramat_David
|
||||||
|
-- * AIRBASE.Syria.Qabr_as_Sitt
|
||||||
-- * AIRBASE.Syria.Minakh
|
-- * AIRBASE.Syria.Minakh
|
||||||
-- * AIRBASE.Syria.Adana_Sakirpasa
|
-- * AIRBASE.Syria.Adana_Sakirpasa
|
||||||
-- * AIRBASE.Syria.Marj_as_Sultan_South
|
|
||||||
-- * AIRBASE.Syria.Hama
|
|
||||||
-- * AIRBASE.Syria.Al_Qusayr
|
|
||||||
-- * AIRBASE.Syria.Palmyra
|
-- * AIRBASE.Syria.Palmyra
|
||||||
|
-- * AIRBASE.Syria.Hama
|
||||||
|
-- * AIRBASE.Syria.Ercan
|
||||||
|
-- * AIRBASE.Syria.Marj_as_Sultan_South
|
||||||
-- * AIRBASE.Syria.Tabqa
|
-- * AIRBASE.Syria.Tabqa
|
||||||
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
||||||
-- * AIRBASE.Syria.An_Nasiriyah
|
-- * AIRBASE.Syria.An_Nasiriyah
|
||||||
-- * AIRBASE.Syria.Abu_al_Duhur
|
-- * AIRBASE.Syria.Abu_al_Duhur
|
||||||
-- * AIRBASE.Syria.H4
|
|
||||||
-- * AIRBASE.Syria.Gaziantep
|
|
||||||
-- * AIRBASE.Syria.Rosh_Pina
|
|
||||||
-- * AIRBASE.Syria.Sayqal
|
|
||||||
-- * AIRBASE.Syria.Shayrat
|
|
||||||
-- * AIRBASE.Syria.Tiyas
|
|
||||||
-- * AIRBASE.Syria.Tha_lah
|
|
||||||
-- * AIRBASE.Syria.Naqoura
|
|
||||||
--
|
--
|
||||||
--@field Syria
|
--@field Syria
|
||||||
AIRBASE.Syria={
|
AIRBASE.Syria={
|
||||||
@@ -380,39 +395,71 @@ AIRBASE.Syria={
|
|||||||
["Incirlik"]="Incirlik",
|
["Incirlik"]="Incirlik",
|
||||||
["Damascus"]="Damascus",
|
["Damascus"]="Damascus",
|
||||||
["Bassel_Al_Assad"]="Bassel Al-Assad",
|
["Bassel_Al_Assad"]="Bassel Al-Assad",
|
||||||
|
["Rosh_Pina"]="Rosh Pina",
|
||||||
["Aleppo"]="Aleppo",
|
["Aleppo"]="Aleppo",
|
||||||
["Qabr_as_Sitt"]="Qabr as Sitt",
|
["Al_Qusayr"]="Al Qusayr",
|
||||||
["Wujah_Al_Hajar"]="Wujah Al Hajar",
|
["Wujah_Al_Hajar"]="Wujah Al Hajar",
|
||||||
["Al_Dumayr"]="Al-Dumayr",
|
["Al_Dumayr"]="Al-Dumayr",
|
||||||
|
["Gazipasa"]="Gazipasa",
|
||||||
|
["Ru_Convoy_4"]="Ru Convoy-4",
|
||||||
["Hatay"]="Hatay",
|
["Hatay"]="Hatay",
|
||||||
|
["Nicosia"]="Nicosia",
|
||||||
|
["Pinarbashi"]="Pinarbashi",
|
||||||
|
["Paphos"]="Paphos",
|
||||||
|
["Kingsfield"]="Kingsfield",
|
||||||
|
["Thalah"]="Tha'lah",
|
||||||
["Haifa"]="Haifa",
|
["Haifa"]="Haifa",
|
||||||
["Khalkhalah"]="Khalkhalah",
|
["Khalkhalah"]="Khalkhalah",
|
||||||
["Megiddo"]="Megiddo",
|
["Megiddo"]="Megiddo",
|
||||||
|
["Lakatamia"]="Lakatamia",
|
||||||
["Rayak"]="Rayak",
|
["Rayak"]="Rayak",
|
||||||
|
["Larnaca"]="Larnaca",
|
||||||
["Mezzeh"]="Mezzeh",
|
["Mezzeh"]="Mezzeh",
|
||||||
["King_Hussein_Air_College"]="King Hussein Air College",
|
["Gecitkale"]="Gecitkale",
|
||||||
["Jirah"]="Jirah",
|
["Akrotiri"]="Akrotiri",
|
||||||
|
["Naqoura"]="Naqoura",
|
||||||
|
["Gaziantep"]="Gaziantep",
|
||||||
|
["Sayqal"]="Sayqal",
|
||||||
|
["Tiyas"]="Tiyas",
|
||||||
|
["Shayrat"]="Shayrat",
|
||||||
["Taftanaz"]="Taftanaz",
|
["Taftanaz"]="Taftanaz",
|
||||||
|
["H4"]="H4",
|
||||||
|
["King_Hussein_Air_College"]="King Hussein Air College",
|
||||||
["Rene_Mouawad"]="Rene Mouawad",
|
["Rene_Mouawad"]="Rene Mouawad",
|
||||||
|
["Jirah"]="Jirah",
|
||||||
["Ramat_David"]="Ramat David",
|
["Ramat_David"]="Ramat David",
|
||||||
|
["Qabr_as_Sitt"]="Qabr as Sitt",
|
||||||
["Minakh"]="Minakh",
|
["Minakh"]="Minakh",
|
||||||
["Adana_Sakirpasa"]="Adana Sakirpasa",
|
["Adana_Sakirpasa"]="Adana Sakirpasa",
|
||||||
["Marj_as_Sultan_South"]="Marj as Sultan South",
|
|
||||||
["Hama"]="Hama",
|
|
||||||
["Al_Qusayr"]="Al Qusayr",
|
|
||||||
["Palmyra"]="Palmyra",
|
["Palmyra"]="Palmyra",
|
||||||
|
["Hama"]="Hama",
|
||||||
|
["Ercan"]="Ercan",
|
||||||
|
["Marj_as_Sultan_South"]="Marj as Sultan South",
|
||||||
["Tabqa"]="Tabqa",
|
["Tabqa"]="Tabqa",
|
||||||
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
||||||
["An_Nasiriyah"]="An Nasiriyah",
|
["An_Nasiriyah"]="An Nasiriyah",
|
||||||
["Abu_al_Duhur"]="Abu al-Duhur",
|
["Abu_al_Duhur"]="Abu al-Duhur",
|
||||||
["H4"]="H4",
|
}
|
||||||
["Gaziantep"]="Gaziantep",
|
|
||||||
["Rosh_Pina"]="Rosh Pina",
|
|
||||||
["Sayqal"]="Sayqal",
|
|
||||||
["Shayrat"]="Shayrat",
|
--- Airbases of the Mariana Islands map:
|
||||||
["Tiyas"]="Tiyas",
|
--
|
||||||
["Tha_lah"]="Tha'lah",
|
-- * AIRBASE.MarianaIslands.Rota_Intl
|
||||||
["Naqoura"]="Naqoura",
|
-- * AIRBASE.MarianaIslands.Andersen_AFB
|
||||||
|
-- * AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Saipan_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Tinian_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Olf_Orote
|
||||||
|
--
|
||||||
|
--@field MarianaIslands
|
||||||
|
AIRBASE.MarianaIslands={
|
||||||
|
["Rota_Intl"]="Rota Intl",
|
||||||
|
["Andersen_AFB"]="Andersen AFB",
|
||||||
|
["Antonio_B_Won_Pat_Intl"]="Antonio B. Won Pat Intl",
|
||||||
|
["Saipan_Intl"]="Saipan Intl",
|
||||||
|
["Tinian_Intl"]="Tinian Intl",
|
||||||
|
["Olf_Orote"]="Olf Orote",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1372,7 +1419,8 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
||||||
name==AIRBASE.PersianGulf.Dubai_Intl or
|
name==AIRBASE.PersianGulf.Dubai_Intl or
|
||||||
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
||||||
name==AIRBASE.PersianGulf.Kish_International_Airport then
|
name==AIRBASE.PersianGulf.Kish_International_Airport or
|
||||||
|
name==AIRBASE.MarianaIslands.Andersen_AFB then
|
||||||
|
|
||||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||||
exception=1
|
exception=1
|
||||||
|
|||||||
@@ -177,9 +177,6 @@
|
|||||||
-- ## 5.5) Air-2-Air missile attack range:
|
-- ## 5.5) Air-2-Air missile attack range:
|
||||||
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets .
|
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets .
|
||||||
--
|
--
|
||||||
-- ## 5.6) GROUND units attack range:
|
|
||||||
-- * @{#CONTROLLABLE.OptionEngageRange}(): Engage range limit in percent (a number between 0 and 100). Default 100. Defines the range at which a GROUND unit/group (e.g. a SAM site) is allowed to use its weapons automatically.
|
|
||||||
--
|
|
||||||
-- @field #CONTROLLABLE
|
-- @field #CONTROLLABLE
|
||||||
CONTROLLABLE = {
|
CONTROLLABLE = {
|
||||||
ClassName = "CONTROLLABLE",
|
ClassName = "CONTROLLABLE",
|
||||||
@@ -1482,6 +1479,7 @@ end
|
|||||||
--- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
|
--- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
|
||||||
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
||||||
-- If the task is assigned to the controllable lead unit will be a FAC.
|
-- If the task is assigned to the controllable lead unit will be a FAC.
|
||||||
|
-- It's important to note that depending on the type of unit that is being assigned the task (AIR or GROUND), you must choose the correct type of callsign enumerator. For airborne controllables use CALLSIGN.Aircraft and for ground based use CALLSIGN.JTAC enumerators.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param Wrapper.Group#GROUP AttackGroup Target GROUP object.
|
-- @param Wrapper.Group#GROUP AttackGroup Target GROUP object.
|
||||||
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
|
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
|
||||||
@@ -1489,7 +1487,7 @@ end
|
|||||||
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
||||||
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
|
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
|
||||||
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
|
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
|
||||||
-- @param #number CallsignName Callsign enumerator name of the FAC.
|
-- @param #number CallsignName Callsign enumerator name of the FAC. (CALLSIGN.Aircraft.{name} for airborne controllables, CALLSIGN.JTACS.{name} for ground units)
|
||||||
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
|
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber )
|
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber )
|
||||||
@@ -1853,7 +1851,26 @@ do -- Patrol methods
|
|||||||
|
|
||||||
-- Calculate the new Route.
|
-- Calculate the new Route.
|
||||||
local FromCoord = PatrolGroup:GetCoordinate()
|
local FromCoord = PatrolGroup:GetCoordinate()
|
||||||
local From = FromCoord:WaypointGround( 120 )
|
|
||||||
|
-- test for submarine
|
||||||
|
local depth = 0
|
||||||
|
local IsSub = false
|
||||||
|
if PatrolGroup:IsShip() then
|
||||||
|
local navalvec3 = FromCoord:GetVec3()
|
||||||
|
if navalvec3.y < 0 then
|
||||||
|
depth = navalvec3.y
|
||||||
|
IsSub = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local Waypoint = Waypoints[1]
|
||||||
|
local Speed = Waypoint.speed or (20 / 3.6)
|
||||||
|
local From = FromCoord:WaypointGround( Speed )
|
||||||
|
|
||||||
|
if IsSub then
|
||||||
|
From = FromCoord:WaypointNaval( Speed, Waypoint.alt )
|
||||||
|
end
|
||||||
|
|
||||||
table.insert( Waypoints, 1, From )
|
table.insert( Waypoints, 1, From )
|
||||||
|
|
||||||
@@ -1894,7 +1911,16 @@ do -- Patrol methods
|
|||||||
if ToWaypoint then
|
if ToWaypoint then
|
||||||
FromWaypoint = ToWaypoint
|
FromWaypoint = ToWaypoint
|
||||||
end
|
end
|
||||||
|
-- test for submarine
|
||||||
|
local depth = 0
|
||||||
|
local IsSub = false
|
||||||
|
if PatrolGroup:IsShip() then
|
||||||
|
local navalvec3 = FromCoord:GetVec3()
|
||||||
|
if navalvec3.y < 0 then
|
||||||
|
depth = navalvec3.y
|
||||||
|
IsSub = true
|
||||||
|
end
|
||||||
|
end
|
||||||
-- Loop until a waypoint has been found that is not the same as the current waypoint.
|
-- Loop until a waypoint has been found that is not the same as the current waypoint.
|
||||||
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly
|
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly
|
||||||
-- what it is supposed to do, which is making groups drive around.
|
-- what it is supposed to do, which is making groups drive around.
|
||||||
@@ -1909,9 +1935,13 @@ do -- Patrol methods
|
|||||||
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
|
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
|
||||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||||
local Route = {}
|
local Route = {}
|
||||||
|
if IsSub then
|
||||||
|
Route[#Route+1] = FromCoord:WaypointNaval( Speed, depth )
|
||||||
|
Route[#Route+1] = ToCoord:WaypointNaval( Speed, Waypoint.alt )
|
||||||
|
else
|
||||||
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
||||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||||
|
end
|
||||||
|
|
||||||
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint )
|
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint )
|
||||||
|
|
||||||
@@ -1956,15 +1986,30 @@ do -- Patrol methods
|
|||||||
-- Calculate the new Route.
|
-- Calculate the new Route.
|
||||||
local FromCoord = PatrolGroup:GetCoordinate()
|
local FromCoord = PatrolGroup:GetCoordinate()
|
||||||
|
|
||||||
|
-- test for submarine
|
||||||
|
local depth = 0
|
||||||
|
local IsSub = false
|
||||||
|
if PatrolGroup:IsShip() then
|
||||||
|
local navalvec3 = FromCoord:GetVec3()
|
||||||
|
if navalvec3.y < 0 then
|
||||||
|
depth = navalvec3.y
|
||||||
|
IsSub = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Select a random Zone and get the Coordinate of the new Zone.
|
-- Select a random Zone and get the Coordinate of the new Zone.
|
||||||
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
||||||
local ToCoord = RandomZone:GetRandomCoordinate( 10 )
|
local ToCoord = RandomZone:GetRandomCoordinate( 10 )
|
||||||
|
|
||||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||||
local Route = {}
|
local Route = {}
|
||||||
|
if IsSub then
|
||||||
|
Route[#Route+1] = FromCoord:WaypointNaval( Speed, depth )
|
||||||
|
Route[#Route+1] = ToCoord:WaypointNaval( Speed, depth )
|
||||||
|
else
|
||||||
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
||||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||||
|
end
|
||||||
|
|
||||||
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation, DelayMin, DelayMax )
|
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation, DelayMin, DelayMax )
|
||||||
|
|
||||||
@@ -3772,10 +3817,30 @@ function CONTROLLABLE:OptionDisperseOnAttack(Seconds)
|
|||||||
local Controller = self:_GetController()
|
local Controller = self:_GetController()
|
||||||
if Controller then
|
if Controller then
|
||||||
if self:IsGround() then
|
if self:IsGround() then
|
||||||
self:SetOption(AI.Option.GROUND.id.DISPERSE_ON_ATTACK, seconds)
|
self:SetOption(AI.Option.Ground.id.DISPERSE_ON_ATTACK, seconds)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
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
|
||||||
|
|||||||
@@ -2551,9 +2551,10 @@ do -- Players
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- GROUND - Switch on/off radar emissions
|
--- GROUND - Switch on/off radar emissions for the group.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #boolean switch
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #GROUP self
|
||||||
function GROUP:EnableEmission(switch)
|
function GROUP:EnableEmission(switch)
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
local switch = switch or false
|
local switch = switch or false
|
||||||
@@ -2566,6 +2567,31 @@ function GROUP:EnableEmission(switch)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch on/off invisible flag for the group.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #GROUP self
|
||||||
|
function GROUP:SetCommandInvisible(switch)
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
local switch = switch or false
|
||||||
|
local SetInvisible = {id = 'SetInvisible', params = {value = true}}
|
||||||
|
self:SetCommand(SetInvisible)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch on/off immortal flag for the group.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #GROUP self
|
||||||
|
function GROUP:SetCommandImmortal(switch)
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
local switch = switch or false
|
||||||
|
local SetInvisible = {id = 'SetImmortal', params = {value = true}}
|
||||||
|
self:SetCommand(SetInvisible)
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--do -- Smoke
|
--do -- Smoke
|
||||||
|
|||||||
@@ -580,6 +580,18 @@ function UNIT:GetAmmo()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Sets the Unit's Internal Cargo Mass, in kg
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #number mass to set cargo to
|
||||||
|
-- @return #UNIT self
|
||||||
|
function UNIT:SetUnitInternalCargo(mass)
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
if DCSUnit then
|
||||||
|
trigger.action.setUnitInternalCargo(DCSUnit:getName(), mass)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has.
|
--- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles.
|
-- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles.
|
||||||
@@ -761,40 +773,6 @@ function UNIT:GetFuel()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets the passed group or unit objects radar emitters on or off. Can be used on sam sites for example to shut down the radar without setting AI off or changing the alarm state.
|
|
||||||
-- @param #UNIT self
|
|
||||||
-- @param #boolean Switch If `true` or `nil`, emission is enabled. If `false`, emission is turned off.
|
|
||||||
-- @return #UNIT self
|
|
||||||
function UNIT:SetEmission(Switch)
|
|
||||||
|
|
||||||
if Switch==nil then
|
|
||||||
Switch=true
|
|
||||||
end
|
|
||||||
|
|
||||||
local DCSUnit = self:GetDCSObject()
|
|
||||||
|
|
||||||
if DCSUnit then
|
|
||||||
DCSUnit:enableEmission(Switch)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Sets the passed group or unit objects radar emitters ON. Can be used on sam sites for example to shut down the radar without setting AI off or changing the alarm state.
|
|
||||||
-- @param #UNIT self
|
|
||||||
-- @return #UNIT self
|
|
||||||
function UNIT:EnableEmission()
|
|
||||||
self:SetEmission(true)
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Sets the passed group or unit objects radar emitters OFF. Can be used on sam sites for example to shut down the radar without setting AI off or changing the alarm state.
|
|
||||||
-- @param #UNIT self
|
|
||||||
-- @return #UNIT self
|
|
||||||
function UNIT:DisableEmission()
|
|
||||||
self:SetEmission(false)
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Returns a list of one @{Wrapper.Unit}.
|
--- Returns a list of one @{Wrapper.Unit}.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
@@ -1429,9 +1407,10 @@ function UNIT:GetTemplateFuel()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- GROUND - Switch on/off radar emissions.
|
--- GROUND - Switch on/off radar emissions of a unit.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @param #boolean switch
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #UNIT self
|
||||||
function UNIT:EnableEmission(switch)
|
function UNIT:EnableEmission(switch)
|
||||||
self:F2( self.UnitName )
|
self:F2( self.UnitName )
|
||||||
|
|
||||||
@@ -1445,4 +1424,5 @@ function UNIT:EnableEmission(switch)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ Utilities/Routines.lua
|
|||||||
Utilities/Utils.lua
|
Utilities/Utils.lua
|
||||||
Utilities/Enums.lua
|
Utilities/Enums.lua
|
||||||
Utilities/Profiler.lua
|
Utilities/Profiler.lua
|
||||||
|
Utilities/Templates.lua
|
||||||
|
Utilities/STTS.lua
|
||||||
|
|
||||||
Core/Base.lua
|
Core/Base.lua
|
||||||
|
Core/Beacon.lua
|
||||||
Core/UserFlag.lua
|
Core/UserFlag.lua
|
||||||
Core/UserSound.lua
|
|
||||||
Core/Report.lua
|
Core/Report.lua
|
||||||
Core/Scheduler.lua
|
Core/Scheduler.lua
|
||||||
Core/ScheduleDispatcher.lua
|
Core/ScheduleDispatcher.lua
|
||||||
@@ -21,9 +23,6 @@ Core/Point.lua
|
|||||||
Core/Velocity.lua
|
Core/Velocity.lua
|
||||||
Core/Message.lua
|
Core/Message.lua
|
||||||
Core/Fsm.lua
|
Core/Fsm.lua
|
||||||
Core/Radio.lua
|
|
||||||
Core/RadioQueue.lua
|
|
||||||
Core/RadioSpeech.lua
|
|
||||||
Core/Spawn.lua
|
Core/Spawn.lua
|
||||||
Core/SpawnStatic.lua
|
Core/SpawnStatic.lua
|
||||||
Core/Timer.lua
|
Core/Timer.lua
|
||||||
@@ -75,6 +74,8 @@ Ops/Airboss.lua
|
|||||||
Ops/RecoveryTanker.lua
|
Ops/RecoveryTanker.lua
|
||||||
Ops/RescueHelo.lua
|
Ops/RescueHelo.lua
|
||||||
Ops/ATIS.lua
|
Ops/ATIS.lua
|
||||||
|
Ops/CTLD.lua
|
||||||
|
Ops/CSAR.lua
|
||||||
|
|
||||||
AI/AI_Balancer.lua
|
AI/AI_Balancer.lua
|
||||||
AI/AI_Air.lua
|
AI/AI_Air.lua
|
||||||
@@ -113,6 +114,13 @@ Actions/Act_Route.lua
|
|||||||
Actions/Act_Account.lua
|
Actions/Act_Account.lua
|
||||||
Actions/Act_Assist.lua
|
Actions/Act_Assist.lua
|
||||||
|
|
||||||
|
Sound/UserSound.lua
|
||||||
|
Sound/SoundOutput.lua
|
||||||
|
Sound/Radio.lua
|
||||||
|
Sound/RadioQueue.lua
|
||||||
|
Sound/RadioSpeech.lua
|
||||||
|
Sound/SRS.lua
|
||||||
|
|
||||||
Tasking/CommandCenter.lua
|
Tasking/CommandCenter.lua
|
||||||
Tasking/Mission.lua
|
Tasking/Mission.lua
|
||||||
Tasking/Task.lua
|
Tasking/Task.lua
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ This repository contains the source lua code of the MOOSE framework.
|
|||||||
|
|
||||||
### [MOOSE_INCLUDE](https://github.com/FlightControl-Master/MOOSE_INCLUDE) - For use and generated
|
### [MOOSE_INCLUDE](https://github.com/FlightControl-Master/MOOSE_INCLUDE) - For use and generated
|
||||||
|
|
||||||
This repository contains the Moose.lua file to be included within your missions.
|
This repository contains the Moose.lua file to be included within your missions. Note that the Moose\_.lua is technically the same as Moose.lua, but without any commentary or unnecessary whitespace in it. You only need to load **one** of those at the beginning of your mission.
|
||||||
|
|
||||||
|
|
||||||
### [MOOSE_DOCS](https://github.com/FlightControl-Master/MOOSE_DOCS) - Not for use
|
### [MOOSE_DOCS](https://github.com/FlightControl-Master/MOOSE_DOCS) - Not for use
|
||||||
|
|||||||
Reference in New Issue
Block a user