Fixed routing issues with defenders/attackers.

This commit is contained in:
iTracerFacer
2025-11-17 12:05:26 -06:00
parent c42bf0451f
commit 0bdf775feb
+52 -40
View File
@@ -291,11 +291,21 @@ local zoneGarrisons = {}
-- Structure: groupGarrisonAssignments[groupName] = zoneName (or nil if not a defender) -- Structure: groupGarrisonAssignments[groupName] = zoneName (or nil if not a defender)
local groupGarrisonAssignments = {} local groupGarrisonAssignments = {}
-- Track all groups spawned by this plugin
-- Structure: spawnedGroups[groupName] = true
local spawnedGroups = {}
-- Reusable SET_GROUP to prevent repeated creation within a single function call -- Reusable SET_GROUP to prevent repeated creation within a single function call
local function getAllGroups() local function getAllGroups()
-- This must be called every time to get a fresh list of all groups, including newly spawned ones. -- Only return groups that were spawned by this plugin
-- The :FilterActive() ensures we only work with groups that are currently alive. local groupSet = SET_GROUP:New()
return SET_GROUP:New():FilterActive() for groupName, _ in pairs(spawnedGroups) do
local group = GROUP:FindByName(groupName)
if group and group:IsAlive() then
groupSet:AddGroup(group)
end
end
return groupSet
end end
-- Function to get zones controlled by a specific coalition -- Function to get zones controlled by a specific coalition
@@ -488,7 +498,7 @@ local function ElectDefender(group, zone, reason)
garrison.lastUpdate = timer.getTime() garrison.lastUpdate = timer.getTime()
-- Assign patrol task for the zone -- Assign patrol task for the zone
group:PatrolZones({zone}, 20, "Cone", 30, 60) group:TaskRouteToZone(zone, true)
env.info(string.format("[DGB PLUGIN] Elected %s as defender of zone %s (%s)", groupName, zoneName, reason)) env.info(string.format("[DGB PLUGIN] Elected %s as defender of zone %s (%s)", groupName, zoneName, reason))
return true return true
@@ -568,11 +578,14 @@ end
-- Function to assign tasks to all groups -- Function to assign tasks to all groups
local function AssignTasksToGroups() local function AssignTasksToGroups()
env.info("[DGB PLUGIN] ============================================")
env.info("[DGB PLUGIN] Starting task assignment cycle...") env.info("[DGB PLUGIN] Starting task assignment cycle...")
local allGroups = getAllGroups() local allGroups = getAllGroups()
local tasksAssigned = 0 local tasksAssigned = 0
local defendersActive = 0 local defendersActive = 0
local mobileAssigned = 0 local mobileAssigned = 0
local groupsProcessed = 0
local groupsSkipped = 0
-- Create a quick lookup table for zone objects by name -- Create a quick lookup table for zone objects by name
local zoneLookup = {} local zoneLookup = {}
@@ -586,8 +599,10 @@ local function AssignTasksToGroups()
allGroups:ForEachGroup(function(group) allGroups:ForEachGroup(function(group)
if not group or not group:IsAlive() then return end if not group or not group:IsAlive() then return end
groupsProcessed = groupsProcessed + 1
local groupName = group:GetName() local groupName = group:GetName()
local groupCoalition = group:GetCoalition() local groupCoalition = group:GetCoalition()
env.info(string.format("[DGB PLUGIN] Processing group %s (coalition %d)", groupName, groupCoalition))
-- 1. HANDLE DEFENDERS -- 1. HANDLE DEFENDERS
if IsDefender(group) then if IsDefender(group) then
@@ -595,9 +610,10 @@ local function AssignTasksToGroups()
local assignedZoneName = groupGarrisonAssignments[groupName] local assignedZoneName = groupGarrisonAssignments[groupName]
local zoneInfo = zoneLookup[assignedZoneName] local zoneInfo = zoneLookup[assignedZoneName]
if zoneInfo then if zoneInfo then
-- Ensure defender patrols its assigned zone -- Ensure defender routes to and patrols its assigned zone
group:PatrolZones({ zoneInfo.zone }, 20, "Cone", 30, 60) group:TaskRouteToZone(zoneInfo.zone, true)
tasksAssigned = tasksAssigned + 1 tasksAssigned = tasksAssigned + 1
env.info(string.format("[DGB PLUGIN] %s: Defender patrolling zone %s", groupName, assignedZoneName))
end end
return -- Defenders do not get any other tasks return -- Defenders do not get any other tasks
end end
@@ -606,13 +622,18 @@ local function AssignTasksToGroups()
-- Skip infantry if movement is disabled -- Skip infantry if movement is disabled
if IsInfantryGroup(group) and not MOVING_INFANTRY_PATROLS then if IsInfantryGroup(group) and not MOVING_INFANTRY_PATROLS then
env.info(string.format("[DGB PLUGIN] %s: Skipped (infantry movement disabled)", groupName))
groupsSkipped = groupsSkipped + 1
return return
end end
-- Don't reassign if already moving -- Don't reassign if already moving
local velocity = group:GetVelocityVec3() local velocity = group:GetVelocityVec3()
local speed = math.sqrt(velocity.x^2 + velocity.y^2 + velocity.z^2) local speed = math.sqrt(velocity.x^2 + velocity.y^2 + velocity.z^2)
env.info(string.format("[DGB PLUGIN] %s: Current speed %.2f m/s", groupName, speed))
if speed > 0.5 then if speed > 0.5 then
env.info(string.format("[DGB PLUGIN] %s: Skipped (already moving)", groupName))
groupsSkipped = groupsSkipped + 1
return return
end end
@@ -628,7 +649,7 @@ local function AssignTasksToGroups()
end end
end end
-- 3. HANDLE GROUPS IN FRIENDLY ZONES (Contested Defense Priority) -- 3. HANDLE GROUPS IN FRIENDLY ZONES
if currentZone and currentZoneCapture:GetCoalition() == groupCoalition then if currentZone and currentZoneCapture:GetCoalition() == groupCoalition then
local zoneName = currentZone:GetName() local zoneName = currentZone:GetName()
@@ -636,15 +657,25 @@ local function AssignTasksToGroups()
local zoneState = currentZoneCapture.GetCurrentState and currentZoneCapture:GetCurrentState() or nil local zoneState = currentZoneCapture.GetCurrentState and currentZoneCapture:GetCurrentState() or nil
if zoneState == "Attacked" then if zoneState == "Attacked" then
env.info(string.format("[DGB PLUGIN] %s defending contested zone %s", groupName, zoneName)) env.info(string.format("[DGB PLUGIN] %s defending contested zone %s", groupName, zoneName))
group:PatrolZones({ currentZone }, 20, "Cone", 30, 60) group:TaskRouteToZone(currentZone, true)
tasksAssigned = tasksAssigned + 1 tasksAssigned = tasksAssigned + 1
mobileAssigned = mobileAssigned + 1 mobileAssigned = mobileAssigned + 1
return return
end end
-- PRIORITY 2: Defender rotation (if enabled and zone is over-garrisoned) -- PRIORITY 2: Elect as defender if zone needs one (before attacking)
if ZoneNeedsDefenders(zoneName) then
if ElectDefender(group, currentZone, "zone under-garrisoned") then
tasksAssigned = tasksAssigned + 1
defendersActive = defendersActive + 1
return
end
end
-- PRIORITY 3: Defender rotation (if enabled and zone is over-garrisoned)
if TryDefenderRotation(group, currentZone) then if TryDefenderRotation(group, currentZone) then
tasksAssigned = tasksAssigned + 1 tasksAssigned = tasksAssigned + 1
defendersActive = defendersActive + 1
return -- Rotated in as a defender, task is set return -- Rotated in as a defender, task is set
end end
end end
@@ -669,45 +700,24 @@ local function AssignTasksToGroups()
end end
if closestEnemyZone then if closestEnemyZone then
if closestDistance > MAX_PATROL_DISTANCE then env.info(string.format("[DGB PLUGIN] %s: Attacking enemy zone %s (%.1fkm away)",
-- Target is too far. Use simple route-to-zone to avoid path complexity. groupName, closestEnemyZone:GetName(), closestDistance / 1000))
local targetCoord = closestEnemyZone:GetCoordinate()
local heading = groupCoordinate:HeadingTo(targetCoord)
local intermediateCoord = groupCoordinate:Translate(MAX_PATROL_DISTANCE, heading)
-- Create a temporary zone for simple routing -- Route to a random point in the enemy zone and patrol
local intermediateZone = ZONE_RADIUS:New("IntermediateRoute_" .. groupName, intermediateCoord, PATROL_WAYPOINT_RADIUS) group:TaskRouteToZone(closestEnemyZone, true)
-- Use TaskRouteToZone instead of PatrolZones - creates simple 2-waypoint path
group:TaskRouteToZone(intermediateZone, true, 20, "Cone")
env.info(string.format("[DGB PLUGIN] %s: Routing to intermediate waypoint (target %s is %.1fkm away, using %.1fkm hop)",
groupName, closestEnemyZone:GetName(), closestDistance / 1000, MAX_PATROL_DISTANCE / 1000))
else
-- Target is close enough. Use normal patrol behavior.
env.info(string.format("[DGB PLUGIN] %s: Patrolling to enemy zone %s (%.1fkm away)",
groupName, closestEnemyZone:GetName(), closestDistance / 1000))
group:PatrolZones({ closestEnemyZone }, 20, "Cone", 30, 60)
end
tasksAssigned = tasksAssigned + 1 tasksAssigned = tasksAssigned + 1
mobileAssigned = mobileAssigned + 1 mobileAssigned = mobileAssigned + 1
return -- Task assigned, done with this group return -- Task assigned, done with this group
end end
-- 5. FALLBACK: NO ENEMY ZONES - Become defender if in friendly zone and zone needs defenders -- 5. FALLBACK: Idle in current zone if no tasks available
if currentZone and currentZoneCapture and currentZoneCapture:GetCoalition() == groupCoalition then env.info(string.format("[DGB PLUGIN] %s: No tasks available (no enemy zones found)", groupName))
local zoneName = currentZone:GetName()
if ZoneNeedsDefenders(zoneName) then
if ElectDefender(group, currentZone, "no enemy zones available") then
tasksAssigned = tasksAssigned + 1
defendersActive = defendersActive + 1
end
end
end
end) end)
env.info(string.format("[DGB PLUGIN] Task assignment complete. %d groups tasked (%d defenders, %d mobile).", tasksAssigned, defendersActive, mobileAssigned)) env.info(string.format("[DGB PLUGIN] Task assignment complete. Processed: %d, Skipped: %d, Tasked: %d (%d defenders, %d mobile)",
groupsProcessed, groupsSkipped, tasksAssigned, defendersActive, mobileAssigned))
env.info("[DGB PLUGIN] ============================================")
end end
-- Function to monitor and announce warehouse status -- Function to monitor and announce warehouse status
@@ -981,8 +991,10 @@ local function ScheduleSpawner(spawnObject, getZonesFn, warehouses, baseFrequenc
local spawnedGroup = spawnObject:SpawnInZone(chosenZone, true) local spawnedGroup = spawnObject:SpawnInZone(chosenZone, true)
if spawnedGroup then if spawnedGroup then
local groupName = spawnedGroup:GetName()
spawnedGroups[groupName] = true
env.info(string.format("[DGB PLUGIN] Spawned %s in zone %s. Task assignment will occur on next cycle.", env.info(string.format("[DGB PLUGIN] Spawned %s in zone %s. Task assignment will occur on next cycle.",
spawnedGroup:GetName(), chosenZone:GetName())) groupName, chosenZone:GetName()))
end end
else else
env.info(string.format("[DGB PLUGIN] %s spawn skipped (no friendly zones).", label)) env.info(string.format("[DGB PLUGIN] %s spawn skipped (no friendly zones).", label))