mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
Bug Fixes to TADC Main and TADC Cargo Systems
This commit is contained in:
parent
a3da2a6b8b
commit
ed662a6289
Binary file not shown.
Binary file not shown.
@ -133,6 +133,20 @@ log("[DEBUG] The Lakes zone initialization complete")
|
|||||||
|
|
||||||
|
|
||||||
-- Helper functions for tactical information
|
-- Helper functions for tactical information
|
||||||
|
|
||||||
|
-- Global cached unit set - created once and maintained automatically by MOOSE
|
||||||
|
local CachedUnitSet = nil
|
||||||
|
|
||||||
|
-- Initialize the cached unit set once
|
||||||
|
local function InitializeCachedUnitSet()
|
||||||
|
if not CachedUnitSet then
|
||||||
|
CachedUnitSet = SET_UNIT:New()
|
||||||
|
:FilterCategories({"ground", "plane", "helicopter"}) -- Only scan relevant unit types
|
||||||
|
:FilterOnce() -- Don't filter continuously, we'll use the live set
|
||||||
|
log("[PERFORMANCE] Initialized cached unit set for zone scanning")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function GetZoneForceStrengths(ZoneCapture)
|
local function GetZoneForceStrengths(ZoneCapture)
|
||||||
local zone = ZoneCapture:GetZone()
|
local zone = ZoneCapture:GetZone()
|
||||||
if not zone then return {red = 0, blue = 0, neutral = 0} end
|
if not zone then return {red = 0, blue = 0, neutral = 0} end
|
||||||
@ -141,15 +155,15 @@ local function GetZoneForceStrengths(ZoneCapture)
|
|||||||
local blueCount = 0
|
local blueCount = 0
|
||||||
local neutralCount = 0
|
local neutralCount = 0
|
||||||
|
|
||||||
-- Simple approach: scan all units and check if they're in the zone
|
-- Use MOOSE's optimized zone scanning instead of manual distance checks
|
||||||
local coord = zone:GetCoordinate()
|
local success, scannedUnits = pcall(function()
|
||||||
local radius = zone:GetRadius() or 1000
|
return zone:GetScannedUnits()
|
||||||
|
end)
|
||||||
|
|
||||||
local allUnits = SET_UNIT:New():FilterStart()
|
if success and scannedUnits then
|
||||||
allUnits:ForEachUnit(function(unit)
|
-- Use MOOSE's built-in scanned units (much faster)
|
||||||
|
for _, unit in pairs(scannedUnits) do
|
||||||
if unit and unit:IsAlive() then
|
if unit and unit:IsAlive() then
|
||||||
local unitCoord = unit:GetCoordinate()
|
|
||||||
if unitCoord and coord:Get2DDistance(unitCoord) <= radius then
|
|
||||||
local unitCoalition = unit:GetCoalition()
|
local unitCoalition = unit:GetCoalition()
|
||||||
if unitCoalition == coalition.side.RED then
|
if unitCoalition == coalition.side.RED then
|
||||||
redCount = redCount + 1
|
redCount = redCount + 1
|
||||||
@ -160,7 +174,32 @@ local function GetZoneForceStrengths(ZoneCapture)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
else
|
||||||
|
-- Fallback: Use zone's built-in scanning with the cached set
|
||||||
|
InitializeCachedUnitSet()
|
||||||
|
|
||||||
|
-- Use zone radius to limit search area
|
||||||
|
local coord = zone:GetCoordinate()
|
||||||
|
local radius = zone:GetRadius() or 1000
|
||||||
|
|
||||||
|
-- Only scan units within a reasonable distance of the zone
|
||||||
|
local nearbyUnits = coord:ScanUnits(radius)
|
||||||
|
if nearbyUnits then
|
||||||
|
for _, unitData in pairs(nearbyUnits) do
|
||||||
|
local unit = unitData -- ScanUnits returns unit objects
|
||||||
|
if unit and type(unit.IsAlive) == "function" and unit:IsAlive() then
|
||||||
|
local unitCoalition = unit:GetCoalition()
|
||||||
|
if unitCoalition == coalition.side.RED then
|
||||||
|
redCount = redCount + 1
|
||||||
|
elseif unitCoalition == coalition.side.BLUE then
|
||||||
|
blueCount = blueCount + 1
|
||||||
|
elseif unitCoalition == coalition.side.NEUTRAL then
|
||||||
|
neutralCount = neutralCount + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
log(string.format("[TACTICAL] Zone %s scan result: R:%d B:%d N:%d",
|
log(string.format("[TACTICAL] Zone %s scan result: R:%d B:%d N:%d",
|
||||||
ZoneCapture:GetZoneName(), redCount, blueCount, neutralCount))
|
ZoneCapture:GetZoneName(), redCount, blueCount, neutralCount))
|
||||||
@ -179,62 +218,57 @@ local function GetRedUnitMGRSCoords(ZoneCapture)
|
|||||||
local coords = {}
|
local coords = {}
|
||||||
local units = nil
|
local units = nil
|
||||||
|
|
||||||
-- Try multiple methods to get units (same as GetZoneForceStrengths)
|
-- Optimized: Try MOOSE's built-in zone scanning first (fastest method)
|
||||||
local success1 = pcall(function()
|
local success1 = pcall(function()
|
||||||
units = zone:GetScannedUnits()
|
units = zone:GetScannedUnits()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Fallback: Use coordinate-based scanning (much faster than SET_UNIT filtering)
|
||||||
if not success1 or not units then
|
if not success1 or not units then
|
||||||
|
local coord = zone:GetCoordinate()
|
||||||
|
local radius = zone:GetRadius() or 1000
|
||||||
|
|
||||||
local success2 = pcall(function()
|
local success2 = pcall(function()
|
||||||
local unitSet = SET_UNIT:New():FilterZones({zone}):FilterStart()
|
units = coord:ScanUnits(radius)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Last resort: Manual zone check with cached unit set
|
||||||
|
if not success2 or not units then
|
||||||
|
InitializeCachedUnitSet()
|
||||||
units = {}
|
units = {}
|
||||||
unitSet:ForEachUnit(function(unit)
|
if CachedUnitSet then
|
||||||
if unit then
|
CachedUnitSet:ForEachUnit(function(unit)
|
||||||
|
if unit and unit:IsAlive() and unit:IsInZone(zone) then
|
||||||
units[unit:GetName()] = unit
|
units[unit:GetName()] = unit
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end)
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not units then
|
-- Extract RED unit coordinates with optimized error handling
|
||||||
local success3 = pcall(function()
|
|
||||||
units = {}
|
|
||||||
local unitSet = SET_UNIT:New():FilterZones({zone}):FilterStart()
|
|
||||||
unitSet:ForEachUnit(function(unit)
|
|
||||||
if unit and unit:IsInZone(zone) then
|
|
||||||
units[unit:GetName()] = unit
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Extract RED unit coordinates
|
|
||||||
if units then
|
if units then
|
||||||
for unitName, unit in pairs(units) do
|
for unitName, unit in pairs(units) do
|
||||||
-- Enhanced nil checking and safe method calls
|
-- Streamlined nil checking
|
||||||
if unit and type(unit) == "table" and unit.IsAlive then
|
if unit and type(unit) == "table" then
|
||||||
local isAlive = false
|
local success, isAlive = pcall(function() return unit:IsAlive() end)
|
||||||
local success_alive = pcall(function()
|
|
||||||
isAlive = unit:IsAlive()
|
|
||||||
end)
|
|
||||||
|
|
||||||
if success_alive and isAlive then
|
if success and isAlive then
|
||||||
local coalition_side = nil
|
local success_coalition, coalition_side = pcall(function() return unit:GetCoalition() end)
|
||||||
local success_coalition = pcall(function()
|
|
||||||
coalition_side = unit:GetCoalition()
|
|
||||||
end)
|
|
||||||
|
|
||||||
if success_coalition and coalition_side == coalition.side.RED then
|
if success_coalition and coalition_side == coalition.side.RED then
|
||||||
local coord = unit:GetCoordinate()
|
local success_coord, coord = pcall(function() return unit:GetCoordinate() end)
|
||||||
if coord then
|
|
||||||
local success, mgrs = pcall(function()
|
if success_coord and coord then
|
||||||
|
local success_mgrs, mgrs = pcall(function()
|
||||||
return coord:ToStringMGRS(5) -- 5-digit precision
|
return coord:ToStringMGRS(5) -- 5-digit precision
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if success and mgrs then
|
if success_mgrs and mgrs then
|
||||||
|
local success_type, unitType = pcall(function() return unit:GetTypeName() end)
|
||||||
table.insert(coords, {
|
table.insert(coords, {
|
||||||
name = unit:GetName(),
|
name = unit:GetName(),
|
||||||
type = unit:GetTypeName(),
|
type = success_type and unitType or "Unknown",
|
||||||
mgrs = mgrs
|
mgrs = mgrs
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@ -673,21 +707,37 @@ end, {}, 10, 300 ) -- Start after 10 seconds, repeat every 300 seconds (5 minute
|
|||||||
local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function()
|
local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function()
|
||||||
log("[ZONE COLORS] Running periodic zone color verification...")
|
log("[ZONE COLORS] Running periodic zone color verification...")
|
||||||
|
|
||||||
-- Verify each zone's visual marker matches its coalition
|
-- Verify each zone's visual marker matches its CURRENT STATE (not just coalition)
|
||||||
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||||
if zoneCapture then
|
if zoneCapture then
|
||||||
local zoneCoalition = zoneCapture:GetCoalition()
|
local zoneCoalition = zoneCapture:GetCoalition()
|
||||||
local zoneName = zoneNames[i] or ("Zone " .. i)
|
local zoneName = zoneNames[i] or ("Zone " .. i)
|
||||||
|
local currentState = zoneCapture:GetCurrentState()
|
||||||
|
|
||||||
-- Force redraw the zone with correct color (helps prevent desync issues)
|
-- Force redraw the zone with correct color based on CURRENT STATE
|
||||||
zoneCapture:UndrawZone()
|
zoneCapture:UndrawZone()
|
||||||
|
|
||||||
if zoneCoalition == coalition.side.BLUE then
|
-- Color priority: State (Attacked/Empty) overrides coalition ownership
|
||||||
zoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue
|
if currentState == "Attacked" then
|
||||||
|
-- Orange for contested zones (highest priority)
|
||||||
|
zoneCapture:DrawZone(-1, {1, 0.5, 0}, 0.5, {1, 0.5, 0}, 0.2, 2, true)
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to ORANGE (Attacked)", zoneName))
|
||||||
|
elseif currentState == "Empty" then
|
||||||
|
-- Green for neutral/empty zones
|
||||||
|
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true)
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to GREEN (Empty)", zoneName))
|
||||||
|
elseif zoneCoalition == coalition.side.BLUE then
|
||||||
|
-- Blue for BLUE-owned zones (Guarded or Captured state)
|
||||||
|
zoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true)
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to BLUE (Owned)", zoneName))
|
||||||
elseif zoneCoalition == coalition.side.RED then
|
elseif zoneCoalition == coalition.side.RED then
|
||||||
zoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red
|
-- Red for RED-owned zones (Guarded or Captured state)
|
||||||
|
zoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true)
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to RED (Owned)", zoneName))
|
||||||
else
|
else
|
||||||
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green (neutral)
|
-- Fallback to green for any other state
|
||||||
|
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true)
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to GREEN (Fallback)", zoneName))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -713,22 +763,29 @@ local function RefreshAllZoneColors()
|
|||||||
|
|
||||||
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||||
if zoneCapture then
|
if zoneCapture then
|
||||||
local coalition = zoneCapture:GetCoalition()
|
local zoneCoalition = zoneCapture:GetCoalition()
|
||||||
local zoneName = zoneNames[i] or ("Zone " .. i)
|
local zoneName = zoneNames[i] or ("Zone " .. i)
|
||||||
|
local currentState = zoneCapture:GetCurrentState()
|
||||||
|
|
||||||
-- Clear existing drawings
|
-- Clear existing drawings
|
||||||
zoneCapture:UndrawZone()
|
zoneCapture:UndrawZone()
|
||||||
|
|
||||||
-- Redraw with correct color based on current coalition
|
-- Redraw with correct color based on CURRENT STATE (priority over coalition)
|
||||||
if coalition == coalition.side.BLUE then
|
if currentState == "Attacked" then
|
||||||
|
zoneCapture:DrawZone(-1, {1, 0.5, 0}, 0.5, {1, 0.5, 0}, 0.2, 2, true) -- Orange
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to ORANGE (Attacked)", zoneName))
|
||||||
|
elseif currentState == "Empty" then
|
||||||
|
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green
|
||||||
|
log(string.format("[ZONE COLORS] %s: Set to GREEN (Empty)", zoneName))
|
||||||
|
elseif zoneCoalition == coalition.side.BLUE then
|
||||||
zoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue
|
zoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue
|
||||||
log(string.format("[ZONE COLORS] %s: Set to BLUE", zoneName))
|
log(string.format("[ZONE COLORS] %s: Set to BLUE (Owned)", zoneName))
|
||||||
elseif coalition == coalition.side.RED then
|
elseif zoneCoalition == coalition.side.RED then
|
||||||
zoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red
|
zoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red
|
||||||
log(string.format("[ZONE COLORS] %s: Set to RED", zoneName))
|
log(string.format("[ZONE COLORS] %s: Set to RED (Owned)", zoneName))
|
||||||
else
|
else
|
||||||
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green (neutral)
|
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green (neutral)
|
||||||
log(string.format("[ZONE COLORS] %s: Set to NEUTRAL/GREEN", zoneName))
|
log(string.format("[ZONE COLORS] %s: Set to NEUTRAL/GREEN (Fallback)", zoneName))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -773,6 +830,10 @@ end
|
|||||||
-- Initialize zone status monitoring
|
-- Initialize zone status monitoring
|
||||||
SCHEDULER:New( nil, function()
|
SCHEDULER:New( nil, function()
|
||||||
log("[VICTORY SYSTEM] Initializing zone monitoring system...")
|
log("[VICTORY SYSTEM] Initializing zone monitoring system...")
|
||||||
|
|
||||||
|
-- Initialize performance optimization caches
|
||||||
|
InitializeCachedUnitSet()
|
||||||
|
|
||||||
SetupZoneStatusCommands()
|
SetupZoneStatusCommands()
|
||||||
|
|
||||||
-- Initial status report
|
-- Initial status report
|
||||||
|
|||||||
@ -212,13 +212,16 @@ end
|
|||||||
hasActiveCargoMission(coalitionKey, airbaseName)
|
hasActiveCargoMission(coalitionKey, airbaseName)
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
Returns true if there is an active (not completed/failed) cargo mission for the given airbase.
|
Returns true if there is an active (not completed/failed) cargo mission for the given airbase.
|
||||||
|
Failed missions are immediately removed from tracking to allow retries.
|
||||||
]]
|
]]
|
||||||
local function hasActiveCargoMission(coalitionKey, airbaseName)
|
local function hasActiveCargoMission(coalitionKey, airbaseName)
|
||||||
for _, mission in pairs(cargoMissions[coalitionKey]) do
|
for i = #cargoMissions[coalitionKey], 1, -1 do
|
||||||
|
local mission = cargoMissions[coalitionKey][i]
|
||||||
if mission.destination == airbaseName then
|
if mission.destination == airbaseName then
|
||||||
-- Ignore completed or failed missions
|
-- Remove completed or failed missions immediately to allow retries
|
||||||
if mission.status == "completed" or mission.status == "failed" then
|
if mission.status == "completed" or mission.status == "failed" then
|
||||||
-- not active
|
log("Removing " .. mission.status .. " cargo mission for " .. airbaseName .. " from tracking")
|
||||||
|
table.remove(cargoMissions[coalitionKey], i)
|
||||||
else
|
else
|
||||||
-- Consider mission active only if the group is alive OR we're still within the grace window
|
-- Consider mission active only if the group is alive OR we're still within the grace window
|
||||||
local stillActive = false
|
local stillActive = false
|
||||||
@ -283,12 +286,25 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
local origin
|
local origin
|
||||||
local attempts = 0
|
local attempts = 0
|
||||||
local maxAttempts = 10
|
local maxAttempts = 10
|
||||||
|
local coalitionSide = getCoalitionSide(coalitionKey)
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
origin = selectRandomAirfield(config.supplyAirfields)
|
origin = selectRandomAirfield(config.supplyAirfields)
|
||||||
attempts = attempts + 1
|
attempts = attempts + 1
|
||||||
|
|
||||||
-- Ensure origin is not the same as destination
|
-- Ensure origin is not the same as destination
|
||||||
if origin == squadron.airbaseName then
|
if origin == squadron.airbaseName then
|
||||||
origin = nil
|
origin = nil
|
||||||
|
else
|
||||||
|
-- Validate that origin airbase exists and is controlled by correct coalition
|
||||||
|
local originAirbase = AIRBASE:FindByName(origin)
|
||||||
|
if not originAirbase then
|
||||||
|
log("WARNING: Origin airbase '" .. tostring(origin) .. "' does not exist. Trying another...")
|
||||||
|
origin = nil
|
||||||
|
elseif originAirbase:GetCoalition() ~= coalitionSide then
|
||||||
|
log("WARNING: Origin airbase '" .. tostring(origin) .. "' is not controlled by " .. coalitionKey .. " coalition. Trying another...")
|
||||||
|
origin = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
until origin or attempts >= maxAttempts
|
until origin or attempts >= maxAttempts
|
||||||
|
|
||||||
@ -372,14 +388,45 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
local alias = cargoTemplate .. "_TO_" .. destination .. "_" .. tostring(math.random(1000,9999))
|
local alias = cargoTemplate .. "_TO_" .. destination .. "_" .. tostring(math.random(1000,9999))
|
||||||
log("DEBUG: Attempting RAT spawn for template: '" .. cargoTemplate .. "' alias: '" .. alias .. "'", true)
|
log("DEBUG: Attempting RAT spawn for template: '" .. cargoTemplate .. "' alias: '" .. alias .. "'", true)
|
||||||
|
|
||||||
-- Check if destination airbase is still controlled by the correct coalition
|
-- Validate destination airbase: RAT's "Airbase doesn't exist" error actually means
|
||||||
|
-- "Airbase not found OR not owned by the correct coalition" because RAT filters by coalition internally.
|
||||||
|
-- We perform the same validation here to fail fast with better error messages.
|
||||||
local destAirbase = AIRBASE:FindByName(destination)
|
local destAirbase = AIRBASE:FindByName(destination)
|
||||||
|
local coalitionSide = getCoalitionSide(coalitionKey)
|
||||||
|
|
||||||
if not destAirbase then
|
if not destAirbase then
|
||||||
log("ERROR: Destination airbase '" .. destination .. "' does not exist. Skipping dispatch.")
|
log("ERROR: Destination airbase '" .. destination .. "' does not exist in DCS (invalid name or not on this map). Skipping dispatch.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (airbase not found on map)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if destAirbase:GetCoalition() ~= getCoalitionSide(coalitionKey) then
|
|
||||||
log("ERROR: Destination airbase '" .. destination .. "' is not controlled by " .. coalitionKey .. " coalition. Skipping dispatch.")
|
local destCoalition = destAirbase:GetCoalition()
|
||||||
|
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.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " aborted (airbase captured by enemy)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Validate origin airbase with same coalition filtering logic
|
||||||
|
local originAirbase = AIRBASE:FindByName(origin)
|
||||||
|
if not originAirbase then
|
||||||
|
log("ERROR: Origin airbase '" .. origin .. "' does not exist in DCS (invalid name or not on this map). Skipping dispatch.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission from " .. origin .. " failed (airbase not found on map)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local originCoalition = originAirbase:GetCoalition()
|
||||||
|
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.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission from " .. origin .. " failed (origin airbase captured by enemy)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -390,6 +437,8 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
log("TRACEBACK: " .. tostring(debug.traceback(rat)), true)
|
log("TRACEBACK: " .. tostring(debug.traceback(rat)), true)
|
||||||
end
|
end
|
||||||
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (spawn init error)!")
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (spawn init error)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately - do NOT track failed RAT spawns
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -478,6 +527,9 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
if debug and debug.traceback then
|
if debug and debug.traceback then
|
||||||
log("TRACEBACK: " .. tostring(debug.traceback(errSpawn)), true)
|
log("TRACEBACK: " .. tostring(debug.traceback(errSpawn)), true)
|
||||||
end
|
end
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (spawn error)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately - do NOT track failed spawns
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -629,6 +681,112 @@ log("═════════════════════════
|
|||||||
-- End Moose_TDAC_CargoDispatcher.lua
|
-- End Moose_TDAC_CargoDispatcher.lua
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
DIAGNOSTIC CONSOLE HELPERS
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
Functions you can call from the DCS Lua console (F12) to debug issues.
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- Check airbase coalition ownership for all configured supply airbases
|
||||||
|
-- Usage: _G.TDAC_CheckAirbaseOwnership()
|
||||||
|
function _G.TDAC_CheckAirbaseOwnership()
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ═══════════════════════════════════════")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] Checking Coalition Ownership of All Supply Airbases")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ═══════════════════════════════════════")
|
||||||
|
|
||||||
|
for _, coalitionKey in ipairs({"red", "blue"}) do
|
||||||
|
local config = CARGO_SUPPLY_CONFIG[coalitionKey]
|
||||||
|
local expectedCoalition = getCoalitionSide(coalitionKey)
|
||||||
|
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] %s COALITION (expected coalition ID: %s)", coalitionKey:upper(), tostring(expectedCoalition)))
|
||||||
|
|
||||||
|
if config and config.supplyAirfields then
|
||||||
|
for _, airbaseName in ipairs(config.supplyAirfields) do
|
||||||
|
local airbase = AIRBASE:FindByName(airbaseName)
|
||||||
|
if not airbase then
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] ✗ %-30s - NOT FOUND (invalid name or not on this map)", airbaseName))
|
||||||
|
else
|
||||||
|
local actualCoalition = airbase:GetCoalition()
|
||||||
|
local coalitionName = "UNKNOWN"
|
||||||
|
local status = "✗"
|
||||||
|
|
||||||
|
if actualCoalition == coalition.side.NEUTRAL then
|
||||||
|
coalitionName = "NEUTRAL"
|
||||||
|
elseif actualCoalition == coalition.side.RED then
|
||||||
|
coalitionName = "RED"
|
||||||
|
elseif actualCoalition == coalition.side.BLUE then
|
||||||
|
coalitionName = "BLUE"
|
||||||
|
end
|
||||||
|
|
||||||
|
if actualCoalition == expectedCoalition then
|
||||||
|
status = "✓"
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] %s %-30s - %s (coalition ID: %s)", status, airbaseName, coalitionName, tostring(actualCoalition)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ERROR: No supply airfields configured!")
|
||||||
|
end
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ───────────────────────────────────────")
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ═══════════════════════════════════════")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] Check complete. ✓ = Owned by correct coalition, ✗ = Wrong coalition or not found")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check specific airbase coalition ownership
|
||||||
|
-- Usage: _G.TDAC_CheckAirbase("Olenya")
|
||||||
|
function _G.TDAC_CheckAirbase(airbaseName)
|
||||||
|
if type(airbaseName) ~= 'string' then
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ERROR: airbaseName must be a string")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local airbase = AIRBASE:FindByName(airbaseName)
|
||||||
|
if not airbase then
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] Airbase '%s' NOT FOUND (invalid name or not on this map)", airbaseName))
|
||||||
|
return false, "not_found"
|
||||||
|
end
|
||||||
|
|
||||||
|
local actualCoalition = airbase:GetCoalition()
|
||||||
|
local coalitionName = "UNKNOWN"
|
||||||
|
|
||||||
|
if actualCoalition == coalition.side.NEUTRAL then
|
||||||
|
coalitionName = "NEUTRAL"
|
||||||
|
elseif actualCoalition == coalition.side.RED then
|
||||||
|
coalitionName = "RED"
|
||||||
|
elseif actualCoalition == coalition.side.BLUE then
|
||||||
|
coalitionName = "BLUE"
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] Airbase '%s' - Coalition: %s (ID: %s)", airbaseName, coalitionName, tostring(actualCoalition)))
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] IsAlive: %s", tostring(airbase:IsAlive())))
|
||||||
|
|
||||||
|
-- Check parking spots
|
||||||
|
local function spotsFor(term, termName)
|
||||||
|
local ok, n = pcall(function() return airbase:GetParkingSpotsNumber(term) end)
|
||||||
|
if ok and n then
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] Parking %-15s: %d spots", termName, n))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
spotsFor(AIRBASE.TerminalType.OpenBig, "OpenBig")
|
||||||
|
spotsFor(AIRBASE.TerminalType.OpenMed, "OpenMed")
|
||||||
|
spotsFor(AIRBASE.TerminalType.OpenMedOrBig, "OpenMedOrBig")
|
||||||
|
spotsFor(AIRBASE.TerminalType.Runway, "Runway")
|
||||||
|
|
||||||
|
return true, coalitionName, actualCoalition
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("[TDAC DIAGNOSTIC] Console helpers loaded:")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_CheckAirbaseOwnership() - Check all supply airbases")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_CheckAirbase('Olenya') - Check specific airbase")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_RunConfigCheck() - Validate dispatcher config")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_LogAirbaseParking('Olenya') - Check parking availability")
|
||||||
|
|
||||||
|
|
||||||
-- Diagnostic helper: call from DCS console to test spawn-by-name and routing.
|
-- Diagnostic helper: call from DCS console to test spawn-by-name and routing.
|
||||||
-- Example (paste into DCS Lua console):
|
-- Example (paste into DCS Lua console):
|
||||||
-- _G.TDAC_CargoDispatcher_TestSpawn("CARGO_BLUE_C130_TEMPLATE", "Kittila", "Luostari Pechenga")
|
-- _G.TDAC_CargoDispatcher_TestSpawn("CARGO_BLUE_C130_TEMPLATE", "Kittila", "Luostari Pechenga")
|
||||||
|
|||||||
@ -147,7 +147,7 @@ local TADC_SETTINGS = {
|
|||||||
-- RED Coalition Settings
|
-- RED Coalition Settings
|
||||||
red = {
|
red = {
|
||||||
maxActiveCAP = 24, -- Maximum RED fighters airborne at once
|
maxActiveCAP = 24, -- Maximum RED fighters airborne at once
|
||||||
squadronCooldown = 300, -- RED cooldown after squadron launch (seconds)
|
squadronCooldown = 600, -- RED cooldown after squadron launch (seconds)
|
||||||
interceptRatio = 0.8, -- RED interceptors per threat aircraft
|
interceptRatio = 0.8, -- RED interceptors per threat aircraft
|
||||||
cargoReplenishmentAmount = 4, -- RED aircraft added per cargo delivery
|
cargoReplenishmentAmount = 4, -- RED aircraft added per cargo delivery
|
||||||
emergencyCleanupTime = 7200, -- RED force cleanup time (seconds)
|
emergencyCleanupTime = 7200, -- RED force cleanup time (seconds)
|
||||||
@ -157,7 +157,7 @@ local TADC_SETTINGS = {
|
|||||||
-- BLUE Coalition Settings
|
-- BLUE Coalition Settings
|
||||||
blue = {
|
blue = {
|
||||||
maxActiveCAP = 24, -- Maximum BLUE fighters airborne at once
|
maxActiveCAP = 24, -- Maximum BLUE fighters airborne at once
|
||||||
squadronCooldown = 300, -- BLUE cooldown after squadron launch (seconds)
|
squadronCooldown = 600, -- BLUE cooldown after squadron launch (seconds)
|
||||||
interceptRatio = 0.8, -- BLUE interceptors per threat aircraft
|
interceptRatio = 0.8, -- BLUE interceptors per threat aircraft
|
||||||
cargoReplenishmentAmount = 4, -- BLUE aircraft added per cargo delivery
|
cargoReplenishmentAmount = 4, -- BLUE aircraft added per cargo delivery
|
||||||
emergencyCleanupTime = 7200, -- BLUE force cleanup time (seconds)
|
emergencyCleanupTime = 7200, -- BLUE force cleanup time (seconds)
|
||||||
|
|||||||
@ -212,13 +212,16 @@ end
|
|||||||
hasActiveCargoMission(coalitionKey, airbaseName)
|
hasActiveCargoMission(coalitionKey, airbaseName)
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
Returns true if there is an active (not completed/failed) cargo mission for the given airbase.
|
Returns true if there is an active (not completed/failed) cargo mission for the given airbase.
|
||||||
|
Failed missions are immediately removed from tracking to allow retries.
|
||||||
]]
|
]]
|
||||||
local function hasActiveCargoMission(coalitionKey, airbaseName)
|
local function hasActiveCargoMission(coalitionKey, airbaseName)
|
||||||
for _, mission in pairs(cargoMissions[coalitionKey]) do
|
for i = #cargoMissions[coalitionKey], 1, -1 do
|
||||||
|
local mission = cargoMissions[coalitionKey][i]
|
||||||
if mission.destination == airbaseName then
|
if mission.destination == airbaseName then
|
||||||
-- Ignore completed or failed missions
|
-- Remove completed or failed missions immediately to allow retries
|
||||||
if mission.status == "completed" or mission.status == "failed" then
|
if mission.status == "completed" or mission.status == "failed" then
|
||||||
-- not active
|
log("Removing " .. mission.status .. " cargo mission for " .. airbaseName .. " from tracking")
|
||||||
|
table.remove(cargoMissions[coalitionKey], i)
|
||||||
else
|
else
|
||||||
-- Consider mission active only if the group is alive OR we're still within the grace window
|
-- Consider mission active only if the group is alive OR we're still within the grace window
|
||||||
local stillActive = false
|
local stillActive = false
|
||||||
@ -283,12 +286,25 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
local origin
|
local origin
|
||||||
local attempts = 0
|
local attempts = 0
|
||||||
local maxAttempts = 10
|
local maxAttempts = 10
|
||||||
|
local coalitionSide = getCoalitionSide(coalitionKey)
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
origin = selectRandomAirfield(config.supplyAirfields)
|
origin = selectRandomAirfield(config.supplyAirfields)
|
||||||
attempts = attempts + 1
|
attempts = attempts + 1
|
||||||
|
|
||||||
-- Ensure origin is not the same as destination
|
-- Ensure origin is not the same as destination
|
||||||
if origin == squadron.airbaseName then
|
if origin == squadron.airbaseName then
|
||||||
origin = nil
|
origin = nil
|
||||||
|
else
|
||||||
|
-- Validate that origin airbase exists and is controlled by correct coalition
|
||||||
|
local originAirbase = AIRBASE:FindByName(origin)
|
||||||
|
if not originAirbase then
|
||||||
|
log("WARNING: Origin airbase '" .. tostring(origin) .. "' does not exist. Trying another...")
|
||||||
|
origin = nil
|
||||||
|
elseif originAirbase:GetCoalition() ~= coalitionSide then
|
||||||
|
log("WARNING: Origin airbase '" .. tostring(origin) .. "' is not controlled by " .. coalitionKey .. " coalition. Trying another...")
|
||||||
|
origin = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
until origin or attempts >= maxAttempts
|
until origin or attempts >= maxAttempts
|
||||||
|
|
||||||
@ -372,14 +388,45 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
local alias = cargoTemplate .. "_TO_" .. destination .. "_" .. tostring(math.random(1000,9999))
|
local alias = cargoTemplate .. "_TO_" .. destination .. "_" .. tostring(math.random(1000,9999))
|
||||||
log("DEBUG: Attempting RAT spawn for template: '" .. cargoTemplate .. "' alias: '" .. alias .. "'", true)
|
log("DEBUG: Attempting RAT spawn for template: '" .. cargoTemplate .. "' alias: '" .. alias .. "'", true)
|
||||||
|
|
||||||
-- Check if destination airbase is still controlled by the correct coalition
|
-- Validate destination airbase: RAT's "Airbase doesn't exist" error actually means
|
||||||
|
-- "Airbase not found OR not owned by the correct coalition" because RAT filters by coalition internally.
|
||||||
|
-- We perform the same validation here to fail fast with better error messages.
|
||||||
local destAirbase = AIRBASE:FindByName(destination)
|
local destAirbase = AIRBASE:FindByName(destination)
|
||||||
|
local coalitionSide = getCoalitionSide(coalitionKey)
|
||||||
|
|
||||||
if not destAirbase then
|
if not destAirbase then
|
||||||
log("ERROR: Destination airbase '" .. destination .. "' does not exist. Skipping dispatch.")
|
log("ERROR: Destination airbase '" .. destination .. "' does not exist in DCS (invalid name or not on this map). Skipping dispatch.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (airbase not found on map)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if destAirbase:GetCoalition() ~= getCoalitionSide(coalitionKey) then
|
|
||||||
log("ERROR: Destination airbase '" .. destination .. "' is not controlled by " .. coalitionKey .. " coalition. Skipping dispatch.")
|
local destCoalition = destAirbase:GetCoalition()
|
||||||
|
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.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " aborted (airbase captured by enemy)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Validate origin airbase with same coalition filtering logic
|
||||||
|
local originAirbase = AIRBASE:FindByName(origin)
|
||||||
|
if not originAirbase then
|
||||||
|
log("ERROR: Origin airbase '" .. origin .. "' does not exist in DCS (invalid name or not on this map). Skipping dispatch.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission from " .. origin .. " failed (airbase not found on map)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local originCoalition = originAirbase:GetCoalition()
|
||||||
|
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.")
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission from " .. origin .. " failed (origin airbase captured by enemy)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -390,6 +437,8 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
log("TRACEBACK: " .. tostring(debug.traceback(rat)), true)
|
log("TRACEBACK: " .. tostring(debug.traceback(rat)), true)
|
||||||
end
|
end
|
||||||
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (spawn init error)!")
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (spawn init error)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately - do NOT track failed RAT spawns
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -478,6 +527,9 @@ local function dispatchCargo(squadron, coalitionKey)
|
|||||||
if debug and debug.traceback then
|
if debug and debug.traceback then
|
||||||
log("TRACEBACK: " .. tostring(debug.traceback(errSpawn)), true)
|
log("TRACEBACK: " .. tostring(debug.traceback(errSpawn)), true)
|
||||||
end
|
end
|
||||||
|
announceToCoalition(coalitionKey, "Resupply mission to " .. destination .. " failed (spawn error)!")
|
||||||
|
-- Mark mission as failed and cleanup immediately - do NOT track failed spawns
|
||||||
|
mission.status = "failed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -629,6 +681,112 @@ log("═════════════════════════
|
|||||||
-- End Moose_TDAC_CargoDispatcher.lua
|
-- End Moose_TDAC_CargoDispatcher.lua
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
DIAGNOSTIC CONSOLE HELPERS
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
Functions you can call from the DCS Lua console (F12) to debug issues.
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- Check airbase coalition ownership for all configured supply airbases
|
||||||
|
-- Usage: _G.TDAC_CheckAirbaseOwnership()
|
||||||
|
function _G.TDAC_CheckAirbaseOwnership()
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ═══════════════════════════════════════")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] Checking Coalition Ownership of All Supply Airbases")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ═══════════════════════════════════════")
|
||||||
|
|
||||||
|
for _, coalitionKey in ipairs({"red", "blue"}) do
|
||||||
|
local config = CARGO_SUPPLY_CONFIG[coalitionKey]
|
||||||
|
local expectedCoalition = getCoalitionSide(coalitionKey)
|
||||||
|
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] %s COALITION (expected coalition ID: %s)", coalitionKey:upper(), tostring(expectedCoalition)))
|
||||||
|
|
||||||
|
if config and config.supplyAirfields then
|
||||||
|
for _, airbaseName in ipairs(config.supplyAirfields) do
|
||||||
|
local airbase = AIRBASE:FindByName(airbaseName)
|
||||||
|
if not airbase then
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] ✗ %-30s - NOT FOUND (invalid name or not on this map)", airbaseName))
|
||||||
|
else
|
||||||
|
local actualCoalition = airbase:GetCoalition()
|
||||||
|
local coalitionName = "UNKNOWN"
|
||||||
|
local status = "✗"
|
||||||
|
|
||||||
|
if actualCoalition == coalition.side.NEUTRAL then
|
||||||
|
coalitionName = "NEUTRAL"
|
||||||
|
elseif actualCoalition == coalition.side.RED then
|
||||||
|
coalitionName = "RED"
|
||||||
|
elseif actualCoalition == coalition.side.BLUE then
|
||||||
|
coalitionName = "BLUE"
|
||||||
|
end
|
||||||
|
|
||||||
|
if actualCoalition == expectedCoalition then
|
||||||
|
status = "✓"
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] %s %-30s - %s (coalition ID: %s)", status, airbaseName, coalitionName, tostring(actualCoalition)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ERROR: No supply airfields configured!")
|
||||||
|
end
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ───────────────────────────────────────")
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ═══════════════════════════════════════")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] Check complete. ✓ = Owned by correct coalition, ✗ = Wrong coalition or not found")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check specific airbase coalition ownership
|
||||||
|
-- Usage: _G.TDAC_CheckAirbase("Olenya")
|
||||||
|
function _G.TDAC_CheckAirbase(airbaseName)
|
||||||
|
if type(airbaseName) ~= 'string' then
|
||||||
|
env.info("[TDAC DIAGNOSTIC] ERROR: airbaseName must be a string")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local airbase = AIRBASE:FindByName(airbaseName)
|
||||||
|
if not airbase then
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] Airbase '%s' NOT FOUND (invalid name or not on this map)", airbaseName))
|
||||||
|
return false, "not_found"
|
||||||
|
end
|
||||||
|
|
||||||
|
local actualCoalition = airbase:GetCoalition()
|
||||||
|
local coalitionName = "UNKNOWN"
|
||||||
|
|
||||||
|
if actualCoalition == coalition.side.NEUTRAL then
|
||||||
|
coalitionName = "NEUTRAL"
|
||||||
|
elseif actualCoalition == coalition.side.RED then
|
||||||
|
coalitionName = "RED"
|
||||||
|
elseif actualCoalition == coalition.side.BLUE then
|
||||||
|
coalitionName = "BLUE"
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] Airbase '%s' - Coalition: %s (ID: %s)", airbaseName, coalitionName, tostring(actualCoalition)))
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] IsAlive: %s", tostring(airbase:IsAlive())))
|
||||||
|
|
||||||
|
-- Check parking spots
|
||||||
|
local function spotsFor(term, termName)
|
||||||
|
local ok, n = pcall(function() return airbase:GetParkingSpotsNumber(term) end)
|
||||||
|
if ok and n then
|
||||||
|
env.info(string.format("[TDAC DIAGNOSTIC] Parking %-15s: %d spots", termName, n))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
spotsFor(AIRBASE.TerminalType.OpenBig, "OpenBig")
|
||||||
|
spotsFor(AIRBASE.TerminalType.OpenMed, "OpenMed")
|
||||||
|
spotsFor(AIRBASE.TerminalType.OpenMedOrBig, "OpenMedOrBig")
|
||||||
|
spotsFor(AIRBASE.TerminalType.Runway, "Runway")
|
||||||
|
|
||||||
|
return true, coalitionName, actualCoalition
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("[TDAC DIAGNOSTIC] Console helpers loaded:")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_CheckAirbaseOwnership() - Check all supply airbases")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_CheckAirbase('Olenya') - Check specific airbase")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_RunConfigCheck() - Validate dispatcher config")
|
||||||
|
env.info("[TDAC DIAGNOSTIC] _G.TDAC_LogAirbaseParking('Olenya') - Check parking availability")
|
||||||
|
|
||||||
|
|
||||||
-- Diagnostic helper: call from DCS console to test spawn-by-name and routing.
|
-- Diagnostic helper: call from DCS console to test spawn-by-name and routing.
|
||||||
-- Example (paste into DCS Lua console):
|
-- Example (paste into DCS Lua console):
|
||||||
-- _G.TDAC_CargoDispatcher_TestSpawn("CARGO_BLUE_C130_TEMPLATE", "Kittila", "Luostari Pechenga")
|
-- _G.TDAC_CargoDispatcher_TestSpawn("CARGO_BLUE_C130_TEMPLATE", "Kittila", "Luostari Pechenga")
|
||||||
|
|||||||
@ -147,7 +147,7 @@ local TADC_SETTINGS = {
|
|||||||
-- RED Coalition Settings
|
-- RED Coalition Settings
|
||||||
red = {
|
red = {
|
||||||
maxActiveCAP = 24, -- Maximum RED fighters airborne at once
|
maxActiveCAP = 24, -- Maximum RED fighters airborne at once
|
||||||
squadronCooldown = 300, -- RED cooldown after squadron launch (seconds)
|
squadronCooldown = 600, -- RED cooldown after squadron launch (seconds)
|
||||||
interceptRatio = 0.8, -- RED interceptors per threat aircraft
|
interceptRatio = 0.8, -- RED interceptors per threat aircraft
|
||||||
cargoReplenishmentAmount = 4, -- RED aircraft added per cargo delivery
|
cargoReplenishmentAmount = 4, -- RED aircraft added per cargo delivery
|
||||||
emergencyCleanupTime = 7200, -- RED force cleanup time (seconds)
|
emergencyCleanupTime = 7200, -- RED force cleanup time (seconds)
|
||||||
@ -157,7 +157,7 @@ local TADC_SETTINGS = {
|
|||||||
-- BLUE Coalition Settings
|
-- BLUE Coalition Settings
|
||||||
blue = {
|
blue = {
|
||||||
maxActiveCAP = 24, -- Maximum BLUE fighters airborne at once
|
maxActiveCAP = 24, -- Maximum BLUE fighters airborne at once
|
||||||
squadronCooldown = 300, -- BLUE cooldown after squadron launch (seconds)
|
squadronCooldown = 600, -- BLUE cooldown after squadron launch (seconds)
|
||||||
interceptRatio = 0.8, -- BLUE interceptors per threat aircraft
|
interceptRatio = 0.8, -- BLUE interceptors per threat aircraft
|
||||||
cargoReplenishmentAmount = 4, -- BLUE aircraft added per cargo delivery
|
cargoReplenishmentAmount = 4, -- BLUE aircraft added per cargo delivery
|
||||||
emergencyCleanupTime = 7200, -- BLUE force cleanup time (seconds)
|
emergencyCleanupTime = 7200, -- BLUE force cleanup time (seconds)
|
||||||
|
|||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user