mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
Version 1.0.4 Running on prod.
This commit is contained in:
parent
4c43418bc1
commit
a2786f2325
@ -511,22 +511,16 @@ ctld.pickupZones = {
|
||||
{ "London FARP Supply", "blue", -1, "yes", 0 },
|
||||
{ "Dallas FARP Supply", "blue", -1, "yes", 0 },
|
||||
{ "Paris FARP Supply", "blue", -1, "yes", 0 },
|
||||
{ "pickzone6", "none", -1, "yes", 0 },
|
||||
{ "pickzone7", "none", -1, "yes", 0 },
|
||||
{ "pickzone8", "none", -1, "yes", 0 },
|
||||
{ "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up
|
||||
{ "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up
|
||||
|
||||
{ "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive!
|
||||
{ "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive!
|
||||
{ "pickzone13", "none", -1, "yes", 0 },
|
||||
{ "pickzone14", "none", -1, "yes", 0 },
|
||||
{ "pickzone15", "none", -1, "yes", 0 },
|
||||
{ "pickzone16", "none", -1, "yes", 0 },
|
||||
{ "pickzone17", "none", -1, "yes", 0 },
|
||||
{ "pickzone18", "none", -1, "yes", 0 },
|
||||
{ "pickzone19", "none", 5, "yes", 0 },
|
||||
{ "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in
|
||||
{ "Kilpyavr Supply", "none", -1, "yes", 0 },
|
||||
{ "Severomorsk-1 Supply", "none", -1, "yes", 0 },
|
||||
{ "Severomorsk-3 Supply", "none", -1, "yes", 0 },
|
||||
{ "Murmansk Supply", "none", -1, "yes", 0 },
|
||||
{ "Olenya Supply", "none", -1, "yes", 0 },
|
||||
{ "Monchegorsk Supply", "none", -1, "yes", 0 },
|
||||
{ "Afrikanda Supply", "none", -1, "yes", 0 },
|
||||
{ "Koshka Supply", "none", -1, "yes", 0 },
|
||||
{ "Alakurtti Supply", "blue", -1, "yes", 0},
|
||||
|
||||
|
||||
{ "CVN-72 Abraham Lincoln", "none", -1, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship
|
||||
}
|
||||
@ -651,16 +645,21 @@ ctld.extractableGroups = {
|
||||
-- Use any of the predefined names or set your own ones
|
||||
-- When a logistic unit is destroyed, you will no longer be able to spawn crates
|
||||
ctld.logisticUnits = {
|
||||
"logistic1",
|
||||
"logistic2",
|
||||
"logistic3",
|
||||
"logistic4",
|
||||
"logistic5",
|
||||
"logistic6",
|
||||
"logistic7",
|
||||
"logistic8",
|
||||
"logistic9",
|
||||
"logistic10",
|
||||
"Luostari Supply",
|
||||
"Ivalo Supply",
|
||||
"London FARP Supply",
|
||||
"Paris FARP Supply",
|
||||
"Dallas FARP Supply",
|
||||
"Kilpyavr Supply",
|
||||
"Severomorsk-1 Supply",
|
||||
"Severomorsk-3 Supply",
|
||||
"Koshka Supply",
|
||||
"Murmansk Supply",
|
||||
"Olenya Supply",
|
||||
"Monchegorsk Supply",
|
||||
"Afrikanda Supply",
|
||||
"Alakurtti Supply"
|
||||
|
||||
}
|
||||
|
||||
-- ************** UNITS ABLE TO TRANSPORT VEHICLES ******************
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -24,112 +24,335 @@ end
|
||||
|
||||
|
||||
-- Red Airbases (from TADC configuration)
|
||||
env.info("[DEBUG] Initializing Capture Zone: Kilpyavr")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Kilpyavr")
|
||||
CaptureZone_Kilpyavr = ZONE:New( "Capture Kilpyavr" )
|
||||
ZoneCapture_Kilpyavr = ZONE_CAPTURE_COALITION:New( CaptureZone_Kilpyavr, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Kilpyavr:__Guard( 1 )
|
||||
ZoneCapture_Kilpyavr:Start( 30, 30 )
|
||||
env.info("[DEBUG] Kilpyavr zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Kilpyavr zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: Severomorsk-1")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Severomorsk-1")
|
||||
CaptureZone_Severomorsk_1 = ZONE:New( "Capture Severomorsk-1" )
|
||||
ZoneCapture_Severomorsk_1 = ZONE_CAPTURE_COALITION:New( CaptureZone_Severomorsk_1, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Severomorsk_1:__Guard( 1 )
|
||||
ZoneCapture_Severomorsk_1:Start( 30, 30 )
|
||||
env.info("[DEBUG] Severomorsk-1 zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Severomorsk-1 zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: Severomorsk-3")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Severomorsk-3")
|
||||
CaptureZone_Severomorsk_3 = ZONE:New( "Capture Severomorsk-3" )
|
||||
ZoneCapture_Severomorsk_3 = ZONE_CAPTURE_COALITION:New( CaptureZone_Severomorsk_3, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Severomorsk_3:__Guard( 1 )
|
||||
ZoneCapture_Severomorsk_3:Start( 30, 30 )
|
||||
env.info("[DEBUG] Severomorsk-3 zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Severomorsk-3 zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: Murmansk International")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Murmansk International")
|
||||
CaptureZone_Murmansk_International = ZONE:New( "Capture Murmansk International" )
|
||||
ZoneCapture_Murmansk_International = ZONE_CAPTURE_COALITION:New( CaptureZone_Murmansk_International, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Murmansk_International:__Guard( 1 )
|
||||
ZoneCapture_Murmansk_International:Start( 30, 30 )
|
||||
env.info("[DEBUG] Murmansk International zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Murmansk International zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: Monchegorsk")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Monchegorsk")
|
||||
CaptureZone_Monchegorsk = ZONE:New( "Capture Monchegorsk" )
|
||||
ZoneCapture_Monchegorsk = ZONE_CAPTURE_COALITION:New( CaptureZone_Monchegorsk, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Monchegorsk:__Guard( 1 )
|
||||
ZoneCapture_Monchegorsk:Start( 30, 30 )
|
||||
env.info("[DEBUG] Monchegorsk zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Monchegorsk zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: Olenya")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Olenya")
|
||||
CaptureZone_Olenya = ZONE:New( "Capture Olenya" )
|
||||
ZoneCapture_Olenya = ZONE_CAPTURE_COALITION:New( CaptureZone_Olenya, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Olenya:__Guard( 1 )
|
||||
ZoneCapture_Olenya:Start( 30, 30 )
|
||||
env.info("[DEBUG] Olenya zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Olenya zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: Afrikanda")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: Afrikanda")
|
||||
CaptureZone_Afrikanda = ZONE:New( "Capture Afrikanda" )
|
||||
ZoneCapture_Afrikanda = ZONE_CAPTURE_COALITION:New( CaptureZone_Afrikanda, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_Afrikanda:__Guard( 1 )
|
||||
ZoneCapture_Afrikanda:Start( 30, 30 )
|
||||
env.info("[DEBUG] Afrikanda zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] Afrikanda zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: The Mountain")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: The Mountain")
|
||||
CaptureZone_The_Mountain = ZONE:New( "Capture The Mountain" )
|
||||
ZoneCapture_The_Mountain = ZONE_CAPTURE_COALITION:New( CaptureZone_The_Mountain, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_The_Mountain:__Guard( 1 )
|
||||
ZoneCapture_The_Mountain:Start( 30, 30 )
|
||||
env.info("[DEBUG] The Mountain zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] The Mountain zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: The River")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: The River")
|
||||
CaptureZone_The_River = ZONE:New( "Capture The River" )
|
||||
ZoneCapture_The_River = ZONE_CAPTURE_COALITION:New( CaptureZone_The_River, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_The_River:__Guard( 1 )
|
||||
ZoneCapture_The_River:Start( 30, 30 )
|
||||
env.info("[DEBUG] The River zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] The River zone initialization complete")
|
||||
|
||||
env.info("[DEBUG] Initializing Capture Zone: The Gulf")
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: The Gulf")
|
||||
CaptureZone_The_Gulf = ZONE:New( "Capture The Gulf" )
|
||||
ZoneCapture_The_Gulf = ZONE_CAPTURE_COALITION:New( CaptureZone_The_Gulf, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_The_Gulf:__Guard( 1 )
|
||||
ZoneCapture_The_Gulf:Start( 30, 30 )
|
||||
env.info("[DEBUG] The Gulf zone initialization complete")
|
||||
env.info("[CAPTURE Module] [DEBUG] The Gulf zone initialization complete")
|
||||
|
||||
env.info("[CAPTURE Module] [DEBUG] Initializing Capture Zone: The Lakes")
|
||||
CaptureZone_The_Lakes = ZONE:New( "Capture The Lakes" )
|
||||
ZoneCapture_The_Lakes = ZONE_CAPTURE_COALITION:New( CaptureZone_The_Lakes, coalition.side.RED )
|
||||
-- SetMarkReadOnly method not available in this MOOSE version - feature disabled
|
||||
ZoneCapture_The_Lakes:__Guard( 1 )
|
||||
ZoneCapture_The_Lakes:Start( 30, 30 )
|
||||
env.info("[CAPTURE Module] [DEBUG] The Lakes zone initialization complete")
|
||||
|
||||
|
||||
|
||||
-- Helper functions for tactical information
|
||||
local function GetZoneForceStrengths(ZoneCapture)
|
||||
local zone = ZoneCapture:GetZone()
|
||||
if not zone then return {red = 0, blue = 0, neutral = 0} end
|
||||
|
||||
local redCount = 0
|
||||
local blueCount = 0
|
||||
local neutralCount = 0
|
||||
|
||||
-- Simple approach: scan all units and check if they're in the zone
|
||||
local coord = zone:GetCoordinate()
|
||||
local radius = zone:GetRadius() or 1000
|
||||
|
||||
local allUnits = SET_UNIT:New():FilterStart()
|
||||
allUnits:ForEachUnit(function(unit)
|
||||
if unit and unit:IsAlive() then
|
||||
local unitCoord = unit:GetCoordinate()
|
||||
if unitCoord and coord:Get2DDistance(unitCoord) <= radius 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)
|
||||
|
||||
env.info(string.format("[CAPTURE Module] [TACTICAL] Zone %s scan result: R:%d B:%d N:%d",
|
||||
ZoneCapture:GetZoneName(), redCount, blueCount, neutralCount))
|
||||
|
||||
return {
|
||||
red = redCount,
|
||||
blue = blueCount,
|
||||
neutral = neutralCount
|
||||
}
|
||||
end
|
||||
|
||||
local function GetRedUnitMGRSCoords(ZoneCapture)
|
||||
local zone = ZoneCapture:GetZone()
|
||||
if not zone then return {} end
|
||||
|
||||
local coords = {}
|
||||
local units = nil
|
||||
|
||||
-- Try multiple methods to get units (same as GetZoneForceStrengths)
|
||||
local success1 = pcall(function()
|
||||
units = zone:GetScannedUnits()
|
||||
end)
|
||||
|
||||
if not success1 or not units then
|
||||
local success2 = pcall(function()
|
||||
local unitSet = SET_UNIT:New():FilterZones({zone}):FilterStart()
|
||||
units = {}
|
||||
unitSet:ForEachUnit(function(unit)
|
||||
if unit then
|
||||
units[unit:GetName()] = unit
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
if not units then
|
||||
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
|
||||
for unitName, unit in pairs(units) do
|
||||
-- Enhanced nil checking and safe method calls
|
||||
if unit and type(unit) == "table" and unit.IsAlive then
|
||||
local isAlive = false
|
||||
local success_alive = pcall(function()
|
||||
isAlive = unit:IsAlive()
|
||||
end)
|
||||
|
||||
if success_alive and isAlive then
|
||||
local coalition_side = nil
|
||||
local success_coalition = pcall(function()
|
||||
coalition_side = unit:GetCoalition()
|
||||
end)
|
||||
|
||||
if success_coalition and coalition_side == coalition.side.RED then
|
||||
local coord = unit:GetCoordinate()
|
||||
if coord then
|
||||
local success, mgrs = pcall(function()
|
||||
return coord:ToStringMGRS(5) -- 5-digit precision
|
||||
end)
|
||||
|
||||
if success and mgrs then
|
||||
table.insert(coords, {
|
||||
name = unit:GetName(),
|
||||
type = unit:GetTypeName(),
|
||||
mgrs = mgrs
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
env.info(string.format("[CAPTURE Module] [TACTICAL] Found %d RED units with coordinates in %s",
|
||||
#coords, ZoneCapture:GetZoneName()))
|
||||
|
||||
return coords
|
||||
end
|
||||
|
||||
local function CreateTacticalInfoMarker(ZoneCapture)
|
||||
local zone = ZoneCapture:GetZone()
|
||||
if not zone then return end
|
||||
|
||||
local forces = GetZoneForceStrengths(ZoneCapture)
|
||||
local zoneName = ZoneCapture:GetZoneName()
|
||||
|
||||
-- Build tactical info text
|
||||
local tacticalText = string.format("TACTICAL: %s\nForces: R:%d B:%d",
|
||||
zoneName, forces.red, forces.blue)
|
||||
|
||||
if forces.neutral > 0 then
|
||||
tacticalText = tacticalText .. string.format(" C:%d", forces.neutral)
|
||||
end
|
||||
|
||||
-- Add MGRS coordinates if RED forces <= 10
|
||||
if forces.red > 0 and forces.red <= 10 then
|
||||
local redCoords = GetRedUnitMGRSCoords(ZoneCapture)
|
||||
if #redCoords > 0 then
|
||||
tacticalText = tacticalText .. "\n\nRED UNITS:"
|
||||
for i, unit in ipairs(redCoords) do
|
||||
if i <= 6 then -- Limit to 6 entries to avoid text overflow
|
||||
-- Shorten unit type names to fit better
|
||||
local shortType = unit.type:gsub("^%w+%-", ""):gsub("%s.*", "")
|
||||
tacticalText = tacticalText .. string.format("\n%s: %s", shortType, unit.mgrs)
|
||||
end
|
||||
end
|
||||
if #redCoords > 6 then
|
||||
tacticalText = tacticalText .. string.format("\n+%d more", #redCoords - 6)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Create tactical marker offset from zone center
|
||||
local coord = zone:GetCoordinate()
|
||||
if coord then
|
||||
-- Offset the tactical marker slightly northeast of the main zone marker
|
||||
local offsetCoord = coord:Translate(200, 45) -- 200m northeast
|
||||
|
||||
-- Remove any existing tactical marker first
|
||||
if ZoneCapture.TacticalMarkerID then
|
||||
env.info(string.format("[CAPTURE Module] [TACTICAL] Removing old marker ID %d for %s", ZoneCapture.TacticalMarkerID, zoneName))
|
||||
-- Try multiple removal methods
|
||||
local success1 = pcall(function()
|
||||
offsetCoord:RemoveMark(ZoneCapture.TacticalMarkerID)
|
||||
end)
|
||||
if not success1 then
|
||||
local success2 = pcall(function()
|
||||
trigger.action.removeMark(ZoneCapture.TacticalMarkerID)
|
||||
end)
|
||||
if not success2 then
|
||||
-- Try using coordinate removal
|
||||
pcall(function()
|
||||
coord:RemoveMark(ZoneCapture.TacticalMarkerID)
|
||||
end)
|
||||
end
|
||||
end
|
||||
ZoneCapture.TacticalMarkerID = nil
|
||||
end
|
||||
|
||||
-- Create new tactical marker for BLUE coalition only
|
||||
local success, markerID = pcall(function()
|
||||
return offsetCoord:MarkToCoalition(tacticalText, coalition.side.BLUE)
|
||||
end)
|
||||
|
||||
if success and markerID then
|
||||
ZoneCapture.TacticalMarkerID = markerID
|
||||
|
||||
-- Try to make the marker read-only (if available in this MOOSE version)
|
||||
pcall(function()
|
||||
offsetCoord:SetMarkReadOnly(markerID, true)
|
||||
end)
|
||||
|
||||
env.info(string.format("[CAPTURE Module] [TACTICAL] Created read-only marker for %s with %d RED, %d BLUE units", zoneName, forces.red, forces.blue))
|
||||
else
|
||||
env.info(string.format("[CAPTURE Module] [TACTICAL] Failed to create marker for %s", zoneName))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Event handler functions - define them separately for each zone
|
||||
local function OnEnterGuarded(ZoneCapture, From, Event, To)
|
||||
if From ~= To then
|
||||
local Coalition = ZoneCapture:GetCoalition()
|
||||
if Coalition == coalition.side.BLUE then
|
||||
ZoneCapture:Smoke( SMOKECOLOR.Blue )
|
||||
-- Update zone visual markers to BLUE
|
||||
ZoneCapture:UndrawZone()
|
||||
ZoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue zone boundary
|
||||
US_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
else
|
||||
ZoneCapture:Smoke( SMOKECOLOR.Red )
|
||||
-- Update zone visual markers to RED
|
||||
ZoneCapture:UndrawZone()
|
||||
ZoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red zone boundary
|
||||
RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
US_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
end
|
||||
-- Create/update tactical information marker
|
||||
CreateTacticalInfoMarker(ZoneCapture)
|
||||
end
|
||||
end
|
||||
|
||||
local function OnEnterEmpty(ZoneCapture)
|
||||
ZoneCapture:Smoke( SMOKECOLOR.Green )
|
||||
-- Update zone visual markers to GREEN (neutral)
|
||||
ZoneCapture:UndrawZone()
|
||||
ZoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green zone boundary
|
||||
US_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
RU_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- Create/update tactical information marker
|
||||
CreateTacticalInfoMarker(ZoneCapture)
|
||||
end
|
||||
|
||||
local function OnEnterAttacked(ZoneCapture)
|
||||
ZoneCapture:Smoke( SMOKECOLOR.White )
|
||||
-- Update zone visual markers to ORANGE (contested)
|
||||
ZoneCapture:UndrawZone()
|
||||
ZoneCapture:DrawZone(-1, {1, 0.5, 0}, 0.5, {1, 0.5, 0}, 0.2, 2, true) -- Orange zone boundary for contested
|
||||
local Coalition = ZoneCapture:GetCoalition()
|
||||
if Coalition == coalition.side.BLUE then
|
||||
US_CC:MessageTypeToCoalition( string.format( "%s is under attack by Russia", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
@ -138,8 +361,25 @@ local function OnEnterAttacked(ZoneCapture)
|
||||
RU_CC:MessageTypeToCoalition( string.format( "%s is under attack by the USA", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
US_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
end
|
||||
-- Create/update tactical information marker
|
||||
CreateTacticalInfoMarker(ZoneCapture)
|
||||
end
|
||||
|
||||
-- Apply event handlers to all zone capture objects
|
||||
local zoneCaptureObjects = {
|
||||
ZoneCapture_Kilpyavr,
|
||||
ZoneCapture_Severomorsk_1,
|
||||
ZoneCapture_Severomorsk_3,
|
||||
ZoneCapture_Murmansk_International,
|
||||
ZoneCapture_Monchegorsk,
|
||||
ZoneCapture_Olenya,
|
||||
ZoneCapture_Afrikanda,
|
||||
ZoneCapture_The_Mountain,
|
||||
ZoneCapture_The_River,
|
||||
ZoneCapture_The_Gulf,
|
||||
ZoneCapture_The_Lakes
|
||||
}
|
||||
|
||||
-- Victory condition monitoring
|
||||
local function CheckVictoryCondition()
|
||||
local blueZonesCount = 0
|
||||
@ -151,11 +391,11 @@ local function CheckVictoryCondition()
|
||||
end
|
||||
end
|
||||
|
||||
env.info(string.format("[VICTORY CHECK] Blue owns %d/%d zones", blueZonesCount, totalZones))
|
||||
env.info(string.format("[CAPTURE Module] [VICTORY CHECK] Blue owns %d/%d zones", blueZonesCount, totalZones))
|
||||
|
||||
if blueZonesCount >= totalZones then
|
||||
-- All zones captured by BLUE - trigger victory condition
|
||||
env.info("[VICTORY] All zones captured by BLUE! Triggering victory sequence...")
|
||||
env.info("[CAPTURE Module] [VICTORY] All zones captured by BLUE! Triggering victory sequence...")
|
||||
|
||||
-- Victory messages
|
||||
US_CC:MessageTypeToCoalition(
|
||||
@ -185,7 +425,7 @@ local function CheckVictoryCondition()
|
||||
|
||||
-- Schedule mission end after 60 seconds
|
||||
SCHEDULER:New( nil, function()
|
||||
env.info("[VICTORY] Ending mission due to complete zone capture by BLUE")
|
||||
env.info("[CAPTURE Module] [VICTORY] Ending mission due to complete zone capture by BLUE")
|
||||
-- You can trigger specific end-mission logic here
|
||||
-- For example: trigger.action.setUserFlag("MissionComplete", 1)
|
||||
-- Or call specific mission ending functions
|
||||
@ -211,9 +451,15 @@ end
|
||||
local function OnEnterCaptured(ZoneCapture)
|
||||
local Coalition = ZoneCapture:GetCoalition()
|
||||
if Coalition == coalition.side.BLUE then
|
||||
-- Update zone visual markers to BLUE for captured
|
||||
ZoneCapture:UndrawZone()
|
||||
ZoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue zone boundary
|
||||
RU_CC:MessageTypeToCoalition( string.format( "%s is captured by the USA, we lost it!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
US_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
else
|
||||
-- Update zone visual markers to RED for captured
|
||||
ZoneCapture:UndrawZone()
|
||||
ZoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red zone boundary
|
||||
US_CC:MessageTypeToCoalition( string.format( "%s is captured by Russia, we lost it!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
RU_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCapture:GetZoneName() ), MESSAGE.Type.Information )
|
||||
end
|
||||
@ -221,28 +467,18 @@ local function OnEnterCaptured(ZoneCapture)
|
||||
ZoneCapture:AddScore( "Captured", "Zone captured: Extra points granted.", 200 )
|
||||
ZoneCapture:__Guard( 30 )
|
||||
|
||||
-- Create/update tactical information marker
|
||||
CreateTacticalInfoMarker(ZoneCapture)
|
||||
|
||||
-- Check victory condition after any zone capture
|
||||
CheckVictoryCondition()
|
||||
end
|
||||
|
||||
-- Apply event handlers to all zone capture objects
|
||||
local zoneCaptureObjects = {
|
||||
ZoneCapture_Kilpyavr,
|
||||
ZoneCapture_Severomorsk_1,
|
||||
ZoneCapture_Severomorsk_3,
|
||||
ZoneCapture_Murmansk_International,
|
||||
ZoneCapture_Monchegorsk,
|
||||
ZoneCapture_Olenya,
|
||||
ZoneCapture_Afrikanda,
|
||||
ZoneCapture_The_Mountain,
|
||||
ZoneCapture_The_River,
|
||||
ZoneCapture_The_Gulf
|
||||
}
|
||||
|
||||
-- Set up event handlers for each zone with proper MOOSE methods and debugging
|
||||
local zoneNames = {
|
||||
"Kilpyavr", "Severomorsk-1", "Severomorsk-3", "Murmansk International",
|
||||
"Monchegorsk", "Olenya", "Afrikanda", "The Mountain", "The River", "The Gulf"
|
||||
"Monchegorsk", "Olenya", "Afrikanda", "The Mountain", "The River", "The Gulf",
|
||||
"The Lakes"
|
||||
}
|
||||
|
||||
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||
@ -258,54 +494,54 @@ for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||
-- Debug: Check if the underlying zone exists
|
||||
local success, zone = pcall(function() return zoneCapture:GetZone() end)
|
||||
if success and zone then
|
||||
env.info("✓ Zone 'Capture " .. zoneName .. "' successfully created and linked")
|
||||
env.info("[CAPTURE Module] ✓ Zone 'Capture " .. zoneName .. "' successfully created and linked")
|
||||
|
||||
-- Try to make zone borders visible with different approach
|
||||
-- Initialize zone borders with initial RED color (all zones start as RED coalition)
|
||||
local drawSuccess, drawError = pcall(function()
|
||||
zone:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true)
|
||||
zone:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red initial boundary
|
||||
end)
|
||||
|
||||
if not drawSuccess then
|
||||
env.info("⚠ Zone 'Capture " .. zoneName .. "' border drawing failed: " .. tostring(drawError))
|
||||
env.info("[CAPTURE Module] ⚠ Zone 'Capture " .. zoneName .. "' border drawing failed: " .. tostring(drawError))
|
||||
-- Alternative: Try simpler zone marking
|
||||
pcall(function()
|
||||
zone:SmokeZone(SMOKECOLOR.Red, 30)
|
||||
end)
|
||||
else
|
||||
env.info("✓ Zone 'Capture " .. zoneName .. "' border drawn successfully")
|
||||
env.info("[CAPTURE Module] ✓ Zone 'Capture " .. zoneName .. "' border drawn successfully with RED initial color")
|
||||
end
|
||||
else
|
||||
env.info("✗ ERROR: Zone 'Capture " .. zoneName .. "' not found in mission editor!")
|
||||
env.info(" Make sure you have a trigger zone named exactly: 'Capture " .. zoneName .. "'")
|
||||
env.info("[CAPTURE Module] ✗ ERROR: Zone 'Capture " .. zoneName .. "' not found in mission editor!")
|
||||
env.info("[CAPTURE Module] Make sure you have a trigger zone named exactly: 'Capture " .. zoneName .. "'")
|
||||
end
|
||||
else
|
||||
env.info("✗ ERROR: Zone capture object " .. i .. " (" .. (zoneNames[i] or "Unknown") .. ") is nil!")
|
||||
env.info("[CAPTURE Module] ✗ ERROR: Zone capture object " .. i .. " (" .. (zoneNames[i] or "Unknown") .. ") is nil!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Additional specific check for Olenya
|
||||
env.info("=== OLENYA SPECIFIC DEBUG ===")
|
||||
env.info("[CAPTURE Module] === OLENYA SPECIFIC DEBUG ===")
|
||||
if ZoneCapture_Olenya then
|
||||
env.info("✓ ZoneCapture_Olenya object exists")
|
||||
env.info("[CAPTURE Module] ✓ ZoneCapture_Olenya object exists")
|
||||
local success, result = pcall(function() return ZoneCapture_Olenya:GetZoneName() end)
|
||||
if success then
|
||||
env.info("✓ Zone name: " .. tostring(result))
|
||||
env.info("[CAPTURE Module] ✓ Zone name: " .. tostring(result))
|
||||
else
|
||||
env.info("✗ Could not get zone name: " .. tostring(result))
|
||||
env.info("[CAPTURE Module] ✗ Could not get zone name: " .. tostring(result))
|
||||
end
|
||||
|
||||
local success2, zone = pcall(function() return ZoneCapture_Olenya:GetZone() end)
|
||||
if success2 and zone then
|
||||
env.info("✓ Underlying zone object exists")
|
||||
env.info("[CAPTURE Module] ✓ Underlying zone object exists")
|
||||
local coord = zone:GetCoordinate()
|
||||
if coord then
|
||||
env.info("✓ Zone coordinate: " .. coord:ToStringLLDMS())
|
||||
env.info("[CAPTURE Module] ✓ Zone coordinate: " .. coord:ToStringLLDMS())
|
||||
end
|
||||
else
|
||||
env.info("✗ Underlying zone object missing: " .. tostring(zone))
|
||||
env.info("[CAPTURE Module] ✗ Underlying zone object missing: " .. tostring(zone))
|
||||
end
|
||||
else
|
||||
env.info("✗ ZoneCapture_Olenya object is nil!")
|
||||
env.info("[CAPTURE Module] ✗ ZoneCapture_Olenya object is nil!")
|
||||
end
|
||||
|
||||
-- ==========================================
|
||||
@ -330,15 +566,32 @@ local function GetZoneOwnershipStatus()
|
||||
local zoneCoalition = zoneCapture:GetCoalition()
|
||||
local zoneName = zoneNames[i] or ("Zone " .. i)
|
||||
|
||||
-- Get the current state of the zone
|
||||
local currentState = zoneCapture:GetCurrentState()
|
||||
local stateString = ""
|
||||
|
||||
-- Determine status based on coalition and state
|
||||
if zoneCoalition == coalitionTable.side.BLUE then
|
||||
status.blue = status.blue + 1
|
||||
status.zones[zoneName] = "BLUE"
|
||||
if currentState == "Attacked" then
|
||||
status.zones[zoneName] = "BLUE (Under Attack)"
|
||||
else
|
||||
status.zones[zoneName] = "BLUE"
|
||||
end
|
||||
elseif zoneCoalition == coalitionTable.side.RED then
|
||||
status.red = status.red + 1
|
||||
status.zones[zoneName] = "RED"
|
||||
if currentState == "Attacked" then
|
||||
status.zones[zoneName] = "RED (Under Attack)"
|
||||
else
|
||||
status.zones[zoneName] = "RED"
|
||||
end
|
||||
else
|
||||
status.neutral = status.neutral + 1
|
||||
status.zones[zoneName] = "NEUTRAL"
|
||||
if currentState == "Attacked" then
|
||||
status.zones[zoneName] = "NEUTRAL (Under Attack)"
|
||||
else
|
||||
status.zones[zoneName] = "NEUTRAL"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -372,7 +625,7 @@ local function BroadcastZoneStatus()
|
||||
|
||||
US_CC:MessageTypeToCoalition( fullMessage, MESSAGE.Type.Information, 15 )
|
||||
|
||||
env.info("[ZONE STATUS] " .. reportMessage:gsub("\n", " | "))
|
||||
env.info("[CAPTURE Module] [ZONE STATUS] " .. reportMessage:gsub("\n", " | "))
|
||||
|
||||
return status
|
||||
end
|
||||
@ -398,6 +651,73 @@ local ZoneMonitorScheduler = SCHEDULER:New( nil, function()
|
||||
|
||||
end, {}, 10, 300 ) -- Start after 10 seconds, repeat every 300 seconds (5 minutes)
|
||||
|
||||
-- Periodic zone color verification system (every 2 minutes)
|
||||
local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function()
|
||||
env.info("[CAPTURE Module] [ZONE COLORS] Running periodic zone color verification...")
|
||||
|
||||
-- Verify each zone's visual marker matches its coalition
|
||||
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||
if zoneCapture then
|
||||
local zoneCoalition = zoneCapture:GetCoalition()
|
||||
local zoneName = zoneNames[i] or ("Zone " .. i)
|
||||
|
||||
-- Force redraw the zone with correct color (helps prevent desync issues)
|
||||
zoneCapture:UndrawZone()
|
||||
|
||||
if zoneCoalition == coalition.side.BLUE then
|
||||
zoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue
|
||||
elseif zoneCoalition == coalition.side.RED then
|
||||
zoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red
|
||||
else
|
||||
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green (neutral)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end, {}, 60, 120 ) -- Start after 60 seconds, repeat every 120 seconds (2 minutes)
|
||||
|
||||
-- Periodic tactical marker update system (every 1 minute)
|
||||
local TacticalMarkerUpdateScheduler = SCHEDULER:New( nil, function()
|
||||
env.info("[CAPTURE Module] [TACTICAL] Running periodic tactical marker update...")
|
||||
|
||||
-- Update tactical markers for all zones
|
||||
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||
if zoneCapture then
|
||||
CreateTacticalInfoMarker(zoneCapture)
|
||||
end
|
||||
end
|
||||
|
||||
end, {}, 30, 60 ) -- Start after 30 seconds, repeat every 60 seconds (1 minute)
|
||||
|
||||
-- Function to refresh all zone colors based on current ownership
|
||||
local function RefreshAllZoneColors()
|
||||
env.info("[CAPTURE Module] [ZONE COLORS] Refreshing all zone visual markers...")
|
||||
|
||||
for i, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||
if zoneCapture then
|
||||
local coalition = zoneCapture:GetCoalition()
|
||||
local zoneName = zoneNames[i] or ("Zone " .. i)
|
||||
|
||||
-- Clear existing drawings
|
||||
zoneCapture:UndrawZone()
|
||||
|
||||
-- Redraw with correct color based on current coalition
|
||||
if coalition == coalition.side.BLUE then
|
||||
zoneCapture:DrawZone(-1, {0, 0, 1}, 0.5, {0, 0, 1}, 0.2, 2, true) -- Blue
|
||||
env.info(string.format("[CAPTURE Module] [ZONE COLORS] %s: Set to BLUE", zoneName))
|
||||
elseif coalition == coalition.side.RED then
|
||||
zoneCapture:DrawZone(-1, {1, 0, 0}, 0.5, {1, 0, 0}, 0.2, 2, true) -- Red
|
||||
env.info(string.format("[CAPTURE Module] [ZONE COLORS] %s: Set to RED", zoneName))
|
||||
else
|
||||
zoneCapture:DrawZone(-1, {0, 1, 0}, 0.5, {0, 1, 0}, 0.2, 2, true) -- Green (neutral)
|
||||
env.info(string.format("[CAPTURE Module] [ZONE COLORS] %s: Set to NEUTRAL/GREEN", zoneName))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
US_CC:MessageTypeToCoalition("Zone visual markers have been refreshed!", MESSAGE.Type.Information, 5)
|
||||
end
|
||||
|
||||
-- Manual zone status command for players (F10 radio menu)
|
||||
local function SetupZoneStatusCommands()
|
||||
-- Add F10 radio menu commands for zone status
|
||||
@ -426,21 +746,24 @@ local function SetupZoneStatusCommands()
|
||||
MESSAGE.Type.Information, 10
|
||||
)
|
||||
end )
|
||||
|
||||
-- Add command to refresh zone colors (troubleshooting tool)
|
||||
MENU_COALITION_COMMAND:New( coalition.side.BLUE, "Refresh Zone Colors", USMenu, RefreshAllZoneColors )
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize zone status monitoring
|
||||
SCHEDULER:New( nil, function()
|
||||
env.info("[VICTORY SYSTEM] Initializing zone monitoring system...")
|
||||
env.info("[CAPTURE Module] [VICTORY SYSTEM] Initializing zone monitoring system...")
|
||||
SetupZoneStatusCommands()
|
||||
|
||||
-- Initial status report
|
||||
SCHEDULER:New( nil, function()
|
||||
env.info("[VICTORY SYSTEM] Broadcasting initial zone status...")
|
||||
env.info("[CAPTURE Module] [VICTORY SYSTEM] Broadcasting initial zone status...")
|
||||
BroadcastZoneStatus()
|
||||
end, {}, 30 ) -- Initial report after 30 seconds
|
||||
|
||||
end, {}, 5 ) -- Initialize after 5 seconds
|
||||
|
||||
env.info("[VICTORY SYSTEM] Zone capture victory monitoring system loaded successfully!")
|
||||
env.info("[CAPTURE Module] [VICTORY SYSTEM] Zone capture victory monitoring system loaded successfully!")
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ end
|
||||
local redHQ = GROUP:FindByName("REDHQ")
|
||||
if redHQ then
|
||||
RU_CC = COMMANDCENTER:New(redHQ, "Russia HQ")
|
||||
RU_Mission = MISSION:New(RU_CC, "Operation Polar Shield", "Primary", "Destroy the City of Ushuaia and its supporting FARPS", coalition.side.RED)
|
||||
RU_Mission = MISSION:New(RU_CC, "Operation Polar Shield", "Primary", "Hold what we have, take what we don't.", coalition.side.RED)
|
||||
--RU_Score = SCORING:New("Operation Polar Shield")
|
||||
--RU_Mission:AddScoring(RU_Score)
|
||||
RU_Mission:Start()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
483
DCS_Kola/Operation_Polar_Shield/Simple_TADC.lua
Normal file
483
DCS_Kola/Operation_Polar_Shield/Simple_TADC.lua
Normal file
@ -0,0 +1,483 @@
|
||||
-- Simple TADC - Just Works
|
||||
-- Detect blue aircraft, launch red fighters, make them intercept
|
||||
|
||||
-- Configuration
|
||||
local TADC_CONFIG = {
|
||||
checkInterval = 30, -- Check for threats every 10 seconds
|
||||
interceptRatio = 1.4, -- Launch 1 fighter per threat (minimum)
|
||||
maxActiveCAP = 24, -- Max fighters airborne at once
|
||||
squadronCooldown = 900, -- Squadron cooldown after destruction (15 minutes)
|
||||
}
|
||||
|
||||
-- Define squadron configurations with their designated airbases and patrol zones
|
||||
local squadronConfigs = {
|
||||
-- Fixed-wing fighters patrol RED BORDER zone
|
||||
{
|
||||
templateName = "FIGHTER_SWEEP_RED_Kilpyavr",
|
||||
displayName = "Kilpyavr CAP",
|
||||
airbaseName = "Kilpyavr",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 15000,
|
||||
speed = 300,
|
||||
patrolTime = 20,
|
||||
type = "FIGHTER"
|
||||
},
|
||||
{
|
||||
templateName = "FIGHTER_SWEEP_RED_Severomorsk-1",
|
||||
displayName = "Severomorsk-1 CAP",
|
||||
airbaseName = "Severomorsk-1",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 20000,
|
||||
speed = 350,
|
||||
patrolTime = 25,
|
||||
type = "FIGHTER"
|
||||
},
|
||||
{
|
||||
templateName = "FIGHTER_SWEEP_RED_Severomorsk-3",
|
||||
displayName = "Severomorsk-3 CAP",
|
||||
airbaseName = "Severomorsk-3",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 25000,
|
||||
speed = 400,
|
||||
patrolTime = 30,
|
||||
type = "FIGHTER"
|
||||
},
|
||||
{
|
||||
templateName = "FIGHTER_SWEEP_RED_Murmansk",
|
||||
displayName = "Murmansk CAP",
|
||||
airbaseName = "Murmansk International",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 18000,
|
||||
speed = 320,
|
||||
patrolTime = 22,
|
||||
type = "FIGHTER"
|
||||
},
|
||||
{
|
||||
templateName = "FIGHTER_SWEEP_RED_Monchegorsk",
|
||||
displayName = "Monchegorsk CAP",
|
||||
airbaseName = "Monchegorsk",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 22000,
|
||||
speed = 380,
|
||||
patrolTime = 25,
|
||||
type = "FIGHTER"
|
||||
},
|
||||
{
|
||||
templateName = "FIGHTER_SWEEP_RED_Olenya",
|
||||
displayName = "Olenya CAP",
|
||||
airbaseName = "Olenya",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 30000,
|
||||
speed = 450,
|
||||
patrolTime = 35,
|
||||
type = "FIGHTER"
|
||||
},
|
||||
--[[]
|
||||
-- Helicopter squadron patrols HELO BORDER zone
|
||||
{
|
||||
templateName = "HELO_SWEEP_RED_Afrikanda",
|
||||
displayName = "Afrikanda Helo CAP",
|
||||
airbaseName = "Afrikanda",
|
||||
aircraft = 4,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 1000,
|
||||
speed = 150,
|
||||
patrolTime = 30,
|
||||
type = "HELICOPTER"
|
||||
}
|
||||
--]]
|
||||
}
|
||||
|
||||
-- Track active missions
|
||||
local activeInterceptors = {}
|
||||
local lastLaunchTime = {}
|
||||
local assignedThreats = {} -- Track which threats already have interceptors assigned
|
||||
local squadronCooldowns = {} -- Track squadron cooldowns after destruction
|
||||
|
||||
-- Simple logging
|
||||
local function log(message)
|
||||
env.info("[Simple TADC] " .. message)
|
||||
end
|
||||
|
||||
-- Send interceptor back to base
|
||||
local function sendInterceptorHome(interceptor)
|
||||
if not interceptor or not interceptor:IsAlive() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Find nearest friendly airbase
|
||||
local interceptorCoord = interceptor:GetCoordinate()
|
||||
local nearestAirbase = nil
|
||||
local shortestDistance = math.huge
|
||||
|
||||
-- Check all squadron airbases to find the nearest one that's still friendly
|
||||
for _, squadron in pairs(squadronConfigs) do
|
||||
local airbase = AIRBASE:FindByName(squadron.airbaseName)
|
||||
if airbase and airbase:GetCoalition() == coalition.side.RED and airbase:IsAlive() then
|
||||
local airbaseCoord = airbase:GetCoordinate()
|
||||
local distance = interceptorCoord:Get2DDistance(airbaseCoord)
|
||||
if distance < shortestDistance then
|
||||
shortestDistance = distance
|
||||
nearestAirbase = airbase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if nearestAirbase then
|
||||
local airbaseCoord = nearestAirbase:GetCoordinate()
|
||||
local rtbAltitude = 3000 -- RTB at 3000 feet
|
||||
local rtbCoord = airbaseCoord:SetAltitude(rtbAltitude * 0.3048) -- Convert feet to meters
|
||||
|
||||
-- Clear current tasks and route home
|
||||
interceptor:ClearTasks()
|
||||
interceptor:RouteAirTo(rtbCoord, 250 * 0.5144, "BARO") -- RTB at 250 knots
|
||||
|
||||
log("Sending " .. interceptor:GetName() .. " back to " .. nearestAirbase:GetName())
|
||||
|
||||
-- Schedule cleanup after they should have landed (give them time to get home)
|
||||
local flightTime = math.ceil(shortestDistance / (250 * 0.5144)) + 300 -- Flight time + 5 min buffer
|
||||
SCHEDULER:New(nil, function()
|
||||
if activeInterceptors[interceptor:GetName()] then
|
||||
activeInterceptors[interceptor:GetName()] = nil
|
||||
log("Cleaned up " .. interceptor:GetName() .. " after RTB")
|
||||
end
|
||||
end, {}, flightTime)
|
||||
else
|
||||
log("No friendly airbase found for " .. interceptor:GetName() .. ", will clean up normally")
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if airbase is still usable
|
||||
local function isAirbaseUsable(airbaseName)
|
||||
local airbase = AIRBASE:FindByName(airbaseName)
|
||||
if not airbase then
|
||||
return false, "not found"
|
||||
elseif airbase:GetCoalition() ~= coalition.side.RED then
|
||||
return false, "captured by " .. (airbase:GetCoalition() == coalition.side.BLUE and "Blue" or "Neutral")
|
||||
elseif not airbase:IsAlive() then
|
||||
return false, "destroyed"
|
||||
else
|
||||
return true, "operational"
|
||||
end
|
||||
end
|
||||
|
||||
-- Count active red fighters
|
||||
local function countActiveFighters()
|
||||
local count = 0
|
||||
for _, interceptorData in pairs(activeInterceptors) do
|
||||
if interceptorData and interceptorData.group and interceptorData.group:IsAlive() then
|
||||
count = count + interceptorData.group:GetSize()
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
-- Find best squadron to launch
|
||||
local function findBestSquadron(threatCoord)
|
||||
local bestSquadron = nil
|
||||
local shortestDistance = math.huge
|
||||
local currentTime = timer.getTime()
|
||||
|
||||
for _, squadron in pairs(squadronConfigs) do
|
||||
-- Check if squadron is on cooldown
|
||||
local squadronAvailable = true
|
||||
if squadronCooldowns[squadron.templateName] then
|
||||
local cooldownEnd = squadronCooldowns[squadron.templateName]
|
||||
if currentTime < cooldownEnd then
|
||||
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
|
||||
log("Squadron " .. squadron.displayName .. " on cooldown for " .. timeLeft .. " more minutes")
|
||||
squadronAvailable = false
|
||||
else
|
||||
-- Cooldown expired, remove it
|
||||
squadronCooldowns[squadron.templateName] = nil
|
||||
log("Squadron " .. squadron.displayName .. " cooldown expired, available for launch")
|
||||
end
|
||||
end
|
||||
|
||||
if squadronAvailable then
|
||||
-- Check if airbase is still under Red control
|
||||
local airbase = AIRBASE:FindByName(squadron.airbaseName)
|
||||
if not airbase then
|
||||
log("Warning: Airbase " .. squadron.airbaseName .. " not found")
|
||||
elseif airbase:GetCoalition() ~= coalition.side.RED then
|
||||
log("Warning: Airbase " .. squadron.airbaseName .. " no longer under Red control")
|
||||
elseif not airbase:IsAlive() then
|
||||
log("Warning: Airbase " .. squadron.airbaseName .. " is destroyed")
|
||||
else
|
||||
-- Airbase is valid, check if squadron can spawn
|
||||
local spawn = SPAWN:New(squadron.templateName)
|
||||
if spawn then
|
||||
-- Get squadron's airbase
|
||||
local template = GROUP:FindByName(squadron.templateName)
|
||||
if template then
|
||||
local airbaseCoord = template:GetCoordinate()
|
||||
if airbaseCoord then
|
||||
local distance = airbaseCoord:Get2DDistance(threatCoord)
|
||||
if distance < shortestDistance then
|
||||
shortestDistance = distance
|
||||
bestSquadron = squadron
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return bestSquadron
|
||||
end
|
||||
|
||||
-- Launch interceptor
|
||||
local function launchInterceptor(threatGroup)
|
||||
if not threatGroup or not threatGroup:IsAlive() then
|
||||
return
|
||||
end
|
||||
|
||||
local threatCoord = threatGroup:GetCoordinate()
|
||||
local threatName = threatGroup:GetName()
|
||||
local threatSize = threatGroup:GetSize() -- Get the number of aircraft in the threat group
|
||||
|
||||
-- Check if threat already has interceptors assigned
|
||||
if assignedThreats[threatName] then
|
||||
local assignedInterceptors = assignedThreats[threatName]
|
||||
local aliveCount = 0
|
||||
|
||||
-- Check if assigned interceptors are still alive
|
||||
if type(assignedInterceptors) == "table" then
|
||||
for _, interceptor in pairs(assignedInterceptors) do
|
||||
if interceptor and interceptor:IsAlive() then
|
||||
aliveCount = aliveCount + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Handle legacy single interceptor assignment
|
||||
if assignedInterceptors and assignedInterceptors:IsAlive() then
|
||||
aliveCount = 1
|
||||
end
|
||||
end
|
||||
|
||||
if aliveCount > 0 then
|
||||
return -- Still being intercepted
|
||||
else
|
||||
-- All interceptors are dead, clear the assignment
|
||||
assignedThreats[threatName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate how many interceptors to launch (at least match threat size, up to ratio)
|
||||
local interceptorsNeeded = math.max(threatSize, math.ceil(threatSize * TADC_CONFIG.interceptRatio))
|
||||
|
||||
-- Check if we have capacity
|
||||
if countActiveFighters() + interceptorsNeeded > TADC_CONFIG.maxActiveCAP then
|
||||
interceptorsNeeded = TADC_CONFIG.maxActiveCAP - countActiveFighters()
|
||||
if interceptorsNeeded <= 0 then
|
||||
log("Max fighters airborne, skipping launch")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Find best squadron
|
||||
local squadron = findBestSquadron(threatCoord)
|
||||
if not squadron then
|
||||
log("No squadron available")
|
||||
return
|
||||
end
|
||||
|
||||
-- Launch multiple interceptors to match threat
|
||||
local spawn = SPAWN:New(squadron.templateName)
|
||||
local interceptors = {}
|
||||
|
||||
for i = 1, interceptorsNeeded do
|
||||
local interceptor = spawn:Spawn()
|
||||
|
||||
if interceptor then
|
||||
table.insert(interceptors, interceptor)
|
||||
|
||||
-- Wait a moment for initialization
|
||||
SCHEDULER:New(nil, function()
|
||||
if interceptor and interceptor:IsAlive() then
|
||||
-- Set aggressive AI
|
||||
interceptor:OptionROEOpenFire()
|
||||
interceptor:OptionROTVertical()
|
||||
|
||||
-- Route to threat
|
||||
local currentThreatCoord = threatGroup:GetCoordinate()
|
||||
if currentThreatCoord then
|
||||
local interceptCoord = currentThreatCoord:SetAltitude(squadron.altitude * 0.3048) -- Convert feet to meters
|
||||
interceptor:RouteAirTo(interceptCoord, squadron.speed * 0.5144, "BARO") -- Convert kts to m/s
|
||||
|
||||
-- Attack the threat
|
||||
local attackTask = {
|
||||
id = 'AttackGroup',
|
||||
params = {
|
||||
groupId = threatGroup:GetID(),
|
||||
weaponType = 'Auto',
|
||||
attackQtyLimit = 0,
|
||||
priority = 1
|
||||
}
|
||||
}
|
||||
interceptor:PushTask(attackTask, 1)
|
||||
end
|
||||
end
|
||||
end, {}, 3)
|
||||
|
||||
-- Track the interceptor with squadron info
|
||||
activeInterceptors[interceptor:GetName()] = {
|
||||
group = interceptor,
|
||||
squadron = squadron.templateName,
|
||||
displayName = squadron.displayName
|
||||
}
|
||||
|
||||
-- Emergency cleanup (safety net - should normally RTB before this)
|
||||
SCHEDULER:New(nil, function()
|
||||
if activeInterceptors[interceptor:GetName()] then
|
||||
log("Emergency cleanup of " .. interceptor:GetName() .. " (should have RTB'd)")
|
||||
activeInterceptors[interceptor:GetName()] = nil
|
||||
end
|
||||
end, {}, 7200) -- Emergency cleanup after 2 hours
|
||||
end
|
||||
end
|
||||
|
||||
-- Log the launch and track assignment
|
||||
if #interceptors > 0 then
|
||||
log("Launched " .. #interceptors .. " x " .. squadron.displayName .. " to intercept " ..
|
||||
threatSize .. " x " .. threatName)
|
||||
assignedThreats[threatName] = interceptors -- Track which interceptors are assigned to this threat
|
||||
lastLaunchTime[threatName] = timer.getTime()
|
||||
end
|
||||
end
|
||||
|
||||
-- Main threat detection loop
|
||||
local function detectThreats()
|
||||
log("Scanning for threats...")
|
||||
|
||||
-- Clean up dead threats from tracking
|
||||
local currentThreats = {}
|
||||
|
||||
-- Find all blue aircraft
|
||||
local blueAircraft = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryAirplane():FilterStart()
|
||||
local threatCount = 0
|
||||
|
||||
blueAircraft:ForEach(function(blueGroup)
|
||||
if blueGroup and blueGroup:IsAlive() then
|
||||
threatCount = threatCount + 1
|
||||
currentThreats[blueGroup:GetName()] = true
|
||||
log("Found threat: " .. blueGroup:GetName() .. " (" .. blueGroup:GetTypeName() .. ")")
|
||||
|
||||
-- Launch interceptor for this threat
|
||||
launchInterceptor(blueGroup)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Clean up assignments for threats that no longer exist and send interceptors home
|
||||
for threatName, assignedInterceptors in pairs(assignedThreats) do
|
||||
if not currentThreats[threatName] then
|
||||
log("Threat " .. threatName .. " eliminated, sending interceptors home...")
|
||||
|
||||
-- Send assigned interceptors back to base
|
||||
if type(assignedInterceptors) == "table" then
|
||||
for _, interceptor in pairs(assignedInterceptors) do
|
||||
if interceptor and interceptor:IsAlive() then
|
||||
sendInterceptorHome(interceptor)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Handle legacy single interceptor assignment
|
||||
if assignedInterceptors and assignedInterceptors:IsAlive() then
|
||||
sendInterceptorHome(assignedInterceptors)
|
||||
end
|
||||
end
|
||||
|
||||
assignedThreats[threatName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Count assigned threats
|
||||
local assignedCount = 0
|
||||
for _ in pairs(assignedThreats) do assignedCount = assignedCount + 1 end
|
||||
|
||||
log("Scan complete: " .. threatCount .. " threats, " .. countActiveFighters() .. " active fighters, " ..
|
||||
assignedCount .. " assigned")
|
||||
end
|
||||
|
||||
-- Monitor interceptor groups and apply cooldowns when destroyed
|
||||
local function monitorInterceptors()
|
||||
local currentTime = timer.getTime()
|
||||
local destroyedSquadrons = {}
|
||||
|
||||
-- Check all active interceptors
|
||||
for interceptorName, interceptorData in pairs(activeInterceptors) do
|
||||
if interceptorData and interceptorData.group then
|
||||
if not interceptorData.group:IsAlive() then
|
||||
-- Interceptor group is destroyed
|
||||
local squadronName = interceptorData.squadron
|
||||
local displayName = interceptorData.displayName
|
||||
|
||||
-- Track destroyed squadrons (avoid duplicate cooldowns)
|
||||
if not destroyedSquadrons[squadronName] then
|
||||
destroyedSquadrons[squadronName] = displayName
|
||||
|
||||
-- Apply cooldown
|
||||
squadronCooldowns[squadronName] = currentTime + TADC_CONFIG.squadronCooldown
|
||||
local cooldownMinutes = TADC_CONFIG.squadronCooldown / 60
|
||||
log("Squadron " .. displayName .. " DESTROYED! Applying " .. cooldownMinutes .. " minute cooldown")
|
||||
end
|
||||
|
||||
-- Remove from active tracking
|
||||
activeInterceptors[interceptorName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Periodic airbase status check
|
||||
local function checkAirbaseStatus()
|
||||
log("=== AIRBASE STATUS REPORT ===")
|
||||
local usableCount = 0
|
||||
local currentTime = timer.getTime()
|
||||
|
||||
for _, squadron in pairs(squadronConfigs) do
|
||||
local usable, status = isAirbaseUsable(squadron.airbaseName)
|
||||
|
||||
-- Check if squadron is on cooldown
|
||||
local cooldownStatus = ""
|
||||
if squadronCooldowns[squadron.templateName] then
|
||||
local cooldownEnd = squadronCooldowns[squadron.templateName]
|
||||
if currentTime < cooldownEnd then
|
||||
local timeLeft = math.ceil((cooldownEnd - currentTime) / 60)
|
||||
cooldownStatus = " (COOLDOWN: " .. timeLeft .. "m)"
|
||||
status = status .. cooldownStatus
|
||||
end
|
||||
end
|
||||
|
||||
if usable and cooldownStatus == "" then
|
||||
usableCount = usableCount + 1
|
||||
log("✓ " .. squadron.airbaseName .. " - " .. status)
|
||||
else
|
||||
log("✗ " .. squadron.airbaseName .. " - " .. status)
|
||||
end
|
||||
end
|
||||
|
||||
log("Status: " .. usableCount .. "/" .. #squadronConfigs .. " airbases operational")
|
||||
end
|
||||
|
||||
-- Start the system
|
||||
log("Simple TADC starting...")
|
||||
log("Squadrons configured: " .. #squadronConfigs)
|
||||
|
||||
-- Run detection every interval
|
||||
SCHEDULER:New(nil, detectThreats, {}, 5, TADC_CONFIG.checkInterval)
|
||||
|
||||
-- Run interceptor monitoring every 30 seconds
|
||||
SCHEDULER:New(nil, monitorInterceptors, {}, 10, 30)
|
||||
|
||||
-- Run airbase status check every 2 minutes
|
||||
SCHEDULER:New(nil, checkAirbaseStatus, {}, 30, 120)
|
||||
|
||||
log("Simple TADC operational!")
|
||||
304
DCS_Kola/Operation_Polar_Shield/TADC_Diagnostic.lua
Normal file
304
DCS_Kola/Operation_Polar_Shield/TADC_Diagnostic.lua
Normal file
@ -0,0 +1,304 @@
|
||||
-- TADC Diagnostic Script
|
||||
-- This script will run diagnostics on the TADC system to identify why no aircraft are launching
|
||||
|
||||
env.info("=== TADC DIAGNOSTIC SCRIPT STARTING ===")
|
||||
|
||||
-- Test 1: Check if zones exist and are properly defined
|
||||
local function testZones()
|
||||
env.info("=== ZONE DIAGNOSTIC ===")
|
||||
|
||||
local redBorderGroup = GROUP:FindByName("RED BORDER")
|
||||
local heloBorderGroup = GROUP:FindByName("HELO BORDER")
|
||||
|
||||
if redBorderGroup then
|
||||
env.info("✓ RED BORDER group found")
|
||||
local redZone = ZONE_POLYGON:New("RED BORDER TEST", redBorderGroup)
|
||||
if redZone then
|
||||
env.info("✓ RED BORDER zone created successfully")
|
||||
local coord = redZone:GetCoordinate()
|
||||
if coord then
|
||||
env.info("✓ RED BORDER zone coordinate: " .. coord:ToStringLLDMS())
|
||||
else
|
||||
env.info("✗ RED BORDER zone has no coordinate")
|
||||
end
|
||||
else
|
||||
env.info("✗ Failed to create RED BORDER zone")
|
||||
end
|
||||
else
|
||||
env.info("✗ RED BORDER group NOT found - this is likely the problem!")
|
||||
end
|
||||
|
||||
if heloBorderGroup then
|
||||
env.info("✓ HELO BORDER group found")
|
||||
local heloZone = ZONE_POLYGON:New("HELO BORDER TEST", heloBorderGroup)
|
||||
if heloZone then
|
||||
env.info("✓ HELO BORDER zone created successfully")
|
||||
local coord = heloZone:GetCoordinate()
|
||||
if coord then
|
||||
env.info("✓ HELO BORDER zone coordinate: " .. coord:ToStringLLDMS())
|
||||
else
|
||||
env.info("✗ HELO BORDER zone has no coordinate")
|
||||
end
|
||||
else
|
||||
env.info("✗ Failed to create HELO BORDER zone")
|
||||
end
|
||||
else
|
||||
env.info("✗ HELO BORDER group NOT found - this may be the problem!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Test 2: Check if Blue aircraft exist on the map
|
||||
local function testBlueAircraft()
|
||||
env.info("=== BLUE AIRCRAFT DIAGNOSTIC ===")
|
||||
|
||||
local BlueAircraft = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryAirplane():FilterStart()
|
||||
local blueCount = BlueAircraft:Count()
|
||||
|
||||
env.info("Total Blue aircraft on map: " .. blueCount)
|
||||
|
||||
if blueCount > 0 then
|
||||
env.info("Blue aircraft found:")
|
||||
BlueAircraft:ForEach(function(blueGroup)
|
||||
if blueGroup and blueGroup:IsAlive() then
|
||||
local coord = blueGroup:GetCoordinate()
|
||||
local name = blueGroup:GetName()
|
||||
if coord then
|
||||
env.info(" - " .. name .. " at " .. coord:ToStringLLDMS())
|
||||
else
|
||||
env.info(" - " .. name .. " (no coordinate)")
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
env.info("✗ NO BLUE AIRCRAFT FOUND - this is likely why no intercepts are launching!")
|
||||
env.info("SOLUTION: Add Blue coalition aircraft to the mission or spawn some for testing")
|
||||
end
|
||||
end
|
||||
|
||||
-- Test 3: Check if Blue aircraft are in the detection zones
|
||||
local function testBlueInZones()
|
||||
env.info("=== BLUE AIRCRAFT IN ZONES DIAGNOSTIC ===")
|
||||
|
||||
local redBorderGroup = GROUP:FindByName("RED BORDER")
|
||||
local heloBorderGroup = GROUP:FindByName("HELO BORDER")
|
||||
|
||||
if not redBorderGroup or not heloBorderGroup then
|
||||
env.info("✗ Cannot test zones - border groups missing")
|
||||
return
|
||||
end
|
||||
|
||||
local redZone = ZONE_POLYGON:New("RED BORDER TEST", redBorderGroup)
|
||||
local heloZone = ZONE_POLYGON:New("HELO BORDER TEST", heloBorderGroup)
|
||||
|
||||
local BlueAircraft = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryAirplane():FilterStart()
|
||||
local blueCount = BlueAircraft:Count()
|
||||
|
||||
if blueCount == 0 then
|
||||
env.info("✗ No Blue aircraft to test zone containment")
|
||||
return
|
||||
end
|
||||
|
||||
local inRedZone = 0
|
||||
local inHeloZone = 0
|
||||
|
||||
BlueAircraft:ForEach(function(blueGroup)
|
||||
if blueGroup and blueGroup:IsAlive() then
|
||||
local coord = blueGroup:GetCoordinate()
|
||||
local name = blueGroup:GetName()
|
||||
|
||||
if coord then
|
||||
local inRed = redZone and redZone:IsCoordinateInZone(coord)
|
||||
local inHelo = heloZone and heloZone:IsCoordinateInZone(coord)
|
||||
|
||||
if inRed then
|
||||
inRedZone = inRedZone + 1
|
||||
env.info(" ✓ " .. name .. " is in RED BORDER zone")
|
||||
elseif inHelo then
|
||||
inHeloZone = inHeloZone + 1
|
||||
env.info(" ✓ " .. name .. " is in HELO BORDER zone")
|
||||
else
|
||||
env.info(" - " .. name .. " is NOT in any border zone")
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
env.info("Summary: " .. inRedZone .. " in RED zone, " .. inHeloZone .. " in HELO zone")
|
||||
|
||||
if inRedZone == 0 and inHeloZone == 0 then
|
||||
env.info("✗ NO BLUE AIRCRAFT IN DETECTION ZONES - this is why no intercepts are launching!")
|
||||
env.info("SOLUTION: Move Blue aircraft into the border zones or expand the zone definitions")
|
||||
end
|
||||
end
|
||||
|
||||
-- Test 4: Check squadron templates
|
||||
local function testSquadronTemplates()
|
||||
env.info("=== SQUADRON TEMPLATE DIAGNOSTIC ===")
|
||||
|
||||
local squadronTemplates = {
|
||||
"FIGHTER_SWEEP_RED_Kilpyavr",
|
||||
"FIGHTER_SWEEP_RED_Severomorsk-1",
|
||||
"FIGHTER_SWEEP_RED_Severomorsk-3",
|
||||
"FIGHTER_SWEEP_RED_Murmansk",
|
||||
"FIGHTER_SWEEP_RED_Monchegorsk",
|
||||
"FIGHTER_SWEEP_RED_Olenya",
|
||||
"HELO_SWEEP_RED_Afrikanda"
|
||||
}
|
||||
|
||||
local found = 0
|
||||
local total = #squadronTemplates
|
||||
|
||||
for _, templateName in pairs(squadronTemplates) do
|
||||
local template = GROUP:FindByName(templateName)
|
||||
if template then
|
||||
env.info("✓ Found template: " .. templateName)
|
||||
|
||||
-- Check if template is alive (should NOT be for Late Activation)
|
||||
local isAlive = template:IsAlive()
|
||||
if isAlive then
|
||||
env.info(" ⚠ WARNING: Template is ALIVE - should be set to Late Activation")
|
||||
else
|
||||
env.info(" ✓ Template correctly set to Late Activation")
|
||||
end
|
||||
|
||||
-- Check coalition
|
||||
local coalition = template:GetCoalition()
|
||||
if coalition == 1 then
|
||||
env.info(" ✓ Template is Red coalition")
|
||||
else
|
||||
env.info(" ✗ Template is NOT Red coalition (coalition=" .. coalition .. ")")
|
||||
end
|
||||
|
||||
found = found + 1
|
||||
else
|
||||
env.info("✗ Missing template: " .. templateName)
|
||||
end
|
||||
end
|
||||
|
||||
env.info("Squadron templates found: " .. found .. "/" .. total)
|
||||
|
||||
if found == 0 then
|
||||
env.info("✗ NO SQUADRON TEMPLATES FOUND - this is why no aircraft can launch!")
|
||||
env.info("SOLUTION: Create squadron groups in Mission Editor with the correct names and set to Late Activation")
|
||||
end
|
||||
end
|
||||
|
||||
-- Test 5: Check airbases
|
||||
local function testAirbases()
|
||||
env.info("=== AIRBASE DIAGNOSTIC ===")
|
||||
|
||||
local airbases = {
|
||||
"Kilpyavr", "Severomorsk-1", "Severomorsk-3",
|
||||
"Murmansk International", "Monchegorsk", "Olenya", "Afrikanda"
|
||||
}
|
||||
|
||||
local found = 0
|
||||
local redBases = 0
|
||||
|
||||
for _, airbaseName in pairs(airbases) do
|
||||
local airbase = AIRBASE:FindByName(airbaseName)
|
||||
if airbase then
|
||||
env.info("✓ Found airbase: " .. airbaseName)
|
||||
|
||||
local coalition = airbase:GetCoalition()
|
||||
local coalitionName = coalition == 1 and "Red" or (coalition == 2 and "Blue" or "Neutral")
|
||||
env.info(" Coalition: " .. coalitionName)
|
||||
|
||||
if coalition == 1 then
|
||||
redBases = redBases + 1
|
||||
end
|
||||
|
||||
found = found + 1
|
||||
else
|
||||
env.info("✗ Airbase not found: " .. airbaseName)
|
||||
end
|
||||
end
|
||||
|
||||
env.info("Airbases found: " .. found .. "/" .. #airbases .. " (Red: " .. redBases .. ")")
|
||||
end
|
||||
|
||||
-- Test 6: Manual threat detection test
|
||||
local function testThreatDetection()
|
||||
env.info("=== THREAT DETECTION TEST ===")
|
||||
|
||||
-- Try to manually detect threats
|
||||
local BlueAircraft = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryAirplane():FilterStart()
|
||||
local blueCount = BlueAircraft:Count()
|
||||
|
||||
env.info("Manual threat scan - found " .. blueCount .. " blue aircraft")
|
||||
|
||||
if blueCount > 0 then
|
||||
local redBorderGroup = GROUP:FindByName("RED BORDER")
|
||||
local heloBorderGroup = GROUP:FindByName("HELO BORDER")
|
||||
|
||||
if redBorderGroup and heloBorderGroup then
|
||||
local redZone = ZONE_POLYGON:New("RED BORDER TEST", redBorderGroup)
|
||||
local heloZone = ZONE_POLYGON:New("HELO BORDER TEST", heloBorderGroup)
|
||||
|
||||
local threatsInZones = 0
|
||||
|
||||
BlueAircraft:ForEach(function(blueGroup)
|
||||
if blueGroup and blueGroup:IsAlive() then
|
||||
local coord = blueGroup:GetCoordinate()
|
||||
local name = blueGroup:GetName()
|
||||
|
||||
if coord then
|
||||
local inRed = redZone:IsCoordinateInZone(coord)
|
||||
local inHelo = heloZone:IsCoordinateInZone(coord)
|
||||
|
||||
if inRed or inHelo then
|
||||
threatsInZones = threatsInZones + 1
|
||||
local classification = "UNKNOWN"
|
||||
local category = blueGroup:GetCategory()
|
||||
local typeName = blueGroup:GetTypeName() or "Unknown"
|
||||
|
||||
if category == Group.Category.AIRPLANE then
|
||||
if string.find(typeName:upper(), "B-") or string.find(typeName:upper(), "BOMBER") then
|
||||
classification = "BOMBER"
|
||||
elseif string.find(typeName:upper(), "A-") or string.find(typeName:upper(), "ATTACK") then
|
||||
classification = "ATTACK"
|
||||
else
|
||||
classification = "FIGHTER"
|
||||
end
|
||||
elseif category == Group.Category.HELICOPTER then
|
||||
classification = "HELICOPTER"
|
||||
end
|
||||
|
||||
env.info(" THREAT DETECTED: " .. name .. " (" .. classification .. ") in " .. (inRed and "RED BORDER" or "HELO BORDER"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if threatsInZones == 0 then
|
||||
env.info("✗ No threats detected in border zones")
|
||||
else
|
||||
env.info("✓ " .. threatsInZones .. " threats detected in border zones")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Run all diagnostic tests
|
||||
local function runAllDiagnostics()
|
||||
env.info("Starting comprehensive TADC diagnostic...")
|
||||
|
||||
testZones()
|
||||
testBlueAircraft()
|
||||
testBlueInZones()
|
||||
testSquadronTemplates()
|
||||
testAirbases()
|
||||
testThreatDetection()
|
||||
|
||||
env.info("=== DIAGNOSTIC COMPLETE ===")
|
||||
env.info("Check the log above for any ✗ (failed) items - these are likely the cause of the problem")
|
||||
end
|
||||
|
||||
-- Schedule the diagnostic to run after a delay
|
||||
SCHEDULER:New(nil, runAllDiagnostics, {}, 3)
|
||||
|
||||
-- Also create a repeating diagnostic every 60 seconds for ongoing monitoring
|
||||
SCHEDULER:New(nil, function()
|
||||
env.info("=== PERIODIC THREAT CHECK ===")
|
||||
testBlueInZones()
|
||||
end, {}, 10, 60) -- Start after 10 seconds, repeat every 60 seconds
|
||||
164
DCS_Kola/Operation_Polar_Shield/TADC_ManualTest.lua
Normal file
164
DCS_Kola/Operation_Polar_Shield/TADC_ManualTest.lua
Normal file
@ -0,0 +1,164 @@
|
||||
-- TADC Manual Launch Test
|
||||
-- This script will attempt to manually launch one aircraft to test if the spawn system works
|
||||
|
||||
env.info("=== TADC MANUAL LAUNCH TEST ===")
|
||||
|
||||
-- Wait a few seconds for MOOSE to initialize, then try a manual launch
|
||||
SCHEDULER:New(nil, function()
|
||||
|
||||
env.info("Attempting manual aircraft launch...")
|
||||
|
||||
-- Test configuration - using the first squadron
|
||||
local testConfig = {
|
||||
templateName = "FIGHTER_SWEEP_RED_Severomorsk-1",
|
||||
displayName = "Severomorsk-1 TEST",
|
||||
airbaseName = "Severomorsk-1",
|
||||
aircraft = 1,
|
||||
skill = AI.Skill.GOOD,
|
||||
altitude = 20000,
|
||||
speed = 350,
|
||||
patrolTime = 25,
|
||||
type = "FIGHTER"
|
||||
}
|
||||
|
||||
-- Manual launch function (simplified version)
|
||||
local function manualLaunch(config)
|
||||
env.info("=== MANUAL LAUNCH ATTEMPT ===")
|
||||
env.info("Template: " .. config.templateName)
|
||||
env.info("Airbase: " .. config.airbaseName)
|
||||
|
||||
local success, errorMsg = pcall(function()
|
||||
-- Check if template exists
|
||||
local templateGroup = GROUP:FindByName(config.templateName)
|
||||
if not templateGroup then
|
||||
env.info("✗ CRITICAL: Template group not found: " .. config.templateName)
|
||||
env.info("DIAGNOSIS: This template does not exist in the mission!")
|
||||
env.info("SOLUTION: Create a group named '" .. config.templateName .. "' in Mission Editor")
|
||||
return
|
||||
end
|
||||
|
||||
env.info("✓ Template group found: " .. config.templateName)
|
||||
|
||||
-- Check template properties
|
||||
local coalition = templateGroup:GetCoalition()
|
||||
local isAlive = templateGroup:IsAlive()
|
||||
|
||||
env.info("Template coalition: " .. (coalition == 1 and "Red" or (coalition == 2 and "Blue" or "Neutral")))
|
||||
env.info("Template alive: " .. tostring(isAlive))
|
||||
|
||||
if coalition ~= 1 then
|
||||
env.info("✗ CRITICAL: Template is not Red coalition!")
|
||||
env.info("SOLUTION: Set '" .. config.templateName .. "' to Red coalition in Mission Editor")
|
||||
return
|
||||
end
|
||||
|
||||
if isAlive then
|
||||
env.info("⚠ WARNING: Template is alive - Late Activation may not be set")
|
||||
env.info("RECOMMENDATION: Set '" .. config.templateName .. "' to Late Activation in Mission Editor")
|
||||
end
|
||||
|
||||
-- Check airbase
|
||||
local airbaseObj = AIRBASE:FindByName(config.airbaseName)
|
||||
if not airbaseObj then
|
||||
env.info("✗ CRITICAL: Airbase not found: " .. config.airbaseName)
|
||||
env.info("SOLUTION: Check airbase name spelling or use a different airbase")
|
||||
return
|
||||
end
|
||||
|
||||
env.info("✓ Airbase found: " .. config.airbaseName)
|
||||
|
||||
local airbaseCoalition = airbaseObj:GetCoalition()
|
||||
env.info("Airbase coalition: " .. (airbaseCoalition == 1 and "Red" or (airbaseCoalition == 2 and "Blue" or "Neutral")))
|
||||
|
||||
-- Create SPAWN object
|
||||
env.info("Creating SPAWN object...")
|
||||
local spawner = SPAWN:New(config.templateName)
|
||||
|
||||
-- Try to spawn
|
||||
env.info("Attempting to spawn aircraft...")
|
||||
local spawnedGroup = nil
|
||||
|
||||
-- Method 1: Air spawn
|
||||
local airbaseCoord = airbaseObj:GetCoordinate()
|
||||
local spawnCoord = airbaseCoord:Translate(2000, math.random(0, 360)):SetAltitude(config.altitude * 0.3048)
|
||||
|
||||
env.info("Trying air spawn at " .. config.altitude .. "ft...")
|
||||
spawnedGroup = spawner:SpawnFromCoordinate(spawnCoord, nil, SPAWN.Takeoff.Air)
|
||||
|
||||
if not spawnedGroup then
|
||||
env.info("Air spawn failed, trying hot start at airbase...")
|
||||
spawnedGroup = spawner:SpawnAtAirbase(airbaseObj, SPAWN.Takeoff.Hot)
|
||||
end
|
||||
|
||||
if not spawnedGroup then
|
||||
env.info("Hot start failed, trying cold start at airbase...")
|
||||
spawnedGroup = spawner:SpawnAtAirbase(airbaseObj, SPAWN.Takeoff.Cold)
|
||||
end
|
||||
|
||||
if spawnedGroup then
|
||||
env.info("✓ SUCCESS: Aircraft spawned successfully!")
|
||||
env.info("Spawned group: " .. spawnedGroup:GetName())
|
||||
|
||||
-- Set basic CAP task after a delay
|
||||
SCHEDULER:New(nil, function()
|
||||
if spawnedGroup and spawnedGroup:IsAlive() then
|
||||
env.info("Setting up basic CAP task...")
|
||||
|
||||
local currentCoord = spawnedGroup:GetCoordinate()
|
||||
if currentCoord then
|
||||
env.info("Aircraft position: " .. currentCoord:ToStringLLDMS())
|
||||
|
||||
-- Clear tasks and set basic patrol
|
||||
spawnedGroup:ClearTasks()
|
||||
spawnedGroup:OptionROEOpenFire()
|
||||
|
||||
-- Simple patrol task
|
||||
local patrolCoord = currentCoord:Translate(10000, math.random(0, 360)):SetAltitude(config.altitude * 0.3048)
|
||||
|
||||
local patrolTask = {
|
||||
id = 'Orbit',
|
||||
params = {
|
||||
pattern = 'Circle',
|
||||
point = {x = patrolCoord.x, y = patrolCoord.z},
|
||||
radius = 5000,
|
||||
altitude = config.altitude * 0.3048,
|
||||
speed = config.speed * 0.514444,
|
||||
}
|
||||
}
|
||||
spawnedGroup:PushTask(patrolTask, 1)
|
||||
|
||||
env.info("✓ Basic CAP task assigned")
|
||||
|
||||
-- Clean up after 10 minutes
|
||||
SCHEDULER:New(nil, function()
|
||||
if spawnedGroup and spawnedGroup:IsAlive() then
|
||||
env.info("Test complete - cleaning up spawned aircraft")
|
||||
spawnedGroup:Destroy()
|
||||
end
|
||||
end, {}, 600) -- 10 minutes
|
||||
|
||||
else
|
||||
env.info("⚠ Could not get aircraft coordinate")
|
||||
end
|
||||
else
|
||||
env.info("⚠ Aircraft not alive for task assignment")
|
||||
end
|
||||
end, {}, 5) -- 5 second delay
|
||||
|
||||
else
|
||||
env.info("✗ CRITICAL: All spawn methods failed!")
|
||||
env.info("DIAGNOSIS: There may be an issue with the template or MOOSE setup")
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then
|
||||
env.info("✗ CRITICAL ERROR in manual launch: " .. tostring(errorMsg))
|
||||
end
|
||||
end
|
||||
|
||||
-- Attempt the manual launch
|
||||
manualLaunch(testConfig)
|
||||
|
||||
end, {}, 5) -- Wait 5 seconds for MOOSE initialization
|
||||
|
||||
env.info("Manual launch test scheduled - check log in 5 seconds")
|
||||
100
DCS_Kola/Operation_Polar_Shield/TADC_Verification.lua
Normal file
100
DCS_Kola/Operation_Polar_Shield/TADC_Verification.lua
Normal file
@ -0,0 +1,100 @@
|
||||
-- TADC Verification Script
|
||||
-- This script provides a simple way to verify that all TADC components are properly configured
|
||||
-- Run this in DCS to check for missing functions, configuration errors, etc.
|
||||
|
||||
-- Verification function to check if all required elements exist
|
||||
local function verifyTADC()
|
||||
local errors = {}
|
||||
local warnings = {}
|
||||
|
||||
-- Check if GCI_Config exists and has required values
|
||||
if not GCI_Config then
|
||||
table.insert(errors, "GCI_Config not found")
|
||||
else
|
||||
local requiredConfig = {
|
||||
"threatRatio", "maxSimultaneousCAP", "useEWRDetection", "ewrDetectionRadius",
|
||||
"mainLoopInterval", "mainLoopDelay", "squadronCooldown", "supplyMode",
|
||||
"defaultSquadronSize", "reservePercent", "responseDelay", "capSetupDelay",
|
||||
"capOrbitRadius", "capEngagementRange", "capZoneConstraint",
|
||||
"statusReportInterval", "engagementUpdateInterval", "fighterVsFighter",
|
||||
"fighterVsBomber", "fighterVsHelicopter", "threatTimeout", "debugLevel"
|
||||
}
|
||||
|
||||
for _, configKey in pairs(requiredConfig) do
|
||||
if GCI_Config[configKey] == nil then
|
||||
table.insert(errors, "Missing config: GCI_Config." .. configKey)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if TADC data structure exists
|
||||
if not TADC then
|
||||
table.insert(errors, "TADC data structure not found")
|
||||
else
|
||||
local requiredTADC = {"squadrons", "activeCAPs", "threats", "missions"}
|
||||
for _, key in pairs(requiredTADC) do
|
||||
if TADC[key] == nil then
|
||||
table.insert(errors, "Missing TADC." .. key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if zones exist
|
||||
if not CCCPBorderZone then
|
||||
table.insert(errors, "CCCPBorderZone not found")
|
||||
end
|
||||
if not HeloBorderZone then
|
||||
table.insert(errors, "HeloBorderZone not found")
|
||||
end
|
||||
|
||||
-- Check if main functions exist
|
||||
local requiredFunctions = {
|
||||
"TADC_Log", "validateConfiguration", "mainTADCLoop", "simpleDetectThreats",
|
||||
"launchCAP", "launchInterceptMission", "maintainPersistentCAP"
|
||||
}
|
||||
|
||||
for _, funcName in pairs(requiredFunctions) do
|
||||
if not _G[funcName] then
|
||||
table.insert(errors, "Missing function: " .. funcName)
|
||||
end
|
||||
end
|
||||
|
||||
-- Count squadrons
|
||||
local squadronCount = 0
|
||||
if TADC and TADC.squadrons then
|
||||
for _ in pairs(TADC.squadrons) do
|
||||
squadronCount = squadronCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Print results
|
||||
env.info("=== TADC VERIFICATION RESULTS ===")
|
||||
|
||||
if #errors > 0 then
|
||||
env.error("VERIFICATION FAILED - " .. #errors .. " errors found:")
|
||||
for _, error in pairs(errors) do
|
||||
env.error(" ❌ " .. error)
|
||||
end
|
||||
else
|
||||
env.info("✅ All critical components verified successfully!")
|
||||
end
|
||||
|
||||
if #warnings > 0 then
|
||||
env.warning("Warnings found:")
|
||||
for _, warning in pairs(warnings) do
|
||||
env.warning(" ⚠️ " .. warning)
|
||||
end
|
||||
end
|
||||
|
||||
env.info("📊 Squadron count: " .. squadronCount)
|
||||
env.info("📊 Config debug level: " .. (GCI_Config and GCI_Config.debugLevel or "N/A"))
|
||||
env.info("=== VERIFICATION COMPLETE ===")
|
||||
|
||||
return #errors == 0
|
||||
end
|
||||
|
||||
-- Run verification after a short delay to ensure everything is loaded
|
||||
SCHEDULER:New(nil, function()
|
||||
env.info("Starting TADC verification...")
|
||||
verifyTADC()
|
||||
end, {}, 10)
|
||||
Loading…
x
Reference in New Issue
Block a user