mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Improvements and Fixes from FF/Develop
This commit is contained in:
parent
921ec8732c
commit
9795d5655f
@ -36,6 +36,7 @@
|
|||||||
-- @field #boolean ReportTargets If true, nearby targets are reported.
|
-- @field #boolean ReportTargets If true, nearby targets are reported.
|
||||||
-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
|
-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
|
||||||
-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
|
-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
|
||||||
|
-- @field #number dtFollow Time step between position updates.
|
||||||
|
|
||||||
|
|
||||||
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
||||||
@ -106,6 +107,7 @@ AI_FORMATION = {
|
|||||||
FollowScheduler = nil,
|
FollowScheduler = nil,
|
||||||
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
|
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
|
||||||
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
|
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
|
||||||
|
dtFollow = 0.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- AI_FORMATION.Mode class
|
--- AI_FORMATION.Mode class
|
||||||
@ -125,6 +127,7 @@ AI_FORMATION = {
|
|||||||
-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet.
|
-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet.
|
||||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||||
-- @param #string FollowName Name of the escort.
|
-- @param #string FollowName Name of the escort.
|
||||||
|
-- @param #string FollowBriefing Briefing.
|
||||||
-- @return #AI_FORMATION self
|
-- @return #AI_FORMATION self
|
||||||
function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1
|
function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1
|
||||||
local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
|
local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
|
||||||
@ -139,7 +142,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
|||||||
|
|
||||||
self:AddTransition( "*", "Stop", "Stopped" )
|
self:AddTransition( "*", "Stop", "Stopped" )
|
||||||
|
|
||||||
self:AddTransition( "None", "Start", "Following" )
|
self:AddTransition( {"None", "Stopped"}, "Start", "Following" )
|
||||||
|
|
||||||
self:AddTransition( "*", "FormationLine", "*" )
|
self:AddTransition( "*", "FormationLine", "*" )
|
||||||
--- FormationLine Handler OnBefore for AI_FORMATION
|
--- FormationLine Handler OnBefore for AI_FORMATION
|
||||||
@ -620,6 +623,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set time interval between updates of the formation.
|
||||||
|
-- @param #AI_FORMATION self
|
||||||
|
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
|
||||||
|
-- @return #AI_FORMATION
|
||||||
|
function AI_FORMATION:SetFollowTimeInterval(dt) --R2.1
|
||||||
|
self.dtFollow=dt or 0.5
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
|
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
|
||||||
-- This allows to visualize where the escort is flying to.
|
-- This allows to visualize where the escort is flying to.
|
||||||
-- @param #AI_FORMATION self
|
-- @param #AI_FORMATION self
|
||||||
@ -893,7 +906,30 @@ function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param Follow#AI_FORMATION self
|
--- Stop function. Formation will not be updated any more.
|
||||||
|
-- @param #AI_FORMATION self
|
||||||
|
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @pram #string To The to state.
|
||||||
|
function AI_FORMATION:onafterStop(FollowGroupSet, From, Event, To) --R2.1
|
||||||
|
self:E("Stopping formation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
|
||||||
|
-- @param #AI_FORMATION self
|
||||||
|
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @pram #string To The to state.
|
||||||
|
function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
|
||||||
|
if From=="Stopped" then
|
||||||
|
return false -- Deny transition.
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param #AI_FORMATION self
|
||||||
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||||
self:F( )
|
self:F( )
|
||||||
|
|
||||||
@ -1033,7 +1069,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
|||||||
self, ClientUnit, CT1, CV1, CT2, CV2
|
self, ClientUnit, CT1, CV1, CT2, CV2
|
||||||
)
|
)
|
||||||
|
|
||||||
self:__Follow( -0.5 )
|
self:__Follow( -self.dtFollow )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -342,18 +342,18 @@ do -- COORDINATE
|
|||||||
return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z
|
return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns if the 2 coordinates are at the same 2D position.
|
--- Scan/find objects (units, statics, scenery) within a certain radius around the coordinate using the world.searchObjects() DCS API function.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||||
-- @param #boolean scanunits (Optional) If true scan for units. Default true.
|
-- @param #boolean scanunits (Optional) If true scan for units. Default true.
|
||||||
-- @param #boolean scanstatics (Optional) If true scan for static objects. Default true.
|
-- @param #boolean scanstatics (Optional) If true scan for static objects. Default true.
|
||||||
-- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false.
|
-- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false.
|
||||||
-- @return True if units were found.
|
-- @return #boolean True if units were found.
|
||||||
-- @return True if statics were found.
|
-- @return #boolean True if statics were found.
|
||||||
-- @return True if scenery objects were found.
|
-- @return #boolean True if scenery objects were found.
|
||||||
-- @return Unit objects found.
|
-- @return #table Table of MOOSE @[#Wrapper.Unit#UNIT} objects found.
|
||||||
-- @return Static objects found.
|
-- @return #table Table of DCS static objects found.
|
||||||
-- @return Scenery objects found.
|
-- @return #table Table of DCS scenery objects found.
|
||||||
function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
|
function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
|
||||||
self:F(string.format("Scanning in radius %.1f m.", radius))
|
self:F(string.format("Scanning in radius %.1f m.", radius))
|
||||||
|
|
||||||
@ -405,18 +405,17 @@ do -- COORDINATE
|
|||||||
local ObjectCategory = ZoneObject:getCategory()
|
local ObjectCategory = ZoneObject:getCategory()
|
||||||
|
|
||||||
-- Check for unit or static objects
|
-- Check for unit or static objects
|
||||||
--if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then
|
if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist() then
|
||||||
if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then
|
|
||||||
|
|
||||||
table.insert(Units, UNIT:Find(ZoneObject))
|
table.insert(Units, UNIT:Find(ZoneObject))
|
||||||
gotunits=true
|
gotunits=true
|
||||||
|
|
||||||
elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist() then
|
||||||
|
|
||||||
table.insert(Statics, ZoneObject)
|
table.insert(Statics, ZoneObject)
|
||||||
gotstatics=true
|
gotstatics=true
|
||||||
|
|
||||||
elseif ObjectCategory == Object.Category.SCENERY then
|
elseif ObjectCategory==Object.Category.SCENERY then
|
||||||
|
|
||||||
table.insert(Scenery, ZoneObject)
|
table.insert(Scenery, ZoneObject)
|
||||||
gotscenery=true
|
gotscenery=true
|
||||||
@ -460,12 +459,12 @@ do -- COORDINATE
|
|||||||
--- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
|
--- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#Distance Distance The Distance to be added in meters.
|
-- @param DCS#Distance Distance The Distance to be added in meters.
|
||||||
-- @param DCS#Angle Angle The Angle in degrees.
|
-- @param DCS#Angle Angle The Angle in degrees. Defaults to 0 if not specified (nil).
|
||||||
-- @return #COORDINATE The new calculated COORDINATE.
|
-- @return Core.Point#COORDINATE The new calculated COORDINATE.
|
||||||
function COORDINATE:Translate( Distance, Angle )
|
function COORDINATE:Translate( Distance, Angle )
|
||||||
local SX = self.x
|
local SX = self.x
|
||||||
local SY = self.z
|
local SY = self.z
|
||||||
local Radians = Angle / 180 * math.pi
|
local Radians = (Angle or 0) / 180 * math.pi
|
||||||
local TX = Distance * math.cos( Radians ) + SX
|
local TX = Distance * math.cos( Radians ) + SX
|
||||||
local TY = Distance * math.sin( Radians ) + SY
|
local TY = Distance * math.sin( Radians ) + SY
|
||||||
|
|
||||||
@ -1121,6 +1120,9 @@ do -- COORDINATE
|
|||||||
--- Build a Waypoint Air "Landing".
|
--- Build a Waypoint Air "Landing".
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#Speed Speed Airspeed in km/h.
|
-- @param DCS#Speed Speed Airspeed in km/h.
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
||||||
|
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
||||||
|
-- @param #string description A text description of the waypoint, which will be shown on the F10 map.
|
||||||
-- @return #table The route point.
|
-- @return #table The route point.
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
@ -1129,8 +1131,8 @@ do -- COORDINATE
|
|||||||
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
|
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
|
||||||
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
|
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
|
||||||
--
|
--
|
||||||
function COORDINATE:WaypointAirLanding( Speed )
|
function COORDINATE:WaypointAirLanding( Speed, airbase, DCSTasks, description )
|
||||||
return self:WaypointAir( nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed )
|
return self:WaypointAir(nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed, nil, airbase, DCSTasks, description)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -9,12 +9,12 @@
|
|||||||
--
|
--
|
||||||
-- The Radio contains 2 classes : RADIO and BEACON
|
-- The Radio contains 2 classes : RADIO and BEACON
|
||||||
--
|
--
|
||||||
-- What are radio communications in DCS ?
|
-- What are radio communications in DCS?
|
||||||
--
|
--
|
||||||
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
||||||
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
||||||
--
|
--
|
||||||
-- How to supply DCS my own Sound Files ?
|
-- How to supply DCS my own Sound Files?
|
||||||
--
|
--
|
||||||
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
||||||
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
||||||
@ -33,7 +33,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: Hugues "Grey_Echo" Bousquet
|
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
||||||
--
|
--
|
||||||
-- @module Core.Radio
|
-- @module Core.Radio
|
||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
@ -66,24 +66,25 @@
|
|||||||
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
|
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
|
||||||
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
|
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||||
--
|
--
|
||||||
-- What is this power thing ?
|
-- What is this power thing?
|
||||||
--
|
--
|
||||||
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
|
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
|
||||||
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
|
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
|
||||||
-- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
|
-- * If the player gets **too far** from the transmitter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
|
||||||
-- * This an automated DCS calculation you have no say on,
|
-- * This an automated DCS calculation you have no say on,
|
||||||
-- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W,
|
-- * For reference, a standard VOR station has a 100 W antenna, a standard AA TACAN has a 120 W antenna, and civilian ATC's antenna usually range between 300 and 500 W,
|
||||||
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
|
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
|
||||||
--
|
--
|
||||||
-- @type RADIO
|
-- @type RADIO
|
||||||
-- @field Positionable#POSITIONABLE Positionable The transmiter
|
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will transmit the radio calls.
|
||||||
-- @field #string FileName Name of the sound file
|
-- @field #string FileName Name of the sound file played.
|
||||||
-- @field #number Frequency Frequency of the transmission in Hz
|
-- @field #number Frequency Frequency of the transmission in Hz.
|
||||||
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)
|
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM).
|
||||||
-- @field #string Subtitle Subtitle of the transmission
|
-- @field #string Subtitle Subtitle of the transmission.
|
||||||
-- @field #number SubtitleDuration Duration of the Subtitle in seconds
|
-- @field #number SubtitleDuration Duration of the Subtitle in seconds.
|
||||||
-- @field #number Power Power of the antenna is Watts
|
-- @field #number Power Power of the antenna is Watts.
|
||||||
-- @field #boolean Loop (default true)
|
-- @field #boolean Loop Transmission is repeated (default true).
|
||||||
|
-- @field #string alias Name of the radio transmitter.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
RADIO = {
|
RADIO = {
|
||||||
ClassName = "RADIO",
|
ClassName = "RADIO",
|
||||||
@ -93,19 +94,19 @@ RADIO = {
|
|||||||
Subtitle = "",
|
Subtitle = "",
|
||||||
SubtitleDuration = 0,
|
SubtitleDuration = 0,
|
||||||
Power = 100,
|
Power = 100,
|
||||||
Loop = true,
|
Loop = false,
|
||||||
|
alias=nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast
|
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast.
|
||||||
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead
|
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||||
-- @return #RADIO Radio
|
-- @return #RADIO The RADIO object or #nil if Positionable is invalid.
|
||||||
-- @return #nil If Positionable is invalid
|
|
||||||
function RADIO:New(Positionable)
|
function RADIO:New(Positionable)
|
||||||
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
|
|
||||||
|
|
||||||
self.Loop = true -- default Loop to true (not sure the above RADIO definition actually is working)
|
-- Inherit base
|
||||||
|
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
|
||||||
self:F(Positionable)
|
self:F(Positionable)
|
||||||
|
|
||||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||||
@ -113,11 +114,27 @@ function RADIO:New(Positionable)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E({"The passed positionable is invalid, no RADIO created", Positionable})
|
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check validity of the filename passed and sets RADIO.FileName
|
--- Set alias of the transmitter.
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #string alias Name of the radio transmitter.
|
||||||
|
-- @return #RADIO self
|
||||||
|
function RADIO:SetAlias(alias)
|
||||||
|
self.alias=tostring(alias)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get alias of the transmitter.
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @return #string Name of the transmitter.
|
||||||
|
function RADIO:GetAlias()
|
||||||
|
return tostring(self.alias)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the file name for the radio transmission.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #string FileName File name of the sound file (i.e. "Noise.ogg")
|
-- @param #string FileName File name of the sound file (i.e. "Noise.ogg")
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
@ -125,49 +142,63 @@ function RADIO:SetFileName(FileName)
|
|||||||
self:F2(FileName)
|
self:F2(FileName)
|
||||||
|
|
||||||
if type(FileName) == "string" then
|
if type(FileName) == "string" then
|
||||||
|
|
||||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||||
if not FileName:find("l10n/DEFAULT/") then
|
if not FileName:find("l10n/DEFAULT/") then
|
||||||
FileName = "l10n/DEFAULT/" .. FileName
|
FileName = "l10n/DEFAULT/" .. FileName
|
||||||
end
|
end
|
||||||
|
|
||||||
self.FileName = FileName
|
self.FileName = FileName
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName})
|
self:E({"File name invalid. Maybe something wrong with the extension?", FileName})
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check validity of the frequency passed and sets RADIO.Frequency
|
--- Set the frequency for the radio transmission.
|
||||||
|
-- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz)
|
-- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:SetFrequency(Frequency)
|
function RADIO:SetFrequency(Frequency)
|
||||||
self:F2(Frequency)
|
self:F2(Frequency)
|
||||||
|
|
||||||
if type(Frequency) == "number" then
|
if type(Frequency) == "number" then
|
||||||
|
|
||||||
-- If frequency is in range
|
-- If frequency is in range
|
||||||
if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then
|
if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then
|
||||||
self.Frequency = Frequency * 1000000 -- Conversion in Hz
|
|
||||||
|
-- Convert frequency from MHz to Hz
|
||||||
|
self.Frequency = Frequency * 1000000
|
||||||
|
|
||||||
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
|
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
|
||||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||||
self.Positionable:SetCommand({
|
|
||||||
|
local commandSetFrequency={
|
||||||
id = "SetFrequency",
|
id = "SetFrequency",
|
||||||
params = {
|
params = {
|
||||||
frequency = self.Frequency,
|
frequency = self.Frequency,
|
||||||
modulation = self.Modulation,
|
modulation = self.Modulation,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
self:T2(commandSetFrequency)
|
||||||
|
self.Positionable:SetCommand(commandSetFrequency)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency})
|
|
||||||
|
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency})
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check validity of the frequency passed and sets RADIO.Modulation
|
--- Set AM or FM modulation of the radio transmitter.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
-- @param #number Modulation Modulation is either radio.modulation.AM or radio.modulation.FM.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:SetModulation(Modulation)
|
function RADIO:SetModulation(Modulation)
|
||||||
self:F2(Modulation)
|
self:F2(Modulation)
|
||||||
@ -183,23 +214,24 @@ end
|
|||||||
|
|
||||||
--- Check validity of the power passed and sets RADIO.Power
|
--- Check validity of the power passed and sets RADIO.Power
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #number Power in W
|
-- @param #number Power Power in W.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:SetPower(Power)
|
function RADIO:SetPower(Power)
|
||||||
self:F2(Power)
|
self:F2(Power)
|
||||||
|
|
||||||
if type(Power) == "number" then
|
if type(Power) == "number" then
|
||||||
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
||||||
return self
|
else
|
||||||
end
|
|
||||||
self:E({"Power is invalid. Power unchanged.", self.Power})
|
self:E({"Power is invalid. Power unchanged.", self.Power})
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check validity of the loop passed and sets RADIO.Loop
|
--- Set message looping on or off.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #boolean Loop
|
-- @param #boolean Loop If true, message is repeated indefinitely.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
-- @usage
|
|
||||||
function RADIO:SetLoop(Loop)
|
function RADIO:SetLoop(Loop)
|
||||||
self:F2(Loop)
|
self:F2(Loop)
|
||||||
if type(Loop) == "boolean" then
|
if type(Loop) == "boolean" then
|
||||||
@ -232,13 +264,12 @@ function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
|||||||
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
|
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
|
||||||
end
|
end
|
||||||
if type(SubtitleDuration) == "number" then
|
if type(SubtitleDuration) == "number" then
|
||||||
if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then
|
|
||||||
self.SubtitleDuration = SubtitleDuration
|
self.SubtitleDuration = SubtitleDuration
|
||||||
return self
|
else
|
||||||
end
|
|
||||||
end
|
|
||||||
self.SubtitleDuration = 0
|
self.SubtitleDuration = 0
|
||||||
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
||||||
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
||||||
@ -246,10 +277,10 @@ end
|
|||||||
-- but it will work with a UNIT or a GROUP anyway.
|
-- but it will work with a UNIT or a GROUP anyway.
|
||||||
-- Only the #RADIO and the Filename are mandatory
|
-- Only the #RADIO and the Filename are mandatory
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #string FileName
|
-- @param #string FileName Name of the sound file that will be transmitted.
|
||||||
-- @param #number Frequency in MHz
|
-- @param #number Frequency Frequency in MHz.
|
||||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
-- @param #number Modulation Modulation of frequency, which is either radio.modulation.AM or radio.modulation.FM.
|
||||||
-- @param #number Power in W
|
-- @param #number Power Power in W.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
|
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
|
||||||
self:F({FileName, Frequency, Modulation, Power})
|
self:F({FileName, Frequency, Modulation, Power})
|
||||||
@ -269,31 +300,43 @@ end
|
|||||||
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
||||||
-- Only the RADIO and the Filename are mandatory.
|
-- Only the RADIO and the Filename are mandatory.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #string FileName
|
-- @param #string FileName Name of sound file.
|
||||||
-- @param #string Subtitle
|
-- @param #string Subtitle Subtitle to be displayed with sound file.
|
||||||
-- @param #number SubtitleDuration in s
|
-- @param #number SubtitleDuration Duration of subtitle display in seconds.
|
||||||
-- @param #number Frequency in MHz
|
-- @param #number Frequency Frequency in MHz.
|
||||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
-- @param #number Modulation Modulation which can be either radio.modulation.AM or radio.modulation.FM
|
||||||
-- @param #boolean Loop
|
-- @param #boolean Loop If true, loop message.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop)
|
function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop)
|
||||||
self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop})
|
self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop})
|
||||||
|
|
||||||
|
-- Set file name.
|
||||||
self:SetFileName(FileName)
|
self:SetFileName(FileName)
|
||||||
local Duration = 5
|
|
||||||
if SubtitleDuration then Duration = SubtitleDuration end
|
-- Set modulation AM/FM.
|
||||||
-- SubtitleDuration argument was missing, adding it
|
if Modulation then
|
||||||
if Subtitle then self:SetSubtitle(Subtitle, Duration) end
|
self:SetModulation(Modulation)
|
||||||
-- self:SetSubtitleDuration is non existent, removing faulty line
|
end
|
||||||
-- if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end
|
|
||||||
if Frequency then self:SetFrequency(Frequency) end
|
-- Set frequency.
|
||||||
if Modulation then self:SetModulation(Modulation) end
|
if Frequency then
|
||||||
if Loop then self:SetLoop(Loop) end
|
self:SetFrequency(Frequency)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set subtitle.
|
||||||
|
if Subtitle then
|
||||||
|
self:SetSubtitle(Subtitle, SubtitleDuration or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set Looping.
|
||||||
|
if Loop then
|
||||||
|
self:SetLoop(Loop)
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Actually Broadcast the transmission
|
--- Broadcast the transmission.
|
||||||
-- * The Radio has to be populated with the new transmission before broadcasting.
|
-- * The Radio has to be populated with the new transmission before broadcasting.
|
||||||
-- * Please use RADIO setters or either @{#RADIO.NewGenericTransmission} or @{#RADIO.NewUnitTransmission}
|
-- * Please use RADIO setters or either @{#RADIO.NewGenericTransmission} or @{#RADIO.NewUnitTransmission}
|
||||||
-- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE
|
-- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE
|
||||||
@ -302,31 +345,38 @@ end
|
|||||||
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
|
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
|
||||||
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
|
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
|
-- @param #boolean viatrigger Use trigger.action.radioTransmission() in any case, i.e. also for UNITS and GROUPS.
|
||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:Broadcast()
|
function RADIO:Broadcast(viatrigger)
|
||||||
self:F()
|
self:F({viatrigger=viatrigger})
|
||||||
|
|
||||||
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system
|
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system.
|
||||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
if (self.Positionable.ClassName=="UNIT" or self.Positionable.ClassName=="GROUP") and (not viatrigger) then
|
||||||
self:T2("Broadcasting from a UNIT or a GROUP")
|
self:T("Broadcasting from a UNIT or a GROUP")
|
||||||
self.Positionable:SetCommand({
|
|
||||||
|
local commandTransmitMessage={
|
||||||
id = "TransmitMessage",
|
id = "TransmitMessage",
|
||||||
params = {
|
params = {
|
||||||
file = self.FileName,
|
file = self.FileName,
|
||||||
duration = self.SubtitleDuration,
|
duration = self.SubtitleDuration,
|
||||||
subtitle = self.Subtitle,
|
subtitle = self.Subtitle,
|
||||||
loop = self.Loop,
|
loop = self.Loop,
|
||||||
}
|
}}
|
||||||
})
|
|
||||||
|
self:T3(commandTransmitMessage)
|
||||||
|
self.Positionable:SetCommand(commandTransmitMessage)
|
||||||
else
|
else
|
||||||
-- If the POSITIONABLE is anything else, we revert to the general singleton function
|
-- If the POSITIONABLE is anything else, we revert to the general singleton function
|
||||||
-- I need to give it a unique name, so that the transmission can be stopped later. I use the class ID
|
-- I need to give it a unique name, so that the transmission can be stopped later. I use the class ID
|
||||||
self:T2("Broadcasting from a POSITIONABLE")
|
self:T("Broadcasting from a POSITIONABLE")
|
||||||
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
|
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Stops a transmission
|
--- Stops a transmission
|
||||||
-- This function is especially usefull to stop the broadcast of looped transmissions
|
-- This function is especially usefull to stop the broadcast of looped transmissions
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
@ -335,10 +385,10 @@ function RADIO:StopBroadcast()
|
|||||||
self:F()
|
self:F()
|
||||||
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
||||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||||
self.Positionable:SetCommand({
|
|
||||||
id = "StopTransmission",
|
local commandStopTransmission={id="StopTransmission", params={}}
|
||||||
params = {}
|
|
||||||
})
|
self.Positionable:SetCommand(commandStopTransmission)
|
||||||
else
|
else
|
||||||
-- Else, we use the appropriate singleton funciton
|
-- Else, we use the appropriate singleton funciton
|
||||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||||
@ -364,22 +414,86 @@ end
|
|||||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
||||||
--
|
--
|
||||||
-- @type BEACON
|
-- @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
|
-- @extends Core.Base#BASE
|
||||||
BEACON = {
|
BEACON = {
|
||||||
ClassName = "BEACON",
|
ClassName = "BEACON",
|
||||||
|
Positionable = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.AATACAN} or @{#BEACON.Generic}
|
--- Beacon types supported by DCS.
|
||||||
|
-- @type BEACON.Type
|
||||||
|
-- @field #number NULL
|
||||||
|
-- @field #number VOR
|
||||||
|
-- @field #number DME
|
||||||
|
-- @field #number VOR_DME
|
||||||
|
-- @field #number TACAN
|
||||||
|
-- @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 NAUTICAL_HOMER
|
||||||
|
-- @field #number ICLS
|
||||||
|
BEACON.Type={
|
||||||
|
NULL = 0,
|
||||||
|
VOR = 1,
|
||||||
|
DME = 2,
|
||||||
|
VOR_DME = 3,
|
||||||
|
TACAN = 4,
|
||||||
|
VORTAC = 5,
|
||||||
|
RSBN = 32,
|
||||||
|
BROADCAST_STATION = 1024,
|
||||||
|
HOMER = 8,
|
||||||
|
AIRPORT_HOMER = 4104,
|
||||||
|
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||||
|
ILS_FAR_HOMER = 16408,
|
||||||
|
ILS_NEAR_HOMER = 16456,
|
||||||
|
ILS_LOCALIZER = 16640,
|
||||||
|
ILS_GLIDESLOPE = 16896,
|
||||||
|
NAUTICAL_HOMER = 32776,
|
||||||
|
ICLS = 131584,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Beacon systems supported by DCS.
|
||||||
|
-- @type BEACON.System
|
||||||
|
-- @field #number PAR_10
|
||||||
|
-- @field #number RSBN_5
|
||||||
|
-- @field #number TACAN
|
||||||
|
-- @field #number TACAN_TANKER
|
||||||
|
-- @field #number ILS_LOCALIZER
|
||||||
|
-- @field #number ILS_GLIDESLOPE
|
||||||
|
-- @field #number BROADCAST_STATION
|
||||||
|
BEACON.System={
|
||||||
|
PAR_10 = 1,
|
||||||
|
RSBN_5 = 2,
|
||||||
|
TACAN = 3,
|
||||||
|
TACAN_TANKER = 4,
|
||||||
|
ILS_LOCALIZER = 5,
|
||||||
|
ILS_GLIDESLOPE = 6,
|
||||||
|
BROADCAST_STATION = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- 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.
|
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
|
||||||
-- @param #BEACON self
|
-- @param #BEACON self
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||||
-- @return #BEACON Beacon
|
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
|
||||||
-- @return #nil If Positionable is invalid
|
|
||||||
function BEACON:New(Positionable)
|
function BEACON:New(Positionable)
|
||||||
local self = BASE:Inherit(self, BASE:New())
|
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#BEACON
|
||||||
|
|
||||||
|
-- Debug.
|
||||||
self:F(Positionable)
|
self:F(Positionable)
|
||||||
|
|
||||||
|
-- Set positionable.
|
||||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||||
self.Positionable = Positionable
|
self.Positionable = Positionable
|
||||||
return self
|
return self
|
||||||
@ -390,44 +504,95 @@ function BEACON:New(Positionable)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
|
--- Activates a TACAN BEACON.
|
||||||
-- @param #BEACON self
|
-- @param #BEACON self
|
||||||
-- @param #number TACANChannel
|
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
|
||||||
-- @param #string TACANMode
|
-- @param #string Mode TACAN mode, i.e. the "Y" part in "10Y".
|
||||||
-- @return #number Frequecy
|
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||||
-- @return #nil if parameters are invalid
|
-- @param #boolean Bearing If true, beacon provides bearing information. If false (or nil), only distance information is available.
|
||||||
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||||
self:F3({TACANChannel, TACANMode})
|
-- @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:TACAN(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})
|
||||||
|
|
||||||
if type(TACANChannel) ~= "number" then
|
-- Get frequency.
|
||||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
||||||
return nil -- error in arguments
|
|
||||||
|
-- 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=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
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
-- Attached unit.
|
||||||
-- I have no idea what it does but it seems to work
|
local UnitID=self.Positionable:GetID()
|
||||||
local A = 1151 -- 'X', channel >= 64
|
|
||||||
local B = 64 -- channel >= 64
|
|
||||||
|
|
||||||
if TACANChannel < 64 then
|
-- Debug.
|
||||||
B = 1
|
self:T({"TACAN BEACON started!"})
|
||||||
|
|
||||||
|
-- Start beacon.
|
||||||
|
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
||||||
|
|
||||||
|
-- Stop sheduler.
|
||||||
|
if Duration then
|
||||||
|
self.Positionable:DeactivateBeacon(Duration)
|
||||||
end
|
end
|
||||||
|
|
||||||
if TACANMode == 'Y' then
|
return self
|
||||||
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
|
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.
|
--- Activates a TACAN BEACON on an Aircraft.
|
||||||
-- @param #BEACON self
|
-- @param #BEACON self
|
||||||
@ -480,7 +645,7 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
|||||||
})
|
})
|
||||||
|
|
||||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
SCHEDULER:New( nil,
|
SCHEDULER:New(nil,
|
||||||
function()
|
function()
|
||||||
self:StopAATACAN()
|
self:StopAATACAN()
|
||||||
end, {}, BeaconDuration)
|
end, {}, BeaconDuration)
|
||||||
@ -591,4 +756,44 @@ function BEACON:StopRadioBeacon()
|
|||||||
self:F()
|
self:F()
|
||||||
-- The unique name of the transmission is the class ID
|
-- The unique name of the transmission is the class ID
|
||||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||||
|
return self
|
||||||
end
|
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
|
||||||
|
|
||||||
|
|||||||
@ -409,9 +409,9 @@ do -- SET_BASE
|
|||||||
for ObjectID, ObjectData in pairs( self.Set ) do
|
for ObjectID, ObjectData in pairs( self.Set ) do
|
||||||
if NearestObject == nil then
|
if NearestObject == nil then
|
||||||
NearestObject = ObjectData
|
NearestObject = ObjectData
|
||||||
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetVec2() )
|
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||||
else
|
else
|
||||||
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetVec2() )
|
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||||
if Distance < ClosestDistance then
|
if Distance < ClosestDistance then
|
||||||
NearestObject = ObjectData
|
NearestObject = ObjectData
|
||||||
ClosestDistance = Distance
|
ClosestDistance = Distance
|
||||||
|
|||||||
@ -195,6 +195,49 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Creates a new @{Static} from a COORDINATE.
|
||||||
|
-- @param #SPAWNSTATIC self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
|
||||||
|
-- @param #number Heading (Optional) Heading The heading of the static, which is a number in degrees from 0 to 360. Default is 0 degrees.
|
||||||
|
-- @param #string NewName (Optional) The name of the new static.
|
||||||
|
-- @return #SPAWNSTATIC
|
||||||
|
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName) --R2.4
|
||||||
|
self:F( { PointVec2, Heading, NewName } )
|
||||||
|
|
||||||
|
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||||
|
|
||||||
|
if StaticTemplate then
|
||||||
|
|
||||||
|
Heading=Heading or 0
|
||||||
|
|
||||||
|
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||||
|
|
||||||
|
StaticUnitTemplate.x = Coordinate.x
|
||||||
|
StaticUnitTemplate.y = Coordinate.z
|
||||||
|
StaticUnitTemplate.alt = Coordinate.y
|
||||||
|
|
||||||
|
StaticTemplate.route = nil
|
||||||
|
StaticTemplate.groupId = nil
|
||||||
|
|
||||||
|
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||||
|
StaticUnitTemplate.name = StaticTemplate.name
|
||||||
|
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
|
||||||
|
|
||||||
|
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
|
||||||
|
|
||||||
|
self:F({StaticTemplate = StaticTemplate})
|
||||||
|
|
||||||
|
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||||
|
|
||||||
|
self.SpawnIndex = self.SpawnIndex + 1
|
||||||
|
|
||||||
|
return _DATABASE:FindStatic(Static:getName())
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Respawns the original @{Static}.
|
--- Respawns the original @{Static}.
|
||||||
-- @param #SPAWNSTATIC self
|
-- @param #SPAWNSTATIC self
|
||||||
-- @return #SPAWNSTATIC
|
-- @return #SPAWNSTATIC
|
||||||
|
|||||||
@ -70,7 +70,7 @@ do -- UserFlag
|
|||||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||||
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
|
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
|
||||||
--
|
--
|
||||||
function USERFLAG:Get( Number ) --R2.3
|
function USERFLAG:Get() --R2.3
|
||||||
|
|
||||||
return trigger.misc.getUserFlag( self.UserFlagName )
|
return trigger.misc.getUserFlag( self.UserFlagName )
|
||||||
end
|
end
|
||||||
|
|||||||
@ -118,15 +118,21 @@ do -- UserSound
|
|||||||
--- Play the usersound to the given @{Wrapper.Group}.
|
--- Play the usersound to the given @{Wrapper.Group}.
|
||||||
-- @param #USERSOUND self
|
-- @param #USERSOUND self
|
||||||
-- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} to play the usersound to.
|
-- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} to play the usersound to.
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
|
||||||
-- @return #USERSOUND The usersound instance.
|
-- @return #USERSOUND The usersound instance.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
|
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
|
||||||
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group.
|
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group.
|
||||||
--
|
--
|
||||||
function USERSOUND:ToGroup( Group ) --R2.3
|
function USERSOUND:ToGroup( Group, Delay ) --R2.3
|
||||||
|
|
||||||
|
Delay=Delay or 0
|
||||||
|
if Delay>0 then
|
||||||
|
SCHEDULER:New(nil, USERSOUND.ToGroup,{self, Group}, Delay)
|
||||||
|
else
|
||||||
trigger.action.outSoundForGroup( Group:GetID(), self.UserSoundFileName )
|
trigger.action.outSoundForGroup( Group:GetID(), self.UserSoundFileName )
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1398,16 +1398,15 @@ 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
|
||||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||||
|
-- @param #number Segments (Optional) Number of segments within boundary line. Default 10.
|
||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
function ZONE_POLYGON_BASE:SmokeZone( SmokeColor )
|
function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments )
|
||||||
self:F2( SmokeColor )
|
self:F2( SmokeColor )
|
||||||
|
|
||||||
local i
|
Segments=Segments or 10
|
||||||
local j
|
|
||||||
local Segments = 10
|
|
||||||
|
|
||||||
i = 1
|
local i=1
|
||||||
j = #self._.Polygon
|
local j=#self._.Polygon
|
||||||
|
|
||||||
while i <= #self._.Polygon do
|
while i <= #self._.Polygon do
|
||||||
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||||
@ -1428,6 +1427,38 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Flare the zone boundaries in a color.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
|
||||||
|
-- @param #number Segments (Optional) Number of segments within boundary line. Default 10.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments )
|
||||||
|
self:F2(FlareColor)
|
||||||
|
|
||||||
|
Segments=Segments or 10
|
||||||
|
|
||||||
|
local i=1
|
||||||
|
local j=#self._.Polygon
|
||||||
|
|
||||||
|
while i <= #self._.Polygon do
|
||||||
|
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||||
|
|
||||||
|
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 -- We divide each line in 5 segments and smoke a point on the line.
|
||||||
|
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||||
|
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||||
|
POINT_VEC2:New( PointX, PointY ):Flare(FlareColor)
|
||||||
|
end
|
||||||
|
j = i
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns if a location is within the zone.
|
--- Returns if a location is within the zone.
|
||||||
|
|||||||
@ -216,7 +216,7 @@
|
|||||||
-- One way to determin which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}().
|
-- One way to determin which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}().
|
||||||
-- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file.
|
-- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file.
|
||||||
--
|
--
|
||||||
-- ## Empoying Selected Weapons
|
-- ## Employing Selected Weapons
|
||||||
--
|
--
|
||||||
-- If an ARTY group carries multiple weapons, which can be used for artillery task, a certain weapon type can be selected to attack the target.
|
-- If an ARTY group carries multiple weapons, which can be used for artillery task, a certain weapon type can be selected to attack the target.
|
||||||
-- This is done via the *weapontype* parameter of the @{#ARTY.AssignTargetCoord}(..., *weapontype*, ...) function.
|
-- This is done via the *weapontype* parameter of the @{#ARTY.AssignTargetCoord}(..., *weapontype*, ...) function.
|
||||||
@ -674,11 +674,13 @@ ARTY.id="ARTY | "
|
|||||||
|
|
||||||
--- Arty script version.
|
--- Arty script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ARTY.version="1.0.6"
|
ARTY.version="1.0.7"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO list:
|
-- TODO list:
|
||||||
|
-- TODO: Add hit event and make the arty group relocate.
|
||||||
|
-- TODO: Handle rearming for ships. How?
|
||||||
-- DONE: Delete targets from queue user function.
|
-- DONE: Delete targets from queue user function.
|
||||||
-- DONE: Delete entire target queue user function.
|
-- DONE: Delete entire target queue user function.
|
||||||
-- DONE: Add weapon types. Done but needs improvements.
|
-- DONE: Add weapon types. Done but needs improvements.
|
||||||
@ -697,11 +699,9 @@ ARTY.version="1.0.6"
|
|||||||
-- DONE: Add command move to make arty group move.
|
-- DONE: Add command move to make arty group move.
|
||||||
-- DONE: remove schedulers for status event.
|
-- DONE: remove schedulers for status event.
|
||||||
-- DONE: Improve handling of special weapons. When winchester if using selected weapons?
|
-- DONE: Improve handling of special weapons. When winchester if using selected weapons?
|
||||||
-- TODO: Handle rearming for ships. How?
|
|
||||||
-- DONE: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location.
|
-- DONE: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location.
|
||||||
-- DONE: Add set commands via markers. E.g. set rearming place.
|
-- DONE: Add set commands via markers. E.g. set rearming place.
|
||||||
-- DONE: Test stationary types like mortas ==> rearming etc.
|
-- DONE: Test stationary types like mortas ==> rearming etc.
|
||||||
-- TODO: Add hit event and make the arty group relocate.
|
|
||||||
-- DONE: Add illumination and smoke.
|
-- DONE: Add illumination and smoke.
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -2878,7 +2878,7 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target)
|
|||||||
self.Controllable:ClearTasks()
|
self.Controllable:ClearTasks()
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(ARTY.id.."ERROR: No target in cease fire for group %s.", self.groupname)
|
self:E(ARTY.id..string.format("ERROR: No target in cease fire for group %s.", self.groupname))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set number of shots to zero.
|
-- Set number of shots to zero.
|
||||||
@ -4253,15 +4253,24 @@ end
|
|||||||
-- @param #ARTY self
|
-- @param #ARTY self
|
||||||
function ARTY:_CheckTargetsInRange()
|
function ARTY:_CheckTargetsInRange()
|
||||||
|
|
||||||
|
local targets2delete={}
|
||||||
|
|
||||||
for i=1,#self.targets do
|
for i=1,#self.targets do
|
||||||
local _target=self.targets[i]
|
local _target=self.targets[i]
|
||||||
|
|
||||||
self:T3(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
self:T3(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
||||||
|
|
||||||
-- Check if target is in range.
|
-- Check if target is in range.
|
||||||
local _inrange,_toofar,_tooclose=self:_TargetInRange(_target)
|
local _inrange,_toofar,_tooclose,_remove=self:_TargetInRange(_target)
|
||||||
self:T3(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose)))
|
self:T3(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose)))
|
||||||
|
|
||||||
|
if _remove then
|
||||||
|
|
||||||
|
-- The ARTY group is immobile and not cargo but the target is not in range!
|
||||||
|
table.insert(targets2delete, _target.name)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
-- Init default for assigning moves into range.
|
-- Init default for assigning moves into range.
|
||||||
local _movetowards=false
|
local _movetowards=false
|
||||||
local _moveaway=false
|
local _moveaway=false
|
||||||
@ -4346,8 +4355,14 @@ function ARTY:_CheckTargetsInRange()
|
|||||||
_target.inrange=_inrange
|
_target.inrange=_inrange
|
||||||
|
|
||||||
self:T3(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
self:T3(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove targets not in range.
|
||||||
|
for _,targetname in pairs(targets2delete) do
|
||||||
|
self:RemoveTarget(targetname)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times.
|
--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times.
|
||||||
@ -4728,6 +4743,7 @@ end
|
|||||||
-- @return #boolean True if target is in range, false otherwise.
|
-- @return #boolean True if target is in range, false otherwise.
|
||||||
-- @return #boolean True if ARTY group is too far away from the target, i.e. distance > max firing range.
|
-- @return #boolean True if ARTY group is too far away from the target, i.e. distance > max firing range.
|
||||||
-- @return #boolean True if ARTY group is too close to the target, i.e. distance < min finring range.
|
-- @return #boolean True if ARTY group is too close to the target, i.e. distance < min finring range.
|
||||||
|
-- @return #boolean True if target should be removed since ARTY group is immobile and not cargo.
|
||||||
function ARTY:_TargetInRange(target, message)
|
function ARTY:_TargetInRange(target, message)
|
||||||
self:F3(target)
|
self:F3(target)
|
||||||
|
|
||||||
@ -4763,11 +4779,13 @@ function ARTY:_TargetInRange(target, message)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range - unless they are cargo.
|
-- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range - unless they are cargo.
|
||||||
|
local _remove=false
|
||||||
if not (self.ismobile or self.iscargo) and _inrange==false then
|
if not (self.ismobile or self.iscargo) and _inrange==false then
|
||||||
self:RemoveTarget(target.name)
|
--self:RemoveTarget(target.name)
|
||||||
|
_remove=true
|
||||||
end
|
end
|
||||||
|
|
||||||
return _inrange,_toofar,_tooclose
|
return _inrange,_toofar,_tooclose,_remove
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the weapon type name, which should be used to attack the target.
|
--- Get the weapon type name, which should be used to attack the target.
|
||||||
|
|||||||
@ -1012,7 +1012,7 @@ do -- DETECTION_BASE
|
|||||||
--- Set the parameters to calculate to optimal intercept point.
|
--- Set the parameters to calculate to optimal intercept point.
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #boolean Intercept Intercept is true if an intercept point is calculated. Intercept is false if it is disabled. The default Intercept is false.
|
-- @param #boolean Intercept Intercept is true if an intercept point is calculated. Intercept is false if it is disabled. The default Intercept is false.
|
||||||
-- @param #number IntereptDelay If Intercept is true, then InterceptDelay is the average time it takes to get airplanes airborne.
|
-- @param #number InterceptDelay If Intercept is true, then InterceptDelay is the average time it takes to get airplanes airborne.
|
||||||
-- @return #DETECTION_BASE self
|
-- @return #DETECTION_BASE self
|
||||||
function DETECTION_BASE:SetIntercept( Intercept, InterceptDelay )
|
function DETECTION_BASE:SetIntercept( Intercept, InterceptDelay )
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|||||||
@ -5435,7 +5435,7 @@ function RAT:_ATCInit(airports_map)
|
|||||||
if not RAT.ATC.init then
|
if not RAT.ATC.init then
|
||||||
local text
|
local text
|
||||||
text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
|
text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
|
||||||
self:T(RAT.id..text)
|
BASE:T(RAT.id..text)
|
||||||
RAT.ATC.init=true
|
RAT.ATC.init=true
|
||||||
for _,ap in pairs(airports_map) do
|
for _,ap in pairs(airports_map) do
|
||||||
local name=ap:GetName()
|
local name=ap:GetName()
|
||||||
@ -5458,7 +5458,7 @@ end
|
|||||||
-- @param #string name Group name of the flight.
|
-- @param #string name Group name of the flight.
|
||||||
-- @param #string dest Name of the destination airport.
|
-- @param #string dest Name of the destination airport.
|
||||||
function RAT:_ATCAddFlight(name, dest)
|
function RAT:_ATCAddFlight(name, dest)
|
||||||
self:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
|
BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
|
||||||
RAT.ATC.flight[name]={}
|
RAT.ATC.flight[name]={}
|
||||||
RAT.ATC.flight[name].destination=dest
|
RAT.ATC.flight[name].destination=dest
|
||||||
RAT.ATC.flight[name].Tarrive=-1
|
RAT.ATC.flight[name].Tarrive=-1
|
||||||
@ -5483,7 +5483,7 @@ end
|
|||||||
-- @param #string name Group name of the flight.
|
-- @param #string name Group name of the flight.
|
||||||
-- @param #number time Time the fight first registered.
|
-- @param #number time Time the fight first registered.
|
||||||
function RAT:_ATCRegisterFlight(name, time)
|
function RAT:_ATCRegisterFlight(name, time)
|
||||||
self:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
|
BASE:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
|
||||||
RAT.ATC.flight[name].Tarrive=time
|
RAT.ATC.flight[name].Tarrive=time
|
||||||
RAT.ATC.flight[name].holding=0
|
RAT.ATC.flight[name].holding=0
|
||||||
end
|
end
|
||||||
@ -5514,7 +5514,7 @@ function RAT:_ATCStatus()
|
|||||||
|
|
||||||
-- Aircraft is holding.
|
-- Aircraft is holding.
|
||||||
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
|
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
|
||||||
self:T(RAT.id..text)
|
BASE:T(RAT.id..text)
|
||||||
|
|
||||||
elseif hold==RAT.ATC.onfinal then
|
elseif hold==RAT.ATC.onfinal then
|
||||||
|
|
||||||
@ -5522,7 +5522,7 @@ function RAT:_ATCStatus()
|
|||||||
local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
|
local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
|
||||||
|
|
||||||
local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60)
|
local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60)
|
||||||
self:T(RAT.id..text)
|
BASE:T(RAT.id..text)
|
||||||
|
|
||||||
elseif hold==RAT.ATC.unregistered then
|
elseif hold==RAT.ATC.unregistered then
|
||||||
|
|
||||||
@ -5530,7 +5530,7 @@ function RAT:_ATCStatus()
|
|||||||
--self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold))
|
--self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold))
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
|
BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -5572,12 +5572,12 @@ function RAT:_ATCCheck()
|
|||||||
|
|
||||||
-- Debug message.
|
-- Debug message.
|
||||||
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
||||||
self:T(RAT.id..text)
|
BASE:T(RAT.id..text)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
||||||
self:T(RAT.id..text)
|
BASE:T(RAT.id..text)
|
||||||
|
|
||||||
-- Clear flight for landing.
|
-- Clear flight for landing.
|
||||||
RAT:_ATCClearForLanding(name, flight)
|
RAT:_ATCClearForLanding(name, flight)
|
||||||
@ -5706,11 +5706,6 @@ function RAT:_ATCQueue()
|
|||||||
table.insert(RAT.ATC.airport[airport].queue, v[1])
|
table.insert(RAT.ATC.airport[airport].queue, v[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
--fvh
|
|
||||||
--for k,v in ipairs(RAT.ATC.airport[airport].queue) do
|
|
||||||
--print(string.format("queue #%02i flight \"%s\" holding %d seconds",k, v, RAT.ATC.flight[v].holding))
|
|
||||||
--end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -276,7 +276,7 @@ RANGE.id="RANGE | "
|
|||||||
|
|
||||||
--- Range script version.
|
--- Range script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
RANGE.version="1.2.1"
|
RANGE.version="1.2.3"
|
||||||
|
|
||||||
--TODO list:
|
--TODO list:
|
||||||
--TODO: Add custom weapons, which can be specified by the user.
|
--TODO: Add custom weapons, which can be specified by the user.
|
||||||
@ -460,9 +460,10 @@ function RANGE:SetBombtrackThreshold(distance)
|
|||||||
self.BombtrackThreshold=distance*1000 or 25*1000
|
self.BombtrackThreshold=distance*1000 or 25*1000
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set range location. If this is not done, one (random) unit position of the range is used to determine the center of the range.
|
--- Set range location. If this is not done, one (random) unit position of the range is used to determine the location of the range.
|
||||||
|
-- The range location determines the position at which the weather data is evaluated.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Core.Point#COORDINATE coordinate Coordinate of the center of the range.
|
-- @param Core.Point#COORDINATE coordinate Coordinate of the range.
|
||||||
function RANGE:SetRangeLocation(coordinate)
|
function RANGE:SetRangeLocation(coordinate)
|
||||||
self.location=coordinate
|
self.location=coordinate
|
||||||
end
|
end
|
||||||
@ -471,7 +472,7 @@ end
|
|||||||
-- If a zone is not explicitly specified, the range zone is determined by its location and radius.
|
-- If a zone is not explicitly specified, the range zone is determined by its location and radius.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Core.Zone#ZONE zone MOOSE zone defining the range perimeters.
|
-- @param Core.Zone#ZONE zone MOOSE zone defining the range perimeters.
|
||||||
function RANGE:SetRangeLocation(zone)
|
function RANGE:SetRangeZone(zone)
|
||||||
self.rangezone=zone
|
self.rangezone=zone
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1163,11 +1164,19 @@ function RANGE:OnEventShot(EventData)
|
|||||||
-- Coordinate of impact point.
|
-- Coordinate of impact point.
|
||||||
local impactcoord=COORDINATE:NewFromVec3(_lastBombPos)
|
local impactcoord=COORDINATE:NewFromVec3(_lastBombPos)
|
||||||
|
|
||||||
|
-- Check if impact happend in range zone.
|
||||||
|
local insidezone=self.rangezone:IsCoordinateInZone(impactcoord)
|
||||||
|
|
||||||
-- Distance from range. We dont want to smoke targets outside of the range.
|
-- Distance from range. We dont want to smoke targets outside of the range.
|
||||||
local impactdist=impactcoord:Get2DDistance(self.location)
|
local impactdist=impactcoord:Get2DDistance(self.location)
|
||||||
|
|
||||||
|
-- Impact point of bomb.
|
||||||
|
if self.Debug then
|
||||||
|
impactcoord:MarkToAll("Bomb impact point")
|
||||||
|
end
|
||||||
|
|
||||||
-- Smoke impact point of bomb.
|
-- Smoke impact point of bomb.
|
||||||
if self.PlayerSettings[_playername].smokebombimpact and impactdist<self.rangeradius then
|
if self.PlayerSettings[_playername].smokebombimpact and insidezone then
|
||||||
if self.PlayerSettings[_playername].delaysmoke then
|
if self.PlayerSettings[_playername].delaysmoke then
|
||||||
timer.scheduleFunction(self._DelayedSmoke, {coord=impactcoord, color=self.PlayerSettings[_playername].smokecolor}, timer.getTime() + self.TdelaySmoke)
|
timer.scheduleFunction(self._DelayedSmoke, {coord=impactcoord, color=self.PlayerSettings[_playername].smokecolor}, timer.getTime() + self.TdelaySmoke)
|
||||||
else
|
else
|
||||||
@ -1185,6 +1194,8 @@ function RANGE:OnEventShot(EventData)
|
|||||||
-- Distance between bomb and target.
|
-- Distance between bomb and target.
|
||||||
local _temp = impactcoord:Get2DDistance(_target:GetCoordinate())
|
local _temp = impactcoord:Get2DDistance(_target:GetCoordinate())
|
||||||
|
|
||||||
|
--env.info(string.format("FF target = %s dist = %d m", _target:GetName(), _temp))
|
||||||
|
|
||||||
-- Find closest target to last known position of the bomb.
|
-- Find closest target to last known position of the bomb.
|
||||||
if _distance == nil or _temp < _distance then
|
if _distance == nil or _temp < _distance then
|
||||||
_distance = _temp
|
_distance = _temp
|
||||||
@ -1203,7 +1214,7 @@ function RANGE:OnEventShot(EventData)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Count if bomb fell less than 1 km away from the target.
|
-- Count if bomb fell less than ~1 km away from the target.
|
||||||
if _distance <= self.scorebombdistance then
|
if _distance <= self.scorebombdistance then
|
||||||
|
|
||||||
-- Init bomb player results.
|
-- Init bomb player results.
|
||||||
@ -1222,10 +1233,10 @@ function RANGE:OnEventShot(EventData)
|
|||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
self:_DisplayMessageToGroup(_unit, _message, nil, true)
|
self:_DisplayMessageToGroup(_unit, _message, nil, true)
|
||||||
elseif _distance <= self.rangeradius then
|
elseif insidezone then
|
||||||
-- Send message
|
-- Send message
|
||||||
local _message=string.format("%s, weapon fell more than %.1f km away from nearest range target. No score!", _callsign, self.scorebombdistance/1000)
|
local _message=string.format("%s, weapon fell more than %.1f km away from nearest range target. No score!", _callsign, self.scorebombdistance/1000)
|
||||||
self:_DisplayMessageToGroup(_unit, _message, nil, true)
|
self:_DisplayMessageToGroup(_unit, _message, nil, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
--Terminate the timer
|
--Terminate the timer
|
||||||
|
|||||||
@ -43,6 +43,19 @@ BIGSMOKEPRESET = {
|
|||||||
HugeSmoke=7,
|
HugeSmoke=7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- DCS map as returned by env.mission.theatre.
|
||||||
|
-- @type DCSMAP
|
||||||
|
-- @field #string Caucasus Caucasus map.
|
||||||
|
-- @field #string Normandy Normandy map.
|
||||||
|
-- @field #string NTTR Nevada Test and Training Range map.
|
||||||
|
-- @field #string PersianGulf Persian Gulf map.
|
||||||
|
DCSMAP = {
|
||||||
|
Caucasus="Caucasus",
|
||||||
|
NTTR="Nevada",
|
||||||
|
Normandy="Normandy",
|
||||||
|
PersianGulf="PersianGulf"
|
||||||
|
}
|
||||||
|
|
||||||
--- Utilities static class.
|
--- Utilities static class.
|
||||||
-- @type UTILS
|
-- @type UTILS
|
||||||
UTILS = {
|
UTILS = {
|
||||||
@ -281,7 +294,26 @@ UTILS.CelciusToFarenheit = function( Celcius )
|
|||||||
return Celcius * 9/5 + 32
|
return Celcius * 9/5 + 32
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert pressure from hecto Pascal (hPa) to inches of mercury (inHg).
|
||||||
|
-- @param #number hPa Pressure in hPa.
|
||||||
|
-- @return #number Pressure in inHg.
|
||||||
|
UTILS.hPa2inHg = function( hPa )
|
||||||
|
return hPa * 0.0295299830714
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert pressure from hecto Pascal (hPa) to millimeters of mercury (mmHg).
|
||||||
|
-- @param #number hPa Pressure in hPa.
|
||||||
|
-- @return #number Pressure in mmHg.
|
||||||
|
UTILS.hPa2mmHg = function( hPa )
|
||||||
|
return hPa * 0.7500615613030
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert kilo gramms (kg) to pounds (lbs).
|
||||||
|
-- @param #number kg Mass in kg.
|
||||||
|
-- @return #number Mass in lbs.
|
||||||
|
UTILS.kg2lbs = function( kg )
|
||||||
|
return kg * 2.20462
|
||||||
|
end
|
||||||
|
|
||||||
--[[acc:
|
--[[acc:
|
||||||
in DM: decimal point of minutes.
|
in DM: decimal point of minutes.
|
||||||
@ -539,7 +571,7 @@ end
|
|||||||
|
|
||||||
--- Convert clock time from hours, minutes and seconds to seconds.
|
--- Convert clock time from hours, minutes and seconds to seconds.
|
||||||
-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days.
|
-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days.
|
||||||
-- @param #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
|
-- @return #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
|
||||||
function UTILS.ClockToSeconds(clock)
|
function UTILS.ClockToSeconds(clock)
|
||||||
|
|
||||||
-- Nil check.
|
-- Nil check.
|
||||||
@ -551,7 +583,7 @@ function UTILS.ClockToSeconds(clock)
|
|||||||
local seconds=0
|
local seconds=0
|
||||||
|
|
||||||
-- Split additional days.
|
-- Split additional days.
|
||||||
local dsplit=UTILS.split(clock, "+")
|
local dsplit=UTILS.Split(clock, "+")
|
||||||
|
|
||||||
-- Convert days to seconds.
|
-- Convert days to seconds.
|
||||||
if #dsplit>1 then
|
if #dsplit>1 then
|
||||||
@ -680,3 +712,77 @@ function UTILS.VecCross(a, b)
|
|||||||
return {x=a.y*b.z - a.z*b.y, y=a.z*b.x - a.x*b.z, z=a.x*b.y - a.y*b.x}
|
return {x=a.y*b.z - a.z*b.y, y=a.z*b.x - a.x*b.z, z=a.x*b.y - a.y*b.x}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Converts a TACAN Channel/Mode couple into a frequency in Hz.
|
||||||
|
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
|
||||||
|
-- @param #string TACANMode The TACAN mode, i.e. the "X" in "10X".
|
||||||
|
-- @return #number Frequency in Hz or #nil if parameters are invalid.
|
||||||
|
function UTILS.TACANToFrequency(TACANChannel, TACANMode)
|
||||||
|
|
||||||
|
if type(TACANChannel) ~= "number" then
|
||||||
|
return nil -- error in arguments
|
||||||
|
end
|
||||||
|
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||||
|
return nil -- error in arguments
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
--- Returns the DCS map/theatre as optained by env.mission.theatre
|
||||||
|
-- @return #string DCS map name .
|
||||||
|
function UTILS.GetDCSMap()
|
||||||
|
return env.mission.theatre
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the magnetic declination of the map.
|
||||||
|
-- Returned values for the current maps are:
|
||||||
|
--
|
||||||
|
-- * Caucasus +6 (East), year ~ 2011
|
||||||
|
-- * NTTR +12 (East), year ~ 2011
|
||||||
|
-- * Normandy -10 (West), year ~ 1944
|
||||||
|
-- * Persian Gulf +2 (East), year ~ 2011
|
||||||
|
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
||||||
|
-- @return #number Declination in degrees.
|
||||||
|
function UTILS.GetMagneticDeclination(map)
|
||||||
|
|
||||||
|
-- Map.
|
||||||
|
map=map or UTILS.GetDCSMap()
|
||||||
|
|
||||||
|
local declination=0
|
||||||
|
if map==DCSMAP.Caucasus then
|
||||||
|
declination=6
|
||||||
|
elseif map==DCSMAP.NTTR then
|
||||||
|
declination=12
|
||||||
|
elseif map==DCSMAP.Normandy then
|
||||||
|
declination=-10
|
||||||
|
elseif map==DCSMAP.PersianGulf then
|
||||||
|
declination=2
|
||||||
|
else
|
||||||
|
declination=0
|
||||||
|
end
|
||||||
|
|
||||||
|
return declination
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -342,16 +342,26 @@ function CONTROLLABLE:PushTask( DCSTask, WaitTime )
|
|||||||
local DCSControllable = self:GetDCSObject()
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
|
||||||
if DCSControllable then
|
if DCSControllable then
|
||||||
local Controller = self:_GetController()
|
|
||||||
|
local DCSControllableName = self:GetName()
|
||||||
|
|
||||||
-- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results.
|
-- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results.
|
||||||
-- Therefore we schedule the functions to set the mission and options for the Controllable.
|
-- Therefore we schedule the functions to set the mission and options for the Controllable.
|
||||||
-- Controller:pushTask( DCSTask )
|
-- Controller:pushTask( DCSTask )
|
||||||
|
|
||||||
if WaitTime then
|
local function PushTask( Controller, DCSTask )
|
||||||
self.TaskScheduler:Schedule( Controller, Controller.pushTask, { DCSTask }, WaitTime )
|
if self and self:IsAlive() then
|
||||||
else
|
local Controller = self:_GetController()
|
||||||
Controller:pushTask( DCSTask )
|
Controller:pushTask( DCSTask )
|
||||||
|
else
|
||||||
|
BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not WaitTime or WaitTime == 0 then
|
||||||
|
PushTask( self, DCSTask )
|
||||||
|
else
|
||||||
|
self.TaskScheduler:Schedule( self, PushTask, { DCSTask }, WaitTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@ -362,7 +372,7 @@ end
|
|||||||
|
|
||||||
--- Clearing the Task Queue and Setting the Task on the queue from the controllable.
|
--- Clearing the Task Queue and Setting the Task on the queue from the controllable.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #DCS.Task DCSTask DCS Task array.
|
-- @param DCS#Task DCSTask DCS Task array.
|
||||||
-- @param #number WaitTime Time in seconds, before the task is set.
|
-- @param #number WaitTime Time in seconds, before the task is set.
|
||||||
-- @return Wrapper.Controllable#CONTROLLABLE self
|
-- @return Wrapper.Controllable#CONTROLLABLE self
|
||||||
function CONTROLLABLE:SetTask( DCSTask, WaitTime )
|
function CONTROLLABLE:SetTask( DCSTask, WaitTime )
|
||||||
@ -540,9 +550,9 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Executes a command action
|
--- Executes a command action for the CONTROLLABLE.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param DCS#Command DCSCommand
|
-- @param DCS#Command DCSCommand The command to be executed.
|
||||||
-- @return #CONTROLLABLE self
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:SetCommand( DCSCommand )
|
function CONTROLLABLE:SetCommand( DCSCommand )
|
||||||
self:F2( DCSCommand )
|
self:F2( DCSCommand )
|
||||||
@ -630,9 +640,122 @@ function CONTROLLABLE:StartUncontrolled(delay)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Give the CONTROLLABLE the command to activate a beacon. See [DCS_command_activateBeacon](https://wiki.hoggitworld.com/view/DCS_command_activateBeacon) on Hoggit.
|
||||||
|
-- For specific beacons like TACAN use the more convenient @{#BEACON} class.
|
||||||
|
-- Note that a controllable can only have one beacon activated at a time with the execption of ICLS.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param Core.Radio#BEACON.Type Type Beacon type (VOR, DME, TACAN, RSBN, ILS etc).
|
||||||
|
-- @param Core.Radio#BEACON.System System Beacon system (VOR, DME, TACAN, RSBN, ILS etc).
|
||||||
|
-- @param #number Frequency Frequency in Hz the beacon is running on. Use @{#UTILS.TACANToFrequency} to generate a frequency for TACAN beacons.
|
||||||
|
-- @param #number UnitID The ID of the unit the beacon is attached to. Usefull if more units are in one group.
|
||||||
|
-- @param #number Channel Channel the beacon is using. For, e.g. TACAN beacons.
|
||||||
|
-- @param #string ModeChannel The TACAN mode of the beacon, i.e. "X" or "Y".
|
||||||
|
-- @param #boolean AA If true, create and Air-Air beacon. IF nil, automatically set if CONTROLLABLE depending on whether unit is and aircraft or not.
|
||||||
|
-- @param #string Callsign Morse code identification callsign.
|
||||||
|
-- @param #boolean Bearing If true, beacon provides bearing information - if supported by the unit the beacon is attached to.
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the beacon is activated.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, ModeChannel, AA, Callsign, Bearing, Delay)
|
||||||
|
|
||||||
|
AA=AA or self:IsAir()
|
||||||
|
UnitID=UnitID or self:GetID()
|
||||||
|
|
||||||
|
-- Command
|
||||||
|
local CommandActivateBeacon= {
|
||||||
|
id = "ActivateBeacon",
|
||||||
|
params = {
|
||||||
|
["type"] = Type,
|
||||||
|
["system"] = System,
|
||||||
|
["frequency"] = Frequency,
|
||||||
|
["unitId"] = UnitID,
|
||||||
|
["channel"] = Channel,
|
||||||
|
["modeChannel"] = ModeChannel,
|
||||||
|
["AA"] = AA,
|
||||||
|
["callsign"] = Callsign,
|
||||||
|
["bearing"] = Bearing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
SCHEDULER:New(nil, self.CommandActivateBeacon, {self, Type, System, Frequency, UnitID, Channel, ModeChannel, AA, Callsign, Bearing}, Delay)
|
||||||
|
else
|
||||||
|
self:SetCommand(CommandActivateBeacon)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Activate ICLS system of the CONTROLLABLE. The controllable should be an aircraft carrier!
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number Channel ICLS channel.
|
||||||
|
-- @param #number UnitID The ID of the unit the ICLS system is attached to. Useful if more units are in one group.
|
||||||
|
-- @param #string Callsign Morse code identification callsign.
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:CommandActivateICLS(Channel, UnitID, Callsign, Delay)
|
||||||
|
self:F()
|
||||||
|
|
||||||
|
-- Command to activate ICLS system.
|
||||||
|
local CommandActivateICLS= {
|
||||||
|
id = "ActivateICLS",
|
||||||
|
params= {
|
||||||
|
["type"] = BEACON.Type.ICLS,
|
||||||
|
["channel"] = Channel,
|
||||||
|
["unitId"] = UnitID,
|
||||||
|
["callsign"] = Callsign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
SCHEDULER:New(nil, self.CommandActivateICLS, {self}, Delay)
|
||||||
|
else
|
||||||
|
self:SetCommand(CommandActivateICLS)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Deactivate the active beacon of the CONTROLLABLE.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the beacon is deactivated.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:CommandDeactivateBeacon(Delay)
|
||||||
|
self:F()
|
||||||
|
|
||||||
|
-- Command to deactivate
|
||||||
|
local CommandDeactivateBeacon={id='DeactivateBeacon', params={}}
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
SCHEDULER:New(nil, self.CommandActivateBeacon, {self}, Delay)
|
||||||
|
else
|
||||||
|
self:SetCommand(CommandDeactivateBeacon)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Deactivate the ICLS of the CONTROLLABLE.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:CommandDeactivateICLS(Delay)
|
||||||
|
self:F()
|
||||||
|
|
||||||
|
-- Command to deactivate
|
||||||
|
local CommandDeactivateICLS={id='DeactivateICLS', params={}}
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
SCHEDULER:New(nil, self.CommandDeactivateICLS, {self}, Delay)
|
||||||
|
else
|
||||||
|
self:SetCommand(CommandDeactivateICLS)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- TASKS FOR AIR CONTROLLABLES
|
-- TASKS FOR AIR CONTROLLABLES
|
||||||
|
|
||||||
|
|
||||||
--- (AIR) Attack a Controllable.
|
--- (AIR) Attack a Controllable.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked.
|
-- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked.
|
||||||
@ -870,6 +993,38 @@ function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed )
|
|||||||
return DCSTask
|
return DCSTask
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (AIR) Orbit at a position with at a given altitude and speed. Optionally, a race track pattern can be specified.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits.
|
||||||
|
-- @param #number Altitude Altitude in meters of the orbit pattern.
|
||||||
|
-- @param #number Speed Speed [m/s] flying the orbit pattern
|
||||||
|
-- @param Core.Point#COORDINATE CoordRaceTrack (Optional) If this coordinate is specified, the CONTROLLABLE will fly a race-track pattern using this and the initial coordinate.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:TaskOrbit(Coord, Altitude, Speed, CoordRaceTrack)
|
||||||
|
|
||||||
|
local Pattern=AI.Task.OrbitPattern.CIRCLE
|
||||||
|
|
||||||
|
local P1=Coord:GetVec2()
|
||||||
|
local P2=nil
|
||||||
|
if CoordRaceTrack then
|
||||||
|
Pattern=AI.Task.OrbitPattern.RACE_TRACK
|
||||||
|
P2=CoordRaceTrack:GetVec2()
|
||||||
|
end
|
||||||
|
|
||||||
|
local Task = {
|
||||||
|
id = 'Orbit',
|
||||||
|
params = {
|
||||||
|
pattern = Pattern,
|
||||||
|
point = P1,
|
||||||
|
point2 = P2,
|
||||||
|
speed = Speed,
|
||||||
|
altitude = Altitude,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task
|
||||||
|
end
|
||||||
|
|
||||||
--- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude.
|
--- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #number Altitude The altitude [m] to hold the position.
|
-- @param #number Altitude The altitude [m] to hold the position.
|
||||||
@ -958,11 +1113,7 @@ function CONTROLLABLE:TaskRefueling()
|
|||||||
-- params = {}
|
-- params = {}
|
||||||
-- }
|
-- }
|
||||||
|
|
||||||
local DCSTask
|
local DCSTask={id='Refueling', params={}}
|
||||||
DCSTask = { id = 'Refueling',
|
|
||||||
params = {
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
self:T3( { DCSTask } )
|
self:T3( { DCSTask } )
|
||||||
return DCSTask
|
return DCSTask
|
||||||
@ -2101,7 +2252,7 @@ do -- Route methods
|
|||||||
FromCoordinate = FromCoordinate or self:GetCoordinate()
|
FromCoordinate = FromCoordinate or self:GetCoordinate()
|
||||||
|
|
||||||
-- Get path and path length on road including the end points (From and To).
|
-- Get path and path length on road including the end points (From and To).
|
||||||
local PathOnRoad, LengthOnRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, true)
|
local PathOnRoad, LengthOnRoad, GotPath =FromCoordinate:GetPathOnRoad(ToCoordinate, true)
|
||||||
|
|
||||||
-- Get the length only(!) on the road.
|
-- Get the length only(!) on the road.
|
||||||
local _,LengthRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, false)
|
local _,LengthRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, false)
|
||||||
@ -2113,7 +2264,7 @@ do -- Route methods
|
|||||||
-- Calculate the direct distance between the initial and final points.
|
-- Calculate the direct distance between the initial and final points.
|
||||||
local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate)
|
local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate)
|
||||||
|
|
||||||
if PathOnRoad then
|
if GotPath then
|
||||||
|
|
||||||
-- Off road part of the rout: Total=OffRoad+OnRoad.
|
-- Off road part of the rout: Total=OffRoad+OnRoad.
|
||||||
LengthOffRoad=LengthOnRoad-LengthRoad
|
LengthOffRoad=LengthOnRoad-LengthRoad
|
||||||
@ -2136,7 +2287,7 @@ do -- Route methods
|
|||||||
local canroad=false
|
local canroad=false
|
||||||
|
|
||||||
-- Check if a valid path on road could be found.
|
-- Check if a valid path on road could be found.
|
||||||
if PathOnRoad and LengthDirect > 2000 then -- if the length of the movement is less than 1 km, drive directly.
|
if GotPath and LengthDirect > 2000 then -- if the length of the movement is less than 1 km, drive directly.
|
||||||
-- Check whether the road is very long compared to direct path.
|
-- Check whether the road is very long compared to direct path.
|
||||||
if LongRoad and Shortcut then
|
if LongRoad and Shortcut then
|
||||||
|
|
||||||
@ -3024,6 +3175,3 @@ function CONTROLLABLE:IsAirPlane()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Message APIs
|
|
||||||
@ -325,7 +325,7 @@ end
|
|||||||
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
||||||
-- To raise these events, provide the `GenerateEvent` parameter.
|
-- To raise these events, provide the `GenerateEvent` parameter.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #boolean GenerateEvent true if you want to generate a crash or dead event for each unit.
|
-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
|
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
|
||||||
-- Helicopter = GROUP:FindByName( "Helicopter" )
|
-- Helicopter = GROUP:FindByName( "Helicopter" )
|
||||||
@ -1483,6 +1483,17 @@ function GROUP:Respawn( Template, Reset )
|
|||||||
Template = self:GetTemplate()
|
Template = self:GetTemplate()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get correct heading.
|
||||||
|
local function _Heading(course)
|
||||||
|
local h
|
||||||
|
if course<=180 then
|
||||||
|
h=math.rad(course)
|
||||||
|
else
|
||||||
|
h=-math.rad(360-course)
|
||||||
|
end
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
|
||||||
if self:IsAlive() then
|
if self:IsAlive() then
|
||||||
local Zone = self.InitRespawnZone -- Core.Zone#ZONE
|
local Zone = self.InitRespawnZone -- Core.Zone#ZONE
|
||||||
local Vec3 = Zone and Zone:GetVec3() or self:GetVec3()
|
local Vec3 = Zone and Zone:GetVec3() or self:GetVec3()
|
||||||
@ -1515,7 +1526,8 @@ function GROUP:Respawn( Template, Reset )
|
|||||||
Template.units[UnitID].alt = self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y
|
Template.units[UnitID].alt = self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y
|
||||||
Template.units[UnitID].x = ( Template.units[UnitID].x - From.x ) + GroupUnitVec3.x -- Keep the original x position of the template and translate to the new position.
|
Template.units[UnitID].x = ( Template.units[UnitID].x - From.x ) + GroupUnitVec3.x -- Keep the original x position of the template and translate to the new position.
|
||||||
Template.units[UnitID].y = ( Template.units[UnitID].y - From.y ) + GroupUnitVec3.z -- Keep the original z position of the template and translate to the new position.
|
Template.units[UnitID].y = ( Template.units[UnitID].y - From.y ) + GroupUnitVec3.z -- Keep the original z position of the template and translate to the new position.
|
||||||
Template.units[UnitID].heading = self.InitRespawnHeading and self.InitRespawnHeading or GroupUnit:GetHeading()
|
Template.units[UnitID].heading = _Heading(self.InitRespawnHeading and self.InitRespawnHeading or GroupUnit:GetHeading())
|
||||||
|
Template.units[UnitID].psi = -Template.units[UnitID].heading
|
||||||
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -706,8 +706,8 @@ end
|
|||||||
|
|
||||||
--- Returns the unit's climb or descent angle.
|
--- Returns the unit's climb or descent angle.
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||||
-- @return #number Climb or descent angle in degrees.
|
-- @return #number Climb or descent angle in degrees. Or 0 if velocity vector norm is zero (or nil). Or nil, if the position of the POSITIONABLE returns nil.
|
||||||
function POSITIONABLE:GetClimbAnge()
|
function POSITIONABLE:GetClimbAngle()
|
||||||
|
|
||||||
-- Get position of the unit.
|
-- Get position of the unit.
|
||||||
local unitpos = self:GetPosition()
|
local unitpos = self:GetPosition()
|
||||||
@ -719,10 +719,17 @@ function POSITIONABLE:GetClimbAnge()
|
|||||||
|
|
||||||
if unitvel and UTILS.VecNorm(unitvel)~=0 then
|
if unitvel and UTILS.VecNorm(unitvel)~=0 then
|
||||||
|
|
||||||
return math.asin(unitvel.y/UTILS.VecNorm(unitvel))
|
-- Calculate climb angle.
|
||||||
|
local angle=math.asin(unitvel.y/UTILS.VecNorm(unitvel))
|
||||||
|
|
||||||
|
-- Return angle in degrees.
|
||||||
|
return math.deg(angle)
|
||||||
|
else
|
||||||
|
return 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the pitch angle of a unit.
|
--- Returns the pitch angle of a unit.
|
||||||
|
|||||||
@ -902,29 +902,31 @@ end
|
|||||||
function UNIT:InAir()
|
function UNIT:InAir()
|
||||||
self:F2( self.UnitName )
|
self:F2( self.UnitName )
|
||||||
|
|
||||||
|
-- Get DCS unit object.
|
||||||
local DCSUnit = self:GetDCSObject() --DCS#Unit
|
local DCSUnit = self:GetDCSObject() --DCS#Unit
|
||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit then
|
||||||
-- Implementation of workaround. The original code is below.
|
|
||||||
-- This to simulate the landing on buildings.
|
|
||||||
|
|
||||||
local UnitInAir = true
|
-- Get DCS result of whether unit is in air or not.
|
||||||
|
local UnitInAir = DCSUnit:inAir()
|
||||||
|
|
||||||
|
-- Get unit category.
|
||||||
local UnitCategory = DCSUnit:getDesc().category
|
local UnitCategory = DCSUnit:getDesc().category
|
||||||
if UnitCategory == Unit.Category.HELICOPTER then
|
|
||||||
|
-- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not.
|
||||||
|
-- This is a workaround since DCS currently does not acknoledge that helos land on buildings.
|
||||||
|
-- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier!
|
||||||
|
if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER then
|
||||||
local VelocityVec3 = DCSUnit:getVelocity()
|
local VelocityVec3 = DCSUnit:getVelocity()
|
||||||
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
|
local Velocity = UTILS.VecNorm(VelocityVec3)
|
||||||
local Coordinate = DCSUnit:getPoint()
|
local Coordinate = DCSUnit:getPoint()
|
||||||
local LandHeight = land.getHeight( { x = Coordinate.x, y = Coordinate.z } )
|
local LandHeight = land.getHeight( { x = Coordinate.x, y = Coordinate.z } )
|
||||||
local Height = Coordinate.y - LandHeight
|
local Height = Coordinate.y - LandHeight
|
||||||
if Velocity < 1 and Height <= 60 then
|
if Velocity < 1 and Height <= 60 then
|
||||||
UnitInAir = false
|
UnitInAir = false
|
||||||
end
|
end
|
||||||
else
|
|
||||||
UnitInAir = DCSUnit:inAir()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
self:T3( UnitInAir )
|
self:T3( UnitInAir )
|
||||||
return UnitInAir
|
return UnitInAir
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user