Created lane feature that should be backwards compatible with non-lane features

This commit is contained in:
2026-08-25 18:40:07 +02:00
parent b9068a7ac8
commit 27548d69bb
13 changed files with 1055 additions and 254 deletions
+410 -35
View File
@@ -406,8 +406,8 @@
"type": 3
},
"seed": 1626473245,
"version": 468,
"versionNonce": 1827077469,
"version": 469,
"versionNonce": 874232424,
"isDeleted": false,
"boundElements": [
{
@@ -417,13 +417,9 @@
{
"id": "kIqbCgtXKr5-2qhRSYTAa",
"type": "arrow"
},
{
"id": "0CY7kgHkgaXEMGTFJiv2H",
"type": "arrow"
}
],
"updated": 1787318071712,
"updated": 1787406477137,
"link": null,
"locked": false
},
@@ -633,11 +629,11 @@
"type": 2
},
"seed": 2125327475,
"version": 36,
"versionNonce": 1265229288,
"version": 40,
"versionNonce": 1013087493,
"isDeleted": true,
"boundElements": [],
"updated": 1787403270962,
"updated": 1787675736572,
"link": null,
"locked": false,
"points": [
@@ -773,8 +769,8 @@
"type": 3
},
"seed": 1931784787,
"version": 595,
"versionNonce": 648660125,
"version": 597,
"versionNonce": 196807877,
"isDeleted": false,
"boundElements": [
{
@@ -786,15 +782,15 @@
"type": "arrow"
},
{
"id": "0CY7kgHkgaXEMGTFJiv2H",
"id": "Q8b54GQSVn5ZBrXMwYjN7",
"type": "arrow"
},
{
"id": "Q8b54GQSVn5ZBrXMwYjN7",
"id": "MwTn5dJVKPmeTHOM-q3f9",
"type": "arrow"
}
],
"updated": 1787318161674,
"updated": 1787675772937,
"link": null,
"locked": false
},
@@ -1488,11 +1484,11 @@
"type": 2
},
"seed": 536727613,
"version": 139,
"versionNonce": 1741218173,
"isDeleted": false,
"version": 140,
"versionNonce": 399876888,
"isDeleted": true,
"boundElements": [],
"updated": 1787318074593,
"updated": 1787406477136,
"link": null,
"locked": false,
"points": [
@@ -1772,8 +1768,8 @@
"type": 3
},
"seed": 1971420499,
"version": 285,
"versionNonce": 598897235,
"version": 286,
"versionNonce": 142740965,
"isDeleted": false,
"boundElements": [
{
@@ -1783,9 +1779,13 @@
{
"id": "w6_Fv5GP_VEFqSQMXy25N",
"type": "arrow"
},
{
"id": "o3WprJN3wZEDcHw1DiFYr",
"type": "arrow"
}
],
"updated": 1787318114912,
"updated": 1787675785325,
"link": null,
"locked": false
},
@@ -1889,7 +1889,7 @@
"id": "qO3tWw8uDrdY-GSL5hbG7",
"type": "rectangle",
"x": 1611.9167066751852,
"y": 1250.2170926328931,
"y": 1206.2170926328931,
"width": 144,
"height": 99,
"angle": 0,
@@ -1907,8 +1907,8 @@
"type": 3
},
"seed": 2008474461,
"version": 330,
"versionNonce": 2001596957,
"version": 502,
"versionNonce": 72829003,
"isDeleted": false,
"boundElements": [
{
@@ -1918,9 +1918,13 @@
{
"id": "Q8b54GQSVn5ZBrXMwYjN7",
"type": "arrow"
},
{
"id": "Z4Fy52Fl_c9lXHJlrhd9g",
"type": "arrow"
}
],
"updated": 1787318163603,
"updated": 1787675800613,
"link": null,
"locked": false
},
@@ -1928,7 +1932,7 @@
"id": "EFDaczSk3yi8gn2b8hoI_",
"type": "text",
"x": 1632.626736277236,
"y": 1287.2170926328931,
"y": 1243.2170926328931,
"width": 102.57994079589844,
"height": 25,
"angle": 0,
@@ -1944,11 +1948,11 @@
"index": "ag",
"roundness": null,
"seed": 958164925,
"version": 298,
"versionNonce": 374795869,
"version": 467,
"versionNonce": 328458027,
"isDeleted": false,
"boundElements": [],
"updated": 1787318158139,
"updated": 1787675761637,
"link": null,
"locked": false,
"text": "STAGE C5",
@@ -1967,7 +1971,7 @@
"x": 1449.9192687785321,
"y": 980.5000000000001,
"width": 155.99743789665308,
"height": 315.1857377213224,
"height": 273.41807104570137,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "#1971c2",
@@ -1983,11 +1987,11 @@
"type": 2
},
"seed": 935284605,
"version": 184,
"versionNonce": 822381789,
"version": 353,
"versionNonce": 667206091,
"isDeleted": false,
"boundElements": [],
"updated": 1787318165497,
"updated": 1787675761637,
"link": null,
"locked": false,
"points": [
@@ -2001,7 +2005,7 @@
],
[
155.99743789665308,
315.1857377213224
273.41807104570137
]
],
"startBinding": {
@@ -2022,6 +2026,377 @@
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false,
"moveMidPointsWithElement": false
},
{
"id": "oIXVKbkqP10kd7HsX3Ma8",
"type": "rectangle",
"x": 1845.5,
"y": 878,
"width": 144,
"height": 99,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#e8590c",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "ai",
"roundness": {
"type": 3
},
"seed": 1637710309,
"version": 783,
"versionNonce": 1692360869,
"isDeleted": false,
"boundElements": [
{
"type": "text",
"id": "7AVtQifmKK8XdbWOLnM1M"
},
{
"id": "MwTn5dJVKPmeTHOM-q3f9",
"type": "arrow"
},
{
"id": "o3WprJN3wZEDcHw1DiFYr",
"type": "arrow"
},
{
"id": "Z4Fy52Fl_c9lXHJlrhd9g",
"type": "arrow"
}
],
"updated": 1787675805417,
"link": null,
"locked": false
},
{
"id": "7AVtQifmKK8XdbWOLnM1M",
"type": "text",
"x": 1872.2800216674805,
"y": 915,
"width": 90.43995666503906,
"height": 25,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "#96f2d7",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aj",
"roundness": null,
"seed": 1576990021,
"version": 739,
"versionNonce": 761448939,
"isDeleted": false,
"boundElements": [],
"updated": 1787675780472,
"link": null,
"locked": false,
"text": "STAGE 6",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "oIXVKbkqP10kd7HsX3Ma8",
"originalText": "STAGE 6",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "MwTn5dJVKPmeTHOM-q3f9",
"type": "arrow",
"x": 1497,
"y": 925.0522131170663,
"width": 342.5,
"height": 2.4153737658673435,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "ak",
"roundness": {
"type": 2
},
"seed": 1633560229,
"version": 99,
"versionNonce": 1709724811,
"isDeleted": false,
"boundElements": null,
"updated": 1787675780472,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
342.5,
2.4153737658673435
]
],
"startBinding": {
"elementId": "UoiunLPIaAwLLJTJGcSj8",
"mode": "orbit",
"fixedPoint": [
1,
0.5001
]
},
"endBinding": {
"elementId": "oIXVKbkqP10kd7HsX3Ma8",
"mode": "orbit",
"fixedPoint": [
0,
0.5001
]
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false,
"moveMidPointsWithElement": false
},
{
"id": "o3WprJN3wZEDcHw1DiFYr",
"type": "arrow",
"x": 1755.5,
"y": 765.5,
"width": 84.00000000000023,
"height": 141.54545454545473,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "al",
"roundness": {
"type": 2
},
"seed": 68654021,
"version": 179,
"versionNonce": 1367219755,
"isDeleted": false,
"boundElements": [],
"updated": 1787675789610,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
43,
33
],
[
42,
132
],
[
84.00000000000023,
141.54545454545473
]
],
"startBinding": {
"elementId": "Dug48Ksr7mdITJ1CSQdgJ",
"mode": "inside",
"fixedPoint": [
0.9971062036445457,
0.47830856212545203
]
},
"endBinding": {
"elementId": "oIXVKbkqP10kd7HsX3Ma8",
"mode": "orbit",
"fixedPoint": [
0.45884773662551637,
0.45884773662551726
]
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
},
{
"id": "GPK9crZA0GMljhRqF5HDa",
"type": "text",
"x": 1794.2073421171517,
"y": 835.5194758205485,
"width": 8,
"height": 25,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "am",
"roundness": null,
"seed": 1055479941,
"version": 3,
"versionNonce": 1276059851,
"isDeleted": true,
"boundElements": null,
"updated": 1787675789610,
"link": null,
"locked": false,
"text": "",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "o3WprJN3wZEDcHw1DiFYr",
"originalText": "",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "v_3ygDgE5RD8LQhGEvhTX",
"type": "arrow",
"x": 1662.1960283823262,
"y": 1200.2170926328931,
"width": 128.30397161767382,
"height": 194.71709263289313,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "an",
"roundness": {
"type": 2
},
"seed": 1602252997,
"version": 261,
"versionNonce": 1283832069,
"isDeleted": true,
"boundElements": null,
"updated": 1787675799132,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
128.30397161767382,
-194.71709263289313
]
],
"startBinding": {
"elementId": "qO3tWw8uDrdY-GSL5hbG7",
"mode": "orbit",
"fixedPoint": [
0.2207233413264886,
0.22072334132648888
]
},
"endBinding": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false,
"fixedSegments": null,
"startIsSpecial": null,
"endIsSpecial": null
},
{
"id": "Z4Fy52Fl_c9lXHJlrhd9g",
"type": "arrow",
"x": 1761.9167066751852,
"y": 1248.0538669619218,
"width": 78.55616398479242,
"height": 286.26754389891505,
"angle": 0,
"strokeColor": "#ffffff",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "ao",
"roundness": {
"type": 2
},
"seed": 1569002603,
"version": 382,
"versionNonce": 2105101093,
"isDeleted": false,
"boundElements": null,
"updated": 1787675810843,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
28.583293324814804,
-36.55386696192181
],
[
4.583293324814804,
-269.5538669619218
],
[
78.55616398479242,
-286.26754389891505
]
],
"startBinding": {
"elementId": "qO3tWw8uDrdY-GSL5hbG7",
"mode": "orbit",
"fixedPoint": [
1,
0.5001
]
},
"endBinding": {
"elementId": "oIXVKbkqP10kd7HsX3Ma8",
"mode": "orbit",
"fixedPoint": [
0.24597691208358835,
0.7540230879164157
]
},
"startArrowhead": null,
"endArrowhead": "arrow",
"elbowed": false
}
],
+2 -3
View File
@@ -10,7 +10,6 @@ local MissionCompleteListeners = {}
---@class SpearheadAPIInternal
---@field notifyMissionComplete fun(zone_name: string)
---@type FullSpearheadAPI
SpearheadAPI = {
Stages = {
@@ -25,12 +24,12 @@ SpearheadAPI = {
getCurrentStage = function()
return GlobalStageManager.getCurrentStage() or nil
end,
isStageComplete = function(stageNumber)
isStageComplete = function(stageNumber, stageLaneIdentifier)
if type(stageNumber) ~= "number" then
return false, "stageNumber " .. stageNumber .. " is not a valid number"
end
local isComplete = GlobalStageManager.isStageComplete(stageNumber)
local isComplete = GlobalStageManager.isStageComplete(stageNumber, stageLaneIdentifier)
if isComplete == nil then
return nil, "no stage found with number " .. stageNumber
end
+1 -2
View File
@@ -6,7 +6,7 @@ SpearheadAPI = SpearheadAPI
---@class SpearheadStagesAPI
---@field changeStage fun(stageNumber: number): boolean, string @Changes the active stage of spearhead. <br/> All other stages will change based on the normal logic. (CAP, BLUE etc.)
---@field getCurrentStage fun(): number | nil @Returns the current stange number <br/> Returns nil when the stagenumber was not set before ever, which means Spearhead was not started.
---@field isStageComplete fun(stageNumber: number): boolean | nil, string @returns whether a stage (by index) is complete. <br/> @param stageNumber number <br/> @return boolean | nil <br/> @return string
---@field isStageComplete fun(stageNumber: number, stageLaneIdentifier: string?): boolean | nil, string @returns whether a stage (by index) is complete. <br/> @param stageNumber number <br/> @param stageLaneIdentifier string? nil for default lane <br/> @return boolean | nil <br/> @return string
---@class OnMissionCompleteListener
---@field onMissionComplete fun(self: OnMissionCompleteListener, zone_name: string) @Called when a mission is completed. @return void
@@ -14,4 +14,3 @@ SpearheadAPI = SpearheadAPI
---@class MissionAPI
---@field addOnMissionCompleteListener fun(listener: OnMissionCompleteListener) @Adds a listener to the mission. <br/> @param listener OnMissionCompleteListener <br/> @return void
+8 -13
View File
@@ -29,7 +29,7 @@ do
end
---@class OnStageChangedListener
---@field OnStageNumberChanged fun(self:OnStageChangedListener, number:integer)
---@field OnStageNumberChanged fun(self:OnStageChangedListener, number:integer, laneIdentifier:string?)
do -- STAGE NUMBER CHANGED
local OnStageNumberChangedListeners = {}
@@ -37,17 +37,11 @@ do
---Add a stage zone number changed listener
---@param listener OnStageChangedListener object with function OnStageNumberChanged(self, number)
SpearheadEvents.AddStageNumberChangedListener = function(listener)
table.insert(OnStageNumberChangedListeners, listener)
end
---Add a stage zone number changed listener
---@param handler function function(number)
SpearheadEvents.AddStageNumberChangedHandler = function(handler)
if type(handler) ~= "function" then
warn("Event handler not of type function, did you mean to use listener?")
if type(listener) ~= "table" or type(listener.OnStageNumberChanged) ~= "function" then
warn("Event handler not of type table/object with function OnStageNumberChanged(self, number)")
return
end
table.insert(OnStageNumberChangedHandlers, handler)
table.insert(OnStageNumberChangedListeners, listener)
end
---@class OnStageNumberChangeCompleteListener
@@ -64,14 +58,15 @@ do
end
---@param newStageNumber number
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber)
---@param laneIdentifier string?
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber, laneIdentifier)
pcall(function ()
Persistence.SetActiveStage(newStageNumber)
end)
for _, callable in pairs(OnStageNumberChangedListeners) do
local succ, err = pcall(function()
callable:OnStageNumberChanged(newStageNumber)
callable:OnStageNumberChanged(newStageNumber, laneIdentifier)
end)
if err then
logError(err)
@@ -79,7 +74,7 @@ do
end
for _, callable in pairs(OnStageNumberChangedHandlers) do
local succ, err = pcall(callable, newStageNumber)
local succ, err = pcall(callable, newStageNumber, laneIdentifier)
if err then
logError(err)
end
+352 -179
View File
@@ -8,17 +8,8 @@ local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
local WaitingStage = require("classes.stageClasses.Stages.WaitingStage")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local StagesByName = {}
---@type table<string, Array<Stage>>
local StagesByIndex = {}
---@type table<string, Array<Stage>>
local SideStageByIndex = {}
---@type table<string, Array<WaitingStage>>
local WaitingStagesByIndex = {}
local StageRepository = require("classes.stageClasses.StageRepository")
local StageLane = require("classes.stageClasses.StageLane")
local currentStage = -99
@@ -26,25 +17,37 @@ local currentStage = -99
---@field private database Database
---@field private logger Logger
---@field private stageConfig StageConfig
---@field private _stageRepository StageRepository
---@field private _missionCommandsHelper MissionCommandsHelper
local GlobalStageManager = {}
GlobalStageManager.__index = GlobalStageManager
GlobalStageManager.getCurrentStage = function() return currentStage end
---@type GlobalStageManager
local singletonInstance = nil
---comment
---@param database Database
---@param stageConfig StageConfig
---@param logLevel LogLevel
---@param spawnManager SpawnManager
---@return nil
---@return GlobalStageManager
function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnManager)
if singletonInstance ~= nil then
return singletonInstance
end
local logger = Logger.new("StageManager", logLevel)
logger:info("Using Stage Log Level: " .. logLevel)
local self = setmetatable({}, GlobalStageManager)
singletonInstance = self
self.database = database
self.stageConfig = stageConfig
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
self._stageRepository = StageRepository.new()
self.logger = logger
if stageConfig.isAutoStages ~= true then
@@ -56,160 +59,221 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
for _, stageName in pairs(database:getStagezoneNames()) do
logger:debug("Found stage zone with name: " .. stageName)
if Util.startswith(stageName, "missionstage", true) then
local valid = true
local split = Util.split_string(stageName, "_")
if Util.tableLength(split) < 2 then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a order number or valid format")
valid = false
end
local parseResult = self:ParseStageName(stageName)
if parseResult.isValid == false then
logger:warn("Stage zone with name " .. stageName .. " is not valid: " .. parseResult.invalidReason)
else
local initData = {
stageZoneName = parseResult.stageZoneName,
stageNumber = parseResult.orderNumber,
stageDisplayName = parseResult.stageDisplayName,
stageLaneIdentifier = parseResult.stageLaneIdentifier
}
if Util.tableLength(split) < 3 then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a stage name")
end
if parseResult.stageType == "PrimaryStage" then
local stage = PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
local orderNumber = nil
local isSideStage = false
if valid == true then
local orderNumberString = string.lower(split[2])
if Util.startswith(orderNumberString, "x") == true then
isSideStage = true
orderNumberString = string.gsub(orderNumberString, "x", "")
orderNumber = tonumber(orderNumberString)
else
orderNumber = tonumber(split[2])
end
if orderNumber == nil then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a valid order number : " .. split[2])
valid = false
end
end
local stageDisplayName = split[3]
local stagelogger = Logger.new(stageName, logLevel)
if valid == true and orderNumber then
---@type StageInitData
local initData = {
stageDisplayName = stageDisplayName,
stageNumber = orderNumber,
stageZoneName = stageName,
}
if isSideStage == true then
local stage = ExtraStage.New(database, stageConfig, stagelogger, initData, spawnManager)
stage:AddStageCompleteListener(self)
if SideStageByIndex[tostring(orderNumber)] == nil then SideStageByIndex[tostring(orderNumber)] = {} end
table.insert(SideStageByIndex[tostring(orderNumber)], stage)
else
local stage = PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager)
stage:AddStageCompleteListener(self)
if StagesByIndex[tostring(orderNumber)] == nil then StagesByIndex[tostring(orderNumber)] = {} end
table.insert(StagesByIndex[tostring(orderNumber)], stage)
end
stage:AddStageCompleteListener(self)
self._stageRepository:AddStage(stage)
elseif parseResult.stageType == "ExtraStage" then
local stage = ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
stage:AddStageCompleteListener(self)
self._stageRepository:AddStage(stage)
elseif parseResult.stageType == "WaitingStage" then
local waitingStage = WaitingStage.New(database, stageConfig, logger, initData, parseResult.waitingStageSeconds, spawnManager)
waitingStage:AddStageCompleteListener(self)
self._stageRepository:AddStage(waitingStage)
end
end
if Util.startswith(stageName, "waitingstage", true) then
local valid = true
local split = Util.split_string(stageName, "_")
if Util.tableLength(split) < 3 then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a order number or valid format")
valid = false
end
if valid == true then
local stageIndexString = split[2]
local stageIndex = tonumber(stageIndexString)
if not stageIndex then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a valid order number")
valid = false
end
local waitingSecondsString = split[3]
local waitingSeconds = tonumber(waitingSecondsString)
if not waitingSeconds then
MissionEditorWarnings.Add("Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter")
valid = false
end
if valid == true then
local stagelogger = Logger.new(stageName, logLevel)
---@type WaitingStageInitData
local initData = {
stageDisplayName = "Waiting Stage " .. stageIndex,
stageNumber = stageIndex or -99,
stageZoneName = stageName,
waitingSeconds = waitingSeconds --[[@as integer]]
}
local waitingStage = WaitingStage.New(database, stageConfig, stagelogger, initData, spawnManager)
if WaitingStagesByIndex[tostring(stageIndex)] == nil then
WaitingStagesByIndex[tostring(stageIndex)] = {}
end
table.insert(WaitingStagesByIndex[tostring(stageIndex)], waitingStage)
waitingStage:AddStageCompleteListener(self)
end
end
end
end
singletonInstance = self
return self
end
---@class StageNameParseResult
---@field isValid boolean
---@field orderNumber integer
---@field stageLaneIdentifier string?
---@field stageDisplayName string
---@field stageZoneName string
---@field stageType StageType
---@field invalidReason string?
---@field waitingStageSeconds integer?
---@param self GlobalStageManager
---@param stageName string
---@return StageNameParseResult
function GlobalStageManager:ParseStageName(stageName)
local split = Util.split_string(stageName, "_")
if Util.tableLength(split) < 3 then
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a order number or valid format" }
end
local typePart = string.lower(split[1])
if typePart == "missionstage" then
local orderNumberString = string.lower(split[2])
---@type StageType
local stageType = "PrimaryStage"
if Util.endsWith(orderNumberString, "x", true) == true then
stageType = "ExtraStage"
orderNumberString = orderNumberString:sub(1, -1)
end
local stageLaneIdentifier = nil
local first = orderNumberString:sub(1, 1)
if tonumber(first) == nil then -- first character is the lane identifier only if it's not a number
stageLaneIdentifier = string.lower(first)
orderNumberString = orderNumberString:sub(2)
end
local orderNumber = tonumber(orderNumberString)
if orderNumber == nil then
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString }
end
local stageDisplayName = split[3]
---@type StageNameParseResult
local result = {
isValid = true,
orderNumber = orderNumber,
stageLaneIdentifier = stageLaneIdentifier,
stageDisplayName = stageDisplayName,
stageZoneName = stageName,
stageType = stageType
}
return result
elseif typePart == "waitingstage" then
local stageType = "WaitingStage"
local orderNumberString = split[2]
local orderNumber = tonumber(orderNumberString)
if orderNumber == nil then
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString }
end
local waitingSecondsString = split[3]
local waitingSeconds = tonumber(waitingSecondsString)
if waitingSeconds == nil then
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter : " .. waitingSecondsString }
end
local stageDisplayName = "Waiting Stage " .. orderNumber
---@type StageNameParseResult
local result = {
isValid = true,
orderNumber = orderNumber,
stageLaneIdentifier = nil,
stageDisplayName = stageDisplayName,
stageZoneName = stageName,
stageType = stageType,
waitingStageSeconds = waitingSeconds
}
return result
end
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " has an unrecognized type: " .. typePart }
end
---@param stage Stage
function GlobalStageManager:OnStageComplete(stage)
self.logger:debug("Receiving stage complete event from: " .. stage.zoneName)
local anyIncomplete = false
self.logger:debug("Checking stages for index: " .. tostring(currentStage))
for index, stage in pairs(StagesByIndex[tostring(currentStage)]) do
if stage:IsComplete() == false then
anyIncomplete = true
self.logger:debug("Need to wait for Stage " .. stage.zoneName .. " to be completed")
else
self.logger:debug("Stage verified to be completed: " .. stage.zoneName)
end
local stageIndex = stage:GetStageIndex()
local laneIdentifier = stage:GetStageLaneIdentifier()
local stageLane = self._stageRepository:getStageLane(laneIdentifier)
if not stageLane or not stageLane:IsCurrentStageIndexComplete() then
self.logger:debug("Stage lane " .. (laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex)
return
end
if anyIncomplete == false and self.stageConfig.isAutoStages == true then
-- CHECK WAITING STAGES
local nextStage = currentStage + 1
local nextStageIndex = stageIndex + 1
if WaitingStagesByIndex[tostring(nextStage)] then
for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do
if waitingStage:IsActive() == false then
waitingStage:ActivateStage()
Events.PublishStageNumberChanged(nextStageIndex, laneIdentifier)
stageLane:SetActiveStageIndex(nextStageIndex)
if stageLane:IsDefaultStageLane() then
-- COMPLETION IN THE DEFAULT LANE
-- 1. Check if any lane is at status "BetweenChapters" and the next chapter start is "next stage index"
local allLanes = self._stageRepository:getAllStageLanes()
for _, lane in ipairs(allLanes) do
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey and lane:GetStageLaneState() == "InChapter" then
local nextChapterStart = lane:GetNextChapterStart()
if nextChapterStart == nextStageIndex then
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is at chapter start for next stage index " .. nextStageIndex)
Events.PublishStageNumberChanged(nextStageIndex, lane:GetStageLaneIdentifier())
lane:SetActiveStageIndex(nextStageIndex)
end
end
end
else
-- COMPLETION IN A SIDE LANE
local anyWaiting = false
if WaitingStagesByIndex[tostring(nextStage)] then
for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do
if waitingStage:IsComplete() == false then
anyWaiting = true
-- 1. Check if the default lane is at status "BetweenChapters"
local defaultStageLane = self._stageRepository:getStageLane(StageLane.DefaultLaneKey)
if defaultStageLane and defaultStageLane:GetStageLaneState() == "BetweenChapters" then
local nextChapterStart = defaultStageLane:GetNextChapterStart()
if nextChapterStart == nextStageIndex then
-- 2. Check if all side lanes are at or beyond the next chapter starts
local allLanes = self._stageRepository:getAllStageLanes()
local allSideLanesReady = true
for _, lane in ipairs(allLanes) do
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey then
-- 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
allSideLanesReady = false
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex)
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
elseif lane:GetStageLaneState() == "BetweenChapters" then
local nextChapter = lane:GetNextChapterStart()
if nextChapter and nextChapter < nextStageIndex then
allSideLanesReady = false
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex)
break
end
end
end
end
if allSideLanesReady then
self.logger:debug("All side lanes are ready for next stage index " .. nextStageIndex .. ", activating default lane")
Events.PublishStageNumberChanged(nextStageIndex, nil)
defaultStageLane:SetActiveStageIndex(nextStageIndex)
else
self.logger:debug("Not all side lanes are ready for next stage index " .. nextStageIndex .. ", default lane will not be activated")
end
end
end
if anyWaiting == false then
local newStageNumber = currentStage + 1
self.logger:debug("Setting next stage to: " .. tostring(newStageNumber))
Events.PublishStageNumberChanged(newStageNumber)
end
end
end
---@param stageNumber number
---@param stageLaneIdentifier string?
---@return boolean?
function GlobalStageManager:IsStageComplete(stageNumber, stageLaneIdentifier)
local stageLane = self._stageRepository:getStageLane(stageLaneIdentifier)
if not stageLane then
self.logger:warn("Stage lane " .. (stageLaneIdentifier or "default") .. " does not exist")
return nil
end
return stageLane:IsStageIndexComplete(stageNumber)
end
---@public
function GlobalStageManager:OnStageNumberChanged(stageNumber)
self.logger:debug("Stage number changed to: " .. tostring(stageNumber))
@@ -252,67 +316,176 @@ function GlobalStageManager:UpdateDrawings(stageNumber)
end
GlobalStageManager.printFullOverview = function ()
function GlobalStageManager:PrintMermaidStage()
local lanes = self._stageRepository:getAllStageLanes()
local logger = Logger.new("StageOverview", "INFO")
logger:info("Stage overview:")
local nodes = {}
local edges = {}
local stageIndicesByLane = {} -- Track all stage indices per lane
local mainLaneId = "default"
local max = 0
local lines = {}
for stageIndex, stages in pairs(StagesByIndex) do
-- Build nodes and collect all stage indices per lane
for _, lane in ipairs(lanes) do
local laneId = lane:GetStageLaneIdentifier() or "default"
local stageIndices = lane:GetAllStageIndices()
stageIndicesByLane[laneId] = stageIndices
local totalStrike = 0
local totalbai = 0
local totaldead = 0
local totalMissions = 0
local totalCas = 0
-- Create nodes for each stage index in this lane
for _, stageIndex in ipairs(stageIndices) do
local stages = lane:GetStagesAtIndex(stageIndex)
for _, stage in pairs(stages) do
if stages then
for _, stage in ipairs(stages) do
local nodeId = laneId .. "_" .. stageIndex
local stageType = stage:GetStageType()
local label = stage.stageName or stage.zoneName
local strike, dead, bai, cas = stage:GetStageStats()
-- Different node shapes for different stage types
local nodeShape = "["
local nodeEnd = "]"
if stageType == "ExtraStage" then
nodeShape = "(["
nodeEnd = "])"
elseif stageType == "WaitingStage" then
nodeShape = "[["
nodeEnd = "]]"
end
totalStrike = totalStrike + strike
totalbai = totalbai + bai
totaldead = totaldead + dead
totalCas = totalCas + cas
totalMissions = totalMissions + strike + dead + bai + cas
end
local index = tonumber(stageIndex)
if index then
if index > max then
max = index
nodes[nodeId] = string.format(' %s%s"%s (S%d)"%s',
nodeId, nodeShape, label, stageIndex, nodeEnd)
end
end
lines[index] ="Stage# " .. tostring(stageIndex).. " | " .. totalStrike .. " strikes | " .. totaldead .. " dead | " .. totalbai .. " BAI | " .. totalCas .. " CAS | Total:" .. totalMissions
else
logger:warn("Stage index is not a number: " .. stageIndex)
end
end
for i = 1, max do
if lines[i] then
logger:info(lines[i])
-- Add edges for sequential stages within the same lane
for laneId, stageIndices in pairs(stageIndicesByLane) do
for i = 1, #stageIndices - 1 do
local fromIdx = stageIndices[i]
local toIdx = stageIndices[i + 1]
local fromNodeId = laneId .. "_" .. fromIdx
local toNodeId = laneId .. "_" .. toIdx
-- Check if this is a chapter boundary (gap)
local isChapter = false
for _, lane in ipairs(lanes) do
local normalizedLaneId = lane:GetStageLaneIdentifier() or "default"
if normalizedLaneId == laneId then
isChapter = lane:IsChapterStart(toIdx)
break
end
end
local label = isChapter and "|chapter|" or ""
table.insert(edges, string.format(' %s -->%s %s', fromNodeId, label, toNodeId))
end
end
-- Get main lane reference
local mainStageLane = nil
for _, lane in ipairs(lanes) do
if lane:IsDefaultStageLane() then
mainStageLane = lane
break
end
end
-- Add dependencies from DEFAULT lane to SIDE lanes at chapter starts
-- Side lanes activate when main reaches a chapter start index for that side lane
if mainStageLane then
for _, lane in ipairs(lanes) do
if lane:IsDefaultStageLane() == false then
local sideId = lane:GetStageLaneIdentifier()
local sideIndices = stageIndicesByLane[sideId]
-- For each stage in the side lane
for _, stageIdx in ipairs(sideIndices) do
-- Check if this is a chapter start in the side lane
if lane:IsChapterStart(stageIdx) then
local fromNodeId = mainLaneId .. "_" .. stageIdx
local toNodeId = sideId .. "_" .. stageIdx
-- Side lane activates when main lane reaches this chapter start
table.insert(edges, string.format(' %s -->|unlock| %s', fromNodeId, toNodeId))
end
end
end
end
end
-- 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
if mainStageLane then
local mainIndices = stageIndicesByLane[mainLaneId]
-- For each chapter start in main lane (skip first one)
for i = 2, #mainIndices do
local nextIdx = mainIndices[i]
-- Check if nextIdx is a chapter start (there's a gap before it)
if mainStageLane:IsChapterStart(nextIdx) then
-- Before main can progress to nextIdx, all side lanes must be at >= nextIdx
for _, lane in ipairs(lanes) do
if lane:IsDefaultStageLane() == false then
local sideId = lane:GetStageLaneIdentifier()
local sideIndices = stageIndicesByLane[sideId]
-- Find the appropriate gate node for this side lane
-- Use first stage >= nextIdx if it exists, otherwise use the highest stage
local gateStageIdx = nil
for _, idx in ipairs(sideIndices) do
if idx >= nextIdx then
gateStageIdx = idx
break
end
end
-- If no stage >= nextIdx, use the highest stage in this lane
if gateStageIdx == nil and #sideIndices > 0 then
gateStageIdx = sideIndices[#sideIndices]
end
if gateStageIdx then
local fromNodeId = sideId .. "_" .. gateStageIdx
local toNodeId = mainLaneId .. "_" .. nextIdx
table.insert(edges, string.format(' %s -->|gate| %s', fromNodeId, toNodeId))
end
end
end
end
end
end
-- Build complete Mermaid diagram
local diagramLines = {"graph TD"}
for _, nodeStr in pairs(nodes) do
table.insert(diagramLines, nodeStr)
end
for _, edgeStr in pairs(edges) do
table.insert(diagramLines, edgeStr)
end
-- Print as single multi-line message
local diagram = "========== STAGE FLOW DIAGRAM ==========\n" .. table.concat(diagramLines, "\n") .. "\n========== END DIAGRAM =========="
self.logger:info(diagram)
end
---comment
---@param stageNumber number
---@param stageLaneIdentifier string? nil for default lan
---@return boolean | nil
GlobalStageManager.isStageComplete = function (stageNumber)
GlobalStageManager.isStageComplete = function (stageNumber, stageLaneIdentifier)
local stageIndex = tostring(stageNumber)
if StagesByIndex[stageIndex] == nil then return nil end
for _, stage in ipairs(StagesByIndex[stageIndex]) do
if stage:IsComplete() == false then
return false
end
if singletonInstance == nil then
Logger.new("StageManager", "INFO"):warn("GlobalStageManager.isStageComplete called before GlobalStageManager was initialized. Returning nil")
return nil
end
return true
if stageLaneIdentifier then
stageLaneIdentifier = string.lower(stageLaneIdentifier)
end
return singletonInstance:IsStageComplete(stageNumber, stageLaneIdentifier)
end
return GlobalStageManager
+178
View File
@@ -0,0 +1,178 @@
---@class StageLane
---@field private _isDefaultStageLane boolean
---@field private _stageLaneIdentifier string
---@field private _activeStageIndex number
---@field private _stagesInLaneByIndex table<number, Array<Stage>>
---@field private _chapterStarts table<number, boolean>?
---@field private _stageLaneState StageLaneState
---@field private _maxStageIndex number
local StageLane = {}
StageLane.__index = StageLane
---@alias StageLaneState
---| "InChapter"
---| "BetweenChapters"
---| "Completed"
StageLane.DefaultLaneKey = nil
function StageLane.New(laneIdentifier)
if not laneIdentifier then
laneIdentifier = StageLane.DefaultLaneKey
end
local self = setmetatable({}, { __index = StageLane }) --[[@as StageLane]]
self._isDefaultStageLane = laneIdentifier == StageLane.DefaultLaneKey
self._stageLaneIdentifier = laneIdentifier
self._activeStageIndex = nil
self._stagesInLaneByIndex = {}
self._chapterStarts = nil
self._stageLaneState = "BetweenChapters"
return self
end
---@param stage Stage
function StageLane:AddStage(stage)
local stageIndex = stage:GetStageIndex()
if not self._stagesInLaneByIndex[stageIndex] then
self._stagesInLaneByIndex[stageIndex] = {}
end
if not self._maxStageIndex or stageIndex > self._maxStageIndex then
self._maxStageIndex = stageIndex
end
table.insert(self._stagesInLaneByIndex[stageIndex], stage)
end
---@param stageNumber number
---@return boolean?
function StageLane:IsStageIndexComplete(stageNumber)
local stages = self._stagesInLaneByIndex[stageNumber]
if not stages then
return nil
end
for _, stage in ipairs(stages) do
if not stage:IsComplete() then
return false
end
end
return true
end
---@return boolean
function StageLane:IsCurrentStageIndexComplete()
return self:IsStageIndexComplete(self._activeStageIndex) == true
end
---@return string?
function StageLane:GetStageLaneIdentifier()
return self._stageLaneIdentifier
end
---@return boolean
function StageLane:IsDefaultStageLane()
return self._isDefaultStageLane
end
---@return number
function StageLane:GetActiveStageIndex()
return self._activeStageIndex
end
---@param stageNumber number
function StageLane:SetActiveStageIndex(stageNumber)
if self._stagesInLaneByIndex[stageNumber] == nil then
self._stageLaneState = "BetweenChapters" -- stage number is not in this lane, so we are between chapters
elseif stageNumber > self._maxStageIndex then
self._stageLaneState = "Completed" -- stage number is beyond the max stage index
else
self._stageLaneState = "InChapter"
end
self._activeStageIndex = stageNumber
end
---@return StageLaneState
function StageLane:GetStageLaneState()
return self._stageLaneState
end
--- Checks if the given stage number is a chapter start. <br>
--- Chapter starts are the first StageIndex after a gap in stage numbers. <br>
--- For example, if the stage numbers are 1, 2, 3, 5, 6, 7, then stage number 5 is a chapter start because there is a gap between 3 and 5. <br>
---@param stageNumber number
function StageLane:IsChapterStart(stageNumber)
if self._chapterStarts == nil then
self:FillChapterStarts()
end
return self._chapterStarts[tostring(stageNumber)] == true
end
--- fills chapter starts, chapter starts are where there's a gap in between stage numbers.
--- The first stage in a lane is always a chapter start.
---@private
function StageLane:FillChapterStarts()
local previousIndex = nil
local stageIndices = {}
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
table.insert(stageIndices, tonumber(stageIndex))
end
table.sort(stageIndices)
self._chapterStarts = self._chapterStarts or {}
for _, stageIndex in ipairs(stageIndices) do
if previousIndex == nil then
-- First stage is always a chapter start
self._chapterStarts[tostring(stageIndex)] = true
elseif stageIndex > previousIndex + 1 then
-- Gap before this stage means it's a chapter start
self._chapterStarts[tostring(stageIndex)] = true
else
-- Consecutive stage, not a chapter start
self._chapterStarts[tostring(stageIndex)] = false
end
previousIndex = stageIndex
end
end
---@return Array<number>
function StageLane:GetAllStageIndices()
local indices = {}
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
table.insert(indices, tonumber(stageIndex))
end
table.sort(indices)
return indices
end
---@param stageIndex number
---@return Array<Stage>?
function StageLane:GetStagesAtIndex(stageIndex)
return self._stagesInLaneByIndex[stageIndex]
end
---@return number?
function StageLane:GetNextChapterStart()
if self._chapterStarts == nil then
self:FillChapterStarts()
end
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
if tonumber(stageIndex) > self._activeStageIndex and self._chapterStarts[tostring(stageIndex)] == true then
return tonumber(stageIndex)
end
end
return nil
end
return StageLane
@@ -0,0 +1,47 @@
local StageLane = require("classes.stageClasses.StageLane")
---@class StageRepository
---@field private StageLanes table<string, StageLane>
local StageRepository = {}
StageRepository.__index = StageRepository
---@return StageRepository
function StageRepository.new()
local self = setmetatable({}, StageRepository)
self.StageLanes = {}
self.StageLanes[tostring(StageLane.DefaultLaneKey)] = StageLane.New(StageLane.DefaultLaneKey)
return self
end
---@param laneName string? `nil` for default lane
---@return StageLane?
function StageRepository:getStageLane(laneName)
if not laneName then
laneName = StageLane.DefaultLaneKey
end
return self.StageLanes[tostring(laneName)]
end
---@return Array<StageLane>
function StageRepository:getAllStageLanes()
local lanes = {}
for _, lane in pairs(self.StageLanes) do
table.insert(lanes, lane)
end
return lanes
end
---@param stage Stage
function StageRepository:AddStage(stage)
local stageLaneIdentifier = stage:GetStageLaneIdentifier() or StageLane.DefaultLaneKey
if self.StageLanes[tostring(stageLaneIdentifier)] == nil then
self.StageLanes[tostring(stageLaneIdentifier)] = StageLane.New(stageLaneIdentifier)
end
self.StageLanes[tostring(stageLaneIdentifier)]:AddStage(stage)
end
return StageRepository
@@ -19,6 +19,11 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
---| "BLUE"
---| "GRAY"
---@alias StageType
---| "PrimaryStage"
---| "ExtraStage"
---| "WaitingStage"
--- @class StageData
--- @field stageBriefing string?
--- @field missionsByCode table<string, Mission>
@@ -33,9 +38,11 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
--- @field supplyHubs Array<SupplyHub>
--- @class StageInitData
--- @field type StageType
--- @field stageZoneName string
--- @field stageNumber integer
--- @field stageDisplayName string
--- @field stageLaneIdentifier string?
--- @class StageCompleteListener
@@ -45,6 +52,8 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
--- @field zoneName string
--- @field stageName string?
--- @field stageNumber number
--- @field protected _stageLaneIdentifier string?
--- @field protected _stageType StageType
--- @field protected _currentStageState CurrentStageState
--- @field protected _missionCommandsHelper MissionCommandsHelper
--- @field protected _missionPriority MissionPriority
@@ -86,15 +95,22 @@ Stage.StageColors = {
---@param initData StageInitData
---@param missionPriority MissionPriority
---@param spawnManager SpawnManager
---@param stageType StageType
---@return Stage
function Stage:superNew(database, stageConfig, logger, initData, missionPriority, spawnManager)
function Stage:superNew(database, stageConfig, logger, initData, stageType, missionPriority, spawnManager)
logger:debug("[BaseStage] Initiating stage with name: " .. initData.stageZoneName)
self._currentStageState = Stage.CurrentStageState.INACTIVE
self.zoneName = initData.stageZoneName
self.stageNumber = initData.stageNumber
if initData.stageLaneIdentifier then
self._stageLaneIdentifier = string.lower(initData.stageLaneIdentifier)
else
self._stageLaneIdentifier = nil
end
self.stageName = initData.stageDisplayName
self._currentStageState = Stage.CurrentStageState.INACTIVE
self._stageType = stageType
self.OnPostStageComplete = nil
self.OnPostBlueActivated = nil
@@ -298,6 +314,21 @@ function Stage:IsActive()
return self._currentStageState == Stage.CurrentStageState.ACTIVE
end
---@return StageType
function Stage:GetStageType()
return self._stageType
end
---@return string?
function Stage:GetStageLaneIdentifier()
return self._stageLaneIdentifier
end
---@return number
function Stage:GetStageIndex()
return self.stageNumber
end
---comment
function Stage:CheckAndUpdateSelf()
self._logger:debug("Checking on Stage: " .. self.zoneName)
@@ -19,7 +19,7 @@ function ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
setmetatable(ExtraStage, Stage)
local self = setmetatable({}, { __index = ExtraStage }) --[[@as ExtraStage]]
self:superNew(database, stageConfig, logger, initData, "secondary", spawnManager)
self:superNew(database, stageConfig, logger, initData, "ExtraStage", "secondary", spawnManager)
self.OnPostBlueActivated = function (selfStage)
@@ -18,7 +18,7 @@ function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
setmetatable(PrimaryStage, Stage)
local self = setmetatable({}, { __index = PrimaryStage }) --[[@as PrimaryStage]]
self:superNew(database, stageConfig, logger, initData, "primary", spawnManager)
self:superNew(database, stageConfig, logger, initData, "PrimaryStage", "primary", spawnManager)
return self
end
@@ -4,28 +4,24 @@ local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
---@field private _waitTimeSeconds integer
---@field private _startTime number
local WaitingStage = {}
WaitingStage.__index = WaitingStage
---@class WaitingStageInitData : StageInitData
---@field waitingSeconds integer
local WaitingStageInitData = {}
---comment
---@param database Database
---@param stageConfig StageConfig
---@param logger any
---@param initData WaitingStageInitData
---@param initData StageInitData
---@param spawnManager SpawnManager
---@param waitingSeconds integer
---@return WaitingStage
function WaitingStage.New(database, stageConfig, logger, initData, spawnManager)
function WaitingStage.New(database, stageConfig, logger, initData, waitingSeconds, spawnManager)
setmetatable(WaitingStage, Stage)
local self = setmetatable({}, { __index = WaitingStage }) --[[@as WaitingStage]]
self:superNew(database, stageConfig, logger, initData, "none", spawnManager)
self:superNew(database, stageConfig, logger, initData, "WaitingStage", "none", spawnManager)
self._waitTimeSeconds = 5
if initData.waitingSeconds and initData.waitingSeconds > 5 then self._waitTimeSeconds = initData.waitingSeconds end
if waitingSeconds and waitingSeconds > 5 then self._waitTimeSeconds = waitingSeconds end
self._startTime = nil
self.CheckContinuousAsync = function (selfA, time)
+8
View File
@@ -128,6 +128,14 @@ do -- INIT UTIL
return str:find('^' .. findable) ~= nil
end
UTIL.endsWith = function(str, findable, ignoreCase)
if ignoreCase == true then
return string.lower(str):find(string.lower(findable) .. '$') ~= nil
end
return str:find(findable .. '$') ~= nil
end
---comment
---@param str string
---@param findable string
+2 -2
View File
@@ -54,7 +54,7 @@ local detectionLogger = Logger.new("DetectionManager", defaultLogLevel)
local detectionManager = DetectionManager.New(detectionLogger)
GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
local globalStageManager = GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
GlobalFleetManager.start(databaseManager)
local SetStageDelayed = function(number, time)
@@ -70,5 +70,5 @@ standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration)
local missionEditorWarningsLogger = Logger.new("MissionEditorWarnings", defaultLogLevel)
MissionEditorWarnings.WriteAll(missionEditorWarningsLogger)
GlobalStageManager:printFullOverview()
globalStageManager:PrintMermaidStage()