mirror of
https://github.com/iTracerFacer/Moose_DualCoalitionZoneCapture.git
synced 2026-08-14 13:18:52 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 431da429db | |||
| 63006a3d99 | |||
| d54a04a212 | |||
| 19e5e17041 | |||
| 0b1e435c5c | |||
| 3a7017a516 | |||
| c5f2352cd5 | |||
| 0e3ba2fbe1 | |||
| 38391e81c5 | |||
| d7229f9aa0 | |||
| 6e802b333f | |||
| afdd777680 | |||
| 505a9663ac | |||
| f02f1b2f73 |
+728
-213
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
|
- **Author**: F99th-TracerFacer
|
||||||
|
- **Discord:** https://discord.gg/NdZ2JuSU (The Fighting 99th Discord Server where I spend most of my time.)
|
||||||
|
|
||||||
Script: Moose_DynamicGroundBattle_Plugin.lua
|
Script: Moose_DynamicGroundBattle_Plugin.lua
|
||||||
Written by: [F99th-TracerFacer]
|
Written by: [F99th-TracerFacer]
|
||||||
Version: 1.0.0
|
Version: 1.0.0
|
||||||
@@ -72,6 +75,8 @@
|
|||||||
- Spawns occur in zones controlled by the appropriate coalition
|
- Spawns occur in zones controlled by the appropriate coalition
|
||||||
- AI tasks units to patrol zones from DualCoalitionZoneCapture's ZONE_CONFIG
|
- AI tasks units to patrol zones from DualCoalitionZoneCapture's ZONE_CONFIG
|
||||||
--]]
|
--]]
|
||||||
|
---@diagnostic disable: undefined-global, lowercase-global
|
||||||
|
-- MOOSE framework globals are defined at runtime by DCS World
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- USER CONFIGURATION SECTION
|
-- USER CONFIGURATION SECTION
|
||||||
@@ -133,63 +138,52 @@ local BLUE_ARMOR_SPAWN_GROUP = "BlueArmorGroup"
|
|||||||
-- AI Tasking Behavior
|
-- AI Tasking Behavior
|
||||||
-- Note: DCS engine can crash with "CREATING PATH MAKES TOO LONG" if units try to path too far
|
-- Note: DCS engine can crash with "CREATING PATH MAKES TOO LONG" if units try to path too far
|
||||||
-- Keep these values conservative to reduce pathfinding load and avoid server crashes
|
-- Keep these values conservative to reduce pathfinding load and avoid server crashes
|
||||||
local MAX_ATTACK_DISTANCE = 22000 -- Maximum distance in meters for attacking enemy zones. Units won't attack zones farther than this. (25km ≈ 13.5nm)
|
-- OPTIMIZATION: Reduced MAX_ATTACK_DISTANCE from 25km to 20km to reduce pathfinding complexity
|
||||||
|
local MAX_ATTACK_DISTANCE = 20000 -- Maximum distance in meters for attacking enemy zones. Units won't attack zones farther than this. (20km ≈ 10.8nm)
|
||||||
local ATTACK_RETRY_COOLDOWN = 1800 -- Seconds a group will wait before re-attempting an attack if no valid enemy zone was found (30 minutes)
|
local ATTACK_RETRY_COOLDOWN = 1800 -- Seconds a group will wait before re-attempting an attack if no valid enemy zone was found (30 minutes)
|
||||||
|
|
||||||
-- Define warehouses for each side
|
-- Define warehouses for each side
|
||||||
local redWarehouses = {
|
local redWarehouses = {
|
||||||
STATIC:FindByName("RedWarehouse1-1"),
|
STATIC:FindByName("RedWarehouse-1-1"),
|
||||||
STATIC:FindByName("RedWarehouse2-1"),
|
STATIC:FindByName("RedWarehouse-2-1"),
|
||||||
STATIC:FindByName("RedWarehouse3-1"),
|
STATIC:FindByName("RedWarehouse-3-1"),
|
||||||
STATIC:FindByName("RedWarehouse4-1"),
|
STATIC:FindByName("RedWarehouse-4-1"),
|
||||||
STATIC:FindByName("RedWarehouse5-1"),
|
STATIC:FindByName("RedWarehouse-5-1"),
|
||||||
STATIC:FindByName("RedWarehouse6-1"),
|
STATIC:FindByName("RedWarehouse-6-1"),
|
||||||
STATIC:FindByName("RedWarehouse7-1"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local blueWarehouses = {
|
local blueWarehouses = {
|
||||||
STATIC:FindByName("BlueWarehouse1-1"),
|
STATIC:FindByName("BlueWarehouse-1-1"),
|
||||||
STATIC:FindByName("BlueWarehouse2-1"),
|
STATIC:FindByName("BlueWarehouse-2-1"),
|
||||||
STATIC:FindByName("BlueWarehouse3-1"),
|
STATIC:FindByName("BlueWarehouse-3-1"),
|
||||||
STATIC:FindByName("BlueWarehouse4-1"),
|
STATIC:FindByName("BlueWarehouse-4-1"),
|
||||||
STATIC:FindByName("BlueWarehouse5-1"),
|
STATIC:FindByName("BlueWarehouse-5-1"),
|
||||||
STATIC:FindByName("BlueWarehouse6-1"),
|
STATIC:FindByName("BlueWarehouse-6-1"),
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Define unit templates (these groups must exist in mission editor as LATE ACTIVATE)
|
-- Define unit templates (these groups must exist in mission editor as LATE ACTIVATE)
|
||||||
local redInfantryTemplates = {
|
local redInfantryTemplates = {
|
||||||
"RedInfantry1",
|
"RedInfantry1",
|
||||||
"RedInfantry2",
|
"RedInfantry2",
|
||||||
"RedInfantry3",
|
|
||||||
"RedInfantry4",
|
|
||||||
"RedInfantry5",
|
|
||||||
"RedInfantry6"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local redArmorTemplates = {
|
local redArmorTemplates = {
|
||||||
"RedArmor1",
|
"RedArmor1",
|
||||||
"RedArmor2",
|
"RedArmor2",
|
||||||
"RedArmor3",
|
|
||||||
"RedArmor4",
|
|
||||||
"RedArmor5",
|
|
||||||
"RedArmor6"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local blueInfantryTemplates = {
|
local blueInfantryTemplates = {
|
||||||
"BlueInfantry1",
|
"BlueInfantry1",
|
||||||
"BlueInfantry2",
|
"BlueInfantry2",
|
||||||
"BlueInfantry3",
|
|
||||||
"BlueInfantry4",
|
|
||||||
"BlueInfantry5",
|
|
||||||
"BlueInfantry6"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local blueArmorTemplates = {
|
local blueArmorTemplates = {
|
||||||
"BlueArmor1",
|
"BlueArmor1",
|
||||||
"BlueArmor2",
|
"BlueArmor2",
|
||||||
"BlueArmor3",
|
|
||||||
"BlueArmor4",
|
|
||||||
"BlueArmor5"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -198,6 +192,318 @@ local blueArmorTemplates = {
|
|||||||
-- DO NOT EDIT BELOW THIS LINE
|
-- DO NOT EDIT BELOW THIS LINE
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- MULTILINGUAL SUPPORT (uses main script's language settings)
|
||||||
|
-- ==========================================
|
||||||
|
-- This plugin uses the playerLanguages table from Moose_DualCoalitionZoneCapture.lua
|
||||||
|
-- Players set their language preference in the main script's F10 menu
|
||||||
|
|
||||||
|
local DGB_LANGUAGES = {
|
||||||
|
EN = {
|
||||||
|
-- Warehouse messages
|
||||||
|
warehouseStatus = "[Warehouse Status]\nRed warehouses alive: %d Reinforcements: %d%%\nBlue warehouses alive: %d Reinforcements: %d%%\n",
|
||||||
|
reinforcementCapacity = "%s reinforcement capacity: %d%%",
|
||||||
|
warehouseFriendly = "Warehouse: %s\nThis warehouse needs to be protected.\n",
|
||||||
|
warehouseEnemy = "Warehouse: %s\nThis is a primary target as it is directly supplying enemy units.\n",
|
||||||
|
|
||||||
|
-- Menu items
|
||||||
|
menuGroundBattle = "Ground Battle",
|
||||||
|
menuWarehouseStatus = "Check Warehouse Status",
|
||||||
|
menuSystemStats = "Show System Statistics",
|
||||||
|
|
||||||
|
-- System statistics
|
||||||
|
statsTitle = "DYNAMIC GROUND BATTLE - SYSTEM STATUS",
|
||||||
|
statsConfiguration = "【CONFIGURATION】",
|
||||||
|
statsDefendersPerZone = " Defenders per Zone: %d",
|
||||||
|
statsDefenderRotation = " Defender Rotation: %s",
|
||||||
|
statsInfantryMovement = " Infantry Movement: %s",
|
||||||
|
statsTaskReassignment = " Task Reassignment: Every %ds",
|
||||||
|
statsWarehouseMarkers = " Warehouse Markers: %s",
|
||||||
|
statsSpawnLimits = "【SPAWN LIMITS】",
|
||||||
|
statsRedInfantry = " Red Infantry: %d/%d",
|
||||||
|
statsRedArmor = " Red Armor: %d/%d",
|
||||||
|
statsBlueInfantry = " Blue Infantry: %d/%d",
|
||||||
|
statsBlueArmor = " Blue Armor: %d/%d",
|
||||||
|
statsRedCoalition = "【RED COALITION】",
|
||||||
|
statsBlueCoalition = "【BLUE COALITION】",
|
||||||
|
statsWarehouses = " Warehouses: %d/%d (%d%%)",
|
||||||
|
statsActiveUnits = " Active Units: %d (%d inf, %d armor)",
|
||||||
|
statsDefenders = " Defenders: %d | Mobile: %d",
|
||||||
|
statsControlledZones = " Controlled Zones: %d",
|
||||||
|
statsGarrisoned = " - Garrisoned: %d",
|
||||||
|
statsUnderGarrisoned = " - Under-Garrisoned: %d",
|
||||||
|
statsInfantrySpawn = " Infantry Spawn: %ds",
|
||||||
|
statsInfantrySpawnPaused = " Infantry Spawn: PAUSED (no warehouses)",
|
||||||
|
statsArmorSpawn = " Armor Spawn: %ds",
|
||||||
|
statsArmorSpawnPaused = " Armor Spawn: PAUSED (no warehouses)",
|
||||||
|
statsSystemInfo = "【SYSTEM INFO】",
|
||||||
|
statsTotalZones = " Total Zones: %d",
|
||||||
|
statsActiveGarrisons = " Active Garrisons: %d",
|
||||||
|
statsTotalActiveUnits = " Total Active Units: %d",
|
||||||
|
statsTrackedGroups = " Tracked Groups: %d",
|
||||||
|
statsLuaMemory = " Lua Memory: %.1f MB",
|
||||||
|
statsWarningMemory = " ⚠️ WARNING: High memory usage!",
|
||||||
|
statsWarningGroups = " ⚠️ WARNING: High group count!",
|
||||||
|
enabled = "ENABLED",
|
||||||
|
disabled = "DISABLED",
|
||||||
|
},
|
||||||
|
|
||||||
|
DE = {
|
||||||
|
-- Warehouse messages
|
||||||
|
warehouseStatus = "[Lagerstatus]\nRote Lager aktiv: %d Verstärkungen: %d%%\nBlaue Lager aktiv: %d Verstärkungen: %d%%\n",
|
||||||
|
reinforcementCapacity = "%s Verstärkungskapazität: %d%%",
|
||||||
|
warehouseFriendly = "Lager: %s\nDieses Lager muss geschützt werden.\n",
|
||||||
|
warehouseEnemy = "Lager: %s\nDies ist ein Hauptziel, da es feindliche Einheiten direkt versorgt.\n",
|
||||||
|
|
||||||
|
-- Menu items
|
||||||
|
menuGroundBattle = "Bodenkampf",
|
||||||
|
menuWarehouseStatus = "Lagerstatus prüfen",
|
||||||
|
menuSystemStats = "Systemstatistiken anzeigen",
|
||||||
|
|
||||||
|
-- System statistics
|
||||||
|
statsTitle = "DYNAMISCHER BODENKAMPF - SYSTEMSTATUS",
|
||||||
|
statsConfiguration = "【KONFIGURATION】",
|
||||||
|
statsDefendersPerZone = " Verteidiger pro Zone: %d",
|
||||||
|
statsDefenderRotation = " Verteidiger-Rotation: %s",
|
||||||
|
statsInfantryMovement = " Infanterie-Bewegung: %s",
|
||||||
|
statsTaskReassignment = " Aufgabenzuweisung: Alle %ds",
|
||||||
|
statsWarehouseMarkers = " Lagermarkierungen: %s",
|
||||||
|
statsSpawnLimits = "【SPAWN-LIMITS】",
|
||||||
|
statsRedInfantry = " Rote Infanterie: %d/%d",
|
||||||
|
statsRedArmor = " Rote Panzer: %d/%d",
|
||||||
|
statsBlueInfantry = " Blaue Infanterie: %d/%d",
|
||||||
|
statsBlueArmor = " Blaue Panzer: %d/%d",
|
||||||
|
statsRedCoalition = "【ROTE KOALITION】",
|
||||||
|
statsBlueCoalition = "【BLAUE KOALITION】",
|
||||||
|
statsWarehouses = " Lager: %d/%d (%d%%)",
|
||||||
|
statsActiveUnits = " Aktive Einheiten: %d (%d Inf, %d Panzer)",
|
||||||
|
statsDefenders = " Verteidiger: %d | Mobil: %d",
|
||||||
|
statsControlledZones = " Kontrollierte Zonen: %d",
|
||||||
|
statsGarrisoned = " - Besetzt: %d",
|
||||||
|
statsUnderGarrisoned = " - Unterbesetzt: %d",
|
||||||
|
statsInfantrySpawn = " Infanterie-Spawn: %ds",
|
||||||
|
statsInfantrySpawnPaused = " Infanterie-Spawn: PAUSIERT (keine Lager)",
|
||||||
|
statsArmorSpawn = " Panzer-Spawn: %ds",
|
||||||
|
statsArmorSpawnPaused = " Panzer-Spawn: PAUSIERT (keine Lager)",
|
||||||
|
statsSystemInfo = "【SYSTEMINFO】",
|
||||||
|
statsTotalZones = " Zonen gesamt: %d",
|
||||||
|
statsActiveGarrisons = " Aktive Garnisonen: %d",
|
||||||
|
statsTotalActiveUnits = " Aktive Einheiten gesamt: %d",
|
||||||
|
statsTrackedGroups = " Verfolgte Gruppen: %d",
|
||||||
|
statsLuaMemory = " Lua-Speicher: %.1f MB",
|
||||||
|
statsWarningMemory = " ⚠️ WARNUNG: Hoher Speicherverbrauch!",
|
||||||
|
statsWarningGroups = " ⚠️ WARNUNG: Hohe Gruppenanzahl!",
|
||||||
|
enabled = "AKTIVIERT",
|
||||||
|
disabled = "DEAKTIVIERT",
|
||||||
|
},
|
||||||
|
|
||||||
|
FR = {
|
||||||
|
-- Warehouse messages
|
||||||
|
warehouseStatus = "[Statut des entrepôts]\nEntrepôts rouges actifs : %d Renforts : %d%%\nEntrepôts bleus actifs : %d Renforts : %d%%\n",
|
||||||
|
reinforcementCapacity = "Capacité de renfort %s : %d%%",
|
||||||
|
warehouseFriendly = "Entrepôt : %s\nCet entrepôt doit être protégé.\n",
|
||||||
|
warehouseEnemy = "Entrepôt : %s\nC'est une cible prioritaire car il approvisionne directement les unités ennemies.\n",
|
||||||
|
|
||||||
|
-- Menu items
|
||||||
|
menuGroundBattle = "Combat terrestre",
|
||||||
|
menuWarehouseStatus = "Vérifier le statut des entrepôts",
|
||||||
|
menuSystemStats = "Afficher les statistiques système",
|
||||||
|
|
||||||
|
-- System statistics
|
||||||
|
statsTitle = "COMBAT TERRESTRE DYNAMIQUE - ÉTAT DU SYSTÈME",
|
||||||
|
statsConfiguration = "【CONFIGURATION】",
|
||||||
|
statsDefendersPerZone = " Défenseurs par zone : %d",
|
||||||
|
statsDefenderRotation = " Rotation des défenseurs : %s",
|
||||||
|
statsInfantryMovement = " Mouvement infanterie : %s",
|
||||||
|
statsTaskReassignment = " Réaffectation des tâches : Tous les %ds",
|
||||||
|
statsWarehouseMarkers = " Marqueurs d'entrepôt : %s",
|
||||||
|
statsSpawnLimits = "【LIMITES DE SPAWN】",
|
||||||
|
statsRedInfantry = " Infanterie rouge : %d/%d",
|
||||||
|
statsRedArmor = " Blindés rouges : %d/%d",
|
||||||
|
statsBlueInfantry = " Infanterie bleue : %d/%d",
|
||||||
|
statsBlueArmor = " Blindés bleus : %d/%d",
|
||||||
|
statsRedCoalition = "【COALITION ROUGE】",
|
||||||
|
statsBlueCoalition = "【COALITION BLEUE】",
|
||||||
|
statsWarehouses = " Entrepôts : %d/%d (%d%%)",
|
||||||
|
statsActiveUnits = " Unités actives : %d (%d inf, %d blindés)",
|
||||||
|
statsDefenders = " Défenseurs : %d | Mobiles : %d",
|
||||||
|
statsControlledZones = " Zones contrôlées : %d",
|
||||||
|
statsGarrisoned = " - En garnison : %d",
|
||||||
|
statsUnderGarrisoned = " - Sous-garnison : %d",
|
||||||
|
statsInfantrySpawn = " Spawn infanterie : %ds",
|
||||||
|
statsInfantrySpawnPaused = " Spawn infanterie : PAUSE (pas d'entrepôts)",
|
||||||
|
statsArmorSpawn = " Spawn blindés : %ds",
|
||||||
|
statsArmorSpawnPaused = " Spawn blindés : PAUSE (pas d'entrepôts)",
|
||||||
|
statsSystemInfo = "【INFO SYSTÈME】",
|
||||||
|
statsTotalZones = " Zones totales : %d",
|
||||||
|
statsActiveGarrisons = " Garnisons actives : %d",
|
||||||
|
statsTotalActiveUnits = " Unités actives totales : %d",
|
||||||
|
statsTrackedGroups = " Groupes suivis : %d",
|
||||||
|
statsLuaMemory = " Mémoire Lua : %.1f Mo",
|
||||||
|
statsWarningMemory = " ⚠️ ATTENTION : Utilisation mémoire élevée !",
|
||||||
|
statsWarningGroups = " ⚠️ ATTENTION : Nombre de groupes élevé !",
|
||||||
|
enabled = "ACTIVÉ",
|
||||||
|
disabled = "DÉSACTIVÉ",
|
||||||
|
},
|
||||||
|
|
||||||
|
ES = {
|
||||||
|
-- Warehouse messages
|
||||||
|
warehouseStatus = "[Estado de almacenes]\nAlmacenes rojos activos: %d Refuerzos: %d%%\nAlmacenes azules activos: %d Refuerzos: %d%%\n",
|
||||||
|
reinforcementCapacity = "Capacidad de refuerzo %s: %d%%",
|
||||||
|
warehouseFriendly = "Almacén: %s\nEste almacén necesita ser protegido.\n",
|
||||||
|
warehouseEnemy = "Almacén: %s\nEste es un objetivo prioritario ya que está suministrando directamente unidades enemigas.\n",
|
||||||
|
|
||||||
|
-- Menu items
|
||||||
|
menuGroundBattle = "Batalla terrestre",
|
||||||
|
menuWarehouseStatus = "Verificar estado de almacenes",
|
||||||
|
menuSystemStats = "Mostrar estadísticas del sistema",
|
||||||
|
|
||||||
|
-- System statistics
|
||||||
|
statsTitle = "BATALLA TERRESTRE DINÁMICA - ESTADO DEL SISTEMA",
|
||||||
|
statsConfiguration = "【CONFIGURACIÓN】",
|
||||||
|
statsDefendersPerZone = " Defensores por zona: %d",
|
||||||
|
statsDefenderRotation = " Rotación de defensores: %s",
|
||||||
|
statsInfantryMovement = " Movimiento de infantería: %s",
|
||||||
|
statsTaskReassignment = " Reasignación de tareas: Cada %ds",
|
||||||
|
statsWarehouseMarkers = " Marcadores de almacén: %s",
|
||||||
|
statsSpawnLimits = "【LÍMITES DE APARICIÓN】",
|
||||||
|
statsRedInfantry = " Infantería roja: %d/%d",
|
||||||
|
statsRedArmor = " Blindados rojos: %d/%d",
|
||||||
|
statsBlueInfantry = " Infantería azul: %d/%d",
|
||||||
|
statsBlueArmor = " Blindados azules: %d/%d",
|
||||||
|
statsRedCoalition = "【COALICIÓN ROJA】",
|
||||||
|
statsBlueCoalition = "【COALICIÓN AZUL】",
|
||||||
|
statsWarehouses = " Almacenes: %d/%d (%d%%)",
|
||||||
|
statsActiveUnits = " Unidades activas: %d (%d inf, %d blindados)",
|
||||||
|
statsDefenders = " Defensores: %d | Móviles: %d",
|
||||||
|
statsControlledZones = " Zonas controladas: %d",
|
||||||
|
statsGarrisoned = " - Guarnecidas: %d",
|
||||||
|
statsUnderGarrisoned = " - Subguarnecidas: %d",
|
||||||
|
statsInfantrySpawn = " Aparición infantería: %ds",
|
||||||
|
statsInfantrySpawnPaused = " Aparición infantería: PAUSADA (sin almacenes)",
|
||||||
|
statsArmorSpawn = " Aparición blindados: %ds",
|
||||||
|
statsArmorSpawnPaused = " Aparición blindados: PAUSADA (sin almacenes)",
|
||||||
|
statsSystemInfo = "【INFO DEL SISTEMA】",
|
||||||
|
statsTotalZones = " Zonas totales: %d",
|
||||||
|
statsActiveGarrisons = " Guarniciones activas: %d",
|
||||||
|
statsTotalActiveUnits = " Unidades activas totales: %d",
|
||||||
|
statsTrackedGroups = " Grupos rastreados: %d",
|
||||||
|
statsLuaMemory = " Memoria Lua: %.1f MB",
|
||||||
|
statsWarningMemory = " ⚠️ ADVERTENCIA: ¡Uso de memoria elevado!",
|
||||||
|
statsWarningGroups = " ⚠️ ADVERTENCIA: ¡Cantidad de grupos elevada!",
|
||||||
|
enabled = "HABILITADO",
|
||||||
|
disabled = "DESHABILITADO",
|
||||||
|
},
|
||||||
|
|
||||||
|
RU = {
|
||||||
|
-- Warehouse messages
|
||||||
|
warehouseStatus = "[Статус складов]\nКрасные склады активны: %d Подкрепления: %d%%\nСиние склады активны: %d Подкрепления: %d%%\n",
|
||||||
|
reinforcementCapacity = "Мощность подкреплений %s: %d%%",
|
||||||
|
warehouseFriendly = "Склад: %s\nЭтот склад нужно защищать.\n",
|
||||||
|
warehouseEnemy = "Склад: %s\nЭто приоритетная цель, так как она напрямую снабжает вражеские подразделения.\n",
|
||||||
|
|
||||||
|
-- Menu items
|
||||||
|
menuGroundBattle = "Наземный бой",
|
||||||
|
menuWarehouseStatus = "Проверить статус складов",
|
||||||
|
menuSystemStats = "Показать статистику системы",
|
||||||
|
|
||||||
|
-- System statistics
|
||||||
|
statsTitle = "ДИНАМИЧЕСКИЙ НАЗЕМНЫЙ БОЙ - СТАТУС СИСТЕМЫ",
|
||||||
|
statsConfiguration = "【КОНФИГУРАЦИЯ】",
|
||||||
|
statsDefendersPerZone = " Защитников на зону: %d",
|
||||||
|
statsDefenderRotation = " Ротация защитников: %s",
|
||||||
|
statsInfantryMovement = " Движение пехоты: %s",
|
||||||
|
statsTaskReassignment = " Переназначение задач: Каждые %ds",
|
||||||
|
statsWarehouseMarkers = " Маркеры складов: %s",
|
||||||
|
statsSpawnLimits = "【ЛИМИТЫ ПОЯВЛЕНИЯ】",
|
||||||
|
statsRedInfantry = " Красная пехота: %d/%d",
|
||||||
|
statsRedArmor = " Красная бронетехника: %d/%d",
|
||||||
|
statsBlueInfantry = " Синяя пехота: %d/%d",
|
||||||
|
statsBlueArmor = " Синяя бронетехника: %d/%d",
|
||||||
|
statsRedCoalition = "【КРАСНАЯ КОАЛИЦИЯ】",
|
||||||
|
statsBlueCoalition = "【СИНЯЯ КОАЛИЦИЯ】",
|
||||||
|
statsWarehouses = " Склады: %d/%d (%d%%)",
|
||||||
|
statsActiveUnits = " Активные подразделения: %d (%d пех, %d брон)",
|
||||||
|
statsDefenders = " Защитники: %d | Мобильные: %d",
|
||||||
|
statsControlledZones = " Контролируемые зоны: %d",
|
||||||
|
statsGarrisoned = " - С гарнизоном: %d",
|
||||||
|
statsUnderGarrisoned = " - Недоукомплектованные: %d",
|
||||||
|
statsInfantrySpawn = " Появление пехоты: %ds",
|
||||||
|
statsInfantrySpawnPaused = " Появление пехоты: ПАУЗА (нет складов)",
|
||||||
|
statsArmorSpawn = " Появление техники: %ds",
|
||||||
|
statsArmorSpawnPaused = " Появление техники: ПАУЗА (нет складов)",
|
||||||
|
statsSystemInfo = "【ИНФОРМАЦИЯ О СИСТЕМЕ】",
|
||||||
|
statsTotalZones = " Всего зон: %d",
|
||||||
|
statsActiveGarrisons = " Активные гарнизоны: %d",
|
||||||
|
statsTotalActiveUnits = " Всего активных единиц: %d",
|
||||||
|
statsTrackedGroups = " Отслеживаемые группы: %d",
|
||||||
|
statsLuaMemory = " Память Lua: %.1f МБ",
|
||||||
|
statsWarningMemory = " ⚠️ ВНИМАНИЕ: Высокое использование памяти!",
|
||||||
|
statsWarningGroups = " ⚠️ ВНИМАНИЕ: Высокое количество групп!",
|
||||||
|
enabled = "ВКЛЮЧЕНО",
|
||||||
|
disabled = "ВЫКЛЮЧЕНО",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Helper function to get player's language from main script
|
||||||
|
-- Falls back to English if playerLanguages is not available or player not found
|
||||||
|
local function DGB_GetPlayerLanguage(playerName)
|
||||||
|
-- Try to use the main script's playerLanguages table
|
||||||
|
if playerLanguages and playerName and playerLanguages[playerName] then
|
||||||
|
return playerLanguages[playerName]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Try to use the main script's GetPlayerLanguage function if available
|
||||||
|
if GetPlayerLanguage and type(GetPlayerLanguage) == "function" and playerName then
|
||||||
|
local lang = GetPlayerLanguage(playerName)
|
||||||
|
if lang then return lang end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fall back to main script's default language
|
||||||
|
if LANGUAGE_CONFIG and LANGUAGE_CONFIG.defaultLanguage then
|
||||||
|
return LANGUAGE_CONFIG.defaultLanguage
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ultimate fallback to English
|
||||||
|
return "EN"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper function to get coalition's language (uses first player's language)
|
||||||
|
local function DGB_GetCoalitionLanguage(coalitionSide)
|
||||||
|
local playerList = coalition.getPlayers(coalitionSide)
|
||||||
|
|
||||||
|
if playerList and #playerList > 0 then
|
||||||
|
local unit = playerList[1]
|
||||||
|
if unit then
|
||||||
|
local playerName = unit:getPlayerName()
|
||||||
|
if playerName then
|
||||||
|
return DGB_GetPlayerLanguage(playerName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fall back to default language
|
||||||
|
if LANGUAGE_CONFIG and LANGUAGE_CONFIG.defaultLanguage then
|
||||||
|
return LANGUAGE_CONFIG.defaultLanguage
|
||||||
|
end
|
||||||
|
|
||||||
|
return "EN"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper function to get translated text
|
||||||
|
local function DGB_GetText(textKey, playerName)
|
||||||
|
local lang = DGB_GetPlayerLanguage(playerName)
|
||||||
|
local langTable = DGB_LANGUAGES[lang] or DGB_LANGUAGES.EN
|
||||||
|
return langTable[textKey] or DGB_LANGUAGES.EN[textKey] or textKey
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper function to get translated text for a coalition
|
||||||
|
local function DGB_GetTextForCoalition(textKey, coalitionSide)
|
||||||
|
local lang = DGB_GetCoalitionLanguage(coalitionSide)
|
||||||
|
local langTable = DGB_LANGUAGES[lang] or DGB_LANGUAGES.EN
|
||||||
|
return langTable[textKey] or DGB_LANGUAGES.EN[textKey] or textKey
|
||||||
|
end
|
||||||
|
|
||||||
env.info("[DGB PLUGIN] Dynamic Ground Battle Plugin initializing...")
|
env.info("[DGB PLUGIN] Dynamic Ground Battle Plugin initializing...")
|
||||||
|
|
||||||
-- Validate that DualCoalitionZoneCapture is loaded
|
-- Validate that DualCoalitionZoneCapture is loaded
|
||||||
@@ -284,8 +590,36 @@ end
|
|||||||
|
|
||||||
env.info("[DGB PLUGIN] Found " .. #zoneCaptureObjects .. " zones from DualCoalitionZoneCapture")
|
env.info("[DGB PLUGIN] Found " .. #zoneCaptureObjects .. " zones from DualCoalitionZoneCapture")
|
||||||
|
|
||||||
-- Track active markers to prevent memory leaks
|
-- Track warehouse markers per warehouse and coalition
|
||||||
local activeMarkers = {}
|
local warehouseMarkers = {}
|
||||||
|
|
||||||
|
-- Add event handlers for warehouse destruction
|
||||||
|
local function SetupWarehouseEventHandlers()
|
||||||
|
local allWarehouses = {}
|
||||||
|
for _, wh in ipairs(redWarehouses) do table.insert(allWarehouses, wh) end
|
||||||
|
for _, wh in ipairs(blueWarehouses) do table.insert(allWarehouses, wh) end
|
||||||
|
|
||||||
|
for _, warehouse in ipairs(allWarehouses) do
|
||||||
|
if warehouse then
|
||||||
|
warehouse:HandleEvent(EVENTS.Dead, function(event)
|
||||||
|
-- Remove markers for this warehouse from both coalitions
|
||||||
|
local name = warehouse:GetName()
|
||||||
|
for _, coalition in ipairs({1, 2}) do
|
||||||
|
local key = name .. "_coalition_" .. coalition
|
||||||
|
if warehouseMarkers[key] then
|
||||||
|
warehouseMarkers[key]:Remove()
|
||||||
|
warehouseMarkers[key] = nil
|
||||||
|
env.info(string.format("[DGB PLUGIN] Removed marker for destroyed warehouse %s (coalition %d)", name, coalition))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("[DGB PLUGIN] Warehouse %s destroyed - markers removed", name))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
SetupWarehouseEventHandlers()
|
||||||
|
|
||||||
-- Zone Garrison Tracking System
|
-- Zone Garrison Tracking System
|
||||||
-- Structure: zoneGarrisons[zoneName] = { defenders = {groupName1, groupName2, ...}, lastUpdate = timestamp }
|
-- Structure: zoneGarrisons[zoneName] = { defenders = {groupName1, groupName2, ...}, lastUpdate = timestamp }
|
||||||
@@ -390,27 +724,28 @@ end
|
|||||||
-- Function to add warehouse markers on the map
|
-- Function to add warehouse markers on the map
|
||||||
local function addMarkPoints(warehouses, coalition)
|
local function addMarkPoints(warehouses, coalition)
|
||||||
for _, warehouse in ipairs(warehouses) do
|
for _, warehouse in ipairs(warehouses) do
|
||||||
if warehouse then
|
if warehouse and warehouse:GetLife() > 0 then
|
||||||
local warehousePos = warehouse:GetVec3()
|
local warehousePos = warehouse:GetVec3()
|
||||||
local details
|
local details
|
||||||
|
|
||||||
if coalition == 2 then -- Blue viewing
|
if coalition == 2 then -- Blue viewing
|
||||||
if warehouse:GetCoalition() == 2 then
|
if warehouse:GetCoalition() == 2 then
|
||||||
details = "Warehouse: " .. warehouse:GetName() .. "\nThis warehouse needs to be protected.\n"
|
details = string.format(DGB_GetTextForCoalition("warehouseFriendly", coalition), warehouse:GetName())
|
||||||
else
|
else
|
||||||
details = "Warehouse: " .. warehouse:GetName() .. "\nThis is a primary target as it is directly supplying enemy units.\n"
|
details = string.format(DGB_GetTextForCoalition("warehouseEnemy", coalition), warehouse:GetName())
|
||||||
end
|
end
|
||||||
elseif coalition == 1 then -- Red viewing
|
elseif coalition == 1 then -- Red viewing
|
||||||
if warehouse:GetCoalition() == 1 then
|
if warehouse:GetCoalition() == 1 then
|
||||||
details = "Warehouse: " .. warehouse:GetName() .. "\nThis warehouse needs to be protected.\n"
|
details = string.format(DGB_GetTextForCoalition("warehouseFriendly", coalition), warehouse:GetName())
|
||||||
else
|
else
|
||||||
details = "Warehouse: " .. warehouse:GetName() .. "\nThis is a primary target as it is directly supplying enemy units.\n"
|
details = string.format(DGB_GetTextForCoalition("warehouseEnemy", coalition), warehouse:GetName())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local coordinate = COORDINATE:NewFromVec3(warehousePos)
|
local coordinate = COORDINATE:NewFromVec3(warehousePos)
|
||||||
local marker = MARKER:New(coordinate, details):ToCoalition(coalition):ReadOnly()
|
local marker = MARKER:New(coordinate, details):ToCoalition(coalition):ReadOnly()
|
||||||
table.insert(activeMarkers, marker)
|
local key = warehouse:GetName() .. "_coalition_" .. coalition
|
||||||
|
warehouseMarkers[key] = marker
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -418,12 +753,9 @@ end
|
|||||||
-- Function to update warehouse markers
|
-- Function to update warehouse markers
|
||||||
local function updateMarkPoints()
|
local function updateMarkPoints()
|
||||||
-- Clean up old markers first
|
-- Clean up old markers first
|
||||||
for i = #activeMarkers, 1, -1 do
|
for key, marker in pairs(warehouseMarkers) do
|
||||||
local marker = activeMarkers[i]
|
marker:Remove()
|
||||||
if marker then
|
warehouseMarkers[key] = nil
|
||||||
marker:Remove()
|
|
||||||
end
|
|
||||||
activeMarkers[i] = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
addMarkPoints(redWarehouses, 2) -- Blue coalition sees red warehouses
|
addMarkPoints(redWarehouses, 2) -- Blue coalition sees red warehouses
|
||||||
@@ -431,7 +763,9 @@ local function updateMarkPoints()
|
|||||||
addMarkPoints(redWarehouses, 1) -- Red coalition sees red warehouses
|
addMarkPoints(redWarehouses, 1) -- Red coalition sees red warehouses
|
||||||
addMarkPoints(blueWarehouses, 1) -- Red coalition sees blue warehouses
|
addMarkPoints(blueWarehouses, 1) -- Red coalition sees blue warehouses
|
||||||
|
|
||||||
env.info(string.format("[DGB PLUGIN] Updated warehouse markers (%d total)", #activeMarkers))
|
local markerCount = 0
|
||||||
|
for _ in pairs(warehouseMarkers) do markerCount = markerCount + 1 end
|
||||||
|
env.info(string.format("[DGB PLUGIN] Updated warehouse markers (%d total)", markerCount))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Function to check if a group contains infantry units
|
-- Function to check if a group contains infantry units
|
||||||
@@ -568,7 +902,7 @@ local function TryDefenderRotation(group, zone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if oldestDefender and oldestDefenderGroup:GetName() ~= group:GetName() then
|
if oldestDefender and oldestDefenderGroup and oldestDefenderGroup:GetName() ~= group:GetName() then
|
||||||
-- Remove old defender
|
-- Remove old defender
|
||||||
for i, defenderName in ipairs(garrison.defenders) do
|
for i, defenderName in ipairs(garrison.defenders) do
|
||||||
if defenderName == oldestDefender then
|
if defenderName == oldestDefender then
|
||||||
@@ -638,9 +972,10 @@ local function AssignTasksToGroups()
|
|||||||
if zoneInfo and zoneInfo.zone then
|
if zoneInfo and zoneInfo.zone then
|
||||||
env.info(string.format("[DGB PLUGIN] %s: Defender patrol in zone %s", groupName, zoneName))
|
env.info(string.format("[DGB PLUGIN] %s: Defender patrol in zone %s", groupName, zoneName))
|
||||||
-- Use simpler patrol method to reduce pathfinding memory
|
-- Use simpler patrol method to reduce pathfinding memory
|
||||||
|
-- Reduced patrol radius from 0.5 to 0.3 to create simpler paths
|
||||||
local zoneCoord = zoneInfo.zone:GetCoordinate()
|
local zoneCoord = zoneInfo.zone:GetCoordinate()
|
||||||
if zoneCoord then
|
if zoneCoord then
|
||||||
local patrolPoint = zoneCoord:GetRandomCoordinateInRadius(zoneInfo.zone:GetRadius() * 0.5)
|
local patrolPoint = zoneCoord:GetRandomCoordinateInRadius(zoneInfo.zone:GetRadius() * 0.3) -- Reduced from 0.5
|
||||||
local speed = IsInfantryGroup(group) and 15 or 25 -- km/h - slow patrol
|
local speed = IsInfantryGroup(group) and 15 or 25 -- km/h - slow patrol
|
||||||
group:RouteGroundTo(patrolPoint, speed, "Vee", 1)
|
group:RouteGroundTo(patrolPoint, speed, "Vee", 1)
|
||||||
end
|
end
|
||||||
@@ -688,7 +1023,7 @@ local function AssignTasksToGroups()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- 3. HANDLE GROUPS IN FRIENDLY ZONES
|
-- 3. HANDLE GROUPS IN FRIENDLY ZONES
|
||||||
if currentZone and currentZoneCapture:GetCoalition() == groupCoalition then
|
if currentZone and currentZoneCapture and currentZoneCapture:GetCoalition() == groupCoalition then
|
||||||
local zoneName = currentZone:GetName()
|
local zoneName = currentZone:GetName()
|
||||||
|
|
||||||
-- PRIORITY 1: If the zone is under attack, all non-defenders should help defend it
|
-- PRIORITY 1: If the zone is under attack, all non-defenders should help defend it
|
||||||
@@ -758,9 +1093,10 @@ local function AssignTasksToGroups()
|
|||||||
|
|
||||||
-- Use simpler waypoint-based routing instead of TaskRouteToZone to reduce pathfinding memory load
|
-- Use simpler waypoint-based routing instead of TaskRouteToZone to reduce pathfinding memory load
|
||||||
-- This prevents the "CREATING PATH MAKES TOO LONG" memory buildup
|
-- This prevents the "CREATING PATH MAKES TOO LONG" memory buildup
|
||||||
|
-- Reduced radius from 0.7 to 0.5 to create simpler, shorter paths
|
||||||
local zoneCoord = closestEnemyZone:GetCoordinate()
|
local zoneCoord = closestEnemyZone:GetCoordinate()
|
||||||
if zoneCoord then
|
if zoneCoord then
|
||||||
local randomPoint = zoneCoord:GetRandomCoordinateInRadius(closestEnemyZone:GetRadius() * 0.7)
|
local randomPoint = zoneCoord:GetRandomCoordinateInRadius(closestEnemyZone:GetRadius() * 0.5) -- Reduced from 0.7
|
||||||
local speed = IsInfantryGroup(group) and 20 or 40 -- km/h
|
local speed = IsInfantryGroup(group) and 20 or 40 -- km/h
|
||||||
group:RouteGroundTo(randomPoint, speed, "Vee", 1)
|
group:RouteGroundTo(randomPoint, speed, "Vee", 1)
|
||||||
end
|
end
|
||||||
@@ -794,10 +1130,23 @@ local function MonitorWarehouses()
|
|||||||
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
||||||
|
|
||||||
if ENABLE_WAREHOUSE_STATUS_MESSAGES then
|
if ENABLE_WAREHOUSE_STATUS_MESSAGES then
|
||||||
local msg = "[Warehouse Status]\n"
|
-- Send to Blue coalition in their language
|
||||||
msg = msg .. "Red warehouses alive: " .. redWarehousesAlive .. " Reinforcements: " .. redSpawnFrequencyPercentage .. "%\n"
|
local blueLang = DGB_GetCoalitionLanguage(coalition.side.BLUE)
|
||||||
msg = msg .. "Blue warehouses alive: " .. blueWarehousesAlive .. " Reinforcements: " .. blueSpawnFrequencyPercentage .. "%\n"
|
local blueMsg = string.format(
|
||||||
MESSAGE:New(msg, 30):ToAll()
|
DGB_LANGUAGES[blueLang].warehouseStatus,
|
||||||
|
redWarehousesAlive, redSpawnFrequencyPercentage,
|
||||||
|
blueWarehousesAlive, blueSpawnFrequencyPercentage
|
||||||
|
)
|
||||||
|
MESSAGE:New(blueMsg, 30):ToBlue()
|
||||||
|
|
||||||
|
-- Send to Red coalition in their language
|
||||||
|
local redLang = DGB_GetCoalitionLanguage(coalition.side.RED)
|
||||||
|
local redMsg = string.format(
|
||||||
|
DGB_LANGUAGES[redLang].warehouseStatus,
|
||||||
|
redWarehousesAlive, redSpawnFrequencyPercentage,
|
||||||
|
blueWarehousesAlive, blueSpawnFrequencyPercentage
|
||||||
|
)
|
||||||
|
MESSAGE:New(redMsg, 30):ToRed()
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info(string.format("[DGB PLUGIN] Warehouse status - Red: %d/%d (%d%%), Blue: %d/%d (%d%%)",
|
env.info(string.format("[DGB PLUGIN] Warehouse status - Red: %d/%d (%d%%), Blue: %d/%d (%d%%)",
|
||||||
@@ -896,73 +1245,77 @@ local function ShowSystemStatistics(playerCoalition)
|
|||||||
local blueInfantryInterval = CalculateSpawnFrequency(blueWarehouses, SPAWN_SCHED_BLUE_INFANTRY, BLUE_INFANTRY_CADENCE_SCALAR)
|
local blueInfantryInterval = CalculateSpawnFrequency(blueWarehouses, SPAWN_SCHED_BLUE_INFANTRY, BLUE_INFANTRY_CADENCE_SCALAR)
|
||||||
local blueArmorInterval = CalculateSpawnFrequency(blueWarehouses, SPAWN_SCHED_BLUE_ARMOR, BLUE_ARMOR_CADENCE_SCALAR)
|
local blueArmorInterval = CalculateSpawnFrequency(blueWarehouses, SPAWN_SCHED_BLUE_ARMOR, BLUE_ARMOR_CADENCE_SCALAR)
|
||||||
|
|
||||||
|
-- Get language for this coalition
|
||||||
|
local lang = DGB_GetCoalitionLanguage(playerCoalition)
|
||||||
|
local T = DGB_LANGUAGES[lang]
|
||||||
|
|
||||||
-- Build comprehensive report
|
-- Build comprehensive report
|
||||||
local msg = "═══════════════════════════════════════\n"
|
local msg = "═══════════════════════════════════════\n"
|
||||||
msg = msg .. "DYNAMIC GROUND BATTLE - SYSTEM STATUS\n"
|
msg = msg .. T.statsTitle .. "\n"
|
||||||
msg = msg .. "═══════════════════════════════════════\n\n"
|
msg = msg .. "═══════════════════════════════════════\n\n"
|
||||||
|
|
||||||
-- Configuration Section
|
-- Configuration Section
|
||||||
msg = msg .. "【CONFIGURATION】\n"
|
msg = msg .. T.statsConfiguration .. "\n"
|
||||||
msg = msg .. " Defenders per Zone: " .. DEFENDERS_PER_ZONE .. "\n"
|
msg = msg .. string.format(T.statsDefendersPerZone, DEFENDERS_PER_ZONE) .. "\n"
|
||||||
msg = msg .. " Defender Rotation: " .. (ALLOW_DEFENDER_ROTATION and "ENABLED" or "DISABLED") .. "\n"
|
msg = msg .. string.format(T.statsDefenderRotation, ALLOW_DEFENDER_ROTATION and T.enabled or T.disabled) .. "\n"
|
||||||
msg = msg .. " Infantry Movement: " .. (MOVING_INFANTRY_PATROLS and "ENABLED" or "DISABLED") .. "\n"
|
msg = msg .. string.format(T.statsInfantryMovement, MOVING_INFANTRY_PATROLS and T.enabled or T.disabled) .. "\n"
|
||||||
msg = msg .. " Task Reassignment: Every " .. ASSIGN_TASKS_SCHED .. "s\n"
|
msg = msg .. string.format(T.statsTaskReassignment, ASSIGN_TASKS_SCHED) .. "\n"
|
||||||
msg = msg .. " Warehouse Markers: " .. (ENABLE_WAREHOUSE_MARKERS and "ENABLED" or "DISABLED") .. "\n\n"
|
msg = msg .. string.format(T.statsWarehouseMarkers, ENABLE_WAREHOUSE_MARKERS and T.enabled or T.disabled) .. "\n\n"
|
||||||
|
|
||||||
-- Spawn Limits Section
|
-- Spawn Limits Section
|
||||||
msg = msg .. "【SPAWN LIMITS】\n"
|
msg = msg .. T.statsSpawnLimits .. "\n"
|
||||||
msg = msg .. " Red Infantry: " .. INIT_RED_INFANTRY .. "/" .. MAX_RED_INFANTRY .. "\n"
|
msg = msg .. string.format(T.statsRedInfantry, INIT_RED_INFANTRY, MAX_RED_INFANTRY) .. "\n"
|
||||||
msg = msg .. " Red Armor: " .. INIT_RED_ARMOR .. "/" .. MAX_RED_ARMOR .. "\n"
|
msg = msg .. string.format(T.statsRedArmor, INIT_RED_ARMOR, MAX_RED_ARMOR) .. "\n"
|
||||||
msg = msg .. " Blue Infantry: " .. INIT_BLUE_INFANTRY .. "/" .. MAX_BLUE_INFANTRY .. "\n"
|
msg = msg .. string.format(T.statsBlueInfantry, INIT_BLUE_INFANTRY, MAX_BLUE_INFANTRY) .. "\n"
|
||||||
msg = msg .. " Blue Armor: " .. INIT_BLUE_ARMOR .. "/" .. MAX_BLUE_ARMOR .. "\n\n"
|
msg = msg .. string.format(T.statsBlueArmor, INIT_BLUE_ARMOR, MAX_BLUE_ARMOR) .. "\n\n"
|
||||||
|
|
||||||
-- Red Coalition Section
|
-- Red Coalition Section
|
||||||
msg = msg .. "【RED COALITION】\n"
|
msg = msg .. T.statsRedCoalition .. "\n"
|
||||||
msg = msg .. " Warehouses: " .. redWarehousesAlive .. "/" .. redWarehouseTotal .. " (" .. redSpawnFreqPct .. "%)\n"
|
msg = msg .. string.format(T.statsWarehouses, redWarehousesAlive, redWarehouseTotal, redSpawnFreqPct) .. "\n"
|
||||||
msg = msg .. " Active Units: " .. redUnits.total .. " (" .. redUnits.infantry .. " inf, " .. redUnits.armor .. " armor)\n"
|
msg = msg .. string.format(T.statsActiveUnits, redUnits.total, redUnits.infantry, redUnits.armor) .. "\n"
|
||||||
msg = msg .. " Defenders: " .. redUnits.defenders .. " | Mobile: " .. redUnits.mobile .. "\n"
|
msg = msg .. string.format(T.statsDefenders, redUnits.defenders, redUnits.mobile) .. "\n"
|
||||||
msg = msg .. " Controlled Zones: " .. redGarrison.totalZones .. "\n"
|
msg = msg .. string.format(T.statsControlledZones, redGarrison.totalZones) .. "\n"
|
||||||
msg = msg .. " - Garrisoned: " .. redGarrison.garrisoned .. "\n"
|
msg = msg .. string.format(T.statsGarrisoned, redGarrison.garrisoned) .. "\n"
|
||||||
msg = msg .. " - Under-Garrisoned: " .. redGarrison.underGarrisoned .. "\n"
|
msg = msg .. string.format(T.statsUnderGarrisoned, redGarrison.underGarrisoned) .. "\n"
|
||||||
|
|
||||||
if redInfantryInterval then
|
if redInfantryInterval then
|
||||||
msg = msg .. " Infantry Spawn: " .. math.floor(redInfantryInterval) .. "s\n"
|
msg = msg .. string.format(T.statsInfantrySpawn, math.floor(redInfantryInterval)) .. "\n"
|
||||||
else
|
else
|
||||||
msg = msg .. " Infantry Spawn: PAUSED (no warehouses)\n"
|
msg = msg .. T.statsInfantrySpawnPaused .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if redArmorInterval then
|
if redArmorInterval then
|
||||||
msg = msg .. " Armor Spawn: " .. math.floor(redArmorInterval) .. "s\n\n"
|
msg = msg .. string.format(T.statsArmorSpawn, math.floor(redArmorInterval)) .. "\n\n"
|
||||||
else
|
else
|
||||||
msg = msg .. " Armor Spawn: PAUSED (no warehouses)\n\n"
|
msg = msg .. T.statsArmorSpawnPaused .. "\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Blue Coalition Section
|
-- Blue Coalition Section
|
||||||
msg = msg .. "【BLUE COALITION】\n"
|
msg = msg .. T.statsBlueCoalition .. "\n"
|
||||||
msg = msg .. " Warehouses: " .. blueWarehousesAlive .. "/" .. blueWarehouseTotal .. " (" .. blueSpawnFreqPct .. "%)\n"
|
msg = msg .. string.format(T.statsWarehouses, blueWarehousesAlive, blueWarehouseTotal, blueSpawnFreqPct) .. "\n"
|
||||||
msg = msg .. " Active Units: " .. blueUnits.total .. " (" .. blueUnits.infantry .. " inf, " .. blueUnits.armor .. " armor)\n"
|
msg = msg .. string.format(T.statsActiveUnits, blueUnits.total, blueUnits.infantry, blueUnits.armor) .. "\n"
|
||||||
msg = msg .. " Defenders: " .. blueUnits.defenders .. " | Mobile: " .. blueUnits.mobile .. "\n"
|
msg = msg .. string.format(T.statsDefenders, blueUnits.defenders, blueUnits.mobile) .. "\n"
|
||||||
msg = msg .. " Controlled Zones: " .. blueGarrison.totalZones .. "\n"
|
msg = msg .. string.format(T.statsControlledZones, blueGarrison.totalZones) .. "\n"
|
||||||
msg = msg .. " - Garrisoned: " .. blueGarrison.garrisoned .. "\n"
|
msg = msg .. string.format(T.statsGarrisoned, blueGarrison.garrisoned) .. "\n"
|
||||||
msg = msg .. " - Under-Garrisoned: " .. blueGarrison.underGarrisoned .. "\n"
|
msg = msg .. string.format(T.statsUnderGarrisoned, blueGarrison.underGarrisoned) .. "\n"
|
||||||
|
|
||||||
if blueInfantryInterval then
|
if blueInfantryInterval then
|
||||||
msg = msg .. " Infantry Spawn: " .. math.floor(blueInfantryInterval) .. "s\n"
|
msg = msg .. string.format(T.statsInfantrySpawn, math.floor(blueInfantryInterval)) .. "\n"
|
||||||
else
|
else
|
||||||
msg = msg .. " Infantry Spawn: PAUSED (no warehouses)\n"
|
msg = msg .. T.statsInfantrySpawnPaused .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if blueArmorInterval then
|
if blueArmorInterval then
|
||||||
msg = msg .. " Armor Spawn: " .. math.floor(blueArmorInterval) .. "s\n\n"
|
msg = msg .. string.format(T.statsArmorSpawn, math.floor(blueArmorInterval)) .. "\n\n"
|
||||||
else
|
else
|
||||||
msg = msg .. " Armor Spawn: PAUSED (no warehouses)\n\n"
|
msg = msg .. T.statsArmorSpawnPaused .. "\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- System Info
|
-- System Info
|
||||||
msg = msg .. "【SYSTEM INFO】\n"
|
msg = msg .. T.statsSystemInfo .. "\n"
|
||||||
msg = msg .. " Total Zones: " .. #zoneCaptureObjects .. "\n"
|
msg = msg .. string.format(T.statsTotalZones, #zoneCaptureObjects) .. "\n"
|
||||||
msg = msg .. " Active Garrisons: " .. (redGarrison.garrisoned + blueGarrison.garrisoned) .. "\n"
|
msg = msg .. string.format(T.statsActiveGarrisons, redGarrison.garrisoned + blueGarrison.garrisoned) .. "\n"
|
||||||
msg = msg .. " Total Active Units: " .. (redUnits.total + blueUnits.total) .. "\n"
|
msg = msg .. string.format(T.statsTotalActiveUnits, redUnits.total + blueUnits.total) .. "\n"
|
||||||
|
|
||||||
-- Memory and Performance Tracking
|
-- Memory and Performance Tracking
|
||||||
local totalSpawnedGroups = 0
|
local totalSpawnedGroups = 0
|
||||||
@@ -971,17 +1324,17 @@ local function ShowSystemStatistics(playerCoalition)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local luaMemoryKB = collectgarbage("count")
|
local luaMemoryKB = collectgarbage("count")
|
||||||
msg = msg .. " Tracked Groups: " .. totalSpawnedGroups .. "\n"
|
msg = msg .. string.format(T.statsTrackedGroups, totalSpawnedGroups) .. "\n"
|
||||||
msg = msg .. " Lua Memory: " .. string.format("%.1f MB", luaMemoryKB / 1024) .. "\n"
|
msg = msg .. string.format(T.statsLuaMemory, luaMemoryKB / 1024) .. "\n"
|
||||||
|
|
||||||
-- Warning if memory is high
|
-- Warning if memory is high
|
||||||
if luaMemoryKB > 512000 then -- More than 500MB
|
if luaMemoryKB > 512000 then -- More than 500MB
|
||||||
msg = msg .. " ⚠️ WARNING: High memory usage!\n"
|
msg = msg .. T.statsWarningMemory .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Warning if too many groups
|
-- Warning if too many groups
|
||||||
if totalSpawnedGroups > 200 then
|
if totalSpawnedGroups > 200 then
|
||||||
msg = msg .. " ⚠️ WARNING: High group count!\n"
|
msg = msg .. T.statsWarningGroups .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
msg = msg .. "\n"
|
msg = msg .. "\n"
|
||||||
@@ -1004,8 +1357,13 @@ local blueZones = GetZonesByCoalition(coalition.side.BLUE)
|
|||||||
local redSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(redWarehouses)
|
local redSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(redWarehouses)
|
||||||
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
||||||
|
|
||||||
MESSAGE:New("Red reinforcement capacity: " .. redSpawnFrequencyPercentage .. "%", 30):ToRed()
|
local redLang = DGB_GetCoalitionLanguage(coalition.side.RED)
|
||||||
MESSAGE:New("Blue reinforcement capacity: " .. blueSpawnFrequencyPercentage .. "%", 30):ToBlue()
|
local redMsg = string.format(DGB_LANGUAGES[redLang].reinforcementCapacity, "Red", redSpawnFrequencyPercentage)
|
||||||
|
MESSAGE:New(redMsg, 30):ToRed()
|
||||||
|
|
||||||
|
local blueLang = DGB_GetCoalitionLanguage(coalition.side.BLUE)
|
||||||
|
local blueMsg = string.format(DGB_LANGUAGES[blueLang].reinforcementCapacity, "Blue", blueSpawnFrequencyPercentage)
|
||||||
|
MESSAGE:New(blueMsg, 30):ToBlue()
|
||||||
|
|
||||||
-- Initialize spawners
|
-- Initialize spawners
|
||||||
env.info("[DGB PLUGIN] Initializing spawn systems...")
|
env.info("[DGB PLUGIN] Initializing spawn systems...")
|
||||||
@@ -1195,8 +1553,10 @@ local function CleanupStaleData()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Force Lua garbage collection to reclaim memory
|
-- Force aggressive Lua garbage collection to reclaim memory
|
||||||
collectgarbage("collect")
|
-- Step-based collection helps ensure thorough cleanup
|
||||||
|
collectgarbage("collect") -- Full collection
|
||||||
|
collectgarbage("collect") -- Second pass to catch finalized objects
|
||||||
|
|
||||||
if cleanedGroups > 0 or cleanedCooldowns > 0 or cleanedGarrisons > 0 then
|
if cleanedGroups > 0 or cleanedCooldowns > 0 or cleanedGarrisons > 0 then
|
||||||
env.info(string.format("[DGB PLUGIN] Cleanup: Removed %d groups, %d cooldowns, %d garrisons",
|
env.info(string.format("[DGB PLUGIN] Cleanup: Removed %d groups, %d cooldowns, %d garrisons",
|
||||||
@@ -1206,10 +1566,13 @@ end
|
|||||||
|
|
||||||
-- Optional periodic memory usage logging (Lua-only; shows in dcs.log)
|
-- Optional periodic memory usage logging (Lua-only; shows in dcs.log)
|
||||||
local ENABLE_MEMORY_LOGGING = true
|
local ENABLE_MEMORY_LOGGING = true
|
||||||
local MEMORY_LOG_INTERVAL = 900 -- seconds (15 minutes)
|
local MEMORY_LOG_INTERVAL = 600 -- seconds (10 minutes) - reduced from 15 minutes
|
||||||
local CLEANUP_INTERVAL = 600 -- seconds (10 minutes)
|
local CLEANUP_INTERVAL = 300 -- seconds (5 minutes) - reduced from 10 minutes for more aggressive cleanup
|
||||||
|
|
||||||
local function LogMemoryUsage()
|
local function LogMemoryUsage()
|
||||||
|
-- Force garbage collection before measuring to get accurate readings
|
||||||
|
collectgarbage("collect")
|
||||||
|
|
||||||
local luaMemoryKB = collectgarbage("count")
|
local luaMemoryKB = collectgarbage("count")
|
||||||
local luaMemoryMB = luaMemoryKB / 1024
|
local luaMemoryMB = luaMemoryKB / 1024
|
||||||
|
|
||||||
@@ -1248,25 +1611,28 @@ SCHEDULER:New(nil, AssignTasksToGroups, {}, 15, ASSIGN_TASKS_SCHED)
|
|||||||
-- Add F10 menu for manual checks (using MenuManager if available)
|
-- Add F10 menu for manual checks (using MenuManager if available)
|
||||||
if MenuManager then
|
if MenuManager then
|
||||||
-- Create coalition-specific menus under Mission Options
|
-- Create coalition-specific menus under Mission Options
|
||||||
local blueMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Ground Battle")
|
local blueMenuText = DGB_GetTextForCoalition("menuGroundBattle", coalition.side.BLUE)
|
||||||
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Check Warehouse Status", blueMenu, MonitorWarehouses)
|
local blueMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, blueMenuText)
|
||||||
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show System Statistics", blueMenu, function()
|
MENU_COALITION_COMMAND:New(coalition.side.BLUE, DGB_GetTextForCoalition("menuWarehouseStatus", coalition.side.BLUE), blueMenu, MonitorWarehouses)
|
||||||
|
MENU_COALITION_COMMAND:New(coalition.side.BLUE, DGB_GetTextForCoalition("menuSystemStats", coalition.side.BLUE), blueMenu, function()
|
||||||
ShowSystemStatistics(coalition.side.BLUE)
|
ShowSystemStatistics(coalition.side.BLUE)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local redMenu = MenuManager.CreateCoalitionMenu(coalition.side.RED, "Ground Battle")
|
local redMenuText = DGB_GetTextForCoalition("menuGroundBattle", coalition.side.RED)
|
||||||
MENU_COALITION_COMMAND:New(coalition.side.RED, "Check Warehouse Status", redMenu, MonitorWarehouses)
|
local redMenu = MenuManager.CreateCoalitionMenu(coalition.side.RED, redMenuText)
|
||||||
MENU_COALITION_COMMAND:New(coalition.side.RED, "Show System Statistics", redMenu, function()
|
MENU_COALITION_COMMAND:New(coalition.side.RED, DGB_GetTextForCoalition("menuWarehouseStatus", coalition.side.RED), redMenu, MonitorWarehouses)
|
||||||
|
MENU_COALITION_COMMAND:New(coalition.side.RED, DGB_GetTextForCoalition("menuSystemStats", coalition.side.RED), redMenu, function()
|
||||||
ShowSystemStatistics(coalition.side.RED)
|
ShowSystemStatistics(coalition.side.RED)
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
-- Fallback to root-level mission menu
|
-- Fallback to root-level mission menu
|
||||||
local missionMenu = MENU_MISSION:New("Ground Battle")
|
local missionMenuText = DGB_GetTextForCoalition("menuGroundBattle", coalition.side.BLUE)
|
||||||
MENU_MISSION_COMMAND:New("Check Warehouse Status", missionMenu, MonitorWarehouses)
|
local missionMenu = MENU_MISSION:New(missionMenuText)
|
||||||
MENU_MISSION_COMMAND:New("Show Blue Statistics", missionMenu, function()
|
MENU_MISSION_COMMAND:New(DGB_GetTextForCoalition("menuWarehouseStatus", coalition.side.BLUE), missionMenu, MonitorWarehouses)
|
||||||
|
MENU_MISSION_COMMAND:New(DGB_GetTextForCoalition("menuSystemStats", coalition.side.BLUE) .. " (Blue)", missionMenu, function()
|
||||||
ShowSystemStatistics(coalition.side.BLUE)
|
ShowSystemStatistics(coalition.side.BLUE)
|
||||||
end)
|
end)
|
||||||
MENU_MISSION_COMMAND:New("Show Red Statistics", missionMenu, function()
|
MENU_MISSION_COMMAND:New(DGB_GetTextForCoalition("menuSystemStats", coalition.side.RED) .. " (Red)", missionMenu, function()
|
||||||
ShowSystemStatistics(coalition.side.RED)
|
ShowSystemStatistics(coalition.side.RED)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
--[[
|
||||||
|
Unified F10 Menu Manager
|
||||||
|
|
||||||
|
Purpose: Provides a centralized menu system to organize all mission scripts
|
||||||
|
into a consistent F10 menu structure.
|
||||||
|
|
||||||
|
Menu Organization:
|
||||||
|
F10 -> F1: Mission Options (all other scripts go here)
|
||||||
|
F10 -> F2: CTLD (reserved position)
|
||||||
|
F10 -> F3: AFAC Control (reserved position)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
1. Load this script FIRST before any other menu-creating scripts
|
||||||
|
2. Other scripts should use MenuManager to register their menus
|
||||||
|
|
||||||
|
Example:
|
||||||
|
-- In your script, instead of:
|
||||||
|
-- local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
|
||||||
|
|
||||||
|
-- Use:
|
||||||
|
-- local MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
MenuManager = {}
|
||||||
|
MenuManager.Version = "1.1"
|
||||||
|
|
||||||
|
-- Configuration
|
||||||
|
MenuManager.Config = {
|
||||||
|
EnableMissionOptionsMenu = true, -- Set to false to disable the parent menu system
|
||||||
|
MissionOptionsMenuName = "Mission Options", -- Name of the parent menu
|
||||||
|
Debug = false -- Set to true for debug messages
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Storage for menu references
|
||||||
|
MenuManager.Menus = {
|
||||||
|
Blue = {},
|
||||||
|
Red = {},
|
||||||
|
Mission = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Parent menu references (created on first use)
|
||||||
|
MenuManager.ParentMenus = {
|
||||||
|
BlueCoalition = nil,
|
||||||
|
RedCoalition = nil,
|
||||||
|
Mission = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Initialize the parent menus
|
||||||
|
function MenuManager.Initialize()
|
||||||
|
if MenuManager.Config.EnableMissionOptionsMenu then
|
||||||
|
-- Create the parent "Mission Options" menu for each coalition
|
||||||
|
MenuManager.ParentMenus.BlueCoalition = MENU_COALITION:New(
|
||||||
|
coalition.side.BLUE,
|
||||||
|
MenuManager.Config.MissionOptionsMenuName
|
||||||
|
)
|
||||||
|
|
||||||
|
MenuManager.ParentMenus.RedCoalition = MENU_COALITION:New(
|
||||||
|
coalition.side.RED,
|
||||||
|
MenuManager.Config.MissionOptionsMenuName
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Note: MENU_MISSION not created to avoid duplicate empty menu
|
||||||
|
-- Scripts that need mission-wide menus should use MENU_MISSION directly
|
||||||
|
|
||||||
|
if MenuManager.Config.Debug then
|
||||||
|
env.info("MenuManager: Initialized parent coalition menus")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a coalition menu under "Mission Options"
|
||||||
|
-- @param coalitionSide: coalition.side.BLUE or coalition.side.RED
|
||||||
|
-- @param menuName: Name of the menu
|
||||||
|
-- @param parentMenu: (Optional) If provided, creates as submenu of this parent instead of Mission Options
|
||||||
|
-- @return: MENU_COALITION object
|
||||||
|
function MenuManager.CreateCoalitionMenu(coalitionSide, menuName, parentMenu)
|
||||||
|
if MenuManager.Config.EnableMissionOptionsMenu and not parentMenu then
|
||||||
|
-- Create under Mission Options
|
||||||
|
local parent = (coalitionSide == coalition.side.BLUE)
|
||||||
|
and MenuManager.ParentMenus.BlueCoalition
|
||||||
|
or MenuManager.ParentMenus.RedCoalition
|
||||||
|
|
||||||
|
local menu = MENU_COALITION:New(coalitionSide, menuName, parent)
|
||||||
|
|
||||||
|
if MenuManager.Config.Debug then
|
||||||
|
local coalitionName = (coalitionSide == coalition.side.BLUE) and "BLUE" or "RED"
|
||||||
|
env.info(string.format("MenuManager: Created coalition menu '%s' for %s", menuName, coalitionName))
|
||||||
|
end
|
||||||
|
|
||||||
|
return menu
|
||||||
|
else
|
||||||
|
-- Create as root menu or under provided parent
|
||||||
|
local menu = MENU_COALITION:New(coalitionSide, menuName, parentMenu)
|
||||||
|
return menu
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a mission menu (not nested under Mission Options, as that causes duplicates)
|
||||||
|
-- @param menuName: Name of the menu
|
||||||
|
-- @param parentMenu: (Optional) Parent menu
|
||||||
|
-- @return: MENU_MISSION object
|
||||||
|
-- Note: Mission menus are visible to all players and cannot be nested under coalition menus
|
||||||
|
function MenuManager.CreateMissionMenu(menuName, parentMenu)
|
||||||
|
-- Always create as root menu or under provided parent
|
||||||
|
-- Mission menus can't be nested under coalition-specific "Mission Options"
|
||||||
|
local menu = MENU_MISSION:New(menuName, parentMenu)
|
||||||
|
|
||||||
|
if MenuManager.Config.Debug then
|
||||||
|
env.info(string.format("MenuManager: Created mission menu '%s'", menuName))
|
||||||
|
end
|
||||||
|
|
||||||
|
return menu
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper to disable the parent menu system at runtime
|
||||||
|
function MenuManager.DisableParentMenus()
|
||||||
|
MenuManager.Config.EnableMissionOptionsMenu = false
|
||||||
|
env.info("MenuManager: Parent menu system disabled")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper to enable the parent menu system at runtime
|
||||||
|
function MenuManager.EnableParentMenus()
|
||||||
|
MenuManager.Config.EnableMissionOptionsMenu = true
|
||||||
|
if not MenuManager.ParentMenus.BlueCoalition then
|
||||||
|
MenuManager.Initialize()
|
||||||
|
end
|
||||||
|
env.info("MenuManager: Parent menu system enabled")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Initialize on load
|
||||||
|
MenuManager.Initialize()
|
||||||
|
|
||||||
|
-- Announcement
|
||||||
|
env.info(string.format("MenuManager v%s loaded - Mission Options menu system ready", MenuManager.Version))
|
||||||
@@ -1,288 +1 @@
|
|||||||
# MOOSE Dual Coalition Zone Capture System
|
|
||||||
|
|
||||||
A dynamic zone capture and control system for DCS World missions using the MOOSE framework. This script enables territory-based gameplay where RED and BLUE coalitions compete to capture and hold strategic zones across the battlefield.
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
## 🎯 Features
|
|
||||||
|
|
||||||
- **🎨 Visual Feedback**: Color-coded zone boundaries (Red/Blue/Green/Orange) that change dynamically
|
|
||||||
- **💨 Smoke Signals**: Automatic smoke markers indicating zone status
|
|
||||||
- **📍 Tactical Information**: Real-time force composition and MGRS coordinates for enemies
|
|
||||||
- **🏆 Victory Conditions**: Automatic win detection when one coalition captures all zones
|
|
||||||
- **📻 F10 Radio Menu**: Player-accessible status reports and progress tracking
|
|
||||||
- **⚙️ Highly Configurable**: Simple zone ownership configuration via Lua tables
|
|
||||||
- **🔄 Dual Coalition**: Full support for both RED and BLUE coalitions
|
|
||||||
- **📊 Auto-Reporting**: Periodic status updates every 5 minutes
|
|
||||||
- **🎮 Player-Friendly**: Clear messaging and intuitive state transitions
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
1. **DCS World** (version 2.9 or higher)
|
|
||||||
2. **MOOSE Framework** ([Download here](https://github.com/FlightControl-Master/MOOSE))
|
|
||||||
3. Basic knowledge of DCS Mission Editor
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
1. **Download the files:**
|
|
||||||
- `Moose_DualCoalitionZoneCapture.lua` - Main script
|
|
||||||
- `Moose_DualCoalitionZoneCapture.miz` - Example mission
|
|
||||||
- `Moose_.lua` - MOOSE framework (get latest version)
|
|
||||||
|
|
||||||
2. **In DCS Mission Editor:**
|
|
||||||
- Create trigger zones for each capture point (e.g., "Capture Zone-1", "Capture Severomorsk")
|
|
||||||
- Create two groups: `BLUEHQ` (any BLUE ground unit) and `REDHQ` (any RED ground unit)
|
|
||||||
|
|
||||||
3. **Configure zones** in `Moose_DualCoalitionZoneCapture.lua`:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local ZONE_CONFIG = {
|
|
||||||
RED = {
|
|
||||||
"Capture Zone-1",
|
|
||||||
"Capture Zone-2"
|
|
||||||
},
|
|
||||||
BLUE = {
|
|
||||||
"Capture Zone-3",
|
|
||||||
"Capture Zone-4"
|
|
||||||
},
|
|
||||||
NEUTRAL = {
|
|
||||||
-- Empty zones at mission start
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Load scripts** via Mission Start trigger:
|
|
||||||
- Action 1: DO SCRIPT FILE → `Moose_.lua`
|
|
||||||
- Action 2: DO SCRIPT FILE → `Moose_DualCoalitionZoneCapture.lua`
|
|
||||||
|
|
||||||
5. **Save and test** your mission!
|
|
||||||
|
|
||||||
## 📖 How It Works
|
|
||||||
|
|
||||||
### Zone States
|
|
||||||
|
|
||||||
Zones transition between four distinct states:
|
|
||||||
|
|
||||||
| State | Color | Smoke | Description |
|
|
||||||
|-------|-------|-------|-------------|
|
|
||||||
| **RED Controlled** | 🔴 Red Border | Red | Zone secured by RED coalition |
|
|
||||||
| **BLUE Controlled** | 🔵 Blue Border | Blue | Zone secured by BLUE coalition |
|
|
||||||
| **Neutral/Empty** | 🟢 Green Border | Green | Uncontrolled, ready for capture |
|
|
||||||
| **Contested** | 🟠 Orange Border | White | Multiple coalitions present - fighting for control |
|
|
||||||
|
|
||||||
### Capture Mechanics
|
|
||||||
|
|
||||||
- **To Capture**: Move ground units into a zone
|
|
||||||
- **To Hold**: Eliminate all enemy forces in the zone
|
|
||||||
- **To Win**: Capture ALL zones on the map
|
|
||||||
|
|
||||||
The script automatically scans zones every 30 seconds (configurable) and updates ownership based on unit presence.
|
|
||||||
|
|
||||||
### Tactical Information Markers
|
|
||||||
|
|
||||||
Each zone displays real-time tactical data:
|
|
||||||
|
|
||||||
```
|
|
||||||
TACTICAL: Capture Severomorsk-1
|
|
||||||
Forces: R:5 B:12
|
|
||||||
TGTS: T-90@38U LV 12345 67890, BTR-80@38U LV 12346 67891
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Force Counts**: Number of units per coalition
|
|
||||||
- **MGRS Coordinates**: Precise enemy locations (when ≤10 units)
|
|
||||||
- **Coalition-Specific**: Each side sees their enemies marked
|
|
||||||
|
|
||||||
## ⚙️ Configuration Options
|
|
||||||
|
|
||||||
### Zone Settings
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local ZONE_SETTINGS = {
|
|
||||||
guardDelay = 1, -- Seconds before entering Guard state after capture
|
|
||||||
scanInterval = 30, -- How often to scan for units (seconds)
|
|
||||||
captureScore = 200 -- Points awarded for zone capture
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Performance Tuning
|
|
||||||
|
|
||||||
For missions with many units:
|
|
||||||
```lua
|
|
||||||
scanInterval = 60 -- Scan less frequently
|
|
||||||
```
|
|
||||||
|
|
||||||
For fast-paced action:
|
|
||||||
```lua
|
|
||||||
scanInterval = 15 -- More responsive zone changes
|
|
||||||
```
|
|
||||||
|
|
||||||
### Logging Control
|
|
||||||
|
|
||||||
Disable detailed logging:
|
|
||||||
```lua
|
|
||||||
CAPTURE_ZONE_LOGGING = { enabled = false }
|
|
||||||
```
|
|
||||||
|
|
||||||
## 👥 Player Features
|
|
||||||
|
|
||||||
### F10 Radio Menu Commands
|
|
||||||
|
|
||||||
Players access zone information via **F10 → Zone Control**:
|
|
||||||
|
|
||||||
- **Get Zone Status Report**: Current ownership of all zones
|
|
||||||
- **Check Victory Progress**: Percentage toward victory
|
|
||||||
- **Refresh Zone Colors**: Manually redraw zone boundaries
|
|
||||||
|
|
||||||
### Automatic Notifications
|
|
||||||
|
|
||||||
- ✅ Zone capture/loss announcements
|
|
||||||
- ⚠️ Attack warnings when zones are contested
|
|
||||||
- 📊 Status reports every 5 minutes
|
|
||||||
- 🏆 Victory alerts at 80% and 100% completion
|
|
||||||
- 🎉 Victory countdown with celebratory effects
|
|
||||||
|
|
||||||
## 🎮 Example Mission
|
|
||||||
|
|
||||||
The included `Moose_DualCoalitionZoneCapture.miz` demonstrates:
|
|
||||||
|
|
||||||
- Proper zone configuration
|
|
||||||
- HQ group placement
|
|
||||||
- Script loading order
|
|
||||||
- AI patrol patterns for testing
|
|
||||||
- All visual and messaging features
|
|
||||||
|
|
||||||
**Use this mission as a template for your own scenarios!**
|
|
||||||
|
|
||||||
## 🔧 Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
#### ❌ Script Won't Load
|
|
||||||
**Error**: "attempt to index a nil value"
|
|
||||||
- **Cause**: MOOSE not loaded first
|
|
||||||
- **Fix**: Ensure load order is MOOSE → Capture Script
|
|
||||||
|
|
||||||
#### ❌ Zone Not Found
|
|
||||||
**Error**: "Zone 'X' not found in mission editor!"
|
|
||||||
- **Cause**: Zone name mismatch
|
|
||||||
- **Fix**: Verify zone names match EXACTLY (case-sensitive!)
|
|
||||||
|
|
||||||
#### ⚠️ Zones Not Capturing
|
|
||||||
- Only ground units, planes, and helicopters are scanned
|
|
||||||
- Wait 30 seconds for scan cycle
|
|
||||||
- Eliminate ALL enemy forces to capture
|
|
||||||
- Check DCS.log for detailed information
|
|
||||||
|
|
||||||
### Checking Logs
|
|
||||||
|
|
||||||
Open `Saved Games\DCS\Logs\DCS.log` and search for:
|
|
||||||
- `[CAPTURE Module]` - General logging
|
|
||||||
- `[INIT]` - Initialization messages
|
|
||||||
- `[TACTICAL]` - Tactical marker updates
|
|
||||||
- `[VICTORY]` - Victory condition checks
|
|
||||||
|
|
||||||
## 🏗️ Mission Design Tips
|
|
||||||
|
|
||||||
### Best Practices
|
|
||||||
|
|
||||||
- **Zone Size**: Large enough for tactical areas, avoid overlaps
|
|
||||||
- **Zone Placement**: Position over airbases, FOBs, strategic terrain
|
|
||||||
- **Starting Balance**: Consider defensive vs. offensive scenarios
|
|
||||||
- **AI Behavior**: Use "Ground Hold" or "Ground On Road" waypoints
|
|
||||||
- **Player Briefing**: Document F10 menu commands in mission brief
|
|
||||||
|
|
||||||
### Integration with Other Scripts
|
|
||||||
|
|
||||||
Access zone data from other scripts:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- Get current ownership status
|
|
||||||
local status = GetZoneOwnershipStatus()
|
|
||||||
-- Returns: { blue = X, red = Y, neutral = Z, total = N, zones = {...} }
|
|
||||||
|
|
||||||
-- Manual status broadcast
|
|
||||||
BroadcastZoneStatus()
|
|
||||||
|
|
||||||
-- Refresh zone visuals
|
|
||||||
RefreshAllZoneColors()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Victory Flags
|
|
||||||
|
|
||||||
The script sets user flags on victory:
|
|
||||||
- `BLUE_VICTORY = 1` when BLUE wins
|
|
||||||
- `RED_VICTORY = 1` when RED wins
|
|
||||||
|
|
||||||
Use these in triggers to end missions or transition to next phase.
|
|
||||||
|
|
||||||
## 📋 Requirements
|
|
||||||
|
|
||||||
### Essential Components
|
|
||||||
|
|
||||||
- ✅ DCS World 2.9 or higher
|
|
||||||
- ✅ MOOSE Framework (latest version)
|
|
||||||
- ✅ Trigger zones in mission editor
|
|
||||||
- ✅ BLUEHQ and REDHQ groups
|
|
||||||
|
|
||||||
### Mission Prerequisites
|
|
||||||
|
|
||||||
- At least one trigger zone per capture point
|
|
||||||
- Exact zone name matching between editor and Lua config
|
|
||||||
- Both HQ groups must exist (can be hidden/inactive)
|
|
||||||
|
|
||||||
## 📞 Support & Resources
|
|
||||||
|
|
||||||
### Get Help
|
|
||||||
|
|
||||||
- **Discord Community**: [https://discord.gg/7wBVWKK3](https://discord.gg/7wBVWKK3)
|
|
||||||
- **Author**: F99th-TracerFacer
|
|
||||||
- **GitHub Issues**: Report bugs or request features
|
|
||||||
|
|
||||||
### Additional Resources
|
|
||||||
|
|
||||||
- [MOOSE Documentation](https://flightcontrol-master.github.io/MOOSE_DOCS/)
|
|
||||||
- [MOOSE Discord](https://discord.gg/gj68fm969S)
|
|
||||||
- [DCS Forums](https://forum.dcs.world)
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
This script is provided free for use in DCS World missions. Feel free to modify and distribute.
|
|
||||||
|
|
||||||
## 🙏 Credits
|
|
||||||
|
|
||||||
- **Author**: F99th-TracerFacer
|
|
||||||
- **Framework**: MOOSE by FlightControl
|
|
||||||
- **Community**: DCS World Mission Makers
|
|
||||||
|
|
||||||
## 🎯 Version History
|
|
||||||
|
|
||||||
### Version 2.0 (Current)
|
|
||||||
- ✨ Full dual coalition support (RED & BLUE)
|
|
||||||
- ✨ Tactical information markers with MGRS coordinates
|
|
||||||
- ✨ Auto-victory detection and countdown
|
|
||||||
- ✨ F10 radio menu commands
|
|
||||||
- ✨ Periodic status reports
|
|
||||||
- ✨ Enhanced visual feedback system
|
|
||||||
- ✨ Configurable zone ownership via Lua tables
|
|
||||||
|
|
||||||
### Version 1.0
|
|
||||||
- Initial release
|
|
||||||
- Basic zone capture mechanics
|
|
||||||
- Single coalition focus
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
**🎮 Happy Mission Making! 🚁**
|
|
||||||
|
|
||||||
*Created with ❤️ for the DCS World Community*
|
|
||||||
|
|
||||||
[Discord](https://discord.gg/kTNmMScQNf) • [Documentation](Mission_Maker_Guide.html) • [Report Issue](#)
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user