mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-26 14:55:24 +00:00
Astar
This commit is contained in:
@@ -21,10 +21,11 @@
|
|||||||
-- @field #ASTAR.Node endNode End node.
|
-- @field #ASTAR.Node endNode End node.
|
||||||
-- @field Core.Point#COORDINATE startCoord Start coordinate.
|
-- @field Core.Point#COORDINATE startCoord Start coordinate.
|
||||||
-- @field Core.Point#COORDINATE endCoord End coordinate.
|
-- @field Core.Point#COORDINATE endCoord End coordinate.
|
||||||
-- @field #func CheckNodeValid Function to check if a node is valid.
|
-- @field #func ValidNeighbourFunc Function to check if a node is valid.
|
||||||
|
-- @field #table ValidNeighbourArg Optional arguments passed to the valid neighbour function.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Be surprised!
|
--- When nothing goes right... Go left!
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -42,7 +43,6 @@ ASTAR = {
|
|||||||
Debug = nil,
|
Debug = nil,
|
||||||
lid = nil,
|
lid = nil,
|
||||||
nodes = {},
|
nodes = {},
|
||||||
CheckNodeValid = nil,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Defence condition.
|
--- Defence condition.
|
||||||
@@ -56,19 +56,20 @@ ASTAR.INF=1/0
|
|||||||
|
|
||||||
--- ASTAR class version.
|
--- ASTAR class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ASTAR.version="0.0.1"
|
ASTAR.version="0.1.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: A lot.
|
-- TODO: Add more valid neighbour functions.
|
||||||
|
-- TODO: Write docs.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Create a new ASTAR object and start the FSM.
|
--- Create a new ASTAR object.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @return #ASTAR self
|
-- @return #ASTAR self
|
||||||
function ASTAR:New()
|
function ASTAR:New()
|
||||||
@@ -76,6 +77,9 @@ function ASTAR:New()
|
|||||||
-- Inherit everything from INTEL class.
|
-- Inherit everything from INTEL class.
|
||||||
local self=BASE:Inherit(self, BASE:New()) --#ASTAR
|
local self=BASE:Inherit(self, BASE:New()) --#ASTAR
|
||||||
|
|
||||||
|
self.lid="ASTAR | "
|
||||||
|
|
||||||
|
self.Debug=true
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -95,7 +99,7 @@ function ASTAR:SetStartCoordinate(Coordinate)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set coordinate from where to go.
|
--- Set coordinate where you want to go.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param Core.Point#COORDINATE Coordinate end coordinate.
|
-- @param Core.Point#COORDINATE Coordinate end coordinate.
|
||||||
-- @return #ASTAR self
|
-- @return #ASTAR self
|
||||||
@@ -106,9 +110,9 @@ function ASTAR:SetEndCoordinate(Coordinate)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a node.
|
--- Create a node from a given coordinate.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param Core.Point#COORDINATE Coordinate The coordinate.
|
-- @param Core.Point#COORDINATE Coordinate The coordinate where to create the node.
|
||||||
-- @return #ASTAR.Node The node.
|
-- @return #ASTAR.Node The node.
|
||||||
function ASTAR:GetNodeFromCoordinate(Coordinate)
|
function ASTAR:GetNodeFromCoordinate(Coordinate)
|
||||||
|
|
||||||
@@ -121,9 +125,9 @@ function ASTAR:GetNodeFromCoordinate(Coordinate)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Add a node.
|
--- Add a node to the table of grid nodes.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param #ASTAR.Node Node The node to be added to the nodes table.
|
-- @param #ASTAR.Node Node The node to be added.
|
||||||
-- @return #ASTAR self
|
-- @return #ASTAR self
|
||||||
function ASTAR:AddNode(Node)
|
function ASTAR:AddNode(Node)
|
||||||
|
|
||||||
@@ -132,18 +136,171 @@ function ASTAR:AddNode(Node)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if the coordinate of a node has is at a valid surface type.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node Node The node to be added.
|
||||||
|
-- @param #table SurfaceTypes Surface types, for example `{land.SurfaceType.WATER}`. By default all surface types are valid.
|
||||||
|
-- @return #boolean If true, surface type of node is valid.
|
||||||
|
function ASTAR:CheckValidSurfaceType(Node, SurfaceTypes)
|
||||||
|
|
||||||
|
if SurfaceTypes then
|
||||||
|
|
||||||
|
if type(SurfaceTypes)~="table" then
|
||||||
|
SurfaceTypes={SurfaceTypes}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,surface in pairs(SurfaceTypes) do
|
||||||
|
if surface==Node.surfacetype then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set valid neighbours to require line of sight between two nodes.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #number CorridorWidth Width of LoS corridor in meters.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourLoS(CorridorWidth)
|
||||||
|
|
||||||
|
self:SetValidNeighbourFunction(ASTAR.LoS, CorridorWidth)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a function to determine if a neighbour of a node is valid.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #function NeighbourFunction Function that needs to return *true* for a neighbour to be valid.
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourFunction(NeighbourFunction, ...)
|
||||||
|
|
||||||
|
self.ValidNeighbourFunc=NeighbourFunction
|
||||||
|
|
||||||
|
self.ValidNeighbourArg={}
|
||||||
|
if arg then
|
||||||
|
self.ValidNeighbourArg=arg
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- User functions
|
-- Grid functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Find the closest node from a given coordinate.
|
--- Create a rectangular grid of nodes between star and end coordinate.
|
||||||
-- @param #ASTAR.Node nodeA
|
-- The coordinate system is oriented along the line between start and end point.
|
||||||
-- @param #ASTAR.Node nodeB
|
-- @param #ASTAR self
|
||||||
function ASTAR.LoS(nodeA, nodeB)
|
-- @param #table ValidSurfaceTypes Valid surface types. By default is all surfaces are allowed.
|
||||||
|
-- @param #number BoxHY Box "height" in meters along the y-coordinate. Default 40000 meters (40 km).
|
||||||
|
-- @param #number SpaceX Additional space in meters before start and after end coordinate. Default 10000 meters (10 km).
|
||||||
|
-- @param #number deltaX Increment in the direction of start to end coordinate in meters. Default 2000 meters.
|
||||||
|
-- @param #number deltaY Increment perpendicular to the direction of start to end coordinate in meters. Default is same as deltaX.
|
||||||
|
-- @param #boolean MarkGrid If true, create F10 map markers at grid nodes.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:CreateGrid(ValidSurfaceTypes, BoxHY, SpaceX, deltaX, deltaY, MarkGrid)
|
||||||
|
|
||||||
|
-- Note that internally
|
||||||
|
-- x coordinate is z: x-->z Line from start to end
|
||||||
|
-- y coordinate is x: y-->x Perpendicular
|
||||||
|
|
||||||
|
-- Grid length and width.
|
||||||
|
local Dz=SpaceX or 10000
|
||||||
|
local Dx=BoxHY and BoxHY/2 or 20000
|
||||||
|
|
||||||
|
-- Increments.
|
||||||
|
local dz=deltaX or 2000
|
||||||
|
local dx=deltaY or dz
|
||||||
|
|
||||||
|
-- Heading from start to end coordinate.
|
||||||
|
local angle=self.startCoord:HeadingTo(self.endCoord)
|
||||||
|
|
||||||
|
--Distance between start and end.
|
||||||
|
local dist=self.startCoord:Get2DDistance(self.endCoord)+2*Dz
|
||||||
|
|
||||||
|
-- Origin of map. Needed to translate back to wanted position.
|
||||||
|
local co=COORDINATE:New(0, 0, 0)
|
||||||
|
local do1=co:Get2DDistance(self.startCoord)
|
||||||
|
local ho1=co:HeadingTo(self.startCoord)
|
||||||
|
|
||||||
|
-- Start of grid.
|
||||||
|
local xmin=-Dx
|
||||||
|
local zmin=-Dz
|
||||||
|
|
||||||
|
-- Number of grid points.
|
||||||
|
local nz=dist/dz+1
|
||||||
|
local nx=2*Dx/dx+1
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("Building grid with nx=%d ny=%d => total=%d nodes", nx, nz, nx*nz)
|
||||||
|
self:I(self.lid..text)
|
||||||
|
MESSAGE:New(text, 10, "ASTAR"):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
|
||||||
|
-- Loop over x and z coordinate to create a 2D grid.
|
||||||
|
for i=1,nx do
|
||||||
|
|
||||||
|
-- x coordinate perpendicular to z.
|
||||||
|
local x=xmin+dx*(i-1)
|
||||||
|
|
||||||
|
for j=1,nz do
|
||||||
|
|
||||||
|
-- z coordinate connecting start and end.
|
||||||
|
local z=zmin+dz*(j-1)
|
||||||
|
|
||||||
|
-- Rotate 2D.
|
||||||
|
local vec3=UTILS.Rotate2D({x=x, y=0, z=z}, angle)
|
||||||
|
|
||||||
|
-- Coordinate of the node.
|
||||||
|
local c=COORDINATE:New(vec3.z, vec3.y, vec3.x):Translate(do1, ho1, true)
|
||||||
|
|
||||||
|
-- Create a node at this coordinate.
|
||||||
|
local node=self:GetNodeFromCoordinate(c)
|
||||||
|
|
||||||
|
-- Check if node has valid surface type.
|
||||||
|
if self:CheckValidSurfaceType(node, ValidSurfaceTypes) then
|
||||||
|
|
||||||
|
if MarkGrid then
|
||||||
|
c:MarkToAll(string.format("i=%d, j=%d surface=%d", i, j, node.surfacetype))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add node to grid.
|
||||||
|
self:AddNode(node)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("Done building grid!")
|
||||||
|
self:I(self.lid..text)
|
||||||
|
MESSAGE:New(text, 10, "ASTAR"):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Valid neighbour functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Function to check if two nodes have line of sight (LoS).
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @param #number corridor (Optional) Width of corridor in meters.
|
||||||
|
-- @return #boolean If true, two nodes have LoS.
|
||||||
|
function ASTAR.LoS(nodeA, nodeB, corridor)
|
||||||
|
|
||||||
local offset=0.1
|
local offset=0.1
|
||||||
|
|
||||||
local dx=200
|
local dx=corridor and corridor/2 or nil
|
||||||
local dy=dx
|
local dy=dx
|
||||||
|
|
||||||
local cA=nodeA.coordinate:SetAltitude(0, true)
|
local cA=nodeA.coordinate:SetAltitude(0, true)
|
||||||
@@ -151,7 +308,7 @@ function ASTAR.LoS(nodeA, nodeB)
|
|||||||
|
|
||||||
local los=cA:IsLOS(cB, offset)
|
local los=cA:IsLOS(cB, offset)
|
||||||
|
|
||||||
if los then
|
if los and corridor then
|
||||||
local heading=cA:HeadingTo(cB)
|
local heading=cA:HeadingTo(cB)
|
||||||
|
|
||||||
local Ap=cA:Translate(dx, heading+90)
|
local Ap=cA:Translate(dx, heading+90)
|
||||||
@@ -173,62 +330,9 @@ function ASTAR.LoS(nodeA, nodeB)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- User functions
|
-- Misc functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Find the closest node from a given coordinate.
|
|
||||||
-- @param #ASTAR self
|
|
||||||
-- @param #number DeltaX Increment in the direction of start to end coordinate in meters. Default 2000 meters.
|
|
||||||
-- @param #number DeltaY Increment perpendicular to the direction of start to end coordinate in meters. Default is same as DeltaX.
|
|
||||||
-- @return #ASTAR self
|
|
||||||
function ASTAR:CreateGrid()
|
|
||||||
|
|
||||||
local Dx=20000
|
|
||||||
local Dz=10000
|
|
||||||
local delta=2000
|
|
||||||
|
|
||||||
local angle=self.startCoord:HeadingTo(self.endCoord)
|
|
||||||
local dist=self.startCoord:Get2DDistance(self.endCoord)+2*Dz
|
|
||||||
|
|
||||||
local co=COORDINATE:New(0, 0, 0)
|
|
||||||
|
|
||||||
local do1=co:Get2DDistance(self.startCoord)
|
|
||||||
local ho1=co:HeadingTo(self.startCoord)
|
|
||||||
|
|
||||||
local xmin=-Dx
|
|
||||||
local zmin=-Dz
|
|
||||||
|
|
||||||
local nz=dist/delta+1
|
|
||||||
local nx=2*Dx/delta+1
|
|
||||||
|
|
||||||
env.info(string.format("FF building grid with nx=%d ny=%d total=%d nodes. Angle=%d, dist=%d meters", nx, nz, nx*nz, angle, dist))
|
|
||||||
for i=1,nx do
|
|
||||||
|
|
||||||
local x=xmin+delta*(i-1)
|
|
||||||
|
|
||||||
for j=1,nz do
|
|
||||||
|
|
||||||
local z=zmin+delta*(j-1)
|
|
||||||
|
|
||||||
local vec3=UTILS.Rotate2D({x=x, y=0, z=z}, angle)
|
|
||||||
|
|
||||||
local c=COORDINATE:New(vec3.z, vec3.y, vec3.x):Translate(do1, ho1, true)
|
|
||||||
|
|
||||||
if c:IsSurfaceTypeWater() then
|
|
||||||
|
|
||||||
--c:MarkToAll(string.format("i=%d, j=%d", i, j))
|
|
||||||
|
|
||||||
local node=self:GetNodeFromCoordinate(c)
|
|
||||||
self:AddNode(node)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
env.info("FF Done building grid!")
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Find the closest node from a given coordinate.
|
--- Find the closest node from a given coordinate.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
@@ -276,41 +380,147 @@ function ASTAR:FindEndNode()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Main A* pathfinding function
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Function
|
--- A* pathfinding function. This seaches the path along nodes between start and end nodes/coordinates.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #boolean ExcludeStartNode If *true*, do not include start node in found path. Default is to include it.
|
||||||
|
-- @param #boolean ExcludeEndNode If *true*, do not include end node in found path. Default is to include it.
|
||||||
|
-- @return #table Table of nodes from start to finish.
|
||||||
|
function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
||||||
|
|
||||||
|
self:FindStartNode()
|
||||||
|
self:FindEndNode()
|
||||||
|
|
||||||
|
local nodes=self.nodes
|
||||||
|
local start=self.startNode
|
||||||
|
local goal=self.endNode
|
||||||
|
|
||||||
|
local closedset = {}
|
||||||
|
local openset = { start }
|
||||||
|
local came_from = {}
|
||||||
|
|
||||||
|
local g_score, f_score = {}, {}
|
||||||
|
|
||||||
|
g_score[start]=0
|
||||||
|
f_score[start]=g_score[start]+self:HeuristicCost(start, goal)
|
||||||
|
|
||||||
|
-- Set start time.
|
||||||
|
local T0=timer.getAbsTime()
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
local text=string.format("Starting A* pathfinding")
|
||||||
|
self:I(self.lid..text)
|
||||||
|
MESSAGE:New(text, 10, "ASTAR"):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
while #openset > 0 do
|
||||||
|
|
||||||
|
local current=self:LowestFscore(openset, f_score)
|
||||||
|
|
||||||
|
-- Check if we are at the end node.
|
||||||
|
if current==goal then
|
||||||
|
|
||||||
|
local path=self:UnwindPath({}, came_from, goal)
|
||||||
|
|
||||||
|
if not ExcludeEndNode then
|
||||||
|
table.insert(path, goal)
|
||||||
|
end
|
||||||
|
|
||||||
|
if ExcludeStartNode then
|
||||||
|
table.remove(path, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set end time.
|
||||||
|
local T9=timer.getAbsTime()
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
local text=string.format("Found path with %d nodes", #path)
|
||||||
|
self:I(self.lid..text)
|
||||||
|
MESSAGE:New(text, 60, "ASTAR"):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
self:RemoveNode(openset, current)
|
||||||
|
table.insert(closedset, current)
|
||||||
|
|
||||||
|
local neighbors=self:NeighbourNodes(current, nodes)
|
||||||
|
|
||||||
|
-- Loop over neighbours.
|
||||||
|
for _,neighbor in ipairs(neighbors) do
|
||||||
|
|
||||||
|
if self:NotIn(closedset, neighbor) then
|
||||||
|
|
||||||
|
local tentative_g_score=g_score[current]+self:DistNodes(current, neighbor)
|
||||||
|
|
||||||
|
if self:NotIn(openset, neighbor) or tentative_g_score < g_score[neighbor] then
|
||||||
|
|
||||||
|
came_from[neighbor]=current
|
||||||
|
|
||||||
|
g_score[neighbor]=tentative_g_score
|
||||||
|
f_score[neighbor]=g_score[neighbor]+self:HeuristicCost(neighbor, goal)
|
||||||
|
|
||||||
|
if self:NotIn(openset, neighbor) then
|
||||||
|
table.insert(openset, neighbor)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
local text=string.format("WARNING: Could NOT find valid path!")
|
||||||
|
self:I(self.lid..text)
|
||||||
|
MESSAGE:New(text, 60, "ASTAR"):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
return nil -- no valid path
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- A* pathfinding helper functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Calculate 2D distance between two nodes.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param #ASTAR.Node nodeA Node A.
|
-- @param #ASTAR.Node nodeA Node A.
|
||||||
-- @param #ASTAR.Node nodeB Node B.
|
-- @param #ASTAR.Node nodeB Node B.
|
||||||
-- @return #number Distance between nodes in meters.
|
-- @return #number Distance between nodes in meters.
|
||||||
function ASTAR:DistNodes ( nodeA, nodeB )
|
function ASTAR:DistNodes(nodeA, nodeB)
|
||||||
return nodeA.coordinate:Get2DDistance(nodeB.coordinate)
|
return nodeA.coordinate:Get2DDistance(nodeB.coordinate)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Heuristic cost function to go from node A to node B. That is simply the distance here.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param #ASTAR.Node nodeA Node A.
|
-- @param #ASTAR.Node nodeA Node A.
|
||||||
-- @param #ASTAR.Node nodeB Node B.
|
-- @param #ASTAR.Node nodeB Node B.
|
||||||
-- @return #number Distance between nodes in meters.
|
-- @return #number Distance between nodes in meters.
|
||||||
function ASTAR:HeuristicCost( nodeA, nodeB )
|
function ASTAR:HeuristicCost(nodeA, nodeB)
|
||||||
return self:DistNodes(nodeA, nodeB)
|
return self:DistNodes(nodeA, nodeB)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Check if going from a node to a neighbour is possible.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
function ASTAR:is_valid_node ( node, neighbor )
|
-- @param #ASTAR.Node node A node.
|
||||||
|
-- @param #ASTAR.Node neighbor Neighbour node.
|
||||||
|
-- @return #boolean If true, transition between nodes is possible.
|
||||||
|
function ASTAR:IsValidNeighbour(node, neighbor)
|
||||||
|
|
||||||
self.CheckNodeValid=ASTAR.LoS
|
if self.ValidNeighbourFunc then
|
||||||
|
|
||||||
if self.CheckNodeValid then
|
return self.ValidNeighbourFunc(node, neighbor, unpack(self.ValidNeighbourArg))
|
||||||
return self.CheckNodeValid(node, neighbor)
|
|
||||||
else
|
else
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Function
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
function ASTAR:lowest_f_score(set, f_score)
|
function ASTAR:LowestFscore(set, f_score)
|
||||||
|
|
||||||
local lowest, bestNode = ASTAR.INF, nil
|
local lowest, bestNode = ASTAR.INF, nil
|
||||||
|
|
||||||
@@ -326,25 +536,37 @@ function ASTAR:lowest_f_score(set, f_score)
|
|||||||
return bestNode
|
return bestNode
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Function to get valid neighbours of a node.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
function ASTAR:neighbor_nodes(theNode, nodes)
|
-- @param #ASTAR.Node theNode The node.
|
||||||
|
-- @param #table nodes Possible neighbours.
|
||||||
|
-- @param #table Valid neighbour nodes.
|
||||||
|
function ASTAR:NeighbourNodes(theNode, nodes)
|
||||||
|
|
||||||
local neighbors = {}
|
local neighbors = {}
|
||||||
for _, node in ipairs ( nodes ) do
|
for _, node in ipairs ( nodes ) do
|
||||||
|
|
||||||
|
if theNode~=node then
|
||||||
if theNode ~= node and self:is_valid_node ( theNode, node ) then
|
|
||||||
table.insert ( neighbors, node )
|
local isvalid=self:IsValidNeighbour(theNode, node)
|
||||||
|
|
||||||
|
if isvalid then
|
||||||
|
table.insert(neighbors, node)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return neighbors
|
return neighbors
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Function to check if a node is not in a set.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
function ASTAR:not_in ( set, theNode )
|
-- @param #table set Set of nodes.
|
||||||
|
-- @param #ASTAR.Node theNode The node to check.
|
||||||
|
-- @return #boolean If true, the node is not in the set.
|
||||||
|
function ASTAR:NotIn(set, theNode)
|
||||||
|
|
||||||
for _, node in ipairs ( set ) do
|
for _, node in ipairs ( set ) do
|
||||||
if node == theNode then
|
if node == theNode then
|
||||||
@@ -355,9 +577,11 @@ function ASTAR:not_in ( set, theNode )
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Function to remove a node from a set.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
function ASTAR:remove_node(set, theNode)
|
-- @param #table set Set of nodes.
|
||||||
|
-- @param #ASTAR.Node theNode The node to check.
|
||||||
|
function ASTAR:RemoveNode(set, theNode)
|
||||||
|
|
||||||
for i, node in ipairs ( set ) do
|
for i, node in ipairs ( set ) do
|
||||||
if node == theNode then
|
if node == theNode then
|
||||||
@@ -365,11 +589,16 @@ function ASTAR:remove_node(set, theNode)
|
|||||||
set [ #set ] = nil
|
set [ #set ] = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function
|
--- Unwind path function.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
|
-- @param #table flat_path Flat path.
|
||||||
|
-- @param #table map Map.
|
||||||
|
-- @param #ASTAR.Node current_node The current node.
|
||||||
|
-- @return #table Unwinded path.
|
||||||
function ASTAR:UnwindPath( flat_path, map, current_node )
|
function ASTAR:UnwindPath( flat_path, map, current_node )
|
||||||
|
|
||||||
if map [ current_node ] then
|
if map [ current_node ] then
|
||||||
@@ -380,70 +609,6 @@ function ASTAR:UnwindPath( flat_path, map, current_node )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------
|
|
||||||
-- pathfinding functions
|
|
||||||
----------------------------------------------------------------
|
|
||||||
|
|
||||||
--- Function
|
|
||||||
-- @param #ASTAR self
|
|
||||||
function ASTAR:GetPath()
|
|
||||||
|
|
||||||
self:FindStartNode()
|
|
||||||
self:FindEndNode()
|
|
||||||
|
|
||||||
local nodes=self.nodes
|
|
||||||
local start=self.startNode
|
|
||||||
local goal=self.endNode
|
|
||||||
|
|
||||||
local closedset = {}
|
|
||||||
local openset = { start }
|
|
||||||
local came_from = {}
|
|
||||||
|
|
||||||
local g_score, f_score = {}, {}
|
|
||||||
|
|
||||||
g_score [ start ] = 0
|
|
||||||
|
|
||||||
f_score [ start ] = g_score [ start ] + self:HeuristicCost ( start, goal )
|
|
||||||
|
|
||||||
while #openset > 0 do
|
|
||||||
|
|
||||||
local current = self:lowest_f_score ( openset, f_score )
|
|
||||||
|
|
||||||
if current == goal then
|
|
||||||
local path = self:UnwindPath ( {}, came_from, goal )
|
|
||||||
table.insert(path, goal)
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
|
|
||||||
self:remove_node( openset, current )
|
|
||||||
table.insert ( closedset, current )
|
|
||||||
|
|
||||||
local neighbors = self:neighbor_nodes( current, nodes )
|
|
||||||
|
|
||||||
for _, neighbor in ipairs ( neighbors ) do
|
|
||||||
|
|
||||||
if self:not_in ( closedset, neighbor ) then
|
|
||||||
|
|
||||||
local tentative_g_score = g_score [ current ] + self:DistNodes ( current, neighbor )
|
|
||||||
|
|
||||||
if self:not_in ( openset, neighbor ) or tentative_g_score < g_score [ neighbor ] then
|
|
||||||
|
|
||||||
came_from [ neighbor ] = current
|
|
||||||
g_score [ neighbor ] = tentative_g_score
|
|
||||||
f_score [ neighbor ] = g_score [ neighbor ] + self:HeuristicCost ( neighbor, goal )
|
|
||||||
|
|
||||||
if self:not_in ( openset, neighbor ) then
|
|
||||||
table.insert ( openset, neighbor )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil -- no valid path
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -740,19 +740,6 @@ function AIRWING:onafterStart(From, Event, To)
|
|||||||
-- Info.
|
-- Info.
|
||||||
self:I(self.lid..string.format("Starting AIRWING v%s", AIRWING.version))
|
self:I(self.lid..string.format("Starting AIRWING v%s", AIRWING.version))
|
||||||
|
|
||||||
-- Menu.
|
|
||||||
if false then
|
|
||||||
|
|
||||||
-- Add F10 radio menu.
|
|
||||||
self:_SetMenuCoalition()
|
|
||||||
|
|
||||||
for _,_squadron in pairs(self.squadrons) do
|
|
||||||
local squadron=_squadron --Ops.Squadron#SQUADRON
|
|
||||||
self:_AddSquadonMenu(squadron)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Update status.
|
--- Update status.
|
||||||
@@ -2162,140 +2149,6 @@ function AIRWING:GetMissionFromRequest(Request)
|
|||||||
return self:GetMissionFromRequestID(Request.uid)
|
return self:GetMissionFromRequestID(Request.uid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
-- Menu Functions
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
--- Patrol carrier.
|
|
||||||
-- @param #AIRWING self
|
|
||||||
-- @return #AIRWING self
|
|
||||||
function AIRWING:_SetMenuCoalition()
|
|
||||||
|
|
||||||
-- Get coalition.
|
|
||||||
local Coalition=self:GetCoalition()
|
|
||||||
|
|
||||||
-- Init menu table.
|
|
||||||
self.menu=self.menu or {}
|
|
||||||
|
|
||||||
-- Init menu coalition table.
|
|
||||||
self.menu[Coalition]=self.menu[Coalition] or {}
|
|
||||||
|
|
||||||
-- Shortcut.
|
|
||||||
local menu=self.menu[Coalition]
|
|
||||||
|
|
||||||
if self.menusingle then
|
|
||||||
-- F10/Skipper/...
|
|
||||||
if not menu.AIRWING then
|
|
||||||
menu.AIRWING=MENU_COALITION:New(Coalition, "AIRWING")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- F10/Skipper/<Carrier Alias>/...
|
|
||||||
if not menu.Root then
|
|
||||||
menu.Root=MENU_COALITION:New(Coalition, "AIRWING")
|
|
||||||
end
|
|
||||||
menu.AIRWING=MENU_COALITION:New(Coalition, self.alias, menu.Root)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
-- Squadron Menu --
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
menu.Squadron={}
|
|
||||||
menu.Squadron.Main= MENU_COALITION:New(Coalition, "Squadrons", menu.AIRWING)
|
|
||||||
|
|
||||||
menu.Warehouse={}
|
|
||||||
menu.Warehouse.Main = MENU_COALITION:New(Coalition, "Warehouse", menu.AIRWING)
|
|
||||||
menu.Warehouse.Reports = MENU_COALITION_COMMAND:New(Coalition, "Reports On/Off", menu.Warehouse.Main, self.WarehouseReportsToggle, self)
|
|
||||||
menu.Warehouse.Assets = MENU_COALITION_COMMAND:New(Coalition, "Report Assets", menu.Warehouse.Main, self.ReportWarehouseStock, self)
|
|
||||||
|
|
||||||
menu.ReportSquadrons = MENU_COALITION_COMMAND:New(Coalition, "Report Squadrons", menu.AIRWING, self.ReportSquadrons, self)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Report squadron status.
|
|
||||||
-- @param #AIRWING self
|
|
||||||
function AIRWING:ReportSquadrons()
|
|
||||||
|
|
||||||
local text="Squadron Report:"
|
|
||||||
|
|
||||||
for i,_squadron in pairs(self.squadrons) do
|
|
||||||
local squadron=_squadron
|
|
||||||
|
|
||||||
local name=squadron.name
|
|
||||||
|
|
||||||
local nspawned=0
|
|
||||||
local nstock=0
|
|
||||||
for _,_asset in pairs(squadron.assets) do
|
|
||||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
|
||||||
|
|
||||||
local n=asset.nunits
|
|
||||||
|
|
||||||
if asset.spawned then
|
|
||||||
nspawned=nspawned+n
|
|
||||||
else
|
|
||||||
nstock=nstock+n
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
text=string.format("\n%s: AC on duty=%d, in stock=%d", name, nspawned, nstock)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
self:I(self.lid..text)
|
|
||||||
MESSAGE:New(text, 10, "AIRWING", true):ToCoalition(self:GetCoalition())
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Add sub menu for this intruder.
|
|
||||||
-- @param #AIRWING self
|
|
||||||
-- @param Ops.Squadron#SQUADRON squadron The squadron data.
|
|
||||||
function AIRWING:_AddSquadonMenu(squadron)
|
|
||||||
|
|
||||||
local Coalition=self:GetCoalition()
|
|
||||||
|
|
||||||
local root=self.menu[Coalition].Squadron.Main
|
|
||||||
|
|
||||||
local menu=MENU_COALITION:New(Coalition, squadron.name, root)
|
|
||||||
|
|
||||||
MENU_COALITION_COMMAND:New(Coalition, "Report", menu, self._ReportSq, self, squadron)
|
|
||||||
MENU_COALITION_COMMAND:New(Coalition, "Launch CAP", menu, self._LaunchCAP, self, squadron)
|
|
||||||
|
|
||||||
-- Set menu.
|
|
||||||
squadron.menu=menu
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Report squadron status.
|
|
||||||
-- @param #AIRWING self
|
|
||||||
-- @param Ops.Squadron#SQUADRON squadron The squadron object.
|
|
||||||
function AIRWING:_ReportSq(squadron)
|
|
||||||
|
|
||||||
local text=string.format("%s: %s assets:", squadron.name, tostring(squadron.categoryname))
|
|
||||||
for i,_asset in pairs(squadron.assets) do
|
|
||||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
|
||||||
text=text..string.format("%d.) ")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Warehouse reports on/off.
|
|
||||||
-- @param #AIRWING self
|
|
||||||
function AIRWING:WarehouseReportsToggle()
|
|
||||||
self.Report=not self.Report
|
|
||||||
MESSAGE:New(string.format("Warehouse reports are now %s", tostring(self.Report)), 10, "AIRWING", true):ToCoalition(self:GetCoalition())
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Report warehouse stock.
|
|
||||||
-- @param #AIRWING self
|
|
||||||
function AIRWING:ReportWarehouseStock()
|
|
||||||
local text=self:_GetStockAssetsText(false)
|
|
||||||
MESSAGE:New(text, 10, "AIRWING", true):ToCoalition(self:GetCoalition())
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -345,11 +345,10 @@ function CHIEF:onafterStart(From, Event, To)
|
|||||||
-- Start parent INTEL.
|
-- Start parent INTEL.
|
||||||
self:GetParent(self).onafterStart(self, From, Event, To)
|
self:GetParent(self).onafterStart(self, From, Event, To)
|
||||||
|
|
||||||
-- Start attached airwings.
|
-- Start wingcommander.
|
||||||
for _,_airwing in pairs(self.airwings) do
|
if self.wingcommander then
|
||||||
local airwing=_airwing --Ops.AirWing#AIRWING
|
if self.wingcommander:GetState()=="NotReadyYet" then
|
||||||
if airwing:GetState()=="NotReadyYet" then
|
self.wingcommander:Start()
|
||||||
airwing:Start()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -480,7 +480,7 @@ function INTEL:CreateDetectedItems(detectedunitset)
|
|||||||
item.attribute=group:GetAttribute()
|
item.attribute=group:GetAttribute()
|
||||||
item.category=group:GetCategory()
|
item.category=group:GetCategory()
|
||||||
item.categoryname=group:GetCategoryName()
|
item.categoryname=group:GetCategoryName()
|
||||||
item.threatlevel=group:GetUnit(1):GetThreatLevel()
|
item.threatlevel=group:GetThreatLevel()
|
||||||
item.position=group:GetCoordinate()
|
item.position=group:GetCoordinate()
|
||||||
item.velocity=group:GetVelocityVec3()
|
item.velocity=group:GetVelocityVec3()
|
||||||
item.speed=group:GetVelocityMPS()
|
item.speed=group:GetVelocityMPS()
|
||||||
@@ -699,7 +699,7 @@ function INTEL:PaintPicture()
|
|||||||
|
|
||||||
|
|
||||||
-- Update F10 marker.
|
-- Update F10 marker.
|
||||||
self:UpdateClusterMarker(cluster, coordinate)
|
self:UpdateClusterMarker(cluster)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -959,7 +959,7 @@ end
|
|||||||
--- Update cluster F10 marker.
|
--- Update cluster F10 marker.
|
||||||
-- @param #INTEL self
|
-- @param #INTEL self
|
||||||
-- @param #INTEL.Cluster cluster The cluster.
|
-- @param #INTEL.Cluster cluster The cluster.
|
||||||
-- @param Core.Point#COORDINATE newcoordinate Updated cluster positon.
|
-- @return #INTEL self
|
||||||
function INTEL:UpdateClusterMarker(cluster, newcoordinate)
|
function INTEL:UpdateClusterMarker(cluster, newcoordinate)
|
||||||
|
|
||||||
-- Create a marker.
|
-- Create a marker.
|
||||||
@@ -976,10 +976,9 @@ function INTEL:UpdateClusterMarker(cluster, newcoordinate)
|
|||||||
refresh=true
|
refresh=true
|
||||||
end
|
end
|
||||||
|
|
||||||
if newcoordinate then
|
if cluster.marker.coordinate~=cluster.coordinate then
|
||||||
cluster.coordinate=newcoordinate
|
|
||||||
cluster.marker.coordinate=cluster.coordinate
|
cluster.marker.coordinate=cluster.coordinate
|
||||||
refresh=true
|
refresh=true
|
||||||
end
|
end
|
||||||
|
|
||||||
if refresh then
|
if refresh then
|
||||||
@@ -988,6 +987,7 @@ function INTEL:UpdateClusterMarker(cluster, newcoordinate)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
-- @field #boolean turning If true, group is currently turning.
|
-- @field #boolean turning If true, group is currently turning.
|
||||||
-- @field #NAVYGROUP.IntoWind intowind Into wind info.
|
-- @field #NAVYGROUP.IntoWind intowind Into wind info.
|
||||||
-- @field #table Qintowind Queue of "into wind" turns.
|
-- @field #table Qintowind Queue of "into wind" turns.
|
||||||
-- @field #boolean adinfinitum Resume route at first waypoint when final waypoint is reached.
|
|
||||||
-- @field #number depth Ordered depth in meters.
|
-- @field #number depth Ordered depth in meters.
|
||||||
-- @extends Ops.OpsGroup#OPSGROUP
|
-- @extends Ops.OpsGroup#OPSGROUP
|
||||||
|
|
||||||
@@ -1230,7 +1229,7 @@ function NAVYGROUP:_CheckGroupDone(delay)
|
|||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
self:UpdateRoute(nil, nil, self.depth)
|
self:__UpdateRoute(-1, nil, nil, self.depth)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
-- @field #boolean isGround If true, group is some ground unit.
|
-- @field #boolean isGround If true, group is some ground unit.
|
||||||
-- @field #table waypoints Table of waypoints.
|
-- @field #table waypoints Table of waypoints.
|
||||||
-- @field #table waypoints0 Table of initial waypoints.
|
-- @field #table waypoints0 Table of initial waypoints.
|
||||||
-- @field #number currentwp Current waypoint.
|
-- @field #number currentwp Current waypoint index. This is the index of the last passed waypoint.
|
||||||
|
-- @field #boolean adinfinitum Resume route at first waypoint when final waypoint is reached.
|
||||||
-- @field #table taskqueue Queue of tasks.
|
-- @field #table taskqueue Queue of tasks.
|
||||||
-- @field #number taskcounter Running number of task ids.
|
-- @field #number taskcounter Running number of task ids.
|
||||||
-- @field #number taskcurrent ID of current task. If 0, there is no current task assigned.
|
-- @field #number taskcurrent ID of current task. If 0, there is no current task assigned.
|
||||||
@@ -434,19 +435,30 @@ end
|
|||||||
|
|
||||||
--- Get next waypoint index.
|
--- Get next waypoint index.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached.
|
-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached. Default is patrol ad infinitum value set.
|
||||||
-- @return #number Next waypoint index.
|
-- @return #number Next waypoint index.
|
||||||
function OPSGROUP:GetWaypointIndexNext(cyclic)
|
function OPSGROUP:GetWaypointIndexNext(cyclic)
|
||||||
|
|
||||||
local n=math.min(self.currentwp+1, #self.waypoints)
|
cyclic=cyclic or self.adinfinitum
|
||||||
|
|
||||||
if cyclic and self.currentwp==#self.waypoints then
|
local N=#self.waypoints
|
||||||
|
|
||||||
|
local n=math.min(self.currentwp+1, N)
|
||||||
|
|
||||||
|
if cyclic and self.currentwp==N then
|
||||||
n=1
|
n=1
|
||||||
end
|
end
|
||||||
|
|
||||||
return n
|
return n
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get current waypoint index. This is the index of the last passed waypoint.
|
||||||
|
-- @param #OPSGROUP self
|
||||||
|
-- @return #number Current waypoint index.
|
||||||
|
function OPSGROUP:GetWaypointIndexCurrent()
|
||||||
|
return self.currentwp or 1
|
||||||
|
end
|
||||||
|
|
||||||
--- Get waypoint speed.
|
--- Get waypoint speed.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param #number indx Waypoint index.
|
-- @param #number indx Waypoint index.
|
||||||
@@ -465,14 +477,14 @@ end
|
|||||||
--- Get waypoint.
|
--- Get waypoint.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param #number indx Waypoint index.
|
-- @param #number indx Waypoint index.
|
||||||
-- @return #table Waypoint table.
|
-- @return #OPSGROUP.Waypoint Waypoint table.
|
||||||
function OPSGROUP:GetWaypoint(indx)
|
function OPSGROUP:GetWaypoint(indx)
|
||||||
return self.waypoints[indx]
|
return self.waypoints[indx]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get final waypoint.
|
--- Get final waypoint.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @return #table Waypoint table.
|
-- @return #OPSGROUP.Waypoint Final waypoint table.
|
||||||
function OPSGROUP:GetWaypointFinal()
|
function OPSGROUP:GetWaypointFinal()
|
||||||
return self.waypoints[#self.waypoints]
|
return self.waypoints[#self.waypoints]
|
||||||
end
|
end
|
||||||
@@ -480,7 +492,7 @@ end
|
|||||||
--- Get next waypoint.
|
--- Get next waypoint.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached.
|
-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached.
|
||||||
-- @return #table Waypoint table.
|
-- @return #OPSGROUP.Waypoint Next waypoint table.
|
||||||
function OPSGROUP:GetWaypointNext(cyclic)
|
function OPSGROUP:GetWaypointNext(cyclic)
|
||||||
|
|
||||||
local n=self:GetWaypointIndexNext(cyclic)
|
local n=self:GetWaypointIndexNext(cyclic)
|
||||||
@@ -490,7 +502,7 @@ end
|
|||||||
|
|
||||||
--- Get current waypoint.
|
--- Get current waypoint.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @return #table Waypoint table.
|
-- @return #OPSGROUP.Waypoint Current waypoint table.
|
||||||
function OPSGROUP:GetWaypointCurrent()
|
function OPSGROUP:GetWaypointCurrent()
|
||||||
return self.waypoints[self.currentwp]
|
return self.waypoints[self.currentwp]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ end
|
|||||||
|
|
||||||
--- Set number of units in groups.
|
--- Set number of units in groups.
|
||||||
-- @param #SQUADRON self
|
-- @param #SQUADRON self
|
||||||
-- @param #nunits Number of units. Must be >=1 and <=4. Default 2.
|
-- @param #number nunits Number of units. Must be >=1 and <=4. Default 2.
|
||||||
-- @return #SQUADRON self
|
-- @return #SQUADRON self
|
||||||
function SQUADRON:SetGrouping(nunits)
|
function SQUADRON:SetGrouping(nunits)
|
||||||
self.ngrouping=nunits or 2
|
self.ngrouping=nunits or 2
|
||||||
|
|||||||
@@ -2100,6 +2100,7 @@ end
|
|||||||
|
|
||||||
--- Calculate the maxium A2G threat level of the Group.
|
--- Calculate the maxium A2G threat level of the Group.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
|
-- @return #number Number between 0 and 10.
|
||||||
function GROUP:CalculateThreatLevelA2G()
|
function GROUP:CalculateThreatLevelA2G()
|
||||||
|
|
||||||
local MaxThreatLevelA2G = 0
|
local MaxThreatLevelA2G = 0
|
||||||
@@ -2115,6 +2116,25 @@ function GROUP:CalculateThreatLevelA2G()
|
|||||||
return MaxThreatLevelA2G
|
return MaxThreatLevelA2G
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get threat level of the group.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @return #number Max threat level (a number between 0 and 10).
|
||||||
|
function GROUP:GetThreatLevel()
|
||||||
|
|
||||||
|
local threatlevelMax = 0
|
||||||
|
for UnitName, UnitData in pairs(self:GetUnits()) do
|
||||||
|
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
local threatlevel = ThreatUnit:GetThreatLevel()
|
||||||
|
if threatlevel > threatlevelMax then
|
||||||
|
threatlevelMax=threatlevel
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return threatlevelMax
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns true if the first unit of the GROUP is in the air.
|
--- Returns true if the first unit of the GROUP is in the air.
|
||||||
-- @param Wrapper.Group#GROUP self
|
-- @param Wrapper.Group#GROUP self
|
||||||
-- @return #boolean true if in the first unit of the group is in the air or #nil if the GROUP is not existing or not alive.
|
-- @return #boolean true if in the first unit of the group is in the air or #nil if the GROUP is not existing or not alive.
|
||||||
|
|||||||
Reference in New Issue
Block a user