Automatically detect when bases are captured

Stop dispatching cargo to captured bases
Stop using captured squadrons for intercepts
Display clear status information to players showing which bases are operational vs captured
Treat base capture as normal mission state, not an error condition
This commit is contained in:
iTracerFacer 2025-10-20 08:21:48 -05:00
parent ed662a6289
commit 4b18037c32
6 changed files with 352 additions and 2762 deletions

View File

@ -404,8 +404,8 @@ local function dispatchCargo(squadron, coalitionKey)
local destCoalition = destAirbase:GetCoalition() local destCoalition = destAirbase:GetCoalition()
if destCoalition ~= coalitionSide then if destCoalition ~= coalitionSide then
log("WARNING: Destination airbase '" .. destination .. "' is not controlled by " .. coalitionKey .. " coalition (currently coalition " .. tostring(destCoalition) .. "). This will cause RAT to fail with 'Airbase doesn't exist' error. Skipping dispatch.") log("INFO: Destination airbase '" .. destination .. "' captured by enemy - cargo dispatch skipped (normal mission state).", true)
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " aborted (airbase captured by enemy)!") -- No announcement to coalition - this is expected behavior when base is captured
-- Mark mission as failed and cleanup immediately -- Mark mission as failed and cleanup immediately
mission.status = "failed" mission.status = "failed"
return return
@ -423,10 +423,8 @@ local function dispatchCargo(squadron, coalitionKey)
local originCoalition = originAirbase:GetCoalition() local originCoalition = originAirbase:GetCoalition()
if originCoalition ~= coalitionSide then if originCoalition ~= coalitionSide then
log("WARNING: Origin airbase '" .. origin .. "' is not controlled by " .. coalitionKey .. " coalition (currently coalition " .. tostring(originCoalition) .. "). This will cause RAT to fail. Skipping dispatch.") log("INFO: Origin airbase '" .. origin .. "' captured by enemy - trying another supply source.", true)
announceToCoalition(coalitionKey, "Resupply mission from " .. origin .. " failed (origin airbase captured by enemy)!") -- Don't announce or mark as failed - the dispatcher will try another origin
-- Mark mission as failed and cleanup immediately
mission.status = "failed"
return return
end end
@ -581,18 +579,24 @@ end
-------------------------------------------------------------------------- --------------------------------------------------------------------------
Checks all squadrons for each coalition. If a squadron is below the resupply threshold and has no active cargo mission, Checks all squadrons for each coalition. If a squadron is below the resupply threshold and has no active cargo mission,
triggers a supply request and dispatches a cargo aircraft. triggers a supply request and dispatches a cargo aircraft.
Skips squadrons that are captured or not operational.
]] ]]
local function monitorSquadrons() local function monitorSquadrons()
for _, coalitionKey in ipairs({"red", "blue"}) do for _, coalitionKey in ipairs({"red", "blue"}) do
local config = CARGO_SUPPLY_CONFIG[coalitionKey] local config = CARGO_SUPPLY_CONFIG[coalitionKey]
local squadrons = (coalitionKey == "red") and RED_SQUADRON_CONFIG or BLUE_SQUADRON_CONFIG local squadrons = (coalitionKey == "red") and RED_SQUADRON_CONFIG or BLUE_SQUADRON_CONFIG
for _, squadron in ipairs(squadrons) do for _, squadron in ipairs(squadrons) do
local current, max, ratio = getSquadronStatus(squadron, coalitionKey) -- Skip non-operational squadrons (captured, destroyed, etc.)
log("Squadron status: " .. squadron.displayName .. " (" .. coalitionKey .. ") " .. current .. "/" .. max .. " ratio: " .. string.format("%.2f", ratio)) if squadron.state and squadron.state ~= "operational" then
if ratio <= config.threshold and not hasActiveCargoMission(coalitionKey, squadron.airbaseName) then log("Squadron " .. squadron.displayName .. " (" .. coalitionKey .. ") is " .. squadron.state .. " - skipping cargo dispatch", true)
log("Supply request triggered for " .. squadron.displayName .. " at " .. squadron.airbaseName) else
announceToCoalition(coalitionKey, "Supply requested for " .. squadron.airbaseName .. "! Squadron: " .. squadron.displayName) local current, max, ratio = getSquadronStatus(squadron, coalitionKey)
dispatchCargo(squadron, coalitionKey) log("Squadron status: " .. squadron.displayName .. " (" .. coalitionKey .. ") " .. current .. "/" .. max .. " ratio: " .. string.format("%.2f", ratio))
if ratio <= config.threshold and not hasActiveCargoMission(coalitionKey, squadron.airbaseName) then
log("Supply request triggered for " .. squadron.displayName .. " at " .. squadron.airbaseName)
announceToCoalition(coalitionKey, "Supply requested for " .. squadron.airbaseName .. "! Squadron: " .. squadron.displayName)
dispatchCargo(squadron, coalitionKey)
end
end end
end end
end end

