mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-20 22:03:25 +00:00
Merge pull request #2442 from FlightControl-Master/FF/Ops
New Classes + OPS Improvements
This commit is contained in:
@@ -79,6 +79,7 @@
|
||||
|
||||
do -- FSM
|
||||
|
||||
--- FSM class
|
||||
-- @type FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
--
|
||||
-- # Mark on F10 map
|
||||
--
|
||||
-- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
|
||||
-- The points of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
|
||||
-- water depth.
|
||||
--
|
||||
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
|
||||
@@ -69,11 +69,12 @@ PATHLINE = {
|
||||
-- @field #number landHeight Land height in meters.
|
||||
-- @field #number depth Water depth in meters.
|
||||
-- @field #number markerID Marker ID.
|
||||
-- @field #number lineID Line marker ID.
|
||||
|
||||
|
||||
--- PATHLINE class version.
|
||||
-- @field #string version
|
||||
PATHLINE.version="0.1.1"
|
||||
PATHLINE.version="0.2.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -301,18 +302,42 @@ function PATHLINE:GetPoint2DFromIndex(n)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Calculate the length of the line.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #boolean Project2D Calculate 2D distance between points.
|
||||
-- @return #number Length in meters.
|
||||
function PATHLINE:GetLength(Project2D)
|
||||
local l=0
|
||||
|
||||
local np=#self.points
|
||||
|
||||
for i=1,np-1 do
|
||||
local p1=self.points[i] --#PATHLINE.Point
|
||||
local p2=self.points[i+1] --#PATHLINE.Point
|
||||
|
||||
if Project2D then
|
||||
l=l+UTILS.VecDist2D(p1.vec2, p2.vec2)
|
||||
else
|
||||
l=l+UTILS.VecDist3D(p1.vec3, p2.vec3)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return l
|
||||
end
|
||||
|
||||
|
||||
--- Mark points on F10 map.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
|
||||
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
||||
-- @return #PATHLINE self
|
||||
function PATHLINE:MarkPoints(Switch)
|
||||
for i,_point in pairs(self.points) do
|
||||
local point=_point --#PATHLINE.Point
|
||||
if Switch==false then
|
||||
|
||||
if point.markerID then
|
||||
UTILS.RemoveMark(point.markerID, Delay)
|
||||
UTILS.RemoveMark(point.markerID)
|
||||
end
|
||||
|
||||
else
|
||||
@@ -329,6 +354,56 @@ function PATHLINE:MarkPoints(Switch)
|
||||
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Draw line on F10 map.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number Recipient Recipent of the line: -1=All.
|
||||
-- @param #table Color Color as RGB table plus alpha value. Default {1, 0, 0, 1.0}.
|
||||
-- @param #number LineType Line type: 1=Solid (default).
|
||||
-- @return #PATHLINE self
|
||||
function PATHLINE:DrawLine(Recipient, Color, LineType)
|
||||
|
||||
-- Input
|
||||
Recipient= Recipient or -1
|
||||
Color= Color or {1,0,0, 1.0}
|
||||
LineType=LineType or 1
|
||||
local ReadOnly=false
|
||||
|
||||
|
||||
local np=#self.points
|
||||
|
||||
for i=1,np-1 do
|
||||
local p1=self.points[i] --#PATHLINE.Point
|
||||
local p2=self.points[i+1] --#PATHLINE.Point
|
||||
|
||||
p1.lineID = UTILS.GetMarkID()
|
||||
|
||||
trigger.action.lineToAll(Recipient, p1.lineID, p1.vec3, p2.vec3, Color, LineType, ReadOnly, "")
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove line on F10 map.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number Delay Delay in seconds before line is removed.
|
||||
-- @return #PATHLINE self
|
||||
function PATHLINE:UnDrawLine(Delay)
|
||||
|
||||
|
||||
local np=#self.points
|
||||
|
||||
for _,_point in pairs(self.points) do
|
||||
local p=_point --#PATHLINE.Point
|
||||
if p.lineID then
|
||||
UTILS.RemoveMark(p.lineID, Delay)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -2060,6 +2060,40 @@ do -- COORDINATE
|
||||
|
||||
return Path, Way, GotPath
|
||||
end
|
||||
|
||||
--- Returns a table of coordinates to a destination using only roads or railroads.
|
||||
-- The first point is the closest point on road of the given coordinate.
|
||||
-- By default, the last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoord Coordinate of destination.
|
||||
-- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path.
|
||||
-- @param #boolean Railroad (Optional) If true, path on railroad is returned. Default false.
|
||||
-- @return Core.Pathline#PATHLINE Pathline containing the points on road. If no path on road can be found, nil is returned or just the endpoints.
|
||||
function COORDINATE:GetPathlineOnRoad(ToCoord, IncludeEndpoints, Railroad)
|
||||
|
||||
-- Set road type.
|
||||
local RoadType="roads"
|
||||
if Railroad==true then
|
||||
RoadType="railroads"
|
||||
end
|
||||
|
||||
-- DCS API function returning a table of vec2.
|
||||
local path = land.findPathOnRoads(RoadType, self.x, self.z, ToCoord.x, ToCoord.z)
|
||||
|
||||
if IncludeEndpoints then
|
||||
path=path or {}
|
||||
table.insert(path, 1, self:GetVec2())
|
||||
table.insert(path, ToCoord:GetVec2())
|
||||
end
|
||||
|
||||
local pathline=nil
|
||||
if path then
|
||||
pathline=PATHLINE:NewFromVec2Array(RoadType, path)
|
||||
end
|
||||
|
||||
return pathline
|
||||
end
|
||||
|
||||
|
||||
--- Gets the surface type at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3340,7 +3340,6 @@ function WAREHOUSE:FindAssetInDB(group)
|
||||
if aid~=nil then
|
||||
|
||||
local asset=_WAREHOUSEDB.Assets[aid]
|
||||
self:T2({asset=asset})
|
||||
if asset==nil then
|
||||
self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0)
|
||||
end
|
||||
@@ -3918,7 +3917,7 @@ end
|
||||
-- @param #string assignment A free to choose string specifying an assignment for the asset. This can be used with the @{#WAREHOUSE.OnAfterNewAsset} function.
|
||||
-- @param #table other (Optional) Table of other useful data. Can be collected via WAREHOUSE.OnAfterNewAsset() function for example
|
||||
function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, skill, liveries, assignment, other)
|
||||
self:T({group=group, ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight})
|
||||
--self:T({group=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight})
|
||||
|
||||
-- Set default.
|
||||
local n=ngroups or 1
|
||||
@@ -4446,7 +4445,6 @@ end
|
||||
-- @param #WAREHOUSE.Queueitem Request Information table of the request.
|
||||
-- @return #boolean If true, request is granted.
|
||||
function WAREHOUSE:onbeforeRequest(From, Event, To, Request)
|
||||
self:T3({warehouse=self.alias, request=Request})
|
||||
|
||||
-- Distance from warehouse to requesting warehouse.
|
||||
local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate())
|
||||
@@ -6153,9 +6151,6 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
|
||||
-- Uncontrolled spawning.
|
||||
template.uncontrolled=uncontrolled
|
||||
|
||||
-- Debug info.
|
||||
self:T2({airtemplate=template})
|
||||
|
||||
-- Spawn group.
|
||||
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
|
||||
|
||||
@@ -8601,6 +8596,8 @@ function WAREHOUSE:_DeleteStockItem(stockitem)
|
||||
local item=self.stock[i] --#WAREHOUSE.Assetitem
|
||||
if item.uid==stockitem.uid then
|
||||
table.remove(self.stock,i)
|
||||
-- remove also from warehouse DB
|
||||
_WAREHOUSEDB.Assets[stockitem.uid]=nil
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,6 +20,13 @@ _DATABASE:_RegisterCargos()
|
||||
_DATABASE:_RegisterZones()
|
||||
_DATABASE:_RegisterAirbases()
|
||||
|
||||
--- Function that writes to DCS log file
|
||||
-- @param #string text Formatted text.
|
||||
-- @param ... Format passed to string.format().
|
||||
function printf(text, ...)
|
||||
env.info(string.format(text, ...))
|
||||
end
|
||||
|
||||
--- Check if os etc is available.
|
||||
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||
local __na = false
|
||||
|
||||
@@ -32,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua')
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Vector.lua')
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
|
||||
@@ -186,4 +187,9 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Zone.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Towns.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' )
|
||||
|
||||
@@ -32,6 +32,7 @@ __Moose.Include( 'Core\\MarkerOps_Base.lua' )
|
||||
__Moose.Include( 'Core\\TextAndSound.lua' )
|
||||
__Moose.Include( 'Core\\Condition.lua' )
|
||||
__Moose.Include( 'Core\\ClientMenu.lua' )
|
||||
__Moose.Include( 'Core\\Vector.lua' )
|
||||
|
||||
__Moose.Include( 'Wrapper\\Object.lua' )
|
||||
__Moose.Include( 'Wrapper\\Identifiable.lua' )
|
||||
@@ -177,4 +178,9 @@ __Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' )
|
||||
|
||||
__Moose.Include( 'Navigation\\Point.lua' )
|
||||
__Moose.Include( 'Navigation\\Beacons.lua' )
|
||||
__Moose.Include( 'Navigation\\Radios.lua' )
|
||||
__Moose.Include( 'Navigation\\Towns.lua' )
|
||||
|
||||
__Moose.Include( 'Globals.lua' )
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
--- **NAVIGATION** - Beacons of the map/theatre.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Access beacons of the map
|
||||
-- * Find closest beacon
|
||||
-- * Get frequencies and channels
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Beacons).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Beacons
|
||||
-- @image NAVIGATION_Beacons.png
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- BEACONS class.
|
||||
-- @type BEACONS
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #table beacons Beacons.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *Hope is the beacon that guides lost ships back to the shore.*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The BEACONS Concept
|
||||
--
|
||||
-- This class is desinged to make information about beacons of a map/theatre easier accessible. The information contains location, type and frequencies of all or specific beacons of the map.
|
||||
--
|
||||
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be
|
||||
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `BEACONS` object can be created with the @{#BEACONS.NewFromFile}(*beacons_lua_file*) function.
|
||||
--
|
||||
-- local beacons=BEACONS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\beacons.lua")
|
||||
-- beacons:MarkerShow()
|
||||
--
|
||||
-- This will load the beacons from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
|
||||
-- you provided is correct and all relevant beacons are present.
|
||||
--
|
||||
-- # User Functions
|
||||
--
|
||||
-- ## F10 Map Markers
|
||||
--
|
||||
-- ## Position
|
||||
--
|
||||
-- ## Get Closest Beacon
|
||||
--
|
||||
--
|
||||
-- @field #BEACONS
|
||||
BEACONS = {
|
||||
ClassName = "BEACONS",
|
||||
verbose = 1,
|
||||
beacons = {},
|
||||
}
|
||||
|
||||
--- Mission capability.
|
||||
-- @type BEACONS.Beacon
|
||||
-- @field #function display_name Function that returns the localized name.
|
||||
-- @field #number type Beacon type.
|
||||
-- @field #string beaconId Beacon ID.
|
||||
-- @field #string callsign Call sign.
|
||||
-- @field #number frequency Frequency in Hz.
|
||||
-- @field #number channel TACAN, RSBN or PRMG channel depending on type.
|
||||
-- @field #table position Position table.
|
||||
-- @field #number direction Direction in degrees.
|
||||
-- @field #table positionGeo Table with latitude and longitude.
|
||||
-- @field #table sceneObjects Table with scenery objects, e.g. `{t:393396742}`.
|
||||
-- @field #number chartOffsetX No idea what this offset is?!
|
||||
-- @field DCS#Vec3 vec3 Position vector 3D.
|
||||
-- @field #number markerID ID for the F10 marker.
|
||||
-- @field #string typeName Name of becon type.
|
||||
-- @field Wrapper.Scenery#SCENERY scenery The scenery object.
|
||||
|
||||
--- BEACONS class version.
|
||||
-- @field #string version
|
||||
BEACONS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
-- DONE: TACAN channel from frequency (was already in beacon.lua as channel)
|
||||
-- DONE: Scenery object
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new BECAONS class instance from a given table.
|
||||
-- @param #BEACONS self
|
||||
-- @param #table BeaconTable Table with beacon info.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:NewFromTable(BeaconTable)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #BEACONS
|
||||
|
||||
for _,_beacon in pairs(BeaconTable) do
|
||||
local beacon=_beacon --#BEACONS.Beacon
|
||||
|
||||
-- Get 3D vector
|
||||
beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]}
|
||||
|
||||
-- Get coordinate
|
||||
beacon.coordinate=COORDINATE:NewFromVec3(beacon.vec3)
|
||||
|
||||
-- Get type name
|
||||
beacon.typeName=self:_GetTypeName(beacon.type)
|
||||
|
||||
-- Find closest scenery object from scan
|
||||
beacon.scenery=beacon.coordinate:FindClosestScenery(20)
|
||||
|
||||
-- Debug stuff for scenery object
|
||||
if false then
|
||||
if beacon.scenery then
|
||||
env.info(string.format("FF Beacon %s %s %s got scenery object %s, %s", beacon.callsign, beacon.beaconId, beacon.typeName, beacon.scenery:GetName(), beacon.scenery:GetTypeName() ))
|
||||
UTILS.PrintTableToLog(beacon.scenery.SceneryObject)
|
||||
UTILS.PrintTableToLog(beacon.sceneObjects)
|
||||
else
|
||||
env.info(string.format("FF NO scenery object %s %s %s ", beacon.callsign, beacon.beaconId, beacon.typeName))
|
||||
end
|
||||
end
|
||||
|
||||
-- Add to table
|
||||
table.insert(self.beacons, beacon)
|
||||
end
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Added %d beacons", #self.beacons))
|
||||
|
||||
if self.verbose > 0 then
|
||||
local text="Beacon types:"
|
||||
for typeName,typeID in pairs(BEACON.Type) do
|
||||
local n=self:CountBeacons(typeID)
|
||||
text=text..string.format("\n%s = %d", typeName, n)
|
||||
end
|
||||
self:I(text)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new BECAONS class instance from a given file.
|
||||
-- @param #BEACONS self
|
||||
-- @param #string FileName Full path to the file containing the map beacons.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:NewFromFile(FileName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #BEACONS
|
||||
|
||||
local exists=UTILS.FileExists(FileName)
|
||||
|
||||
if exists==false then
|
||||
self:E(string.format("ERROR: file with beacon info does not exist!"))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- This will create a global table `beacons`
|
||||
dofile(FileName)
|
||||
|
||||
-- Get beacons from table.
|
||||
self=self:NewFromTable(beacons)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 3D position vector of a specific beacon.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon beacon The beacon data structure.
|
||||
-- @return DCS#Vec3 Position vector.
|
||||
function BEACONS:GetVec3(beacon)
|
||||
return beacon.vec3
|
||||
end
|
||||
|
||||
--- Get COORDINATE of a specific beacon.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon beacon The beacon data structure.
|
||||
-- @return Core.Point#COORDINATE The coordinate.
|
||||
function BEACONS:GetCoordinate(beacon)
|
||||
local coordinate=COORDINATE:NewFromVec3(beacon.vec3)
|
||||
return coordinate
|
||||
end
|
||||
|
||||
--- Find closest beacon to a given coordinate.
|
||||
-- @param #BEACONS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @param #table ExcludeList (Optional) List of beacons to exclude.
|
||||
-- @return #BEACONS.Beacon The closest beacon.
|
||||
function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList)
|
||||
|
||||
local beacon=nil --#BEACONS.Beacon
|
||||
local distmin=math.huge
|
||||
|
||||
ExcludeList=ExcludeList or {}
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local bc=_beacon --#BEACONS.Beacon
|
||||
|
||||
if (TypeID==nil or TypeID==bc.type) and (not UTILS.IsInTable(ExcludeList, bc, "beaconId")) then
|
||||
|
||||
local dist=Coordinate:Get2DDistance(bc.vec3)
|
||||
|
||||
if dist<distmin and (DistMax==nil or dist<=DistMax) then
|
||||
distmin=dist
|
||||
beacon=bc
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return beacon
|
||||
end
|
||||
|
||||
--- Find closest beacons to a given coordinate.
|
||||
-- @param #BEACONS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number Nmax Max number of beacons. Default 5.
|
||||
-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @return #table Table of #BEACONS.Beacon closest beacons.
|
||||
function BEACONS:GetClosestBeacons(Coordinate, Nmax, TypeID, DistMax)
|
||||
|
||||
Nmax=Nmax or 5
|
||||
|
||||
local closest={}
|
||||
for i=1,Nmax do
|
||||
|
||||
local beacon=self:GetClosestBeacon(Coordinate, TypeID, DistMax, closest)
|
||||
|
||||
if beacon then
|
||||
table.insert(closest, beacon)
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
--- Get table of all beacons, optionally of a given type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number TypeID (Optional) Only return specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #table Table of beacons. Each element is of type #BEACON.Beacon.
|
||||
function BEACONS:GetBeacons(TypeID)
|
||||
|
||||
local beacons={}
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local bc=_beacon --#BEACONS.Beacon
|
||||
|
||||
if TypeID==nil or TypeID==bc.type then
|
||||
table.insert(beacons, bc)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return beacons
|
||||
end
|
||||
|
||||
--- Count beacons, optionally of a given type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number TypeID (Optional) Only count specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #number Number of beacons.
|
||||
function BEACONS:CountBeacons(TypeID)
|
||||
|
||||
local n=0
|
||||
|
||||
if TypeID then
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local bc=_beacon --#BEACONS.Beacon
|
||||
|
||||
if TypeID==bc.type then
|
||||
n=n+1
|
||||
end
|
||||
end
|
||||
else
|
||||
n=#self.beacons
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
--- Add markers for all beacons on the F10 map. Optionally, only a specific beacon or a certain beacon type can be marked.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon.
|
||||
-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:MarkerShow(Beacon, TypeID)
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local beacon=_beacon --#BEACONS.Beacon
|
||||
if Beacon==nil or Beacon.beaconId==beacon.beaconId then
|
||||
if TypeID==nil or beacon.type==TypeID then
|
||||
local text=self:_GetMarkerText(beacon)
|
||||
local coord=COORDINATE:NewFromVec3(beacon.vec3)
|
||||
if beacon.markerID then
|
||||
UTILS.RemoveMark(beacon.markerID)
|
||||
end
|
||||
beacon.markerID=coord:MarkToAll(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove markers of all beacons from the F10 map. Optionally, remove only marker of a specific beacon or a certain beacon type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon.
|
||||
-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:MarkerRemove(Beacon, TypeID)
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local beacon=_beacon --#BEACONS.Beacon
|
||||
if Beacon==nil or Beacon.beaconId==beacon.beaconId then
|
||||
if TypeID==nil or beacon.type==TypeID then
|
||||
if beacon.markerID then
|
||||
UTILS.RemoveMark(beacon.markerID)
|
||||
beacon.markerID=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon beacon The beacon data structure.
|
||||
-- @return #string Marker text.
|
||||
function BEACONS:_GetMarkerText(beacon)
|
||||
|
||||
local frequency, funit=self:_GetFrequency(beacon.frequency)
|
||||
local direction=beacon.direction~=nil and beacon.direction or -1
|
||||
|
||||
local text=string.format("Beacon %s [ID=%s]", tostring(beacon.typeName), tostring(beacon.beaconId))
|
||||
text=text..string.format("\nCallsign: %s", tostring(beacon.callsign))
|
||||
if UTILS.IsInTable({BEACON.Type.TACAN, BEACON.Type.RSBN, BEACON.Type.PRMG_GLIDESLOPE, BEACON.Type.PRMG_LOCALIZER}, beacon.type) then
|
||||
text=text..string.format("\nChannel: %s", tostring(beacon.channel))
|
||||
end
|
||||
text=text..string.format("\nFrequency: %.3f %s", frequency, funit)
|
||||
text=text..string.format("\nDirection: %.1f°", direction)
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
--- Get converted frequency.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number freq Frequency in Hz.
|
||||
-- @return #number Frequency in better unit.
|
||||
-- @return #string Unit ("Hz", "kHz", "MHz").
|
||||
function BEACONS:_GetFrequency(freq)
|
||||
|
||||
freq=freq or 0
|
||||
local unit="Hz"
|
||||
|
||||
if freq>=1e6 then
|
||||
freq=freq/1e6
|
||||
unit="MHz"
|
||||
elseif freq>=1e3 then
|
||||
freq=freq/1e3
|
||||
unit="kHz"
|
||||
end
|
||||
|
||||
return freq, unit
|
||||
end
|
||||
|
||||
--- Get name of beacon type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number typeID Beacon type number.
|
||||
-- @return #string Type name.
|
||||
function BEACONS:_GetTypeName(typeID)
|
||||
|
||||
if typeID~=nil then
|
||||
for typeName,_typeID in pairs(BEACON.Type) do
|
||||
if _typeID==typeID then
|
||||
return typeName
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,587 @@
|
||||
--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Navigation Fixes
|
||||
-- * Navigation Aids
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20NavFix).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Point
|
||||
-- @image NAVIGATION_Point.png
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- NAVFIX class.
|
||||
-- @type NAVFIX
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #string name Name of the point.
|
||||
-- @field #string typePoint Type of the point, *e.g. "Intersection", "VOR", "Airport".
|
||||
-- @field Core.Vector#VECTOR vector Position vector of the fix.
|
||||
-- @field Wrapper.Marker#MARKER marker Marker on F10 map.
|
||||
-- @field #number altMin Minimum altitude in meters.
|
||||
-- @field #number altMax Maximum altitude in meters.
|
||||
-- @field #number speedMin Minimum speed in knots.
|
||||
-- @field #number speedMax Maximum speed in knots.
|
||||
--
|
||||
-- @field #boolean isCompulsory Is this a compulsory fix.
|
||||
-- @field #boolean isFlyover Is this a flyover fix (`true`) or turning point otherwise.
|
||||
-- @field #boolean isFAF Is this a final approach fix.
|
||||
-- @field #boolean isIAF Is this an initial approach fix.
|
||||
-- @field #boolean isIF Is this an initial fix.
|
||||
-- @field #boolean isMAF Is this an initial fix.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The NAVFIX Concept
|
||||
--
|
||||
-- The NAVFIX class has a great concept!
|
||||
--
|
||||
-- A NAVFIX describes a geo position and can, *e.g.*, be part of a FLIGHTPLAN. It has a unique name and is of a certain type, *e.g.* "Intersection", "VOR", "Airbase" etc.
|
||||
-- It can also have further properties as min/max altitudes and speeds that aircraft need to obey when they pass the point.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `NAVFIX` object can be created with the @{#NAVFIX.New}() function.
|
||||
--
|
||||
-- myNavPoint=NAVFIX:New()
|
||||
-- myTemplate:SetXYZ(X, Y, Z)
|
||||
--
|
||||
-- This is how it works.
|
||||
--
|
||||
-- @field #NAVFIX
|
||||
NAVFIX = {
|
||||
ClassName = "NAVFIX",
|
||||
verbose = 0,
|
||||
}
|
||||
|
||||
--- Type of point.
|
||||
-- @type NAVFIX.Type
|
||||
-- @field #string POINT Waypoint.
|
||||
-- @field #string INTERSECTION Intersection of airway.
|
||||
-- @field #string AIRPORT Airport.
|
||||
-- @field #string VOR Very High Frequency Omnidirectional Range Station.
|
||||
-- @field #string DME Distance Measuring Equipment.
|
||||
-- @field #string NDB Non-Directional Beacon.
|
||||
-- @field #string VORDME Combined VHF omnidirectional range (VOR) with a distance-measuring equipment (DME).
|
||||
-- @field #string LOC Localizer.
|
||||
-- @field #string ILS Instrument Landing System.
|
||||
-- @field #string TACAN TACtical Air Navigation System (TACAN).
|
||||
NAVFIX.Type={
|
||||
POINT="Point",
|
||||
INTERSECTION="Intersection",
|
||||
AIRPORT="Airport",
|
||||
NDB="NDB",
|
||||
VOR="VOR",
|
||||
DME="DME",
|
||||
VORDME="VOR/DME",
|
||||
LOC="Localizer",
|
||||
ILS="ILS",
|
||||
TACAN="TACAN"
|
||||
}
|
||||
|
||||
--- NAVFIX class version.
|
||||
-- @field #string version
|
||||
NAVFIX.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new NAVFIX class instance from a given VECTOR.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name/ident of the point. Should be unique!
|
||||
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param Core.Vector#VECTOR Vector Position vector of the navpoint.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #NAVFIX
|
||||
|
||||
-- Vector of point.
|
||||
self.vector=Vector
|
||||
|
||||
-- Name of point.
|
||||
self.name=Name
|
||||
|
||||
-- Type of the point.
|
||||
self.typePoint=Type or NAVFIX.Type.POINT
|
||||
|
||||
local coord=COORDINATE:NewFromVec3(self.vector)
|
||||
|
||||
-- Marker on F10.
|
||||
self.marker=MARKER:New(coord, self:_GetMarkerText())
|
||||
|
||||
-- Log ID string.
|
||||
self.lid=string.format("NAVFIX %s [%s] | ", tostring(self.name), tostring(self.typePoint))
|
||||
|
||||
-- Debug info.
|
||||
self:I(self.lid..string.format("Created NAVFIX"))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new NAVFIX class instance from a given COORDINATE.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the point.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromCoordinate(Name, Type, Coordinate)
|
||||
|
||||
-- Create a VECTOR from the coordinate.
|
||||
local Vector=VECTOR:NewFromVec(Coordinate)
|
||||
|
||||
-- Create NAVFIX.
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new NAVFIX instance from given latitude and longitude in degrees, minutes and seconds (DMS).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param #string Latitude Latitude in DMS as string.
|
||||
-- @param #string Longitude Longitude in DMS as string.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromLLDMS(Name, Type, Latitude, Longitude)
|
||||
|
||||
-- Create a VECTOR from the coordinate.
|
||||
local Vector=VECTOR:NewFromLLDMS(Latitude, Longitude)
|
||||
|
||||
-- Create NAVFIX.
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new NAVFIX instance from given latitude and longitude in decimal degrees (DD).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param #number Latitude Latitude in DD.
|
||||
-- @param #number Longitude Longitude in DD.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromLLDD(Name, Type, Latitude, Longitude)
|
||||
|
||||
-- Create a VECTOR from the coordinate.
|
||||
local Vector=VECTOR:NewFromLLDD(Latitude, Longitude)
|
||||
|
||||
-- Create NAVFIX.
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new NAVFIX class instance relative to a given other NAVFIX.
|
||||
-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the
|
||||
-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps.
|
||||
-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type Type of navfix.
|
||||
-- @param #NAVFIX NavFix The given/existing navigation fix relative to which the new fix is created.
|
||||
-- @param #number Distance Distance from the given to the new point in nautical miles.
|
||||
-- @param #number Bearing Bearing [Deg] from the new point to the given one.
|
||||
-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the given point to the new one.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromNavFix(Name, Type, NavFix, Distance, Bearing, Reciprocal)
|
||||
|
||||
-- Convert magnetic to true bearing by adding magnetic declination, e.g. mag. bearing 10°M ==> true bearing 16°M (for 6° variation on Caucasus map)
|
||||
Bearing=Bearing+UTILS.GetMagneticDeclination()
|
||||
|
||||
if Reciprocal then
|
||||
Bearing=Bearing-180
|
||||
end
|
||||
|
||||
-- Translate.
|
||||
local Vector=NavFix.vector:Translate(UTILS.NMToMeters(Distance), Bearing, true)
|
||||
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set whether this is the intermediate fix (IF).
|
||||
-- @param #NAVFIX self
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetIntermediateFix(IntermediateFix)
|
||||
self.isIF=IntermediateFix
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether this is an initial approach fix (IAF).
|
||||
-- The IAF is the point where the initial approach segment of an instrument approach begins.
|
||||
-- It is usually a designated intersection, VHF omidirectional range (VOR) non-directional beacon (NDB)
|
||||
-- or distance measuring equipment (DME) fix.
|
||||
-- The IAF may be collocated with the intermediate fix (IF) of the instrument apprach an in such case they designate the
|
||||
-- beginning of the intermediate segment of the approach. When the IAF and the IF are combined, there is no inital approach segment.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean IntermediateFix If `true`, this is an intermediate fix.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetInitialApproachFix(IntermediateFix)
|
||||
self.isIAF=IntermediateFix
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set whether this is the final approach fix (FAF).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean FinalApproachFix If `true`, this is a final approach fix.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetFinalApproachFix(FinalApproachFix)
|
||||
self.isFAF=FinalApproachFix
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether this is the final approach fix (FAF).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean FinalApproachFix If `true`, this is a final approach fix.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetMissedApproachFix(MissedApproachFix)
|
||||
self.isMAF=MissedApproachFix
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set minimum altitude.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Altitude Min altitude in feet.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetAltMin(Altitude)
|
||||
|
||||
self.altMin=Altitude
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set maximum altitude.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Altitude Max altitude in feet.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetAltMax(Altitude)
|
||||
|
||||
self.altMax=Altitude
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set mandatory altitude (min alt = max alt).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Altitude Altitude in feet.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetAltMandatory(Altitude)
|
||||
|
||||
self.altMin=Altitude
|
||||
self.altMax=Altitude
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set minimum allowed speed at this fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Speed Min speed in knots.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetSpeedMin(Speed)
|
||||
|
||||
self.speedMin=Speed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set maximum allowed speed at this fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Speed Max speed in knots.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetSpeedMax(Speed)
|
||||
|
||||
self.speedMax=Speed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set mandatory speed (min speed = max speed) at this fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Speed Mandatory speed in knots.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetSpeedMandatory(Speed)
|
||||
|
||||
self.speedMin=Speed
|
||||
self.speedMax=Speed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set whether this fix is compulsory.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean Compulsory If `true`, this is a compusory fix. If `false` or nil, it is non-compulsory.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetCompulsory(Compulsory)
|
||||
self.isCompulsory=Compulsory
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether this is a fly-over fix fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetFlyOver(FlyOver)
|
||||
self.isFlyover=FlyOver
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the altitude in feet MSL. If min and max altitudes are set, it will return a random altitude between min and max.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set.
|
||||
function NAVFIX:GetAltitude()
|
||||
|
||||
local alt=nil
|
||||
if self.altMin and self.altMax and self.altMin~=self.altMax then
|
||||
alt=math.random(self.altMin, self.altMax)
|
||||
elseif self.altMin then
|
||||
alt=self.altMin
|
||||
elseif self.altMax then
|
||||
alt=self.altMax
|
||||
end
|
||||
|
||||
return alt
|
||||
end
|
||||
|
||||
|
||||
--- Get the speed. If min and max speeds are set, it will return a random speed between min and max.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #number Speed in knots. Can be `nil`, if neither min nor max speeds have beeen set.
|
||||
function NAVFIX:GetSpeed()
|
||||
|
||||
local speed=nil
|
||||
if self.speedMin and self.speedMax and self.speedMin~=self.speedMax then
|
||||
speed=math.random(self.speedMin, self.speedMax)
|
||||
elseif self.speedMin then
|
||||
speed=self.speedMin
|
||||
elseif self.speedMax then
|
||||
speed=self.speedMax
|
||||
end
|
||||
|
||||
return speed
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Add marker the NAVFIX on the F10 map.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:MarkerShow()
|
||||
|
||||
self.marker:ToAll()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove marker of the NAVFIX from the F10 map.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:MarkerRemove()
|
||||
|
||||
self.marker:Remove()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #string Marker text.
|
||||
function NAVFIX:_GetMarkerText()
|
||||
|
||||
local altmin=self.altMin and tostring(self.altMin) or ""
|
||||
local altmax=self.altMax and tostring(self.altMax) or ""
|
||||
local speedmin=self.speedMin and tostring(self.speedMin) or ""
|
||||
local speedmax=self.speedMax and tostring(self.speedMax) or ""
|
||||
|
||||
|
||||
local text=string.format("NAVFIX %s", self.name)
|
||||
if self.isIAF then
|
||||
text=text..string.format(" (IAF)")
|
||||
end
|
||||
if self.isIF then
|
||||
text=text..string.format(" (IF)")
|
||||
end
|
||||
text=text..string.format("\nAltitude [ft]: %s - %s", altmin, altmax)
|
||||
text=text..string.format("\nSpeed [knots]: %s - %s", speedmin, speedmax)
|
||||
text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory))
|
||||
text=text..string.format("\nFly Over: %s", tostring(self.isFlyover))
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- NAVAID class.
|
||||
-- @type NAVAID
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @extends Navigation.Point#NAVFIX
|
||||
|
||||
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The NAVAID Concept
|
||||
--
|
||||
-- A NAVAID consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `NAVAID` object can be created with the @{#NAVAID.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet
|
||||
-- and `FleetName` is the name you want to give the fleet. This must be *unique*!
|
||||
--
|
||||
-- myFleet=NAVAID:New("myWarehouseName", "1st Fleet")
|
||||
-- myFleet:SetPortZone(ZonePort1stFleet)
|
||||
-- myFleet:Start()
|
||||
--
|
||||
-- A fleet needs a *port zone*, which is set via the @{#NAVAID.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to.
|
||||
--
|
||||
-- Finally, the fleet needs to be started using the @{#NAVAID.Start}() function. If the fleet is not started, it will not process any requests.
|
||||
--
|
||||
-- @field #NAVAID
|
||||
NAVAID = {
|
||||
ClassName = "NAVAID",
|
||||
verbose = 0,
|
||||
}
|
||||
|
||||
--- NAVAID class version.
|
||||
-- @field #string version
|
||||
NAVAID.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Add frequencies. Which unit MHz, kHz, Hz?
|
||||
-- TODO: Add radial function
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new NAVAID class instance.
|
||||
-- @param #NAVAID self
|
||||
-- @param #string Name Name/ident of this navaid.
|
||||
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param #string ZoneName Name of the zone to scan the scenery.
|
||||
-- @param #string SceneryName Name of the scenery object.
|
||||
-- @return #NAVAID self
|
||||
function NAVAID:NewFromScenery(Name, Type, ZoneName, SceneryName)
|
||||
|
||||
-- Get the zone.
|
||||
local zone=ZONE:FindByName(ZoneName)
|
||||
|
||||
-- Get coordinate.
|
||||
local Coordinate=zone:GetCoordinate()
|
||||
|
||||
-- Inherit everything from NAVFIX class.
|
||||
self=BASE:Inherit(self, NAVFIX:NewFromCoordinate(Name, Type, Coordinate)) -- #NAVAID
|
||||
|
||||
-- Set zone.
|
||||
self.zone=ZONE:FindByName(ZoneName)
|
||||
|
||||
-- Try to get the scenery object. Note not all can be found unfortunately.
|
||||
if SceneryName then
|
||||
self.scenery=SCENERY:FindByNameInZone(SceneryName, ZoneName)
|
||||
if not self.scenery then
|
||||
self:E(string.format("ERROR: Could not find scenery object %s in zone %s", SceneryName, ZoneName))
|
||||
end
|
||||
end
|
||||
|
||||
-- Alias.
|
||||
self.alias=string.format("%s %s %s", tostring(ZoneName), tostring(SceneryName), tostring(Type))
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("NAVAID %s | ", self.alias)
|
||||
|
||||
-- Debug info.
|
||||
self:I(self.lid..string.format("Created NAVAID!"))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set frequency the beacon transmits on.
|
||||
-- @param #NAVAID self
|
||||
-- @param #number Frequency Frequency in Hz.
|
||||
-- @return #NAVAID self
|
||||
function NAVAID:SetFrequency(Frequency)
|
||||
|
||||
self.frequency=Frequency
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set channel of, *e.g.*, TACAN beacons.
|
||||
-- @param #NAVAID self
|
||||
-- @param #number Channel The channel.
|
||||
-- @param #string Band The band either `"X"` (default) or `"Y"`.
|
||||
-- @return #NAVAID self
|
||||
function NAVAID:SetChannel(Channel, Band)
|
||||
|
||||
self.channel=Channel
|
||||
self.band=Band or "X"
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Add private CLASS functions here.
|
||||
-- No private NAVAID functions yet.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,422 @@
|
||||
--- **NAVIGATION** - Airbase radios.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Get radio frequencies of airbases
|
||||
-- * Find closest airbase radios
|
||||
-- * Mark radio frequencies on F10 map
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Radios).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Radios
|
||||
-- @image NAVIGATION_Radios.png
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- RADIOS class.
|
||||
-- @type RADIOS
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #table radios Radios.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *It's not true I had nothing on, I had the radio on.* -- *Marilyn Monroe*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The RADIOS Concept
|
||||
--
|
||||
-- This class is desinged to make information about radios of a map/theatre easier accessible. The information contains mostly the frequencies of airbases of the map.
|
||||
--
|
||||
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be
|
||||
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `RADIOS` object can be created with the @{#RADIOS.NewFromFile}(*radio_lua_file*) function.
|
||||
--
|
||||
-- local radios=RADIOS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\radios.lua")
|
||||
-- radios:MarkerShow()
|
||||
--
|
||||
-- This will load the radios from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
|
||||
-- you provided is correct and all relevant radios are present.
|
||||
--
|
||||
-- # User Functions
|
||||
--
|
||||
-- ## F10 Map Markers
|
||||
--
|
||||
-- ## Position
|
||||
--
|
||||
-- ## Closest Radio
|
||||
--
|
||||
--
|
||||
-- @field #RADIOS
|
||||
RADIOS = {
|
||||
ClassName = "RADIOS",
|
||||
verbose = 0,
|
||||
radios = {},
|
||||
}
|
||||
|
||||
--- Radio item data structure.
|
||||
-- @type RADIOS.Radio
|
||||
-- @field #string radioId Radio ID.
|
||||
-- @field #table role Roles of the radio (usually {"ground", "tower", "approach"}).
|
||||
-- @field #table callsign Callsigns of the radio (usually the airbase name).
|
||||
-- @field #table frequency Frequencies of the radios.
|
||||
-- @field #table position Position table.
|
||||
-- @field #table sceneObjects Scenery objects.
|
||||
-- @field #string name Name of the airbase.
|
||||
-- @field Wrapper.Airbase#AIRBASE airbase Airbase.
|
||||
-- @field Core.Point#COORDINATE coordinate The COORDINATE of the radio.
|
||||
-- @field DCS#Vec3 vec3 3D vector.
|
||||
-- @field #number markerID Marker ID.
|
||||
|
||||
--- Radio item data structure.
|
||||
-- @type RADIOS.Frequency
|
||||
-- @field #number modu Modulation type.
|
||||
-- @field #number freq Frequency in Hz.
|
||||
|
||||
|
||||
--- RADIOS class version.
|
||||
-- @field #string version
|
||||
RADIOS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new RADIOS class instance from a given table.
|
||||
-- @param #RADIOS self
|
||||
-- @param #table RadioTable Table with radios info.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:NewFromTable(RadioTable)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #RADIOS
|
||||
|
||||
--local airbasenames=AIRBASE.GetAllAirbaseNames()
|
||||
|
||||
-- Get all airdromes
|
||||
local airdromes=AIRBASE.GetAllAirbases(nil, Airbase.Category.AIRDROME)
|
||||
|
||||
for _,_radio in pairs(RadioTable) do
|
||||
local radio=_radio --#RADIOS.Radio
|
||||
|
||||
-- The table structure of callsign is a bit awkward. We need to get the airbase name.
|
||||
-- Note that unfortunately, the callsign does not always correspond to the airbase name.
|
||||
if false then
|
||||
local cs=radio.callsign[1]
|
||||
if cs and cs.common then
|
||||
radio.name=cs.common[1]
|
||||
elseif cs and cs.nato then
|
||||
radio.name=cs.nato[1]
|
||||
else
|
||||
radio.name="Unknown"
|
||||
end
|
||||
radio.name=self:_GetAirbaseName(airbasenames, radio.name)
|
||||
radio.airbase=AIRBASE:FindByName(radio.name)
|
||||
end
|
||||
|
||||
-- Each radio item has a key radioId = 'airfield106_0', where 106 is the UID of the airbase.
|
||||
-- So we can use that to get the airbase.
|
||||
local aid = tonumber(string.match(radio.radioId, "airfield(%d+)_"))
|
||||
|
||||
-- Get airbase
|
||||
radio.airbase=self:_GetAirbaseByID(airdromes, aid)
|
||||
|
||||
-- Set other stuff
|
||||
if radio.airbase then
|
||||
radio.coordinate=radio.airbase:GetCoordinate()
|
||||
radio.vec3=radio.airbase:GetVec3()
|
||||
radio.name=radio.airbase:GetName()
|
||||
end
|
||||
|
||||
-- Add to table
|
||||
table.insert(self.radios, radio)
|
||||
end
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Added %d radios", #self.radios))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new RADIOS class instance from a given file.
|
||||
-- @param #RADIOS self
|
||||
-- @param #string FileName Full path to the file containing the map radios.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:NewFromFile(FileName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #RADIOS
|
||||
|
||||
local exists=UTILS.FileExists(FileName)
|
||||
|
||||
if exists==false then
|
||||
self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName)))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Backup DCS radio table
|
||||
local radiobak=UTILS.DeepCopy(radio)
|
||||
|
||||
-- This will create a global table `radio`
|
||||
dofile(FileName)
|
||||
|
||||
-- Get radios from table.
|
||||
self=self:NewFromTable(radio)
|
||||
|
||||
-- Restore DCS radio table
|
||||
radio=UTILS.DeepCopy(radiobak)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 3D position vector of a specific radio.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio radio The radio data structure.
|
||||
-- @return DCS#Vec3 Position vector.
|
||||
function RADIOS:GetVec3(radio)
|
||||
return radio.vec3
|
||||
end
|
||||
|
||||
--- Get COORDINATE of a specific radio.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio radio The radio data structure.
|
||||
-- @return Core.Point#COORDINATE The coordinate.
|
||||
function RADIOS:GetCoordinate(radio)
|
||||
return radio.coordinate
|
||||
end
|
||||
|
||||
|
||||
--- Find closest radio to a given coordinate.
|
||||
-- @param #RADIOS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @param #table ExcludeList (Optional) List of radios to exclude.
|
||||
-- @return #RADIOS.Radio The closest radio.
|
||||
function RADIOS:GetClosestRadio(Coordinate, DistMax, ExcludeList)
|
||||
|
||||
local radio=nil --#RADIOS.Radio
|
||||
local distmin=math.huge
|
||||
|
||||
ExcludeList=ExcludeList or {}
|
||||
|
||||
for _,_radio in pairs(self.radios) do
|
||||
local ra=_radio --#RADIOS.Radio
|
||||
|
||||
if (not UTILS.IsInTable(ExcludeList, ra, "radioId")) then
|
||||
|
||||
local dist=Coordinate:Get2DDistance(ra.coordinate)
|
||||
|
||||
if dist<distmin and (DistMax==nil or dist<=DistMax) then
|
||||
distmin=dist
|
||||
radio=ra
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return radio
|
||||
end
|
||||
|
||||
--- Find closest radios to a given coordinate.
|
||||
-- @param #RADIOS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number Nmax Max number of radios. Default 5.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @return #table Table of #RADIOS.Radio closest radios.
|
||||
function RADIOS:GetClosestRadios(Coordinate, Nmax, DistMax)
|
||||
|
||||
Nmax=Nmax or 5
|
||||
|
||||
local closest={}
|
||||
for i=1,Nmax do
|
||||
|
||||
local radio=self:GetClosestRadio(Coordinate, DistMax, closest)
|
||||
|
||||
if radio then
|
||||
table.insert(closest, radio)
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
|
||||
--- Add markers for all radios on the F10 map.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:MarkerShow(Radio)
|
||||
|
||||
for _,_radio in pairs(self.radios) do
|
||||
local radio=_radio --#RADIOS.Radio
|
||||
if Radio==nil or Radio.radioId==radio.radioId then
|
||||
local coord=self:GetCoordinate(radio)
|
||||
if coord then
|
||||
local text=self:_GetMarkerText(radio)
|
||||
if radio.markerID then
|
||||
UTILS.RemoveMark(radio.markerID)
|
||||
end
|
||||
radio.markerID=coord:MarkToAll(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove markers of all radios from the F10 map.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:MarkerRemove(Radio)
|
||||
|
||||
for _,_radio in pairs(self.radios) do
|
||||
local radio=_radio --#RADIOS.Radio
|
||||
if Radio==nil or Radio.radioId==radio.radioId then
|
||||
if radio.markerID then
|
||||
UTILS.RemoveMark(radio.markerID)
|
||||
radio.markerID=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio radio The radio data structure.
|
||||
-- @return #string Marker text.
|
||||
function RADIOS:_GetMarkerText(radio)
|
||||
|
||||
local text=string.format("Radio %s", tostring(radio.name))
|
||||
for b,f in pairs(radio.frequency) do
|
||||
local frequency=f --#RADIOS.Frequency
|
||||
local mod=frequency[1]
|
||||
local fre=frequency[2]
|
||||
local freq, funit=self:_GetFrequency(fre)
|
||||
--UTILS.PrintTableToLog(frequency)
|
||||
local band=self:_GetBandName(b)
|
||||
text=text..string.format("\n%s: %.3f %s", band, freq, funit)
|
||||
end
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
--- Get converted frequency.
|
||||
-- @param #RADIOS self
|
||||
-- @param #number freq Frequency in Hz.
|
||||
-- @return #number Frequency in better unit.
|
||||
-- @return #string Unit ("Hz", "kHz", "MHz").
|
||||
function RADIOS:_GetFrequency(freq)
|
||||
|
||||
freq=freq or 0
|
||||
local unit="Hz"
|
||||
|
||||
if freq>=1e6 then
|
||||
freq=freq/1e6
|
||||
unit="MHz"
|
||||
elseif freq>=1e3 then
|
||||
freq=freq/1e3
|
||||
unit="kHz"
|
||||
end
|
||||
|
||||
return freq, unit
|
||||
end
|
||||
|
||||
--- Get name of frequency band.
|
||||
-- @param #RADIOS self
|
||||
-- @param #number BandNumber Band as number.
|
||||
-- @return #string Band name.
|
||||
function RADIOS:_GetBandName(BandNumber)
|
||||
|
||||
if BandNumber~=nil then
|
||||
for bandName,bandNumber in pairs(ENUMS.FrequencyBand) do
|
||||
if bandNumber==BandNumber then
|
||||
return bandName
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
--- Get name of frequency band.
|
||||
-- @param #RADIOS self
|
||||
-- @param #table airbasenames Names of all airbases.
|
||||
-- @param #string name Name of airbase.
|
||||
-- @return #string Name of airbase
|
||||
function RADIOS:_GetAirbaseName(airbasenames, name)
|
||||
|
||||
local airbase=AIRBASE:FindByName(name)
|
||||
|
||||
if airbase then
|
||||
return name
|
||||
else
|
||||
for _,airbasename in pairs(airbasenames) do
|
||||
if string.find(airbasename, name) then
|
||||
return airbasename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
--- Get name of frequency band.
|
||||
-- @param #RADIOS self
|
||||
-- @param #table airbases Table of airbases.
|
||||
-- @param #number aid Airbase ID.
|
||||
-- @return Wrapper.Airbase#AIRBASE Airbase matching the ID or nil.
|
||||
function RADIOS:_GetAirbaseByID(airbases, aid)
|
||||
|
||||
for _,_airbase in pairs(airbases) do
|
||||
local airbase=_airbase --Wrapper.Airbase#AIRBASE
|
||||
local id=airbase:GetID(true)
|
||||
if id==aid then
|
||||
return airbase
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,331 @@
|
||||
--- **NAVIGATION** - Towns of the map/theatre.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Find towns of map
|
||||
-- * Road and rail connections
|
||||
-- * Find closest town to a given coordinate
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Towns).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Towns
|
||||
-- @image NAVIGATION_Towns.png
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- TOWNS class.
|
||||
-- @type TOWNS
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #table towns Towns.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *Hope is the beacon that guides lost ships back to the shore.*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The TOWNS Concept
|
||||
--
|
||||
-- This class is desinged to make information about towns of a map/theatre easier accessible. The information contains location and road/rail connections of the towns.
|
||||
--
|
||||
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be
|
||||
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `TOWNS` object can be created with the @{#TOWNS.NewFromFile}(*towns_lua_file*) function.
|
||||
--
|
||||
-- local towns=TOWNS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\towns.lua")
|
||||
-- towns:MarkerShow()
|
||||
--
|
||||
-- This will load the towns from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
|
||||
-- you provided is correct and all relevant towns are present.
|
||||
--
|
||||
-- # User Functions
|
||||
--
|
||||
-- ## F10 Map Markers
|
||||
--
|
||||
-- ## Position
|
||||
--
|
||||
-- ## Get Closest Town
|
||||
--
|
||||
--
|
||||
-- @field #TOWNS
|
||||
TOWNS = {
|
||||
ClassName = "TOWNS",
|
||||
verbose = 0,
|
||||
towns = {},
|
||||
}
|
||||
|
||||
--- Town data.
|
||||
-- @type TOWNS.Town
|
||||
-- @field #string display_name Displayed name.
|
||||
-- @field #string name Name of the town.
|
||||
-- @field #number latitude Latitude.
|
||||
-- @field #number longitude Longitude
|
||||
-- @field DCS#Vec3 vec3 Position vector 3D.
|
||||
-- @field Core.Point#COORDINATE coordinate The coordinate.
|
||||
-- @field Core.Point#COORDINATE coordRoad The coordinate of the closest road.
|
||||
-- @field Core.Point#COORDINATE coordRail The coordinate of the closest railway.
|
||||
-- @field #number markerID ID for the F10 marker.
|
||||
|
||||
--- TOWNS class version.
|
||||
-- @field #string version
|
||||
TOWNS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
-- DONE: Road connection
|
||||
-- DONE: Rail connection
|
||||
-- DONE: Connection between towns
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new TOWNS class instance from a given table.
|
||||
-- @param #TOWNS self
|
||||
-- @param #table TownTable Table with all towns data.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:NewFromTable(TownTable)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #TOWNS
|
||||
|
||||
for TownName,_town in pairs(TownTable) do
|
||||
local town=_town --#TOWNS.Town
|
||||
|
||||
town.name=TownName
|
||||
|
||||
-- Get coordinate
|
||||
town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude)
|
||||
|
||||
-- Get coordinate of closest road
|
||||
town.coordRoad=town.coordinate:GetClosestPointToRoad()
|
||||
|
||||
-- Get coordinate of closest rail
|
||||
town.coordRail=town.coordinate:GetClosestPointToRoad(true)
|
||||
|
||||
-- Add to table
|
||||
table.insert(self.towns, town)
|
||||
end
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Added %d towns", #self.towns))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new TOWNS class instance from a given file.
|
||||
-- @param #TOWNS self
|
||||
-- @param #string FileName Full path to the file containing the towns data.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:NewFromFile(FileName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #TOWNS
|
||||
|
||||
local exists=UTILS.FileExists(FileName)
|
||||
|
||||
if exists==false then
|
||||
self:E(string.format("ERROR: file with towns info does not exist!"))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- This will create a global table `towns`
|
||||
dofile(FileName)
|
||||
|
||||
-- Get towns from table.
|
||||
self=self:NewFromTable(towns)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 3D position vector of a specific town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return DCS#Vec3 Position vector.
|
||||
function TOWNS:GetVec3(town)
|
||||
return town.vec3
|
||||
end
|
||||
|
||||
--- Get COORDINATE of a specific town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return Core.Point#COORDINATE The coordinate.
|
||||
function TOWNS:GetCoordinate(town)
|
||||
return town.coordinate
|
||||
end
|
||||
|
||||
--- Get closest road coordinate of a town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return Core.Point#COORDINATE The closest road coordinate.
|
||||
function TOWNS:GetCoordRoad(town)
|
||||
return town.coordRoad
|
||||
end
|
||||
|
||||
--- Get closest rail coordinate of a town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return Core.Point#COORDINATE The closest rail coordinate.
|
||||
function TOWNS:GetCoordRail(town)
|
||||
return town.coordRail
|
||||
end
|
||||
|
||||
--- Get road or rail connection between two towns.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town townA The town data structure.
|
||||
-- @param #TOWNS.Town townB The town data structure.
|
||||
-- @param #boolean Railroad If `true`, find rail road connection
|
||||
-- @return Core.Pathline#PATHLINE Pathline connecting the two towns on road.
|
||||
function TOWNS:GetConnectionRoad(townA, townB, Railroad)
|
||||
|
||||
local path=townA.coordRoad:GetPathlineOnRoad(townB.coordRoad, false, Railroad)
|
||||
|
||||
return path
|
||||
end
|
||||
|
||||
--- Find closest town to a given coordinate.
|
||||
-- @param #TOWNS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @param #table ExcludeList (Optional) List of towns excluded from the search.
|
||||
-- @return #TOWNS.Town The closest town.
|
||||
function TOWNS:GetClosestTown(Coordinate, DistMax, ExcludeList)
|
||||
|
||||
local Town=nil --#TOWNS.Town
|
||||
local distmin=math.huge
|
||||
ExcludeList=ExcludeList or {}
|
||||
|
||||
for _,_town in pairs(self.towns) do
|
||||
local town=_town --#TOWNS.Town
|
||||
|
||||
if (not UTILS.IsInTable(ExcludeList, town, "name")) then
|
||||
|
||||
local dist=Coordinate:Get2DDistance(town.coordinate)
|
||||
|
||||
if dist<distmin then
|
||||
distmin=dist
|
||||
Town=town
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Town
|
||||
end
|
||||
|
||||
--- Find closest towns to a given coordinate.
|
||||
-- @param #TOWNS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number Nmax Max number of towns. Default 5.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @return #table Table of #TOWNS.Town closest towns.
|
||||
function TOWNS:GetClosestTowns(Coordinate, Nmax, DistMax)
|
||||
|
||||
Nmax=Nmax or 5
|
||||
|
||||
local closest={}
|
||||
for i=1,Nmax do
|
||||
|
||||
local town=self:GetClosestTown(Coordinate, DistMax, closest)
|
||||
|
||||
if town then
|
||||
table.insert(closest, town)
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
|
||||
--- Get table of all towns, optionally of a given type.
|
||||
-- @param #TOWNS self
|
||||
-- @return #table Table of towns. Each element is of type #TOWN.Town.
|
||||
function TOWNS:GetTowns()
|
||||
return self.towns
|
||||
end
|
||||
|
||||
--- Add markers for all towns on the F10 map.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town Town (Optional) Only this specifc town.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:MarkerShow(Town)
|
||||
|
||||
for _,_town in pairs(self.towns) do
|
||||
local town=_town --#TOWNS.Town
|
||||
if Town==nil or Town.name==town.name then
|
||||
local text=self:_GetMarkerText(town)
|
||||
local coord=town.coordinate
|
||||
if town.markerID then
|
||||
UTILS.RemoveMark(town.markerID)
|
||||
end
|
||||
town.markerID=coord:MarkToAll(text)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove markers of all towns from the F10 map.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town Town (Optional) Only this specifc town.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:MarkerRemove(Town)
|
||||
|
||||
for _,_town in pairs(self.towns) do
|
||||
local town=_town --#TOWNS.Town
|
||||
if Town==nil or Town.name==town.name then
|
||||
if town.markerID then
|
||||
UTILS.RemoveMark(town.markerID)
|
||||
town.markerID=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return #string Marker text.
|
||||
function TOWNS:_GetMarkerText(town)
|
||||
|
||||
local text=string.format("Town %s", town.name)
|
||||
--text=text..string.format("\nCallsign: %s", tostring(beacon.callsign))
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -839,8 +839,8 @@ function ARMYGROUP:Status()
|
||||
local ammo=self:GetAmmoElement(element)
|
||||
|
||||
-- Output text for element.
|
||||
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d, cargo=%d/%d kg",
|
||||
i, name, status, life, life0, ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
|
||||
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, cannons=%d, rockets=%d, missiles=%d, cargo=%d/%d kg",
|
||||
i, name, status, life, life0, ammo.Guns, ammo.Cannons, ammo.Rockets, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
|
||||
end
|
||||
if #self.elements==0 then
|
||||
text=text.." none!"
|
||||
@@ -1571,7 +1571,7 @@ end
|
||||
-- @param Core.Zone#ZONE Zone The zone to return to.
|
||||
-- @param #number Formation Formation of the group.
|
||||
function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
|
||||
self:T2(self.lid.."onafterRTZ")
|
||||
self:T(self.lid.."onafterRTZ")
|
||||
|
||||
-- Zone.
|
||||
local zone=Zone or self.homezone
|
||||
@@ -1841,8 +1841,6 @@ function ARMYGROUP:_UpdateEngageTarget()
|
||||
-- Check if target moved more than 100 meters or we do not have line of sight.
|
||||
if dist>100 or los==false then
|
||||
|
||||
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
|
||||
|
||||
-- Update new position.
|
||||
self.engage.Coordinate:UpdateFromVec3(vec3)
|
||||
|
||||
@@ -1852,13 +1850,14 @@ function ARMYGROUP:_UpdateEngageTarget()
|
||||
-- Remove current waypoint
|
||||
self:RemoveWaypointByID(self.engage.Waypoint.uid)
|
||||
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
|
||||
-- Get new coordinate where to go.
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.95)
|
||||
|
||||
-- Add waypoint after current.
|
||||
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true)
|
||||
|
||||
-- Set if we want to resume route after reaching the detour waypoint.
|
||||
self.engage.Waypoint.detour=0
|
||||
self.engage.Waypoint.detour=1
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -324,6 +324,10 @@
|
||||
--
|
||||
-- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function.
|
||||
--
|
||||
-- ## NAVALENGAGEMENT
|
||||
--
|
||||
-- A naval engagement mission can be created with the @{#AUFTRAG.NewNAVALENGAGEMENT}() function.
|
||||
--
|
||||
-- # Assigning Missions
|
||||
--
|
||||
-- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER.
|
||||
@@ -443,6 +447,7 @@ _AUFTRAGSNR=0
|
||||
-- @field #string HOVER Hover.
|
||||
-- @field #string LANDATCOORDINATE Land at coordinate.
|
||||
-- @field #string GROUNDATTACK Ground attack.
|
||||
-- @field #string NAVALENGAGEMENT Naval engagement (similar to GROUNDATTACK).
|
||||
-- @field #string CARGOTRANSPORT Cargo transport.
|
||||
-- @field #string RELOCATECOHORT Relocate a cohort from one legion to another.
|
||||
-- @field #string AIRDEFENSE Air defense.
|
||||
@@ -491,6 +496,7 @@ AUFTRAG.Type={
|
||||
HOVER="Hover",
|
||||
LANDATCOORDINATE="Land at Coordinate",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
NAVALENGAGEMENT="Naval Engagement",
|
||||
CARGOTRANSPORT="Cargo Transport",
|
||||
RELOCATECOHORT="Relocate Cohort",
|
||||
AIRDEFENSE="Air Defence",
|
||||
@@ -515,6 +521,7 @@ AUFTRAG.Type={
|
||||
-- @field #string BARRAGE Barrage.
|
||||
-- @field #string HOVER Hover.
|
||||
-- @field #string GROUNDATTACK Ground attack.
|
||||
-- @field #string NAVALENGAGEMENT Naval engagement.
|
||||
-- @field #string FERRY Ferry mission.
|
||||
-- @field #string RELOCATECOHORT Relocate cohort.
|
||||
-- @field #string AIRDEFENSE Air defense.
|
||||
@@ -537,6 +544,7 @@ AUFTRAG.SpecialTask={
|
||||
ARMORATTACK="AmorAttack",
|
||||
HOVER="Hover",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
NAVALENGAGEMENT="Naval Engagement",
|
||||
FERRY="Ferry",
|
||||
RELOCATECOHORT="Relocate Cohort",
|
||||
AIRDEFENSE="Air Defense",
|
||||
@@ -666,7 +674,7 @@ AUFTRAG.Category={
|
||||
|
||||
--- AUFTRAG class version.
|
||||
-- @field #string version
|
||||
AUFTRAG.version="1.2.1"
|
||||
AUFTRAG.version="1.3.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -2210,7 +2218,7 @@ end
|
||||
-- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target Center of the firing solution.
|
||||
-- @param #number Nshots Number of shots to be fired. Default `#nil`.
|
||||
-- @param #number Nshots Number of shots to be fired. Default `#nil`. If value is in (0,1), it is interpreted as per cent of available ammo.
|
||||
-- @param #number Radius Radius of the shells in meters. Default 100 meters.
|
||||
-- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`.
|
||||
-- @return #AUFTRAG self
|
||||
@@ -2382,11 +2390,13 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation)
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack.
|
||||
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack at their own discretion.
|
||||
-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft).
|
||||
-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
|
||||
-- @param #number Speed Speed in knots. Default max.
|
||||
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`.
|
||||
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. Only working for ground, not naval!
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
|
||||
@@ -2413,6 +2423,38 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[NAVAL]** Create a NAVALENGAGEMENT mission. Naval group(s) will go to a target object and attack at their own discretion.
|
||||
-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft).
|
||||
-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
|
||||
-- @param #number Speed Speed in knots. Default max.
|
||||
-- @param #number Depth The attack depth in meters. Only for submarines!
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewNAVALENGAGEMENT(Target, Speed, Depth)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.NAVALENGAGEMENT)
|
||||
|
||||
mission:_TargetFromObject(Target)
|
||||
|
||||
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.NAVALENGAGEMENT)
|
||||
|
||||
-- Defaults.
|
||||
mission.optionROE=ENUMS.ROE.OpenFire
|
||||
mission.optionAlarm=ENUMS.AlarmState.Auto
|
||||
mission.missionFraction=0.70
|
||||
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
|
||||
mission.missionAltitude=Depth or 0
|
||||
|
||||
mission.categories={AUFTRAG.Category.NAVAL}
|
||||
|
||||
mission.DCStask=mission:GetDCSMissionTask()
|
||||
|
||||
mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
|
||||
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[AIR, GROUND, NAVAL]** Create a RECON mission.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Set#SET_ZONE ZoneSet The recon zones.
|
||||
@@ -2705,33 +2747,35 @@ function AUFTRAG:NewFromTarget(Target, MissionType)
|
||||
local mission=nil --#AUFTRAG
|
||||
|
||||
if MissionType==AUFTRAG.Type.ANTISHIP then
|
||||
mission=self:NewANTISHIP(Target, Altitude)
|
||||
mission=self:NewANTISHIP(Target)
|
||||
elseif MissionType==AUFTRAG.Type.ARTY then
|
||||
mission=self:NewARTY(Target, Nshots, Radius)
|
||||
mission=self:NewARTY(Target, 0.3) -- use 30% of the available ammo
|
||||
elseif MissionType==AUFTRAG.Type.BAI then
|
||||
mission=self:NewBAI(Target, Altitude)
|
||||
mission=self:NewBAI(Target)
|
||||
elseif MissionType==AUFTRAG.Type.BOMBCARPET then
|
||||
mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength)
|
||||
mission=self:NewBOMBCARPET(Target)
|
||||
elseif MissionType==AUFTRAG.Type.BOMBING then
|
||||
mission=self:NewBOMBING(Target, Altitude)
|
||||
mission=self:NewBOMBING(Target)
|
||||
elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then
|
||||
mission=self:NewBOMBRUNWAY(Target, Altitude)
|
||||
mission=self:NewBOMBRUNWAY(Target)
|
||||
elseif MissionType==AUFTRAG.Type.STRAFING then
|
||||
mission=self:NewSTRAFING(Target, Altitude)
|
||||
mission=self:NewSTRAFING(Target)
|
||||
elseif MissionType==AUFTRAG.Type.CAS then
|
||||
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, Target:GetAverageCoordinate(), Heading, Leg, TargetTypes)
|
||||
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), nil, nil, Target:GetAverageCoordinate())
|
||||
elseif MissionType==AUFTRAG.Type.CASENHANCED then
|
||||
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes)
|
||||
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000))
|
||||
elseif MissionType==AUFTRAG.Type.INTERCEPT then
|
||||
mission=self:NewINTERCEPT(Target)
|
||||
elseif MissionType==AUFTRAG.Type.SEAD then
|
||||
mission=self:NewSEAD(Target, Altitude)
|
||||
mission=self:NewSEAD(Target)
|
||||
elseif MissionType==AUFTRAG.Type.STRIKE then
|
||||
mission=self:NewSTRIKE(Target, Altitude)
|
||||
mission=self:NewSTRIKE(Target)
|
||||
elseif MissionType==AUFTRAG.Type.ARMORATTACK then
|
||||
mission=self:NewARMORATTACK(Target, Speed)
|
||||
mission=self:NewARMORATTACK(Target)
|
||||
elseif MissionType==AUFTRAG.Type.GROUNDATTACK then
|
||||
mission=self:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
mission=self:NewGROUNDATTACK(Target)
|
||||
elseif MissionType==AUFTRAG.Type.NAVALENGAGEMENT then
|
||||
mission=self:NewNAVALENGAGEMENT(Target)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
@@ -2848,7 +2892,7 @@ function AUFTRAG:NewAUTO(EngageGroup)
|
||||
if auftrag==AUFTRAG.Type.ANTISHIP then
|
||||
mission=AUFTRAG:NewANTISHIP(Target)
|
||||
elseif auftrag==AUFTRAG.Type.ARTY then
|
||||
mission=AUFTRAG:NewARTY(Target)
|
||||
mission=AUFTRAG:NewARTY(Target, 0.2)
|
||||
elseif auftrag==AUFTRAG.Type.AWACS then
|
||||
mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg)
|
||||
elseif auftrag==AUFTRAG.Type.BAI then
|
||||
@@ -4399,7 +4443,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
|
||||
-- Group info.
|
||||
if self.verbose>=3 then
|
||||
-- Data on assigned groups.
|
||||
local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0)
|
||||
local text=string.format("Assets [N=%d, Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0)
|
||||
for i,_asset in pairs(self.assets or {}) do
|
||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s", i, asset.spawngroupname, tostring(asset.spawned), tostring(asset.requested), tostring(asset.reserved))
|
||||
@@ -4440,7 +4484,7 @@ function AUFTRAG:Evaluate()
|
||||
local owndamage=self.Ncasualties/self.Nelements*100
|
||||
|
||||
-- Current number of mission targets.
|
||||
local Ntargets=self:CountMissionTargets()
|
||||
local Ntargets=self:CountMissionTargets(true)
|
||||
local Ntargets0=self:GetTargetInitialNumber()
|
||||
|
||||
local Life=self:GetTargetLife()
|
||||
@@ -4891,16 +4935,25 @@ function AUFTRAG:CheckGroupsDone()
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce))
|
||||
return false
|
||||
end
|
||||
|
||||
local NopsgroupsAlive=self:CountOpsGroups()
|
||||
local NopsgroupsDone=self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.DONE)+self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.CANCELLED)
|
||||
|
||||
-- It could be that all groups were destroyed on the way to the mission execution waypoint.
|
||||
-- TODO: would be better to check if everybody is dead by now.
|
||||
if self:IsStarted() and self:CountOpsGroups()==0 then
|
||||
if self:IsStarted() and NopsgroupsAlive==0 then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState()))
|
||||
return true
|
||||
end
|
||||
|
||||
if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and self:CountOpsGroups()>0 then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState()))
|
||||
-- Every group alive is done or cancelled
|
||||
if NopsgroupsAlive==NopsgroupsDone then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] but all groups [=%d] are done or cancelled. Mission DONE!", self.status, self:GetState(), NopsgroupsAlive))
|
||||
return true
|
||||
end
|
||||
|
||||
if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and NopsgroupsAlive>0 then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState()))
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -5494,8 +5547,9 @@ end
|
||||
|
||||
--- Count alive mission targets.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
|
||||
-- @return #number Number of alive target units.
|
||||
function AUFTRAG:CountMissionTargets()
|
||||
function AUFTRAG:CountMissionTargets(OnlyReallyAlive)
|
||||
|
||||
local N=0
|
||||
|
||||
@@ -5503,7 +5557,7 @@ function AUFTRAG:CountMissionTargets()
|
||||
local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition, true) or nil
|
||||
|
||||
if self.engageTarget then
|
||||
N=self.engageTarget:CountTargets(Coalitions)
|
||||
N=self.engageTarget:CountTargets(Coalitions, OnlyReallyAlive)
|
||||
end
|
||||
|
||||
return N
|
||||
@@ -6642,6 +6696,26 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
|
||||
elseif self.type==AUFTRAG.Type.NAVALENGAGEMENT then
|
||||
|
||||
---------------------------
|
||||
-- NAVAL ENGAGEMENT Mission --
|
||||
---------------------------
|
||||
|
||||
local DCStask={}
|
||||
|
||||
DCStask.id=AUFTRAG.SpecialTask.NAVALENGAGEMENT
|
||||
|
||||
-- We create a "fake" DCS task and pass the parameters to the NAVYGROUP.
|
||||
local param={}
|
||||
param.target=self:GetTargetData()
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
param.altitude=self.missionAltitude or 0
|
||||
|
||||
DCStask.params=param
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
|
||||
elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
|
||||
|
||||
-------------------------
|
||||
|
||||
@@ -599,7 +599,80 @@ function BRIGADE:onafterStatus(From, Event, To)
|
||||
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
|
||||
end
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
end
|
||||
|
||||
if self.verbose>=3 then
|
||||
|
||||
-- Count numbers
|
||||
local Ntotal=0
|
||||
local Nspawned=0
|
||||
local Nrequested=0
|
||||
local Nreserved=0
|
||||
local Nstock=0
|
||||
|
||||
local text="\n===========================================\n"
|
||||
text=text.."Assets:"
|
||||
local legion=self --Ops.Legion#LEGION
|
||||
|
||||
for _,_cohort in pairs(legion.cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
for _,_asset in pairs(cohort.assets) do
|
||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
|
||||
local state="In Stock"
|
||||
if asset.flightgroup then
|
||||
state=asset.flightgroup:GetState()
|
||||
local mission=legion:GetAssetCurrentMission(asset)
|
||||
if mission then
|
||||
state=state..string.format(", Mission \"%s\" [%s]", mission:GetName(), mission:GetType())
|
||||
end
|
||||
else
|
||||
if asset.spawned then
|
||||
env.info("FF ERROR: asset has opsgroup but is NOT spawned!")
|
||||
end
|
||||
if asset.requested and asset.isReserved then
|
||||
env.info("FF ERROR: asset is requested and reserved. Should not be both!")
|
||||
state="Reserved+Requested!"
|
||||
elseif asset.isReserved then
|
||||
state="Reserved"
|
||||
elseif asset.requested then
|
||||
state="Requested"
|
||||
end
|
||||
end
|
||||
|
||||
-- Text.
|
||||
text=text..string.format("\n[UID=%03d] %s Legion=%s [%s]: State=%s [RID=%s]",
|
||||
asset.uid, asset.spawngroupname, legion.alias, cohort.name, state, tostring(asset.rid))
|
||||
|
||||
|
||||
if asset.spawned then
|
||||
Nspawned=Nspawned+1
|
||||
end
|
||||
if asset.requested then
|
||||
Nrequested=Nrequested+1
|
||||
end
|
||||
if asset.isReserved then
|
||||
Nreserved=Nreserved+1
|
||||
end
|
||||
if not (asset.spawned or asset.requested or asset.isReserved) then
|
||||
Nstock=Nstock+1
|
||||
end
|
||||
|
||||
Ntotal=Ntotal+1
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
text=text.."\n-------------------------------------------"
|
||||
text=text..string.format("\nNstock = %d", Nstock)
|
||||
text=text..string.format("\nNreserved = %d", Nreserved)
|
||||
text=text..string.format("\nNrequested = %d", Nrequested)
|
||||
text=text..string.format("\nNspawned = %d", Nspawned)
|
||||
text=text..string.format("\nNtotal = %d (=%d)", Ntotal, Nstock+Nspawned+Nrequested+Nreserved)
|
||||
text=text.."\n==========================================="
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
-- @field #number Nsuccess Number of successful missions.
|
||||
-- @field #number Nfailure Number of failed mission.
|
||||
-- @field #table assetNumbers Asset numbers. Each entry is a table of data type `#CHIEF.AssetNumber`.
|
||||
-- @extends Ops.Intel#INTEL
|
||||
-- @extends Ops.Intelligence#INTEL
|
||||
|
||||
--- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower
|
||||
--
|
||||
@@ -48,7 +48,7 @@
|
||||
--
|
||||
-- # Territory
|
||||
--
|
||||
-- The chief class allows you to define boarder zones, conflict zones and attack zones.
|
||||
-- The chief class allows you to define border zones, conflict zones and attack zones.
|
||||
--
|
||||
-- ## Border Zones
|
||||
--
|
||||
@@ -332,7 +332,7 @@ CHIEF.Strategy = {
|
||||
|
||||
--- CHIEF class version.
|
||||
-- @field #string version
|
||||
CHIEF.version="0.6.1"
|
||||
CHIEF.version="0.7.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -2897,6 +2897,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
|
||||
|
||||
else
|
||||
|
||||
-- Everything else
|
||||
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100))
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
|
||||
@@ -2912,6 +2914,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
|
||||
---
|
||||
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ANTISHIP, 100))
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.NAVALENGAGEMENT, 50))
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: Unknown Group category!")
|
||||
|
||||
@@ -629,32 +629,42 @@ end
|
||||
--- Remove assets from pool. Not that assets must not be spawned or already reserved or requested.
|
||||
-- @param #COHORT self
|
||||
-- @param #number N Number of assets to be removed. Default 1.
|
||||
-- @param #number Delay Delay in seconds before assets are removed.
|
||||
-- @return #COHORT self
|
||||
function COHORT:RemoveAssets(N)
|
||||
function COHORT:RemoveAssets(N, Delay)
|
||||
self:T2(self.lid..string.format("Remove %d assets of Cohort", N))
|
||||
|
||||
N=N or 1
|
||||
|
||||
local n=0
|
||||
for i=#self.assets,1,-1 do
|
||||
local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
if Delay and Delay>0 then
|
||||
-- Delayed call
|
||||
self:ScheduleOnce(Delay, COHORT.RemoveAssets, self, N, 0)
|
||||
else
|
||||
|
||||
self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname))
|
||||
if not (asset.requested or asset.spawned or asset.isReserved) then
|
||||
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
|
||||
table.remove(self.assets, i)
|
||||
n=n+1
|
||||
else
|
||||
self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname))
|
||||
N=N or 1
|
||||
|
||||
local n=0
|
||||
for i=#self.assets,1,-1 do
|
||||
local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
|
||||
self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname))
|
||||
if not (asset.requested or asset.spawned or asset.isReserved) then
|
||||
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
|
||||
-- Remove from warehouse and warehouse DB
|
||||
asset.legion:_DeleteStockItem(asset)
|
||||
table.remove(self.assets, i)
|
||||
n=n+1
|
||||
else
|
||||
self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname))
|
||||
end
|
||||
|
||||
if n>=N then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if n>=N then
|
||||
break
|
||||
end
|
||||
self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets))
|
||||
|
||||
end
|
||||
|
||||
self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1901,7 +1901,7 @@ function LEGION:_TacticalOverview()
|
||||
for _,mtype in pairs(AUFTRAG.Type) do
|
||||
local n=self:CountMissionsInQueue(mtype)
|
||||
if n>0 then
|
||||
local N=self:CountMissionsInQueue(mtype)
|
||||
local N=self:CountMissionsInQueue(mtype, true)
|
||||
text=text..string.format(" - %s: %d [Running=%d]\n", mtype, n, N)
|
||||
end
|
||||
end
|
||||
@@ -2057,18 +2057,23 @@ end
|
||||
--- Count missions in mission queue.
|
||||
-- @param #LEGION self
|
||||
-- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`.
|
||||
-- @param #boolean OnlyRunning If `true`, only count running missions.
|
||||
-- @return #number Number of missions that are not over yet.
|
||||
function LEGION:CountMissionsInQueue(MissionTypes)
|
||||
function LEGION:CountMissionsInQueue(MissionTypes, OnlyRunning)
|
||||
|
||||
MissionTypes=MissionTypes or AUFTRAG.Type
|
||||
|
||||
local N=0
|
||||
for _,_mission in pairs(self.missionqueue) do
|
||||
local mission=_mission --Ops.Auftrag#AUFTRAG
|
||||
|
||||
if (not OnlyRunning) or (mission.statusLegion~=AUFTRAG.Status.PLANNED) then
|
||||
|
||||
-- Check if this mission type is requested.
|
||||
if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then
|
||||
N=N+1
|
||||
-- Check if this mission type is requested.
|
||||
if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then
|
||||
N=N+1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -86,12 +86,14 @@ NAVYGROUP = {
|
||||
-- @field Ops.Target#TARGET Target The target.
|
||||
-- @field Core.Point#COORDINATE Coordinate Last known coordinate of the target.
|
||||
-- @field Ops.OpsGroup#OPSGROUP.Waypoint Waypoint the waypoint created to go to the target.
|
||||
-- @field #number Speed Speed in knots.
|
||||
-- @field #number Depth Depth of the engagement (submarines).
|
||||
-- @field #number roe ROE backup.
|
||||
-- @field #number alarmstate Alarm state backup.
|
||||
|
||||
--- NavyGroup version.
|
||||
-- @field #string version
|
||||
NAVYGROUP.version="1.0.3"
|
||||
NAVYGROUP.version="1.0.4"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1081,7 +1083,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
|
||||
text=text..string.format("Elements = %d\n", #self.elements)
|
||||
text=text..string.format("Waypoints = %d\n", #self.waypoints)
|
||||
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
|
||||
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
|
||||
text=text..string.format("Ammo = %d (G=%d/C=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Cannons, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
|
||||
text=text..string.format("FSM state = %s\n", self:GetState())
|
||||
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
|
||||
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
|
||||
@@ -1602,8 +1604,10 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Group#GROUP Group the group to be engaged.
|
||||
function NAVYGROUP:onafterEngageTarget(From, Event, To, Target)
|
||||
-- @param Ops.Target#TARGET Target The target to be engaged. Can also be a GROUP or UNIT object.
|
||||
-- @param #number Speed Attack speed in knots.
|
||||
-- @param #number Depth The depth in meters. Only for submarins.
|
||||
function NAVYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Depth)
|
||||
self:T(self.lid.."Engaging Target")
|
||||
|
||||
if Target:IsInstanceOf("TARGET") then
|
||||
@@ -1615,11 +1619,9 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target)
|
||||
-- Target coordinate.
|
||||
self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
|
||||
|
||||
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
|
||||
-- Get a coordinate close to the target.
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8)
|
||||
|
||||
|
||||
|
||||
-- Backup ROE and alarm state.
|
||||
self.engage.roe=self:GetROE()
|
||||
self.engage.alarmstate=self:GetAlarmstate()
|
||||
@@ -1629,11 +1631,17 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target)
|
||||
self:SwitchROE(ENUMS.ROE.OpenFire)
|
||||
|
||||
-- ID of current waypoint.
|
||||
local uid=self:GetWaypointCurrent().uid
|
||||
local uid=self:GetWaypointCurrentUID()
|
||||
|
||||
-- Set formation.
|
||||
self.engage.Depth=Depth or 0
|
||||
|
||||
-- Set speed.
|
||||
self.engage.Speed=Speed
|
||||
|
||||
-- Add waypoint after current.
|
||||
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
|
||||
|
||||
self.engage.Waypoint=self:AddWaypoint(intercoord, Speed, uid, Depth, true)
|
||||
|
||||
-- Set if we want to resume route after reaching the detour waypoint.
|
||||
self.engage.Waypoint.detour=1
|
||||
|
||||
@@ -1656,24 +1664,22 @@ function NAVYGROUP:_UpdateEngageTarget()
|
||||
-- Check if target moved more than 100 meters.
|
||||
if dist>100 then
|
||||
|
||||
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
|
||||
|
||||
-- Update new position.
|
||||
self.engage.Coordinate:UpdateFromVec3(vec3)
|
||||
|
||||
-- ID of current waypoint.
|
||||
local uid=self:GetWaypointCurrent().uid
|
||||
local uid=self:GetWaypointCurrentUID()
|
||||
|
||||
-- Remove current waypoint
|
||||
self:RemoveWaypointByID(self.engage.Waypoint.uid)
|
||||
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8)
|
||||
|
||||
-- Add waypoint after current.
|
||||
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
|
||||
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Depth, true)
|
||||
|
||||
-- Set if we want to resume route after reaching the detour waypoint.
|
||||
self.engage.Waypoint.detour=0
|
||||
self.engage.Waypoint.detour=1
|
||||
|
||||
end
|
||||
|
||||
@@ -1709,8 +1715,8 @@ function NAVYGROUP:onafterDisengage(From, Event, To)
|
||||
local task=self:GetTaskCurrent()
|
||||
|
||||
-- Get if current task is ground attack.
|
||||
if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
|
||||
self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!")
|
||||
if task and (task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT) then
|
||||
self:T(self.lid.."Disengage with current task GROUNDATTACK/NAVALENGAGEMENT ==> Task Done!")
|
||||
self:TaskDone(task)
|
||||
end
|
||||
|
||||
|
||||
@@ -405,7 +405,9 @@ OPSGROUP.TaskType={
|
||||
--- Ammo data.
|
||||
-- @type OPSGROUP.Ammo
|
||||
-- @field #number Total Total amount of ammo.
|
||||
-- @field #number Guns Amount of gun shells.
|
||||
-- @field #number Shells Amount of shells (guns + cannons).
|
||||
-- @field #number Guns Amount of gun shells (caliber < 25).
|
||||
-- @field #number Cannons Amount of cannon shells (caliber >= 25).
|
||||
-- @field #number Bombs Amount of bombs.
|
||||
-- @field #number Rockets Amount of rockets.
|
||||
-- @field #number Torpedos Amount of torpedos.
|
||||
@@ -1189,8 +1191,8 @@ function OPSGROUP:GetDCSObject()
|
||||
return self.dcsgroup
|
||||
end
|
||||
|
||||
--- Set detection on or off.
|
||||
-- If detection is on, detected targets of the group will be evaluated and FSM events triggered.
|
||||
--- Make a target (unit, group, opsgroup) known to this group.
|
||||
-- This is useing the DCS function `controller.knowTarget`.
|
||||
-- @param #OPSGROUP self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE TargetObject The target object.
|
||||
-- @param #boolean KnowType Make type known.
|
||||
@@ -4513,6 +4515,25 @@ function OPSGROUP:_UpdateTask(Task, Mission)
|
||||
if target then
|
||||
self:EngageTarget(target, speed, Task.dcstask.params.formation)
|
||||
end
|
||||
|
||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then
|
||||
|
||||
---
|
||||
-- Task "Naval Engagement" Mission.
|
||||
---
|
||||
|
||||
-- Engage target.
|
||||
local target=Task.dcstask.params.target --Ops.Target#TARGET
|
||||
|
||||
-- Set speed. Default max.
|
||||
local speed=self.speedMax and UTILS.KmphToKnots(self.speedMax) or nil
|
||||
if Task.dcstask.params.speed then
|
||||
speed=UTILS.MpsToKnots(Task.dcstask.params.speed)
|
||||
end
|
||||
|
||||
if target then
|
||||
self:EngageTarget(target, speed, Task.dcstask.params.altitude)
|
||||
end
|
||||
|
||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
|
||||
|
||||
@@ -4739,7 +4760,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
|
||||
elseif weaponType==ENUMS.WeaponFlag.AnyRocket then
|
||||
nAmmo=ammo.Rockets
|
||||
elseif weaponType==ENUMS.WeaponFlag.Cannons then
|
||||
nAmmo=ammo.Guns
|
||||
nAmmo=ammo.Cannons
|
||||
end
|
||||
|
||||
--TODO: Update target location while we're at it anyway.
|
||||
@@ -4886,7 +4907,7 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task)
|
||||
done=true
|
||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then
|
||||
done=true
|
||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then
|
||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then
|
||||
done=true
|
||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.NOTHING then
|
||||
done=true
|
||||
@@ -5718,7 +5739,7 @@ end
|
||||
function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Mission %s DONE!", Mission.name)
|
||||
local text=string.format("Mission DONE %s!", Mission.name)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- Set group status.
|
||||
@@ -6231,11 +6252,12 @@ function OPSGROUP:RouteToMission(mission, delay)
|
||||
self:T(self.lid.."Already in mission zone ==> TaskExecute()")
|
||||
self:TaskExecute(waypointtask)
|
||||
-- TODO: Calling PassingWaypoint here is probably better as it marks the mission waypoint as passed!
|
||||
--self:PassingWaypoint(waypoint)
|
||||
self:PassingWaypoint(waypoint)
|
||||
return
|
||||
elseif d<25 then
|
||||
self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
|
||||
self:TaskExecute(waypointtask)
|
||||
self:PassingWaypoint(waypoint)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -7850,7 +7872,7 @@ function OPSGROUP:_Spawn(Delay, Template)
|
||||
self:ScheduleOnce(Delay, OPSGROUP._Spawn, self, 0, Template)
|
||||
else
|
||||
-- Debug output.
|
||||
self:T2({Template=Template})
|
||||
--self:T2({Template=Template})
|
||||
|
||||
if self:IsArmygroup() and self.ValidateAndRepositionGroundUnits then
|
||||
UTILS.ValidateAndRepositionGroundUnits(Template.units)
|
||||
@@ -11190,11 +11212,11 @@ function OPSGROUP:_CheckAmmoStatus()
|
||||
self:OutOfAmmo()
|
||||
end
|
||||
|
||||
-- Guns.
|
||||
if self.outofGuns and ammo.Guns>0 then
|
||||
-- Guns (changed to shells)
|
||||
if self.outofGuns and ammo.Shells>0 then
|
||||
self.outofGuns=false
|
||||
end
|
||||
if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then
|
||||
if ammo.Shells==0 and self.ammo.Shells>0 and not self.outofGuns then
|
||||
self.outofGuns=true
|
||||
self:OutOfGuns()
|
||||
end
|
||||
@@ -13370,7 +13392,9 @@ function OPSGROUP:GetAmmoTot()
|
||||
|
||||
local Ammo={} --#OPSGROUP.Ammo
|
||||
Ammo.Total=0
|
||||
Ammo.Shells=0
|
||||
Ammo.Guns=0
|
||||
Ammo.Cannons=0
|
||||
Ammo.Rockets=0
|
||||
Ammo.Bombs=0
|
||||
Ammo.Torpedos=0
|
||||
@@ -13391,7 +13415,9 @@ function OPSGROUP:GetAmmoTot()
|
||||
|
||||
-- Add up total.
|
||||
Ammo.Total=Ammo.Total+ammo.Total
|
||||
Ammo.Shells=Ammo.Shells+ammo.Shells
|
||||
Ammo.Guns=Ammo.Guns+ammo.Guns
|
||||
Ammo.Cannons=Ammo.Cannons+ammo.Cannons
|
||||
Ammo.Rockets=Ammo.Rockets+ammo.Rockets
|
||||
Ammo.Bombs=Ammo.Bombs+ammo.Bombs
|
||||
Ammo.Torpedos=Ammo.Torpedos+ammo.Torpedos
|
||||
@@ -13424,6 +13450,8 @@ function OPSGROUP:GetAmmoUnit(unit, display)
|
||||
-- Init counter.
|
||||
local nammo=0
|
||||
local nshells=0
|
||||
local nguns=0
|
||||
local ncannons=0
|
||||
local nrockets=0
|
||||
local nmissiles=0
|
||||
local nmissilesAA=0
|
||||
@@ -13446,10 +13474,10 @@ function OPSGROUP:GetAmmoUnit(unit, display)
|
||||
local ammotable=unit:GetAmmo()
|
||||
|
||||
if ammotable then
|
||||
|
||||
local weapons=#ammotable
|
||||
|
||||
--self:I(ammotable)
|
||||
--self:I(ammotable)
|
||||
--UTILS.PrintTableToLog(ammotable)
|
||||
|
||||
-- Loop over all weapons.
|
||||
for w=1,weapons do
|
||||
@@ -13457,9 +13485,9 @@ function OPSGROUP:GetAmmoUnit(unit, display)
|
||||
-- Number of current weapon.
|
||||
local Nammo=ammotable[w]["count"]
|
||||
|
||||
-- Range in meters. Seems only to exist for missiles (not shells).
|
||||
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
|
||||
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
|
||||
-- Range in meters. Seems only to exist for missiles (not shells).
|
||||
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
|
||||
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
|
||||
|
||||
-- Type name of current weapon.
|
||||
local Tammo=ammotable[w]["desc"]["typeName"]
|
||||
@@ -13481,6 +13509,16 @@ function OPSGROUP:GetAmmoUnit(unit, display)
|
||||
|
||||
-- Add up all shells.
|
||||
nshells=nshells+Nammo
|
||||
|
||||
-- Add small and large caliber shells for guns and cannons
|
||||
if ammotable[w]["desc"]["warhead"] and ammotable[w]["desc"]["warhead"]["caliber"] then
|
||||
local caliber=ammotable[w]["desc"]["warhead"]["caliber"]
|
||||
if caliber<25 then
|
||||
nguns=nguns+Nammo
|
||||
else
|
||||
ncannons=ncannons+Nammo
|
||||
end
|
||||
end
|
||||
|
||||
-- Debug info.
|
||||
text=text..string.format("- %d shells of type %s, range=%d - %d meters\n", Nammo, _weaponName, rmin, rmax)
|
||||
@@ -13559,7 +13597,9 @@ function OPSGROUP:GetAmmoUnit(unit, display)
|
||||
|
||||
local ammo={} --#OPSGROUP.Ammo
|
||||
ammo.Total=nammo
|
||||
ammo.Guns=nshells
|
||||
ammo.Shells=nshells
|
||||
ammo.Guns=nguns
|
||||
ammo.Cannons=ncannons
|
||||
ammo.Rockets=nrockets
|
||||
ammo.Bombs=nbombs
|
||||
ammo.Torpedos=ntorps
|
||||
|
||||
@@ -2006,9 +2006,10 @@ end
|
||||
--- Count alive objects.
|
||||
-- @param #TARGET self
|
||||
-- @param #TARGET.Object Target Target objective.
|
||||
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
|
||||
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
|
||||
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
|
||||
-- @return #number Number of alive target objects.
|
||||
function TARGET:CountObjectives(Target, Coalitions)
|
||||
function TARGET:CountObjectives(Target, Coalitions, OnlyReallyAlive)
|
||||
|
||||
local N=0
|
||||
|
||||
@@ -2067,13 +2068,17 @@ function TARGET:CountObjectives(Target, Coalitions)
|
||||
|
||||
-- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate.
|
||||
-- This is also more consitent with the life and is alive status.
|
||||
N=N+1
|
||||
if not OnlyReallyAlive then
|
||||
N=N+1
|
||||
end
|
||||
|
||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
||||
|
||||
-- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate.
|
||||
-- This is also more consitent with the life and is alive status.
|
||||
N=N+1
|
||||
if not OnlyReallyAlive then
|
||||
N=N+1
|
||||
end
|
||||
|
||||
elseif Target.Type==TARGET.ObjectType.OPSZONE then
|
||||
|
||||
@@ -2093,15 +2098,16 @@ end
|
||||
--- Count alive targets.
|
||||
-- @param #TARGET self
|
||||
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
|
||||
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
|
||||
-- @return #number Number of alive target objects.
|
||||
function TARGET:CountTargets(Coalitions)
|
||||
function TARGET:CountTargets(Coalitions, OnlyReallyAlive)
|
||||
|
||||
local N=0
|
||||
|
||||
for _,_target in pairs(self.targets) do
|
||||
local Target=_target --#TARGET.Object
|
||||
|
||||
N=N+self:CountObjectives(Target, Coalitions)
|
||||
N=N+self:CountObjectives(Target, Coalitions, OnlyReallyAlive)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1816,3 +1816,28 @@ ENUMS.FARPObjectTypeNamesAndShape ={
|
||||
[ENUMS.FARPType.PADSINGLE] = { TypeName="FARP_SINGLE_01", ShapeName="FARP_SINGLE_01"},
|
||||
}
|
||||
|
||||
--- Radio frequency bands (HF, VHF, UHF)
|
||||
-- @type ENUMS.FrequencyBand
|
||||
-- @field #number HF High frequency
|
||||
-- @field #number VHF_LOW Very high frequency
|
||||
-- @field #number VHF_HI Very high frequency
|
||||
-- @field #number UHF Ultra high frequency
|
||||
ENUMS.FrequencyBand = {
|
||||
HF = 0,
|
||||
VHF_LOW = 1,
|
||||
VHF_HI = 2,
|
||||
UHF = 3,
|
||||
}
|
||||
|
||||
--- Radio modulation types (AM, FM)
|
||||
-- @type ENUMS.ModulationType
|
||||
-- @field #number AM Amplitude modulation
|
||||
-- @field #number FM Frequency modulation
|
||||
-- @field #number AMFM Amplitude and frequency modulation
|
||||
-- @field #number DISCARD Discard modulation
|
||||
ENUMS.ModulationType = {
|
||||
AM = 0,
|
||||
FM = 1,
|
||||
AMFM = 2,
|
||||
DISCARD = -1,
|
||||
}
|
||||
|
||||
@@ -3227,6 +3227,22 @@ function UTILS.BearingToCardinal(Heading)
|
||||
end
|
||||
end
|
||||
|
||||
--- Adjust given heading so that is is in [0, 360).
|
||||
-- @param #number Heading The heading in degrees.
|
||||
-- @return #number Adjust heading in [0,360).
|
||||
function UTILS.AdjustHeading360(Heading)
|
||||
|
||||
while Heading>=360 or Heading<0 do
|
||||
if Heading>=360 then
|
||||
Heading=Heading-360
|
||||
elseif Heading<0 then
|
||||
Heading=Heading+360
|
||||
end
|
||||
end
|
||||
|
||||
return Heading
|
||||
end
|
||||
|
||||
--- Create a BRAA NATO call string BRAA between two GROUP objects
|
||||
-- @param Wrapper.Group#GROUP FromGrp GROUP object
|
||||
-- @param Wrapper.Group#GROUP ToGrp GROUP object
|
||||
|
||||
@@ -1164,6 +1164,23 @@ function GROUP:GetVec3()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the current {@Core.Vector#VECTOR} of the first Unit in the GROUP.
|
||||
-- @param #GROUP self
|
||||
-- @return Core.Vector#VECTOR Vector of the first Unit of the GROUP or nil if cannot be found.
|
||||
function GROUP:GetVector()
|
||||
|
||||
-- Get first unit.
|
||||
local unit=self:GetUnit(1)
|
||||
|
||||
if unit then
|
||||
local vector=unit:GetVector()
|
||||
return vector
|
||||
end
|
||||
|
||||
self:E("ERROR: Cannot get Vector of group "..tostring(self.GroupName))
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the average Vec3 vector of the Units in the GROUP.
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#Vec3 Current Vec3 of the GROUP or nil if cannot be found.
|
||||
|
||||
@@ -219,6 +219,25 @@ function POSITIONABLE:GetOrientationZ()
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the vectors of the orientation of the object:
|
||||
-- X is the orientation parallel to the movement of the object, Z perpendicular and Y vertical orientation.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Vector#VECTOR X orientation, i.e. parallel to the direction of movement.
|
||||
-- @return Core.Vector#VECTOR Y orientation, i.e. vertical.
|
||||
-- @return Core.Vector#VECTOR Z orientation, i.e. perpendicular to the direction of movement.
|
||||
function POSITIONABLE:GetOrientationVectors()
|
||||
local position = self:GetPosition()
|
||||
if position then
|
||||
local vecx=VECTOR:NewFromVec(position.x)
|
||||
local vecy=VECTOR:NewFromVec(position.y)
|
||||
local vecz=VECTOR:NewFromVec(position.z)
|
||||
return vecx, vecy, vecz
|
||||
else
|
||||
BASE:E( { "Cannot GetOrientation", Positionable = self, Alive = self:IsAlive() } )
|
||||
return nil, nil, nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
|
||||
@@ -286,6 +305,27 @@ function POSITIONABLE:GetVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the @{Core.Vector#VECTOR} indicating the 3D position of the object on the map.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Vector#VECTOR The 3D vector.
|
||||
function POSITIONABLE:GetVector()
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
|
||||
local Vec3 = DCSPositionable:getPoint() -- DCS#Vec3
|
||||
|
||||
local vector=VECTOR:NewFromVec(Vec3)
|
||||
|
||||
return vector
|
||||
end
|
||||
|
||||
self:E( { "Cannot GetVec2", Positionable = self, Alive = self:IsAlive() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a COORDINATE object indicating the point in 2D of the POSITIONABLE within the mission.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Point#COORDINATE The 3D point vector of the POSITIONABLE.
|
||||
@@ -914,6 +954,24 @@ function POSITIONABLE:GetVelocityVec3()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the velocity vector.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Vector#VECTOR The velocity vector or `nil` if the object does not exist.
|
||||
function POSITIONABLE:GetVelocityVector()
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable and DCSPositionable:isExist() then
|
||||
local vec3 = DCSPositionable:getVelocity()
|
||||
local vector=VECTOR:NewFromVec(vec3)
|
||||
return vector
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetVelocityVector", Positionable = self, Alive = self:IsAlive() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get relative velocity with respect to another POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param #POSITIONABLE Positionable Other POSITIONABLE.
|
||||
|
||||
Reference in New Issue
Block a user