mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
Updated all scripts.
This commit is contained in:
parent
fd8dcfab73
commit
2bce81c559
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -6,7 +6,20 @@
|
|||||||
-- @module BOMBER_ESCORT
|
-- @module BOMBER_ESCORT
|
||||||
-- @author F99th-TracerFacer
|
-- @author F99th-TracerFacer
|
||||||
-- @copyright 2025
|
-- @copyright 2025
|
||||||
|
|
||||||
|
--Naming Convention - Make sure these groups exist in the mission editor and are spelled EXACTLY as shown.
|
||||||
--
|
--
|
||||||
|
--B-17G -> Template name: BOMBER_B17G
|
||||||
|
--B-24J -> Template name: BOMBER_B24J
|
||||||
|
--B-52H -> Template name: BOMBER_B52H
|
||||||
|
--B-1B -> Template name: BOMBER_B1B
|
||||||
|
--Tu-95MS -> Template name: BOMBER_TU95
|
||||||
|
--Tu-142 -> Template name: BOMBER_TU142
|
||||||
|
--Tu-22M3 -> Template name: BOMBER_TU22
|
||||||
|
--Tu-160 -> Template name: BOMBER_TU160
|
||||||
|
--
|
||||||
|
---@diagnostic disable: undefined-global, lowercase-global
|
||||||
|
-- MOOSE framework globals are defined at runtime by DCS World
|
||||||
|
|
||||||
-- Global spawn counter to ensure unique MOOSE spawn indices
|
-- Global spawn counter to ensure unique MOOSE spawn indices
|
||||||
if not _BOMBER_GLOBAL_SPAWN_COUNTER then
|
if not _BOMBER_GLOBAL_SPAWN_COUNTER then
|
||||||
@ -23,17 +36,6 @@ if not _ACTIVE_MISSION_IDS then
|
|||||||
_ACTIVE_MISSION_IDS = {}
|
_ACTIVE_MISSION_IDS = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--Naming Convention:
|
|
||||||
--
|
|
||||||
--B-17G -> Template name: BOMBER_B17G
|
|
||||||
--B-24J -> Template name: BOMBER_B24J
|
|
||||||
--B-52H -> Template name: BOMBER_B52H
|
|
||||||
--B-1B -> Template name: BOMBER_B1B
|
|
||||||
--Tu-95MS -> Template name: BOMBER_TU95
|
|
||||||
--Tu-142 -> Template name: BOMBER_TU142
|
|
||||||
--Tu-22M3 -> Template name: BOMBER_TU22M3
|
|
||||||
--Tu-160 -> Template name: BOMBER_TU160
|
|
||||||
|
|
||||||
---
|
---
|
||||||
-- LOGGING SYSTEM
|
-- LOGGING SYSTEM
|
||||||
-- Central logging function with configurable log levels
|
-- Central logging function with configurable log levels
|
||||||
@ -132,8 +134,8 @@ BOMBER_ESCORT_CONFIG = {
|
|||||||
|
|
||||||
-- Escort Requirements
|
-- Escort Requirements
|
||||||
RequireEscort = true, -- Bombers require player escort to proceed with mission (default: true, set false to allow solo bomber missions)
|
RequireEscort = true, -- Bombers require player escort to proceed with mission (default: true, set false to allow solo bomber missions)
|
||||||
EscortAirborneJoinGrace = 120, -- Seconds of grace after liftoff before escort warnings begin (default: 90)
|
EscortAirborneJoinGrace = 300, -- Seconds of grace after liftoff before escort warnings begin (default: 90)
|
||||||
EscortFormUpAnnouncementInterval = 60, -- Seconds between "need escort" calls during form-up (default: 60)
|
EscortFormUpAnnouncementInterval = 120, -- Seconds between "need escort" calls during form-up (default: 60)
|
||||||
EscortFormUpMaxAnnouncements = 5, -- Number of calls before aborting form-up (default: 5)
|
EscortFormUpMaxAnnouncements = 5, -- Number of calls before aborting form-up (default: 5)
|
||||||
EscortLossAnnouncementInterval = 60, -- Seconds between in-flight escort loss warnings (default: 60)
|
EscortLossAnnouncementInterval = 60, -- Seconds between in-flight escort loss warnings (default: 60)
|
||||||
EscortLossMaxAnnouncements = 5, -- Number of warnings before aborting due to no escort (default: 5)
|
EscortLossMaxAnnouncements = 5, -- Number of warnings before aborting due to no escort (default: 5)
|
||||||
@ -150,8 +152,9 @@ BOMBER_ESCORT_CONFIG = {
|
|||||||
EscortFormationComplimentInterval = 180, -- Seconds between formation flying compliments (default: 180s = 3 minutes)
|
EscortFormationComplimentInterval = 180, -- Seconds between formation flying compliments (default: 180s = 3 minutes)
|
||||||
|
|
||||||
-- Threat Detection
|
-- Threat Detection
|
||||||
SAMThreatDistance = 100000, -- Meters - SAM detection range (default: 100km - extended for early avoidance)
|
-- OPTIMIZATION: Reduced from 100km to 80km to lower pathfinding memory usage
|
||||||
FighterThreatDistance = 100000, -- Meters - Fighter detection range (default: 100km - extended for escort positioning time)
|
SAMThreatDistance = 80000, -- Meters - SAM detection range (default: 80km - optimized for memory)
|
||||||
|
FighterThreatDistance = 80000, -- Meters - Fighter detection range (default: 80km - optimized for memory)
|
||||||
ThreatCheckInterval = 10, -- Seconds between threat scans (default: 10)
|
ThreatCheckInterval = 10, -- Seconds between threat scans (default: 10)
|
||||||
|
|
||||||
-- SAM Warning System
|
-- SAM Warning System
|
||||||
@ -163,7 +166,7 @@ BOMBER_ESCORT_CONFIG = {
|
|||||||
EnableSAMAvoidance = true, -- Enable SAM threat detection and mission abort (default: true)
|
EnableSAMAvoidance = true, -- Enable SAM threat detection and mission abort (default: true)
|
||||||
SAMAvoidanceBuffer = 25000, -- Meters - Buffer added to SAM range for threat assessment (default: 25km)
|
SAMAvoidanceBuffer = 25000, -- Meters - Buffer added to SAM range for threat assessment (default: 25km)
|
||||||
SAMCorridorBuffer = 10000, -- Meters - Smaller buffer for corridor finding (pre-planning can be more aggressive, default: 10km)
|
SAMCorridorBuffer = 10000, -- Meters - Smaller buffer for corridor finding (pre-planning can be more aggressive, default: 10km)
|
||||||
SAMRouteLookAhead = 150000, -- Meters - Check route this far ahead for SAMs (default: 150km)
|
SAMRouteLookAhead = 120000, -- Meters - Check route this far ahead for SAMs (OPTIMIZED: reduced from 150km to 120km for memory)
|
||||||
SAMAvoidOnlyIfCanEngage = true, -- Only abort for SAMs that can engage at current altitude (default: true)
|
SAMAvoidOnlyIfCanEngage = true, -- Only abort for SAMs that can engage at current altitude (default: true)
|
||||||
SAMRerouteCheckInterval = 10, -- Seconds between route threat checks during flight (default: 10s)
|
SAMRerouteCheckInterval = 10, -- Seconds between route threat checks during flight (default: 10s)
|
||||||
|
|
||||||
@ -198,6 +201,9 @@ BOMBER_ESCORT_CONFIG = {
|
|||||||
-- Debug/Instrumentation
|
-- Debug/Instrumentation
|
||||||
EnableRouteDebugSnapshots = true, -- Dump controller route tables after each route apply (set false to disable heavy TRACE logs)
|
EnableRouteDebugSnapshots = true, -- Dump controller route tables after each route apply (set false to disable heavy TRACE logs)
|
||||||
RouteSnapshotDelaySeconds = 0.75, -- Delay before sampling controller route (allows DCS AI to register the new plan)
|
RouteSnapshotDelaySeconds = 0.75, -- Delay before sampling controller route (allows DCS AI to register the new plan)
|
||||||
|
|
||||||
|
-- Memory Management
|
||||||
|
GarbageCollectionInterval = 600, -- Seconds between forced Lua garbage collection cycles (default: 600 = 10 minutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -944,10 +950,14 @@ function BOMBER_MARKER:_ExecuteMultiMissions(missions, coalitionSide)
|
|||||||
-- Always show what was parsed
|
-- Always show what was parsed
|
||||||
if hasSpawn then
|
if hasSpawn then
|
||||||
feedbackMsg = feedbackMsg .. string.format("[OK] SPAWN: %s\n", mission.spawn.markerText)
|
feedbackMsg = feedbackMsg .. string.format("[OK] SPAWN: %s\n", mission.spawn.markerText)
|
||||||
feedbackMsg = feedbackMsg .. string.format(" Type: %s, Size: %s, Alt: %s, Speed: %s\n",
|
if params then
|
||||||
params.type or "[MISSING]", tostring(params.size or "[DEFAULT]"), tostring(params.altitude or "[DEFAULT]"), tostring(params.speed or "[DEFAULT]"))
|
feedbackMsg = feedbackMsg .. string.format(" Type: %s, Size: %s, Alt: %s, Speed: %s\n",
|
||||||
if not params.type then
|
params.type or "[MISSING]", tostring(params.size or "[DEFAULT]"), tostring(params.altitude or "[DEFAULT]"), tostring(params.speed or "[DEFAULT]"))
|
||||||
feedbackMsg = feedbackMsg .. " [X] Bomber type missing or malformed!\n"
|
if not params.type then
|
||||||
|
feedbackMsg = feedbackMsg .. " [X] Bomber type missing or malformed!\n"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
feedbackMsg = feedbackMsg .. " [X] Failed to parse spawn marker parameters!\n"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
feedbackMsg = feedbackMsg .. "[X] SPAWN: NONE (required)\n"
|
feedbackMsg = feedbackMsg .. "[X] SPAWN: NONE (required)\n"
|
||||||
@ -1222,10 +1232,10 @@ end
|
|||||||
function BOMBER_MARKER:_GetNextAvailableMissionNumber()
|
function BOMBER_MARKER:_GetNextAvailableMissionNumber()
|
||||||
local maxNum = 0
|
local maxNum = 0
|
||||||
for missionId in pairs(_ACTIVE_MISSION_IDS) do
|
for missionId in pairs(_ACTIVE_MISSION_IDS) do
|
||||||
local num = string.match(missionId, "BOMBER(%d+)")
|
local numStr = string.match(missionId, "BOMBER(%d+)")
|
||||||
if num then
|
if numStr then
|
||||||
num = tonumber(num)
|
local num = tonumber(numStr)
|
||||||
if num > maxNum then
|
if num and num > maxNum then
|
||||||
maxNum = num
|
maxNum = num
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1356,6 +1366,10 @@ function BOMBER_ESCORT_MONITOR:Stop()
|
|||||||
self.EscortUnits = {}
|
self.EscortUnits = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Force two-pass garbage collection for thorough cleanup
|
||||||
|
collectgarbage("collect")
|
||||||
|
collectgarbage("collect")
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2022,6 +2036,9 @@ function BOMBER_THREAT_MANAGER:Stop()
|
|||||||
self.ThreatHistory = {}
|
self.ThreatHistory = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Force garbage collection after clearing tables
|
||||||
|
collectgarbage("collect")
|
||||||
|
|
||||||
BOMBER_LOGGER:Info("THREAT", "ThreatManager:Stop() completed")
|
BOMBER_LOGGER:Info("THREAT", "ThreatManager:Stop() completed")
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -3239,6 +3256,10 @@ function BOMBER_MISSION_MANAGER:UnregisterMission(mission)
|
|||||||
if pruneCount > 0 then
|
if pruneCount > 0 then
|
||||||
BOMBER_LOGGER:Debug("MISSION", "Pruned %d unused SPAWN objects from global cache", pruneCount)
|
BOMBER_LOGGER:Debug("MISSION", "Pruned %d unused SPAWN objects from global cache", pruneCount)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Force two-pass garbage collection after cleanup
|
||||||
|
collectgarbage("collect")
|
||||||
|
collectgarbage("collect")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3444,8 +3465,8 @@ function BOMBER_MISSION:_BuildRoute()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Waypoint 1: Start (takeoff)
|
-- Waypoint 1: Start (takeoff)
|
||||||
local cruiseAlt = self.CruiseAlt or profile.CruiseAlt
|
local cruiseAlt = self.CruiseAlt or (profile and profile.CruiseAlt) or BOMBER_ESCORT_CONFIG.DefaultAltitude
|
||||||
local cruiseSpeed = self.CruiseSpeed or profile.CruiseSpeed
|
local cruiseSpeed = self.CruiseSpeed or (profile and profile.CruiseSpeed) or BOMBER_ESCORT_CONFIG.DefaultSpeed
|
||||||
local cruiseAltMeters = cruiseAlt * 0.3048 -- Convert feet to meters
|
local cruiseAltMeters = cruiseAlt * 0.3048 -- Convert feet to meters
|
||||||
local cruiseSpeedMPS = cruiseSpeed * 0.514444 -- Convert knots to m/s
|
local cruiseSpeedMPS = cruiseSpeed * 0.514444 -- Convert knots to m/s
|
||||||
|
|
||||||
@ -4384,9 +4405,10 @@ function BOMBER_MISSION:_PlayerRequestSpeedUp()
|
|||||||
local profile = self.Bomber.Profile
|
local profile = self.Bomber.Profile
|
||||||
local currentSpeed = self.Bomber.Group:GetVelocityKNOTS()
|
local currentSpeed = self.Bomber.Group:GetVelocityKNOTS()
|
||||||
|
|
||||||
if currentSpeed < profile.MaxSpeed - 10 then
|
local maxSpeed = (profile and profile.MaxSpeed) or 500 -- Default max speed if not defined
|
||||||
|
if currentSpeed < maxSpeed - 10 then
|
||||||
self.Bomber:_BroadcastMessage(string.format("%s: Increasing speed.", self.Callsign))
|
self.Bomber:_BroadcastMessage(string.format("%s: Increasing speed.", self.Callsign))
|
||||||
local newSpeed = math.min(currentSpeed + 20, profile.MaxSpeed)
|
local newSpeed = math.min(currentSpeed + 20, maxSpeed)
|
||||||
self.Bomber.Group:SetSpeed(newSpeed * 0.514444) -- Convert to m/s
|
self.Bomber.Group:SetSpeed(newSpeed * 0.514444) -- Convert to m/s
|
||||||
else
|
else
|
||||||
self.Bomber:_BroadcastMessage(string.format("%s: Negative, already at max speed.", self.Callsign))
|
self.Bomber:_BroadcastMessage(string.format("%s: Negative, already at max speed.", self.Callsign))
|
||||||
@ -4400,10 +4422,11 @@ function BOMBER_MISSION:_PlayerRequestSlowDown()
|
|||||||
if self.Bomber and self.Bomber:IsAlive() then
|
if self.Bomber and self.Bomber:IsAlive() then
|
||||||
local profile = self.Bomber.Profile
|
local profile = self.Bomber.Profile
|
||||||
local currentSpeed = self.Bomber.Group:GetVelocityKNOTS()
|
local currentSpeed = self.Bomber.Group:GetVelocityKNOTS()
|
||||||
|
local minSpeed = (profile and profile.MinSpeed) or 200 -- Default min speed if not defined
|
||||||
if currentSpeed > profile.MinSpeed + 10 then
|
if currentSpeed > minSpeed + 10 then
|
||||||
self.Bomber:_BroadcastMessage(string.format("%s: Reducing speed.", self.Callsign))
|
self.Bomber:_BroadcastMessage(string.format("%s: Reducing speed.", self.Callsign))
|
||||||
local newSpeed = math.max(currentSpeed - 20, profile.MinSpeed)
|
local newSpeed = math.max(currentSpeed - 20, minSpeed)
|
||||||
|
self.Bomber.Group:SetSpeed(newSpeed * 0.514444)
|
||||||
self.Bomber.Group:SetSpeed(newSpeed * 0.514444)
|
self.Bomber.Group:SetSpeed(newSpeed * 0.514444)
|
||||||
else
|
else
|
||||||
self.Bomber:_BroadcastMessage(string.format("%s: Negative, already at minimum speed.", self.Callsign))
|
self.Bomber:_BroadcastMessage(string.format("%s: Negative, already at minimum speed.", self.Callsign))
|
||||||
@ -6646,7 +6669,7 @@ function BOMBER:_StartRTBMonitor()
|
|||||||
self.LandingMonitor = nil
|
self.LandingMonitor = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
self:Destroy(1)
|
self:Destroy()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -8386,7 +8409,7 @@ function BOMBER:_UpdateSAMStatusSummary()
|
|||||||
end
|
end
|
||||||
elseif threatCount > 1 and primaryThreat then
|
elseif threatCount > 1 and primaryThreat then
|
||||||
local closestNm = math.floor(closestDist / 1852)
|
local closestNm = math.floor(closestDist / 1852)
|
||||||
local closestBearing = math.floor(closest.Bearing)
|
local closestBearing = closest and closest.Bearing and math.floor(closest.Bearing) or 0
|
||||||
local primaryType = primaryThreat.SAMType or "Unknown"
|
local primaryType = primaryThreat.SAMType or "Unknown"
|
||||||
local canEngage = primaryThreat.CanEngage
|
local canEngage = primaryThreat.CanEngage
|
||||||
|
|
||||||
@ -9960,6 +9983,16 @@ function BOMBER_ESCORT_INIT(options)
|
|||||||
BOMBER_LOGGER:Info("INIT", "==============================================")
|
BOMBER_LOGGER:Info("INIT", "==============================================")
|
||||||
BOMBER_LOGGER:Info("INIT", "For complete documentation, see MARKER_GUIDE.md")
|
BOMBER_LOGGER:Info("INIT", "For complete documentation, see MARKER_GUIDE.md")
|
||||||
BOMBER_LOGGER:Info("INIT", "==============================================")
|
BOMBER_LOGGER:Info("INIT", "==============================================")
|
||||||
|
|
||||||
|
-- Schedule periodic garbage collection to prevent memory buildup
|
||||||
|
if BOMBER_ESCORT_CONFIG.GarbageCollectionInterval and BOMBER_ESCORT_CONFIG.GarbageCollectionInterval > 0 then
|
||||||
|
SCHEDULER:New(nil, function()
|
||||||
|
collectgarbage("collect")
|
||||||
|
local memKB = collectgarbage("count")
|
||||||
|
BOMBER_LOGGER:Debug("MEMORY", "Garbage collection complete. Current Lua memory: %.1f MB", memKB / 1024)
|
||||||
|
end, {}, 120, BOMBER_ESCORT_CONFIG.GarbageCollectionInterval)
|
||||||
|
BOMBER_LOGGER:Info("INIT", "Memory Management: Garbage collection scheduled every %d seconds", BOMBER_ESCORT_CONFIG.GarbageCollectionInterval)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
return _BOMBER_MARKER_SYSTEM
|
return _BOMBER_MARKER_SYSTEM
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
-- Refactored version with configurable zone ownership
|
-- Refactored version with configurable zone ownership
|
||||||
|
---@diagnostic disable: undefined-global, lowercase-global
|
||||||
|
-- MOOSE framework globals are defined at runtime by DCS World
|
||||||
|
-- **Author**: F99th-TracerFacer
|
||||||
|
-- **Discord:** https://discord.gg/NdZ2JuSU (The Fighting 99th Discord Server where I spend most of my time.)
|
||||||
|
|
||||||
|
|
||||||
-- ==========================================
|
-- ==========================================
|
||||||
-- MESSAGE AND TIMING CONFIGURATION
|
-- MESSAGE AND TIMING CONFIGURATION
|
||||||
@ -13,7 +18,8 @@ local MESSAGE_CONFIG = {
|
|||||||
STATUS_MESSAGE_DURATION = 15, -- How long general status messages stay onscreen
|
STATUS_MESSAGE_DURATION = 15, -- How long general status messages stay onscreen
|
||||||
VICTORY_MESSAGE_DURATION = 300, -- How long victory/defeat alerts stay onscreen
|
VICTORY_MESSAGE_DURATION = 300, -- How long victory/defeat alerts stay onscreen
|
||||||
CAPTURE_MESSAGE_DURATION = 15, -- Duration for capture/guard/empty notices
|
CAPTURE_MESSAGE_DURATION = 15, -- Duration for capture/guard/empty notices
|
||||||
ATTACK_MESSAGE_DURATION = 15 -- Duration for attack alerts
|
ATTACK_MESSAGE_DURATION = 15, -- Duration for attack alerts
|
||||||
|
GARBAGE_COLLECTION_FREQUENCY = 600 -- Lua garbage collection cadence (seconds) - helps prevent memory buildup
|
||||||
}
|
}
|
||||||
|
|
||||||
-- ==========================================
|
-- ==========================================
|
||||||
@ -225,7 +231,8 @@ end
|
|||||||
-- NOTE: These are exported as globals for plugin compatibility (e.g., Moose_DynamicGroundBattle_Plugin.lua)
|
-- NOTE: These are exported as globals for plugin compatibility (e.g., Moose_DynamicGroundBattle_Plugin.lua)
|
||||||
zoneCaptureObjects = {} -- Global: accessible by other scripts
|
zoneCaptureObjects = {} -- Global: accessible by other scripts
|
||||||
zoneNames = {} -- Global: accessible by other scripts
|
zoneNames = {} -- Global: accessible by other scripts
|
||||||
local zoneMetadata = {} -- Stores coalition ownership info
|
local zoneMetadata = {} -- Stores coalition ownership info
|
||||||
|
local activeTacticalMarkers = {} -- Track tactical markers to prevent memory leaks
|
||||||
|
|
||||||
-- Function to initialize all zones from configuration
|
-- Function to initialize all zones from configuration
|
||||||
local function InitializeZones()
|
local function InitializeZones()
|
||||||
@ -481,16 +488,16 @@ local function CreateTacticalInfoMarker(ZoneCapture)
|
|||||||
text = text .. string.format(" C:%d", forces.neutral)
|
text = text .. string.format(" C:%d", forces.neutral)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Append TGTS for the enemy of the viewer, capped at 10 units
|
-- Append TGTS for the enemy of the viewer, capped at 5 units (reduced from 10 to lower memory usage)
|
||||||
local enemyCoalition = (viewerCoalition == coalition.side.BLUE) and coalition.side.RED or coalition.side.BLUE
|
local enemyCoalition = (viewerCoalition == coalition.side.BLUE) and coalition.side.RED or coalition.side.BLUE
|
||||||
local enemyCount = (enemyCoalition == coalition.side.RED) and (forces.red or 0) or (forces.blue or 0)
|
local enemyCount = (enemyCoalition == coalition.side.RED) and (forces.red or 0) or (forces.blue or 0)
|
||||||
if enemyCount > 0 and enemyCount <= 10 then
|
if enemyCount > 0 and enemyCount <= 8 then -- Only process if 8 or fewer enemies (reduced from 10)
|
||||||
local enemyCoords = GetEnemyUnitMGRSCoords(ZoneCapture, enemyCoalition)
|
local enemyCoords = GetEnemyUnitMGRSCoords(ZoneCapture, enemyCoalition)
|
||||||
log(string.format("[TACTICAL DEBUG] Building marker text for %s viewer: %d enemy units", (viewerCoalition==coalition.side.BLUE and "BLUE" or "RED"), #enemyCoords))
|
log(string.format("[TACTICAL DEBUG] Building marker text for %s viewer: %d enemy units", (viewerCoalition==coalition.side.BLUE and "BLUE" or "RED"), #enemyCoords))
|
||||||
if #enemyCoords > 0 then
|
if #enemyCoords > 0 then
|
||||||
text = text .. "\nTGTS:"
|
text = text .. "\nTGTS:"
|
||||||
for i, unit in ipairs(enemyCoords) do
|
for i, unit in ipairs(enemyCoords) do
|
||||||
if i <= 10 then
|
if i <= 5 then -- Reduced from 10 to 5 to save memory
|
||||||
local shortType = (unit.type or "Unknown"):gsub("^%w+%-", ""):gsub("%s.*", "")
|
local shortType = (unit.type or "Unknown"):gsub("^%w+%-", ""):gsub("%s.*", "")
|
||||||
local cleanMgrs = (unit.mgrs or ""):gsub("^MGRS%s+", ""):gsub("%s+", " ")
|
local cleanMgrs = (unit.mgrs or ""):gsub("^MGRS%s+", ""):gsub("%s+", " ")
|
||||||
if i == 1 then
|
if i == 1 then
|
||||||
@ -500,8 +507,8 @@ local function CreateTacticalInfoMarker(ZoneCapture)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #enemyCoords > 10 then
|
if #enemyCoords > 5 then -- Updated threshold
|
||||||
text = text .. string.format(" (+%d)", #enemyCoords - 10)
|
text = text .. string.format(" (+%d)", #enemyCoords - 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -967,6 +974,13 @@ local ZoneMonitorScheduler = SCHEDULER:New( nil, function()
|
|||||||
|
|
||||||
end, {}, MESSAGE_CONFIG.STATUS_BROADCAST_START_DELAY, MESSAGE_CONFIG.STATUS_BROADCAST_FREQUENCY )
|
end, {}, MESSAGE_CONFIG.STATUS_BROADCAST_START_DELAY, MESSAGE_CONFIG.STATUS_BROADCAST_FREQUENCY )
|
||||||
|
|
||||||
|
-- Periodic garbage collection to prevent Lua memory buildup
|
||||||
|
SCHEDULER:New( nil, function()
|
||||||
|
collectgarbage("collect")
|
||||||
|
local memKB = collectgarbage("count")
|
||||||
|
log(string.format("[MEMORY] Lua garbage collection complete. Current usage: %.1f MB", memKB / 1024))
|
||||||
|
end, {}, 120, MESSAGE_CONFIG.GARBAGE_COLLECTION_FREQUENCY )
|
||||||
|
|
||||||
-- Periodic zone color verification system (every 2 minutes)
|
-- Periodic zone color verification system (every 2 minutes)
|
||||||
local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function()
|
local ZoneColorVerificationScheduler = SCHEDULER:New( nil, function()
|
||||||
log("[ZONE COLORS] Running periodic zone color verification...")
|
log("[ZONE COLORS] Running periodic zone color verification...")
|
||||||
|
|||||||
@ -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
|
||||||
@ -100,8 +105,8 @@ local INIT_RED_INFANTRY = 15 -- Initial number of Red Infantry groups
|
|||||||
local MAX_RED_INFANTRY = 100 -- Maximum number of Red Infantry groups
|
local MAX_RED_INFANTRY = 100 -- Maximum number of Red Infantry groups
|
||||||
local SPAWN_SCHED_RED_INFANTRY = 1200 -- Base spawn frequency for Red Infantry (seconds)
|
local SPAWN_SCHED_RED_INFANTRY = 1200 -- Base spawn frequency for Red Infantry (seconds)
|
||||||
|
|
||||||
local INIT_RED_ARMOR = 25 -- Initial number of Red Armor groups
|
local INIT_RED_ARMOR = 30 -- Initial number of Red Armor groups
|
||||||
local MAX_RED_ARMOR = 100 -- Maximum number of Red Armor groups
|
local MAX_RED_ARMOR = 500 -- Maximum number of Red Armor groups
|
||||||
local SPAWN_SCHED_RED_ARMOR = 200 -- Base spawn frequency for Red Armor (seconds)
|
local SPAWN_SCHED_RED_ARMOR = 200 -- Base spawn frequency for Red Armor (seconds)
|
||||||
|
|
||||||
-- Blue Side Settings
|
-- Blue Side Settings
|
||||||
@ -109,8 +114,8 @@ local INIT_BLUE_INFANTRY = 15 -- Initial number of Blue Infantry group
|
|||||||
local MAX_BLUE_INFANTRY = 100 -- Maximum number of Blue Infantry groups
|
local MAX_BLUE_INFANTRY = 100 -- Maximum number of Blue Infantry groups
|
||||||
local SPAWN_SCHED_BLUE_INFANTRY = 1200 -- Base spawn frequency for Blue Infantry (seconds)
|
local SPAWN_SCHED_BLUE_INFANTRY = 1200 -- Base spawn frequency for Blue Infantry (seconds)
|
||||||
|
|
||||||
local INIT_BLUE_ARMOR = 25 -- Initial number of Blue Armor groups
|
local INIT_BLUE_ARMOR = 30 -- Initial number of Blue Armor groups
|
||||||
local MAX_BLUE_ARMOR = 100 -- Maximum number of Blue Armor groups
|
local MAX_BLUE_ARMOR = 500 -- Maximum number of Blue Armor groups
|
||||||
local SPAWN_SCHED_BLUE_ARMOR = 200 -- Base spawn frequency for Blue Armor (seconds)
|
local SPAWN_SCHED_BLUE_ARMOR = 200 -- Base spawn frequency for Blue Armor (seconds)
|
||||||
|
|
||||||
local ASSIGN_TASKS_SCHED = 900 -- How often to reassign tasks to idle groups (seconds)
|
local ASSIGN_TASKS_SCHED = 900 -- How often to reassign tasks to idle groups (seconds)
|
||||||
@ -133,7 +138,8 @@ 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 = 45000 -- 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
|
||||||
@ -562,7 +568,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
|
||||||
@ -632,9 +638,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
|
||||||
@ -682,7 +689,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
|
||||||
@ -752,9 +759,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
|
||||||
@ -1189,8 +1197,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",
|
||||||
@ -1200,10 +1210,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
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user