View File

@ -319,11 +319,15 @@ end
-- Squadron resource summary generator -- Squadron resource summary generator
local function getSquadronResourceSummary(coalitionSide) local function getSquadronResourceSummary(coalitionSide)
local function getStatus(remaining, max) local function getStatus(remaining, max, state)
if state == "captured" then return "[CAPTURED]" end
if state == "destroyed" then return "[DESTROYED]" end
if state ~= "operational" then return "[OFFLINE]" end
local percent = (remaining / max) * 100 local percent = (remaining / max) * 100
if percent <= 10 then return "[CRITICAL]" end if percent <= 10 then return "[CRITICAL]" end
if percent <= 25 then return "[LOW]" end if percent <= 25 then return "[LOW]" end
return "OK" return "OK"
end end
local lines = {} local lines = {}
@ -336,19 +340,21 @@ local function getSquadronResourceSummary(coalitionSide)
for _, squadron in pairs(RED_SQUADRON_CONFIG) do for _, squadron in pairs(RED_SQUADRON_CONFIG) do
local remaining = squadronAircraftCounts.red[squadron.templateName] or 0 local remaining = squadronAircraftCounts.red[squadron.templateName] or 0
local max = squadron.aircraft or 0 local max = squadron.aircraft or 0
local status = getStatus(remaining, max) local state = squadron.state or "operational"
local status = getStatus(remaining, max, state)
table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status)) table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status))
end end
elseif coalitionSide == coalition.side.BLUE then elseif coalitionSide == coalition.side.BLUE then
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
local remaining = squadronAircraftCounts.blue[squadron.templateName] or 0 local remaining = squadronAircraftCounts.blue[squadron.templateName] or 0
local max = squadron.aircraft or 0 local max = squadron.aircraft or 0
local status = getStatus(remaining, max) local state = squadron.state or "operational"
local status = getStatus(remaining, max, state)
table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status)) table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status))
end end
end end
table.insert(lines, "\n- [LOW]: Below 25%\n- [CRITICAL]: Below 10%\n- OK: Above 25%") table.insert(lines, "\n- [CAPTURED]: Airbase captured by enemy\n- [LOW]: Below 25%\n- [CRITICAL]: Below 10%\n- OK: Above 25%")
return table.concat(lines, "\n") return table.concat(lines, "\n")
end end
@ -1404,8 +1410,20 @@ local function findBestSquadron(threatCoord, threatSize, coalitionSide)
local squadronAvailable = true local squadronAvailable = true
local unavailableReason = "" local unavailableReason = ""
-- Check squadron state first
if squadron.state and squadron.state ~= "operational" then
squadronAvailable = false
if squadron.state == "captured" then
unavailableReason = "airbase captured by enemy"
elseif squadron.state == "destroyed" then
unavailableReason = "airbase destroyed"
else
unavailableReason = "squadron not operational (state: " .. tostring(squadron.state) .. ")"
end
end
-- Check cooldown -- Check cooldown
if squadronCooldowns[coalitionKey][squadron.templateName] then if squadronAvailable and squadronCooldowns[coalitionKey][squadron.templateName] then
local cooldownEnd = squadronCooldowns[coalitionKey][squadron.templateName] local cooldownEnd = squadronCooldowns[coalitionKey][squadron.templateName]
if currentTime < cooldownEnd then if currentTime < cooldownEnd then
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60) local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
@ -1808,12 +1826,36 @@ local function checkAirbaseStatus()
if TADC_SETTINGS.enableRed then if TADC_SETTINGS.enableRed then
log("=== RED COALITION STATUS ===") log("=== RED COALITION STATUS ===")
for _, squadron in pairs(RED_SQUADRON_CONFIG) do for _, squadron in pairs(RED_SQUADRON_CONFIG) do
local usable, status = isAirbaseUsable(squadron.airbaseName, coalition.side.RED) local airbase = AIRBASE:FindByName(squadron.airbaseName)
-- Add aircraft count to status
local aircraftCount = squadronAircraftCounts.red[squadron.templateName] or 0 local aircraftCount = squadronAircraftCounts.red[squadron.templateName] or 0
local maxAircraft = squadron.aircraft local maxAircraft = squadron.aircraft
local aircraftStatus = " Aircraft: " .. aircraftCount .. "/" .. maxAircraft
-- Determine status based on squadron state
local statusPrefix = ""
local statusText = ""
local usable = false
if squadron.state == "operational" then
statusPrefix = ""
statusText = "Operational: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
usable = true
elseif squadron.state == "captured" then
-- Determine who captured it
local capturedBy = "enemy"
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.BLUE then
capturedBy = "Blue"
elseif airbaseCoalition == coalition.side.NEUTRAL then
capturedBy = "neutral forces"
end
end
statusText = "Captured by " .. capturedBy .. ": " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
elseif squadron.state == "destroyed" then
statusText = "Destroyed: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
else
statusText = "Unknown state: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
end
-- Add zone information if configured -- Add zone information if configured
local zoneStatus = "" local zoneStatus = ""
@ -1825,9 +1867,9 @@ local function checkAirbaseStatus()
zoneStatus = " Zones: " .. table.concat(zones, " ") zoneStatus = " Zones: " .. table.concat(zones, " ")
end end
-- Check if squadron is on cooldown -- Check if squadron is on cooldown (only show for operational squadrons)
local cooldownStatus = "" local cooldownStatus = ""
if squadronCooldowns.red[squadron.templateName] then if squadron.state == "operational" and squadronCooldowns.red[squadron.templateName] then
local cooldownEnd = squadronCooldowns.red[squadron.templateName] local cooldownEnd = squadronCooldowns.red[squadron.templateName]
if currentTime < cooldownEnd then if currentTime < cooldownEnd then
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60) local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
@ -1835,14 +1877,13 @@ local function checkAirbaseStatus()
end end
end end
local fullStatus = status .. aircraftStatus .. zoneStatus .. cooldownStatus local fullStatus = statusText .. zoneStatus .. cooldownStatus
if usable and cooldownStatus == "" and aircraftCount > 0 then if usable and cooldownStatus == "" and aircraftCount > 0 then
redUsableCount = redUsableCount + 1 redUsableCount = redUsableCount + 1
log("" .. squadron.airbaseName .. " - " .. fullStatus)
else
log("" .. squadron.airbaseName .. " - " .. fullStatus)
end end
log(statusPrefix .. " " .. squadron.airbaseName .. " - " .. fullStatus)
end end
log("RED Status: " .. redUsableCount .. "/" .. #RED_SQUADRON_CONFIG .. " airbases operational") log("RED Status: " .. redUsableCount .. "/" .. #RED_SQUADRON_CONFIG .. " airbases operational")
end end
@ -1851,12 +1892,36 @@ local function checkAirbaseStatus()
if TADC_SETTINGS.enableBlue then if TADC_SETTINGS.enableBlue then
log("=== BLUE COALITION STATUS ===") log("=== BLUE COALITION STATUS ===")
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
local usable, status = isAirbaseUsable(squadron.airbaseName, coalition.side.BLUE) local airbase = AIRBASE:FindByName(squadron.airbaseName)
-- Add aircraft count to status
local aircraftCount = squadronAircraftCounts.blue[squadron.templateName] or 0 local aircraftCount = squadronAircraftCounts.blue[squadron.templateName] or 0
local maxAircraft = squadron.aircraft local maxAircraft = squadron.aircraft
local aircraftStatus = " Aircraft: " .. aircraftCount .. "/" .. maxAircraft
-- Determine status based on squadron state
local statusPrefix = ""
local statusText = ""
local usable = false
if squadron.state == "operational" then
statusPrefix = ""
statusText = "Operational: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
usable = true
elseif squadron.state == "captured" then
-- Determine who captured it
local capturedBy = "enemy"
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.RED then
capturedBy = "Red"
elseif airbaseCoalition == coalition.side.NEUTRAL then
capturedBy = "neutral forces"
end
end
statusText = "Captured by " .. capturedBy .. ": " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
elseif squadron.state == "destroyed" then
statusText = "Destroyed: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
else
statusText = "Unknown state: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
end
-- Add zone information if configured -- Add zone information if configured
local zoneStatus = "" local zoneStatus = ""
@ -1868,9 +1933,9 @@ local function checkAirbaseStatus()
zoneStatus = " Zones: " .. table.concat(zones, " ") zoneStatus = " Zones: " .. table.concat(zones, " ")
end end
-- Check if squadron is on cooldown -- Check if squadron is on cooldown (only show for operational squadrons)
local cooldownStatus = "" local cooldownStatus = ""
if squadronCooldowns.blue[squadron.templateName] then if squadron.state == "operational" and squadronCooldowns.blue[squadron.templateName] then
local cooldownEnd = squadronCooldowns.blue[squadron.templateName] local cooldownEnd = squadronCooldowns.blue[squadron.templateName]
if currentTime < cooldownEnd then if currentTime < cooldownEnd then
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60) local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
@ -1878,14 +1943,14 @@ local function checkAirbaseStatus()
end end
end end
local fullStatus = status .. aircraftStatus .. zoneStatus .. cooldownStatus local fullStatus = statusText .. zoneStatus .. cooldownStatus
if usable and cooldownStatus == "" and aircraftCount > 0 then if usable and cooldownStatus == "" and aircraftCount > 0 then
blueUsableCount = blueUsableCount + 1 blueUsableCount = blueUsableCount + 1
log("" .. squadron.airbaseName .. " - " .. fullStatus)
else
log("" .. squadron.airbaseName .. " - " .. fullStatus)
end end
log(statusPrefix .. " " .. squadron.airbaseName .. " - " .. fullStatus)
end
end end
log("BLUE Status: " .. blueUsableCount .. "/" .. #BLUE_SQUADRON_CONFIG .. " airbases operational") log("BLUE Status: " .. blueUsableCount .. "/" .. #BLUE_SQUADRON_CONFIG .. " airbases operational")
end end
@ -1911,6 +1976,63 @@ local function cleanupOldDeliveries()
end end
end end
-- Update squadron states based on airbase coalition control
local function updateSquadronStates()
-- Update RED squadrons
for _, squadron in pairs(RED_SQUADRON_CONFIG) do
local airbase = AIRBASE:FindByName(squadron.airbaseName)
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.RED then
-- Only update to operational if not already operational (avoid spam)
if squadron.state ~= "operational" then
squadron.state = "operational"
log("RED Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " is now operational")
end
else
-- Airbase captured
if squadron.state ~= "captured" then
squadron.state = "captured"
log("RED Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " has been captured by enemy")
end
end
else
-- Airbase destroyed or not found
if squadron.state ~= "destroyed" then
squadron.state = "destroyed"
log("RED Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " airbase destroyed or not found")
end
end
end
-- Update BLUE squadrons
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
local airbase = AIRBASE:FindByName(squadron.airbaseName)
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.BLUE then
-- Only update to operational if not already operational (avoid spam)
if squadron.state ~= "operational" then
squadron.state = "operational"
log("BLUE Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " is now operational")
end
else
-- Airbase captured
if squadron.state ~= "captured" then
squadron.state = "captured"
log("BLUE Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " has been captured by enemy")
end
end
else
-- Airbase destroyed or not found
if squadron.state ~= "destroyed" then
squadron.state = "destroyed"
log("BLUE Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " airbase destroyed or not found")
end
end
end
end
-- System initialization -- System initialization
local function initializeSystem() local function initializeSystem()
log("Universal Dual-Coalition TADC starting...") log("Universal Dual-Coalition TADC starting...")
@ -1962,6 +2084,15 @@ local function initializeSystem()
return false return false
end end
-- Initialize squadron states
for _, squadron in pairs(RED_SQUADRON_CONFIG) do
squadron.state = "operational"
end
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
squadron.state = "operational"
end
log("Squadron states initialized")
-- Log enabled coalitions -- Log enabled coalitions
local enabledCoalitions = {} local enabledCoalitions = {}
if TADC_SETTINGS.enableRed then if TADC_SETTINGS.enableRed then
@ -2037,6 +2168,7 @@ local function initializeSystem()
SCHEDULER:New(nil, detectThreats, {}, 5, TADC_SETTINGS.checkInterval) SCHEDULER:New(nil, detectThreats, {}, 5, TADC_SETTINGS.checkInterval)
SCHEDULER:New(nil, monitorInterceptors, {}, 10, TADC_SETTINGS.monitorInterval) SCHEDULER:New(nil, monitorInterceptors, {}, 10, TADC_SETTINGS.monitorInterval)
SCHEDULER:New(nil, checkAirbaseStatus, {}, 30, TADC_SETTINGS.statusReportInterval) SCHEDULER:New(nil, checkAirbaseStatus, {}, 30, TADC_SETTINGS.statusReportInterval)
SCHEDULER:New(nil, updateSquadronStates, {}, 15, 30) -- Update squadron states every 30 seconds
SCHEDULER:New(nil, cleanupOldDeliveries, {}, 60, 3600) -- Cleanup old delivery records every hour SCHEDULER:New(nil, cleanupOldDeliveries, {}, 60, 3600) -- Cleanup old delivery records every hour
-- Start periodic squadron summary broadcast -- Start periodic squadron summary broadcast

View File

@ -404,8 +404,8 @@ local function dispatchCargo(squadron, coalitionKey)
local destCoalition = destAirbase:GetCoalition() local destCoalition = destAirbase:GetCoalition()
if destCoalition ~= coalitionSide then if destCoalition ~= coalitionSide then
log("WARNING: Destination airbase '" .. destination .. "' is not controlled by " .. coalitionKey .. " coalition (currently coalition " .. tostring(destCoalition) .. "). This will cause RAT to fail with 'Airbase doesn't exist' error. Skipping dispatch.") log("INFO: Destination airbase '" .. destination .. "' captured by enemy - cargo dispatch skipped (normal mission state).", true)
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " aborted (airbase captured by enemy)!") -- No announcement to coalition - this is expected behavior when base is captured
-- Mark mission as failed and cleanup immediately -- Mark mission as failed and cleanup immediately
mission.status = "failed" mission.status = "failed"
return return
@ -423,10 +423,8 @@ local function dispatchCargo(squadron, coalitionKey)
local originCoalition = originAirbase:GetCoalition() local originCoalition = originAirbase:GetCoalition()
if originCoalition ~= coalitionSide then if originCoalition ~= coalitionSide then
log("WARNING: Origin airbase '" .. origin .. "' is not controlled by " .. coalitionKey .. " coalition (currently coalition " .. tostring(originCoalition) .. "). This will cause RAT to fail. Skipping dispatch.") log("INFO: Origin airbase '" .. origin .. "' captured by enemy - trying another supply source.", true)
announceToCoalition(coalitionKey, "Resupply mission from " .. origin .. " failed (origin airbase captured by enemy)!") -- Don't announce or mark as failed - the dispatcher will try another origin
-- Mark mission as failed and cleanup immediately
mission.status = "failed"
return return
end end
@ -581,18 +579,24 @@ end
-------------------------------------------------------------------------- --------------------------------------------------------------------------
Checks all squadrons for each coalition. If a squadron is below the resupply threshold and has no active cargo mission, Checks all squadrons for each coalition. If a squadron is below the resupply threshold and has no active cargo mission,
triggers a supply request and dispatches a cargo aircraft. triggers a supply request and dispatches a cargo aircraft.
Skips squadrons that are captured or not operational.
]] ]]
local function monitorSquadrons() local function monitorSquadrons()
for _, coalitionKey in ipairs({"red", "blue"}) do for _, coalitionKey in ipairs({"red", "blue"}) do
local config = CARGO_SUPPLY_CONFIG[coalitionKey] local config = CARGO_SUPPLY_CONFIG[coalitionKey]
local squadrons = (coalitionKey == "red") and RED_SQUADRON_CONFIG or BLUE_SQUADRON_CONFIG local squadrons = (coalitionKey == "red") and RED_SQUADRON_CONFIG or BLUE_SQUADRON_CONFIG
for _, squadron in ipairs(squadrons) do for _, squadron in ipairs(squadrons) do
local current, max, ratio = getSquadronStatus(squadron, coalitionKey) -- Skip non-operational squadrons (captured, destroyed, etc.)
log("Squadron status: " .. squadron.displayName .. " (" .. coalitionKey .. ") " .. current .. "/" .. max .. " ratio: " .. string.format("%.2f", ratio)) if squadron.state and squadron.state ~= "operational" then
if ratio <= config.threshold and not hasActiveCargoMission(coalitionKey, squadron.airbaseName) then log("Squadron " .. squadron.displayName .. " (" .. coalitionKey .. ") is " .. squadron.state .. " - skipping cargo dispatch", true)
log("Supply request triggered for " .. squadron.displayName .. " at " .. squadron.airbaseName) else
announceToCoalition(coalitionKey, "Supply requested for " .. squadron.airbaseName .. "! Squadron: " .. squadron.displayName) local current, max, ratio = getSquadronStatus(squadron, coalitionKey)
dispatchCargo(squadron, coalitionKey) log("Squadron status: " .. squadron.displayName .. " (" .. coalitionKey .. ") " .. current .. "/" .. max .. " ratio: " .. string.format("%.2f", ratio))
if ratio <= config.threshold and not hasActiveCargoMission(coalitionKey, squadron.airbaseName) then
log("Supply request triggered for " .. squadron.displayName .. " at " .. squadron.airbaseName)
announceToCoalition(coalitionKey, "Supply requested for " .. squadron.airbaseName .. "! Squadron: " .. squadron.displayName)
dispatchCargo(squadron, coalitionKey)
end
end end
end end
end end

View File

@ -319,11 +319,15 @@ end
-- Squadron resource summary generator -- Squadron resource summary generator
local function getSquadronResourceSummary(coalitionSide) local function getSquadronResourceSummary(coalitionSide)
local function getStatus(remaining, max) local function getStatus(remaining, max, state)
if state == "captured" then return "[CAPTURED]" end
if state == "destroyed" then return "[DESTROYED]" end
if state ~= "operational" then return "[OFFLINE]" end
local percent = (remaining / max) * 100 local percent = (remaining / max) * 100
if percent <= 10 then return "[CRITICAL]" end if percent <= 10 then return "[CRITICAL]" end
if percent <= 25 then return "[LOW]" end if percent <= 25 then return "[LOW]" end
return "OK" return "OK"
end end
local lines = {} local lines = {}
@ -336,19 +340,21 @@ local function getSquadronResourceSummary(coalitionSide)
for _, squadron in pairs(RED_SQUADRON_CONFIG) do for _, squadron in pairs(RED_SQUADRON_CONFIG) do
local remaining = squadronAircraftCounts.red[squadron.templateName] or 0 local remaining = squadronAircraftCounts.red[squadron.templateName] or 0
local max = squadron.aircraft or 0 local max = squadron.aircraft or 0
local status = getStatus(remaining, max) local state = squadron.state or "operational"
local status = getStatus(remaining, max, state)
table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status)) table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status))
end end
elseif coalitionSide == coalition.side.BLUE then elseif coalitionSide == coalition.side.BLUE then
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
local remaining = squadronAircraftCounts.blue[squadron.templateName] or 0 local remaining = squadronAircraftCounts.blue[squadron.templateName] or 0
local max = squadron.aircraft or 0 local max = squadron.aircraft or 0
local status = getStatus(remaining, max) local state = squadron.state or "operational"
local status = getStatus(remaining, max, state)
table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status)) table.insert(lines, string.format("| %-13s | %2d / %-15d | %-11s |", squadron.displayName or squadron.templateName, remaining, max, status))
end end
end end
table.insert(lines, "\n- [LOW]: Below 25%\n- [CRITICAL]: Below 10%\n- OK: Above 25%") table.insert(lines, "\n- [CAPTURED]: Airbase captured by enemy\n- [LOW]: Below 25%\n- [CRITICAL]: Below 10%\n- OK: Above 25%")
return table.concat(lines, "\n") return table.concat(lines, "\n")
end end
@ -1404,8 +1410,20 @@ local function findBestSquadron(threatCoord, threatSize, coalitionSide)
local squadronAvailable = true local squadronAvailable = true
local unavailableReason = "" local unavailableReason = ""
-- Check squadron state first
if squadron.state and squadron.state ~= "operational" then
squadronAvailable = false
if squadron.state == "captured" then
unavailableReason = "airbase captured by enemy"
elseif squadron.state == "destroyed" then
unavailableReason = "airbase destroyed"
else
unavailableReason = "squadron not operational (state: " .. tostring(squadron.state) .. ")"
end
end
-- Check cooldown -- Check cooldown
if squadronCooldowns[coalitionKey][squadron.templateName] then if squadronAvailable and squadronCooldowns[coalitionKey][squadron.templateName] then
local cooldownEnd = squadronCooldowns[coalitionKey][squadron.templateName] local cooldownEnd = squadronCooldowns[coalitionKey][squadron.templateName]
if currentTime < cooldownEnd then if currentTime < cooldownEnd then
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60) local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
@ -1808,12 +1826,36 @@ local function checkAirbaseStatus()
if TADC_SETTINGS.enableRed then if TADC_SETTINGS.enableRed then
log("=== RED COALITION STATUS ===") log("=== RED COALITION STATUS ===")
for _, squadron in pairs(RED_SQUADRON_CONFIG) do for _, squadron in pairs(RED_SQUADRON_CONFIG) do
local usable, status = isAirbaseUsable(squadron.airbaseName, coalition.side.RED) local airbase = AIRBASE:FindByName(squadron.airbaseName)
-- Add aircraft count to status
local aircraftCount = squadronAircraftCounts.red[squadron.templateName] or 0 local aircraftCount = squadronAircraftCounts.red[squadron.templateName] or 0
local maxAircraft = squadron.aircraft local maxAircraft = squadron.aircraft
local aircraftStatus = " Aircraft: " .. aircraftCount .. "/" .. maxAircraft
-- Determine status based on squadron state
local statusPrefix = ""
local statusText = ""
local usable = false
if squadron.state == "operational" then
statusPrefix = ""
statusText = "Operational: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
usable = true
elseif squadron.state == "captured" then
-- Determine who captured it
local capturedBy = "enemy"
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.BLUE then
capturedBy = "Blue"
elseif airbaseCoalition == coalition.side.NEUTRAL then
capturedBy = "neutral forces"
end
end
statusText = "Captured by " .. capturedBy .. ": " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
elseif squadron.state == "destroyed" then
statusText = "Destroyed: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
else
statusText = "Unknown state: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
end
-- Add zone information if configured -- Add zone information if configured
local zoneStatus = "" local zoneStatus = ""
@ -1825,9 +1867,9 @@ local function checkAirbaseStatus()
zoneStatus = " Zones: " .. table.concat(zones, " ") zoneStatus = " Zones: " .. table.concat(zones, " ")
end end
-- Check if squadron is on cooldown -- Check if squadron is on cooldown (only show for operational squadrons)
local cooldownStatus = "" local cooldownStatus = ""
if squadronCooldowns.red[squadron.templateName] then if squadron.state == "operational" and squadronCooldowns.red[squadron.templateName] then
local cooldownEnd = squadronCooldowns.red[squadron.templateName] local cooldownEnd = squadronCooldowns.red[squadron.templateName]
if currentTime < cooldownEnd then if currentTime < cooldownEnd then
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60) local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
@ -1835,14 +1877,13 @@ local function checkAirbaseStatus()
end end
end end
local fullStatus = status .. aircraftStatus .. zoneStatus .. cooldownStatus local fullStatus = statusText .. zoneStatus .. cooldownStatus
if usable and cooldownStatus == "" and aircraftCount > 0 then if usable and cooldownStatus == "" and aircraftCount > 0 then
redUsableCount = redUsableCount + 1 redUsableCount = redUsableCount + 1
log("" .. squadron.airbaseName .. " - " .. fullStatus)
else
log("" .. squadron.airbaseName .. " - " .. fullStatus)
end end
log(statusPrefix .. " " .. squadron.airbaseName .. " - " .. fullStatus)
end end
log("RED Status: " .. redUsableCount .. "/" .. #RED_SQUADRON_CONFIG .. " airbases operational") log("RED Status: " .. redUsableCount .. "/" .. #RED_SQUADRON_CONFIG .. " airbases operational")
end end
@ -1851,12 +1892,36 @@ local function checkAirbaseStatus()
if TADC_SETTINGS.enableBlue then if TADC_SETTINGS.enableBlue then
log("=== BLUE COALITION STATUS ===") log("=== BLUE COALITION STATUS ===")
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
local usable, status = isAirbaseUsable(squadron.airbaseName, coalition.side.BLUE) local airbase = AIRBASE:FindByName(squadron.airbaseName)
-- Add aircraft count to status
local aircraftCount = squadronAircraftCounts.blue[squadron.templateName] or 0 local aircraftCount = squadronAircraftCounts.blue[squadron.templateName] or 0
local maxAircraft = squadron.aircraft local maxAircraft = squadron.aircraft
local aircraftStatus = " Aircraft: " .. aircraftCount .. "/" .. maxAircraft
-- Determine status based on squadron state
local statusPrefix = ""
local statusText = ""
local usable = false
if squadron.state == "operational" then
statusPrefix = ""
statusText = "Operational: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
usable = true
elseif squadron.state == "captured" then
-- Determine who captured it
local capturedBy = "enemy"
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.RED then
capturedBy = "Red"
elseif airbaseCoalition == coalition.side.NEUTRAL then
capturedBy = "neutral forces"
end
end
statusText = "Captured by " .. capturedBy .. ": " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
elseif squadron.state == "destroyed" then
statusText = "Destroyed: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
else
statusText = "Unknown state: " .. aircraftCount .. "/" .. maxAircraft .. " aircraft"
end
-- Add zone information if configured -- Add zone information if configured
local zoneStatus = "" local zoneStatus = ""
@ -1868,9 +1933,9 @@ local function checkAirbaseStatus()
zoneStatus = " Zones: " .. table.concat(zones, " ") zoneStatus = " Zones: " .. table.concat(zones, " ")
end end
-- Check if squadron is on cooldown -- Check if squadron is on cooldown (only show for operational squadrons)
local cooldownStatus = "" local cooldownStatus = ""
if squadronCooldowns.blue[squadron.templateName] then if squadron.state == "operational" and squadronCooldowns.blue[squadron.templateName] then
local cooldownEnd = squadronCooldowns.blue[squadron.templateName] local cooldownEnd = squadronCooldowns.blue[squadron.templateName]
if currentTime < cooldownEnd then if currentTime < cooldownEnd then
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60) local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
@ -1878,14 +1943,14 @@ local function checkAirbaseStatus()
end end
end end
local fullStatus = status .. aircraftStatus .. zoneStatus .. cooldownStatus local fullStatus = statusText .. zoneStatus .. cooldownStatus
if usable and cooldownStatus == "" and aircraftCount > 0 then if usable and cooldownStatus == "" and aircraftCount > 0 then
blueUsableCount = blueUsableCount + 1 blueUsableCount = blueUsableCount + 1
log("" .. squadron.airbaseName .. " - " .. fullStatus)
else
log("" .. squadron.airbaseName .. " - " .. fullStatus)
end end
log(statusPrefix .. " " .. squadron.airbaseName .. " - " .. fullStatus)
end
end end
log("BLUE Status: " .. blueUsableCount .. "/" .. #BLUE_SQUADRON_CONFIG .. " airbases operational") log("BLUE Status: " .. blueUsableCount .. "/" .. #BLUE_SQUADRON_CONFIG .. " airbases operational")
end end
@ -1911,6 +1976,63 @@ local function cleanupOldDeliveries()
end end
end end
-- Update squadron states based on airbase coalition control
local function updateSquadronStates()
-- Update RED squadrons
for _, squadron in pairs(RED_SQUADRON_CONFIG) do
local airbase = AIRBASE:FindByName(squadron.airbaseName)
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.RED then
-- Only update to operational if not already operational (avoid spam)
if squadron.state ~= "operational" then
squadron.state = "operational"
log("RED Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " is now operational")
end
else
-- Airbase captured
if squadron.state ~= "captured" then
squadron.state = "captured"
log("RED Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " has been captured by enemy")
end
end
else
-- Airbase destroyed or not found
if squadron.state ~= "destroyed" then
squadron.state = "destroyed"
log("RED Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " airbase destroyed or not found")
end
end
end
-- Update BLUE squadrons
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
local airbase = AIRBASE:FindByName(squadron.airbaseName)
if airbase and airbase:IsAlive() then
local airbaseCoalition = airbase:GetCoalition()
if airbaseCoalition == coalition.side.BLUE then
-- Only update to operational if not already operational (avoid spam)
if squadron.state ~= "operational" then
squadron.state = "operational"
log("BLUE Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " is now operational")
end
else
-- Airbase captured
if squadron.state ~= "captured" then
squadron.state = "captured"
log("BLUE Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " has been captured by enemy")
end
end
else
-- Airbase destroyed or not found
if squadron.state ~= "destroyed" then
squadron.state = "destroyed"
log("BLUE Squadron " .. squadron.displayName .. " at " .. squadron.airbaseName .. " airbase destroyed or not found")
end
end
end
end
-- System initialization -- System initialization
local function initializeSystem() local function initializeSystem()
log("Universal Dual-Coalition TADC starting...") log("Universal Dual-Coalition TADC starting...")
@ -1962,6 +2084,15 @@ local function initializeSystem()
return false return false
end end
-- Initialize squadron states
for _, squadron in pairs(RED_SQUADRON_CONFIG) do
squadron.state = "operational"
end
for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
squadron.state = "operational"
end
log("Squadron states initialized")
-- Log enabled coalitions -- Log enabled coalitions
local enabledCoalitions = {} local enabledCoalitions = {}
if TADC_SETTINGS.enableRed then if TADC_SETTINGS.enableRed then
@ -2037,6 +2168,7 @@ local function initializeSystem()
SCHEDULER:New(nil, detectThreats, {}, 5, TADC_SETTINGS.checkInterval) SCHEDULER:New(nil, detectThreats, {}, 5, TADC_SETTINGS.checkInterval)
SCHEDULER:New(nil, monitorInterceptors, {}, 10, TADC_SETTINGS.monitorInterval) SCHEDULER:New(nil, monitorInterceptors, {}, 10, TADC_SETTINGS.monitorInterval)
SCHEDULER:New(nil, checkAirbaseStatus, {}, 30, TADC_SETTINGS.statusReportInterval) SCHEDULER:New(nil, checkAirbaseStatus, {}, 30, TADC_SETTINGS.statusReportInterval)
SCHEDULER:New(nil, updateSquadronStates, {}, 15, 30) -- Update squadron states every 30 seconds
SCHEDULER:New(nil, cleanupOldDeliveries, {}, 60, 3600) -- Cleanup old delivery records every hour SCHEDULER:New(nil, cleanupOldDeliveries, {}, 60, 3600) -- Cleanup old delivery records every hour
-- Start periodic squadron summary broadcast -- Start periodic squadron summary broadcast

Binary file not shown.