- Improved FLIGHTCONTROL and minor other classes
This commit is contained in:
Frank 2022-06-28 18:55:43 +02:00
parent a8477940e2
commit 638f261bf4
8 changed files with 1047 additions and 284 deletions

View File

@ -5376,7 +5376,6 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param DCS#coalition.side Coalition Coalition side which originally captured the warehouse.
function WAREHOUSE:onafterRunwayDestroyed(From, Event, To)
-- Message.

File diff suppressed because it is too large Load Diff

View File

@ -870,21 +870,21 @@ function FLIGHTGROUP:Status()
if self:IsParking() then
for _,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
-- Check for parking spot.
if element.parking then
-- Get distance to assigned parking spot.
local dist=element.unit:GetCoordinate():Get2DDistance(element.parking.Coordinate)
local dist=element.unit:GetCoord():Get2DDistance(element.parking.Coordinate)
--env.info(string.format("FF dist to parking spot %d = %.1f meters", element.parking.TerminalID, dist))
-- If distance >10 meters, we consider the unit as taxiing.
-- At least for fighters, the initial distance seems to be 1.8 meters.
-- TODO: Check distance threshold! If element is taxiing, the parking spot is free again.
-- When the next plane is spawned on this spot, collisions should be avoided!
if dist>10 then
if element.status==OPSGROUP.ElementStatus.ENGINEON then
-- At least for fighters, the initial distance seems to be around 1.8 meters.
if dist>12 and element.engineOn then
--if element.status==OPSGROUP.ElementStatus.ENGINEON then
self:ElementTaxiing(element)
end
--end
end
else
@ -1163,6 +1163,9 @@ function FLIGHTGROUP:OnEventEngineStartup(EventData)
-- Element started engies.
self:ElementEngineOn(element)
-- Engines are on.
element.engineOn=true
--[[
-- TODO: could be that this element is part of a human flight group.
-- Problem: when player starts hot, the AI does too and starts to taxi immidiately :(
@ -1254,6 +1257,9 @@ function FLIGHTGROUP:OnEventEngineShutdown(EventData)
if element then
-- Engines are off.
element.engineOn=false
if element.unit and element.unit:IsAlive() then
local airbase=self:GetClosestAirbase()
@ -1395,8 +1401,10 @@ function FLIGHTGROUP:onafterElementParking(From, Event, To, Element, Spot)
-- Wait for engine startup event.
elseif self:IsTakeoffHot() then
self:__ElementEngineOn(0.5, Element) -- delay a bit to allow all elements
Element.engineOn=true
elseif self:IsTakeoffRunway() then
self:__ElementEngineOn(0.5, Element)
Element.engineOn=true
end
end
@ -1618,6 +1626,11 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To)
text=text..string.format("Start Cold = %s\n", tostring(self:IsTakeoffCold()))
text=text..string.format("Start Hot = %s\n", tostring(self:IsTakeoffHot()))
text=text..string.format("Start Rwy = %s\n", tostring(self:IsTakeoffRunway()))
text=text..string.format("Elements:")
for i,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
text=text..string.format("\n[%d] %s: callsign=%s, modex=%s, player=%s", i, element.name, tostring(element.callsign), tostring(element.modex), tostring(element.playerName))
end
self:I(self.lid..text)
end
@ -2651,6 +2664,22 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand)
-- Add flight to inbound queue.
self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.INBOUND)
-- Callsign.
local callsign=self:GetCallsignName()
-- Pilot calls inbound for landing.
local text=string.format("%s, %s, inbound for landing", fc.alias, callsign)
-- Radio message.
fc:TransmissionPilot(text, self)
-- Message text.
local text=string.format("%s, %s, roger, hold at angels %d. Report entering the pattern.", callsign, fc.alias, stack.angels)
-- Send message.
fc:TransmissionTower(text, self, 10)
end
-- Some intermediate coordinate to climb to the default cruise alitude.
@ -2914,6 +2943,29 @@ function FLIGHTGROUP:onafterHolding(From, Event, To)
-- Set flight status to holding.
self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.HOLDING)
if self.isAI then
-- Callsign.
local callsign=self:GetCallsignName()
-- Pilot arrived at holding pattern.
local text=string.format("%s, %s, arrived at holding pattern", self.flightcontrol.alias, callsign)
if self.stack then
text=text..string.format(", angels %d.", self.stack.angels)
end
-- Radio message.
self.flightcontrol:TransmissionPilot(text, self)
-- Message to flight
local text=string.format("%s, roger, fly heading %d and wait for landing clearance", callsign, self.stack.heading)
-- Radio message from tower.
self.flightcontrol:TransmissionTower(text, self, 10)
end
elseif self.airboss then
if self.isHelo then
@ -3900,16 +3952,16 @@ function FLIGHTGROUP:GetParkingSpot(element, maxdist, airbase)
local coord=element.unit:GetCoordinate()
-- Airbase.
airbase=airbase or self:GetClosestAirbase() --coord:GetClosestAirbase(nil, self:GetCoalition())
airbase=airbase or self:GetClosestAirbase()
-- TODO: replace by airbase.parking if AIRBASE is updated.
local parking=airbase:GetParkingSpotsTable()
-- Parking table of airbase.
local parking=airbase.parking --:GetParkingSpotsTable()
-- If airbase is ship, translate parking coords. Alternatively, we just move the coordinate of the unit to the origin of the map, which is way more efficient.
if airbase and airbase:IsShip() then
coord.x=0
coord.z=0
maxdist=500 -- 100 meters was not enough, e.g. on the Seawise Giant, where the spot is 139 meters from the "center"
maxdist=500 -- 100 meters was not enough, e.g. on the Seawise Giant, where the spot is 139 meters from the "center".
end
local spot=nil --Wrapper.Airbase#AIRBASE.ParkingSpot
@ -3917,8 +3969,10 @@ function FLIGHTGROUP:GetParkingSpot(element, maxdist, airbase)
local distmin=math.huge
for _,_parking in pairs(parking) do
local parking=_parking --Wrapper.Airbase#AIRBASE.ParkingSpot
-- Distance to spot.
dist=coord:Get2DDistance(parking.Coordinate)
--env.info(string.format("FF parking %d dist=%.1f", parking.TerminalID, dist))
if dist<distmin then
distmin=dist
spot=_parking
@ -4357,6 +4411,7 @@ function FLIGHTGROUP:_CreateMenuAtcHelp(rootmenu)
---
MENU_GROUP_COMMAND:New(self.group, "Subtitles On/Off", helpmenu, self._PlayerSubtitles, self)
MENU_GROUP_COMMAND:New(self.group, "My Voice On/Off", helpmenu, self._MenuNotImplemented, self, groupname)
MENU_GROUP_COMMAND:New(self.group, "Mark Parking", helpmenu, self._MarkParking, self)
MENU_GROUP_COMMAND:New(self.group, "Update Menu", helpmenu, self._UpdateMenu, self, 0)
MENU_GROUP_COMMAND:New(self.group, "My Status", helpmenu, self._PlayerMyStatus, self, groupname)
@ -4390,6 +4445,9 @@ function FLIGHTGROUP:_PlayerMyStatus()
-- Player data.
local playerdata=self:_GetPlayerData()
-- Player element.
local playerElement=self:GetPlayerElement()
-- Status text.
local text=string.format("My Status:")
text=text..string.format("\nPlayer Name: %s", tostring(playerdata.name))
@ -4399,6 +4457,15 @@ function FLIGHTGROUP:_PlayerMyStatus()
text=text..string.format("\nSubtitles: %s", tostring(playerdata.subtitles))
text=text..string.format("\nMy Voice: %s", tostring(playerdata.myvoice))
if fc then
if playerElement.parking then
local spot=fc:GetParkingSpotByID(playerElement.parking.TerminalID)
if spot then
text=text..string.format("\nParking spot: %d [%s]", spot.TerminalID, spot.Status or "Unknown")
end
end
end
-- Send message.
MESSAGE:New(text, 10, nil, true):ToGroup(self.group)
@ -4425,6 +4492,43 @@ function FLIGHTGROUP:_PlayerSubtitles()
end
--- Player mark parking.
-- @param #FLIGHTGROUP self
function FLIGHTGROUP:_MarkParking()
local playerElement=self:GetPlayerElement()
if playerElement then
-- Player name.
local playerName=tostring(playerElement.playerName)
-- Message text.
local message=string.format("No assigned parking spot for you could be found, %s", playerName)
if playerElement.parking then
local terminalID=playerElement.parking.TerminalID
local spotStatus=tostring(playerElement.parking.Status)
-- Marker text.
local text=string.format("Your parking spot, %s\nTerminal ID=%d [%s]", playerName, terminalID, spotStatus)
-- Text message.
message=string.format("%s, your parking spot is Terminal ID=%d [%s]. Check the marker on the F10 map.", playerName, terminalID, spotStatus)
-- New marker.
playerElement.parking.Coordinate:MarkToGroup(text, self.group)
end
-- Text message to group.
MESSAGE:New(string.format(message, playerName), 10):ToGroup(self.group)
end
end
--- Player set skill.
-- @param #FLIGHTGROUP self
-- @param #string Skill Skill.

