mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-09-01 02:22:15 +00:00
Merge branch 'master-ng' into FF/Ops
This commit is contained in:
@@ -2717,17 +2717,27 @@ function AIRBOSS:DeleteRecoveryWindow( Window, Delay )
|
||||
self:ScheduleOnce( Delay, self.DeleteRecoveryWindow, self, Window )
|
||||
else
|
||||
|
||||
for i, _recovery in pairs( self.recoverytimes ) do
|
||||
local recovery = _recovery -- #AIRBOSS.Recovery
|
||||
if not Window then
|
||||
return
|
||||
end
|
||||
|
||||
if Window and Window.ID == recovery.ID then
|
||||
if Window.OPEN then
|
||||
-- Window is currently open.
|
||||
self:RecoveryStop()
|
||||
else
|
||||
table.remove( self.recoverytimes, i )
|
||||
end
|
||||
-- If this window is currently open, stop recovery first. Mark it OVER so the
|
||||
-- recovery time check cannot re-open it before/while we remove it.
|
||||
if Window.OPEN then
|
||||
Window.OPEN = false
|
||||
Window.OVER = true
|
||||
if self:IsRecovering() then
|
||||
self:RecoveryStop()
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove the window from the queue by its unique ID. Iterate over a numerically
|
||||
-- indexed copy of the keys and remove via ipairs-safe reverse loop so that the
|
||||
-- removal does not corrupt traversal (the window may appear once).
|
||||
for i = #self.recoverytimes, 1, -1 do
|
||||
local recovery = self.recoverytimes[i] -- #AIRBOSS.Recovery
|
||||
if recovery and recovery.ID == Window.ID then
|
||||
table.remove( self.recoverytimes, i )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4078,15 +4088,23 @@ function AIRBOSS:_CheckRecoveryTimes()
|
||||
if self:IsRecovering() then
|
||||
-- Carrier is already recovering.
|
||||
state = "in progress"
|
||||
else
|
||||
-- Start recovery.
|
||||
elseif not recovery.OVER then
|
||||
-- Start recovery. Only if the window has not already been closed/cancelled.
|
||||
-- The OVER guard prevents a window that was stopped manually (e.g. via the
|
||||
-- Skipper "Stop Recovery" menu) from being immediately re-opened on the next
|
||||
-- status tick while its [START,STOP) range is still active.
|
||||
self:RecoveryStart( recovery.CASE, recovery.OFFSET )
|
||||
state = "starting now"
|
||||
recovery.OPEN = true
|
||||
else
|
||||
-- Window was already closed/cancelled within its active time range.
|
||||
state = "cancelled"
|
||||
end
|
||||
|
||||
-- Set current recovery window.
|
||||
currwindow = recovery
|
||||
-- Set current recovery window (unless this window has been cancelled).
|
||||
if not recovery.OVER then
|
||||
currwindow = recovery
|
||||
end
|
||||
|
||||
else -- Stop time HAS passed.
|
||||
|
||||
@@ -4183,13 +4201,16 @@ function AIRBOSS:_CheckRecoveryTimes()
|
||||
-- Time into the wind 1 day or if longer recovery time + the 5 min early.
|
||||
local t = math.max( nextwindow.STOP - nextwindow.START + self.dTturn, 60 * 60 * 24 )
|
||||
|
||||
-- Recovery wind on deck in knots.
|
||||
-- Recovery wind on deck in knots.
|
||||
-- NOTE: Do NOT clamp the desired wind-over-deck (WOD) to the carrier's max hull
|
||||
-- speed here. WOD = carrier speed + headwind, so a WOD target above the hull's
|
||||
-- top speed is achievable whenever there is wind. CarrierTurnIntoWind ->
|
||||
-- GetHeadingIntoWind already converts the WOD target into the required hull
|
||||
-- speed and caps THAT at the carrier's max speed (Vmax) internally. Clamping the
|
||||
-- WOD target itself capped achievable WOD at ~30 kts (the supercarrier's max
|
||||
-- hull speed) even in strong wind, which made high-WOD recovery windows fall
|
||||
-- short of their requested value.
|
||||
local v = UTILS.KnotsToMps( nextwindow.SPEED )
|
||||
|
||||
-- Check that we do not go above max possible speed.
|
||||
local vmax = self.carrier:GetSpeedMax() / 3.6 -- convert to m/s
|
||||
v = math.min( v, vmax )
|
||||
|
||||
-- Route carrier into the wind. Sets self.turnintowind=true
|
||||
self:CarrierTurnIntoWind( t, v, uturn )
|
||||
|
||||
@@ -4361,11 +4382,20 @@ function AIRBOSS:onafterRecoveryStop( From, Event, To )
|
||||
self:CarrierResumeRoute( coord )
|
||||
end
|
||||
|
||||
-- Delete current recovery window if open.
|
||||
if self.recoverywindow and self.recoverywindow.OPEN == true then
|
||||
-- Mark the current recovery window closed and cancelled, then remove it from the
|
||||
-- queue. We do NOT gate this on Window.OPEN: that flag is only set by
|
||||
-- _CheckRecoveryTimes (not by RecoveryStart), and the recovery time check nils and
|
||||
-- rebuilds self.recoverywindow every status tick, so OPEN is not a reliable signal
|
||||
-- here. Setting OVER=true is what actually prevents the window from being re-opened
|
||||
-- on the next tick while its [START,STOP) range is still active.
|
||||
--
|
||||
-- The removal is deferred by one tick (Delay>0) so it does not mutate the
|
||||
-- recoverytimes table while _CheckRecoveryTimes may be iterating over it (the natural
|
||||
-- close path calls RecoveryStop() from inside that loop).
|
||||
if self.recoverywindow then
|
||||
self.recoverywindow.OPEN = false
|
||||
self.recoverywindow.OVER = true
|
||||
self:DeleteRecoveryWindow( self.recoverywindow )
|
||||
self:DeleteRecoveryWindow( self.recoverywindow, 1 )
|
||||
end
|
||||
|
||||
-- Check recovery windows. This sets self.recoverywindow to the next window.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
--
|
||||
-------------------------------------------------------------------------
|
||||
-- Date: September 2023
|
||||
-- Last Update: Mar 2026
|
||||
-- Last Update: June 2026
|
||||
-------------------------------------------------------------------------
|
||||
--
|
||||
--- **Ops** - Easy GCI & CAP Manager
|
||||
@@ -286,7 +286,7 @@ EASYGCICAP = {
|
||||
|
||||
--- EASYGCICAP class version.
|
||||
-- @field #string version
|
||||
EASYGCICAP.version="0.1.35"
|
||||
EASYGCICAP.version="0.1.36"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
--
|
||||
@@ -854,16 +854,18 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
end
|
||||
|
||||
if self.noalert5 > 0 then
|
||||
local alert
|
||||
if self.ClassName == "EASYGCICAP" then
|
||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
|
||||
elseif self.ClassName == "EASYA2G" then
|
||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.BAI)
|
||||
for i=1,self.noalert5 do
|
||||
local alert
|
||||
if self.ClassName == "EASYGCICAP" then
|
||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
|
||||
elseif self.ClassName == "EASYA2G" then
|
||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.BAI)
|
||||
end
|
||||
alert:SetRequiredAssets(self.capgrouping)
|
||||
alert:SetRepeat(99)
|
||||
CAP_Wing:AddMission(alert)
|
||||
table.insert(self.ListOfAuftrag,alert)
|
||||
end
|
||||
alert:SetRequiredAssets(self.noalert5)
|
||||
alert:SetRepeat(99)
|
||||
CAP_Wing:AddMission(alert)
|
||||
table.insert(self.ListOfAuftrag,alert)
|
||||
end
|
||||
|
||||
self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename }
|
||||
|
||||
Reference in New Issue
Block a user