mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
Created DynamicGroundBattle_Plugin - a fork from my DynamicGroundBattle script. Plugin as it's name implies plugs into the DualCoalitionZoneCapture scirpt seemlessly using the zones as the rally points for the dynamic ground battle.
This commit is contained in:
parent
d9d5c9c73a
commit
b79a8f9b0a
@ -217,6 +217,9 @@ local blueArmorTemplates = {
|
|||||||
-- DO NOT EDIT BELOW THIS LINE
|
-- DO NOT EDIT BELOW THIS LINE
|
||||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Track active markers to prevent memory leaks
|
||||||
|
local activeMarkers = {}
|
||||||
|
|
||||||
-- Function to add mark points on the map for each warehouse in the provided list
|
-- Function to add mark points on the map for each warehouse in the provided list
|
||||||
local function addMarkPoints(warehouses, coalition)
|
local function addMarkPoints(warehouses, coalition)
|
||||||
for _, warehouse in ipairs(warehouses) do
|
for _, warehouse in ipairs(warehouses) do
|
||||||
@ -239,7 +242,7 @@ local function addMarkPoints(warehouses, coalition)
|
|||||||
|
|
||||||
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()
|
||||||
marker:Remove(UPDATE_MARK_POINTS_SCHED)
|
table.insert(activeMarkers, marker)
|
||||||
else
|
else
|
||||||
env.info("addMarkPoints: Warehouse not found or is nil")
|
env.info("addMarkPoints: Warehouse not found or is nil")
|
||||||
end
|
end
|
||||||
@ -247,6 +250,14 @@ local function addMarkPoints(warehouses, coalition)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function updateMarkPoints()
|
local function updateMarkPoints()
|
||||||
|
-- Clean up old markers first
|
||||||
|
for _, marker in ipairs(activeMarkers) do
|
||||||
|
if marker then
|
||||||
|
marker:Remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
activeMarkers = {}
|
||||||
|
|
||||||
addMarkPoints(redWarehouses, 2) -- Blue coalition sees red warehouses as targets
|
addMarkPoints(redWarehouses, 2) -- Blue coalition sees red warehouses as targets
|
||||||
addMarkPoints(blueWarehouses, 2) -- Blue coalition sees blue warehouses as needing protection
|
addMarkPoints(blueWarehouses, 2) -- Blue coalition sees blue warehouses as needing protection
|
||||||
addMarkPoints(redWarehouses, 1) -- Red coalition sees red warehouses as needing protection
|
addMarkPoints(redWarehouses, 1) -- Red coalition sees red warehouses as needing protection
|
||||||
@ -261,6 +272,15 @@ end
|
|||||||
-- Table to keep track of zones and their statuses
|
-- Table to keep track of zones and their statuses
|
||||||
local zoneStatuses = {}
|
local zoneStatuses = {}
|
||||||
|
|
||||||
|
-- Reusable SET_GROUP to prevent memory leaks from repeated creation
|
||||||
|
local cachedAllGroups = nil
|
||||||
|
local function getAllGroups()
|
||||||
|
if not cachedAllGroups then
|
||||||
|
cachedAllGroups = SET_GROUP:New():FilterActive():FilterStart()
|
||||||
|
end
|
||||||
|
return cachedAllGroups
|
||||||
|
end
|
||||||
|
|
||||||
-- Function to create a capture zone
|
-- Function to create a capture zone
|
||||||
local function CreateCaptureZone(zone, coalition)
|
local function CreateCaptureZone(zone, coalition)
|
||||||
local captureZone = ZONE_CAPTURE_COALITION:New(zone, coalition)
|
local captureZone = ZONE_CAPTURE_COALITION:New(zone, coalition)
|
||||||
@ -516,7 +536,7 @@ local function CheckZoneStates()
|
|||||||
env.info("Processing " .. zoneType)
|
env.info("Processing " .. zoneType)
|
||||||
env.info("Number of zones: " .. #zones)
|
env.info("Number of zones: " .. #zones)
|
||||||
|
|
||||||
local allGroups = SET_GROUP:New():FilterActive():FilterStart()
|
local allGroups = getAllGroups()
|
||||||
|
|
||||||
for _, zone in ipairs(zones) do
|
for _, zone in ipairs(zones) do
|
||||||
if zone then
|
if zone then
|
||||||
@ -671,7 +691,7 @@ end
|
|||||||
local function AssignTasksToGroups()
|
local function AssignTasksToGroups()
|
||||||
env.info("AssignTasksToGroups: Starting task assignments")
|
env.info("AssignTasksToGroups: Starting task assignments")
|
||||||
local zoneStates = CheckZoneStates()
|
local zoneStates = CheckZoneStates()
|
||||||
local allGroups = SET_GROUP:New():FilterActive():FilterStart()
|
local allGroups = getAllGroups()
|
||||||
|
|
||||||
local function processZone(zone, zoneColor)
|
local function processZone(zone, zoneColor)
|
||||||
if zone then
|
if zone then
|
||||||
@ -871,6 +891,7 @@ local function MonitorWarehouses()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Function to check the wincondition. If either side owns all zones, mission ends.
|
-- Function to check the wincondition. If either side owns all zones, mission ends.
|
||||||
|
local winConditionScheduler = nil
|
||||||
local function checkWinCondition()
|
local function checkWinCondition()
|
||||||
local blueOwned = true
|
local blueOwned = true
|
||||||
local redOwned = true
|
local redOwned = true
|
||||||
@ -887,26 +908,24 @@ local function checkWinCondition()
|
|||||||
if blueOwned then
|
if blueOwned then
|
||||||
MESSAGE:New("Blue side wins! They own all the capture zones.", 60):ToAll()
|
MESSAGE:New("Blue side wins! They own all the capture zones.", 60):ToAll()
|
||||||
SOUND:New("UsaTheme.ogg"):ToAll()
|
SOUND:New("UsaTheme.ogg"):ToAll()
|
||||||
|
if winConditionScheduler then
|
||||||
|
winConditionScheduler:Stop()
|
||||||
|
end
|
||||||
return true
|
return true
|
||||||
elseif redOwned then
|
elseif redOwned then
|
||||||
MESSAGE:New("Red side wins! They own all the capture zones.", 60):ToAll()
|
MESSAGE:New("Red side wins! They own all the capture zones.", 60):ToAll()
|
||||||
SOUND:New("MotherRussia.ogg"):ToAll()
|
SOUND:New("MotherRussia.ogg"):ToAll()
|
||||||
|
if winConditionScheduler then
|
||||||
|
winConditionScheduler:Stop()
|
||||||
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Timer function to periodically check the win condition
|
-- Start monitoring the win condition with SCHEDULER instead of recursive TIMER
|
||||||
local function monitorWinCondition()
|
winConditionScheduler = SCHEDULER:New(nil, checkWinCondition, {}, 60, 60)
|
||||||
if not checkWinCondition() then
|
|
||||||
-- Schedule the next check in 60 seconds
|
|
||||||
TIMER:New(monitorWinCondition):Start(60)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Start monitoring the win condition
|
|
||||||
monitorWinCondition()
|
|
||||||
|
|
||||||
-- Scheduler to monitor warehouses every 120 seconds
|
-- Scheduler to monitor warehouses every 120 seconds
|
||||||
SCHEDULER:New(nil, MonitorWarehouses, {}, 0, 120)
|
SCHEDULER:New(nil, MonitorWarehouses, {}, 0, 120)
|
||||||
|
|||||||
502
Moose_DynamicGroundBattle/Moose_DynamicGroundBattle_Plugin.lua
Normal file
502
Moose_DynamicGroundBattle/Moose_DynamicGroundBattle_Plugin.lua
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
--[[
|
||||||
|
Script: Moose_DynamicGroundBattle_Plugin.lua
|
||||||
|
Written by: [F99th-TracerFacer]
|
||||||
|
Version: 1.0.0
|
||||||
|
Date: 15 November 2024
|
||||||
|
Description: Warehouse-driven ground unit spawning system that works as a plugin
|
||||||
|
with Moose_DualCoalitionZoneCapture.lua
|
||||||
|
|
||||||
|
This script handles:
|
||||||
|
- Warehouse-based reinforcement system
|
||||||
|
- Dynamic spawn frequency based on warehouse survival
|
||||||
|
- Automated AI tasking to patrol nearest enemy zones
|
||||||
|
- Optional infantry patrol control
|
||||||
|
- Warehouse status intel markers
|
||||||
|
- CTLD troop integration
|
||||||
|
|
||||||
|
What this script DOES NOT do:
|
||||||
|
- Zone capture logic (handled by Moose_DualCoalitionZoneCapture.lua)
|
||||||
|
- Win conditions (handled by Moose_DualCoalitionZoneCapture.lua)
|
||||||
|
- Zone coloring/messaging (handled by Moose_DualCoalitionZoneCapture.lua)
|
||||||
|
|
||||||
|
Load Order (in Mission Editor Triggers):
|
||||||
|
1. DO SCRIPT FILE Moose_.lua
|
||||||
|
2. DO SCRIPT FILE Moose_DualCoalitionZoneCapture.lua
|
||||||
|
3. DO SCRIPT FILE Moose_DynamicGroundBattle_Plugin.lua <-- This file
|
||||||
|
4. DO SCRIPT FILE CTLD.lua (optional)
|
||||||
|
5. DO SCRIPT FILE CSAR.lua (optional)
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- MOOSE framework must be loaded first
|
||||||
|
- Moose_DualCoalitionZoneCapture.lua must be loaded BEFORE this script
|
||||||
|
- Zone configuration comes from DualCoalitionZoneCapture's ZONE_CONFIG
|
||||||
|
- Groups and warehouses must exist in mission editor (see below)
|
||||||
|
|
||||||
|
Warehouse System & Spawn Frequency Behavior:
|
||||||
|
1. Each side has warehouses defined in `redWarehouses` and `blueWarehouses` tables
|
||||||
|
2. Spawn frequency dynamically adjusts based on alive warehouses:
|
||||||
|
- 100% alive = 100% spawn rate (base frequency)
|
||||||
|
- 50% alive = 50% spawn rate (2x delay)
|
||||||
|
- 0% alive = no spawns (critical attrition)
|
||||||
|
3. Map markers show warehouse locations and nearby units
|
||||||
|
4. Updated every UPDATE_MARK_POINTS_SCHED seconds
|
||||||
|
|
||||||
|
AI Task Assignment:
|
||||||
|
- Groups spawn in friendly zones, then patrol toward nearest enemy zone
|
||||||
|
- Reassignment occurs every ASSIGN_TASKS_SCHED seconds
|
||||||
|
- Only stationary units get new orders (moving units are left alone)
|
||||||
|
- CTLD-dropped troops automatically integrate
|
||||||
|
|
||||||
|
Groups to Create in Mission Editor (all LATE ACTIVATE):
|
||||||
|
RED SIDE:
|
||||||
|
- Infantry Templates: RedInfantry1, RedInfantry2, RedInfantry3, RedInfantry4, RedInfantry5, RedInfantry6
|
||||||
|
- Armor Templates: RedArmor1, RedArmor2, RedArmor3, RedArmor4, RedArmor5, RedArmor6
|
||||||
|
- Warehouses (Static Objects): RedWarehouse1-1, RedWarehouse2-1, RedWarehouse3-1, etc.
|
||||||
|
|
||||||
|
BLUE SIDE:
|
||||||
|
- Infantry Templates: BlueInfantry1, BlueInfantry2, BlueInfantry3, BlueInfantry4, BlueInfantry5, BlueInfantry6
|
||||||
|
- Armor Templates: BlueArmor1, BlueArmor2, BlueArmor3, BlueArmor4, BlueArmor5
|
||||||
|
- Warehouses (Static Objects): BlueWarehouse1-1, BlueWarehouse2-1, BlueWarehouse3-1, etc.
|
||||||
|
|
||||||
|
NOTE: Warehouse names use the static "Unit Name" in mission editor, not the "Name" field!
|
||||||
|
|
||||||
|
Integration with DualCoalitionZoneCapture:
|
||||||
|
- This script reads zoneCaptureObjects and zoneNames from DualCoalitionZoneCapture
|
||||||
|
- Spawns occur in zones controlled by the appropriate coalition
|
||||||
|
- AI tasks units to patrol zones from DualCoalitionZoneCapture's ZONE_CONFIG
|
||||||
|
--]]
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- USER CONFIGURATION SECTION
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Infantry Patrol Settings
|
||||||
|
local MOVING_INFANTRY_PATROLS = false -- Set to false to disable infantry movement (they spawn and hold position)
|
||||||
|
|
||||||
|
-- Warehouse Marker Settings
|
||||||
|
local ENABLE_WAREHOUSE_MARKERS = true -- Enable/disable warehouse map markers (disabled by default if you have other marker systems)
|
||||||
|
local UPDATE_MARK_POINTS_SCHED = 300 -- Update warehouse markers every 300 seconds (5 minutes)
|
||||||
|
local MAX_WAREHOUSE_UNIT_LIST_DISTANCE = 5000 -- Max distance to search for units near warehouses for markers
|
||||||
|
|
||||||
|
-- Spawn Frequency and Limits
|
||||||
|
local INIT_RED_INFANTRY = 5 -- Initial number of Red Infantry groups
|
||||||
|
local MAX_RED_INFANTRY = 100 -- Maximum number of Red Infantry groups
|
||||||
|
local SPAWN_SCHED_RED_INFANTRY = 1800 -- Base spawn frequency for Red Infantry (seconds)
|
||||||
|
|
||||||
|
local INIT_RED_ARMOR = 25 -- Initial number of Red Armor groups
|
||||||
|
local MAX_RED_ARMOR = 200 -- Maximum number of Red Armor groups
|
||||||
|
local SPAWN_SCHED_RED_ARMOR = 300 -- Base spawn frequency for Red Armor (seconds)
|
||||||
|
|
||||||
|
local INIT_BLUE_INFANTRY = 5 -- Initial number of Blue Infantry groups
|
||||||
|
local MAX_BLUE_INFANTRY = 100 -- Maximum number of Blue Infantry groups
|
||||||
|
local SPAWN_SCHED_BLUE_INFANTRY = 1800 -- Base spawn frequency for Blue Infantry (seconds)
|
||||||
|
|
||||||
|
local INIT_BLUE_ARMOR = 25 -- Initial number of Blue Armor groups
|
||||||
|
local MAX_BLUE_ARMOR = 200 -- Maximum number of Blue Armor groups
|
||||||
|
local SPAWN_SCHED_BLUE_ARMOR = 300 -- Base spawn frequency for Blue Armor (seconds)
|
||||||
|
|
||||||
|
local ASSIGN_TASKS_SCHED = 600 -- How often to reassign tasks to idle groups (seconds)
|
||||||
|
|
||||||
|
-- Define warehouses for each side
|
||||||
|
local redWarehouses = {
|
||||||
|
STATIC:FindByName("RedWarehouse1-1"),
|
||||||
|
STATIC:FindByName("RedWarehouse2-1"),
|
||||||
|
STATIC:FindByName("RedWarehouse3-1"),
|
||||||
|
STATIC:FindByName("RedWarehouse4-1"),
|
||||||
|
STATIC:FindByName("RedWarehouse5-1"),
|
||||||
|
STATIC:FindByName("RedWarehouse6-1")
|
||||||
|
}
|
||||||
|
|
||||||
|
local blueWarehouses = {
|
||||||
|
STATIC:FindByName("BlueWarehouse1-1"),
|
||||||
|
STATIC:FindByName("BlueWarehouse2-1"),
|
||||||
|
STATIC:FindByName("BlueWarehouse3-1"),
|
||||||
|
STATIC:FindByName("BlueWarehouse4-1"),
|
||||||
|
STATIC:FindByName("BlueWarehouse5-1"),
|
||||||
|
STATIC:FindByName("BlueWarehouse6-1")
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Define unit templates (these groups must exist in mission editor as LATE ACTIVATE)
|
||||||
|
local redInfantryTemplates = {
|
||||||
|
"RedInfantry1",
|
||||||
|
"RedInfantry2",
|
||||||
|
"RedInfantry3",
|
||||||
|
"RedInfantry4",
|
||||||
|
"RedInfantry5",
|
||||||
|
"RedInfantry6"
|
||||||
|
}
|
||||||
|
|
||||||
|
local redArmorTemplates = {
|
||||||
|
"RedArmor1",
|
||||||
|
"RedArmor2",
|
||||||
|
"RedArmor3",
|
||||||
|
"RedArmor4",
|
||||||
|
"RedArmor5",
|
||||||
|
"RedArmor6"
|
||||||
|
}
|
||||||
|
|
||||||
|
local blueInfantryTemplates = {
|
||||||
|
"BlueInfantry1",
|
||||||
|
"BlueInfantry2",
|
||||||
|
"BlueInfantry3",
|
||||||
|
"BlueInfantry4",
|
||||||
|
"BlueInfantry5",
|
||||||
|
"BlueInfantry6"
|
||||||
|
}
|
||||||
|
|
||||||
|
local blueArmorTemplates = {
|
||||||
|
"BlueArmor1",
|
||||||
|
"BlueArmor2",
|
||||||
|
"BlueArmor3",
|
||||||
|
"BlueArmor4",
|
||||||
|
"BlueArmor5"
|
||||||
|
}
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- DO NOT EDIT BELOW THIS LINE
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
env.info("[DGB PLUGIN] Dynamic Ground Battle Plugin initializing...")
|
||||||
|
|
||||||
|
-- Validate that DualCoalitionZoneCapture is loaded
|
||||||
|
if not zoneCaptureObjects or not zoneNames then
|
||||||
|
env.error("[DGB PLUGIN] ERROR: Moose_DualCoalitionZoneCapture.lua must be loaded BEFORE this plugin!")
|
||||||
|
env.error("[DGB PLUGIN] Make sure zoneCaptureObjects and zoneNames are available.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("[DGB PLUGIN] Found " .. #zoneCaptureObjects .. " zones from DualCoalitionZoneCapture")
|
||||||
|
|
||||||
|
-- Track active markers to prevent memory leaks
|
||||||
|
local activeMarkers = {}
|
||||||
|
|
||||||
|
-- Reusable SET_GROUP to prevent memory leaks from repeated creation
|
||||||
|
local cachedAllGroups = nil
|
||||||
|
local function getAllGroups()
|
||||||
|
if not cachedAllGroups then
|
||||||
|
cachedAllGroups = SET_GROUP:New():FilterActive():FilterStart()
|
||||||
|
env.info("[DGB PLUGIN] Created cached SET_GROUP for performance")
|
||||||
|
end
|
||||||
|
return cachedAllGroups
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to get zones controlled by a specific coalition
|
||||||
|
local function GetZonesByCoalition(targetCoalition)
|
||||||
|
local zones = {}
|
||||||
|
|
||||||
|
for idx, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||||
|
if zoneCapture and zoneCapture:GetCoalition() == targetCoalition then
|
||||||
|
local zone = zoneCapture:GetZone()
|
||||||
|
if zone then
|
||||||
|
table.insert(zones, zone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("[DGB PLUGIN] Found %d zones for coalition %d", #zones, targetCoalition))
|
||||||
|
return zones
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to calculate spawn frequency based on warehouse survival
|
||||||
|
local function CalculateSpawnFrequency(warehouses, baseFrequency)
|
||||||
|
local totalWarehouses = #warehouses
|
||||||
|
local aliveWarehouses = 0
|
||||||
|
|
||||||
|
for _, warehouse in ipairs(warehouses) do
|
||||||
|
if warehouse then
|
||||||
|
local life = warehouse:GetLife()
|
||||||
|
if life and life > 0 then
|
||||||
|
aliveWarehouses = aliveWarehouses + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if totalWarehouses == 0 or aliveWarehouses == 0 then
|
||||||
|
return math.huge -- Stop spawning if no warehouses remain
|
||||||
|
end
|
||||||
|
|
||||||
|
local frequency = baseFrequency * (totalWarehouses / aliveWarehouses)
|
||||||
|
return frequency
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to calculate spawn frequency as a percentage
|
||||||
|
local function CalculateSpawnFrequencyPercentage(warehouses)
|
||||||
|
local totalWarehouses = #warehouses
|
||||||
|
local aliveWarehouses = 0
|
||||||
|
|
||||||
|
for _, warehouse in ipairs(warehouses) do
|
||||||
|
if warehouse then
|
||||||
|
local life = warehouse:GetLife()
|
||||||
|
if life and life > 0 then
|
||||||
|
aliveWarehouses = aliveWarehouses + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if totalWarehouses == 0 then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local percentage = (aliveWarehouses / totalWarehouses) * 100
|
||||||
|
return math.floor(percentage)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to add warehouse markers on the map
|
||||||
|
local function addMarkPoints(warehouses, coalition)
|
||||||
|
for _, warehouse in ipairs(warehouses) do
|
||||||
|
if warehouse then
|
||||||
|
local warehousePos = warehouse:GetVec3()
|
||||||
|
local details
|
||||||
|
|
||||||
|
if coalition == 2 then -- Blue viewing
|
||||||
|
if warehouse:GetCoalition() == 2 then
|
||||||
|
details = "Warehouse: " .. warehouse:GetName() .. "\nThis warehouse needs to be protected.\n"
|
||||||
|
else
|
||||||
|
details = "Warehouse: " .. warehouse:GetName() .. "\nThis is a primary target as it is directly supplying enemy units.\n"
|
||||||
|
end
|
||||||
|
elseif coalition == 1 then -- Red viewing
|
||||||
|
if warehouse:GetCoalition() == 1 then
|
||||||
|
details = "Warehouse: " .. warehouse:GetName() .. "\nThis warehouse needs to be protected.\n"
|
||||||
|
else
|
||||||
|
details = "Warehouse: " .. warehouse:GetName() .. "\nThis is a primary target as it is directly supplying enemy units.\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local coordinate = COORDINATE:NewFromVec3(warehousePos)
|
||||||
|
local marker = MARKER:New(coordinate, details):ToCoalition(coalition):ReadOnly()
|
||||||
|
table.insert(activeMarkers, marker)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to update warehouse markers
|
||||||
|
local function updateMarkPoints()
|
||||||
|
-- Clean up old markers first
|
||||||
|
for _, marker in ipairs(activeMarkers) do
|
||||||
|
if marker then
|
||||||
|
marker:Remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
activeMarkers = {}
|
||||||
|
|
||||||
|
addMarkPoints(redWarehouses, 2) -- Blue coalition sees red warehouses
|
||||||
|
addMarkPoints(blueWarehouses, 2) -- Blue coalition sees blue warehouses
|
||||||
|
addMarkPoints(redWarehouses, 1) -- Red coalition sees red warehouses
|
||||||
|
addMarkPoints(blueWarehouses, 1) -- Red coalition sees blue warehouses
|
||||||
|
|
||||||
|
env.info("[DGB PLUGIN] Updated warehouse markers")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to check if a group contains infantry units
|
||||||
|
local function IsInfantryGroup(group)
|
||||||
|
for _, unit in ipairs(group:GetUnits()) do
|
||||||
|
local unitTypeName = unit:GetTypeName()
|
||||||
|
if unitTypeName:find("Infantry") or unitTypeName:find("Soldier") or unitTypeName:find("Paratrooper") then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to assign tasks to a group
|
||||||
|
local function AssignTasks(group)
|
||||||
|
if not group or not group.GetCoalition or not group.GetCoordinate or not group.GetVelocityVec3 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Don't reassign if already moving
|
||||||
|
local velocity = group:GetVelocityVec3()
|
||||||
|
local speed = math.sqrt(velocity.x^2 + velocity.y^2 + velocity.z^2)
|
||||||
|
if speed > 0.5 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local groupCoalition = group:GetCoalition()
|
||||||
|
local groupCoordinate = group:GetCoordinate()
|
||||||
|
local closestZone = nil
|
||||||
|
local closestDistance = math.huge
|
||||||
|
|
||||||
|
-- Find nearest enemy zone
|
||||||
|
for idx, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||||
|
local zoneCoalition = zoneCapture:GetCoalition()
|
||||||
|
|
||||||
|
if zoneCoalition ~= groupCoalition and zoneCoalition ~= coalition.side.NEUTRAL then
|
||||||
|
local zone = zoneCapture:GetZone()
|
||||||
|
if zone then
|
||||||
|
local zoneCoordinate = zone:GetCoordinate()
|
||||||
|
local distance = groupCoordinate:Get2DDistance(zoneCoordinate)
|
||||||
|
|
||||||
|
if distance < closestDistance then
|
||||||
|
closestDistance = distance
|
||||||
|
closestZone = zone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if closestZone then
|
||||||
|
env.info(string.format("[DGB PLUGIN] %s patrolling %s", group:GetName(), closestZone:GetName()))
|
||||||
|
group:PatrolZones({closestZone}, 20, "Cone", 30, 60)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to assign tasks to all groups
|
||||||
|
local function AssignTasksToGroups()
|
||||||
|
env.info("[DGB PLUGIN] Starting task assignment cycle...")
|
||||||
|
local allGroups = getAllGroups()
|
||||||
|
local tasksAssigned = 0
|
||||||
|
|
||||||
|
allGroups:ForEachGroup(function(group)
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
-- Check if group is in a friendly zone
|
||||||
|
local groupCoalition = group:GetCoalition()
|
||||||
|
local inFriendlyZone = false
|
||||||
|
|
||||||
|
for idx, zoneCapture in ipairs(zoneCaptureObjects) do
|
||||||
|
if zoneCapture:GetCoalition() == groupCoalition then
|
||||||
|
local zone = zoneCapture:GetZone()
|
||||||
|
if zone and group:IsCompletelyInZone(zone) then
|
||||||
|
inFriendlyZone = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if inFriendlyZone then
|
||||||
|
-- Skip infantry if movement is disabled
|
||||||
|
if IsInfantryGroup(group) and not MOVING_INFANTRY_PATROLS then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
AssignTasks(group)
|
||||||
|
tasksAssigned = tasksAssigned + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
env.info(string.format("[DGB PLUGIN] Task assignment complete. %d groups tasked.", tasksAssigned))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to monitor and announce warehouse status
|
||||||
|
local function MonitorWarehouses()
|
||||||
|
local blueWarehousesAlive = 0
|
||||||
|
local redWarehousesAlive = 0
|
||||||
|
|
||||||
|
for _, warehouse in ipairs(blueWarehouses) do
|
||||||
|
if warehouse and warehouse:IsAlive() then
|
||||||
|
blueWarehousesAlive = blueWarehousesAlive + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, warehouse in ipairs(redWarehouses) do
|
||||||
|
if warehouse and warehouse:IsAlive() then
|
||||||
|
redWarehousesAlive = redWarehousesAlive + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local redSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(redWarehouses)
|
||||||
|
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
||||||
|
|
||||||
|
local msg = "[Warehouse Status]\n"
|
||||||
|
msg = msg .. "Red warehouses alive: " .. redWarehousesAlive .. " Reinforcements: " .. redSpawnFrequencyPercentage .. "%\n"
|
||||||
|
msg = msg .. "Blue warehouses alive: " .. blueWarehousesAlive .. " Reinforcements: " .. blueSpawnFrequencyPercentage .. "%\n"
|
||||||
|
MESSAGE:New(msg, 30):ToAll()
|
||||||
|
|
||||||
|
env.info(string.format("[DGB PLUGIN] Warehouse status - Red: %d/%d (%d%%), Blue: %d/%d (%d%%)",
|
||||||
|
redWarehousesAlive, #redWarehouses, redSpawnFrequencyPercentage,
|
||||||
|
blueWarehousesAlive, #blueWarehouses, blueSpawnFrequencyPercentage))
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- INITIALIZATION
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Get initial zone lists for each coalition
|
||||||
|
local redZones = GetZonesByCoalition(coalition.side.RED)
|
||||||
|
local blueZones = GetZonesByCoalition(coalition.side.BLUE)
|
||||||
|
|
||||||
|
-- Calculate initial spawn frequencies
|
||||||
|
local redInfantrySpawnFrequency = CalculateSpawnFrequency(redWarehouses, SPAWN_SCHED_RED_INFANTRY)
|
||||||
|
local redArmorSpawnFrequency = CalculateSpawnFrequency(redWarehouses, SPAWN_SCHED_RED_ARMOR)
|
||||||
|
local blueInfantrySpawnFrequency = CalculateSpawnFrequency(blueWarehouses, SPAWN_SCHED_BLUE_INFANTRY)
|
||||||
|
local blueArmorSpawnFrequency = CalculateSpawnFrequency(blueWarehouses, SPAWN_SCHED_BLUE_ARMOR)
|
||||||
|
|
||||||
|
-- Calculate and display initial spawn frequency percentages
|
||||||
|
local redSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(redWarehouses)
|
||||||
|
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
||||||
|
|
||||||
|
MESSAGE:New("Red reinforcement capacity: " .. redSpawnFrequencyPercentage .. "%", 30):ToRed()
|
||||||
|
MESSAGE:New("Blue reinforcement capacity: " .. blueSpawnFrequencyPercentage .. "%", 30):ToBlue()
|
||||||
|
|
||||||
|
-- Initialize spawners
|
||||||
|
env.info("[DGB PLUGIN] Initializing spawn systems...")
|
||||||
|
|
||||||
|
-- Note: Spawn zones will be dynamically updated based on zone capture states
|
||||||
|
-- We'll use a function to get current friendly zones on each spawn
|
||||||
|
local function GetRedZones()
|
||||||
|
return GetZonesByCoalition(coalition.side.RED)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function GetBlueZones()
|
||||||
|
return GetZonesByCoalition(coalition.side.BLUE)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Red Infantry Spawner
|
||||||
|
redInfantrySpawn = SPAWN:New("RedInfantryGroup")
|
||||||
|
:InitRandomizeTemplate(redInfantryTemplates)
|
||||||
|
:InitLimit(INIT_RED_INFANTRY, MAX_RED_INFANTRY)
|
||||||
|
|
||||||
|
-- Red Armor Spawner
|
||||||
|
redArmorSpawn = SPAWN:New("RedArmorGroup")
|
||||||
|
:InitRandomizeTemplate(redArmorTemplates)
|
||||||
|
:InitLimit(INIT_RED_ARMOR, MAX_RED_ARMOR)
|
||||||
|
|
||||||
|
-- Blue Infantry Spawner
|
||||||
|
blueInfantrySpawn = SPAWN:New("BlueInfantryGroup")
|
||||||
|
:InitRandomizeTemplate(blueInfantryTemplates)
|
||||||
|
:InitLimit(INIT_BLUE_INFANTRY, MAX_BLUE_INFANTRY)
|
||||||
|
|
||||||
|
-- Blue Armor Spawner
|
||||||
|
blueArmorSpawn = SPAWN:New("BlueArmorGroup")
|
||||||
|
:InitRandomizeTemplate(blueArmorTemplates)
|
||||||
|
:InitLimit(INIT_BLUE_ARMOR, MAX_BLUE_ARMOR)
|
||||||
|
|
||||||
|
-- Custom spawn function that updates zones dynamically
|
||||||
|
local function SpawnWithDynamicZones()
|
||||||
|
local currentRedZones = GetRedZones()
|
||||||
|
local currentBlueZones = GetBlueZones()
|
||||||
|
|
||||||
|
if #currentRedZones > 0 then
|
||||||
|
local randomRedZone = currentRedZones[math.random(#currentRedZones)]
|
||||||
|
redInfantrySpawn:SpawnInZone(randomRedZone, false)
|
||||||
|
redArmorSpawn:SpawnInZone(randomRedZone, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #currentBlueZones > 0 then
|
||||||
|
local randomBlueZone = currentBlueZones[math.random(#currentBlueZones)]
|
||||||
|
blueInfantrySpawn:SpawnInZone(randomBlueZone, false)
|
||||||
|
blueArmorSpawn:SpawnInZone(randomBlueZone, false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Schedule spawns
|
||||||
|
SCHEDULER:New(nil, SpawnWithDynamicZones, {}, 10, math.max(SPAWN_SCHED_RED_INFANTRY, SPAWN_SCHED_BLUE_INFANTRY))
|
||||||
|
|
||||||
|
-- Schedule warehouse marker updates
|
||||||
|
if ENABLE_WAREHOUSE_MARKERS then
|
||||||
|
SCHEDULER:New(nil, updateMarkPoints, {}, 10, UPDATE_MARK_POINTS_SCHED)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Schedule warehouse monitoring
|
||||||
|
SCHEDULER:New(nil, MonitorWarehouses, {}, 30, 120)
|
||||||
|
|
||||||
|
-- Schedule task assignments
|
||||||
|
SCHEDULER:New(nil, AssignTasksToGroups, {}, 120, ASSIGN_TASKS_SCHED)
|
||||||
|
|
||||||
|
-- Add F10 menu for manual checks
|
||||||
|
local missionMenu = MENU_MISSION:New("Ground Battle")
|
||||||
|
MENU_MISSION_COMMAND:New("Check Warehouse Status", missionMenu, MonitorWarehouses)
|
||||||
|
|
||||||
|
env.info("[DGB PLUGIN] Dynamic Ground Battle Plugin initialized successfully!")
|
||||||
|
env.info(string.format("[DGB PLUGIN] Infantry movement: %s", MOVING_INFANTRY_PATROLS and "ENABLED" or "DISABLED"))
|
||||||
|
env.info(string.format("[DGB PLUGIN] Warehouse markers: %s", ENABLE_WAREHOUSE_MARKERS and "ENABLED" or "DISABLED"))
|
||||||
224
Moose_DynamicGroundBattle/PLUGIN_README.md
Normal file
224
Moose_DynamicGroundBattle/PLUGIN_README.md
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
# Dynamic Ground Battle Plugin
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This is a **plugin version** of the Dynamic Ground Battle system designed to work alongside `Moose_DualCoalitionZoneCapture.lua`.
|
||||||
|
|
||||||
|
**Key difference from the standalone version:**
|
||||||
|
- ✅ Uses zones from `Moose_DualCoalitionZoneCapture.lua` (no duplicate zone management)
|
||||||
|
- ✅ Handles ONLY warehouse system + AI spawning + task assignment
|
||||||
|
- ✅ Zone capture, colors, messaging, win conditions = handled by DualCoalitionZoneCapture
|
||||||
|
- ✅ Cleaner separation of concerns
|
||||||
|
|
||||||
|
## Load Order (Critical!)
|
||||||
|
|
||||||
|
In your Mission Editor → Triggers → Mission Start:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. DO SCRIPT FILE Moose_.lua
|
||||||
|
2. DO SCRIPT FILE Moose_DualCoalitionZoneCapture.lua ← Must load FIRST
|
||||||
|
3. DO SCRIPT FILE Moose_DynamicGroundBattle_Plugin.lua ← This file
|
||||||
|
4. DO SCRIPT FILE CTLD.lua (optional)
|
||||||
|
5. DO SCRIPT FILE CSAR.lua (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
## What This Plugin Does
|
||||||
|
|
||||||
|
### ✅ Warehouse System
|
||||||
|
- Red/Blue warehouses affect reinforcement spawn rates
|
||||||
|
- 100% alive = 100% spawn rate
|
||||||
|
- 50% alive = 50% spawn rate (2x delay)
|
||||||
|
- 0% alive = no more spawns
|
||||||
|
- Map markers show warehouse locations and intel
|
||||||
|
|
||||||
|
### ✅ Dynamic AI Spawning
|
||||||
|
- Spawns infantry and armor groups in **friendly zones only**
|
||||||
|
- Zones come from `DualCoalitionZoneCapture`'s `ZONE_CONFIG`
|
||||||
|
- As zones are captured/lost, spawn locations automatically update
|
||||||
|
- Configurable limits and spawn frequencies
|
||||||
|
|
||||||
|
### ✅ AI Task Assignment
|
||||||
|
- Groups patrol toward nearest enemy zone
|
||||||
|
- Reassignment every 600s (configurable)
|
||||||
|
- Only idle units get new orders (moving units ignored)
|
||||||
|
- Infantry movement can be disabled
|
||||||
|
|
||||||
|
### ✅ CTLD Integration
|
||||||
|
- Dropped troops automatically get AI tasking
|
||||||
|
- Works seamlessly with logistics operations
|
||||||
|
|
||||||
|
## What This Plugin Does NOT Do
|
||||||
|
|
||||||
|
- ❌ Zone capture logic (handled by DualCoalitionZoneCapture)
|
||||||
|
- ❌ Zone coloring/messaging (handled by DualCoalitionZoneCapture)
|
||||||
|
- ❌ Win conditions (handled by DualCoalitionZoneCapture)
|
||||||
|
- ❌ Scoring (handled by DualCoalitionZoneCapture)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit the top section of `Moose_DynamicGroundBattle_Plugin.lua`:
|
||||||
|
|
||||||
|
### Infantry Movement
|
||||||
|
```lua
|
||||||
|
local MOVING_INFANTRY_PATROLS = false -- Set true to enable infantry patrols
|
||||||
|
```
|
||||||
|
|
||||||
|
### Warehouse Markers
|
||||||
|
```lua
|
||||||
|
local ENABLE_WAREHOUSE_MARKERS = true
|
||||||
|
local UPDATE_MARK_POINTS_SCHED = 300 -- Update every 5 minutes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spawn Settings
|
||||||
|
```lua
|
||||||
|
-- Red Side
|
||||||
|
local INIT_RED_INFANTRY = 5
|
||||||
|
local MAX_RED_INFANTRY = 100
|
||||||
|
local SPAWN_SCHED_RED_INFANTRY = 1800 -- Every 30 minutes
|
||||||
|
|
||||||
|
local INIT_RED_ARMOR = 25
|
||||||
|
local MAX_RED_ARMOR = 200
|
||||||
|
local SPAWN_SCHED_RED_ARMOR = 300 -- Every 5 minutes
|
||||||
|
|
||||||
|
-- Blue Side
|
||||||
|
local INIT_BLUE_INFANTRY = 5
|
||||||
|
local MAX_BLUE_INFANTRY = 100
|
||||||
|
local SPAWN_SCHED_BLUE_INFANTRY = 1800
|
||||||
|
|
||||||
|
local INIT_BLUE_ARMOR = 25
|
||||||
|
local MAX_BLUE_ARMOR = 200
|
||||||
|
local SPAWN_SCHED_BLUE_ARMOR = 300
|
||||||
|
|
||||||
|
-- Task Assignment
|
||||||
|
local ASSIGN_TASKS_SCHED = 600 -- Reassign idle units every 10 minutes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Warehouses
|
||||||
|
```lua
|
||||||
|
local redWarehouses = {
|
||||||
|
STATIC:FindByName("RedWarehouse1-1"),
|
||||||
|
STATIC:FindByName("RedWarehouse2-1"),
|
||||||
|
-- Add more as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
local blueWarehouses = {
|
||||||
|
STATIC:FindByName("BlueWarehouse1-1"),
|
||||||
|
STATIC:FindByName("BlueWarehouse2-1"),
|
||||||
|
-- Add more as needed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unit Templates
|
||||||
|
```lua
|
||||||
|
local redInfantryTemplates = {
|
||||||
|
"RedInfantry1",
|
||||||
|
"RedInfantry2",
|
||||||
|
-- Add more for variety
|
||||||
|
}
|
||||||
|
|
||||||
|
local redArmorTemplates = {
|
||||||
|
"RedArmor1",
|
||||||
|
"RedArmor2",
|
||||||
|
-- Add more for variety
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Same for blue side...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mission Editor Setup
|
||||||
|
|
||||||
|
### Required Groups (All LATE ACTIVATE)
|
||||||
|
|
||||||
|
**Red Side:**
|
||||||
|
- Infantry: `RedInfantry1`, `RedInfantry2`, `RedInfantry3`, `RedInfantry4`, `RedInfantry5`, `RedInfantry6`
|
||||||
|
- Armor: `RedArmor1`, `RedArmor2`, `RedArmor3`, `RedArmor4`, `RedArmor5`, `RedArmor6`
|
||||||
|
|
||||||
|
**Blue Side:**
|
||||||
|
- Infantry: `BlueInfantry1`, `BlueInfantry2`, `BlueInfantry3`, `BlueInfantry4`, `BlueInfantry5`, `BlueInfantry6`
|
||||||
|
- Armor: `BlueArmor1`, `BlueArmor2`, `BlueArmor3`, `BlueArmor4`, `BlueArmor5`
|
||||||
|
|
||||||
|
### Required Static Objects (Warehouses)
|
||||||
|
|
||||||
|
**Red Side:**
|
||||||
|
- Static objects with **Unit Name** (not Name field!): `RedWarehouse1-1`, `RedWarehouse2-1`, `RedWarehouse3-1`, etc.
|
||||||
|
|
||||||
|
**Blue Side:**
|
||||||
|
- Static objects with **Unit Name**: `BlueWarehouse1-1`, `BlueWarehouse2-1`, `BlueWarehouse3-1`, etc.
|
||||||
|
|
||||||
|
⚠️ **Important:** Warehouses use the "Unit Name" field in the static object properties, not the "Name" field!
|
||||||
|
|
||||||
|
### Zone Configuration
|
||||||
|
|
||||||
|
Zones come from `Moose_DualCoalitionZoneCapture.lua`. Edit that file's `ZONE_CONFIG`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local ZONE_CONFIG = {
|
||||||
|
RED = {
|
||||||
|
"Capture Zone-1",
|
||||||
|
"Capture Zone-2",
|
||||||
|
"Capture Zone-3",
|
||||||
|
},
|
||||||
|
|
||||||
|
BLUE = {
|
||||||
|
"Capture Zone-4",
|
||||||
|
"Capture Zone-5",
|
||||||
|
"Capture Zone-6",
|
||||||
|
},
|
||||||
|
|
||||||
|
NEUTRAL = {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## F10 Menu
|
||||||
|
|
||||||
|
- **Ground Battle → Check Warehouse Status** - Shows current warehouse count and reinforcement capacity
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
### With DualCoalitionZoneCapture
|
||||||
|
- Reads `zoneCaptureObjects` and `zoneNames` arrays
|
||||||
|
- Dynamically spawns in zones controlled by the appropriate coalition
|
||||||
|
- AI routes to enemy zones based on current ownership
|
||||||
|
|
||||||
|
### With CTLD
|
||||||
|
- Any troops dropped via CTLD in friendly zones will automatically receive AI tasking
|
||||||
|
- They'll patrol toward nearest enemy zone like spawned units
|
||||||
|
|
||||||
|
## Performance & Memory Safety
|
||||||
|
|
||||||
|
This plugin version includes all the memory leak fixes from the standalone version:
|
||||||
|
|
||||||
|
✅ Marker cleanup prevents orphaning
|
||||||
|
✅ Reuses SET_GROUP instead of recreating
|
||||||
|
✅ Uses SCHEDULER instead of recursive TIMERs
|
||||||
|
✅ Efficient zone polling
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "ERROR: Moose_DualCoalitionZoneCapture.lua must be loaded BEFORE this plugin!"
|
||||||
|
- Check your trigger load order
|
||||||
|
- DualCoalitionZoneCapture must be loaded in an earlier trigger
|
||||||
|
|
||||||
|
### Units not spawning
|
||||||
|
- Check `dcs.log` for errors
|
||||||
|
- Verify warehouse static objects exist with correct **Unit Names**
|
||||||
|
- Verify template groups exist and are set to LATE ACTIVATE
|
||||||
|
- Check that friendly zones exist (units only spawn in controlled zones)
|
||||||
|
|
||||||
|
### Units not moving
|
||||||
|
- If infantry: check `MOVING_INFANTRY_PATROLS` setting
|
||||||
|
- Verify enemy zones exist for AI to target
|
||||||
|
- Check `dcs.log` for task assignment messages (`[DGB PLUGIN]`)
|
||||||
|
|
||||||
|
### Warehouse markers not showing
|
||||||
|
- Set `ENABLE_WAREHOUSE_MARKERS = true`
|
||||||
|
- Check warehouse static objects are alive
|
||||||
|
- Markers update every `UPDATE_MARK_POINTS_SCHED` seconds
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **1.0.0** (2024-11-15) - Initial plugin version
|
||||||
|
- Extracted from standalone Dynamic Ground Battle
|
||||||
|
- Integrated with DualCoalitionZoneCapture
|
||||||
|
- Added memory leak fixes
|
||||||
|
- Dynamic zone-based spawning
|
||||||
Loading…
x
Reference in New Issue
Block a user