Fixed timing issues at mission start that was causing Kilpyavr to go into a captured state. Cleaned up logging functions. Fixed capture zone unit counting error. Other cool stuff. :)

This commit is contained in:
iTracerFacer 2025-10-20 11:47:03 -05:00
parent 4b18037c32
commit 37b7e34c8d
7 changed files with 1451 additions and 109 deletions

View File

@ -0,0 +1,155 @@
# Dual Coalition Zone Capture - Full Analysis & Changes
## Summary
The script has been refactored to provide **complete parity between RED and BLUE coalitions**. Both sides now have equal access to all features, information, and victory conditions.
---
## ✅ Changes Made for Full Dual-Coalition Support
### 1. **Tactical Markers** (Lines ~390-440)
**BEFORE:** Only BLUE coalition received tactical markers
**AFTER:** Both RED and BLUE receive separate tactical markers
- Each coalition sees enemy unit positions (when ≤10 units)
- Markers are coalition-specific and read-only
- Uses `TacticalMarkerID_BLUE` and `TacticalMarkerID_RED` for tracking
### 2. **Victory Conditions** (Lines ~490-600)
**BEFORE:** Only checked for BLUE victory
**AFTER:** Both coalitions can win
- BLUE Victory: All zones captured → "BLUE_VICTORY" flag set
- RED Victory: All zones captured → "RED_VICTORY" flag set
- Each side gets appropriate celebration effects (smoke colors, flares)
- Proper victory/defeat messages for both sides
### 3. **Zone Status Reports** (Lines ~710-740)
**BEFORE:** Only BLUE received status broadcasts
**AFTER:** Both coalitions receive status reports
- Each coalition sees their specific victory progress percentage
- Same zone ownership data, customized messaging per coalition
### 4. **Victory Progress Monitoring** (Lines ~745-780)
**BEFORE:** Only warned BLUE when approaching victory
**AFTER:** Both sides get symmetric warnings
- BLUE approaching victory (80%+) → BLUE gets encouragement, RED gets warning
- RED approaching victory (80%+) → RED gets encouragement, BLUE gets warning
### 5. **F10 Radio Menu Commands** (Lines ~840-900)
**BEFORE:** Only BLUE had F10 menu access
**AFTER:** Both coalitions have identical F10 menus
- "Get Zone Status Report" - Shows current zone ownership
- "Check Victory Progress" - Shows their specific progress percentage
- "Refresh Zone Colors" - Forces zone border redraw
### 6. **Zone Color Refresh Messages** (Line ~835)
**BEFORE:** Only BLUE notified when colors refreshed
**AFTER:** Both coalitions receive confirmation message
### 7. **Mission Definitions** (Lines 52-88)
**BEFORE:** Only BLUE mission defined
**AFTER:** Both coalitions have missions
- BLUE: "Capture the Airfields" (offensive mission)
- RED: "Defend the Motherland" (defensive mission)
---
## 🎯 Features Now Available to BOTH Coalitions
| Feature | BLUE | RED |
|---------|------|-----|
| Mission Objectives | ✅ | ✅ |
| Tactical Markers (enemy positions) | ✅ | ✅ |
| Zone Status Reports | ✅ | ✅ |
| Victory Progress Tracking | ✅ | ✅ |
| Victory Conditions | ✅ | ✅ |
| F10 Menu Commands | ✅ | ✅ |
| Zone Color Indicators | ✅ | ✅ |
| Capture/Attack/Guard Messages | ✅ | ✅ |
---
## 🔧 Configuration
Mission makers can now set up asymmetric scenarios by configuring the `ZONE_CONFIG` table:
```lua
local ZONE_CONFIG = {
RED = {
"Kilpyavr",
"Severomorsk-1",
-- ... more zones
},
BLUE = {
"Banak", -- Example: BLUE starting zone
"Kirkenes"
},
NEUTRAL = {
"Contested Valley" -- Starts empty
}
}
```
---
## 🎮 Gameplay Impact
### Balanced Competition
- Both sides can now win by capturing all zones
- Victory celebrations are coalition-specific (blue/red smoke & flares)
- Mission end triggers coalition-specific flags
### Equal Information Access
- Both coalitions see enemy positions in contested zones
- Both receive periodic status updates
- Both have access to F10 menu commands
### Symmetric Design
- All event handlers work equally for both sides
- Messages are dynamically generated based on zone ownership
- No hardcoded coalition bias anywhere in the code
---
## 📝 Technical Notes
### Global Variables Used
- `US_CC` - BLUE coalition command center
- `RU_CC` - RED coalition command center
- Both must be defined before loading this script
### User Flags Set on Victory
- `BLUE_VICTORY` = 1 when BLUE wins
- `RED_VICTORY` = 1 when RED wins
### Storage Structure
- `zoneCaptureObjects[]` - Array of zone capture objects
- `zoneNames[]` - Array of zone names
- `zoneMetadata{}` - Dictionary with coalition info
- All zones accessible via table iteration (no global zone variables)
---
## 🚀 Migration from Old Script
If migrating from `Moose_CaptureZones.lua`:
1. **Update zone configuration** - Move zone names to `ZONE_CONFIG` table
2. **Remove manual zone creation** - The loop handles it now
3. **No code changes needed** for existing trigger zones in mission editor
4. **F10 menus now available** to RED players automatically
---
## ✨ Benefits of Refactoring
1. **Easy to configure** - Simple table instead of repetitive code
2. **Coalition agnostic** - Works equally for RED/BLUE/NEUTRAL
3. **Maintainable** - Zone logic centralized in loops
4. **Extensible** - Easy to add new features for both sides
5. **Balanced** - True dual-coalition gameplay
---
*Last Updated: Analysis completed with full dual-coalition parity*

