diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 361ddd1..81123cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,6 +92,11 @@ jobs: git tag ${{ steps.calculate_tag.outputs.new_tag }} git push origin ${{ steps.calculate_tag.outputs.new_tag }} + - name: Copy Release to Versioned File + run: | + cp ./output/spearhead.lua ./output/spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua + echo "Versioned file created: spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua" + - name: Create GitHub Release uses: actions/create-release@v1 id: create_release @@ -114,3 +119,13 @@ jobs: asset_name: spearhead.lua asset_content_type: application/octet-stream + - name: Upload Spearhead.lua Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./output/spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua + asset_name: spearhead.lua + asset_content_type: application/octet-stream + diff --git a/_docs/pages/reference.html b/_docs/pages/reference.html index 332cb11..f15ee47 100644 --- a/_docs/pages/reference.html +++ b/_docs/pages/reference.html @@ -62,6 +62,7 @@
  • SAM
  • DEAD
  • BAI
  • +
  • BAI
  • STRIKE
  • @@ -206,6 +207,13 @@ BAI missions involve battlefield air interdiction. These missions target enemy ground forces.

    +

    CAS

    +

    + CAS missions involve close air support for friendly ground forces.
    + CAS mission have a special "BattleManager" module added by defualt. This will make blue and red units shoot at eachoter.
    + This creates a nice effect.
    +

    +

    STRIKE

    STRIKE missions target strategic objectives, such as supply depots or command centers.
    diff --git a/_miz/SPEARHEADDEV.miz b/_miz/SPEARHEADDEV.miz index 9198c6b..b58e16a 100644 Binary files a/_miz/SPEARHEADDEV.miz and b/_miz/SPEARHEADDEV.miz differ diff --git a/classes/spearhead_base.lua b/classes/spearhead_base.lua index 39653d2..7ab860d 100644 --- a/classes/spearhead_base.lua +++ b/classes/spearhead_base.lua @@ -287,6 +287,48 @@ do -- INIT UTIL return UTIL.getConvexHull(allpoints) end + + ---Returns hull points visible from origin (not blocked by hull edges) +---@param hull Array +---@param origin Vec2 +---@return Array +function UTIL.GetVisibleHullPointsFromOrigin(hull, origin) + local function segmentsIntersect(a, b, c, d) + -- Helper: returns true if segment ab intersects cd (excluding endpoints) + local function ccw(p1, p2, p3) + return (p3.y - p1.y) * (p2.x - p1.x) > (p2.y - p1.y) * (p3.x - p1.x) + end + return (ccw(a, c, d) ~= ccw(b, c, d)) and (ccw(a, b, c) ~= ccw(a, b, d)) + end + + local n = #hull + local visible = {} + + for i = 1, n do + local p = hull[i] + local isVisible = true + + -- Check against all hull edges except those incident to p + for j = 1, n do + local a = hull[j] + local b = hull[(j % n) + 1] + -- skip edges incident to p + if (a ~= p and b ~= p) then + if segmentsIntersect(origin, p, a, b) then + isVisible = false + break + end + end + end + + if isVisible then + table.insert(visible, p) + end + end + + return visible +end + end Spearhead.Util = UTIL diff --git a/classes/spearhead_db.lua b/classes/spearhead_db.lua index 270e379..817a233 100644 --- a/classes/spearhead_db.lua +++ b/classes/spearhead_db.lua @@ -56,7 +56,8 @@ ---@field buildingKilos number? ---@class MissionZoneData ----@field Groups Array +---@field RedGroups Array +---@field BlueGroups Array ---@class MissionData ---@field Groups Array @@ -423,7 +424,14 @@ function Database.New(Logger) local missions = 0 for _, data in pairs(self._tables.MissionZoneData) do missions = missions + 1 - for _, groupName in pairs(data.Groups) do + for _, groupName in pairs(data.RedGroups) do + local group = Group.getByName(groupName) + if group then + totalUnits = totalUnits + group:getInitialSize() + end + end + + for _, groupName in pairs(data.BlueGroups) do local group = Group.getByName(groupName) if group then totalUnits = totalUnits + group:getInitialSize() @@ -645,13 +653,19 @@ function Database:loadMissionzoneUnits() local all_groups = getAvailableGroups() for _, missionZoneName in pairs(self._tables.MissionZones) do self._tables.MissionZoneData[missionZoneName] = { - Groups = {} + RedGroups = {}, + BlueGroups = {}, } local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName) for _, groupName in pairs(groups) do + local group = Group.getByName(groupName) + if group and group:getCoalition() == coalition.side.RED then + table.insert(self._tables.MissionZoneData[missionZoneName].RedGroups, groupName) + elseif group and group:getCoalition() == coalition.side.BLUE then + table.insert(self._tables.MissionZoneData[missionZoneName].BlueGroups, groupName) + end is_group_taken[groupName] = true - table.insert(self._tables.MissionZoneData[missionZoneName].Groups, groupName) end end end @@ -661,12 +675,19 @@ function Database:loadRandomMissionzoneUnits() local all_groups = getAvailableGroups() for _, missionZoneName in pairs(self._tables.RandomMissionZones) do self._tables.MissionZoneData[missionZoneName] = { - Groups = {} + RedGroups = {}, + BlueGroups = {}, } local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName) for _, groupName in pairs(groups) do + local group = Group.getByName(groupName) + if group and group:getCoalition() == coalition.side.RED then + table.insert(self._tables.MissionZoneData[missionZoneName].RedGroups, groupName) + elseif group and group:getCoalition() == coalition.side.BLUE then + table.insert(self._tables.MissionZoneData[missionZoneName].BlueGroups, groupName) + end + is_group_taken[groupName] = true - table.insert(self._tables.MissionZoneData[missionZoneName].Groups, groupName) end end end @@ -885,11 +906,9 @@ function Database:getRandomMissionsForStage(stagename) return stageZone.RandomMissionZones end ----@return Array -function Database:getGroupsForMissionZone(missionZoneName) - local missionZoneData = self._tables.MissionZoneData[missionZoneName] - if not missionZoneData then return {} end - return missionZoneData.Groups +---@return MissionZoneData? +function Database:getMissionDataForZone(missionZoneName) + return self._tables.MissionZoneData[missionZoneName] end ---@param stageName string diff --git a/classes/stageClasses/Groups/SpearheadGroup.lua b/classes/stageClasses/Groups/SpearheadGroup.lua index a915c74..50edd3d 100644 --- a/classes/stageClasses/Groups/SpearheadGroup.lua +++ b/classes/stageClasses/Groups/SpearheadGroup.lua @@ -6,13 +6,10 @@ ---@field private _isStatic boolean ---@field private _isSpawned boolean local SpearheadGroup = {} +SpearheadGroup.__index = SpearheadGroup function SpearheadGroup.New(groupName) - - SpearheadGroup.__index = SpearheadGroup - - local o = {} - local self = setmetatable(o, SpearheadGroup) + local self = setmetatable({}, SpearheadGroup) self._isStatic = Spearhead.DcsUtil.IsGroupStatic(groupName) == true self.groupName = groupName @@ -132,7 +129,7 @@ end ---comment ---@return table result list of objects -function SpearheadGroup:GetUnits() +function SpearheadGroup:GetObjects() local result = {} if self._isStatic == true then @@ -150,6 +147,24 @@ function SpearheadGroup:GetUnits() return result end + +---comment +---@return Array result list of objects +function SpearheadGroup:GetAsUnits() + + if self._isStatic == true then + return {} + end + + local result = {} + local group = Group.getByName(self.groupName) + if not group then return {} end + for _, unit in pairs(group:getUnits()) do + table.insert(result, unit) + end + return result +end + ---@return Array function SpearheadGroup:GetAllUnitPositions() @@ -167,7 +182,35 @@ function SpearheadGroup:GetAllUnitPositions() end end return result +end +function SpearheadGroup:SetInvisible() + + local group = Group.getByName(self.groupName) + if group then + local setInvisible = { + id = 'SetInvisible', + params = { + value = true + } + } + group:getController():setCommand(setInvisible) + end +end + + +function SpearheadGroup:SetVisible() + + local group = Group.getByName(self.groupName) + if group then + local setInvisible = { + id = 'SetInvisible', + params = { + value = false + } + } + group:getController():setCommand(setInvisible) + end end if not Spearhead.classes then Spearhead.classes = {} end diff --git a/classes/stageClasses/SpecialZones/BlueSam.lua b/classes/stageClasses/SpecialZones/BlueSam.lua index 27e7882..bdc2de8 100644 --- a/classes/stageClasses/SpecialZones/BlueSam.lua +++ b/classes/stageClasses/SpecialZones/BlueSam.lua @@ -53,7 +53,7 @@ function BlueSam.New(database, logger, zoneName) end - for _, unit in pairs(SpearheadGroup:GetUnits()) do + for _, unit in pairs(SpearheadGroup:GetObjects()) do if SpearheadGroup:GetCoalition() == 1 then table.insert(blueUnitsPos, unit:getPoint()) elseif SpearheadGroup:GetCoalition() == 2 then diff --git a/classes/stageClasses/SpecialZones/StageBase.lua b/classes/stageClasses/SpecialZones/StageBase.lua index 1487eca..dc20472 100644 --- a/classes/stageClasses/SpecialZones/StageBase.lua +++ b/classes/stageClasses/SpecialZones/StageBase.lua @@ -50,7 +50,7 @@ function StageBase.New(databaseManager, logger, airbaseName) local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName) table.insert(self._red_groups, shGroup) - for _, unit in pairs(shGroup:GetUnits()) do + for _, unit in pairs(shGroup:GetObjects()) do redUnitsPos[unit:getName()] = unit:getPoint() end @@ -61,7 +61,7 @@ function StageBase.New(databaseManager, logger, airbaseName) local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName) table.insert(self._blue_groups, shGroup) - for _, unit in pairs(shGroup:GetUnits()) do + for _, unit in pairs(shGroup:GetObjects()) do blueUnitsPos[unit:getName()] = unit:getPoint() end diff --git a/classes/stageClasses/helpers/BattleManager.lua b/classes/stageClasses/helpers/BattleManager.lua new file mode 100644 index 0000000..1926e8e --- /dev/null +++ b/classes/stageClasses/helpers/BattleManager.lua @@ -0,0 +1,232 @@ +---@class BattleManager +---@field private _name string +---@field private _logger Logger +---@field private _redGroups Array +---@field private _blueGroups Array +---@field private _redShootAtPoints Array +---@field private _blueShootAtPoints Array +---@field private _isActive boolean +local BattleManager = {} +BattleManager.__index = BattleManager + +---@param redGroups Array +---@param blueGroups Array +---@param name string +---@param logLevel LogLevel +---@return BattleManager +function BattleManager.New(redGroups, blueGroups, name, logLevel) + local self = setmetatable({}, BattleManager) + + self._isActive = false + self._name = name + self._logger = Spearhead.LoggerTemplate.new("BattleManager_" .. name, logLevel) + + self._redGroups = redGroups + self._blueGroups = blueGroups + + self._logger:debug("BattleManager created with name: " .. self._name + .. ", red groups: " .. #self._redGroups + .. ", blue groups: " .. #self._blueGroups) + + return self +end + +---@param self BattleManager +---@param time number +local function CheckTask(self, time) + local interval = self:Update() + if not interval then return end + return time + interval +end + +function BattleManager:Start() + self._logger:info("BattleManager started: " .. self._name) + self._isActive = true + self:SetAllInvisible() + + timer.scheduleFunction(CheckTask, self, timer.getTime() + 5) +end + +function BattleManager:Stop() + self._isActive = false + self:SetAllVisible() +end + +---@private +function BattleManager:SetAllInvisible() + for _, group in pairs(self._redGroups) do + group:SetInvisible() + end + + for _, group in pairs(self._blueGroups) do + group:SetInvisible() + end +end + + +---@private +function BattleManager:SetAllVisible() + for _, group in pairs(self._redGroups) do + group:SetVisible() + end + + for _, group in pairs(self._blueGroups) do + group:SetVisible() + end +end + +---comment +---@return number? +function BattleManager:Update() + if self._isActive == false then + return nil + end + + self._logger:debug("BattleManager Update called for " .. self._name) + + local shootChance = 1 -- Adjust this value to control the shooting probability (0.0 to 1.0) + + self:LetUnitsShoot(self._redGroups, self._blueGroups) + self:LetUnitsShoot(self._blueGroups, self._redGroups) + + return math.random(4, 10) -- Return a random interval between 5 and 10 seconds for the next update +end + +---@private +---@param fromGroups Array +---@param targetGroups Array +---@return Array +function BattleManager:CalculateShootAtPoints(fromGroups, targetGroups) + + ---@type Array + local result = {} + + for _, group in pairs(targetGroups) do + ---@type Array + local points = {} + + self._logger:debug("Processing red group: " .. group.groupName) + local groupUnits = group:GetAsUnits() + for _, unit in pairs(group:GetAsUnits()) do + local pos = unit:getPoint() + table.insert(points, {x = pos.x, y = pos.z}) + end + + local hull = Spearhead.Util.getConvexHull(points) + local enlargedHull = Spearhead.Util.enlargeConvexHull(hull, 30) -- Enlarge the hull by 50 meters + + local randomGroup = Spearhead.Util.randomFromList(fromGroups) --[[@as SpearheadGroup]] + if randomGroup then + + local randomUnit = Spearhead.Util.randomFromList(randomGroup:GetObjects()) --[[@as Object]] + local pos = randomUnit:getPoint() + + ---@type Vec2 + local vec2 = {x = pos.x, y = pos.z} + + local shootPoints = Spearhead.Util.GetVisibleHullPointsFromOrigin(enlargedHull, vec2) + for _, point in pairs(shootPoints) do + table.insert(result, point) + end + end + end + return result + +end + +---@private +---@param groups Array +---@param targetGroups Array +function BattleManager:LetUnitsShoot(groups, targetGroups) + + local shootChance = math.random(3, 7) / 10 + + for _, group in pairs(groups) do + + local units = group:GetAsUnits() + + for _, unit in pairs(units) do + + if self:IsUnitApplicable(unit) == true then + if math.random() <= shootChance then + local unitPos = unit:getPoint() + local point = self:GetRandomPoint({x = unitPos.x, y = unitPos.z }, targetGroups) + if point then + + local qty = 1 + local ammo = unit:getAmmo() + + if ammo and ammo[1] then + qty = math.ceil((ammo[1].count or 1) / 50) + end + + local shootTask = { + id = "FireAtPoint", + params = { + point = point, + radius = 1, + expendQty = qty, + expendQtyEnabled = true, + counterbattaryRadius = math.random(5, 10) + } + } + + self._logger:debug("Red unit " .. unit:getName() .. " will shoot " .. qty .. " rounds at point: " .. tostring(point)) + + local controller = unit:getController() + if controller then + controller:setTask(shootTask) + end + end + end + end + end + end +end + +---@private +---@param unit Unit +---@return boolean +function BattleManager:IsUnitApplicable(unit) + if not unit or not unit:isExist() then + return false + end + + if + unit:hasAttribute("AAA") == true + or unit:hasAttribute("Air Defence") == true + or unit:hasAttribute("Mobile AAA") == true + then + return false + end + + return true + +end + +---@private +---@param origin Vec2 +---@param groups Array +---@return Vec2? +function BattleManager:GetRandomPoint(origin, groups) + local group = Spearhead.Util.randomFromList(groups) --[[@as SpearheadGroup]] + if not group then return nil end + + local points = {} + local groupUnits = group:GetAsUnits() + for _, unit in pairs(group:GetAsUnits()) do + local pos = unit:getPoint() + table.insert(points, {x = pos.x, y = pos.z}) + end + + local hull = Spearhead.Util.getConvexHull(points) + local enlargedHull = Spearhead.Util.enlargeConvexHull(hull, 30) + local shootPoints = Spearhead.Util.GetVisibleHullPointsFromOrigin(enlargedHull, origin) + return Spearhead.Util.randomFromList(shootPoints) --[[@as Vec2]] +end + +if Spearhead == nil then Spearhead = {} end +if Spearhead.classes == nil then Spearhead.classes = {} end +if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end +if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end +Spearhead.classes.stageClasses.helpers.BattleManager = BattleManager diff --git a/classes/stageClasses/helpers/MissionCommandsHelper.lua b/classes/stageClasses/helpers/MissionCommandsHelper.lua index 75867c8..3474bd2 100644 --- a/classes/stageClasses/helpers/MissionCommandsHelper.lua +++ b/classes/stageClasses/helpers/MissionCommandsHelper.lua @@ -1,3 +1,4 @@ + ---@class MissionCommandsHelper ---@field missionsByCode table @table of missions by their code ---@field enabledByCode table @table of enabled missions by their code @@ -31,6 +32,7 @@ function MissionCommandsHelper.getOrCreate(logLevel) instance.pinnedByGroup = {} instance.lastUpdate = 0 instance._supplyHubGroups = {} + instance._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logLevel) ---comment diff --git a/classes/stageClasses/helpers/SupplyUnitsTracker.lua b/classes/stageClasses/helpers/SupplyUnitsTracker.lua index 3adc129..b7288f4 100644 --- a/classes/stageClasses/helpers/SupplyUnitsTracker.lua +++ b/classes/stageClasses/helpers/SupplyUnitsTracker.lua @@ -1,3 +1,6 @@ + +env.info("Spearhead SupplyUnitsTracker loaded") + ---@class SupplyUnitsTracker ---@field private _supplyUnitsByName table ---@field private _cargoInUnits table> @@ -404,7 +407,6 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit) end - if Spearhead == nil then Spearhead = {} end if Spearhead.classes == nil then Spearhead.classes = {} end if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end diff --git a/classes/stageClasses/missions/ZoneMission.lua b/classes/stageClasses/missions/ZoneMission.lua index e2e79a7..b67f1e2 100644 --- a/classes/stageClasses/missions/ZoneMission.lua +++ b/classes/stageClasses/missions/ZoneMission.lua @@ -5,11 +5,13 @@ ---@field private _dependencies table ---@field private _completeAtIndex number ---@field private _parentStage Stage +---@field private _battleManager? BattleManager local ZoneMission = {} --- @class MissionGroups --- @field hasTargets boolean ---- @field groups Array +--- @field redGroups Array +--- @field blueGroups Array --- @field unitsAlive table> --- @field targetsAlive table> --- @field groupNamesPerunit table @@ -38,6 +40,7 @@ local function ParseZoneName(input) local inputType = string.lower(split_name[2]) if inputType == "dead" then parsedType = "DEAD" end if inputType == "strike" then parsedType = "STRIKE" end + if inputType == "cas" then parsedType = "CAS" end if inputType == "bai" then parsedType = "BAI" end if inputType == "sam" then parsedType = "SAM" end @@ -91,7 +94,8 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage) end self._missionGroups = { - groups = {}, + redGroups = {}, + blueGroups = {}, unitsAlive = {}, targetsAlive = {}, hasTargets = false, @@ -107,7 +111,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage) end local completeAtIndex = database:getMissionCompleteAt(zoneName) - if completeAtIndex == nil and self.missionType == "BAI" then + if completeAtIndex == nil and self.missionType == "BAI" or self.missionType == "CAS" then self._completeAtIndex = 0.8 elseif completeAtIndex == nil then self._completeAtIndex = 1 @@ -118,13 +122,24 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage) self._logger:debug("Complete at index " .. self.zoneName .. ": " .. self._completeAtIndex) local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup - local groupNames = database:getGroupsForMissionZone(zoneName) - for _, groupName in pairs(groupNames) do + + local missionData = database:getMissionDataForZone(zoneName) + if not missionData then return end + + for _, groupName in pairs(missionData.BlueGroups) do local spearheadGroup = SpearheadGroup.New(groupName) - table.insert(self._missionGroups.groups, spearheadGroup) + if spearheadGroup then + table.insert(self._missionGroups.blueGroups, spearheadGroup) + end + spearheadGroup:Destroy() + end + + for _, groupName in pairs(missionData.RedGroups) do + local spearheadGroup = SpearheadGroup.New(groupName) + table.insert(self._missionGroups.redGroups, spearheadGroup) local isGroupTarget = Spearhead.Util.startswith(string.lower(groupName), "tgt_") - for _, unit in pairs(spearheadGroup:GetUnits()) do + for _, unit in pairs(spearheadGroup:GetObjects()) do local unitName = unit:getName() local isUnitTarget = Spearhead.Util.startswith(string.lower(unitName), "tgt_") @@ -148,10 +163,15 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage) Spearhead.Events.addOnUnitLostEventListener(unitName, self) end - Spearhead.DcsUtil.DestroyGroup(groupName) + spearheadGroup:Destroy() end - self._logger:debug("Mission " .. self.name .. " group count: " .. Spearhead.Util.tableLength(groupNames)) + if self.missionType == "CAS" then + self._battleManager = Spearhead.classes.stageClasses.helpers.BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel) + + end + + self._logger:debug("Mission " .. self.name .. " group count: " .. Spearhead.Util.tableLength(missionData.RedGroups)) return self end @@ -291,10 +311,14 @@ function ZoneMission:UpdateState(checkHealth, messageIfDone) self._state = "COMPLETED" end end + + if self._state == "COMPLETED" and self._battleManager then + self._battleManager:Stop() + end end function ZoneMission:SpawnPersistedState() - for _, group in pairs(self._missionGroups.groups) do + for _, group in pairs(self._missionGroups.redGroups) do group:Spawn() end end @@ -303,7 +327,7 @@ end function ZoneMission:SpawnInactive() self._logger:info("PreActivating " .. self.name) - for _, group in pairs(self._missionGroups.groups) do + for _, group in pairs(self._missionGroups.redGroups) do group:Spawn() end end @@ -323,10 +347,18 @@ function ZoneMission:SpawnActive() end self._state = "ACTIVE" - for _, group in pairs(self._missionGroups.groups) do + for _, group in pairs(self._missionGroups.redGroups) do group:Spawn() end + for _, group in pairs(self._missionGroups.blueGroups) do + group:Spawn() + end + + if self._battleManager then + self._battleManager:Start() + end + self._missionCommandsHelper:AddMissionToCommands(self) self:StartCheckingContinuous() diff --git a/classes/stageClasses/missions/baseMissions/Mission.lua b/classes/stageClasses/missions/baseMissions/Mission.lua index eaeac3e..159f0be 100644 --- a/classes/stageClasses/missions/baseMissions/Mission.lua +++ b/classes/stageClasses/missions/baseMissions/Mission.lua @@ -154,6 +154,7 @@ do --aliases --- @alias MissionType --- | "nil" --- | "STRIKE" + --- | "CAS" --- | "BAI" --- | "DEAD" --- | "SAM" diff --git a/dev/dev_classes.lua b/dev/dev_classes.lua index 865408b..c1efe59 100644 --- a/dev/dev_classes.lua +++ b/dev/dev_classes.lua @@ -37,6 +37,7 @@ assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadGroup.lua"))() assert(loadfile(classPath .. "stageClasses\\helpers\\MissionCommandsHelper.lua"))() assert(loadfile(classPath .. "stageClasses\\helpers\\SupplyConfig.lua"))() assert(loadfile(classPath .. "stageClasses\\helpers\\SupplyUnitsTracker.lua"))() +assert(loadfile(classPath .. "stageClasses\\helpers\\BattleManager.lua"))() assert(loadfile(classPath .. "stageClasses\\SpecialZones\\StageBase.lua"))() assert(loadfile(classPath .. "stageClasses\\SpecialZones\\BlueSam.lua"))()