added lua check CI step #49

Merged
dutchie031 merged 72 commits from CI into develop 2026-09-20 16:08:04 +00:00
40 changed files with 367 additions and 536 deletions
Showing only changes of commit 0346f8a7a8 - Show all commits
+3 -3
View File
@@ -15,7 +15,7 @@ SpearheadConfig = {
--quickly enable of disable the entire CAP Logic --quickly enable of disable the entire CAP Logic
--(you can also just rename all units to not be named "CAP_") --(you can also just rename all units to not be named "CAP_")
enabled = true, -- default true enabled = true, -- default true
--min ground speed for CAP aircraft during patrol --min ground speed for CAP aircraft during patrol
-- unit: knots -- unit: knots
minSpeed = 400, -- default 400 minSpeed = 400, -- default 400
@@ -62,7 +62,7 @@ SpearheadConfig = {
deathDelay = 1800, -- default 1800 deathDelay = 1800, -- default 1800
}, },
StageConfig = { StageConfig = {
-- management of stages and its missions. -- management of stages and its missions.
-- This is not related to CAP managers which will continue to work even if stage management is disabled -- This is not related to CAP managers which will continue to work even if stage management is disabled
enabled = true, -- default true enabled = true, -- default true
@@ -98,7 +98,7 @@ SpearheadConfig = {
}, },
Persistence = { Persistence = {
--- io and lfs cannot be sanitized in the MissionScripting.lua --- io and lfs cannot be sanitized in the MissionScripting.lua
--- enables or disables the persistence logic in spearhead --- enables or disables the persistence logic in spearhead
enabled = false, enabled = false,
-73
View File
@@ -1,73 +0,0 @@
--assert(loadfile("C:\\Repos\\DCS\\Spearhead\\dev\\dev_classes.lua"))()
local basePath = "C:\\Repos\\DCS\\Spearhead\\"
local classPath = basePath .. "classes\\"
assert(loadfile(classPath .. "_baseClasses\\Queue.lua"))()
assert(loadfile(classPath .. "spearhead_base.lua"))()
assert(loadfile(classPath .. "spearhead_routeutil.lua"))()
assert(loadfile(classPath .. "spearhead_events.lua"))()
assert(loadfile(classPath .. "spearhead_db.lua"))()
assert(loadfile(classPath .. "fleetClasses\\FleetGroup.lua"))()
assert(loadfile(classPath .. "fleetClasses\\GlobalFleetManager.lua"))()
assert(loadfile(classPath .. "helpers\\MizGroupsManager.lua"))()
assert(loadfile(classPath .. "helpers\\SpawnManager.lua"))()
assert(loadfile(classPath .. "configuration\\CapConfig.lua"))()
assert(loadfile(classPath .. "configuration\\StageConfig.lua"))()
assert(loadfile(classPath .. "stageClasses\\GlobalStageManager.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\baseMissions\\Mission.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\ZoneMission.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\RunwayStrikeMission.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\BuildableMission.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\BaseStage\\Stage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\PrimaryStage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\ExtraStage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\WaitingStage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadGroup.lua"))()
assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadSceneryObject.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\\abstract\\BuildableZone.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\StageBase.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\BlueSam.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\FarpZone.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\SupplyHub.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\RTB.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\CAP.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\SWEEP.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\INTERCEPT.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\AirGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\CapGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\SweepGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\InterceptGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\detection\\DetectionManager.lua"))()
assert(loadfile(classPath .. "capClasses\\GlobalCapManager.lua"))()
assert(loadfile(classPath .. "capClasses\\CapAirbase.lua"))()
assert(loadfile(classPath .. "capClasses\\runwayBombing\\RunwayBombingTracker.lua"))()
assert(loadfile(classPath .. "persistence\\Persistence.lua"))()
-- Startup:
assert(loadfile(basePath .. "main.lua"))()
-79
View File
@@ -1,79 +0,0 @@
SpearheadConfig = {
debugEnabled = true,
CapConfig = {
--quickly enable of disable the entire CAP Logic
--(you can also just rename all units to not be named "CAP_")
enabled = true, -- default true
--min ground speed for CAP aircraft during patrol
-- unit: knots
minSpeed = 400, -- default 400
--max speed for CAP aircraft during patrol
-- unit: knots
maxSpeed = 500, -- default 500
--minAlt for aircraft on patrol
-- unit: feet
minAlt = 18000, -- default 18000
--maxAlt for aircraft on patrol
-- unit: feet
maxAlt = 28000, -- default 28000
--Delay for aircraft from touchdown to off the chocks.
-- unit: seconds
rearmDelay = 180, -- default 600
repairDelay = 600, -- default 600
--Delay for aircraft from death to takeoff.
--When the seconds remaining is the same at the rearmDelay it will be spawned on the ramp and follow the rearm logic.
-- !! Can not be lower than rearmDelay
-- unit: seconds
deathDelay = 1800, -- default 1800
},
StageConfig = {
-- management of stages and its missions.
-- This is not related to CAP managers which will continue to work even if stage management is disabled
enabled = true, -- default true
--Will draw the active and the next stage
drawStages = true, -- default true
drawPreActivated = true,
markLastContact = true,
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
--[[
TODO: Add manual stage transition documentation
]]
autoStages = true, --default true
--Maximum missions per stage (includes all types of missions)
maxMissionStage = 100,
--Stage starting number
startingStage = 1,
---DEBUG logging. Consider keeping this disabled
debugEnabled = true
},
Persistence = {
--- io and lfs cannot be sanitized in the MissionScripting.lua
--- enables or disables the persistence logic in spearhead
enabled = true,
--- sets the directory where the persistence file is stored
--- if nil then lfs.writedir() will be used.
--- which will
directory = nil ,
--- the filename of the persistence file. Should end with .json for convention, but any text extension should do.
fileName = "Spearhead_Persistence_Dev"
}
}
+8 -7
View File
@@ -95,7 +95,6 @@ function CapBase.new(airbaseName, database, logger, capConfig, runwayBombingTrac
SpearheadEvents.AddStageNumberChangedListener(self) SpearheadEvents.AddStageNumberChangedListener(self)
timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15) timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15)
return self return self
end end
@@ -152,8 +151,9 @@ end
function CapBase:CheckAndScheduleCAP() function CapBase:CheckAndScheduleCAP()
self.logger:debug("Check taskings for airbase " .. self.airbaseName) self.logger:debug("Check taskings for airbase " .. self.airbaseName)
---@type table<string, number>
local countPerStage = {} local countPerStage = {}
---@type table<string, number>
local requiredPerStage = {} local requiredPerStage = {}
local airbase = Airbase.getByName(self.airbaseName) local airbase = Airbase.getByName(self.airbaseName)
@@ -168,7 +168,7 @@ function CapBase:CheckAndScheduleCAP()
if group:IsBackup() == true then if group:IsBackup() == true then
local state = group:GetState() local state = group:GetState()
if state == "InTransit" or state == "OnStation" or state == "RtbInTen" then if state == "InTransit" or state == "OnStation" or state == "RtbInTen" then
local supposedTargetZoneID = group:GetZoneIDWhenStageID(activeStageID) local supposedTargetZoneID = group:GetZoneIDWhenStageID(activeStageID)
local currentTargetZone = group:GetCurrentTargetZoneID() local currentTargetZone = group:GetCurrentTargetZoneID()
@@ -190,7 +190,7 @@ function CapBase:CheckAndScheduleCAP()
end end
end end
end end
if countPerStage[supposedTargetZoneID] == nil then if countPerStage[supposedTargetZoneID] == nil then
countPerStage[supposedTargetZoneID] = 0 countPerStage[supposedTargetZoneID] = 0
end end
@@ -229,7 +229,7 @@ function CapBase:CheckAndScheduleCAP()
countPerStage[supposedZone] = countPerStage[supposedZone] + 1 countPerStage[supposedZone] = countPerStage[supposedZone] + 1
end end
elseif state == "InTransit" or state == "OnStation" then elseif state == "InTransit" or state == "OnStation" then
if supposedZone ~= group:GetCurrentTargetZoneID() then if supposedZone ~= group:GetCurrentTargetZoneID() then
if countPerStage[supposedZone] < requiredPerStage[supposedZone] then if countPerStage[supposedZone] < requiredPerStage[supposedZone] then
local triggerZone = self.database:GetCapZoneForZoneID(supposedZone) local triggerZone = self.database:GetCapZoneForZoneID(supposedZone)
@@ -271,7 +271,7 @@ function CapBase:CheckAndScheduleCAP()
if triggerZone then if triggerZone then
group:SendToZone(triggerZone, supposedZone, airbase) group:SendToZone(triggerZone, supposedZone, airbase)
end end
countPerStage[supposedZone] = countPerStage[supposedZone] + 1 countPerStage[supposedZone] = countPerStage[supposedZone] + 1
end end
end end
@@ -310,6 +310,7 @@ function CapBase:CheckAndScheduleIntercept()
self.logger:debug("Check intercept taskings for airbase " .. self.airbaseName) self.logger:debug("Check intercept taskings for airbase " .. self.airbaseName)
---@type table<string, boolean>
local interceptZoneIDs = {} local interceptZoneIDs = {}
local airbase = Airbase.getByName(self.airbaseName) local airbase = Airbase.getByName(self.airbaseName)
@@ -353,7 +354,7 @@ function CapBase:CheckAndScheduleIntercept()
end end
end end
end end
---RATIO. Amount of enemy fighters required per zone before another group gets added. ---RATIO. Amount of enemy fighters required per zone before another group gets added.
local ratio = 4 -- Ratio of units to intercept per zone, can be adjusted local ratio = 4 -- Ratio of units to intercept per zone, can be adjusted
+1 -2
View File
@@ -35,9 +35,8 @@ do
for _, airbaseName in pairs(airbaseNames) do for _, airbaseName in pairs(airbaseNames) do
if airbaseName then if airbaseName then
local airbaseSpecificLogger = Logger.new("CAP_" .. airbaseName, logLevel) local airbaseSpecificLogger = Logger.new("CAP_" .. airbaseName, logLevel)
local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, runwayBombingTracker, detectionManager, spawnManager) local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, runwayBombingTracker, detectionManager, spawnManager)
if airbase then if airbase then
table.insert(airbasesPerStage[stageName], airbase) table.insert(airbasesPerStage[stageName], airbase)
allAirbasesByName[airbaseName] = airbase allAirbasesByName[airbaseName] = airbase
@@ -103,7 +103,7 @@ function CapGroup:InitWithName(groupName)
return return
end end
local subsplit = Util.split_string(configPart, "|") local subsplit = Util.split_string(configPart, "|")
if subsplit then if subsplit then
@@ -112,8 +112,8 @@ function CapGroup:InitWithName(groupName)
local targetZone = keySplit[2] local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1]) local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Util.split_string(allActives, ",") local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do for _, childValue in pairs(commaSeperated) do
local dashSeperated = Util.split_string(value, "-") local dashSeperated = Util.split_string(childValue, "-")
if Util.tableLength(dashSeperated) > 1 then if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1]) local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2]) local till = tonumber(dashSeperated[2])
@@ -190,7 +190,7 @@ function InterceptGroup:UpdateTask()
self:SendRTB(self._airbase) self:SendRTB(self._airbase)
return nil -- Return to base if bingo fuel return nil -- Return to base if bingo fuel
end end
if selfDetected and self:IsInAir() == true then if selfDetected and self:IsInAir() == true then
if self._currentTargetName and self._currentTargetName == closestUnit:getName() then if self._currentTargetName and self._currentTargetName == closestUnit:getName() then
local distance = Util.VectorDistance3d(closestUnit:getPoint(), groupPoint) local distance = Util.VectorDistance3d(closestUnit:getPoint(), groupPoint)
@@ -213,8 +213,6 @@ function InterceptGroup:UpdateTask()
self._currentTargetName = nil self._currentTargetName = nil
end end
local speed = self._config:getMaxSpeed() local speed = self._config:getMaxSpeed()
local interceptPoint = self:GetInterceptPoint(groupPoint, speed, closestUnit) local interceptPoint = self:GetInterceptPoint(groupPoint, speed, closestUnit)
@@ -328,8 +326,8 @@ function InterceptGroup:InitWithName(groupName)
local targetZone = keySplit[2] local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1]) local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Util.split_string(allActives, ",") local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do for _, childValue in pairs(commaSeperated) do
local dashSeperated = Util.split_string(value, "-") local dashSeperated = Util.split_string(childValue, "-")
if Util.tableLength(dashSeperated) > 1 then if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1]) local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2]) local till = tonumber(dashSeperated[2])
@@ -77,7 +77,7 @@ function SweepGroup:InitWithName(groupName)
local configPart = split_string[2] local configPart = split_string[2]
configPart = string.sub(configPart, 2, #configPart) configPart = string.sub(configPart, 2, #configPart)
local subsplit = Util.split_string(configPart, "|") local subsplit = Util.split_string(configPart, "|")
if subsplit then if subsplit then
for _, value in pairs(subsplit) do for _, value in pairs(subsplit) do
@@ -85,8 +85,8 @@ function SweepGroup:InitWithName(groupName)
local targetZone = keySplit[2] local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1]) local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Util.split_string(allActives, ",") local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do for _, childValue in pairs(commaSeperated) do
local dashSeperated = Util.split_string(value, "-") local dashSeperated = Util.split_string(childValue, "-")
if Util.tableLength(dashSeperated) > 1 then if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1]) local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2]) local till = tonumber(dashSeperated[2])
@@ -37,7 +37,6 @@ function DetectionManager.New(logger)
return time + 10 return time + 10
end end
timer.scheduleFunction(updateDetected, self, timer.getTime() + 130) timer.scheduleFunction(updateDetected, self, timer.getTime() + 130)
return self return self
end end
@@ -30,7 +30,7 @@ function RunwayBombingTracker:OnWeaponFired(_, weapon, _)
local desc = weapon:getDesc() local desc = weapon:getDesc()
local isTrackable = desc.category == Weapon.Category.BOMB or (desc.category == Weapon.Category.MISSILE and desc.missileCategory == Weapon.MissileCategory.CRUISE) local isTrackable = desc.category == Weapon.Category.BOMB or (desc.category == Weapon.Category.MISSILE and desc.missileCategory == Weapon.MissileCategory.CRUISE)
if isTrackable == true then if isTrackable == true then
---@type WeaponTrackingArgs ---@type WeaponTrackingArgs
local weaponTrackingArgs = { local weaponTrackingArgs = {
weapon = weapon, weapon = weapon,
@@ -61,7 +61,7 @@ function RunwayBombingTracker.trackWeaponTask(weaponTrackingArgs, time)
local weapon = weaponTrackingArgs.weapon local weapon = weaponTrackingArgs.weapon
local self = weaponTrackingArgs.self local self = weaponTrackingArgs.self
if not weapon or weapon:isExist() == false then return nil end if not weapon or weapon:isExist() == false then return nil end
local pos = weapon:getPoint() local pos = weapon:getPoint()
@@ -83,11 +83,7 @@ function RunwayBombingTracker.trackWeaponTask(weaponTrackingArgs, time)
x = pos.x + velocity.x * nextInterval, x = pos.x + velocity.x * nextInterval,
y = pos.z + velocity.z * nextInterval y = pos.z + velocity.z * nextInterval
} }
self:OnWeaponImpact(weapon:getDesc(), impactPoint) self:OnWeaponImpact(weapon:getDesc(), impactPoint)
return nil return nil
end end
@@ -38,7 +38,7 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con
id = 'Mission', id = 'Mission',
params = { params = {
airborne = true, airborne = true,
route = { route = {
points = points points = points
} }
} }
@@ -159,10 +159,10 @@ function INTERCEPT.getInterceptTaskPoint(groupName, currentPoint, targetPoint, a
params = { params = {
tasks = { tasks = {
id = 'EngageTargetsInZone', id = 'EngageTargetsInZone',
params = { params = {
point = targetPoint, point = targetPoint,
zoneRadius = 10 * 1852, -- 10 NM, point will be updated, so target should be in this zone. zoneRadius = 10 * 1852, -- 10 NM, point will be updated, so target should be in this zone.
targetTypes = { targetTypes = {
[1] = "Planes", [1] = "Planes",
}, },
priority = 0 priority = 0
@@ -188,10 +188,10 @@ function INTERCEPT.getInterceptTaskPoint(groupName, currentPoint, targetPoint, a
params = { params = {
tasks = { tasks = {
id = 'EngageTargetsInZone', id = 'EngageTargetsInZone',
params = { params = {
point = targetPoint, point = targetPoint,
zoneRadius = 10 * 1852, -- 10 NM, point will be updated, so target should be in this zone. zoneRadius = 10 * 1852, -- 10 NM, point will be updated, so target should be in this zone.
targetTypes = { targetTypes = {
[1] = "Planes", [1] = "Planes",
}, },
priority = 0 priority = 0
+3 -3
View File
@@ -47,7 +47,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
else else
rad = 0 - rad rad = 0 - rad
end end
local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0} local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0}
local alignment = Util.vectorAlignment(windVec, runwayVec) local alignment = Util.vectorAlignment(windVec, runwayVec)
@@ -60,14 +60,14 @@ local getRunwayIntoWindCourseRad = function(airbase)
do --inverse do --inverse
local degree = math.deg(runway.course) local degree = math.deg(runway.course)
degree = (degree + 180) % 360 degree = (degree + 180) % 360
local rad = math.rad(degree) local rad = math.rad(degree)
if rad < 0 then if rad < 0 then
rad = math.abs(rad) rad = math.abs(rad)
else else
rad = 0 - rad rad = 0 - rad
end end
local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0} local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0}
local alignment = Util.vectorAlignment(windVec, runwayVec) local alignment = Util.vectorAlignment(windVec, runwayVec)
+1 -1
View File
@@ -18,7 +18,7 @@ local _logger = Logger.new("StageConfig", Logger.LogLevel)
---@return StageConfig ---@return StageConfig
local function new() local function new()
if SpearheadConfig == nil then if SpearheadConfig == nil then
_logger:warn("SpearheadConfig is nil, creating default SpearheadConfig") _logger:warn("SpearheadConfig is nil, creating default SpearheadConfig")
SpearheadConfig = {} SpearheadConfig = {}
end end
+48 -42
View File
@@ -4,37 +4,43 @@ local MissionEditorWarning = require("classes.util.MissionEditorWarnings")
local RouteUtil = require("classes.spearhead_routeutil") local RouteUtil = require("classes.spearhead_routeutil")
local SpearheadEvents = require("classes.spearhead_events") local SpearheadEvents = require("classes.spearhead_events")
---@class FleetGroup ---@class FleetGroup : OnStageChangedListener
---@field private fleetGroupName string
---@field private logger Logger
---@field private fleetNameIdentifier string
---@field private targetZonePerStage table<string, string>
---@field private currentTargetZone string
---@field private pointsPerZone table<string, {pointA: Vec2, pointB: Vec2}>
local FleetGroup = {} local FleetGroup = {}
FleetGroup.__index = FleetGroup
---comment ---comment
---@param fleetGroupName string ---@param fleetGroupName string
---@param database Database ---@param database Database
---@param logger Logger ---@param logger Logger
---@return nil ---@return FleetGroup?
function FleetGroup:new(fleetGroupName, database, logger) function FleetGroup.new(fleetGroupName, database, logger)
local o = {}
setmetatable(o, { __index = self }) local self = setmetatable({}, FleetGroup)
o.fleetGroupName = fleetGroupName self.fleetGroupName = fleetGroupName
o.logger = logger self.logger = logger
local split_name = Util.split_string(fleetGroupName, "_") local split_name = Util.split_string(fleetGroupName, "_")
if Util.tableLength(split_name) < 2 then if Util.tableLength(split_name) < 2 then
MissionEditorWarning.Add("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_<fleetname>") MissionEditorWarning.Add("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_<fleetname>")
return nil return nil
end end
o.fleetNameIdentifier = split_name[2] self.fleetNameIdentifier = split_name[2]
o.targetZonePerStage = {} self.targetZonePerStage = {}
o.currentTargetZone = nil self.currentTargetZone = nil
o.pointsPerZone = {} self.pointsPerZone = {}
do --INIT do --INIT
local carrierRouteZones = database:getCarrierRouteZones() local carrierRouteZones = database:getCarrierRouteZones()
for _, zoneName in pairs(carrierRouteZones) do for _, zoneName in pairs(carrierRouteZones) do
if Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then if Util.strContains(string.lower(zoneName), "_".. string.lower(self.fleetNameIdentifier) .. "_" ) == true then
local zone = DcsUtil.getZoneByName(zoneName) local zone = DcsUtil.getZoneByName(zoneName)
if zone and zone.zone_type == DcsUtil.ZoneType.Polygon then if zone and zone.zone_type == DcsUtil.ZoneType.Polygon then
local split_string = Util.split_string(zoneName, "_") local split_string = Util.split_string(zoneName, "_")
@@ -43,18 +49,18 @@ function FleetGroup:new(fleetGroupName, database, logger)
"CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName) "CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName)
else else
---@param zone SpearheadTriggerZone ---@param zoneA SpearheadTriggerZone
---@return Vec2, Vec2 ---@return Vec2, Vec2
local function GetTwoFurthestPoints(zone) local function GetTwoFurthestPoints(zoneA)
local biggest = nil local biggest = nil
local biggestA = zone.verts[1] local biggestA = zoneA.verts[1]
local biggestB = zone.verts[2] local biggestB = zoneA.verts[2]
for i = 1, 3 do for i = 1, 3 do
for ii = i + 1, 4 do for ii = i + 1, 4 do
local a = zone.verts[i] local a = zoneA.verts[i]
local b = zone.verts[ii] local b = zoneA.verts[ii]
local dist = Util.VectorDistance2d(a, b) local dist = Util.VectorDistance2d(a, b)
if biggest == nil or dist > biggest then if biggest == nil or dist > biggest then
@@ -100,9 +106,9 @@ function FleetGroup:new(fleetGroupName, database, logger)
local first, second = getMinMaxStage(split_string[3]) local first, second = getMinMaxStage(split_string[3])
if first ~= nil and second ~= nil then if first ~= nil and second ~= nil then
for i = first, second do for i = first, second do
o.targetZonePerStage[tostring(i)] = zoneName self.targetZonePerStage[tostring(i)] = zoneName
end end
o.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} } self.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} }
else else
MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName) MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
end end
@@ -114,32 +120,32 @@ function FleetGroup:new(fleetGroupName, database, logger)
end end
end end
local SetTaskAsync = function(input, _) SpearheadEvents.AddStageNumberChangedListener(self)
local targetZone = input.targetZone return self
local task = input.task end
local groupName = input.groupName
local logger = input.logger
local group = Group.getByName(groupName) local SetTaskAsync = function(input, _)
if group then local targetZone = input.targetZone
logger:info("Sending " .. groupName .. " to " .. targetZone) local task = input.task
group:getController():setTask(task) local groupName = input.groupName
end local l_logger = input.logger
local group = Group.getByName(groupName)
if group then
l_logger:info("Sending " .. groupName .. " to " .. targetZone)
group:getController():setTask(task)
end end
end
o.OnStageNumberChanged = function(self, number, laneIdentifier) function FleetGroup:OnStageNumberChanged(number, laneIdentifier)
-- only react on "main" lane changes, ignore other lanes for now -- only react on "main" lane changes, ignore other lanes for now
if laneIdentifier ~= nil then return end if laneIdentifier ~= nil then return end
local targetZone = self.targetZonePerStage[tostring(number)] local targetZone = self.targetZonePerStage[tostring(number)]
if targetZone and targetZone ~= self.currentTargetZone then if targetZone and targetZone ~= self.currentTargetZone then
local points = self.pointsPerZone[targetZone] local points = self.pointsPerZone[targetZone]
local task = RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB) local task = RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB)
timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5) timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5)
end
end end
SpearheadEvents.AddStageNumberChangedListener(o)
return o
end end
return FleetGroup return FleetGroup
@@ -17,7 +17,7 @@ GlobalFleetManager.start = function(database)
for _, groupName in pairs(all_groups) do for _, groupName in pairs(all_groups) do
if Util.startswith(string.lower(groupName), "carriergroup" ) == true then if Util.startswith(string.lower(groupName), "carriergroup" ) == true then
logger:info("Registering " .. groupName .. " as a managed fleet") logger:info("Registering " .. groupName .. " as a managed fleet")
local carrierGroup = FleetGroup:new(groupName, database, logger) local carrierGroup = FleetGroup.new(groupName, database, logger)
table.insert(fleetGroups, carrierGroup) table.insert(fleetGroups, carrierGroup)
end end
end end
+2 -3
View File
@@ -33,7 +33,7 @@ end
---@return StaticObject|Group|nil spawned ---@return StaticObject|Group|nil spawned
---@return boolean isStatic ---@return boolean isStatic
function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant) function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant)
local spawnData = MizGroupsManager.getSpawnTemplateData(groupName) local spawnData = MizGroupsManager.getSpawnTemplateData(groupName)
if spawnData == nil then if spawnData == nil then
@@ -50,7 +50,7 @@ end
function SpawnManager:DestroyGroup(groupName) function SpawnManager:DestroyGroup(groupName)
if groupName == nil then return end if groupName == nil then return end
if self:IsGroupStatic(groupName) == true then if self:IsGroupStatic(groupName) == true then
local object = StaticObject.getByName(groupName) local object = StaticObject.getByName(groupName)
if object ~= nil then if object ~= nil then
@@ -181,7 +181,6 @@ do --- privates
return group return group
end end
return nil return nil
end end
+5 -5
View File
@@ -89,7 +89,7 @@ do
else else
logger:error("Could not load persistence file, using default tables") logger:error("Could not load persistence file, using default tables")
end end
if tables.version == nil then tables.version = version end if tables.version == nil then tables.version = version end
end end
@@ -114,7 +114,7 @@ do
local UpdateContinuous = function(_, time) local UpdateContinuous = function(_, time)
env.info("[Spearhead][Persistence] Checking up on persistence state...") env.info("[Spearhead][Persistence] Checking up on persistence state...")
if Persistence._updateRequired == true then if Persistence._updateRequired == true then
local status, result = pcall(writeToFile) local status, result = pcall(writeToFile)
if status == false then if status == false then
env.error("[Spearhead][Persistence] Could not write state to file: " .. result) env.error("[Spearhead][Persistence] Could not write state to file: " .. result)
@@ -194,7 +194,7 @@ do
split[#split+1] = "0" split[#split+1] = "0"
split[#split+1] = "spearhead" split[#split+1] = "spearhead"
end end
fileName = table.concat(split, ".") fileName = table.concat(split, ".")
end end
@@ -218,12 +218,12 @@ do
logger:info("No previous persistence file found, using default: " .. fileName) logger:info("No previous persistence file found, using default: " .. fileName)
end end
logger:info("New Persistence file name: " .. tostring(fileName)) logger:info("New Persistence file name: " .. tostring(fileName))
local lastPath = dir .. "\\" .. lastFile local lastPath = dir .. "\\" .. lastFile
local path = dir .. "\\" .. fileName local path = dir .. "\\" .. fileName
Persistence._path = path Persistence._path = path
createFileIfNotExists(path) createFileIfNotExists(path)
loadTablesFromFile(lastPath) loadTablesFromFile(lastPath)
timer.scheduleFunction(UpdateContinuous, nil, timer.getTime() + 120) timer.scheduleFunction(UpdateContinuous, nil, timer.getTime() + 120)
+12 -14
View File
@@ -258,7 +258,7 @@ function Database.New(Logger)
if env.mission.drawings and env.mission.drawings.layers then if env.mission.drawings and env.mission.drawings.layers then
for _, layer in pairs(env.mission.drawings.layers) do for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for _, layer_object in pairs(layer.objects) do
if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then
local zone = DcsUtil.getZoneByName(stageZoneName) local zone = DcsUtil.getZoneByName(stageZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY } local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
@@ -339,14 +339,14 @@ function Database.New(Logger)
end end
-- fill missions -- fill missions
for key, missionZone in pairs(self._tables.MissionZones) do for _, missionZone in pairs(self._tables.MissionZones) do
if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
table.insert(stageData.MissionZones, missionZone) table.insert(stageData.MissionZones, missionZone)
end end
end end
-- fill random missions -- fill random missions
for key, missionZone in pairs(self._tables.RandomMissionZones) do for _, missionZone in pairs(self._tables.RandomMissionZones) do
if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
table.insert(stageData.RandomMissionZones, missionZone) table.insert(stageData.RandomMissionZones, missionZone)
end end
@@ -620,9 +620,9 @@ function Database:loadBlueSamUnits()
samData.buildingKilos = number samData.buildingKilos = number
if env.mission.drawings and env.mission.drawings.layers then if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for _, layer_object in pairs(layer.objects) do
local vec2 = { x = layer_object.mapX, y = layer_object.mapY } local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
@@ -637,7 +637,7 @@ function Database:loadBlueSamUnits()
end end
end end
end end
else else
MissionEditorWarnings.Add("Buildable number for " .. blueSamZone .. " is invalid") MissionEditorWarnings.Add("Buildable number for " .. blueSamZone .. " is invalid")
end end
end end
@@ -670,13 +670,13 @@ function Database:LoadZoneData(missionZoneName)
for _, groupName in pairs(groups) do for _, groupName in pairs(groups) do
if MizGroupsManager.IsGroupStatic(groupName) == true then if MizGroupsManager.IsGroupStatic(groupName) == true then
local object = StaticObject.getByName(groupName) local object = StaticObject.getByName(groupName)
if object and object:getCoalition() == coalition.side.RED then if object and object:getCoalition() == coalition.side.RED then
table.insert(self._tables.MissionZoneData[missionZoneName].RedGroups, groupName) table.insert(self._tables.MissionZoneData[missionZoneName].RedGroups, groupName)
elseif object then elseif object then
table.insert(self._tables.MissionZoneData[missionZoneName].BlueGroups, groupName) table.insert(self._tables.MissionZoneData[missionZoneName].BlueGroups, groupName)
end end
else else
local group = Group.getByName(groupName) local group = Group.getByName(groupName)
if group and group:getCoalition() == coalition.side.RED then if group and group:getCoalition() == coalition.side.RED then
@@ -720,9 +720,9 @@ function Database:LoadZoneData(missionZoneName)
---Adds Briefings ---Adds Briefings
if env.mission.drawings and env.mission.drawings.layers then if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for _, layer_object in pairs(layer.objects) do
local vec2 = { x = layer_object.mapX, y = layer_object.mapY } local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
@@ -776,9 +776,9 @@ function Database:loadFarpData()
-- check briefings -- check briefings
if env.mission.drawings and env.mission.drawings.layers then if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for _, layer_object in pairs(layer.objects) do
local vec2 = { x = layer_object.mapX, y = layer_object.mapY } local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
@@ -796,8 +796,6 @@ function Database:loadFarpData()
else else
MissionEditorWarnings.Add("Buildable number for " .. farpZone .. " is invalid.") MissionEditorWarnings.Add("Buildable number for " .. farpZone .. " is invalid.")
end end
end end
end end
end end
+27 -28
View File
@@ -46,7 +46,7 @@ do
---@class OnStageNumberChangeCompleteListener ---@class OnStageNumberChangeCompleteListener
---@field OnStageNumberChangeComplete fun(self:OnStageNumberChangeCompleteListener, number:integer, laneIdentifier:string?) ---@field OnStageNumberChangeComplete fun(self:OnStageNumberChangeCompleteListener, number:integer, laneIdentifier:string?)
local OnStageNumberChangeCompleteListeners = {} local OnStageNumberChangeCompleteListeners = {}
---@param listener OnStageNumberChangeCompleteListener ---@param listener OnStageNumberChangeCompleteListener
SpearheadEvents.AddStageNumberChangeCompleteListener = function(listener) SpearheadEvents.AddStageNumberChangeCompleteListener = function(listener)
@@ -65,7 +65,7 @@ do
end) end)
for _, callable in pairs(OnStageNumberChangedListeners) do for _, callable in pairs(OnStageNumberChangedListeners) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnStageNumberChanged(newStageNumber, laneIdentifier) callable:OnStageNumberChanged(newStageNumber, laneIdentifier)
end) end)
if err then if err then
@@ -74,14 +74,14 @@ do
end end
for _, callable in pairs(OnStageNumberChangedHandlers) do for _, callable in pairs(OnStageNumberChangedHandlers) do
local succ, err = pcall(callable, newStageNumber, laneIdentifier) local _, err = pcall(callable, newStageNumber, laneIdentifier)
if err then if err then
logError(err) logError(err)
end end
end end
for _, callable in pairs(OnStageNumberChangeCompleteListeners) do for _, callable in pairs(OnStageNumberChangeCompleteListeners) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnStageNumberChangeComplete(newStageNumber, laneIdentifier) callable:OnStageNumberChangeComplete(newStageNumber, laneIdentifier)
end) end)
if err then if err then
@@ -112,7 +112,7 @@ do
local triggerWeaponFired = function(unit, weapon, target) local triggerWeaponFired = function(unit, weapon, target)
for _, callable in pairs(onWeaponFiredListeners) do for _, callable in pairs(onWeaponFiredListeners) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnWeaponFired(unit, weapon, target) callable:OnWeaponFired(unit, weapon, target)
end) end)
@@ -121,7 +121,7 @@ do
end end
end end
end end
local onLandEventListeners = {} local onLandEventListeners = {}
---Add an event listener to a specific unit ---Add an event listener to a specific unit
@@ -186,7 +186,7 @@ do
if groupName ~= nil then if groupName ~= nil then
if OnGroupRTBListeners[groupName] then if OnGroupRTBListeners[groupName] then
for _, callable in pairs(OnGroupRTBListeners[groupName]) do for _, callable in pairs(OnGroupRTBListeners[groupName]) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnGroupRTB(groupName) callable:OnGroupRTB(groupName)
end) end)
if err then if err then
@@ -221,7 +221,7 @@ do
if groupName ~= nil then if groupName ~= nil then
if OnGroupRTBInTenListeners[groupName] then if OnGroupRTBInTenListeners[groupName] then
for _, callable in pairs(OnGroupRTBInTenListeners[groupName]) do for _, callable in pairs(OnGroupRTBInTenListeners[groupName]) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnGroupRTBInTen(groupName) callable:OnGroupRTBInTen(groupName)
end) end)
if err then if err then
@@ -257,7 +257,7 @@ do
if groupName ~= nil then if groupName ~= nil then
if OnGroupOnStationListeners[groupName] then if OnGroupOnStationListeners[groupName] then
for _, callable in pairs(OnGroupOnStationListeners[groupName]) do for _, callable in pairs(OnGroupOnStationListeners[groupName]) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnGroupOnStation(groupName) callable:OnGroupOnStation(groupName)
end) end)
if err then if err then
@@ -286,7 +286,7 @@ do
if unit ~= nil then if unit ~= nil then
if playerEnterUnitListeners then if playerEnterUnitListeners then
for _, callable in pairs(playerEnterUnitListeners) do for _, callable in pairs(playerEnterUnitListeners) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnPlayerEntersUnit(unit) callable:OnPlayerEntersUnit(unit)
end) end)
if err then if err then
@@ -299,7 +299,7 @@ do
end end
do -- Ejection events do -- Ejection events
local unitEjectListeners = {} local unitEjectListeners = {}
SpearheadEvents.AddOnUnitEjectedListener = function(listener) SpearheadEvents.AddOnUnitEjectedListener = function(listener)
if type(listener) ~= "table" then if type(listener) ~= "table" then
@@ -321,7 +321,7 @@ do
local name = unit:getName() local name = unit:getName()
if onLandEventListeners[name] then if onLandEventListeners[name] then
for _, callable in pairs(onLandEventListeners[name]) do for _, callable in pairs(onLandEventListeners[name]) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnUnitLanded(unit, airbase) callable:OnUnitLanded(unit, airbase)
end) end)
if err then if err then
@@ -341,10 +341,10 @@ do
if object and object.getName then if object and object.getName then
logDebug("Receiving death event from: " .. object:getName()) logDebug("Receiving death event from: " .. object:getName())
end end
if object and object.getName and OnUnitLostListeners[object:getName()] then if object and object.getName and OnUnitLostListeners[object:getName()] then
for _, callable in pairs(OnUnitLostListeners[object:getName()]) do for _, callable in pairs(OnUnitLostListeners[object:getName()]) do
local succ, err = pcall(function() local _, err = pcall(function()
callable:OnUnitLost(object) callable:OnUnitLost(object)
end) end)
@@ -356,11 +356,11 @@ do
end end
if event.id == world.event.S_EVENT_EJECTION then if event.id == world.event.S_EVENT_EJECTION then
end end
if event.id == world.event.S_EVENT_SHOT then if event.id == world.event.S_EVENT_SHOT then
local shooter = event.initiator local shooter = event.initiator
local weapon = event.weapon local weapon = event.weapon
local target = event.target local target = event.target
@@ -374,26 +374,26 @@ do
local AI_GROUPS = {} local AI_GROUPS = {}
local function CheckAndTriggerSpawnAsync(unit, time) local function CheckAndTriggerSpawnAsync(unit, _)
local function isPlayer(unit) local function isPlayer(checkUnit)
if unit == nil then return false, "unit is nil" end if checkUnit == nil then return false, "unit is nil" end
if unit.getGroup == nil then return false, 'no get group function in unit object, most likely static' end if checkUnit.getGroup == nil then return false, 'no get group function in unit object, most likely static' end
if Object.getCategory(unit) ~= Object.Category.UNIT then if Object.getCategory(checkUnit) ~= Object.Category.UNIT then
return false, "object is not a unit" return false, "object is not a unit"
end end
if unit:isExist() ~= true then return false, "unit does not exist" end if checkUnit:isExist() ~= true then return false, "unit does not exist" end
local group = unit:getGroup() local group = checkUnit:getGroup()
if group ~= nil then if group ~= nil then
if AI_GROUPS[group:getName()] == true then if AI_GROUPS[group:getName()] == true then
return false return false
end end
local players = DcsUtil.getAllPlayerUnits() local players = DcsUtil.getAllPlayerUnits()
local unitName = unit:getName() local unitName = checkUnit:getName()
for i, unit in pairs(players) do for _, playerUnit in pairs(players) do
if unit:getName() == unitName then if playerUnit:getName() == unitName then
return true return true
end end
end end
@@ -403,7 +403,6 @@ do
end end
if isPlayer(unit) == true then if isPlayer(unit) == true then
local groupId = unit:getGroup():getID()
SpearheadEvents.TriggerPlayerEntersUnit(unit) SpearheadEvents.TriggerPlayerEntersUnit(unit)
end end
+105 -97
View File
@@ -2,7 +2,6 @@ local Events = require("classes.spearhead_events")
local Util = require("classes.util.Util") local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil") local DcsUtil = require("classes.util.DcsUtil")
local Logger = require("classes.util.Logger") local Logger = require("classes.util.Logger")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local PersistenceConfig = require("classes.configuration.PersistenceConfig") local PersistenceConfig = require("classes.configuration.PersistenceConfig")
local ExtraStage = require("classes.stageClasses.Stages.ExtraStage") local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage") local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
@@ -35,7 +34,6 @@ local singletonInstance = nil
---@param spawnManager SpawnManager ---@param spawnManager SpawnManager
---@return GlobalStageManager ---@return GlobalStageManager
function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager) function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
if singletonInstance ~= nil then if singletonInstance ~= nil then
return singletonInstance return singletonInstance
end end
@@ -44,7 +42,7 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
logger:info("Using Stage Log Level: " .. logLevel) logger:info("Using Stage Log Level: " .. logLevel)
local self = setmetatable({}, GlobalStageManager) local self = setmetatable({}, GlobalStageManager)
singletonInstance = self singletonInstance = self
self.database = database self.database = database
self.stageConfig = stageConfig self.stageConfig = stageConfig
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate() self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
@@ -52,14 +50,15 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
self.logger = logger self.logger = logger
if stageConfig.isAutoStages ~= true then if stageConfig.isAutoStages ~= true then
logger:warn("Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message") logger:warn(
"Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message")
end end
Events.AddStageNumberChangedListener(self) Events.AddStageNumberChangedListener(self)
for _, stageName in pairs(database:getStagezoneNames()) do for _, stageName in pairs(database:getStagezoneNames()) do
logger:debug("Found stage zone with name: " .. stageName) logger:debug("Found stage zone with name: " .. stageName)
local parseResult = self:ParseStageName(stageName) local parseResult = self:ParseStageName(stageName)
if parseResult.isValid == false then if parseResult.isValid == false then
logger:warn("Stage zone with name " .. stageName .. " is not valid: " .. parseResult.invalidReason) logger:warn("Stage zone with name " .. stageName .. " is not valid: " .. parseResult.invalidReason)
@@ -81,12 +80,12 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
stage:AddStageCompleteListener(self) stage:AddStageCompleteListener(self)
self._stageRepository:AddStage(stage) self._stageRepository:AddStage(stage)
elseif parseResult.stageType == "WaitingStage" then elseif parseResult.stageType == "WaitingStage" then
local waitingStage = WaitingStage.New(database, stageConfig, logger, initData, parseResult.waitingStageSeconds, spawnManager) local waitingStage = WaitingStage.New(database, stageConfig, logger, initData,
parseResult.waitingStageSeconds, spawnManager)
waitingStage:AddStageCompleteListener(self) waitingStage:AddStageCompleteListener(self)
self._stageRepository:AddStage(waitingStage) self._stageRepository:AddStage(waitingStage)
end end
end end
end end
singletonInstance = self singletonInstance = self
@@ -103,11 +102,11 @@ function GlobalStageManager:Start()
local stageLanes = self._stageRepository:getAllStageLanes() local stageLanes = self._stageRepository:getAllStageLanes()
for _, stageLane in pairs(stageLanes) do for _, stageLane in pairs(stageLanes) do
local stageLaneIdentifier = stageLane:GetStageLaneIdentifier() local stageLaneIdentifier = stageLane:GetStageLaneIdentifier()
local persistedStage = Persistence.GetActiveStage(stageLaneIdentifier) local persistedStage = Persistence.GetActiveStage(stageLaneIdentifier)
if persistedStage then if persistedStage then
self.logger:info("Loaded persisted stage " .. persistedStage .. " for lane " .. (stageLaneIdentifier or "default")) self.logger:info("Loaded persisted stage " ..
persistedStage .. " for lane " .. (stageLaneIdentifier or "default"))
stageLane:SetActiveStageIndex(persistedStage) stageLane:SetActiveStageIndex(persistedStage)
Events.PublishStageNumberChanged(persistedStage, stageLaneIdentifier) Events.PublishStageNumberChanged(persistedStage, stageLaneIdentifier)
else else
@@ -139,17 +138,16 @@ end
---@param stageName string ---@param stageName string
---@return StageNameParseResult ---@return StageNameParseResult
function GlobalStageManager:ParseStageName(stageName) function GlobalStageManager:ParseStageName(stageName)
local split = Util.split_string(stageName, "_") local split = Util.split_string(stageName, "_")
if Util.tableLength(split) < 3 then if Util.tableLength(split) < 3 then
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a order number or valid format" } return { isValid = false, invalidReason = "Stage zone with name " ..
stageName .. " does not have a order number or valid format" }
end end
local typePart = string.lower(split[1]) local typePart = string.lower(split[1])
if typePart == "missionstage" then if typePart == "missionstage" then
local orderNumberString = string.lower(split[2]) local orderNumberString = string.lower(split[2])
---@type StageType ---@type StageType
local stageType = "PrimaryStage" local stageType = "PrimaryStage"
@@ -161,15 +159,16 @@ function GlobalStageManager:ParseStageName(stageName)
local first = orderNumberString:sub(1, 1) local first = orderNumberString:sub(1, 1)
if tonumber(first) == nil then -- first character is the lane identifier only if it's not a number if tonumber(first) == nil then -- first character is the lane identifier only if it's not a number
stageLaneIdentifier = string.lower(first) stageLaneIdentifier = string.lower(first)
orderNumberString = orderNumberString:sub(2) orderNumberString = orderNumberString:sub(2)
end end
local orderNumber = tonumber(orderNumberString) local orderNumber = tonumber(orderNumberString)
if orderNumber == nil then if orderNumber == nil then
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString } return { isValid = false, invalidReason = "Stage zone with name " ..
stageName .. " does not have a valid order number : " .. orderNumberString }
end end
local stageDisplayName = split[3] local stageDisplayName = split[3]
---@type StageNameParseResult ---@type StageNameParseResult
local result = { local result = {
@@ -182,18 +181,19 @@ function GlobalStageManager:ParseStageName(stageName)
} }
return result return result
elseif typePart == "waitingstage" then elseif typePart == "waitingstage" then
local stageType = "WaitingStage" local stageType = "WaitingStage"
local orderNumberString = split[2] local orderNumberString = split[2]
local orderNumber = tonumber(orderNumberString) local orderNumber = tonumber(orderNumberString)
if orderNumber == nil then if orderNumber == nil then
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString } return { isValid = false, invalidReason = "Waiting Stage zone with name " ..
stageName .. " does not have a valid order number : " .. orderNumberString }
end end
local waitingSecondsString = split[3] local waitingSecondsString = split[3]
local waitingSeconds = tonumber(waitingSecondsString) local waitingSeconds = tonumber(waitingSecondsString)
if waitingSeconds == nil then if waitingSeconds == nil then
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter : " .. waitingSecondsString } return { isValid = false, invalidReason = "Waiting Stage zone with name " ..
stageName .. " does not have a valid amount of seconds parameter : " .. waitingSecondsString }
end end
local stageDisplayName = "Waiting Stage " .. orderNumber local stageDisplayName = "Waiting Stage " .. orderNumber
@@ -209,9 +209,9 @@ function GlobalStageManager:ParseStageName(stageName)
waitingStageSeconds = waitingSeconds waitingStageSeconds = waitingSeconds
} }
return result return result
end end
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " has an unrecognized type: " .. typePart } return { isValid = false, invalidReason = "Stage zone with name " ..
stageName .. " has an unrecognized type: " .. typePart }
end end
---@param stage Stage ---@param stage Stage
@@ -223,7 +223,8 @@ function GlobalStageManager:OnStageComplete(stage)
local stageLane = self._stageRepository:getStageLane(laneIdentifier) local stageLane = self._stageRepository:getStageLane(laneIdentifier)
if not stageLane or not stageLane:IsCurrentStageIndexComplete() then if not stageLane or not stageLane:IsCurrentStageIndexComplete() then
self.logger:debug("Stage lane " .. (laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex) self.logger:debug("Stage lane " ..
(laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex)
return return
end end
@@ -234,7 +235,7 @@ function GlobalStageManager:OnStageComplete(stage)
stageLane:SetActiveStageIndex(nextStageIndex) stageLane:SetActiveStageIndex(nextStageIndex)
if stageLane:IsDefaultStageLane() then if stageLane:IsDefaultStageLane() then
-- COMPLETION IN THE DEFAULT LANE -- COMPLETION IN THE DEFAULT LANE
-- 1. Check if any lane is at status "BetweenChapters" and the next chapter start is "next stage index" -- 1. Check if any lane is at status "BetweenChapters" and the next chapter start is "next stage index"
local allLanes = self._stageRepository:getAllStageLanes() local allLanes = self._stageRepository:getAllStageLanes()
@@ -242,7 +243,9 @@ function GlobalStageManager:OnStageComplete(stage)
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey and lane:GetStageLaneState() == "BetweenChapters" then if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey and lane:GetStageLaneState() == "BetweenChapters" then
local nextChapterStart = lane:GetNextChapterStart() local nextChapterStart = lane:GetNextChapterStart()
if nextChapterStart == nextStageIndex then if nextChapterStart == nextStageIndex then
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is at chapter start for next stage index " .. nextStageIndex) self.logger:debug("Lane " ..
(lane:GetStageLaneIdentifier() or "default") ..
" is at chapter start for next stage index " .. nextStageIndex)
Events.PublishStageNumberChanged(nextStageIndex, lane:GetStageLaneIdentifier()) Events.PublishStageNumberChanged(nextStageIndex, lane:GetStageLaneIdentifier())
lane:SetActiveStageIndex(nextStageIndex) lane:SetActiveStageIndex(nextStageIndex)
end end
@@ -256,7 +259,6 @@ function GlobalStageManager:OnStageComplete(stage)
if defaultStageLane and defaultStageLane:GetStageLaneState() == "BetweenChapters" then if defaultStageLane and defaultStageLane:GetStageLaneState() == "BetweenChapters" then
local nextChapterStart = defaultStageLane:GetNextChapterStart() local nextChapterStart = defaultStageLane:GetNextChapterStart()
if nextChapterStart == nextStageIndex then if nextChapterStart == nextStageIndex then
-- 2. Check if all side lanes are at or beyond the next chapter starts -- 2. Check if all side lanes are at or beyond the next chapter starts
local allLanes = self._stageRepository:getAllStageLanes() local allLanes = self._stageRepository:getAllStageLanes()
local allSideLanesReady = true local allSideLanesReady = true
@@ -265,15 +267,19 @@ function GlobalStageManager:OnStageComplete(stage)
-- Check if the lane is at or beyond the next chapter starts when "InChapter" eq "Active" -- Check if the lane is at or beyond the next chapter starts when "InChapter" eq "Active"
if lane:GetStageLaneState() == "InChapter" and lane:GetActiveStageIndex() < nextStageIndex then if lane:GetStageLaneState() == "InChapter" and lane:GetActiveStageIndex() < nextStageIndex then
allSideLanesReady = false allSideLanesReady = false
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex) self.logger:debug("Lane " ..
(lane:GetStageLaneIdentifier() or "default") ..
" is not ready for next stage index " .. nextStageIndex)
break break
-- When in between chapters, check if the next chapter start is less than the next stage index, meaning there is still stages to be completed before the main lane can be activated again -- When in between chapters, check if the next chapter start is less than the next stage index, meaning there is still stages to be completed before the main lane can be activated again
elseif lane:GetStageLaneState() == "BetweenChapters" then elseif lane:GetStageLaneState() == "BetweenChapters" then
local nextChapter = lane:GetNextChapterStart() local nextChapter = lane:GetNextChapterStart()
if nextChapter and nextChapter < nextStageIndex then if nextChapter and nextChapter < nextStageIndex then
allSideLanesReady = false allSideLanesReady = false
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex) self.logger:debug("Lane " ..
(lane:GetStageLaneIdentifier() or "default") ..
" is not ready for next stage index " .. nextStageIndex)
break break
end end
end end
@@ -281,13 +287,14 @@ function GlobalStageManager:OnStageComplete(stage)
end end
if allSideLanesReady then if allSideLanesReady then
self.logger:debug("All side lanes are ready for next stage index " .. nextStageIndex .. ", activating default lane") self.logger:debug("All side lanes are ready for next stage index " ..
nextStageIndex .. ", activating default lane")
Events.PublishStageNumberChanged(nextStageIndex, nil) Events.PublishStageNumberChanged(nextStageIndex, nil)
defaultStageLane:SetActiveStageIndex(nextStageIndex) defaultStageLane:SetActiveStageIndex(nextStageIndex)
else else
self.logger:debug("Not all side lanes are ready for next stage index " .. nextStageIndex .. ", default lane will not be activated") self.logger:debug("Not all side lanes are ready for next stage index " ..
nextStageIndex .. ", default lane will not be activated")
end end
end end
end end
end end
@@ -308,7 +315,6 @@ end
---@public ---@public
function GlobalStageManager:OnStageNumberChanged(stageNumber, stageLaneIdentifier) function GlobalStageManager:OnStageNumberChanged(stageNumber, stageLaneIdentifier)
-- only react on "main" lane changes, ignore other lanes for now -- only react on "main" lane changes, ignore other lanes for now
if stageLaneIdentifier ~= nil then return end if stageLaneIdentifier ~= nil then return end
@@ -321,7 +327,6 @@ function GlobalStageManager:OnStageNumberChangeComplete(stageNumber, stageLaneId
if stageLaneIdentifier ~= nil then return end if stageLaneIdentifier ~= nil then return end
self.logger:debug("Stage number change complete to: " .. tostring(stageNumber)) self.logger:debug("Stage number change complete to: " .. tostring(stageNumber))
local groups = {} local groups = {}
for _, player in pairs(DcsUtil.getAllPlayerUnits()) do for _, player in pairs(DcsUtil.getAllPlayerUnits()) do
@@ -349,80 +354,81 @@ function GlobalStageManager:UpdateDrawings(stageNumber, stageLaneIdentifier)
if laneIdentifier == stageLaneIdentifier then if laneIdentifier == stageLaneIdentifier then
if stageNumber >= startStage and stageNumber < stopStage then if stageNumber >= startStage and stageNumber < stopStage then
self.logger:debug("Drawing " .. drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber)) self.logger:debug("Drawing " ..
drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber))
drawing:Draw() drawing:Draw()
else else
self.logger:debug("Drawing " .. drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber)) self.logger:debug("Drawing " ..
drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber))
drawing:Remove() drawing:Remove()
end end
end end
end end
end end
function GlobalStageManager:PrintMermaidStage() function GlobalStageManager:PrintMermaidStage()
local lanes = self._stageRepository:getAllStageLanes() local lanes = self._stageRepository:getAllStageLanes()
local nodes = {} local nodes = {}
local edges = {} local edges = {}
local stageIndicesByLane = {} -- Track all stage indices per lane local stageIndicesByLane = {} -- Track all stage indices per lane
local mainLaneId = "default" local mainLaneId = "default"
-- Color palette for lanes (26 vibrant colors optimized for dark mode) -- Color palette for lanes (26 vibrant colors optimized for dark mode)
local laneColors = { local laneColors = {
"#FF6B6B", -- 1: Red "#FF6B6B", -- 1: Red
"#4ECDC4", -- 2: Teal "#4ECDC4", -- 2: Teal
"#45B7D1", -- 3: Blue "#45B7D1", -- 3: Blue
"#FFA502", -- 4: Orange "#FFA502", -- 4: Orange
"#95E1D3", -- 5: Mint "#95E1D3", -- 5: Mint
"#F38181", -- 6: Coral "#F38181", -- 6: Coral
"#AA96DA", -- 7: Purple "#AA96DA", -- 7: Purple
"#FCBAD3", -- 8: Pink "#FCBAD3", -- 8: Pink
"#A8E6CF", -- 9: Light green "#A8E6CF", -- 9: Light green
"#FFD3B6", -- 10: Peach "#FFD3B6", -- 10: Peach
"#FFAAA5", -- 11: Light red "#FFAAA5", -- 11: Light red
"#FF8B94", -- 12: Rose "#FF8B94", -- 12: Rose
"#FFEAA7", -- 13: Butter "#FFEAA7", -- 13: Butter
"#DFE6E9", -- 14: Gray "#DFE6E9", -- 14: Gray
"#00B894", -- 15: Emerald "#00B894", -- 15: Emerald
"#0984E3", -- 16: Cobalt "#0984E3", -- 16: Cobalt
"#6C5CE7", -- 17: Indigo "#6C5CE7", -- 17: Indigo
"#A29BFE", -- 18: Lavender "#A29BFE", -- 18: Lavender
"#FD79A8", -- 19: Magenta "#FD79A8", -- 19: Magenta
"#FDCB6E", -- 20: Gold "#FDCB6E", -- 20: Gold
"#6C757D", -- 21: Slate "#6C757D", -- 21: Slate
"#20C997", -- 22: Seafoam "#20C997", -- 22: Seafoam
"#E74C3C", -- 23: Scarlet "#E74C3C", -- 23: Scarlet
"#3498DB", -- 24: Dodger blue "#3498DB", -- 24: Dodger blue
"#9B59B6", -- 25: Amethyst "#9B59B6", -- 25: Amethyst
"#1ABC9C", -- 26: Turquoise "#1ABC9C", -- 26: Turquoise
} }
local laneColorMap = {} -- Map lane ID to color local laneColorMap = {} -- Map lane ID to color
local colorIndex = 1 local colorIndex = 1
-- Build nodes and collect all stage indices per lane -- Build nodes and collect all stage indices per lane
for _, lane in ipairs(lanes) do for _, lane in ipairs(lanes) do
local laneId = lane:GetStageLaneIdentifier() or "default" local laneId = lane:GetStageLaneIdentifier() or "default"
local stageIndices = lane:GetAllStageIndices() local stageIndices = lane:GetAllStageIndices()
stageIndicesByLane[laneId] = stageIndices stageIndicesByLane[laneId] = stageIndices
-- Assign color to this lane -- Assign color to this lane
laneColorMap[laneId] = laneColors[colorIndex] laneColorMap[laneId] = laneColors[colorIndex]
colorIndex = colorIndex + 1 colorIndex = colorIndex + 1
if colorIndex > #laneColors then if colorIndex > #laneColors then
colorIndex = 1 colorIndex = 1
end end
-- Create nodes for each stage index in this lane -- Create nodes for each stage index in this lane
for _, stageIndex in ipairs(stageIndices) do for _, stageIndex in ipairs(stageIndices) do
local stages = lane:GetStagesAtIndex(stageIndex) local stages = lane:GetStagesAtIndex(stageIndex)
if stages then if stages then
for _, stage in ipairs(stages) do for _, stage in ipairs(stages) do
local nodeId = laneId .. "_" .. stageIndex local nodeId = laneId .. "_" .. stageIndex
local stageType = stage:GetStageType() local stageType = stage:GetStageType()
local stageName = stage.stageName or stage.zoneName local stageName = stage.stageName or stage.zoneName
-- Generate bracket label: [1], [2] for default, [w1], [e2] for other lanes -- Generate bracket label: [1], [2] for default, [w1], [e2] for other lanes
local bracketLabel local bracketLabel
if laneId == "default" then if laneId == "default" then
@@ -430,10 +436,10 @@ function GlobalStageManager:PrintMermaidStage()
else else
bracketLabel = "[" .. laneId .. stageIndex .. "]" bracketLabel = "[" .. laneId .. stageIndex .. "]"
end end
-- Combined label: [bracket] Name -- Combined label: [bracket] Name
local label = bracketLabel .. " " .. stageName local label = bracketLabel .. " " .. stageName
-- Different node shapes for different stage types -- Different node shapes for different stage types
local nodeShape = "[" local nodeShape = "["
local nodeEnd = "]" local nodeEnd = "]"
@@ -444,14 +450,14 @@ function GlobalStageManager:PrintMermaidStage()
nodeShape = "[[" nodeShape = "[["
nodeEnd = "]]" nodeEnd = "]]"
end end
nodes[nodeId] = string.format(' %s%s"%s"%s', nodes[nodeId] = string.format(' %s%s"%s"%s',
nodeId, nodeShape, label, nodeEnd) nodeId, nodeShape, label, nodeEnd)
end end
end end
end end
end end
-- Add edges for sequential stages within the same lane -- Add edges for sequential stages within the same lane
for laneId, stageIndices in pairs(stageIndicesByLane) do for laneId, stageIndices in pairs(stageIndicesByLane) do
for i = 1, #stageIndices - 1 do for i = 1, #stageIndices - 1 do
@@ -459,7 +465,7 @@ function GlobalStageManager:PrintMermaidStage()
local toIdx = stageIndices[i + 1] local toIdx = stageIndices[i + 1]
local fromNodeId = laneId .. "_" .. fromIdx local fromNodeId = laneId .. "_" .. fromIdx
local toNodeId = laneId .. "_" .. toIdx local toNodeId = laneId .. "_" .. toIdx
-- Check if this is a chapter boundary (gap) -- Check if this is a chapter boundary (gap)
local isChapter = false local isChapter = false
for _, lane in ipairs(lanes) do for _, lane in ipairs(lanes) do
@@ -469,12 +475,12 @@ function GlobalStageManager:PrintMermaidStage()
break break
end end
end end
local label = isChapter and "|chapter|" or "" local label = isChapter and "|chapter|" or ""
table.insert(edges, string.format(' %s -->%s %s', fromNodeId, label, toNodeId)) table.insert(edges, string.format(' %s -->%s %s', fromNodeId, label, toNodeId))
end end
end end
-- Get main lane reference -- Get main lane reference
local mainStageLane = nil local mainStageLane = nil
for _, lane in ipairs(lanes) do for _, lane in ipairs(lanes) do
@@ -483,7 +489,7 @@ function GlobalStageManager:PrintMermaidStage()
break break
end end
end end
-- Add dependencies from DEFAULT lane to SIDE lanes at chapter starts -- Add dependencies from DEFAULT lane to SIDE lanes at chapter starts
-- Side lanes activate when main completes the stage BEFORE the chapter start -- Side lanes activate when main completes the stage BEFORE the chapter start
if mainStageLane then if mainStageLane then
@@ -491,7 +497,7 @@ function GlobalStageManager:PrintMermaidStage()
if lane:IsDefaultStageLane() == false then if lane:IsDefaultStageLane() == false then
local sideId = lane:GetStageLaneIdentifier() local sideId = lane:GetStageLaneIdentifier()
local sideIndices = stageIndicesByLane[sideId] local sideIndices = stageIndicesByLane[sideId]
-- For each stage in the side lane -- For each stage in the side lane
for _, stageIdx in ipairs(sideIndices) do for _, stageIdx in ipairs(sideIndices) do
-- Check if this is a chapter start in the side lane -- Check if this is a chapter start in the side lane
@@ -506,16 +512,16 @@ function GlobalStageManager:PrintMermaidStage()
end end
end end
end end
-- Add dependencies from SIDE lanes gating the MAIN lane at chapter boundaries -- Add dependencies from SIDE lanes gating the MAIN lane at chapter boundaries
-- Main cannot advance to the next chapter until ALL side lanes are at or above that chapter index -- Main cannot advance to the next chapter until ALL side lanes are at or above that chapter index
if mainStageLane then if mainStageLane then
local mainIndices = stageIndicesByLane[mainLaneId] local mainIndices = stageIndicesByLane[mainLaneId]
-- For each chapter start in main lane (skip first one) -- For each chapter start in main lane (skip first one)
for i = 2, #mainIndices do for i = 2, #mainIndices do
local nextIdx = mainIndices[i] local nextIdx = mainIndices[i]
-- Check if nextIdx is a chapter start (there's a gap before it) -- Check if nextIdx is a chapter start (there's a gap before it)
if mainStageLane:IsChapterStart(nextIdx) then if mainStageLane:IsChapterStart(nextIdx) then
-- Before main can progress to nextIdx, all side lanes must be at >= nextIdx -- Before main can progress to nextIdx, all side lanes must be at >= nextIdx
@@ -523,23 +529,23 @@ function GlobalStageManager:PrintMermaidStage()
if lane:IsDefaultStageLane() == false then if lane:IsDefaultStageLane() == false then
local sideId = lane:GetStageLaneIdentifier() local sideId = lane:GetStageLaneIdentifier()
local sideIndices = stageIndicesByLane[sideId] local sideIndices = stageIndicesByLane[sideId]
-- Find the appropriate gate node for this side lane -- Find the appropriate gate node for this side lane
-- Use first stage >= nextIdx if it exists, otherwise use the highest stage -- Use first stage >= nextIdx if it exists, otherwise use the highest stage
local gateStageIdx = nil local gateStageIdx = nil
for _, idx in ipairs(sideIndices) do for _, idx in ipairs(sideIndices) do
if idx >= nextIdx then if idx >= nextIdx then
gateStageIdx = idx gateStageIdx = idx
break break
end end
end end
-- If no stage >= nextIdx, use the highest stage in this lane -- If no stage >= nextIdx, use the highest stage in this lane
if gateStageIdx == nil and #sideIndices > 0 then if gateStageIdx == nil and #sideIndices > 0 then
gateStageIdx = sideIndices[#sideIndices] gateStageIdx = sideIndices[#sideIndices]
end end
if gateStageIdx then if gateStageIdx then
local fromNodeId = sideId .. "_" .. gateStageIdx local fromNodeId = sideId .. "_" .. gateStageIdx
local toNodeId = mainLaneId .. "_" .. nextIdx local toNodeId = mainLaneId .. "_" .. nextIdx
@@ -550,25 +556,26 @@ function GlobalStageManager:PrintMermaidStage()
end end
end end
end end
-- Build complete Mermaid diagram -- Build complete Mermaid diagram
local diagramLines = {"graph TD"} local diagramLines = { "graph TD" }
-- Add class definitions for each lane with colors and contrasting text -- Add class definitions for each lane with colors and contrasting text
for laneId, color in pairs(laneColorMap) do for laneId, color in pairs(laneColorMap) do
table.insert(diagramLines, string.format(' classDef lane_%s fill:%s,stroke:#333,stroke-width:2px,color:#000', laneId, color)) table.insert(diagramLines,
string.format(' classDef lane_%s fill:%s,stroke:#333,stroke-width:2px,color:#000', laneId, color))
end end
-- Add nodes -- Add nodes
for _, nodeStr in pairs(nodes) do for _, nodeStr in pairs(nodes) do
table.insert(diagramLines, nodeStr) table.insert(diagramLines, nodeStr)
end end
-- Add edges -- Add edges
for _, edgeStr in pairs(edges) do for _, edgeStr in pairs(edges) do
table.insert(diagramLines, edgeStr) table.insert(diagramLines, edgeStr)
end end
-- Apply classes to nodes -- Apply classes to nodes
for nodeId in pairs(nodes) do for nodeId in pairs(nodes) do
local laneId = nodeId:match("(.+)_[0-9]+$") local laneId = nodeId:match("(.+)_[0-9]+$")
@@ -576,9 +583,10 @@ function GlobalStageManager:PrintMermaidStage()
table.insert(diagramLines, string.format(' class %s lane_%s', nodeId, laneId)) table.insert(diagramLines, string.format(' class %s lane_%s', nodeId, laneId))
end end
end end
-- Print as single multi-line message -- Print as single multi-line message
local diagram = "========== STAGE FLOW DIAGRAM ==========\n" .. table.concat(diagramLines, "\n") .. "\n========== END DIAGRAM ==========" local diagram = "========== STAGE FLOW DIAGRAM ==========\n" ..
table.concat(diagramLines, "\n") .. "\n========== END DIAGRAM =========="
self.logger:info(diagram) self.logger:info(diagram)
end end
@@ -586,10 +594,10 @@ end
---@param stageNumber number ---@param stageNumber number
---@param stageLaneIdentifier string? nil for default lan ---@param stageLaneIdentifier string? nil for default lan
---@return boolean | nil ---@return boolean | nil
GlobalStageManager.isStageComplete = function (stageNumber, stageLaneIdentifier) GlobalStageManager.isStageComplete = function(stageNumber, stageLaneIdentifier)
if singletonInstance == nil then if singletonInstance == nil then
Logger.new("StageManager", "INFO"):warn("GlobalStageManager.isStageComplete called before GlobalStageManager was initialized. Returning nil") Logger.new("StageManager", "INFO"):warn(
"GlobalStageManager.isStageComplete called before GlobalStageManager was initialized. Returning nil")
return nil return nil
end end
@@ -41,7 +41,7 @@ end
function SpearheadGroup:SpawnCorpsesOnly() function SpearheadGroup:SpawnCorpsesOnly()
if self._isSpawned == true then return end if self._isSpawned == true then return end
self._spawnManager:SpawnCorpsesOnly(self._groupName) self._spawnManager:SpawnCorpsesOnly(self._groupName)
self._isSpawned = true self._isSpawned = true
@@ -97,7 +97,7 @@ function SpearheadGroup:GetObjects()
local result = {} local result = {}
if self._isStatic == true then if self._isStatic == true then
local staticObject = StaticObject.getByName(self._groupName) local staticObject = StaticObject.getByName(self._groupName)
if staticObject then if staticObject then
table.insert(result, staticObject) table.insert(result, staticObject)
end end
else else
@@ -105,7 +105,7 @@ function SpearheadGroup:GetObjects()
if not group then return {} end if not group then return {} end
for _, unit in pairs(group:getUnits()) do for _, unit in pairs(group:getUnits()) do
table.insert(result, unit) table.insert(result, unit)
end end
end end
return result return result
end end
@@ -123,7 +123,7 @@ function SpearheadGroup:GetAsUnits()
if not group then return {} end if not group then return {} end
for _, unit in pairs(group:getUnits()) do for _, unit in pairs(group:getUnits()) do
table.insert(result, unit) table.insert(result, unit)
end end
return result return result
end end
@@ -133,7 +133,7 @@ function SpearheadGroup:GetAllUnitPositions()
local result = {} local result = {}
if self._isStatic == true then if self._isStatic == true then
local staticObject = StaticObject.getByName(self._groupName) local staticObject = StaticObject.getByName(self._groupName)
if staticObject then if staticObject then
table.insert(result, staticObject:getPoint()) table.insert(result, staticObject:getPoint())
end end
else else
@@ -141,7 +141,7 @@ function SpearheadGroup:GetAllUnitPositions()
if not group then return {} end if not group then return {} end
for _, unit in pairs(group:getUnits()) do for _, unit in pairs(group:getUnits()) do
table.insert(result, unit:getPoint()) table.insert(result, unit:getPoint())
end end
end end
return result return result
end end
@@ -50,16 +50,10 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
---@type table<string, Vec3> ---@type table<string, Vec3>
local redUnitsPos = {} local redUnitsPos = {}
local buildable = false
if self._buildableCrateKilos and self._buildableCrateKilos > 0 then
buildable = true
end
for _, groupName in pairs(blueSamData.groups) do for _, groupName in pairs(blueSamData.groups) do
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true) local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
if spearheadGroup then if spearheadGroup then
if spearheadGroup:GetCoalition() == 2 or spearheadGroup:GetCoalition() == 0 then if spearheadGroup:GetCoalition() == 2 or spearheadGroup:GetCoalition() == 0 then
table.insert(self._blueGroups, spearheadGroup) table.insert(self._blueGroups, spearheadGroup)
end end
@@ -78,7 +72,7 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
--Cleanup units --Cleanup units
local cleanup_distance = 5 local cleanup_distance = 5
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do for _, blueUnitPos in pairs(blueUnitsPos) do
for redUnitName, redUnitPos in pairs(redUnitsPos) do for redUnitName, redUnitPos in pairs(redUnitsPos) do
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos) local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
if distance <= cleanup_distance then if distance <= cleanup_distance then
@@ -140,7 +134,9 @@ end
function BlueSam:SpawnGroups() function BlueSam:SpawnGroups()
for unitName, needsCleanup in pairs(self._cleanupUnits) do for unitName, needsCleanup in pairs(self._cleanupUnits) do
DcsUtil.DestroyUnit(unitName) if needsCleanup then
DcsUtil.DestroyUnit(unitName)
end
end end
for _, group in pairs(self._blueGroups) do for _, group in pairs(self._blueGroups) do
@@ -42,11 +42,11 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
self._groups = {} self._groups = {}
self._padNames = {} self._padNames = {}
self._supplyHubs = {} self._supplyHubs = {}
if farpData then if farpData then
self._padNames = farpData.padNames self._padNames = farpData.padNames
for _, supplyHubName in pairs(farpData.supplyHubNames) do for _, supplyHubName in pairs(farpData.supplyHubNames) do
local supplyHub = SupplyHub.new(database, logger, supplyHubName) local supplyHub = SupplyHub.new(database, logger, supplyHubName)
if supplyHub then if supplyHub then
@@ -55,7 +55,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
end end
for _, groupName in pairs(farpData.groups) do for _, groupName in pairs(farpData.groups) do
local group = SpearheadGroup.New(groupName, spawnManager, true) local group = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._groups, group) table.insert(self._groups, group)
group:Destroy() group:Destroy()
@@ -90,7 +90,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
local cleanup_distance = 5 local cleanup_distance = 5
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do for _, blueUnitPos in pairs(blueUnitsPos) do
for redUnitName, redUnitPos in pairs(redUnitsPos) do for redUnitName, redUnitPos in pairs(redUnitsPos) do
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos) local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
if distance <= cleanup_distance then if distance <= cleanup_distance then
@@ -172,7 +172,7 @@ function StageBase:ActivateBlueStage()
else else
self:FinaliseBlueStage() self:FinaliseBlueStage()
end end
end end
function StageBase:FinaliseBlueStage() function StageBase:FinaliseBlueStage()
@@ -42,7 +42,7 @@ function SupplyHub.new(database, logger, zoneName)
end end
self._zone = DcsUtil.getZoneByName(zoneName) self._zone = DcsUtil.getZoneByName(zoneName)
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate() self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate()
self._inZone = {} self._inZone = {}
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate() self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
@@ -50,7 +50,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
if params.unpackedKilos >= params.kilos or spawned == false then if params.unpackedKilos >= params.kilos or spawned == false then
return return
end end
return time + 0.5 return time + 0.5
end end
@@ -76,8 +76,6 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
self._buildableMission = nil self._buildableMission = nil
end end
if self._buildableMission == nil then if self._buildableMission == nil then
self._buildableLogger:debug("No buildable mission for zone: " .. targetZone.name) self._buildableLogger:debug("No buildable mission for zone: " .. targetZone.name)
end end
@@ -99,23 +97,11 @@ function BuildableZone:OnBuildingComplete() end
---@field unpackedItems number ---@field unpackedItems number
---@field unpackedKilos number ---@field unpackedKilos number
---@param mission BuildableMission? ---@param _ BuildableMission?
---@param kilos number ---@param kilos number
function BuildableZone:OnCrateDroppedOff(mission, kilos) function BuildableZone:OnCrateDroppedOff(_, kilos)
self._buildableLogger:debug("Crate dropped off in zone: " .. self._targetZone.name) self._buildableLogger:debug("Crate dropped off in zone: " .. self._targetZone.name)
local timeToUnpack = (kilos / 500) * 15
---@type UnpackCrateParam
local params = {
self = self,
groupsPerKilo = self._groupsPerKilo,
unpackedItems = 0,
kilosPerSecond = kilos/timeToUnpack,
unpackedKilos = 0,
kilos = kilos
}
---@param params UnpackCrateParam ---@param params UnpackCrateParam
---@param time number ---@param time number
local startUnpackingCrate = function(params, time) local startUnpackingCrate = function(params, time)
@@ -132,10 +118,21 @@ function BuildableZone:OnCrateDroppedOff(mission, kilos)
params.self:FinaliseCrate(params.kilos) params.self:FinaliseCrate(params.kilos)
return return
end end
return time + 2 return time + 2
end end
local timeToUnpack = (kilos / 500) * 15
---@type UnpackCrateParam
local params = {
self = self,
groupsPerKilo = self._groupsPerKilo,
unpackedItems = 0,
kilosPerSecond = kilos/timeToUnpack,
unpackedKilos = 0,
kilos = kilos
}
timer.scheduleFunction(startUnpackingCrate, params, timer.getTime() + 2) timer.scheduleFunction(startUnpackingCrate, params, timer.getTime() + 2)
end end
@@ -190,7 +187,7 @@ function BuildableZone:SpawnAmount(amount)
return nil return nil
end end
for i = 1, amount do for _ = 1, amount do
local spawned = spawnOne() local spawned = spawnOne()
if spawned ~= true then if spawned ~= true then
self._buildableLogger:debug("No more groups to spawn in zone: " .. self._targetZone.name) self._buildableLogger:debug("No more groups to spawn in zone: " .. self._targetZone.name)
@@ -49,7 +49,6 @@ end
function StageRepository:AddStage(stage) function StageRepository:AddStage(stage)
local stageLaneIdentifier = stage:GetStageLaneIdentifier() or StageLane.DefaultLaneKey local stageLaneIdentifier = stage:GetStageLaneIdentifier() or StageLane.DefaultLaneKey
if self.StageLanes[tostring(stageLaneIdentifier)] == nil then if self.StageLanes[tostring(stageLaneIdentifier)] == nil then
self.StageLanes[tostring(stageLaneIdentifier)] = StageLane.New(stageLaneIdentifier) self.StageLanes[tostring(stageLaneIdentifier)] = StageLane.New(stageLaneIdentifier)
end end
@@ -105,7 +105,7 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
end end
self.stageName = initData.stageDisplayName self.stageName = initData.stageDisplayName
self._stageType = stageType self._stageType = stageType
self.OnPostStageComplete = nil self.OnPostStageComplete = nil
self.OnPostBlueActivated = nil self.OnPostBlueActivated = nil
@@ -160,25 +160,25 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
self._logger:info("Initiating new Stage with name: " .. self.zoneName) self._logger:info("Initiating new Stage with name: " .. self.zoneName)
---comment ---comment
---@param self Stage ---@param selfA Stage
---@param time number? ---@param time number?
self.CheckContinuousAsync = function (self, time) self.CheckContinuousAsync = function (selfA, time)
self:CheckAndUpdateSelf() selfA:CheckAndUpdateSelf()
if self:IsComplete() == true then if selfA:IsComplete() == true then
self:NotifyComplete() selfA:NotifyComplete()
return nil return nil
end end
return time + 20 return time + 20
end end
do -- load tables do -- load tables
local missionZones = database:getMissionsForStage(self.zoneName) local missionZones = database:getMissionsForStage(self.zoneName)
self._logger:debug("Found " .. Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName) self._logger:debug("Found " .. Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName)
for _, missionZone in pairs(missionZones) do for _, missionZone in pairs(missionZones) do
local mission = ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager) local mission = ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager)
if mission then if mission then
self._db.missionsByCode[mission.code] = mission self._db.missionsByCode[mission.code] = mission
@@ -233,7 +233,7 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
table.insert(self._db.missions, mission) table.insert(self._db.missions, mission)
end end
end end
else else
self._logger:info("Using persisted random mission with name: " .. missionName .. " and zone: " .. missionZonePicked) self._logger:info("Using persisted random mission with name: " .. missionName .. " and zone: " .. missionZonePicked)
for _, mission in pairs(missions) do for _, mission in pairs(missions) do
if string.lower(mission.zoneName) == string.lower(missionZonePicked) then if string.lower(mission.zoneName) == string.lower(missionZonePicked) then
@@ -284,14 +284,14 @@ end
function Stage:IsComplete() function Stage:IsComplete()
if self._currentStageState >= StageState.Blue then return true end if self._currentStageState >= StageState.Blue then return true end
for i, mission in pairs(self._db.sams) do for _, mission in pairs(self._db.sams) do
local state = mission:getState() local state = mission:getState()
if state == "ACTIVE" or state == "NEW" or state =="WAITING" then if state == "ACTIVE" or state == "NEW" or state =="WAITING" then
return false return false
end end
end end
for i, mission in pairs(self._db.missions) do for _, mission in pairs(self._db.missions) do
local state = mission:getState() local state = mission:getState()
if state == "ACTIVE" or state == "NEW" then if state == "ACTIVE" or state == "NEW" then
return false return false
@@ -370,9 +370,9 @@ function Stage:CheckAndUpdateSelf()
local availableMissionsCount = Util.tableLength(getAvailableMissions()) local availableMissionsCount = Util.tableLength(getAvailableMissions())
local activeCount = getActiveMissionsCount() local activeCount = getActiveMissionsCount()
if activeCount < max and availableMissionsCount > 0 then if activeCount < max and availableMissionsCount > 0 then
for i = activeCount+1, max do for _ = activeCount+1, max do
if availableMissionsCount == 0 then if availableMissionsCount == 0 then
i = max+1 --exits this loop break
else else
local mission = Util.randomFromList(getAvailableMissions()) --[[@as Mission]] local mission = Util.randomFromList(getAvailableMissions()) --[[@as Mission]]
if mission then if mission then
@@ -426,7 +426,7 @@ function Stage:PreActivate()
end end
self._currentStageState = StageState.PreActivated self._currentStageState = StageState.PreActivated
for key, mission in pairs(self._db.sams) do for _, mission in pairs(self._db.sams) do
if mission then if mission then
mission:SpawnInactive() mission:SpawnInactive()
end end
@@ -473,7 +473,7 @@ function Stage:MarkStage()
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.INVISIBLE) drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.INVISIBLE)
drawing.style = "no line" drawing.style = "no line"
end end
return drawing return drawing
end) end)
self._customDrawing:Draw() self._customDrawing:Draw()
@@ -585,12 +585,12 @@ function Stage:OnStageNumberChanged(number, stageLaneIdentifier)
if needsBlueActivation() == true then if needsBlueActivation() == true then
self:ActivateBlueStage() self:ActivateBlueStage()
end end
end end
---@param self Stage ---@param self Stage
---@param mission Mission ---@param _ Mission
Stage.OnMissionComplete = function(self, mission) Stage.OnMissionComplete = function(self, _)
self:CheckAndUpdateSelf() self:CheckAndUpdateSelf()
end end
@@ -645,7 +645,7 @@ function Stage:GetStageStats()
end end
end end
for _, mission in pairs(self._db.sams) do for _, _ in pairs(self._db.sams) do
dead = dead + 1 dead = dead + 1
end end
@@ -673,13 +673,13 @@ function Stage:ActivateBlueStage()
miscGroup:Spawn() miscGroup:Spawn()
end end
---@param self Stage ---@param selfA Stage
local ActivateBlueAsync = function(self) local ActivateBlueAsync = function(selfA)
pcall(function() pcall(function()
self:MarkStage() selfA:MarkStage()
end) end)
self:ActivateBlueGroups() selfA:ActivateBlueGroups()
return nil return nil
end end
@@ -23,10 +23,9 @@ function ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
self:superNew(database, stageConfig, logger, initData, "ExtraStage", "secondary", spawnManager) self:superNew(database, stageConfig, logger, initData, "ExtraStage", "secondary", spawnManager)
self.OnPostBlueActivated = function (selfStage) self.OnPostBlueActivated = function (selfStage)
selfStage:MarkStage() selfStage:MarkStage()
end end
self.OnPostStageComplete = function (selfStage) self.OnPostStageComplete = function (selfStage)
selfStage:ActivateBlueStage() selfStage:ActivateBlueStage()
end end
@@ -48,7 +47,6 @@ function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
return return
end end
local previousActive = self._activeStage
self._activeStage = number self._activeStage = number
if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then
@@ -25,7 +25,7 @@ function WaitingStage.New(database, stageConfig, logger, initData, waitingSecond
self._startTime = nil self._startTime = nil
self.CheckContinuousAsync = function (selfA, time) self.CheckContinuousAsync = function (selfA, time)
if selfA:IsComplete() == true then if selfA:IsComplete() == true then
selfA:NotifyComplete() selfA:NotifyComplete()
return nil return nil
@@ -47,7 +47,7 @@ function WaitingStage:ActivateStage()
timer.scheduleFunction(self.CheckContinuousAsync, self, self._startTime + self._waitTimeSeconds) timer.scheduleFunction(self.CheckContinuousAsync, self, self._startTime + self._waitTimeSeconds)
end end
function WaitingStage:IsComplete() function WaitingStage:IsComplete()
if timer.getTime() > (self._startTime + self._waitTimeSeconds) then return true end if timer.getTime() > (self._startTime + self._waitTimeSeconds) then return true end
return false return false
end end
@@ -56,12 +56,12 @@ function WaitingStage:OnStageNumberChanged()
self._logger:debug("Waiting Stage OnStageNumberChanged override") self._logger:debug("Waiting Stage OnStageNumberChanged override")
end end
function WaitingStage:MarkStage(stageColor) function WaitingStage:MarkStage(_)
self._logger:debug("Waiting Stage MarkStage override") self._logger:debug("Waiting Stage MarkStage override")
end end
function WaitingStage:GetExpectedTime() function WaitingStage:GetExpectedTime()
return self._startTime + self._waitTimeSeconds return self._startTime + self._waitTimeSeconds
end end
return WaitingStage return WaitingStage
@@ -77,7 +77,7 @@ local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineS
end end
trigger.action.setMarkupColor(drawID, lineColor) trigger.action.setMarkupColor(drawID, lineColor)
trigger.action.setMarkupTypeLine(drawID, lineStyle) trigger.action.setMarkupTypeLine(drawID, lineStyle)
end end
---@private ---@private
@@ -200,7 +200,7 @@ function DrawingHelper.DrawLine(object)
---@type Array<Vec3> ---@type Array<Vec3>
local points = {} local points = {}
local ids = {} local ids = {}
for _, point in ipairs(object.points) do for _, point in ipairs(object.points) do
table.insert(points, { x = object.mapX + point.x, y = 0, z = object.mapY + point.y } ) table.insert(points, { x = object.mapX + point.x, y = 0, z = object.mapY + point.y } )
@@ -264,7 +264,7 @@ function DrawingHelper.ColorTableToColorString(rgba)
logger:warn("ColorTableToColorString called with invalid rgba table, returning default color string '0x00000000'") logger:warn("ColorTableToColorString called with invalid rgba table, returning default color string '0x00000000'")
return "0x00000000" return "0x00000000"
end end
local r = string.format("%02X", math.floor(rgba[1] * 255)) local r = string.format("%02X", math.floor(rgba[1] * 255))
local g = string.format("%02X", math.floor(rgba[2] * 255)) local g = string.format("%02X", math.floor(rgba[2] * 255))
local b = string.format("%02X", math.floor(rgba[3] * 255)) local b = string.format("%02X", math.floor(rgba[3] * 255))
@@ -27,8 +27,8 @@ function BattleManager.New(redGroups, blueGroups, name, logLevel)
self._redGroups = redGroups self._redGroups = redGroups
self._blueGroups = blueGroups self._blueGroups = blueGroups
self._logger:debug("BattleManager created with name: " .. self._name self._logger:debug("BattleManager created with name: " .. self._name
.. ", red groups: " .. #self._redGroups .. ", red groups: " .. #self._redGroups
.. ", blue groups: " .. #self._blueGroups) .. ", blue groups: " .. #self._blueGroups)
return self return self
@@ -131,7 +131,7 @@ function BattleManager:LetUnitsShoot(groups, targetGroups)
expendQtyEnabled = true expendQtyEnabled = true
} }
} }
local controller = unit:getController() local controller = unit:getController()
if controller then if controller then
controller:setTask(shootTask) controller:setTask(shootTask)
@@ -141,7 +141,7 @@ function BattleManager:LetUnitsShoot(groups, targetGroups)
end end
end end
end end
end end
---@param unit Unit ---@param unit Unit
---@return number ---@return number
@@ -191,7 +191,7 @@ function BattleManager:IsUnitApplicable(unit)
end end
return true return true
end end
---@private ---@private
@@ -231,7 +231,7 @@ end
---@param groupHulls Array<Array<Vec2>> ---@param groupHulls Array<Array<Vec2>>
---@return Vec2? ---@return Vec2?
function BattleManager:GetRandomPoint(origin, groupHulls) function BattleManager:GetRandomPoint(origin, groupHulls)
local hull = Util.randomFromList(groupHulls) --[[@as Array<Vec2>]] local hull = Util.randomFromList(groupHulls) --[[@as Array<Vec2>]]
if not hull then return nil end if not hull then return nil end
local shootPoints = Util.GetTangentHullPointsFromOrigin(hull, origin) local shootPoints = Util.GetTangentHullPointsFromOrigin(hull, origin)
@@ -29,7 +29,7 @@ local SupplyLoadConfig = {
{ centerAngle = 270, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 }, { centerAngle = 270, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 },
{ centerAngle = 90, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 }, { centerAngle = 90, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 },
} }
}, },
["UH-1H"] = { ["UH-1H"] = {
maxInternalLoad = 2000, maxInternalLoad = 2000,
dropZones = { dropZones = {
@@ -236,7 +236,7 @@ function SupplyUnitsTracker:CheckUnitsInZones()
end) end)
end end
end end
else else
if self._unitInSupplyHub[tostring(unit:getID())] == true then if self._unitInSupplyHub[tostring(unit:getID())] == true then
self._unitInSupplyHub[tostring(unit:getID())] = false self._unitInSupplyHub[tostring(unit:getID())] = false
@@ -290,11 +290,11 @@ local cargoCount = 0
---@param crateType CrateType ---@param crateType CrateType
---@param missionCommandsHelper MissionCommandsHelper ---@param missionCommandsHelper MissionCommandsHelper
function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHelper) function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHelper)
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType) self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
local unit = DcsUtil.GetPlayerUnitByID(unitID) local unit = DcsUtil.GetPlayerUnitByID(unitID)
if unit == nil or unit:isExist() == false then if unit == nil or unit:isExist() == false then
self._logger:warn("Unload requested for non-existent unit: " .. unitID) self._logger:warn("Unload requested for non-existent unit: " .. unitID)
return return
end end
@@ -303,7 +303,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe
self._logger:warn("Unload requested for unit with no group: " .. unit:getName()) self._logger:warn("Unload requested for unit with no group: " .. unit:getName())
return return
end end
local cargoConfig = SupplyConfigHelper.getSupplyConfig(crateType) local cargoConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if cargoConfig == nil then if cargoConfig == nil then
@@ -360,11 +360,11 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType, missionC
end end
local unit = group:getUnit(1) local unit = group:getUnit(1)
if unit == nil then return end if unit == nil then return end
if unit:isExist() == false then return end if unit:isExist() == false then return end
if unit:inAir() == true then if unit:inAir() == true then
trigger.action.outTextForUnit(unit:getID(), "Land first before crates can be loaded", 10) trigger.action.outTextForUnit(unit:getID(), "Land first before crates can be loaded", 10)
return return
@@ -381,7 +381,7 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType, missionC
---@param params LoadCargoParams ---@param params LoadCargoParams
local LoadCrateTask = function(params) local LoadCrateTask = function(params)
local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType, params.commandHelper) local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType, params.commandHelper)
if loaded ~= false then if loaded ~= false then
trigger.action.outTextForUnit(unit:getID(), "Loaded crate :" .. params.crateType, 10) trigger.action.outTextForUnit(unit:getID(), "Loaded crate :" .. params.crateType, 10)
@@ -408,7 +408,7 @@ end
---@param commandHelper MissionCommandsHelper ---@param commandHelper MissionCommandsHelper
---@return boolean ---@return boolean
function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper) function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper)
local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType) local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfigA == nil then if crateConfigA == nil then
trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5) trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5)
@@ -442,7 +442,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper)
if group == nil then return false end if group == nil then return false end
local groupID = group:getID() local groupID = group:getID()
commandHelper:updateCommandsForGroup(groupID) commandHelper:updateCommandsForGroup(groupID)
return true return true
end end
@@ -460,7 +460,7 @@ function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType)
return return
end end
end end
end end
@@ -482,11 +482,11 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
if desc == nil or desc.box == nil then if desc == nil or desc.box == nil then
return nil return nil
end end
local objPos = foundObject:getPoint() local objPos = foundObject:getPoint()
local box = desc.box local box = desc.box
local heading = 0 local heading = 0
-- Try to get object heading from position vector's forward direction -- Try to get object heading from position vector's forward direction
-- This works for units and other objects that support getPosition -- This works for units and other objects that support getPosition
pcall(function() pcall(function()
@@ -495,13 +495,13 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
heading = math.atan2(objPosition.x.z, objPosition.x.x) heading = math.atan2(objPosition.x.z, objPosition.x.x)
end end
end) end)
-- For rotated objects, we need to rotate the bounding box -- For rotated objects, we need to rotate the bounding box
local minX = box.min.x local minX = box.min.x
local maxX = box.max.x local maxX = box.max.x
local minZ = box.min.z local minZ = box.min.z
local maxZ = box.max.z local maxZ = box.max.z
-- If object has significant rotation, apply rotation to bbox corners -- If object has significant rotation, apply rotation to bbox corners
if math.abs(heading) > 0.1 then if math.abs(heading) > 0.1 then
-- Get all 4 corners of bbox in local space -- Get all 4 corners of bbox in local space
@@ -511,11 +511,11 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
{maxX, minZ}, {maxX, minZ},
{maxX, maxZ} {maxX, maxZ}
} }
-- Rotate corners and find new min/max -- Rotate corners and find new min/max
minX, maxX = math.huge, -math.huge minX, maxX = math.huge, -math.huge
minZ, maxZ = math.huge, -math.huge minZ, maxZ = math.huge, -math.huge
for _, corner in ipairs(corners) do for _, corner in ipairs(corners) do
local rotX = corner[1] * math.cos(heading) - corner[2] * math.sin(heading) local rotX = corner[1] * math.cos(heading) - corner[2] * math.sin(heading)
local rotZ = corner[1] * math.sin(heading) + corner[2] * math.cos(heading) local rotZ = corner[1] * math.sin(heading) + corner[2] * math.cos(heading)
@@ -525,7 +525,7 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
maxZ = math.max(maxZ, rotZ) maxZ = math.max(maxZ, rotZ)
end end
end end
-- Convert relative bbox to world space by adding object position -- Convert relative bbox to world space by adding object position
---@type SupplyUnitsBoundingBox ---@type SupplyUnitsBoundingBox
return { return {
@@ -549,7 +549,7 @@ end
---@param safetyMargin number Safety margin around objects ---@param safetyMargin number Safety margin around objects
---@return boolean True if collision detected ---@return boolean True if collision detected
function SupplyUnitsTracker:CheckBBoxCollision(crateBBox, objBBox, safetyMargin) function SupplyUnitsTracker:CheckBBoxCollision(crateBBox, objBBox, safetyMargin)
-- Apply safety margin to object bbox -- Apply safety margin to object bbox
local objMin = { local objMin = {
x = objBBox.min.x - safetyMargin, x = objBBox.min.x - safetyMargin,
@@ -561,7 +561,7 @@ function SupplyUnitsTracker:CheckBBoxCollision(crateBBox, objBBox, safetyMargin)
y = objBBox.max.y + safetyMargin, y = objBBox.max.y + safetyMargin,
z = objBBox.max.z + safetyMargin z = objBBox.max.z + safetyMargin
} }
-- AABB collision detection -- AABB collision detection
return crateBBox.min.x <= objMax.x and crateBBox.max.x >= objMin.x and return crateBBox.min.x <= objMax.x and crateBBox.max.x >= objMin.x and
crateBBox.min.y <= objMax.y and crateBBox.max.y >= objMin.y and crateBBox.min.y <= objMax.y and crateBBox.max.y >= objMin.y and
@@ -575,7 +575,7 @@ end
function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName) function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
local unitPos = unit:getPosition() local unitPos = unit:getPosition()
-- Get unit's heading from the forward vector (x component) -- Get unit's heading from the forward vector (x component)
-- Heading is calculated as: atan2(forward.z, forward.x) -- Heading is calculated as: atan2(forward.z, forward.x)
local unitHeading = math.atan2(unitPos.x.z, unitPos.x.x) local unitHeading = math.atan2(unitPos.x.z, unitPos.x.x)
@@ -586,14 +586,14 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
self._logger:error("Could not get bbox for crate type: " .. crateTypeName) self._logger:error("Could not get bbox for crate type: " .. crateTypeName)
return nil return nil
end end
local crateRelativeBBox = crateDesc.box local crateRelativeBBox = crateDesc.box
-- Get drop zone config for this unit -- Get drop zone config for this unit
local dropZones = { local dropZones = {
{ centerAngle = 180, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 } { centerAngle = 180, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 }
} }
if SupplyLoadConfig[unit:getTypeName()] ~= nil then if SupplyLoadConfig[unit:getTypeName()] ~= nil then
dropZones = SupplyLoadConfig[unit:getTypeName()].dropZones dropZones = SupplyLoadConfig[unit:getTypeName()].dropZones
end end
@@ -646,15 +646,15 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
for distance = zone.minRadius, zone.maxRadius, zone.spacing do for distance = zone.minRadius, zone.maxRadius, zone.spacing do
-- Check multiple positions within the angular slice -- Check multiple positions within the angular slice
local angleStep = math.min(15, zone.angleWidth / 3) -- Divide slice into sections local angleStep = math.min(15, zone.angleWidth / 3) -- Divide slice into sections
for angle = minAngle, maxAngle, angleStep do for angle = minAngle, maxAngle, angleStep do
local radians = math.rad(angle) local radians = math.rad(angle)
-- Calculate position at this angle and distance, relative to unit's heading -- Calculate position at this angle and distance, relative to unit's heading
-- Angle 0 = forward, 90 = right, 180 = rear, 270 = left -- Angle 0 = forward, 90 = right, 180 = rear, 270 = left
-- Apply unit heading to make angles relative to unit orientation -- Apply unit heading to make angles relative to unit orientation
local worldAngle = radians + unitHeading local worldAngle = radians + unitHeading
local candidateX = unitPos.p.x + distance * math.sin(worldAngle) local candidateX = unitPos.p.x + distance * math.sin(worldAngle)
local candidateZ = unitPos.p.z + distance * math.cos(worldAngle) local candidateZ = unitPos.p.z + distance * math.cos(worldAngle)
local candidateY = land.getHeight({ x = candidateX, y = candidateZ }) local candidateY = land.getHeight({ x = candidateX, y = candidateZ })
@@ -25,7 +25,7 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
local BuildableMission = {} local BuildableMission = {}
BuildableMission.__index = BuildableMission BuildableMission.__index = BuildableMission
---@return string
local function getDefaultBriefing(siteType, coords) local function getDefaultBriefing(siteType, coords)
return "We've dispatched forward units to find a proper spot for a new " .. siteType .. "." .. return "We've dispatched forward units to find a proper spot for a new " .. siteType .. "." ..
"\nYou will need to drop off supplies so they can start building." .. "\nYou will need to drop off supplies so they can start building." ..
@@ -48,7 +48,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
setmetatable(BuildableMission, Mission) setmetatable(BuildableMission, Mission)
local self = setmetatable({}, { __index = BuildableMission }) local self = setmetatable({}, { __index = BuildableMission })
self._targetZone = targetZone self._targetZone = targetZone
self._database = database self._database = database
self._requiredKilos = requiredKilos self._requiredKilos = requiredKilos
@@ -75,7 +75,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
end end
self.code = tostring(database:GetNewMissionCode()) self.code = tostring(database:GetNewMissionCode())
local splitTargetZoneName = Util.split_string(targetZone.name, "_") local splitTargetZoneName = Util.split_string(targetZone.name, "_")
if splitTargetZoneName and splitTargetZoneName[3] and splitTargetZoneName[3] ~= "" then if splitTargetZoneName and splitTargetZoneName[3] and splitTargetZoneName[3] ~= "" then
self.name = splitTargetZoneName[3] self.name = splitTargetZoneName[3]
@@ -83,13 +83,6 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self.name = "Resupply" self.name = "Resupply"
end end
local type = "site"
if requiredCrateType == "SAM_CRATE" then
type = "SAM site"
elseif requiredCrateType == "FARP_CRATE" then
type = "FARP"
end
self.zoneName = targetZone.name .. "_supply" self.zoneName = targetZone.name .. "_supply"
self._logger = logger self._logger = logger
self._onCrateDroppedOfListeners = {} self._onCrateDroppedOfListeners = {}
@@ -139,7 +132,7 @@ function BuildableMission:ShowBriefing(groupID)
briefingPart = getDefaultBriefing(siteType, coords) briefingPart = getDefaultBriefing(siteType, coords)
end end
local briefing = "Mission [" .. self.code .. "] " .. self.name .. local briefing = "Mission [" .. self.code .. "] " .. self.name ..
"\n \n" .. "\n \n" ..
briefingPart .. briefingPart ..
"\n\n" .. "\n\n" ..
@@ -201,7 +194,7 @@ function BuildableMission:SpawnActive()
local fillColor2 = DrawingHelper.ColorTableToColorString({ 0, 0, 1, 0}) local fillColor2 = DrawingHelper.ColorTableToColorString({ 0, 0, 1, 0})
self._dropOffZoneDrawing = CustomDrawing.FromZone(self._dropOffZone, lineColor2, fillColor2, 2, 6) self._dropOffZoneDrawing = CustomDrawing.FromZone(self._dropOffZone, lineColor2, fillColor2, 2, 6)
self._dropOffZoneDrawing:Draw() self._dropOffZoneDrawing:Draw()
---@param selfA BuildableMission ---@param selfA BuildableMission
---@param time number ---@param time number
local checkForCrateTasks = function (selfA, time) local checkForCrateTasks = function (selfA, time)
@@ -266,8 +259,8 @@ function BuildableMission:CheckCratesInZone()
end end
end end
end end
for _, foundCrate in pairs(foundCrates) do for _, foundCrate in pairs(foundCrates) do
local crateConfig = SupplyConfigHelper.fromObjectName(foundCrate:getName()) local crateConfig = SupplyConfigHelper.fromObjectName(foundCrate:getName())
if crateConfig then if crateConfig then
self._droppedKilos = self._droppedKilos + crateConfig.weight self._droppedKilos = self._droppedKilos + crateConfig.weight
@@ -282,7 +275,7 @@ function BuildableMission:CheckCratesInZone()
self:NotifyMissionComplete() self:NotifyMissionComplete()
self._state = "COMPLETED" self._state = "COMPLETED"
end end
if self._state == "COMPLETED" then if self._state == "COMPLETED" then
for groupID, markID in pairs(self._markIDsPerGroup) do for groupID, markID in pairs(self._markIDsPerGroup) do
if markID then if markID then
@@ -42,7 +42,7 @@ function RunwayStrikeMission.new(runway, airbaseName, database, logger, runwayBo
self._runwayZone = self:RunwayToSpearheadZone(runway) self._runwayZone = self:RunwayToSpearheadZone(runway)
self._repairInProgress = false self._repairInProgress = false
self._minKilosForDamage = 100 self._minKilosForDamage = 100
local sections = self:ToSections(runway, 5) local sections = self:ToSections(runway, 5)
--[[ --[[
+-----------+-----------+-----------+-----------+-----------+ +-----------+-----------+-----------+-----------+-----------+
@@ -84,7 +84,6 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
self._logger:debug("Runway hit: " .. self._airportName .. ":" .. self._runway.Name) self._logger:debug("Runway hit: " .. self._airportName .. ":" .. self._runway.Name)
for _, section in pairs(self._runwaySections) do for _, section in pairs(self._runwaySections) do
local zone = self:SectionToSpearheadZone(section) local zone = self:SectionToSpearheadZone(section)
if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
if section.kilosHit == nil then if section.kilosHit == nil then
@@ -96,7 +95,7 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
end end
---@param selfA RunwayStrikeMission ---@param selfA RunwayStrikeMission
local updateState = function(selfA, time) local updateState = function(selfA, _)
selfA:UpdateState() selfA:UpdateState()
end end
@@ -154,7 +153,7 @@ function RunwayStrikeMission:Draw()
if runwaySection.drawID == nil then if runwaySection.drawID == nil then
local zone = self:SectionToSpearheadZone(runwaySection) local zone = self:SectionToSpearheadZone(runwaySection)
---@type Free ---@type Free
local drawObject = { local drawObject = {
primitiveType = "Polygon", primitiveType = "Polygon",
@@ -213,7 +212,6 @@ function RunwayStrikeMission:StartRepair()
if interval == nil then return nil end if interval == nil then return nil end
return time + interval return time + interval
end end
timer.scheduleFunction(repairTask, self, timer.getTime() + 5) timer.scheduleFunction(repairTask, self, timer.getTime() + 5)
end end
@@ -211,7 +211,7 @@ end
---@return boolean ---@return boolean
function ZoneMission:AllDependenciesMet() function ZoneMission:AllDependenciesMet()
local allDependenciesMet = true local allDependenciesMet = true
for missionName, value in pairs(self._dependencies) do for missionName, _ in pairs(self._dependencies) do
if self._parentStage:IsMissionComplete(missionName) == false then if self._parentStage:IsMissionComplete(missionName) == false then
allDependenciesMet = false allDependenciesMet = false
self._dependencies[missionName] = false self._dependencies[missionName] = false
@@ -503,12 +503,12 @@ end
---@param unit Object ---@param unit Object
function ZoneMission:MarkLastContact(unit) function ZoneMission:MarkLastContact(unit)
if not unit then if not unit then
self._logger:error("MarkLastContact called with nil unit") self._logger:error("MarkLastContact called with nil unit")
return return
end end
local point = unit:getPoint() local point = unit:getPoint()
if not point then if not point then
self._logger:error("MarkLastContact called with unit without point") self._logger:error("MarkLastContact called with unit without point")
return return
@@ -51,7 +51,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
self.location = database:GetLocationForMissionZone(zoneName) self.location = database:GetLocationForMissionZone(zoneName)
self.missionTypeDisplay = self.missionType self.missionTypeDisplay = self.missionType
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate() self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
return true, "success" return true, "success"
+22 -23
View File
@@ -77,7 +77,7 @@ do -- INIT DCS_UTIL
do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment
do --init trigger zones do --init trigger zones
for i, trigger_zone in pairs(env.mission.triggers.zones) do for _, trigger_zone in pairs(env.mission.triggers.zones) do
-- reorder verts as they are not ordered correctly in the ME -- reorder verts as they are not ordered correctly in the ME
local verts = {} local verts = {}
if Util.tableLength(trigger_zone.verticies) >= 4 then if Util.tableLength(trigger_zone.verticies) >= 4 then
@@ -192,7 +192,7 @@ do -- INIT DCS_UTIL
BLUE = 2 BLUE = 2
} }
]] -- ]] --
local input = string.lower(input) input = string.lower(input)
if input == 'neutrals' or input == "neutral" or input == "0" then if input == 'neutrals' or input == "neutral" or input == "0" then
return DCS_UTIL.Coalition.NEUTRAL return DCS_UTIL.Coalition.NEUTRAL
end end
@@ -235,7 +235,7 @@ do -- INIT DCS_UTIL
end end
end end
for index, zone_name in pairs(zone_names) do for _, zone_name in pairs(zone_names) do
local zone = DCS_UTIL.__trigger_zones[zone_name] local zone = DCS_UTIL.__trigger_zones[zone_name]
if zone then if zone then
zones[#zones + 1] = zone zones[#zones + 1] = zone
@@ -247,7 +247,7 @@ do -- INIT DCS_UTIL
local lUnit = units[units_ind] local lUnit = units[units_ind]
local unit_pos = lUnit:getPosition().p local unit_pos = lUnit:getPosition().p
local lCat = Object.getCategory(lUnit) local lCat = Object.getCategory(lUnit)
for zone_name, zone in pairs(zones) do for _, zone in pairs(zones) do
if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit
local isInZone = Util.is3dPointInZone(unit_pos, zone) local isInZone = Util.is3dPointInZone(unit_pos, zone)
if isInZone == true then if isInZone == true then
@@ -313,7 +313,7 @@ do -- INIT DCS_UTIL
function DCS_UTIL.isPositionInZones(x, z, zone_names) function DCS_UTIL.isPositionInZones(x, z, zone_names)
---@type Array<SpearheadTriggerZone> ---@type Array<SpearheadTriggerZone>
local zones = {} local zones = {}
for index, zone_name in pairs(zone_names) do for _, zone_name in pairs(zone_names) do
local zone = DCS_UTIL.__trigger_zones[zone_name] local zone = DCS_UTIL.__trigger_zones[zone_name]
if zone then if zone then
zones[#zones + 1] = zone zones[#zones + 1] = zone
@@ -321,7 +321,7 @@ do -- INIT DCS_UTIL
end end
local result_zones = {} local result_zones = {}
for zone_name, zone in pairs(zones) do for _, zone in pairs(zones) do
if Util.is3dPointInZone({ x = x, z = z, y = 0 }, zone) == true then if Util.is3dPointInZone({ x = x, z = z, y = 0 }, zone) == true then
result_zones[#result_zones + 1] = zone.name result_zones[#result_zones + 1] = zone.name
end end
@@ -414,7 +414,7 @@ do -- INIT DCS_UTIL
local height = land.getHeight(location) local height = land.getHeight(location)
local vec3 = { x = location.x, y = height, z = location.y } local vec3 = { x = location.x, y = height, z = location.y }
local unitType = string.lower(unitType or "") unitType = string.lower(unitType or "")
local conversionType = config[unitType] local conversionType = config[unitType]
if not conversionType then conversionType = "DDM" end if not conversionType then conversionType = "DDM" end
@@ -471,7 +471,7 @@ do -- INIT DCS_UTIL
lon_hemisphere, math.abs(lon_deg), lon_min_display, lon_sec_display, lon_hemisphere, math.abs(lon_deg), lon_min_display, lon_sec_display,
altitude * 3,28084) altitude * 3,28084)
end end
end end
---@param zone SpearheadTriggerZone ---@param zone SpearheadTriggerZone
@@ -574,7 +574,7 @@ do -- INIT DCS_UTIL
local units = {} local units = {}
for i = 0, 2 do for i = 0, 2 do
local players = coalition.getPlayers(i) local players = coalition.getPlayers(i)
for key, unit in pairs(players) do for _, unit in pairs(players) do
units[#units + 1] = unit units[#units + 1] = unit
end end
end end
@@ -617,8 +617,7 @@ do -- INIT DCS_UTIL
end end
function DCS_UTIL.CleanCorpse(unitName) function DCS_UTIL.CleanCorpse(unitName)
local unitName = "dead_" .. unitName unitName = "dead_" .. unitName
local object = StaticObject.getByName(unitName) local object = StaticObject.getByName(unitName)
if object then if object then
@@ -632,11 +631,11 @@ do -- INIT DCS_UTIL
---@field b number ---@field b number
---@field a number ---@field a number
local drawID = 4210 local __drawID = 4210
function DCS_UTIL.GetNextDrawID() function DCS_UTIL.GetNextDrawID()
drawID = drawID + 1 __drawID = __drawID + 1
return drawID return __drawID
end end
---@param groupID number ---@param groupID number
@@ -644,9 +643,9 @@ do -- INIT DCS_UTIL
---@param location Vec3 ---@param location Vec3
---@return number markID ---@return number markID
function DCS_UTIL.AddMarkToGroup(groupID, text, location) function DCS_UTIL.AddMarkToGroup(groupID, text, location)
local drawID = DCS_UTIL.GetNextDrawID() local nextId = DCS_UTIL.GetNextDrawID()
trigger.action.markToGroup(drawID, text, location, groupID, true, nil) trigger.action.markToGroup(nextId, text, location, groupID, true, nil)
return drawID return nextId
end end
---comment ---comment
@@ -654,9 +653,9 @@ do -- INIT DCS_UTIL
---@param location Vec3 ---@param location Vec3
---@return integer ---@return integer
function DCS_UTIL.AddMarkToAll(text, location) function DCS_UTIL.AddMarkToAll(text, location)
local drawID = DCS_UTIL.GetNextDrawID() local nextId = DCS_UTIL.GetNextDrawID()
trigger.action.markToAll(drawID, text, location, true, nil) trigger.action.markToAll(nextId, text, location, true, nil)
return drawID return nextId
end end
---@param markId number ---@param markId number
@@ -705,7 +704,7 @@ do -- INIT DCS_UTIL
---@return number? id ---@return number? id
function DCS_UTIL.GetNeutralCountry() function DCS_UTIL.GetNeutralCountry()
for name, id in pairs(country.id) do for _, id in pairs(country.id) do
if coalition.getCountryCoalition(id) == DCS_UTIL.Coalition.NEUTRAL then if coalition.getCountryCoalition(id) == DCS_UTIL.Coalition.NEUTRAL then
return id return id
end end
@@ -759,7 +758,7 @@ do -- INIT DCS_UTIL
function DCS_UTIL.GetPlayerGroupByGroupID(groupId) function DCS_UTIL.GetPlayerGroupByGroupID(groupId)
for i = 0, 2 do for i = 0, 2 do
local players = coalition.getPlayers(i) local players = coalition.getPlayers(i)
for key, unit in pairs(players) do for _, unit in pairs(players) do
if unit and unit:isExist() == true then if unit and unit:isExist() == true then
local group = unit:getGroup() local group = unit:getGroup()
if group and group:getID() == groupId then if group and group:getID() == groupId then
@@ -775,7 +774,7 @@ do -- INIT DCS_UTIL
function DCS_UTIL.GetPlayerUnitByID(unitID) function DCS_UTIL.GetPlayerUnitByID(unitID)
for i = 0, 2 do for i = 0, 2 do
local players = coalition.getPlayers(i) local players = coalition.getPlayers(i)
for key, unit in pairs(players) do for _, unit in pairs(players) do
if unit and unit:getID() == unitID then if unit and unit:getID() == unitID then
return unit return unit
end end
+1 -1
View File
@@ -149,7 +149,7 @@ do -- INIT UTIL
---@param findableTable table ---@param findableTable table
---@return boolean ---@return boolean
UTIL.startswithAny = function(str, findableTable) UTIL.startswithAny = function(str, findableTable)
for key, value in pairs(findableTable) do for _, value in pairs(findableTable) do
if type(value) == "string" and UTIL.startswith(str, value) then return true end if type(value) == "string" and UTIL.startswith(str, value) then return true end
end end
return false return false