mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Runways
**AIRBASE** - Runways are now retrieved from DCS API function
This commit is contained in:
parent
a53595a055
commit
8926e06e44
@ -1037,7 +1037,7 @@ function DATABASE:_RegisterAirbases()
|
||||
local airbaseUID=airbase:GetID(true)
|
||||
|
||||
-- Debug output.
|
||||
local text=string.format("Register %s: %s (ID=%d UID=%d), parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseID, airbaseUID, airbase.NparkingTotal)
|
||||
local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal)
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then
|
||||
text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType])
|
||||
|
||||
@ -1939,6 +1939,7 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
self:SetMarker(true)
|
||||
self:SetReportOff()
|
||||
self:SetRunwayRepairtime()
|
||||
self.allowSpawnOnClientSpots=false
|
||||
|
||||
-- Add warehouse to database.
|
||||
_WAREHOUSEDB.Warehouses[self.uid]=self
|
||||
@ -2584,6 +2585,14 @@ function WAREHOUSE:SetSafeParkingOff()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set wether client parking spots can be used for spawning.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetAllowSpawnOnClientParking()
|
||||
self.allowSpawnOnClientSpots=true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set low fuel threshold. If one unit of an asset has less fuel than this number, the event AssetLowFuel will be fired.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number threshold Relative low fuel threshold, i.e. a number in [0,1]. Default 0.15 (15%).
|
||||
@ -7878,14 +7887,16 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
||||
|
||||
-- Get client coordinates.
|
||||
local function _clients()
|
||||
local clients=_DATABASE.CLIENTS
|
||||
local coords={}
|
||||
for clientname, client in pairs(clients) do
|
||||
local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
|
||||
local units=template.units
|
||||
for i,unit in pairs(units) do
|
||||
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
|
||||
coords[unit.name]=coord
|
||||
if not self.allowSpawnOnClientSpots then
|
||||
local clients=_DATABASE.CLIENTS
|
||||
for clientname, client in pairs(clients) do
|
||||
local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
|
||||
local units=template.units
|
||||
for i,unit in pairs(units) do
|
||||
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
|
||||
coords[unit.name]=coord
|
||||
end
|
||||
end
|
||||
end
|
||||
return coords
|
||||
|
||||
@ -317,9 +317,6 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS)
|
||||
|
||||
-- Wait at least 10 seconds after last radio message before calling the next status update.
|
||||
self.dTmessage=10
|
||||
|
||||
-- Init runways.
|
||||
self:_InitRunwayData()
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Stopped")
|
||||
@ -801,7 +798,7 @@ function FLIGHTCONTROL:OnEventBirth(EventData)
|
||||
-- Create player menu.
|
||||
self:ScheduleOnce(0.5, self._CreateFlightGroup, self, EventData.IniGroup)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Spawn parking guard.
|
||||
if bornhere then
|
||||
@ -1540,35 +1537,19 @@ end
|
||||
-- Runway Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Initialize data of runways.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
function FLIGHTCONTROL:_InitRunwayData()
|
||||
self.runways=self.airbase:GetRunwayData()
|
||||
end
|
||||
|
||||
--- Get the active runway based on current wind direction.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @return Wrapper.Airbase#AIRBASE.Runway Active runway.
|
||||
function FLIGHTCONTROL:GetActiveRunway()
|
||||
return self.airbase:GetActiveRunway()
|
||||
local rwy=self.airbase:GetActiveRunway()
|
||||
return rwy
|
||||
end
|
||||
|
||||
--- Get the active runway based on current wind direction.
|
||||
--- Get the name of the active runway.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @return #string Runway text, e.g. "31L" or "09".
|
||||
function FLIGHTCONTROL:GetActiveRunwayText()
|
||||
local rwy=""
|
||||
local rwyL
|
||||
if self.atis then
|
||||
rwy, rwyL=self.atis:GetActiveRunway()
|
||||
if rwyL==true then
|
||||
rwy=rwy.."L"
|
||||
elseif rwyL==false then
|
||||
rwy=rwy.."R"
|
||||
end
|
||||
else
|
||||
rwy=self.airbase:GetActiveRunway().idx
|
||||
end
|
||||
local rwy=self.airbase:GetRunwayName()
|
||||
return rwy
|
||||
end
|
||||
|
||||
@ -2408,8 +2389,7 @@ function FLIGHTCONTROL:_PlayerVectorInbound(groupname)
|
||||
local dist=UTILS.MetersToNM(distance)
|
||||
|
||||
-- Message text.
|
||||
local text=string.format("%s, fly heading %03d for %d nautical miles, hold at angels %d.",
|
||||
callsign, self.alias, heading, dist, flight.stack.angels)
|
||||
local text=string.format("%s, fly heading %03d for %d nautical miles, hold at angels %d.", callsign, heading, dist, flight.stack.angels)
|
||||
|
||||
-- Send message.
|
||||
self:TextMessageToFlight(text, flight)
|
||||
@ -2513,16 +2493,24 @@ function FLIGHTCONTROL:_PlayerHolding(groupname)
|
||||
|
||||
if stack then
|
||||
|
||||
-- Pilot calls inbound for landing.
|
||||
local text=string.format("%s, %s, arrived at holding pattern", self.alias, callsign)
|
||||
|
||||
-- Radio message.
|
||||
self:TransmissionPilot(text, flight)
|
||||
|
||||
-- Current coordinate.
|
||||
local Coordinate=flight:GetCoordinate(nil, player.name)
|
||||
|
||||
-- Distance.
|
||||
local dist=stack.pos0:Get2DDistance(Coordinate)
|
||||
|
||||
if dist<5000 then
|
||||
local dmax=UTILS.NMToMeters(5)
|
||||
|
||||
if dist<dmax then
|
||||
|
||||
-- Message to flight
|
||||
local text=string.format("%s, roger, you are added to the holding queue!", callsign)
|
||||
local text=string.format("%s, roger, fly heading %d at angels %d.", callsign, stack.heading, stack.angels)
|
||||
self:TextMessageToFlight(text, flight, 10, true)
|
||||
|
||||
-- Call holding event.
|
||||
@ -2531,13 +2519,15 @@ function FLIGHTCONTROL:_PlayerHolding(groupname)
|
||||
else
|
||||
|
||||
-- Message to flight
|
||||
local text=string.format("Negative, you have to be within 5 km!")
|
||||
local text=string.format("Negative, you have to be within %d NM of the arrival zone! You still %d NM away.", UTILS.MetersToNM(dmax), UTILS.MetersToNM(dist))
|
||||
self:TextMessageToFlight(text, flight, 10, true)
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
--TODO: Error not holding stack.
|
||||
-- Message to flight
|
||||
local text=string.format("Negative, we have no holding stack for you!")
|
||||
self:TextMessageToFlight(text, flight, 10, true)
|
||||
end
|
||||
|
||||
else
|
||||
@ -3419,9 +3409,12 @@ end
|
||||
-- @param Ops.FlightGroup#FLIGHTGROUP Flight The flight.
|
||||
-- @param #number Delay Delay in seconds before the text is transmitted. Default 0 sec.
|
||||
function FLIGHTCONTROL:TransmissionTower(Text, Flight, Delay)
|
||||
|
||||
-- Spoken text.
|
||||
local text=self:_GetTextForSpeech(Text)
|
||||
|
||||
-- Tower radio call.
|
||||
self.msrsTower:PlayText(Text, Delay)
|
||||
self.msrsTower:PlayText(text, Delay)
|
||||
|
||||
-- "Subtitle".
|
||||
if Flight and not Flight.isAI then
|
||||
@ -3442,9 +3435,12 @@ end
|
||||
-- @param Ops.FlightGroup#FLIGHTGROUP Flight The flight.
|
||||
-- @param #number Delay Delay in seconds before the text is transmitted. Default 0 sec.
|
||||
function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay)
|
||||
|
||||
-- Spoken text.
|
||||
local text=self:_GetTextForSpeech(Text)
|
||||
|
||||
-- Pilot radio call.
|
||||
self.msrsPilot:PlayText(Text, Delay)
|
||||
self.msrsPilot:PlayText(text, Delay)
|
||||
|
||||
-- "Subtitle".
|
||||
if Flight and not Flight.isAI then
|
||||
@ -3564,6 +3560,45 @@ function FLIGHTCONTROL:RemoveParkingGuard(spot, delay)
|
||||
|
||||
end
|
||||
|
||||
--- Get callsign name of a given flight.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group.
|
||||
-- @return #string Callsign or "Ghostrider 1-1".
|
||||
function FLIGHTCONTROL:_GetCallsignName(flight)
|
||||
|
||||
local callsign=flight:GetCallsignName()
|
||||
|
||||
local name=string.match(callsign, "%a+")
|
||||
local number=string.match(callsign, "%d+")
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Get text for text
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param #string text
|
||||
-- @return #string Callsign or "Ghostrider 1-1".
|
||||
function FLIGHTCONTROL:_GetTextForSpeech(text)
|
||||
|
||||
--- Function to space out text.
|
||||
local function space(text)
|
||||
|
||||
local res=""
|
||||
|
||||
for i=1, #text do
|
||||
local char=text:sub(i,i)
|
||||
res=res..char.." "
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
-- Space out numbers.
|
||||
local t=text:gsub("(%d+)", space)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
|
||||
@ -393,7 +393,7 @@ function FLIGHTGROUP:SetFlightControl(flightcontrol)
|
||||
|
||||
-- Check if there is already a FC.
|
||||
if self.flightcontrol then
|
||||
if self.flightcontrol.airbasename==flightcontrol.airbasename then
|
||||
if self.flightcontrol:IsControlling(self) then
|
||||
-- Flight control is already controlling this flight!
|
||||
return
|
||||
else
|
||||
@ -3680,6 +3680,13 @@ function FLIGHTGROUP:_SetElementParkingAt(Element, Spot)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Element %s is parking on spot %d", Element.name, Spot.TerminalID))
|
||||
|
||||
-- Get flightcontrol.
|
||||
local fc=_DATABASE:GetFlightControl(Spot.AirbaseName)
|
||||
|
||||
if fc and not self.flightcontrol then
|
||||
self:SetFlightControl(fc)
|
||||
end
|
||||
|
||||
if self.flightcontrol then
|
||||
|
||||
|
||||
@ -11684,6 +11684,15 @@ end
|
||||
-- @return #string Callsign name, e.g. Uzi-1
|
||||
function OPSGROUP:GetCallsignName()
|
||||
|
||||
local element=self:GetElementAlive()
|
||||
|
||||
if element then
|
||||
env.info("FF callsign "..tostring(element.callsign))
|
||||
return element.callsign
|
||||
end
|
||||
|
||||
--[[
|
||||
|
||||
local numberSquad=self.callsign.NumberSquad or self.callsignDefault.NumberSquad
|
||||
local numberGroup=self.callsign.NumberGroup or self.callsignDefault.NumberGroup
|
||||
|
||||
@ -11698,6 +11707,8 @@ function OPSGROUP:GetCallsignName()
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
]]
|
||||
|
||||
return callsign
|
||||
end
|
||||
|
||||
@ -1153,7 +1153,7 @@ function UTILS.VecHdg(a)
|
||||
end
|
||||
|
||||
--- Calculate "heading" of a 2D vector in the X-Y plane.
|
||||
-- @param DCS#Vec2 a Vector in "D with x, y components.
|
||||
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||
-- @return #number Heading in degrees in [0,360).
|
||||
function UTILS.Vec2Hdg(a)
|
||||
local h=math.deg(math.atan2(a.y, a.x))
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
-- @field #number activerwyno Active runway number (forced).
|
||||
-- @field #table parkingWhitelist List of parking spot terminal IDs considered for spawning.
|
||||
-- @field #table parkingBlacklist List of parking spot terminal IDs **not** considered for spawning.
|
||||
-- @field #table runways Runways of airdromes.
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- Wrapper class to handle the DCS Airbase objects:
|
||||
@ -551,11 +552,17 @@ AIRBASE.SpotStatus = {
|
||||
|
||||
--- Runway data.
|
||||
-- @type AIRBASE.Runway
|
||||
-- @field #number heading Heading of the runway in degrees.
|
||||
-- @field #string name Runway name.
|
||||
-- @field #string idx Runway ID: heading 070° ==> idx="07".
|
||||
-- @field #number heading True heading of the runway in degrees.
|
||||
-- @field #number magheading Magnetic heading of the runway in degrees. This is what is marked on the runway.
|
||||
-- @field #number length Length of runway in meters.
|
||||
-- @field #number width Width of runway in meters.
|
||||
-- @field Core.Zone#ZONE_POLYGON zone Runway zone.
|
||||
-- @field Core.Point#COORDINATE center Center of the runway.
|
||||
-- @field Core.Point#COORDINATE position Position of runway start.
|
||||
-- @field Core.Point#COORDINATE endpoint End point of runway.
|
||||
-- @field #boolean isLeft If `true`, this is the left of two parallel runways. If `false`, this is the right of two runways. If `nil`, no parallel runway exists.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Registration
|
||||
@ -602,6 +609,9 @@ function AIRBASE:Register(AirbaseName)
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
end
|
||||
|
||||
-- Init Runways.
|
||||
self:_InitRunways()
|
||||
|
||||
-- Init parking spots.
|
||||
self:_InitParkingSpots()
|
||||
@ -819,6 +829,42 @@ function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets the ATC belonging to an airbase object to be silent and unresponsive. This is useful for disabling the award winning ATC behavior in DCS.
|
||||
-- Note that this DOES NOT remove the airbase from the list. It just makes it unresponsive and silent to any radio calls to it.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #boolean Silent If `true`, enable silent mode. If `false` or `nil`, disable silent mode.
|
||||
-- @return #AIRBASE self
|
||||
function AIRBASE:SetRadioSilentMode(Silent)
|
||||
|
||||
-- Get DCS airbase object.
|
||||
local airbase=self:GetDCSObject()
|
||||
|
||||
-- Set mode.
|
||||
if airbase then
|
||||
airbase:setRadioSilentMode(Silent)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check whether or not the airbase has been silenced.
|
||||
-- @param #AIRBASE self
|
||||
-- @return #boolean If `true`, silent mode is enabled.
|
||||
function AIRBASE:GetRadioSilentMode()
|
||||
|
||||
-- Is silent?
|
||||
local silent=nil
|
||||
|
||||
-- Get DCS airbase object.
|
||||
local airbase=self:GetDCSObject()
|
||||
|
||||
-- Set mode.
|
||||
if airbase then
|
||||
silent=airbase:getRadioSilentMode()
|
||||
end
|
||||
|
||||
return silent
|
||||
end
|
||||
|
||||
--- Get category of airbase.
|
||||
-- @param #AIRBASE self
|
||||
@ -1478,6 +1524,221 @@ end
|
||||
-- Runway
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get runways.
|
||||
-- @param #AIRBASE self
|
||||
-- @return #table Runway data.
|
||||
function AIRBASE:GetRunways()
|
||||
return self.runways or {}
|
||||
end
|
||||
|
||||
--- Init runways.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #boolean IncludeInverse If `true` or `nil`, include inverse runways.
|
||||
-- @return #table Runway data.
|
||||
function AIRBASE:_InitRunways(IncludeInverse)
|
||||
|
||||
-- Default is true.
|
||||
if IncludeInverse==nil then
|
||||
IncludeInverse=true
|
||||
end
|
||||
|
||||
-- Runway table.
|
||||
local Runways={}
|
||||
|
||||
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||
self.runways={}
|
||||
return {}
|
||||
end
|
||||
|
||||
--- Function to create a runway data table.
|
||||
local function _createRunway(name, course, width, length, center)
|
||||
|
||||
-- Bearing in rad.
|
||||
local bearing=-1*course
|
||||
|
||||
-- Heading in degrees.
|
||||
local heading=math.deg(bearing)
|
||||
|
||||
-- Data table.
|
||||
local runway={} --#AIRBASE.Runway
|
||||
runway.name=string.format("%02d", tonumber(name))
|
||||
runway.magheading=tonumber(runway.name)*10
|
||||
runway.heading=heading
|
||||
runway.width=width or 0
|
||||
runway.length=length or 0
|
||||
runway.center=COORDINATE:NewFromVec3(center)
|
||||
|
||||
-- Ensure heading is [0,360]
|
||||
if runway.heading>360 then
|
||||
runway.heading=runway.heading-360
|
||||
elseif runway.heading<0 then
|
||||
runway.heading=runway.heading+360
|
||||
end
|
||||
|
||||
-- For example at Nellis, DCS reports two runways, i.e. 03 and 21, BUT the "course" of both is -0.700 rad = 40 deg!
|
||||
-- As a workaround, I check the difference between the "magnetic" heading derived from the name and the true heading.
|
||||
-- If this is too large then very likely the "inverse" heading is the one we are looking for.
|
||||
if math.abs(runway.heading-runway.magheading)>60 then
|
||||
self:T(string.format("WARNING: Runway %s: heading=%.1f magheading=%.1f", runway.name, runway.heading, runway.magheading))
|
||||
runway.heading=runway.heading-180
|
||||
end
|
||||
|
||||
-- Ensure heading is [0,360]
|
||||
if runway.heading>360 then
|
||||
runway.heading=runway.heading-360
|
||||
elseif runway.heading<0 then
|
||||
runway.heading=runway.heading+360
|
||||
end
|
||||
|
||||
-- Start and endpoint of runway.
|
||||
runway.position=runway.center:Translate(-runway.length/2, runway.heading)
|
||||
runway.endpoint=runway.center:Translate( runway.length/2, runway.heading)
|
||||
|
||||
local init=runway.center:GetVec3()
|
||||
local width = runway.width/2
|
||||
local L2=runway.length/2
|
||||
|
||||
local offset1 = {x = init.x + (math.cos(bearing + math.pi) * L2), y = init.z + (math.sin(bearing + math.pi) * L2)}
|
||||
local offset2 = {x = init.x - (math.cos(bearing + math.pi) * L2), y = init.z - (math.sin(bearing + math.pi) * L2)}
|
||||
|
||||
local points={}
|
||||
points[1] = {x = offset1.x + (math.cos(bearing + (math.pi/2)) * width), y = offset1.y + (math.sin(bearing + (math.pi/2)) * width)}
|
||||
points[2] = {x = offset1.x + (math.cos(bearing - (math.pi/2)) * width), y = offset1.y + (math.sin(bearing - (math.pi/2)) * width)}
|
||||
points[3] = {x = offset2.x + (math.cos(bearing - (math.pi/2)) * width), y = offset2.y + (math.sin(bearing - (math.pi/2)) * width)}
|
||||
points[4] = {x = offset2.x + (math.cos(bearing + (math.pi/2)) * width), y = offset2.y + (math.sin(bearing + (math.pi/2)) * width)}
|
||||
|
||||
-- Runway zone.
|
||||
runway.zone=ZONE_POLYGON_BASE:New(string.format("%s Runway %s", self.AirbaseName, runway.name), points)
|
||||
|
||||
return runway
|
||||
end
|
||||
|
||||
|
||||
-- Get DCS object.
|
||||
local airbase=self:GetDCSObject()
|
||||
|
||||
if airbase then
|
||||
|
||||
|
||||
-- Get DCS runways.
|
||||
local runways=airbase:getRunways()
|
||||
|
||||
-- Debug info.
|
||||
self:T2(runways)
|
||||
|
||||
if runways then
|
||||
|
||||
-- Loop over runways.
|
||||
for _,rwy in pairs(runways) do
|
||||
|
||||
-- Debug info.
|
||||
self:T(rwy)
|
||||
|
||||
-- Get runway data.
|
||||
local runway=_createRunway(rwy.Name, rwy.course, rwy.width, rwy.length, rwy.position) --#AIRBASE.Runway
|
||||
|
||||
-- Add to table.
|
||||
table.insert(Runways, runway)
|
||||
|
||||
-- Include "inverse" runway.
|
||||
if IncludeInverse then
|
||||
|
||||
-- Create "inverse".
|
||||
local idx=tonumber(runway.name)
|
||||
local name2=tostring(idx-18)
|
||||
if idx<18 then
|
||||
name2=tostring(idx+18)
|
||||
end
|
||||
|
||||
-- Create "inverse" runway.
|
||||
local runway=_createRunway(name2, rwy.course-math.pi, rwy.width, rwy.length, rwy.position) --#AIRBASE.Runway
|
||||
|
||||
-- Add inverse to table.
|
||||
table.insert(Runways, runway)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Look for identical (parallel) runways, e.g. 03L and 03R at Nellis.
|
||||
local rpairs={}
|
||||
for i,_ri in pairs(Runways) do
|
||||
local ri=_ri --#AIRBASE.Runway
|
||||
for j,_rj in pairs(Runways) do
|
||||
local rj=_rj --#AIRBASE.Runway
|
||||
if i<j then
|
||||
if ri.name==rj.name then
|
||||
rpairs[i]=j
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function isLeft(a, b, c)
|
||||
--return ((b.x - a.x)*(c.z - a.z) - (b.z - a.z)*(c.x - a.x)) > 0
|
||||
return ((b.z - a.z)*(c.x - a.x) - (b.x - a.x)*(c.z - a.z)) > 0
|
||||
end
|
||||
|
||||
--[[
|
||||
local a={x=1, y=0, z=0}
|
||||
local A={x=0, y=0, z=0}
|
||||
local b={x=0, y=0, z=1}
|
||||
local c={x=0, y=0, z=-1}
|
||||
local bl=isLeft(A, a, b)
|
||||
local cl=isLeft(A, a, c)
|
||||
env.info(string.format("b left=%s, c left=%s", tostring(bl), tostring(cl)))
|
||||
]]
|
||||
|
||||
for i,j in pairs(rpairs) do
|
||||
local ri=Runways[i] --#AIRBASE.Runway
|
||||
local rj=Runways[j] --#AIRBASE.Runway
|
||||
|
||||
-- Draw arrow.
|
||||
--ri.center:ArrowToAll(rj.center)
|
||||
|
||||
local c0=ri.center
|
||||
|
||||
-- Vector in the direction of the runway.
|
||||
local a=UTILS.VecTranslate(c0, 1000, ri.heading)
|
||||
|
||||
-- Vector from runway i to runway j.
|
||||
local b=UTILS.VecSubstract(rj.center, ri.center)
|
||||
b=UTILS.VecAdd(ri.center, b)
|
||||
|
||||
--[[
|
||||
local ca=COORDINATE:NewFromVec3(a)
|
||||
local cb=COORDINATE:NewFromVec3(b)
|
||||
c0:ArrowToAll(ca, nil , {0,1,0})
|
||||
c0:ArrowToAll(cb, nil , {0,0,1})
|
||||
]]
|
||||
|
||||
-- Check if rj is left of ri.
|
||||
local left=isLeft(c0, a, b)
|
||||
|
||||
--env.info(string.format("Found pair %s: i=%d, j=%d, left==%s", ri.name, i, j, tostring(left)))
|
||||
|
||||
if left then
|
||||
ri.isLeft=false
|
||||
rj.isLeft=true
|
||||
else
|
||||
ri.isLeft=true
|
||||
rj.isLeft=false
|
||||
end
|
||||
|
||||
--break
|
||||
end
|
||||
|
||||
-- Set runways.
|
||||
self.runways=Runways
|
||||
|
||||
return Runways
|
||||
end
|
||||
|
||||
|
||||
--- Get runways data. Only for airdromes!
|
||||
-- @param #AIRBASE self
|
||||
-- @param #number magvar (Optional) Magnetic variation in degrees.
|
||||
@ -1659,7 +1920,10 @@ end
|
||||
function AIRBASE:GetActiveRunway(magvar)
|
||||
|
||||
-- Get runways data (initialize if necessary).
|
||||
local runways=self:GetRunwayData(magvar)
|
||||
--local runways=self:GetRunwayData(magvar)
|
||||
|
||||
-- Get runway data.
|
||||
local runways=self:GetRunways()
|
||||
|
||||
-- Return user forced active runway if it was set.
|
||||
if self.activerwyno then
|
||||
@ -1695,9 +1959,6 @@ function AIRBASE:GetActiveRunway(magvar)
|
||||
-- Dot product: parallel component of the two vectors.
|
||||
local dot=UTILS.VecDot(Vwind, Vrunway)
|
||||
|
||||
-- Debug.
|
||||
--env.info(string.format("runway=%03d° dot=%.3f", runway.heading, dot))
|
||||
|
||||
-- New min?
|
||||
if dotmin==nil or dot<dotmin then
|
||||
dotmin=dot
|
||||
@ -1712,6 +1973,27 @@ function AIRBASE:GetActiveRunway(magvar)
|
||||
return runways[iact]
|
||||
end
|
||||
|
||||
--- Get name of the runway, e.g. "31L".
|
||||
-- @param #AIRBASE self
|
||||
-- @param #AIRBASE.Runway Runway The runway. Default is the active runway.
|
||||
-- @return #AIRBASE.Runway Active runway data table.
|
||||
function AIRBASE:GetRunwayName(Runway)
|
||||
|
||||
Runway=Runway or self:GetActiveRunway()
|
||||
|
||||
local name="Unknown"
|
||||
if Runway then
|
||||
name=Runway.name
|
||||
if Runway.isLeft==true then
|
||||
name=name.."L"
|
||||
elseif Runway.isLeft==false then
|
||||
name=name.."R"
|
||||
end
|
||||
end
|
||||
|
||||
return name
|
||||
end
|
||||
|
||||
--- Function that checks if at leat one unit of a group has been spawned close to a spawn point on the runway.
|
||||
-- @param #AIRBASE self
|
||||
-- @param Wrapper.Group#GROUP group Group to be checked.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user