View File

@ -25,7 +25,7 @@ end
-- Logging configuration: toggle logging behavior for this module
-- Set `CAPTURE_ZONE_LOGGING.enabled = false` to silence module logs
if not CAPTURE_ZONE_LOGGING then
CAPTURE_ZONE_LOGGING = { enabled = false, prefix = "[CAPTURE Module]" }
CAPTURE_ZONE_LOGGING = { enabled = true, prefix = "[CAPTURE Module]" }
end
local function log(message, detailed)
@ -155,14 +155,13 @@ local function GetZoneForceStrengths(ZoneCapture)
local blueCount = 0
local neutralCount = 0
-- Use MOOSE's optimized zone scanning instead of manual distance checks
local success, scannedUnits = pcall(function()
return zone:GetScannedUnits()
end)
-- Get all units in the zone using MOOSE's zone scanning
local unitsInZone = SET_UNIT:New()
:FilterZones({zone})
:FilterOnce()
if success and scannedUnits then
-- Use MOOSE's built-in scanned units (much faster)
for _, unit in pairs(scannedUnits) do
if unitsInZone then
unitsInZone:ForEachUnit(function(unit)
if unit and unit:IsAlive() then
local unitCoalition = unit:GetCoalition()
if unitCoalition == coalition.side.RED then
@ -173,32 +172,7 @@ local function GetZoneForceStrengths(ZoneCapture)
neutralCount = neutralCount + 1
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)
end
log(string.format("[TACTICAL] Zone %s scan result: R:%d B:%d N:%d",
@ -216,69 +190,81 @@ local function GetRedUnitMGRSCoords(ZoneCapture)
if not zone then return {} end
local coords = {}
local units = nil
-- Optimized: Try MOOSE's built-in zone scanning first (fastest method)
local success1 = pcall(function()
units = zone:GetScannedUnits()
end)
-- Get all units in the zone using MOOSE's zone scanning
local unitsInZone = SET_UNIT:New()
:FilterZones({zone})
:FilterOnce()
-- Fallback: Use coordinate-based scanning (much faster than SET_UNIT filtering)
if not success1 or not units then
local coord = zone:GetCoordinate()
local radius = zone:GetRadius() or 1000
local success2 = pcall(function()
units = coord:ScanUnits(radius)
end)
-- Last resort: Manual zone check with cached unit set
if not success2 or not units then
InitializeCachedUnitSet()
units = {}
if CachedUnitSet then
CachedUnitSet:ForEachUnit(function(unit)
if unit and unit:IsAlive() and unit:IsInZone(zone) then
units[unit:GetName()] = unit
end
end)
end
end
end
local totalUnits = 0
local redUnits = 0
local unitsWithCoords = 0
-- Extract RED unit coordinates with optimized error handling
if units then
for unitName, unit in pairs(units) do
-- Streamlined nil checking
if unit and type(unit) == "table" then
local success, isAlive = pcall(function() return unit:IsAlive() end)
if unitsInZone then
unitsInZone:ForEachUnit(function(unit)
totalUnits = totalUnits + 1
if unit and unit:IsAlive() then
local unitCoalition = unit:GetCoalition()
if success and isAlive then
local success_coalition, coalition_side = pcall(function() return unit:GetCoalition() end)
-- Only process RED units
if unitCoalition == coalition.side.RED then
redUnits = redUnits + 1
local coord = unit:GetCoordinate()
if success_coalition and coalition_side == coalition.side.RED then
local success_coord, coord = pcall(function() return unit:GetCoordinate() end)
if coord then
-- Try multiple methods to get coordinates
local mgrs = nil
local success_mgrs = false
if success_coord and coord then
local success_mgrs, mgrs = pcall(function()
return coord:ToStringMGRS(5) -- 5-digit precision
-- Method 1: Try ToStringMGRS
success_mgrs, mgrs = pcall(function()
return coord:ToStringMGRS(5)
end)
-- Method 2: Try ToStringMGRS without precision parameter
if not success_mgrs or not mgrs then
success_mgrs, mgrs = pcall(function()
return coord:ToStringMGRS()
end)
if success_mgrs and mgrs then
local success_type, unitType = pcall(function() return unit:GetTypeName() end)
table.insert(coords, {
name = unit:GetName(),
type = success_type and unitType or "Unknown",
mgrs = mgrs
})
end
end
-- Method 3: Try ToMGRS
if not success_mgrs or not mgrs then
success_mgrs, mgrs = pcall(function()
return coord:ToMGRS()
end)
end
-- Method 4: Fallback to Lat/Long
if not success_mgrs or not mgrs then
success_mgrs, mgrs = pcall(function()
local lat, lon = coord:GetLLDDM()
return string.format("N%s E%s", lat, lon)
end)
end
if success_mgrs and mgrs then
unitsWithCoords = unitsWithCoords + 1
local unitType = unit:GetTypeName() or "Unknown"
table.insert(coords, {
name = unit:GetName(),
type = unitType,
mgrs = mgrs
})
else
log(string.format("[TACTICAL DEBUG] All coordinate methods failed for unit %s", unit:GetName() or "unknown"))
end
else
log(string.format("[TACTICAL DEBUG] No coordinate for unit %s", unit:GetName() or "unknown"))
end
end
end
end
end)
end
log(string.format("[TACTICAL DEBUG] %s - Total units scanned: %d, RED units: %d, units with MGRS: %d",
ZoneCapture:GetZoneName(), totalUnits, redUnits, unitsWithCoords))
log(string.format("[TACTICAL] Found %d RED units with coordinates in %s",
#coords, ZoneCapture:GetZoneName()))
@ -303,21 +289,34 @@ local function CreateTacticalInfoMarker(ZoneCapture)
-- Add MGRS coordinates if RED forces <= 10
if forces.red > 0 and forces.red <= 10 then
local redCoords = GetRedUnitMGRSCoords(ZoneCapture)
log(string.format("[TACTICAL DEBUG] Building marker text for %d RED units", #redCoords))
if #redCoords > 0 then
tacticalText = tacticalText .. "\n\nRED UNITS:"
tacticalText = tacticalText .. "\nTGTS:"
for i, unit in ipairs(redCoords) do
if i <= 6 then -- Limit to 6 entries to avoid text overflow
if i <= 10 then -- Show up to 10 units (the threshold)
-- 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)
-- Clean up MGRS string - remove "MGRS " prefix and compress spacing
local cleanMgrs = unit.mgrs:gsub("^MGRS%s+", ""):gsub("%s+", " ")
-- Ultra-compact: comma-separated on same line
if i == 1 then
tacticalText = tacticalText .. string.format(" %s@%s", shortType, cleanMgrs)
else
tacticalText = tacticalText .. string.format(", %s@%s", shortType, cleanMgrs)
end
log(string.format("[TACTICAL DEBUG] Added unit %d: %s at %s", i, shortType, cleanMgrs))
end
end
if #redCoords > 6 then
tacticalText = tacticalText .. string.format("\n+%d more", #redCoords - 6)
if #redCoords > 10 then
tacticalText = tacticalText .. string.format(" (+%d)", #redCoords - 10)
end
end
end
-- Debug: Log the complete marker text that will be displayed
log(string.format("[TACTICAL DEBUG] Complete marker text for %s:\n%s", zoneName, tacticalText))
log(string.format("[TACTICAL DEBUG] Marker text length: %d characters", string.len(tacticalText)))
-- Create tactical marker offset from zone center
local coord = zone:GetCoordinate()
if coord then

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,8 @@ MAX_RU_IFV_Technicals = 45
MAX_RU_SA08 = 15
MAX_RU_SA19 = 15
MAX_RU_SA15 = 30 -- This is a group of 3 . Sa15 + Shilka + Ammo truck.
MAX_RU_SA2 = 120 -- Each group has 15 units, so 120 = 8 groups of 15.
MAX_RU_SA6 = 88 -- Each group has 11 units, so 88 = 8 groups of 11.
MIN_RU_INTERCEPTORS = 1 -- Each group has 2 units, so 2 = 1 group of 2. This is the minimum number of interceptors that will always be present.
MAX_RU_INTERCEPTORS = 500 -- This the total number of interceptors that can be spawned. The script will maintain at least the minimum number above.
@ -286,6 +288,18 @@ RandomSpawns_RU_SA15 = SPAWN:New( "RU_SA-15" )
:InitRandomizeZones( RandomSpawnZoneTable )
:SpawnScheduled( .1, .5 )
env.info("Spawning SA-02 SAMs...")
RandomSpawns_RU_SA02 = SPAWN:New( "RU_SA2" )
:InitLimit( MAX_RU_SA2, MAX_RU_SA2 )
:InitRandomizeZones( RandomSpawnZoneTable )
:SpawnScheduled( .1, .5 )
env.info("Spawning SA6 SAMs...")
RandomSpawns_RU_SA6 = SPAWN:New( "RU_SA6" )
:InitLimit( MAX_RU_SA6, MAX_RU_SA6 )
:InitRandomizeZones( RandomSpawnZoneTable )
:SpawnScheduled( .1, .5 )
--[[
RU_INTERCEPTOR_SPAWN = SPAWN:New("RU_INTERCEPT-1")
:InitLimit( MIN_RU_INTERCEPTORS, MAX_RU_INTERCEPTORS )

View File

@ -314,8 +314,6 @@ for _, squadron in pairs(BLUE_SQUADRON_CONFIG) do
end
end
-- Squadron resource summary generator
local function getSquadronResourceSummary(coalitionSide)
@ -1883,7 +1881,7 @@ local function checkAirbaseStatus()
redUsableCount = redUsableCount + 1
end
log(statusPrefix .. " " .. squadron.airbaseName .. " - " .. fullStatus)
log(statusPrefix .. " " .. squadron.displayName .. " (" .. squadron.airbaseName .. ") - " .. fullStatus)
end
log("RED Status: " .. redUsableCount .. "/" .. #RED_SQUADRON_CONFIG .. " airbases operational")
end
@ -1949,8 +1947,7 @@ local function checkAirbaseStatus()
blueUsableCount = blueUsableCount + 1
end
log(statusPrefix .. " " .. squadron.airbaseName .. " - " .. fullStatus)
end
log(statusPrefix .. " " .. squadron.displayName .. " (" .. squadron.airbaseName .. ") - " .. fullStatus)
end
log("BLUE Status: " .. blueUsableCount .. "/" .. #BLUE_SQUADRON_CONFIG .. " airbases operational")
end
@ -2168,7 +2165,7 @@ local function initializeSystem()
SCHEDULER:New(nil, detectThreats, {}, 5, TADC_SETTINGS.checkInterval)
SCHEDULER:New(nil, monitorInterceptors, {}, 10, TADC_SETTINGS.monitorInterval)
SCHEDULER:New(nil, checkAirbaseStatus, {}, 30, TADC_SETTINGS.statusReportInterval)
SCHEDULER:New(nil, updateSquadronStates, {}, 15, 30) -- Update squadron states every 30 seconds
SCHEDULER:New(nil, updateSquadronStates, {}, 60, 30) -- Update squadron states every 30 seconds (60 sec initial delay to allow DCS airbase coalition to stabilize)
SCHEDULER:New(nil, cleanupOldDeliveries, {}, 60, 3600) -- Cleanup old delivery records every hour
-- Start periodic squadron summary broadcast

View File

@ -79,8 +79,36 @@ RED_SQUADRON_CONFIG = {
-- ADD YOUR RED SQUADRONS HERE
{
templateName = "FIGHTER_SWEEP_RED_Kilpyavr", -- Change to your RED template name
displayName = "Kilpyavr CAP", -- Change to your preferred name
templateName = "FIGHTER_SWEEP_RED_Kilpyavr-MiG29A", -- Change to your RED template name
displayName = "Kilpyavr CAP MiG-29A", -- Change to your preferred name
airbaseName = "Kilpyavr", -- Change to your RED airbase
aircraft = 12, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 15400, -- Patrol altitude (feet)
speed = 312, -- Patrol speed (knots)
patrolTime = 32, -- Time on station (minutes)
type = "FIGHTER",
-- Zone-based Areas of Responsibility (optional - leave nil for global response)
primaryZone = "RED BORDER", -- Main responsibility area (zone name from mission editor)
secondaryZone = nil, -- Secondary coverage area (zone name)
tertiaryZone = nil, -- Emergency/fallback zone (zone name)
-- Zone behavior settings (optional - uses defaults if not specified)
zoneConfig = {
primaryResponse = 1.0, -- Intercept ratio multiplier in primary zone
secondaryResponse = 0.6, -- Intercept ratio multiplier in secondary zone
tertiaryResponse = 1.4, -- Intercept ratio multiplier in tertiary zone
maxRange = 200, -- Maximum engagement range from airbase (nm)
enableFallback = false, -- Auto-switch to tertiary when base threatened
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
{
templateName = "FIGHTER_SWEEP_RED_Kilpyavr-MiG29S", -- Change to your RED template name
displayName = "Kilpyavr CAP MiG-29S", -- Change to your preferred name
airbaseName = "Kilpyavr", -- Change to your RED airbase
aircraft = 12, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
@ -107,8 +135,36 @@ RED_SQUADRON_CONFIG = {
},
{
templateName = "FIGHTER_SWEEP_RED_Severomorsk-1", -- Change to your RED template name
displayName = "Severomorsk-1 CAP", -- Change to your preferred name
templateName = "FIGHTER_SWEEP_RED_Severomorsk-1-MiG23", -- Change to your RED template name
displayName = "Severomorsk-1 CAP MiG-23", -- Change to your preferred name
airbaseName = "Severomorsk-1", -- Change to your RED airbase
aircraft = 10, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 18800, -- Patrol altitude (feet)
speed = 420, -- Patrol speed (knots)
patrolTime = 23, -- Time on station (minutes)
type = "FIGHTER",
-- Zone-based Areas of Responsibility (optional - leave nil for global response)
primaryZone = "RED BORDER", -- Main responsibility area (zone name from mission editor)
secondaryZone = nil, -- Secondary coverage area (zone name)
tertiaryZone = nil, -- Emergency/fallback zone (zone name)
-- Zone behavior settings (optional - uses defaults if not specified)
zoneConfig = {
primaryResponse = 1.0, -- Intercept ratio multiplier in primary zone
secondaryResponse = 0.6, -- Intercept ratio multiplier in secondary zone
tertiaryResponse = 1.4, -- Intercept ratio multiplier in tertiary zone
maxRange = 200, -- Maximum engagement range from airbase (nm)
enableFallback = false, -- Auto-switch to tertiary when base threatened
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
{
templateName = "FIGHTER_SWEEP_RED_Severomorsk-1-MiG25", -- Change to your RED template name
displayName = "Severomorsk-1 CAP MiG-25", -- Change to your preferred name
airbaseName = "Severomorsk-1", -- Change to your RED airbase
aircraft = 10, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
@ -135,8 +191,36 @@ RED_SQUADRON_CONFIG = {
},
{
templateName = "FIGHTER_SWEEP_RED_Severomorsk-3", -- Change to your RED template name
displayName = "Severomorsk-3 CAP", -- Change to your preferred name
templateName = "FIGHTER_SWEEP_RED_Severomorsk-3-SU27", -- Change to your RED template name
displayName = "Severomorsk-3 CAP SU-27", -- Change to your preferred name
airbaseName = "Severomorsk-3", -- Change to your RED airbase
aircraft = 15, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 26700, -- Patrol altitude (feet)
speed = 335, -- Patrol speed (knots)
patrolTime = 28, -- Time on station (minutes)
type = "FIGHTER",
-- Zone-based Areas of Responsibility (optional - leave nil for global response)
primaryZone = "RED BORDER", -- Main responsibility area (zone name from mission editor)
secondaryZone = nil, -- Secondary coverage area (zone name)
tertiaryZone = nil, -- Emergency/fallback zone (zone name)
-- Zone behavior settings (optional - uses defaults if not specified)
zoneConfig = {
primaryResponse = 1.0, -- Intercept ratio multiplier in primary zone
secondaryResponse = 0.6, -- Intercept ratio multiplier in secondary zone
tertiaryResponse = 1.4, -- Intercept ratio multiplier in tertiary zone
maxRange = 200, -- Maximum engagement range from airbase (nm)
enableFallback = false, -- Auto-switch to tertiary when base threatened
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
{
templateName = "FIGHTER_SWEEP_RED_Severomorsk-3-MiG-21", -- Change to your RED template name
displayName = "Severomorsk-3 CAP MiG-21", -- Change to your preferred name
airbaseName = "Severomorsk-3", -- Change to your RED airbase
aircraft = 15, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
@ -163,8 +247,36 @@ RED_SQUADRON_CONFIG = {
},
{
templateName = "FIGHTER_SWEEP_RED_Murmansk", -- Change to your RED template name
displayName = "Murmansk CAP", -- Change to your preferred name
templateName = "FIGHTER_SWEEP_RED_Murmansk-JF17", -- Change to your RED template name
displayName = "Murmansk CAP JF-17", -- Change to your preferred name
airbaseName = "Murmansk International", -- Change to your RED airbase
aircraft = 8, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 22100, -- Patrol altitude (feet)
speed = 390, -- Patrol speed (knots)
patrolTime = 20, -- Time on station (minutes)
type = "FIGHTER",
-- Zone-based Areas of Responsibility (optional - leave nil for global response)
primaryZone = "RED BORDER", -- Main responsibility area (zone name from mission editor)
secondaryZone = nil, -- Secondary coverage area (zone name)
tertiaryZone = nil, -- Emergency/fallback zone (zone name)
-- Zone behavior settings (optional - uses defaults if not specified)
zoneConfig = {
primaryResponse = 1.0, -- Intercept ratio multiplier in primary zone
secondaryResponse = 0.6, -- Intercept ratio multiplier in secondary zone
tertiaryResponse = 1.4, -- Intercept ratio multiplier in tertiary zone
maxRange = 200, -- Maximum engagement range from airbase (nm)
enableFallback = false, -- Auto-switch to tertiary when base threatened
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
{
templateName = "FIGHTER_SWEEP_RED_Murmansk-MiG29A", -- Change to your RED template name
displayName = "Murmansk CAP MiG-29A", -- Change to your preferred name
airbaseName = "Murmansk International", -- Change to your RED airbase
aircraft = 8, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
@ -191,12 +303,12 @@ RED_SQUADRON_CONFIG = {
},
{
templateName = "FIGHTER_SWEEP_RED_Monchegorsk", -- Change to your RED template name
displayName = "Monchegorsk CAP", -- Change to your preferred name
templateName = "FIGHTER_SWEEP_RED_Monchegorsk-F4", -- Change to your RED template name
displayName = "Monchegorsk CAP F-4", -- Change to your preferred name
airbaseName = "Monchegorsk", -- Change to your RED airbase
aircraft = 16, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 30000, -- Patrol altitude (feet)
altitude = 12000, -- Patrol altitude (feet)
speed = 305, -- Patrol speed (knots)
patrolTime = 35, -- Time on station (minutes)
type = "FIGHTER",
@ -216,11 +328,67 @@ RED_SQUADRON_CONFIG = {
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
},
{
templateName = "FIGHTER_SWEEP_RED_Monchegorsk-F5", -- Change to your RED template name
displayName = "Monchegorsk CAP F-5", -- Change to your preferred name
airbaseName = "Monchegorsk", -- Change to your RED airbase
aircraft = 16, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 15000, -- Patrol altitude (feet)
speed = 305, -- Patrol speed (knots)
patrolTime = 35, -- Time on station (minutes)
type = "FIGHTER",
-- Zone-based Areas of Responsibility (optional - leave nil for global response)
primaryZone = "RED BORDER", -- Main responsibility area (zone name from mission editor)
secondaryZone = nil, -- Secondary coverage area (zone name)
tertiaryZone = nil, -- Emergency/fallback zone (zone name)
-- Zone behavior settings (optional - uses defaults if not specified)
zoneConfig = {
primaryResponse = 1.0, -- Intercept ratio multiplier in primary zone
secondaryResponse = 0.6, -- Intercept ratio multiplier in secondary zone
tertiaryResponse = 1.4, -- Intercept ratio multiplier in tertiary zone
maxRange = 200, -- Maximum engagement range from airbase (nm)
enableFallback = false, -- Auto-switch to tertiary when base threatened
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
{
templateName = "FIGHTER_SWEEP_RED_Olenya", -- Change to your RED template name
displayName = "Olenya CAP", -- Change to your preferred name
templateName = "FIGHTER_SWEEP_RED_Olenya-MiG21", -- Change to your RED template name
displayName = "Olenya CAP MiG-21", -- Change to your preferred name
airbaseName = "Olenya", -- Change to your RED airbase
aircraft = 12, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE
altitude = 17800, -- Patrol altitude (feet)
speed = 445, -- Patrol speed (knots)
patrolTime = 27, -- Time on station (minutes)
type = "FIGHTER",
-- Zone-based Areas of Responsibility (optional - leave nil for global response)
primaryZone = "RED BORDER", -- Main responsibility area (zone name from mission editor)
secondaryZone = nil, -- Secondary coverage area (zone name)
tertiaryZone = nil, -- Emergency/fallback zone (zone name)
-- Zone behavior settings (optional - uses defaults if not specified)
zoneConfig = {
primaryResponse = 1.0, -- Intercept ratio multiplier in primary zone
secondaryResponse = 0.6, -- Intercept ratio multiplier in secondary zone
tertiaryResponse = 1.4, -- Intercept ratio multiplier in tertiary zone
maxRange = 200, -- Maximum engagement range from airbase (nm)
enableFallback = false, -- Auto-switch to tertiary when base threatened
priorityThreshold = 4, -- Min aircraft count for "major threat"
ignoreLowPriority = false, -- Ignore threats below threshold in secondary zones
}
},
{
templateName = "FIGHTER_SWEEP_RED_Olenya-MiG31", -- Change to your RED template name
displayName = "Olenya CAP MiG-31", -- Change to your preferred name
airbaseName = "Olenya", -- Change to your RED airbase
aircraft = 12, -- Adjust aircraft count
skill = AI.Skill.ACE, -- AVERAGE, GOOD, HIGH, EXCELLENT, ACE