Added "Vector me to nearest airbase" ATC command

This commit is contained in:
Ambroise Garel 2025-09-14 11:37:01 +02:00
parent e522d110bb
commit 5f220884bd
3 changed files with 73 additions and 26 deletions

View File

@ -202,9 +202,9 @@ Library.radioMessages = {
}, },
atcRequireNearestAirbase = { -- TODO: voiceover atcRequireNearestAirbase = { -- TODO: voiceover
"Roger. Vectoring you to the nearest airbase.\n$1", "Roger. Vectoring you to the nearest airbase.\n\n$1",
"Copy. Coordinates to nearest field inbound.\n$1", "Copy. Coordinates to nearest field inbound.\n\n$1",
"Roger. Guide you direct to the nearest recovery airfield.\n$1" "Roger. Guide you direct to the nearest recovery airfield.\n\n$1"
}, },
atcRequireNearestAirbaseNone = { -- TODO: voiceover atcRequireNearestAirbaseNone = { -- TODO: voiceover
"No friendly airbase is available at the moment.", "No friendly airbase is available at the moment.",

View File

@ -1,6 +1,25 @@
TUM.atc = {} TUM.atc = {}
do do
local function getFlyTime(playerUnit, point3)
local point2 = DCSEx.math.vec3ToVec2(point3)
local velocity = playerUnit:getVelocity()
local speed = math.max(1, math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z))
local distance = DCSEx.math.getDistance2D(point2, DCSEx.math.vec3ToVec2(playerUnit:getPoint()))
local timeInMinutes = math.max(1, math.floor(distance / (speed * 60)))
local eta = DCSEx.string.getTimeString(timer.getAbsTime() + timeInMinutes * 60)
if timeInMinutes > 600 then
return "More than ten hours of flight time at current airspeed\n"
elseif timeInMinutes > 120 then
return tostring(math.floor(timeInMinutes / 60)).." hours of flight time at current airspeed, ETA "..eta.."\n"
elseif timeInMinutes < 2 then
return "Less than 2 minutes of flight time at current airspeed, ETA "..eta.."\n"
else
return tostring(timeInMinutes).." minutes of flight time at current airspeed, ETA "..eta.."\n"
end
end
function TUM.atc.requestNavAssistanceToObjective(index, delayRadioAnswer) function TUM.atc.requestNavAssistanceToObjective(index, delayRadioAnswer)
local obj = TUM.objectives.getObjective(index) local obj = TUM.objectives.getObjective(index)
if not obj then return end if not obj then return end
@ -12,20 +31,7 @@ do
for _,p in ipairs(players) do for _,p in ipairs(players) do
-- Give BRA to objective -- Give BRA to objective
local navInfo = "- Fly "..DCSEx.dcs.getBRAA(obj.waypoint3, p:getPoint(), false).."\n" local navInfo = "- Fly "..DCSEx.dcs.getBRAA(obj.waypoint3, p:getPoint(), false).."\n"
navInfo = navInfo.."- "..getFlyTime(p, obj.waypoint3) -- Give flight time and ETA
-- Give flight time and ETA
local velocity = p:getVelocity()
local speed = math.max(1, math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z))
local distance = DCSEx.math.getDistance2D(obj.waypoint2, DCSEx.math.vec3ToVec2(p:getPoint()))
local timeInMinutes = math.max(1, math.floor(distance / (speed * 60)))
local eta = DCSEx.string.getTimeString(timer.getAbsTime() + timeInMinutes * 60)
if timeInMinutes > 600 then
navInfo = navInfo.."- More than ten hours of flight time at current airspeed\n"
elseif timeInMinutes > 120 then
navInfo = navInfo.."- "..tostring(math.floor(timeInMinutes / 60)).." hours of flight time at current airspeed, ETA "..eta.."\n"
else
navInfo = navInfo.."- "..tostring(timeInMinutes).." minute(s) of flight time at current airspeed, ETA "..eta.."\n"
end
-- Give objective coordinates -- Give objective coordinates
if obj.preciseCoordinates then if obj.preciseCoordinates then
@ -39,16 +45,16 @@ do
end end
end end
function TUM.atc.requireNearestAirbase(delayRadioAnswer) function TUM.atc.requestNavAssistanceToAirbase(delayRadioAnswer)
local players = coalition.getPlayers(TUM.settings.getPlayerCoalition()) local players = coalition.getPlayers(TUM.settings.getPlayerCoalition())
for _,p in ipairs(players) do for _,p in ipairs(players) do
local airbaseInfo = "- No airbase available near you at the moment." -- TODO: proper "no airbase" message local airbaseInfo = nil -- Mark as nil, in case no valid airbase is found
local validAirbaseTypes = { Airbase.Category.AIRDROME } local validAirbaseTypes = { Airbase.Category.AIRDROME }
if p:hasAttribute("Helicopters") then table.insert(validAirbaseTypes, Airbase.Category.HELIPAD) end if p:hasAttribute("Helicopters") then table.insert(validAirbaseTypes, Airbase.Category.HELIPAD) end
local pDesc = p:getDesc() local pDesc = p:getDesc()
if pDesc.LandRWCategories and #pDesc.LandRWCategories > 0 then if pDesc.LandRWCategories and #pDesc.LandRWCategories > 0 then
-- TODO: check player unit description to filter compatible carrier types -- TODO: check player unit description to filter compatible carrier types (Harrier/helos can land anywhere, naval fighters can land on carriers, etc)
table.insert(validAirbaseTypes, Airbase.Category.SHIP) table.insert(validAirbaseTypes, Airbase.Category.SHIP)
end end
@ -57,16 +63,57 @@ do
allAirbases = DCSEx.dcs.getNearestObjects(DCSEx.math.vec3ToVec2(p:getPoint()), allAirbases) allAirbases = DCSEx.dcs.getNearestObjects(DCSEx.math.vec3ToVec2(p:getPoint()), allAirbases)
for i=1,#allAirbases do for i=1,#allAirbases do
local abDesc = airbaseInfo[i]:getDesc() local abDesc = allAirbases[i]:getDesc()
if DCSEx.table.contains(validAirbaseTypes, abDesc.category) then if DCSEx.table.contains(validAirbaseTypes, abDesc.category) then
airbaseInfo = abDesc.displayName local abPoint = allAirbases[i]:getPoint()
break
if abDesc.category == Airbase.Category.AIRDROME then
airbaseInfo = abDesc.displayName:upper().." AIRBASE:\n"
else -- Helipad or ship
airbaseInfo = abDesc.displayName:upper()..":\n"
end
airbaseInfo = airbaseInfo.."- Fly "..DCSEx.dcs.getBRAA(abPoint, p:getPoint(), false).."\n"
airbaseInfo = airbaseInfo.."- "..getFlyTime(p, abPoint).."\n"
airbaseInfo = airbaseInfo..DCSEx.world.getCoordinatesAsString(abPoint, false)
local runways = allAirbases[i]:getRunways()
if #runways > 0 then
airbaseInfo = airbaseInfo.."\n\nRunways: "
for j=1,#runways do
-- Compute the runway course (in degrees, divided by 10)
local courseDeg = math.floor(DCSEx.converter.radiansToDegrees(runways[j].course * -1))
if courseDeg < 0 then courseDeg = courseDeg + 360 end
if courseDeg >= 360 then courseDeg = courseDeg - 360 end
courseDeg = math.floor(courseDeg / 10)
-- Compute the opposite runway coursecourseDegNeg
local courseDegNeg = courseDeg + 18
if courseDegNeg >= 36 then courseDegNeg = courseDegNeg - 36 end
-- Make sure the lowest runway heading is displayed first
if courseDeg > courseDegNeg then
local tmp = courseDegNeg
courseDegNeg = courseDeg
courseDeg = tmp
end
airbaseInfo = airbaseInfo..tostring(courseDeg).."/"..tostring(courseDegNeg).." ("..tostring(math.floor(runways[j].length)).." m)"
if j < #runways then airbaseInfo = airbaseInfo..", " end
end
end
-- TODO: radio tower frequency?
break -- Stop after finding one
end end
end end
end end
TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "atcRequireNearestAirbase", { airbaseInfo }, "Control", delayRadioAnswer) if airbaseInfo then
TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "atcRequireNearestAirbase", { airbaseInfo }, "Control", delayRadioAnswer, nil, false, 2.5)
else
TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "atcRequireNearestAirbaseNone", nil, "Control", delayRadioAnswer)
end
end end
end end