View File

@ -1869,8 +1869,14 @@ function LEGION:GetOpsGroups(MissionTypes, Attributes)
for _,_cohort in pairs(self.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
-- Get cohort set.
local setCohort=cohort:GetOpsGroups(MissionTypes, Attributes)
self:I(self.lid..string.format("Found %d opsgroups of cohort %s", setCohort:Count(), cohort.name))
-- Debug info.
self:T2(self.lid..string.format("Found %d opsgroups of cohort %s", setCohort:Count(), cohort.name))
-- Add to legion set.
setLegion:AddSet(setCohort)
end
@ -2348,7 +2354,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
end
-- Debug info.
cohort:I(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, Category=%s, Attribute=%s, Property=%s, Weapon=%s",
cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, Category=%s, Attribute=%s, Property=%s, Weapon=%s",
cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty), tostring(RightWeapon)))
-- Check OnDuty, capable, in range and refueling type (if TANKER).

View File

@ -81,7 +81,7 @@ OPERATION.PhaseStatus={
--- OPERATION class version.
-- @field #string version
OPERATION.version="0.0.1"
OPERATION.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -232,6 +232,15 @@ end
-- User API Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set verbosity level.
-- @param #OPERATION self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #OPERATION self
function OPERATION:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Create a new generic OPERATION object.
-- @param #OPERATION self
-- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number.
@ -658,6 +667,9 @@ function OPERATION:onafterStatusUpdate(From, Event, To)
self:_CheckPhases()
end
-- Debug output.
if self.verbose>=1 then
-- Current phase.
local currphase=self:GetPhaseActive()
local phaseName="None"
@ -673,6 +685,11 @@ function OPERATION:onafterStatusUpdate(From, Event, To)
local text=string.format("State=%s: Phase=%s, Phases=%d [Active=%d, Planned=%d, Over=%d]", fsmstate, phaseName, NphaseTot, NphaseAct, NphasePla, NphaseOvr)
self:I(self.lid..text)
end
-- Debug output.
if self.verbose>=2 then
-- Info on phases.
local text="Phases:"
for i,_phase in pairs(self.phases) do
@ -682,6 +699,8 @@ function OPERATION:onafterStatusUpdate(From, Event, To)
if text=="Phases:" then text=text.." None" end
self:I(self.lid..text)
end
-- Next status update.
self:__StatusUpdate(-30)
end

View File

@ -210,6 +210,7 @@ OPSGROUP = {
-- @field #string skill Skill level.
-- @field #string playerName Name of player if this is a client.
-- @field #number Nhit Number of times the element was hit.
-- @field #boolean engineOn If `true`, engines were started.
--
-- @field Core.Zone#ZONE_POLYGON_BASE zoneBoundingbox Bounding box zone of the element unit.
-- @field Core.Zone#ZONE_POLYGON_BASE zoneLoad Loading zone.
@ -5831,8 +5832,6 @@ end
-- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever).
function OPSGROUP:onbeforeWait(From, Event, To, Duration)
env.info("FF before wait")
local allowed=true
local Tsuspend=nil
@ -11716,28 +11715,11 @@ function OPSGROUP:GetCallsignName()
if element then
self:T2(self.lid..string.format("Callsign %s", tostring(element.callsign)))
return element.callsign
local name=element.callsign or "Ghostrider11"
name=name:gsub("-", "")
return name
end
--[[
local numberSquad=self.callsign.NumberSquad or self.callsignDefault.NumberSquad
local numberGroup=self.callsign.NumberGroup or self.callsignDefault.NumberGroup
local callsign="Unknown 1"
if numberSquad and numberGroup then
local nameSquad=UTILS.GetCallsignName(numberSquad)
callsign=string.format("%s %d", nameSquad, numberGroup)
else
end
]]
return "Ghostrider11"
end

View File

@ -529,37 +529,41 @@ function MSRS:PlayText(Text, Delay)
-- Execute command.
self:_ExecCommand(command)
--[[
-- Check that length of command is max 255 chars or os.execute() will not work!
if string.len(command)>255 then
-- Create a tmp file.
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat"
local script = io.open(filename, "w+")
script:write(command.." && exit")
script:close()
-- Play command.
command=string.format("\"%s\"", filename)
-- Play file in 0.05 seconds
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
else
-- Debug output.
self:I(string.format("MSRS Text command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
end
]]
return self
end
--- Play text message via STTS with explicitly specified options.
-- @param #MSRS self
-- @param #string Text Text message.
-- @param #number Delay Delay in seconds, before the message is played.
-- @return #MSRS self
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
else
-- Ensure table.
if Frequencies and type(Frequencies)~="table" then
Frequencies={Frequencies}
end
-- Ensure table.
if Modulations and type(Modulations)~="table" then
Modulations={Modulations}
end
-- Get command line.
local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label)
-- Append text.
command=command..string.format(" --text=\"%s\"", tostring(Text))
-- Execute command.
self:_ExecCommand(command)
end
return self

View File

@ -523,8 +523,9 @@ AIRBASE.SouthAtlantic={
-- @field #string AirbaseName Name of the airbase.
-- @field #number MarkerID Numerical ID of marker placed at parking spot.
-- @field Wrapper.Marker#MARKER Marker The marker on the F10 map.
-- @field #string ClientSpot Client unit sitting at this spot or *nil*.
-- @field #string Status Status of spot e.g. AIRBASE.SpotStatus.FREE.
-- @field #string ClientSpot If `true`, this is a parking spot of a client aircraft.
-- @field #string ClientName Client unit name of this spot.
-- @field #string Status Status of spot e.g. `AIRBASE.SpotStatus.FREE`.
-- @field #string OccupiedBy Name of the aircraft occupying the spot or "unknown". Can be *nil* if spot is not occupied.
-- @field #string ReservedBy Name of the aircraft for which this spot is reserved. Can be *nil* if spot is not reserved.
@ -1081,11 +1082,11 @@ function AIRBASE:_InitParkingSpots()
local Coord=COORDINATE:New(unit.x, unit.alt, unit.y)
local dist=Coord:Get2DDistance(coord)
if dist<2 then
return true
return true, clientname
end
end
end
return false
return false, nil
end
-- Put coordinates of parking spots into table.
@ -1101,7 +1102,7 @@ function AIRBASE:_InitParkingSpots()
park.TerminalID0=spot.Term_Index_0
park.TerminalType=spot.Term_Type
park.TOAC=spot.TO_AC
park.ClientSpot=isClient(park.Coordinate)
park.ClientSpot, park.ClientName=isClient(park.Coordinate)
self.NparkingTotal=self.NparkingTotal+1
@ -2103,8 +2104,9 @@ end
--- Get name of a given runway, e.g. "31L".
-- @param #AIRBASE self
-- @param #AIRBASE.Runway Runway The runway. Default is the active runway.
-- @param #boolean LongLeftRight If `true`, return "Left" or "Right" instead of "L" or "R".
-- @return #string Name of the runway or "XX" if it could not be found.
function AIRBASE:GetRunwayName(Runway)
function AIRBASE:GetRunwayName(Runway, LongLeftRight)
Runway=Runway or self:GetActiveRunway()
@ -2112,11 +2114,19 @@ function AIRBASE:GetRunwayName(Runway)
if Runway then
name=Runway.name
if Runway.isLeft==true then
if LongLeftRight then
name=name.." Left"
else
name=name.."L"
end
elseif Runway.isLeft==false then
if LongLeftRight then
name=name.." Right"
else
name=name.."R"
end
end
end
return name
end