mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
1008 lines
44 KiB
Lua
1008 lines
44 KiB
Lua
--[[
|
|
Script: Moose_DynamicGroundBattle.lua
|
|
Written by: [F99th-TracerFacer]
|
|
Version: 1.0.3
|
|
Date: 11 November 2024
|
|
Updated: 12 November 2024
|
|
Description: This script creates a dynamic ground battle between Red and Blue coalitions
|
|
along a series of zones which can be arranged in a line or any other configuration creating a dynamic ground battle.
|
|
|
|
Capture Zone Behavior
|
|
- Zone Capture states: Captured, Guarded, Empty, Attacked, Neutral
|
|
- Zone Colors: Red, Blue, Green, Orange
|
|
Red: Captured by Red
|
|
Blue: Captured by Blue
|
|
Orange: Contested
|
|
Green: Empty
|
|
|
|
Spawning And Patrol Behavior:
|
|
- Infantry and armor groups for both sides spawn at random locations in their own zones.
|
|
- Each group then calculates the shortest distance to the nearest enemy zone and moves to that zone to patrol.
|
|
- Every ASSIGN_TASKS_SCHED seconds, the script will check the ZONE_CAPTURE states of all zones and assign tasks to groups accordingly.
|
|
- Any group NOT moving, will recieve orders to patrol the nearest enemy zone. Any unit already moving will be left alone.
|
|
- Any troops dropped off through CTLD in these zones will begin to obey these orders as well.
|
|
- Spawn frequency calculated based on the number of alive warehouses.
|
|
- Infantry can be disabled from moving patrols if desired.
|
|
- In the event of DCS assigning a ridiculous path to an object, simply stop the object and it will be reassigned a new patrol path next round.
|
|
|
|
Warehouse System & Spawn Frequencey Behavior:
|
|
1. Warehouses:
|
|
- Each side (Red and Blue) has a set of warehouses defined in the `redWarehouses` and `blueWarehouses` tables.
|
|
- The number of warehouses can be adjusted by adding or removing entries from these tables and ensuring there is a matching object in the mission editor.
|
|
|
|
2. Spawn Frequency Calculation:
|
|
- The function `CalculateSpawnFrequency` calculates the spawn frequency based on the number of alive warehouses.
|
|
- The spawn frequency is a ratio of alive warehouses to total warehouses.
|
|
- If all warehouses are alive, the spawn frequency is (100%) of the base setting.
|
|
- If half of the warehouses are alive, the spawn frequency is (50%).
|
|
- If no warehouses are alive, the spawn frequency is (0%) (no more spawns).
|
|
- So for example, if you set your spawn frequency to 300 seconds, and only 50% of your warehouses are alive, the actual spawn frequency will be 600 seconds.
|
|
- This dynamic adjustment ensures that the reinforcement rate is directly impacted by the number of operational warehouses.
|
|
|
|
3. Mark points are automatically added to the map for each warehouse.
|
|
- Include the warehouse name and a list of nearby ground units within a specified radius.
|
|
- Uppdated every `UPDATE_MARK_POINTS_SCHED` seconds.
|
|
- The maximum distance to search for units near a warehouse is defined by the `MAX_WAREHOUSE_UNIT_LIST_DISTANCE` variable.
|
|
- The mark points are displayed to all players on the map as a form of "intel" on the battlefield.
|
|
- Can be disabled by setting `ENABLE_WAREHOUSE_MARKERS` to false.
|
|
|
|
General Setup Requirements:
|
|
- The script relies on the MOOSE framework for DCS World. Ensure that the MOOSE framework is installed and running on the mission.
|
|
- Ensure that all groups and zones mentioned below are created in the mission editor. You can adjust the names of the groups and zones as needed or
|
|
add more groups and zones to the script. Ensure that the names in this script match the names in the mission editor.
|
|
|
|
|
|
Groups and Zones to be created in the editor (all LATE ACTIVATE):
|
|
- Red Infantry Groups: RedInfantry1, RedInfantry2, RedInfantry3, RedInfantry4, RedInfantry5, RedInfantry6
|
|
- Red Armor Groups: RedArmor1, RedArmor2, RedArmor3, RedArmor4, RedArmor5, RedArmor6
|
|
- Blue Infantry Groups: BlueInfantry1, BlueInfantry2, BlueInfantry3, BlueInfantry4, BlueInfantry5, BlueInfantry6
|
|
- Blue Armor Groups: BlueArmor1, BlueArmor2, BlueArmor3, BlueArmor4, BlueArmor5
|
|
|
|
- Red Zones: FrontLine1, FrontLine2, FrontLine3, FrontLine4, FrontLine5, FrontLine6
|
|
- Blue Zones: FrontLine7, FrontLine8, FrontLine9, FrontLine10, FrontLine11, FrontLine12
|
|
|
|
- Red Warehouses: RedWarehouse1-1, RedWarehouse2-1, RedWarehouse3-1, RedWarehouse4-1, RedWarehouse5-1, RedWarehouse6-1
|
|
- Blue Warehouses: BlueWarehouse1-1, BlueWarehouse2-1, BlueWarehouse3-1, BlueWarehouse4-1, BlueWarehouse5-1, BlueWarehouse6-1
|
|
- ** Note Warehouse names are based on the static "unit name" in the mission editor. **
|
|
|
|
--]]
|
|
|
|
--[[
|
|
--If you don't have command centers setup in another file, uncommnent this section below:
|
|
-- Create Command Centers and Missions for each side
|
|
-- Must have a blue unit named "BLUEHQ" and a red unit named "REDHQ" in the mission editor.
|
|
|
|
--Build Command Center and Mission for Blue
|
|
US_CC = COMMANDCENTER:New( GROUP:FindByName( "BLUEHQ" ), "USA HQ" )
|
|
US_Mission = MISSION:New( US_CC, "Insurgent Sandstorm", "Primary", "Clear the front lines of enemy activity.", coalition.side.BLUE)
|
|
US_Score = SCORING:New( "Insurgent Sandstorm - Blue" )
|
|
US_Mission:AddScoring( US_Score )
|
|
US_Mission:Start()
|
|
US_Score:SetMessagesHit(false)
|
|
US_Score:SetMessagesDestroy(false)
|
|
US_Score:SetMessagesScore(false)
|
|
|
|
--Build Command Center and Mission Red
|
|
RU_CC = COMMANDCENTER:New( GROUP:FindByName( "REDHQ" ), "Russia HQ" )
|
|
RU_Mission = MISSION:New (RU_CC, "Insurgent Sandstorm", "Primary", "Destroy U.S. and NATO forces.", coalition.side.RED)
|
|
RU_Score = SCORING:New("Insurgent Sandstorm - Red")
|
|
RU_Mission:AddScoring( RU_Score)
|
|
RU_Mission:Start()
|
|
RU_Score:SetMessagesHit(false)
|
|
RU_Score:SetMessagesDestroy(false)
|
|
RU_Score:SetMessagesScore(false)
|
|
|
|
]]
|
|
|
|
-- Infantry Patrol Settings
|
|
-- Due to some maps or locations where infantry moving is either not desired or has problems with the terrain you can disable infantry moving patrols.
|
|
-- Set to false, infantry units will spawn, and never move from their spawn location. This could be considered a defensive position and probably a good idea.
|
|
local ENABLE_CAPTURE_ZONE_MESSAGES = false -- Enable or disable attack messages when a zone is attacked.
|
|
local MOVING_INFANTRY_PATROLS = false
|
|
local ENABLE_WAREHOUSE_MARKERS = true -- Enable or disable the warehouse markers on the map.
|
|
local UPDATE_MARK_POINTS_SCHED = 60 -- Update the map markers for warehouses every 300 seconds. ENABLE_WAREHOUSE_MARKERS must be set to true for this to work.
|
|
|
|
-- Control Spawn frequency and limits of ground units.
|
|
local INIT_RED_INFANTRY = 5 -- Initial number of Red Infantry groups
|
|
local MAX_RED_INFANTRY = 25 -- Maximum number of Red Infantry groups
|
|
local SPAWN_SCHED_RED_INFANTRY = 1800 -- Spawn Red Infantry groups every 1800 seconds
|
|
|
|
local INIT_RED_ARMOR = 15 -- Initial number of Red Armor groups
|
|
local MAX_RED_ARMOR = 200 -- Maximum number of Red Armor groups
|
|
local SPAWN_SCHED_RED_ARMOR = 600 -- Spawn Red Armor groups every 300 seconds
|
|
|
|
local INIT_BLUE_INFANTRY = 5 -- Initial number of Blue Infantry groups
|
|
local MAX_BLUE_INFANTRY = 25 -- Maximum number of Blue Infantry groups
|
|
local SPAWN_SCHED_BLUE_INFANTRY = 1800 -- Spawn Blue Infantry groups every 1800 seconds
|
|
|
|
local INIT_BLUE_ARMOR = 15 -- Initial number of Blue Armor groups0
|
|
local MAX_BLUE_ARMOR = 200 -- Maximum number of Blue Armor groups
|
|
local SPAWN_SCHED_BLUE_ARMOR = 60 -- Spawn Blue Armor groups every 300 seconds
|
|
|
|
local ASSIGN_TASKS_SCHED = 900 -- Assign tasks to groups every 600 seconds. New groups added will wait this long before moving.
|
|
|
|
|
|
|
|
|
|
|
|
-- Define capture zones for each side with a visible radius.
|
|
-- These zones will be used to create capture zones for each side. The capture zones will be used to determine the state of each zone (captured, guarded, empty, attacked, neutral).
|
|
-- The zones will also be used to spawn ground units for each side.
|
|
-- The zones should be created in the mission editor and named accordingly.
|
|
-- You can add more zones as needed. The script will create capture zones for each zone and assign tasks to groups based on the zone states.
|
|
-- Maybe the zones are along a front line, or they follow a road, or they are scattered around the map. You can arrange the zones in any configuration you like.
|
|
|
|
local redZones = {
|
|
ZONE:New("FrontLine1"),
|
|
ZONE:New("FrontLine2"),
|
|
ZONE:New("FrontLine3"),
|
|
ZONE:New("FrontLine4"),
|
|
ZONE:New("FrontLine5"),
|
|
ZONE:New("FrontLine6"),
|
|
ZONE:New("FrontLine7"),
|
|
ZONE:New("FrontLine8")
|
|
}
|
|
|
|
local blueZones = {
|
|
ZONE:New("FrontLine9"),
|
|
ZONE:New("FrontLine10"),
|
|
ZONE:New("FrontLine11"),
|
|
ZONE:New("FrontLine12"),
|
|
ZONE:New("FrontLine13"),
|
|
ZONE:New("FrontLine14"),
|
|
ZONE:New("FrontLine15"),
|
|
ZONE:New("FrontLine16")
|
|
}
|
|
|
|
-- Define warehouses for each side. These warehouses will be used to calculate the spawn frequency of ground units.
|
|
-- The warehouses should be created in the mission editor and named accordingly.
|
|
local redWarehouses = {
|
|
STATIC:FindByName("RedWarehouse1-1"), -- Static units key of off unit name in mission editor rather than just the name field. weird. =\ (hours wasted! ha!)
|
|
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 templates for infantry and armor groups. These templates will be used to randomize the groups spawned in the zones.
|
|
-- The templates should be created in the mission editor and named accordingly.
|
|
-- You can add more templates as needed. The script will randomly select a template for each group spawned.
|
|
-- The more templates you make, the more variety you can add to the groups that are spawned.
|
|
local redInfantryTemplates = {
|
|
"RedInfantry1",
|
|
"RedInfantry2",
|
|
"RedInfantry3",
|
|
"RedInfantry4",
|
|
"RedInfantry5",
|
|
"RedInfantry6"
|
|
}
|
|
|
|
local redArmorTemplates = {
|
|
"RedArmor1",
|
|
"RedArmor2",
|
|
"RedArmor3",
|
|
"RedArmor4",
|
|
"RedArmor5",
|
|
"RedArmor6",
|
|
"RedArmor7",
|
|
"RedArmor8",
|
|
"RedArmor9",
|
|
"RedArmor10"
|
|
|
|
}
|
|
|
|
local blueInfantryTemplates = {
|
|
"BlueInfantry1",
|
|
"BlueInfantry2",
|
|
"BlueInfantry3",
|
|
"BlueInfantry4",
|
|
"BlueInfantry5",
|
|
"BlueInfantry6"
|
|
}
|
|
|
|
local blueArmorTemplates = {
|
|
"BlueArmor1",
|
|
"BlueArmor2",
|
|
"BlueArmor3",
|
|
"BlueArmor4",
|
|
"BlueArmor5"
|
|
}
|
|
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- DO NOT EDIT BELOW THIS LINE
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Function to handle warehouse destruction
|
|
local function onWarehouseDestroyed(warehouseName, coalition)
|
|
local message = string.format("%s warehouse %s has been destroyed!", coalition, warehouseName)
|
|
MESSAGE:New(message, 15):ToAll()
|
|
USERSOUND:New("beeps-and-clicks.wav"):ToAll()
|
|
env.info(message)
|
|
end
|
|
|
|
-- Create an event handler class
|
|
local WarehouseEventHandler = EVENTHANDLER:New()
|
|
|
|
-- Define the event handler function
|
|
function WarehouseEventHandler:OnEventDead(EventData)
|
|
env.info("OnEventDead triggered")
|
|
if EventData then
|
|
local unitName = EventData.IniUnitName or EventData.IniDCSUnitName
|
|
if unitName then
|
|
|
|
-- Check red warehouses
|
|
for _, warehouse in ipairs(redWarehouses) do
|
|
if warehouse:GetName() == unitName then
|
|
onWarehouseDestroyed(unitName, "Red")
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Check blue warehouses
|
|
for _, warehouse in ipairs(blueWarehouses) do
|
|
if warehouse:GetName() == unitName then
|
|
onWarehouseDestroyed(unitName, "Blue")
|
|
return
|
|
end
|
|
end
|
|
|
|
local notWarehouseMessage = "Destroyed unit is not a warehouse: " .. unitName
|
|
env.info(notWarehouseMessage)
|
|
else
|
|
local noUnitMessage = "No unit name available in EventData"
|
|
env.info(noUnitMessage)
|
|
end
|
|
else
|
|
env.info("EventData is nil")
|
|
end
|
|
end
|
|
|
|
-- Set up the event handler globally
|
|
WarehouseEventHandler:HandleEvent(EVENTS.Dead)
|
|
|
|
-- Function to add mark points on the map for each warehouse in the provided list
|
|
local function addMarkPoints(warehouses, coalition)
|
|
for _, warehouse in ipairs(warehouses) do
|
|
if warehouse then
|
|
local warehousePos = warehouse:GetVec3()
|
|
local details
|
|
if warehouse:IsAlive() then
|
|
if coalition == 2 then
|
|
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
|
|
if warehouse:GetCoalition() == 1 then
|
|
details = "Warehouse: " .. warehouse:GetName() .. "\nThis warehouse needs to be protected.\nNearby Units:\n"
|
|
else
|
|
details = "Warehouse: " .. warehouse:GetName() .. "\nThis is a primary target as it is directly supplying enemy units.\n"
|
|
end
|
|
end
|
|
else
|
|
details = "Warehouse: " .. warehouse:GetName() .. "\nTHIS TARGET HAS BEEN DESTROYED"
|
|
end
|
|
|
|
local coordinate = COORDINATE:NewFromVec3(warehousePos)
|
|
local marker = MARKER:New(coordinate, details):ToCoalition(coalition):ReadOnly()
|
|
marker:Remove(UPDATE_MARK_POINTS_SCHED)
|
|
else
|
|
env.info("addMarkPoints: Warehouse not found or is nil")
|
|
end
|
|
end
|
|
end
|
|
|
|
local function updateMarkPoints()
|
|
addMarkPoints(redWarehouses, 2) -- Blue coalition sees red warehouses as targets
|
|
addMarkPoints(blueWarehouses, 2) -- Blue coalition sees blue warehouses as needing protection
|
|
addMarkPoints(redWarehouses, 1) -- Red coalition sees red warehouses as needing protection
|
|
addMarkPoints(blueWarehouses, 1) -- Red coalition sees blue warehouses as targets
|
|
end
|
|
|
|
-- If enabled, update the mark points for the warehouses every UPDATE_MARK_POINTS_SCHED seconds.
|
|
if ENABLE_WAREHOUSE_MARKERS then
|
|
SCHEDULER:New(nil, updateMarkPoints, {}, 10, UPDATE_MARK_POINTS_SCHED)
|
|
end
|
|
|
|
-- Table to keep track of zones and their statuses
|
|
local zoneStatuses = {}
|
|
|
|
-- Function to create a capture zone
|
|
local function CreateCaptureZone(zone, coalition)
|
|
local captureZone = ZONE_CAPTURE_COALITION:New(zone, coalition)
|
|
if captureZone then
|
|
local coordinate = captureZone:GetCoordinate()
|
|
if coordinate then
|
|
env.info("Created capture zone at coordinates: " .. coordinate:ToStringLLDMS())
|
|
captureZone:Start(5, 30) -- Check every 5 seconds, capture after 30 seconds
|
|
else
|
|
env.error("Failed to get coordinates for zone: " .. zone:GetName() .. " Did you add the group to the editor?")
|
|
end
|
|
else
|
|
env.error("Failed to create capture zone for zone: " .. zone:GetName() .. " Did you add the group to the editor?")
|
|
end
|
|
return captureZone
|
|
end
|
|
|
|
-- Custom OnEnterCaptured method
|
|
--- @param Functional.Protect#ZONE_CAPTURE_COALITION self
|
|
function ZONE_CAPTURE_COALITION:OnEnterCaptured(From, Event, To)
|
|
if From ~= To then
|
|
local Coalition = self:GetCoalition()
|
|
self:E({ Coalition = Coalition })
|
|
local zoneName = self:GetZoneName()
|
|
zoneStatuses[zoneName] = { zone = self, coalition = Coalition }
|
|
if Coalition == coalition.side.BLUE then
|
|
self:Smoke(SMOKECOLOR.Blue)
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {0, 0, 1}, 2) -- Draw the zone on the map for 30 seconds, blue color, and thickness 2
|
|
--US_CC:MessageTypeToCoalition(string.format("%s has been captured by the USA", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s has been captured by the USA", self:GetZoneName()), MESSAGE.Type.Information)
|
|
else
|
|
self:Smoke(SMOKECOLOR.Red)
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {1, 0, 0}, 2) -- Draw the zone on the map for 30 seconds, red color, and thickness 2
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s has been captured by Russia", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--US_CC:MessageTypeToCoalition(string.format("%s has been captured by Russia", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Custom OnEnterGuarded method
|
|
--- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self
|
|
function ZONE_CAPTURE_COALITION:OnEnterGuarded(From, Event, To)
|
|
if From ~= To then
|
|
local Coalition = self:GetCoalition()
|
|
self:E({ Coalition = Coalition })
|
|
local zoneName = self:GetZoneName()
|
|
zoneStatuses[zoneName] = { zone = self, coalition = Coalition }
|
|
if Coalition == coalition.side.BLUE then
|
|
self:Smoke(SMOKECOLOR.Blue)
|
|
-- Draw zone DARK BLUE for guarded
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {0, 0, 0.5}, 2) -- Draw the zone on the map for 30 seconds, dark blue color, and thickness 2
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
--US_CC:MessageTypeToCoalition(string.format("%s is under protection of the USA", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s is under protection of the USA", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
else
|
|
self:Smoke(SMOKECOLOR.Red)
|
|
-- Draw zone DARK RED for guarded
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {0.5, 0, 0}, 2) -- Draw the zone on the map for 30 seconds, dark red color, and thickness 2
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s is under protection of Russia", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--US_CC:MessageTypeToCoalition(string.format("%s is under protection of Russia", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Custom OnEnterEmpty method
|
|
--- @param Functional.Protect#ZONE_CAPTURE_COALITION self
|
|
function ZONE_CAPTURE_COALITION:OnEnterEmpty(From, Event, To)
|
|
if From ~= To then
|
|
self:E({ Coalition = "None" })
|
|
local zoneName = self:GetZoneName()
|
|
zoneStatuses[zoneName] = { zone = self, coalition = "None" }
|
|
self:Smoke(SMOKECOLOR.Green)
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {0, 1, 0}, 2) -- Draw the zone on the map for 30 seconds, green color, and thickness 2
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
--US_CC:MessageTypeToCoalition(string.format("%s is now empty", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s is now empty", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Custom OnEnterAttacked method
|
|
--- @param Functional.Protect#ZONE_CAPTURE_COALITION self
|
|
function ZONE_CAPTURE_COALITION:OnEnterAttacked(From, Event, To)
|
|
if From ~= To then
|
|
local Coalition = self:GetCoalition()
|
|
self:E({ Coalition = Coalition })
|
|
local zoneName = self:GetZoneName()
|
|
zoneStatuses[zoneName] = { zone = self, coalition = Coalition }
|
|
if Coalition == coalition.side.BLUE then
|
|
self:Smoke(SMOKECOLOR.Blue)
|
|
-- Draw the zone orange for contested
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {1, 0.5, 0}, 2) -- Draw the zone on the map for 30 seconds, orange color, and thickness 2
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
--US_CC:MessageTypeToCoalition(string.format("%s is under attack by Russia", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s is attacking the USA", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
else
|
|
self:Smoke(SMOKECOLOR.Red)
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {1, 0.5, 0}, 2) -- Draw the zone on the map for 30 seconds, orange color, and thickness 2
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s is under attack by the USA", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--US_CC:MessageTypeToCoalition(string.format("%s is attacking Russia", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Custom OnEnterNeutral method
|
|
--- @param Functional.Protect#ZONE_CAPTURE_COALITION self
|
|
function ZONE_CAPTURE_COALITION:OnEnterNeutral(From, Event, To)
|
|
if From ~= To then
|
|
self:E({ Coalition = "Neutral" })
|
|
local zoneName = self:GetZoneName()
|
|
zoneStatuses[zoneName] = { zone = self, coalition = "Neutral" }
|
|
self:Smoke(SMOKECOLOR.Green)
|
|
self:UndrawZone()
|
|
self:DrawZone(-1, {0, 1, 0}, 2) -- Draw the zone on the map for 30 seconds, green color, and thickness 2
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
--US_CC:MessageTypeToCoalition(string.format("%s is now neutral", self:GetZoneName()), MESSAGE.Type.Information)
|
|
--RU_CC:MessageTypeToCoalition(string.format("%s is now neutral", self:GetZoneName()), MESSAGE.Type.Information)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Create capture zones for Red and Blue
|
|
local redCaptureZones = {}
|
|
local blueCaptureZones = {}
|
|
|
|
-- Iterate over all red zones to create capture zones
|
|
for _, zone in ipairs(redZones) do
|
|
-- Attempt to create a capture zone for the current red zone
|
|
local captureZone = CreateCaptureZone(zone, coalition.side.RED)
|
|
if captureZone then
|
|
-- If successful, add the capture zone to the redCaptureZones table
|
|
table.insert(redCaptureZones, captureZone)
|
|
-- Log the creation of the capture zone
|
|
env.info("Created Red capture zone: " .. zone:GetName())
|
|
-- Draw the zone on the map with infinite duration, red color, and thickness 2
|
|
zone:DrawZone(30, {1, 0, 0}, 2)
|
|
-- Initialize the zone status
|
|
zoneStatuses[zone:GetName()] = { zone = captureZone, coalition = coalition.side.RED }
|
|
else
|
|
-- If creation fails, log an error message
|
|
env.error("Failed to create Red capture zone: " .. zone:GetName())
|
|
end
|
|
end
|
|
|
|
-- Iterate over all blue zones to create capture zones
|
|
for _, zone in ipairs(blueZones) do
|
|
-- Attempt to create a capture zone for the current blue zone
|
|
local captureZone = CreateCaptureZone(zone, coalition.side.BLUE)
|
|
if captureZone then
|
|
-- If successful, add the capture zone to the blueCaptureZones table
|
|
table.insert(blueCaptureZones, captureZone)
|
|
-- Log the creation of the capture zone
|
|
env.info("Created Blue capture zone: " .. zone:GetName())
|
|
-- Draw the zone on the map with infinite duration, blue color, and thickness 2
|
|
zone:DrawZone(30, {0, 0, 1}, 2)
|
|
-- Initialize the zone status
|
|
zoneStatuses[zone:GetName()] = { zone = captureZone, coalition = coalition.side.BLUE }
|
|
else
|
|
-- If creation fails, log an error message
|
|
env.error("Failed to create Blue capture zone: " .. zone:GetName())
|
|
end
|
|
end
|
|
|
|
-- Function to handle zone capture
|
|
local function OnZoneCaptured(event)
|
|
local zone = event.zone
|
|
local coalition = event.coalition
|
|
|
|
if zone and coalition then
|
|
env.info("OnZoneCaptured: Zone " .. zone:GetName() .. " captured by coalition " .. coalition)
|
|
|
|
-- Update the zone state
|
|
if coalition == coalition.side.RED then
|
|
zoneStates[zone:GetName()] = "RED"
|
|
zone:SetCoalition(coalition.side.RED)
|
|
elseif coalition == coalition.side.BLUE then
|
|
zoneStates[zone:GetName()] = "BLUE"
|
|
zone:SetCoalition(coalition.side.BLUE)
|
|
else
|
|
zoneStates[zone:GetName()] = "NEUTRAL"
|
|
zone:SetCoalition(coalition.side.NEUTRAL)
|
|
end
|
|
else
|
|
env.error("OnZoneCaptured: Invalid zone or coalition")
|
|
end
|
|
end
|
|
|
|
-- Function to handle zone guarded events
|
|
local function OnZoneGuarded(event)
|
|
local zone = event.Zone
|
|
local coalition = event.Coalition
|
|
|
|
if coalition == coalition.side.RED then
|
|
env.info("Red is guarding zone: " .. zone:GetName())
|
|
elseif coalition == coalition.side.BLUE then
|
|
env.info("Blue is guarding zone: " .. zone:GetName())
|
|
end
|
|
end
|
|
|
|
-- Function to handle zone empty events
|
|
local function OnZoneEmpty(event)
|
|
local zone = event.Zone
|
|
env.info("Zone is empty: " .. zone:GetName())
|
|
end
|
|
|
|
-- Function to handle zone attacked events
|
|
local function OnZoneAttacked(event)
|
|
local zone = event.Zone
|
|
local attackingGroups = zone:GetGroups()
|
|
local makeup = {}
|
|
|
|
for _, group in ipairs(attackingGroups) do
|
|
local groupName = group:GetName()
|
|
local unitTypes = {}
|
|
|
|
for _, unit in ipairs(group:GetUnits()) do
|
|
local unitType = unit:GetTypeName()
|
|
unitTypes[unitType] = (unitTypes[unitType] or 0) + 1
|
|
end
|
|
|
|
table.insert(makeup, {groupName = groupName, unitTypes = unitTypes})
|
|
end
|
|
|
|
local makeupMessage = ""
|
|
for _, groupInfo in ipairs(makeup) do
|
|
makeupMessage = makeupMessage .. "Group: " .. groupInfo.groupName .. "\n"
|
|
for unitType, count in pairs(groupInfo.unitTypes) do
|
|
makeupMessage = makeupMessage .. " " .. unitType .. ": " .. count .. "\n"
|
|
end
|
|
end
|
|
|
|
local messageText = "Zone is being attacked: " .. zone:GetName() .. "\n" .. makeupMessage
|
|
env.info(messageText)
|
|
|
|
-- Announce to the player
|
|
if ENABLE_CAPTURE_ZONE_MESSAGES then
|
|
MESSAGE:New(messageText, 15):ToAll()
|
|
end
|
|
end
|
|
|
|
-- Function to handle zone neutral events
|
|
local function OnZoneNeutral(event)
|
|
local zone = event.Zone
|
|
env.info("Zone is neutral: " .. zone:GetName())
|
|
end
|
|
|
|
-- Function to check the ZONE_CAPTURE states of all zones
|
|
local function CheckZoneStates()
|
|
env.info("Checking zone states...")
|
|
|
|
local zoneStates = {}
|
|
|
|
local function processZones(zones, zoneType)
|
|
env.info("Processing " .. zoneType)
|
|
env.info("Number of zones: " .. #zones)
|
|
|
|
local allGroups = SET_GROUP:New():FilterActive():FilterStart()
|
|
|
|
for _, zone in ipairs(zones) do
|
|
if zone then
|
|
env.info("processZones: Zone object is valid")
|
|
-- Check if the zone is of the correct type
|
|
if zone.ClassName == "ZONE_CAPTURE_COALITION" then
|
|
env.info("processZones: Zone is of type ZONE_CAPTURE_COALITION")
|
|
local coalition = zone:GetCoalition()
|
|
env.info("processZones: Zone coalition: " .. tostring(coalition))
|
|
if coalition == 1 then
|
|
zoneStates[zone:GetZoneName()] = "RED"
|
|
env.info("processZones: Zone: " .. (zone:GetZoneName() or "nil") .. " State: RED")
|
|
elseif coalition == 2 then
|
|
zoneStates[zone:GetZoneName()] = "BLUE"
|
|
env.info("processZones: Zone: " .. (zone:GetZoneName() or "nil") .. " State: BLUE")
|
|
else
|
|
zoneStates[zone:GetZoneName()] = "NEUTRAL"
|
|
env.info("processZones: Zone: " .. (zone:GetZoneName() or "nil") .. " State: NEUTRAL")
|
|
end
|
|
|
|
local groupsInZone = {}
|
|
allGroups:ForEachGroup(function(group)
|
|
if group then
|
|
env.info("processZones: Checking group: " .. group:GetName())
|
|
if group.IsCompletelyInZone then
|
|
if group:IsCompletelyInZone(zone) then
|
|
table.insert(groupsInZone, group)
|
|
end
|
|
else
|
|
env.error("processZones: IsCompletelyInZone method not found in group: " .. group:GetName())
|
|
-- Log available methods on the group object
|
|
for k, v in pairs(group) do
|
|
env.info("processZones: Group method: " .. tostring(k) .. " = " .. tostring(v))
|
|
end
|
|
end
|
|
else
|
|
env.error("processZones: Invalid group")
|
|
end
|
|
end)
|
|
|
|
env.info("processZones: Number of groups in zone: " .. #groupsInZone)
|
|
else
|
|
env.error("processZones: Zone is not of type ZONE_CAPTURE_COALITION")
|
|
-- Log available methods on the zone object
|
|
for k, v in pairs(zone) do
|
|
env.info("processZones: Zone method: " .. tostring(k) .. " = " .. tostring(v))
|
|
end
|
|
end
|
|
if not zone.GetZoneName then
|
|
env.error("processZones: Missing GetZoneName method in " .. zoneType)
|
|
end
|
|
else
|
|
env.error("processZones: Invalid zone in " .. zoneType)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
processZones(redCaptureZones, "redZones")
|
|
processZones(blueCaptureZones, "blueZones")
|
|
|
|
-- Log the zoneStates table
|
|
for zoneName, state in pairs(zoneStates) do
|
|
env.info("CheckZoneStates: Zone: " .. zoneName .. " State: " .. state)
|
|
end
|
|
|
|
return zoneStates
|
|
end
|
|
|
|
-- Function to assign tasks to groups
|
|
local function AssignTasks(group, zoneStates)
|
|
if not group or not group.GetCoalition or not group.GetCoordinate or not group.GetVelocity then
|
|
env.info("AssignTasks: Invalid group or missing methods")
|
|
return
|
|
end
|
|
|
|
local velocity = group:GetVelocityVec3()
|
|
local speed = math.sqrt(velocity.x^2 + velocity.y^2 + velocity.z^2)
|
|
if speed > 0 then
|
|
env.info("AssignTasks: Group " .. group:GetName() .. " is already moving. No new orders sent.")
|
|
return
|
|
end
|
|
|
|
env.info("Assigning tasks to group: " .. group:GetName())
|
|
local groupCoalition = group:GetCoalition()
|
|
local groupCoordinate = group:GetCoordinate()
|
|
local closestZone = nil
|
|
local closestDistance = math.huge
|
|
|
|
env.info("Group Coalition: " .. tostring(groupCoalition))
|
|
env.info("Group Coordinate: " .. groupCoordinate:ToStringLLDMS())
|
|
|
|
for zoneName, state in pairs(zoneStates) do
|
|
env.info("Checking Zone: " .. zoneName .. " with state: " .. tostring(state))
|
|
|
|
-- Convert state to a number for comparison
|
|
local stateCoalition = (state == "RED" and 1) or (state == "BLUE" and 2) or nil
|
|
|
|
if stateCoalition and stateCoalition ~= groupCoalition then
|
|
local zone = ZONE:FindByName(zoneName)
|
|
if zone then
|
|
local zoneCoordinate = zone:GetCoordinate()
|
|
local distance = groupCoordinate:Get2DDistance(zoneCoordinate)
|
|
--env.info("Zone Coordinate: " .. zoneCoordinate:ToStringLLDMS())
|
|
--env.info("Distance to zone " .. zoneName .. ": " .. distance)
|
|
if distance < closestDistance then
|
|
closestDistance = distance
|
|
closestZone = zone
|
|
env.info("New closest zone: " .. zoneName .. " with distance: " .. distance)
|
|
end
|
|
else
|
|
env.info("AssignTasks: Zone not found - " .. zoneName)
|
|
end
|
|
else
|
|
env.info("Zone " .. zoneName .. " is already controlled by coalition: " .. tostring(state))
|
|
end
|
|
end
|
|
|
|
if closestZone then
|
|
env.info(group:GetName() .. " is moving to and patrolling zone " .. closestZone:GetName())
|
|
--MESSAGE:New(group:GetName() .. " is moving to and patrolling zone " .. closestZone:GetName(), 10):ToAll()
|
|
|
|
-- Create a patrol task using the GROUP:PatrolZones method
|
|
local patrolZones = {closestZone}
|
|
local speed = 20 -- Example speed, adjust as needed
|
|
local formation = "Cone" -- Example formation, adjust as needed
|
|
local delayMin = 30 -- Example minimum delay, adjust as needed
|
|
local delayMax = 60 -- Example maximum delay, adjust as needed
|
|
|
|
group:PatrolZones(patrolZones, speed, formation, delayMin, delayMax)
|
|
else
|
|
env.info("AssignTasks: No suitable zone found for group " .. group:GetName())
|
|
end
|
|
end
|
|
|
|
|
|
-- Function to check if a group contains infantry units
|
|
local function IsInfantryGroup(group)
|
|
env.info("IsInfantryGroup: Checking group: " .. group:GetName())
|
|
for _, unit in ipairs(group:GetUnits()) do
|
|
local unitTypeName = unit:GetTypeName()
|
|
env.info("IsInfantryGroup: Checking unit: " .. unit:GetName() .. " with type: " .. unitTypeName)
|
|
if unitTypeName:find("Infantry") or unitTypeName:find("Soldier") or unitTypeName:find("Paratrooper") then
|
|
env.info("IsInfantryGroup: Found infantry unit in group: " .. group:GetName())
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Function to assign tasks to groups
|
|
local function AssignTasksToGroups()
|
|
env.info("AssignTasksToGroups: Starting task assignments")
|
|
local zoneStates = CheckZoneStates()
|
|
local allGroups = SET_GROUP:New():FilterActive():FilterStart()
|
|
|
|
local function processZone(zone, zoneColor)
|
|
if zone then
|
|
env.info("AssignTasksToGroups: Processing " .. zoneColor .. " zone: " .. zone:GetName())
|
|
local groupsInZone = {}
|
|
allGroups:ForEachGroup(function(group)
|
|
if group then
|
|
if group.IsCompletelyInZone then
|
|
if group:IsCompletelyInZone(zone) then
|
|
table.insert(groupsInZone, group)
|
|
end
|
|
else
|
|
env.error("AssignTasksToGroups: IsCompletelyInZone method not found in group: " .. group:GetName())
|
|
for k, v in pairs(group) do
|
|
env.info("AssignTasksToGroups: Group method: " .. tostring(k) .. " = " .. tostring(v))
|
|
end
|
|
end
|
|
else
|
|
env.error("AssignTasksToGroups: Invalid group")
|
|
end
|
|
end)
|
|
env.info("AssignTasksToGroups: Found " .. #groupsInZone .. " groups in " .. zoneColor .. " zone: " .. zone:GetName())
|
|
for _, group in ipairs(groupsInZone) do
|
|
if IsInfantryGroup(group) == true then
|
|
if MOVING_INFANTRY_PATROLS == true then
|
|
env.info("AssignTasksToGroups: Assigning tasks to infantry group: " .. group:GetName())
|
|
AssignTasks(group, zoneStates)
|
|
else
|
|
env.info("AssignTasksToGroups: Skipping infantry group: " .. group:GetName())
|
|
end
|
|
else
|
|
env.info("AssignTasksToGroups: Assigning tasks to group: " .. group:GetName())
|
|
AssignTasks(group, zoneStates)
|
|
end
|
|
end
|
|
else
|
|
env.info("AssignTasksToGroups: Invalid " .. zoneColor .. " zone")
|
|
end
|
|
end
|
|
|
|
for _, zone in ipairs(redZones) do
|
|
processZone(zone, "red")
|
|
end
|
|
|
|
for _, zone in ipairs(blueZones) do
|
|
processZone(zone, "blue")
|
|
end
|
|
|
|
env.info("AssignTasksToGroups: Task assignments completed. Running again in " .. ASSIGN_TASKS_SCHED .. " seconds.")
|
|
end
|
|
|
|
|
|
-- Function to calculate spawn frequency in seconds
|
|
local function CalculateSpawnFrequency(warehouses, baseFrequency)
|
|
local totalWarehouses = #warehouses
|
|
local aliveWarehouses = 0
|
|
|
|
for _, warehouse in ipairs(warehouses) do
|
|
local life = warehouse:GetLife()
|
|
if life and life > 0 then
|
|
aliveWarehouses = aliveWarehouses + 1
|
|
end
|
|
end
|
|
|
|
if totalWarehouses == 0 or aliveWarehouses == 0 then
|
|
return math.huge -- Stop spawning if there are no warehouses or no alive warehouses
|
|
end
|
|
|
|
local frequency = baseFrequency * (totalWarehouses / aliveWarehouses)
|
|
|
|
return frequency
|
|
end
|
|
|
|
local function CalculateSpawnFrequencyPercentage(warehouses)
|
|
local totalWarehouses = #warehouses
|
|
local aliveWarehouses = 0
|
|
|
|
for _, warehouse in ipairs(warehouses) do
|
|
local life = warehouse:GetLife()
|
|
if life and life > 0 then
|
|
aliveWarehouses = aliveWarehouses + 1
|
|
end
|
|
end
|
|
|
|
if totalWarehouses == 0 then
|
|
return 0 -- Avoid division by zero
|
|
end
|
|
|
|
local percentage = (aliveWarehouses / totalWarehouses) * 100
|
|
return math.floor(percentage)
|
|
end
|
|
|
|
-- Add event handlers for zone capture
|
|
for _, captureZone in ipairs(redCaptureZones) do
|
|
captureZone:OnEnterCaptured(OnZoneCaptured)
|
|
captureZone:OnEnterGuarded(captureZone.OnEnterGuarded)
|
|
captureZone:OnEnterEmpty(OnZoneEmpty)
|
|
captureZone:OnEnterAttacked(OnZoneAttacked)
|
|
captureZone:OnEnterNeutral(OnZoneNeutral)
|
|
end
|
|
|
|
for _, captureZone in ipairs(blueCaptureZones) do
|
|
captureZone:OnEnterCaptured(OnZoneCaptured)
|
|
captureZone:OnEnterGuarded(captureZone.OnEnterGuarded)
|
|
captureZone:OnEnterEmpty(OnZoneEmpty)
|
|
captureZone:OnEnterAttacked(OnZoneAttacked)
|
|
captureZone:OnEnterNeutral(OnZoneNeutral)
|
|
end
|
|
|
|
-- Calculate 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 spawn frequency percentages
|
|
local redSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(redWarehouses, coalition.side.RED)
|
|
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses, coalition.side.BLUE)
|
|
|
|
-- Display spawn frequency percentages to the user
|
|
MESSAGE:New("Red side spawn frequency: " .. redSpawnFrequencyPercentage .. "%", 30):ToRed()
|
|
MESSAGE:New("Blue side spawn frequency: " .. blueSpawnFrequencyPercentage .. "%", 30):ToBlue()
|
|
|
|
-- Schedule ground spawns using the calculated frequencies
|
|
redInfantrySpawn = SPAWN:New("RedInfantryGroup")
|
|
:InitRandomizeTemplate(redInfantryTemplates)
|
|
:InitRandomizeZones(redZones)
|
|
:InitLimit(INIT_RED_INFANTRY, MAX_RED_INFANTRY)
|
|
:SpawnScheduled(redInfantrySpawnFrequency, 0.5)
|
|
|
|
redArmorSpawn = SPAWN:New("RedArmorGroup")
|
|
:InitRandomizeTemplate(redArmorTemplates)
|
|
:InitRandomizeZones(redZones)
|
|
:InitLimit(INIT_RED_ARMOR, MAX_RED_ARMOR)
|
|
:SpawnScheduled(redArmorSpawnFrequency, 0.5)
|
|
|
|
blueInfantrySpawn = SPAWN:New("BlueInfantryGroup")
|
|
:InitRandomizeTemplate(blueInfantryTemplates)
|
|
:InitRandomizeZones(blueZones)
|
|
:InitLimit(INIT_BLUE_INFANTRY, MAX_BLUE_INFANTRY)
|
|
:SpawnScheduled(blueInfantrySpawnFrequency, 0.5)
|
|
|
|
blueArmorSpawn = SPAWN:New("BlueArmorGroup")
|
|
:InitRandomizeTemplate(blueArmorTemplates)
|
|
:InitRandomizeZones(blueZones)
|
|
:InitLimit(INIT_BLUE_ARMOR, MAX_BLUE_ARMOR)
|
|
:SpawnScheduled(blueArmorSpawnFrequency, 0.5)
|
|
|
|
env.info("Dynamic Ground Battle & Zone capture initialized.")
|
|
|
|
|
|
-- Function to monitor and announce warehouse status
|
|
local function MonitorWarehouses()
|
|
local blueWarehousesAlive = 0
|
|
local redWarehousesAlive = 0
|
|
|
|
for _, warehouse in ipairs(blueWarehouses) do
|
|
if warehouse:IsAlive() then
|
|
blueWarehousesAlive = blueWarehousesAlive + 1
|
|
end
|
|
end
|
|
|
|
for _, warehouse in ipairs(redWarehouses) do
|
|
if warehouse:IsAlive() then
|
|
redWarehousesAlive = redWarehousesAlive + 1
|
|
end
|
|
end
|
|
|
|
-- Debug messages to check values
|
|
env.info("MonitorWarehouses: blueWarehousesAlive = " .. blueWarehousesAlive)
|
|
env.info("MonitorWarehouses: redWarehousesAlive = " .. redWarehousesAlive)
|
|
|
|
-- Calculate 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 spawn frequency percentages
|
|
local redSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(redWarehouses)
|
|
local blueSpawnFrequencyPercentage = CalculateSpawnFrequencyPercentage(blueWarehouses)
|
|
|
|
-- Log the values
|
|
env.info("MonitorWarehouses: redInfantrySpawnFrequency = " .. redInfantrySpawnFrequency)
|
|
env.info("MonitorWarehouses: redArmorSpawnFrequency = " .. redArmorSpawnFrequency)
|
|
env.info("MonitorWarehouses: blueInfantrySpawnFrequency = " .. blueInfantrySpawnFrequency)
|
|
env.info("MonitorWarehouses: blueArmorSpawnFrequency = " .. blueArmorSpawnFrequency)
|
|
env.info("MonitorWarehouses: redSpawnFrequencyPercentage = " .. redSpawnFrequencyPercentage)
|
|
env.info("MonitorWarehouses: blueSpawnFrequencyPercentage = " .. blueSpawnFrequencyPercentage)
|
|
|
|
local msg = "[Warehouse status:]\n"
|
|
msg = msg .. "Red warehouses alive: " .. redWarehousesAlive .. " Reinforcements Capacity: " .. redSpawnFrequencyPercentage .. "%" .. "\n"
|
|
msg = msg .. "Blue warehouses alive: " .. blueWarehousesAlive .. " Reinforcements Capacity: " .. blueSpawnFrequencyPercentage .. "%" .. "\n"
|
|
MESSAGE:New(msg, 30):ToAll()
|
|
|
|
|
|
end
|
|
|
|
-- Function to check the wincondition. If either side owns all zones, mission ends.
|
|
local function checkWinCondition()
|
|
local blueOwned = true
|
|
local redOwned = true
|
|
|
|
for zoneName, owner in pairs(zoneStatuses) do
|
|
if owner ~= 1 then
|
|
redOwned = false
|
|
end
|
|
if owner ~= 2 then
|
|
blueOwned = false
|
|
end
|
|
end
|
|
|
|
if blueOwned then
|
|
MESSAGE:New("Blue side wins! They own all the capture zones.", 1800):ToAll()
|
|
USERSOUND:New("UsaTheme.ogg"):ToAll()
|
|
return true
|
|
elseif redOwned then
|
|
MESSAGE:New("Red side wins! They own all the capture zones.", 1800):ToAll()
|
|
USERSOUND:New("MotherRussia.ogg"):ToAll()
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Function to toggle capture zone messages
|
|
local function ToggleCaptureZoneMessages()
|
|
ENABLE_CAPTURE_ZONE_MESSAGES = not ENABLE_CAPTURE_ZONE_MESSAGES
|
|
local status = ENABLE_CAPTURE_ZONE_MESSAGES and "enabled" or "disabled"
|
|
MESSAGE:New("Capture zone messages are now " .. status, 15):ToAll()
|
|
end
|
|
|
|
-- Timer function to periodically check the win condition
|
|
local function monitorWinCondition()
|
|
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:New(nil, MonitorWarehouses, {}, 0, 120)
|
|
|
|
-- Scheduler to assign tasks to groups periodically
|
|
SCHEDULER:New(nil, AssignTasksToGroups, {}, 0, ASSIGN_TASKS_SCHED) -- Check every 600 seconds (10 minutes) - Adjust as needed
|
|
|
|
|
|
|
|
MENU_MISSION_COMMAND:New("Check Warehouse Status", missionMenu, MonitorWarehouses)
|
|
|
|
-- Add a menu item to toggle capture zone messages under the sub menu
|
|
MENU_MISSION_COMMAND:New("Toggle Capture Zone Messages", missionMenu, ToggleCaptureZoneMessages)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|