View File

@ -17,7 +17,7 @@ do
local function doCommandNearestAirbase() local function doCommandNearestAirbase()
TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerATCRequireNearestAirbase", nil, TUM.mission.getPlayerCallsign(), false) TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerATCRequireNearestAirbase", nil, TUM.mission.getPlayerCallsign(), false)
TUM.atc.requestNavAssistanceToAirbase(false) TUM.atc.requestNavAssistanceToAirbase(true)
end end
local function doCommandObjectiveLocation(index) local function doCommandObjectiveLocation(index)
@ -39,7 +39,7 @@ do
local objectivesMenuRoot = missionCommands.addSubMenu("❖ Objectives", rootMenu) local objectivesMenuRoot = missionCommands.addSubMenu("❖ Objectives", rootMenu)
local navigationMenuRoot = missionCommands.addSubMenu("➽ Navigation", rootMenu) local navigationMenuRoot = missionCommands.addSubMenu("➽ Navigation", rootMenu)
-- missionCommands.addCommand("Nav to nearest airbase", navigationMenuRoot, doCommandNearestAirbase, nil) missionCommands.addCommand("Nav to nearest airbase", navigationMenuRoot, doCommandNearestAirbase, nil)
for i=1,TUM.objectives.getCount() do for i=1,TUM.objectives.getCount() do
local obj = TUM.objectives.getObjective(i) local obj = TUM.objectives.getObjective(i)