merged develop
This commit is contained in:
@@ -2,7 +2,6 @@ local Events = require("classes.spearhead_events")
|
||||
local Util = require("classes.util.Util")
|
||||
local DcsUtil = require("classes.util.DcsUtil")
|
||||
local Logger = require("classes.util.Logger")
|
||||
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
||||
local PersistenceConfig = require("classes.configuration.PersistenceConfig")
|
||||
local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
|
||||
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
|
||||
@@ -35,7 +34,6 @@ local singletonInstance = nil
|
||||
---@param spawnManager SpawnManager
|
||||
---@return GlobalStageManager
|
||||
function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
|
||||
|
||||
if singletonInstance ~= nil then
|
||||
return singletonInstance
|
||||
end
|
||||
@@ -44,7 +42,7 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
|
||||
logger:info("Using Stage Log Level: " .. logLevel)
|
||||
local self = setmetatable({}, GlobalStageManager)
|
||||
singletonInstance = self
|
||||
|
||||
|
||||
self.database = database
|
||||
self.stageConfig = stageConfig
|
||||
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
||||
@@ -52,14 +50,15 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
|
||||
|
||||
self.logger = logger
|
||||
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
|
||||
|
||||
Events.AddStageNumberChangedListener(self)
|
||||
|
||||
for _, stageName in pairs(database:getStagezoneNames()) do
|
||||
logger:debug("Found stage zone with name: " .. stageName)
|
||||
|
||||
|
||||
local parseResult = self:ParseStageName(stageName)
|
||||
if parseResult.isValid == false then
|
||||
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)
|
||||
self._stageRepository:AddStage(stage)
|
||||
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)
|
||||
self._stageRepository:AddStage(waitingStage)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
singletonInstance = self
|
||||
@@ -103,11 +102,11 @@ function GlobalStageManager:Start()
|
||||
|
||||
local stageLanes = self._stageRepository:getAllStageLanes()
|
||||
for _, stageLane in pairs(stageLanes) do
|
||||
|
||||
local stageLaneIdentifier = stageLane:GetStageLaneIdentifier()
|
||||
local persistedStage = Persistence.GetActiveStage(stageLaneIdentifier)
|
||||
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)
|
||||
Events.PublishStageNumberChanged(persistedStage, stageLaneIdentifier)
|
||||
else
|
||||
@@ -139,17 +138,16 @@ end
|
||||
---@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" }
|
||||
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"
|
||||
@@ -161,15 +159,16 @@ function GlobalStageManager:ParseStageName(stageName)
|
||||
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 }
|
||||
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 = {
|
||||
@@ -182,18 +181,19 @@ function GlobalStageManager:ParseStageName(stageName)
|
||||
}
|
||||
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 }
|
||||
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 }
|
||||
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
|
||||
@@ -209,9 +209,9 @@ function GlobalStageManager:ParseStageName(stageName)
|
||||
waitingStageSeconds = waitingSeconds
|
||||
}
|
||||
return result
|
||||
|
||||
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
|
||||
|
||||
---@param stage Stage
|
||||
@@ -223,7 +223,8 @@ function GlobalStageManager:OnStageComplete(stage)
|
||||
|
||||
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)
|
||||
self.logger:debug("Stage lane " ..
|
||||
(laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -234,7 +235,7 @@ function GlobalStageManager:OnStageComplete(stage)
|
||||
stageLane:SetActiveStageIndex(nextStageIndex)
|
||||
|
||||
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"
|
||||
local allLanes = self._stageRepository:getAllStageLanes()
|
||||
@@ -242,7 +243,9 @@ function GlobalStageManager:OnStageComplete(stage)
|
||||
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey and lane:GetStageLaneState() == "BetweenChapters" 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)
|
||||
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
|
||||
@@ -256,7 +259,6 @@ function GlobalStageManager:OnStageComplete(stage)
|
||||
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
|
||||
@@ -265,15 +267,19 @@ function GlobalStageManager:OnStageComplete(stage)
|
||||
-- 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)
|
||||
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
|
||||
-- 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)
|
||||
self.logger:debug("Lane " ..
|
||||
(lane:GetStageLaneIdentifier() or "default") ..
|
||||
" is not ready for next stage index " .. nextStageIndex)
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -281,13 +287,14 @@ function GlobalStageManager:OnStageComplete(stage)
|
||||
end
|
||||
|
||||
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)
|
||||
defaultStageLane:SetActiveStageIndex(nextStageIndex)
|
||||
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
|
||||
@@ -308,7 +315,6 @@ end
|
||||
|
||||
---@public
|
||||
function GlobalStageManager:OnStageNumberChanged(stageNumber, stageLaneIdentifier)
|
||||
|
||||
-- only react on "main" lane changes, ignore other lanes for now
|
||||
if stageLaneIdentifier ~= nil then return end
|
||||
|
||||
@@ -321,13 +327,13 @@ function GlobalStageManager:OnStageNumberChangeComplete(stageNumber, stageLaneId
|
||||
if stageLaneIdentifier ~= nil then return end
|
||||
|
||||
self.logger:debug("Stage number change complete to: " .. tostring(stageNumber))
|
||||
|
||||
---@type Array<Group>
|
||||
local groups = {}
|
||||
|
||||
for _, player in pairs(DcsUtil.getAllPlayerUnits()) do
|
||||
local group = player:getGroup()
|
||||
if group then
|
||||
groups[group:getID()] = group
|
||||
table.insert(groups, group)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -349,91 +355,98 @@ function GlobalStageManager:UpdateDrawings(stageNumber, stageLaneIdentifier)
|
||||
|
||||
if laneIdentifier == stageLaneIdentifier 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()
|
||||
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()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function GlobalStageManager:PrintMermaidStage()
|
||||
local lanes = self._stageRepository:getAllStageLanes()
|
||||
|
||||
|
||||
---@type table<string, string>
|
||||
local nodes = {}
|
||||
---@type table<string, string>
|
||||
local edges = {}
|
||||
---@type table<string, Array<number>>
|
||||
local stageIndicesByLane = {} -- Track all stage indices per lane
|
||||
local mainLaneId = "default"
|
||||
|
||||
|
||||
-- Color palette for lanes (26 vibrant colors optimized for dark mode)
|
||||
local laneColors = {
|
||||
"#FF6B6B", -- 1: Red
|
||||
"#4ECDC4", -- 2: Teal
|
||||
"#45B7D1", -- 3: Blue
|
||||
"#FFA502", -- 4: Orange
|
||||
"#95E1D3", -- 5: Mint
|
||||
"#F38181", -- 6: Coral
|
||||
"#AA96DA", -- 7: Purple
|
||||
"#FCBAD3", -- 8: Pink
|
||||
"#A8E6CF", -- 9: Light green
|
||||
"#FFD3B6", -- 10: Peach
|
||||
"#FFAAA5", -- 11: Light red
|
||||
"#FF8B94", -- 12: Rose
|
||||
"#FFEAA7", -- 13: Butter
|
||||
"#DFE6E9", -- 14: Gray
|
||||
"#00B894", -- 15: Emerald
|
||||
"#0984E3", -- 16: Cobalt
|
||||
"#6C5CE7", -- 17: Indigo
|
||||
"#A29BFE", -- 18: Lavender
|
||||
"#FD79A8", -- 19: Magenta
|
||||
"#FDCB6E", -- 20: Gold
|
||||
"#6C757D", -- 21: Slate
|
||||
"#20C997", -- 22: Seafoam
|
||||
"#E74C3C", -- 23: Scarlet
|
||||
"#3498DB", -- 24: Dodger blue
|
||||
"#9B59B6", -- 25: Amethyst
|
||||
"#1ABC9C", -- 26: Turquoise
|
||||
"#FF6B6B", -- 1: Red
|
||||
"#4ECDC4", -- 2: Teal
|
||||
"#45B7D1", -- 3: Blue
|
||||
"#FFA502", -- 4: Orange
|
||||
"#95E1D3", -- 5: Mint
|
||||
"#F38181", -- 6: Coral
|
||||
"#AA96DA", -- 7: Purple
|
||||
"#FCBAD3", -- 8: Pink
|
||||
"#A8E6CF", -- 9: Light green
|
||||
"#FFD3B6", -- 10: Peach
|
||||
"#FFAAA5", -- 11: Light red
|
||||
"#FF8B94", -- 12: Rose
|
||||
"#FFEAA7", -- 13: Butter
|
||||
"#DFE6E9", -- 14: Gray
|
||||
"#00B894", -- 15: Emerald
|
||||
"#0984E3", -- 16: Cobalt
|
||||
"#6C5CE7", -- 17: Indigo
|
||||
"#A29BFE", -- 18: Lavender
|
||||
"#FD79A8", -- 19: Magenta
|
||||
"#FDCB6E", -- 20: Gold
|
||||
"#6C757D", -- 21: Slate
|
||||
"#20C997", -- 22: Seafoam
|
||||
"#E74C3C", -- 23: Scarlet
|
||||
"#3498DB", -- 24: Dodger blue
|
||||
"#9B59B6", -- 25: Amethyst
|
||||
"#1ABC9C", -- 26: Turquoise
|
||||
}
|
||||
---@type table<string, string>
|
||||
local laneColorMap = {} -- Map lane ID to color
|
||||
---@type number
|
||||
local colorIndex = 1
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
-- Assign color to this lane
|
||||
laneColorMap[laneId] = laneColors[colorIndex]
|
||||
colorIndex = colorIndex + 1
|
||||
if colorIndex > #laneColors then
|
||||
colorIndex = 1
|
||||
end
|
||||
|
||||
|
||||
-- Create nodes for each stage index in this lane
|
||||
for _, stageIndex in ipairs(stageIndices) do
|
||||
local stages = lane:GetStagesAtIndex(stageIndex)
|
||||
|
||||
|
||||
if stages then
|
||||
for _, stage in ipairs(stages) do
|
||||
local nodeId = laneId .. "_" .. stageIndex
|
||||
local stageType = stage:GetStageType()
|
||||
local stageName = stage.stageName or stage.zoneName
|
||||
|
||||
|
||||
-- Generate bracket label: [1], [2] for default, [w1], [e2] for other lanes
|
||||
---@type string
|
||||
local bracketLabel
|
||||
if laneId == "default" then
|
||||
bracketLabel = "[" .. stageIndex .. "]"
|
||||
else
|
||||
bracketLabel = "[" .. laneId .. stageIndex .. "]"
|
||||
end
|
||||
|
||||
|
||||
-- Combined label: [bracket] Name
|
||||
local label = bracketLabel .. " " .. stageName
|
||||
|
||||
|
||||
-- Different node shapes for different stage types
|
||||
local nodeShape = "["
|
||||
local nodeEnd = "]"
|
||||
@@ -444,14 +457,14 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
nodeShape = "[["
|
||||
nodeEnd = "]]"
|
||||
end
|
||||
|
||||
nodes[nodeId] = string.format(' %s%s"%s"%s',
|
||||
|
||||
nodes[nodeId] = string.format(' %s%s"%s"%s',
|
||||
nodeId, nodeShape, label, nodeEnd)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Add edges for sequential stages within the same lane
|
||||
for laneId, stageIndices in pairs(stageIndicesByLane) do
|
||||
for i = 1, #stageIndices - 1 do
|
||||
@@ -459,7 +472,7 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
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
|
||||
@@ -469,12 +482,12 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
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
|
||||
@@ -483,15 +496,15 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Add dependencies from DEFAULT lane to SIDE lanes at chapter starts
|
||||
-- Side lanes activate when main completes the stage BEFORE the chapter start
|
||||
if mainStageLane then
|
||||
for _, lane in ipairs(lanes) do
|
||||
if lane:IsDefaultStageLane() == false then
|
||||
local sideId = lane:GetStageLaneIdentifier()
|
||||
local sideIndices = stageIndicesByLane[sideId]
|
||||
|
||||
local sideIndices = stageIndicesByLane[sideId] or {} --[[@as Array<number>]]
|
||||
|
||||
-- For each stage in the side lane
|
||||
for _, stageIdx in ipairs(sideIndices) do
|
||||
-- Check if this is a chapter start in the side lane
|
||||
@@ -506,40 +519,40 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
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]
|
||||
|
||||
local sideIndices = stageIndicesByLane[sideId] or {} --[[@as Array<number>]]
|
||||
|
||||
-- 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
|
||||
@@ -550,25 +563,26 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Build complete Mermaid diagram
|
||||
local diagramLines = {"graph TD"}
|
||||
|
||||
local diagramLines = { "graph TD" }
|
||||
|
||||
-- Add class definitions for each lane with colors and contrasting text
|
||||
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
|
||||
|
||||
|
||||
-- Add nodes
|
||||
for _, nodeStr in pairs(nodes) do
|
||||
table.insert(diagramLines, nodeStr)
|
||||
end
|
||||
|
||||
|
||||
-- Add edges
|
||||
for _, edgeStr in pairs(edges) do
|
||||
table.insert(diagramLines, edgeStr)
|
||||
end
|
||||
|
||||
|
||||
-- Apply classes to nodes
|
||||
for nodeId in pairs(nodes) do
|
||||
local laneId = nodeId:match("(.+)_[0-9]+$")
|
||||
@@ -576,9 +590,10 @@ function GlobalStageManager:PrintMermaidStage()
|
||||
table.insert(diagramLines, string.format(' class %s lane_%s', nodeId, laneId))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- 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)
|
||||
end
|
||||
|
||||
@@ -586,10 +601,10 @@ end
|
||||
---@param stageNumber number
|
||||
---@param stageLaneIdentifier string? nil for default lan
|
||||
---@return boolean | nil
|
||||
GlobalStageManager.isStageComplete = function (stageNumber, stageLaneIdentifier)
|
||||
|
||||
GlobalStageManager.isStageComplete = function(stageNumber, stageLaneIdentifier)
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ end
|
||||
function SpearheadGroup:SpawnCorpsesOnly()
|
||||
|
||||
if self._isSpawned == true then return end
|
||||
|
||||
|
||||
self._spawnManager:SpawnCorpsesOnly(self._groupName)
|
||||
self._isSpawned = true
|
||||
|
||||
@@ -57,7 +57,7 @@ function SpearheadGroup:Spawn(lateStart)
|
||||
uncontrolled = lateStart,
|
||||
}
|
||||
|
||||
local spawnedObject, isStatic = self._spawnManager:SpawnGroup(self._groupName, overrides, self._isPersistent)
|
||||
local _, isStatic = self._spawnManager:SpawnGroup(self._groupName, overrides, self._isPersistent)
|
||||
self._isStatic = isStatic
|
||||
self._isSpawned = true
|
||||
end
|
||||
@@ -91,13 +91,13 @@ function SpearheadGroup:GetCoalition()
|
||||
end
|
||||
|
||||
---comment
|
||||
---@return table result list of objects
|
||||
---@return Array<Unit|StaticObject> result list of objects
|
||||
function SpearheadGroup:GetObjects()
|
||||
|
||||
---@type Array<Unit|StaticObject>
|
||||
local result = {}
|
||||
if self._isStatic == true then
|
||||
local staticObject = StaticObject.getByName(self._groupName)
|
||||
if staticObject then
|
||||
if staticObject then
|
||||
table.insert(result, staticObject)
|
||||
end
|
||||
else
|
||||
@@ -105,7 +105,7 @@ function SpearheadGroup:GetObjects()
|
||||
if not group then return {} end
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
table.insert(result, unit)
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
@@ -123,7 +123,7 @@ function SpearheadGroup:GetAsUnits()
|
||||
if not group then return {} end
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
table.insert(result, unit)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -133,7 +133,7 @@ function SpearheadGroup:GetAllUnitPositions()
|
||||
local result = {}
|
||||
if self._isStatic == true then
|
||||
local staticObject = StaticObject.getByName(self._groupName)
|
||||
if staticObject then
|
||||
if staticObject then
|
||||
table.insert(result, staticObject:getPoint())
|
||||
end
|
||||
else
|
||||
@@ -141,7 +141,7 @@ function SpearheadGroup:GetAllUnitPositions()
|
||||
if not group then return {} end
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
table.insert(result, unit:getPoint())
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -50,16 +50,10 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
|
||||
---@type table<string, Vec3>
|
||||
local redUnitsPos = {}
|
||||
|
||||
|
||||
local buildable = false
|
||||
if self._buildableCrateKilos and self._buildableCrateKilos > 0 then
|
||||
buildable = true
|
||||
end
|
||||
|
||||
for _, groupName in pairs(blueSamData.groups) do
|
||||
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
|
||||
if spearheadGroup then
|
||||
|
||||
|
||||
if spearheadGroup:GetCoalition() == 2 or spearheadGroup:GetCoalition() == 0 then
|
||||
table.insert(self._blueGroups, spearheadGroup)
|
||||
end
|
||||
@@ -78,7 +72,7 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
|
||||
|
||||
--Cleanup units
|
||||
local cleanup_distance = 5
|
||||
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
|
||||
for _, blueUnitPos in pairs(blueUnitsPos) do
|
||||
for redUnitName, redUnitPos in pairs(redUnitsPos) do
|
||||
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
|
||||
if distance <= cleanup_distance then
|
||||
@@ -140,7 +134,9 @@ end
|
||||
|
||||
function BlueSam:SpawnGroups()
|
||||
for unitName, needsCleanup in pairs(self._cleanupUnits) do
|
||||
DcsUtil.DestroyUnit(unitName)
|
||||
if needsCleanup then
|
||||
DcsUtil.DestroyUnit(unitName)
|
||||
end
|
||||
end
|
||||
|
||||
for _, group in pairs(self._blueGroups) do
|
||||
|
||||
@@ -42,11 +42,11 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
|
||||
self._groups = {}
|
||||
self._padNames = {}
|
||||
self._supplyHubs = {}
|
||||
|
||||
|
||||
|
||||
if farpData then
|
||||
self._padNames = farpData.padNames
|
||||
|
||||
|
||||
for _, supplyHubName in pairs(farpData.supplyHubNames) do
|
||||
local supplyHub = SupplyHub.new(database, logger, supplyHubName)
|
||||
if supplyHub then
|
||||
@@ -55,7 +55,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
|
||||
end
|
||||
|
||||
|
||||
for _, groupName in pairs(farpData.groups) do
|
||||
for _, groupName in pairs(farpData.groups) do
|
||||
local group = SpearheadGroup.New(groupName, spawnManager, true)
|
||||
table.insert(self._groups, group)
|
||||
group:Destroy()
|
||||
|
||||
@@ -90,7 +90,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
|
||||
|
||||
local cleanup_distance = 5
|
||||
|
||||
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
|
||||
for _, blueUnitPos in pairs(blueUnitsPos) do
|
||||
for redUnitName, redUnitPos in pairs(redUnitsPos) do
|
||||
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
|
||||
if distance <= cleanup_distance then
|
||||
@@ -113,6 +113,7 @@ end
|
||||
function StageBase:SpawnRedUnits()
|
||||
---comment
|
||||
---@param groups Array<SpearheadGroup>
|
||||
---@return number|nil
|
||||
local spawnAsync = function(groups)
|
||||
for _, group in pairs(groups) do
|
||||
group:Spawn()
|
||||
@@ -142,6 +143,7 @@ end
|
||||
function StageBase:SpawnBlueUnits()
|
||||
---comment
|
||||
---@param groups Array<SpearheadGroup>
|
||||
---@return number|nil
|
||||
local spawnAsync = function(groups)
|
||||
for _, group in pairs(groups) do
|
||||
group:Spawn()
|
||||
@@ -172,7 +174,7 @@ function StageBase:ActivateBlueStage()
|
||||
else
|
||||
self:FinaliseBlueStage()
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
function StageBase:FinaliseBlueStage()
|
||||
|
||||
@@ -42,7 +42,7 @@ function SupplyHub.new(database, logger, zoneName)
|
||||
end
|
||||
|
||||
self._zone = DcsUtil.getZoneByName(zoneName)
|
||||
|
||||
|
||||
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate()
|
||||
self._inZone = {}
|
||||
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
||||
|
||||
@@ -37,6 +37,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
|
||||
|
||||
---@param params UnpackCrateParam
|
||||
---@param time number
|
||||
---@return number|nil
|
||||
local startUnpackingCrate = function(params, time)
|
||||
local unpacked = params.unpackedKilos + (params.kilosPerSecond * 2)
|
||||
local alreadySpawned = params.unpackedItems / params.groupsPerKilo
|
||||
@@ -50,7 +51,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
|
||||
if params.unpackedKilos >= params.kilos or spawned == false then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
return time + 0.5
|
||||
end
|
||||
|
||||
@@ -76,8 +77,6 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
|
||||
self._buildableMission = nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
if self._buildableMission == nil then
|
||||
self._buildableLogger:debug("No buildable mission for zone: " .. targetZone.name)
|
||||
end
|
||||
@@ -99,25 +98,14 @@ function BuildableZone:OnBuildingComplete() end
|
||||
---@field unpackedItems number
|
||||
---@field unpackedKilos number
|
||||
|
||||
---@param mission BuildableMission?
|
||||
---@param _ BuildableMission?
|
||||
---@param kilos number
|
||||
function BuildableZone:OnCrateDroppedOff(mission, kilos)
|
||||
function BuildableZone:OnCrateDroppedOff(_, kilos)
|
||||
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 time number
|
||||
---@return number|nil
|
||||
local startUnpackingCrate = function(params, time)
|
||||
local unpacked = params.unpackedKilos + (params.kilosPerSecond * 2)
|
||||
local alreadySpawned = params.unpackedItems / params.groupsPerKilo
|
||||
@@ -132,10 +120,21 @@ function BuildableZone:OnCrateDroppedOff(mission, kilos)
|
||||
params.self:FinaliseCrate(params.kilos)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
return time + 2
|
||||
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)
|
||||
end
|
||||
|
||||
@@ -190,7 +189,7 @@ function BuildableZone:SpawnAmount(amount)
|
||||
return nil
|
||||
end
|
||||
|
||||
for i = 1, amount do
|
||||
for _ = 1, amount do
|
||||
local spawned = spawnOne()
|
||||
if spawned ~= true then
|
||||
self._buildableLogger:debug("No more groups to spawn in zone: " .. self._targetZone.name)
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
---@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 _stagesInLaneByIndex table<string, Array<Stage>>
|
||||
---@field private _chapterStarts table<string, boolean>?
|
||||
---@field private _stageLaneState StageLaneState
|
||||
---@field private _maxStageIndex number
|
||||
local StageLane = {}
|
||||
@@ -16,6 +16,7 @@ StageLane.__index = StageLane
|
||||
---| "BetweenChapters"
|
||||
---| "Completed"
|
||||
|
||||
---@type string?
|
||||
StageLane.DefaultLaneKey = nil
|
||||
|
||||
function StageLane.New(laneIdentifier)
|
||||
@@ -37,21 +38,21 @@ end
|
||||
---@param stage Stage
|
||||
function StageLane:AddStage(stage)
|
||||
local stageIndex = stage:GetStageIndex()
|
||||
if not self._stagesInLaneByIndex[stageIndex] then
|
||||
self._stagesInLaneByIndex[stageIndex] = {}
|
||||
if not self._stagesInLaneByIndex[tostring(stageIndex)] then
|
||||
self._stagesInLaneByIndex[tostring(stageIndex)] = {}
|
||||
end
|
||||
|
||||
if not self._maxStageIndex or stageIndex > self._maxStageIndex then
|
||||
self._maxStageIndex = stageIndex
|
||||
end
|
||||
|
||||
table.insert(self._stagesInLaneByIndex[stageIndex], stage)
|
||||
table.insert(self._stagesInLaneByIndex[tostring(stageIndex)], stage)
|
||||
end
|
||||
|
||||
---@param stageNumber number
|
||||
---@return boolean?
|
||||
function StageLane:IsStageIndexComplete(stageNumber)
|
||||
local stages = self._stagesInLaneByIndex[stageNumber]
|
||||
local stages = self._stagesInLaneByIndex[tostring(stageNumber)]
|
||||
if not stages then
|
||||
return nil
|
||||
end
|
||||
@@ -88,7 +89,7 @@ end
|
||||
---@param stageNumber number
|
||||
function StageLane:SetActiveStageIndex(stageNumber)
|
||||
|
||||
if self._stagesInLaneByIndex[stageNumber] == nil then
|
||||
if self._stagesInLaneByIndex[tostring(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
|
||||
@@ -108,6 +109,7 @@ end
|
||||
--- 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
|
||||
---@return boolean
|
||||
function StageLane:IsChapterStart(stageNumber)
|
||||
|
||||
if self._chapterStarts == nil then
|
||||
@@ -123,13 +125,14 @@ end
|
||||
function StageLane:FillChapterStarts()
|
||||
|
||||
local previousIndex = nil
|
||||
---@type table<string, number>
|
||||
local stageIndices = {}
|
||||
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
|
||||
table.insert(stageIndices, tonumber(stageIndex))
|
||||
end
|
||||
table.sort(stageIndices)
|
||||
|
||||
self._chapterStarts = self._chapterStarts or {}
|
||||
if self._chapterStarts == nil then self._chapterStarts = {} end
|
||||
for _, stageIndex in ipairs(stageIndices) do
|
||||
if previousIndex == nil then
|
||||
-- First stage is always a chapter start
|
||||
@@ -158,7 +161,7 @@ end
|
||||
---@param stageIndex number
|
||||
---@return Array<Stage>?
|
||||
function StageLane:GetStagesAtIndex(stageIndex)
|
||||
return self._stagesInLaneByIndex[stageIndex]
|
||||
return self._stagesInLaneByIndex[tostring(stageIndex)]
|
||||
end
|
||||
|
||||
---@return number?
|
||||
|
||||
@@ -49,7 +49,6 @@ end
|
||||
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
|
||||
|
||||
@@ -105,7 +105,7 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
|
||||
end
|
||||
self.stageName = initData.stageDisplayName
|
||||
self._stageType = stageType
|
||||
|
||||
|
||||
self.OnPostStageComplete = nil
|
||||
self.OnPostBlueActivated = nil
|
||||
|
||||
@@ -160,25 +160,26 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
|
||||
self._logger:info("Initiating new Stage with name: " .. self.zoneName)
|
||||
|
||||
---comment
|
||||
---@param self Stage
|
||||
---@param selfA Stage
|
||||
---@param time number?
|
||||
self.CheckContinuousAsync = function (self, time)
|
||||
|
||||
self:CheckAndUpdateSelf()
|
||||
if self:IsComplete() == true then
|
||||
self:NotifyComplete()
|
||||
---@return number|nil
|
||||
self.CheckContinuousAsync = function (selfA, time)
|
||||
|
||||
selfA:CheckAndUpdateSelf()
|
||||
if selfA:IsComplete() == true then
|
||||
selfA:NotifyComplete()
|
||||
return nil
|
||||
end
|
||||
|
||||
return time + 20
|
||||
end
|
||||
|
||||
|
||||
|
||||
do -- load tables
|
||||
local missionZones = database:getMissionsForStage(self.zoneName)
|
||||
self._logger:debug("Found " .. Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName)
|
||||
for _, missionZone in pairs(missionZones) do
|
||||
|
||||
|
||||
local mission = ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager)
|
||||
if mission then
|
||||
self._db.missionsByCode[mission.code] = mission
|
||||
@@ -233,7 +234,7 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
|
||||
table.insert(self._db.missions, mission)
|
||||
end
|
||||
end
|
||||
else
|
||||
else
|
||||
self._logger:info("Using persisted random mission with name: " .. missionName .. " and zone: " .. missionZonePicked)
|
||||
for _, mission in pairs(missions) do
|
||||
if string.lower(mission.zoneName) == string.lower(missionZonePicked) then
|
||||
@@ -284,14 +285,14 @@ end
|
||||
function Stage:IsComplete()
|
||||
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()
|
||||
if state == "ACTIVE" or state == "NEW" or state =="WAITING" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
for i, mission in pairs(self._db.missions) do
|
||||
for _, mission in pairs(self._db.missions) do
|
||||
local state = mission:getState()
|
||||
if state == "ACTIVE" or state == "NEW" then
|
||||
return false
|
||||
@@ -325,7 +326,9 @@ function Stage:GetStageName()
|
||||
return self.stageName
|
||||
end
|
||||
|
||||
---@return Array<Mission>
|
||||
function Stage:GetMissions()
|
||||
---@type Array<Mission>
|
||||
local missions = {}
|
||||
for _, mission in pairs(self._db.missions) do
|
||||
table.insert(missions, mission)
|
||||
@@ -370,9 +373,9 @@ function Stage:CheckAndUpdateSelf()
|
||||
local availableMissionsCount = Util.tableLength(getAvailableMissions())
|
||||
local activeCount = getActiveMissionsCount()
|
||||
if activeCount < max and availableMissionsCount > 0 then
|
||||
for i = activeCount+1, max do
|
||||
for _ = activeCount+1, max do
|
||||
if availableMissionsCount == 0 then
|
||||
i = max+1 --exits this loop
|
||||
break
|
||||
else
|
||||
local mission = Util.randomFromList(getAvailableMissions()) --[[@as Mission]]
|
||||
if mission then
|
||||
@@ -426,7 +429,7 @@ function Stage:PreActivate()
|
||||
end
|
||||
|
||||
self._currentStageState = StageState.PreActivated
|
||||
for key, mission in pairs(self._db.sams) do
|
||||
for _, mission in pairs(self._db.sams) do
|
||||
if mission then
|
||||
mission:SpawnInactive()
|
||||
end
|
||||
@@ -479,7 +482,7 @@ function Stage:MarkStage()
|
||||
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.INVISIBLE)
|
||||
drawing.style = "no line"
|
||||
end
|
||||
|
||||
|
||||
return drawing
|
||||
end)
|
||||
self._customDrawing:Draw()
|
||||
@@ -591,12 +594,12 @@ function Stage:OnStageNumberChanged(number, stageLaneIdentifier)
|
||||
if needsBlueActivation() == true then
|
||||
self:ActivateBlueStage()
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
---@param self Stage
|
||||
---@param mission Mission
|
||||
Stage.OnMissionComplete = function(self, mission)
|
||||
---@param _ Mission
|
||||
Stage.OnMissionComplete = function(self, _)
|
||||
self:CheckAndUpdateSelf()
|
||||
end
|
||||
|
||||
@@ -651,7 +654,7 @@ function Stage:GetStageStats()
|
||||
end
|
||||
end
|
||||
|
||||
for _, mission in pairs(self._db.sams) do
|
||||
for _, _ in pairs(self._db.sams) do
|
||||
dead = dead + 1
|
||||
end
|
||||
|
||||
@@ -679,13 +682,14 @@ function Stage:ActivateBlueStage()
|
||||
miscGroup:Spawn()
|
||||
end
|
||||
|
||||
---@param self Stage
|
||||
local ActivateBlueAsync = function(self)
|
||||
---@param selfA Stage
|
||||
---@return number|nil
|
||||
local ActivateBlueAsync = function(selfA)
|
||||
pcall(function()
|
||||
self:MarkStage()
|
||||
selfA:MarkStage()
|
||||
end)
|
||||
|
||||
self:ActivateBlueGroups()
|
||||
selfA:ActivateBlueGroups()
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
|
||||
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
||||
local StageState = require("classes.stageClasses.Stages.StageState")
|
||||
local StageState = require("classes.stageClasses.Stages.BaseStage.StageState")
|
||||
|
||||
---@class ExtraStage : Stage
|
||||
local ExtraStage = {}
|
||||
@@ -23,10 +23,9 @@ function ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
|
||||
self:superNew(database, stageConfig, logger, initData, "ExtraStage", "secondary", spawnManager)
|
||||
|
||||
self.OnPostBlueActivated = function (selfStage)
|
||||
|
||||
selfStage:MarkStage()
|
||||
end
|
||||
|
||||
|
||||
self.OnPostStageComplete = function (selfStage)
|
||||
selfStage:ActivateBlueStage()
|
||||
end
|
||||
@@ -48,7 +47,6 @@ function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
|
||||
return
|
||||
end
|
||||
|
||||
local previousActive = self._activeStage
|
||||
self._activeStage = number
|
||||
|
||||
if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then
|
||||
@@ -62,7 +60,7 @@ function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
|
||||
self:ActivateStage()
|
||||
end
|
||||
|
||||
if self._currentStageState == StageState.BLUE then
|
||||
if self._currentStageState == StageState.Blue then
|
||||
self:ActivateBlueStage()
|
||||
end
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ function WaitingStage.New(database, stageConfig, logger, initData, waitingSecond
|
||||
self._startTime = nil
|
||||
|
||||
self.CheckContinuousAsync = function (selfA, time)
|
||||
|
||||
|
||||
if selfA:IsComplete() == true then
|
||||
selfA:NotifyComplete()
|
||||
return nil
|
||||
@@ -47,7 +47,7 @@ function WaitingStage:ActivateStage()
|
||||
timer.scheduleFunction(self.CheckContinuousAsync, self, self._startTime + self._waitTimeSeconds)
|
||||
end
|
||||
|
||||
function WaitingStage:IsComplete()
|
||||
function WaitingStage:IsComplete()
|
||||
if timer.getTime() > (self._startTime + self._waitTimeSeconds) then return true end
|
||||
return false
|
||||
end
|
||||
@@ -56,12 +56,12 @@ function WaitingStage:OnStageNumberChanged()
|
||||
self._logger:debug("Waiting Stage OnStageNumberChanged override")
|
||||
end
|
||||
|
||||
function WaitingStage:MarkStage(stageColor)
|
||||
function WaitingStage:MarkStage(_)
|
||||
self._logger:debug("Waiting Stage MarkStage override")
|
||||
end
|
||||
|
||||
function WaitingStage:GetExpectedTime()
|
||||
return self._startTime + self._waitTimeSeconds
|
||||
return self._startTime + self._waitTimeSeconds
|
||||
end
|
||||
|
||||
return WaitingStage
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
|
||||
local Util = require("classes.util.Util")
|
||||
local Logger = require("classes.util.Logger")
|
||||
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
||||
local drawingLogger = Logger.new("CustomDrawing")
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local Util = require("classes.util.Util")
|
||||
local DcsUtil = require("classes.util.DcsUtil")
|
||||
local Logger = require("classes.util.Logger")
|
||||
local GlobalConfig = require("classes.configuration.GlobalConfig")
|
||||
@@ -55,7 +54,7 @@ local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineS
|
||||
if lineThickness == nil or lineThickness <= 0 then
|
||||
lineStyle = 0
|
||||
end
|
||||
|
||||
---@type string
|
||||
local functionString = "trigger.action.markupToAll(" .. shapeID .. ", -1, " .. drawID .. ","
|
||||
|
||||
for _, point in pairs(points) do
|
||||
@@ -78,7 +77,7 @@ local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineS
|
||||
end
|
||||
trigger.action.setMarkupColor(drawID, lineColor)
|
||||
trigger.action.setMarkupTypeLine(drawID, lineStyle)
|
||||
|
||||
|
||||
end
|
||||
|
||||
---@private
|
||||
@@ -126,14 +125,14 @@ function DrawingHelper.DrawPolygon(object)
|
||||
local fillColor = DrawingHelper.ColorToColorTable(free.fillColorString)
|
||||
local color = DrawingHelper.ColorToColorTable(free.colorString)
|
||||
local lineStyle = DrawingHelper.ToLineStyleInteger(free.style)
|
||||
|
||||
---@type Array<number>
|
||||
local keys = {}
|
||||
for k, _ in pairs(free.points) do
|
||||
table.insert(keys, k)
|
||||
end
|
||||
|
||||
table.sort(keys, function(a, b) return a < b end)
|
||||
|
||||
---@type Array<Vec3>
|
||||
local points = {}
|
||||
for _, k in ipairs(keys) do
|
||||
local point = free.points[k]
|
||||
@@ -201,7 +200,7 @@ function DrawingHelper.DrawLine(object)
|
||||
|
||||
---@type Array<Vec3>
|
||||
local points = {}
|
||||
local ids = {}
|
||||
local ids = {}
|
||||
|
||||
for _, point in ipairs(object.points) do
|
||||
table.insert(points, { x = object.mapX + point.x, y = 0, z = object.mapY + point.y } )
|
||||
@@ -265,7 +264,7 @@ function DrawingHelper.ColorTableToColorString(rgba)
|
||||
logger:warn("ColorTableToColorString called with invalid rgba table, returning default color string '0x00000000'")
|
||||
return "0x00000000"
|
||||
end
|
||||
|
||||
|
||||
local r = string.format("%02X", math.floor(rgba[1] * 255))
|
||||
local g = string.format("%02X", math.floor(rgba[2] * 255))
|
||||
local b = string.format("%02X", math.floor(rgba[3] * 255))
|
||||
@@ -275,6 +274,7 @@ function DrawingHelper.ColorTableToColorString(rgba)
|
||||
end
|
||||
|
||||
---@param lineStyle string
|
||||
---@return integer
|
||||
function DrawingHelper.ToLineStyleInteger(lineStyle)
|
||||
lineStyle = lineStyle:lower()
|
||||
if lineStyle == "no line" then
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
local Logger = require("classes.util.Logger")
|
||||
local Util = require("classes.util.Util")
|
||||
local DcsUtil = require("classes.util.DcsUtil")
|
||||
|
||||
---@class BattleManager
|
||||
---@field private _name string
|
||||
@@ -13,8 +12,6 @@ local DcsUtil = require("classes.util.DcsUtil")
|
||||
local BattleManager = {}
|
||||
BattleManager.__index = BattleManager
|
||||
|
||||
local debugDrawing = false
|
||||
|
||||
---@param redGroups Array<SpearheadGroup>
|
||||
---@param blueGroups Array<SpearheadGroup>
|
||||
---@param name string
|
||||
@@ -30,8 +27,8 @@ function BattleManager.New(redGroups, blueGroups, name, logLevel)
|
||||
self._redGroups = redGroups
|
||||
self._blueGroups = blueGroups
|
||||
|
||||
self._logger:debug("BattleManager created with name: " .. self._name
|
||||
.. ", red groups: " .. #self._redGroups
|
||||
self._logger:debug("BattleManager created with name: " .. self._name
|
||||
.. ", red groups: " .. #self._redGroups
|
||||
.. ", blue groups: " .. #self._blueGroups)
|
||||
|
||||
return self
|
||||
@@ -39,6 +36,7 @@ end
|
||||
|
||||
---@param self BattleManager
|
||||
---@param time number
|
||||
---@return number?
|
||||
local function CheckTask(self, time)
|
||||
local interval = self:Update()
|
||||
if not interval then return end
|
||||
@@ -90,8 +88,6 @@ function BattleManager:Update()
|
||||
|
||||
self._logger:debug("BattleManager Update called for " .. self._name)
|
||||
|
||||
local shootChance = 1 -- Adjust this value to control the shooting probability (0.0 to 1.0)
|
||||
|
||||
self:LetUnitsShoot(self._redGroups, self._blueGroups)
|
||||
self:LetUnitsShoot(self._blueGroups, self._redGroups)
|
||||
|
||||
@@ -136,7 +132,7 @@ function BattleManager:LetUnitsShoot(groups, targetGroups)
|
||||
expendQtyEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
local controller = unit:getController()
|
||||
if controller then
|
||||
controller:setTask(shootTask)
|
||||
@@ -146,14 +142,14 @@ function BattleManager:LetUnitsShoot(groups, targetGroups)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param unit Unit
|
||||
---@return number
|
||||
---@return number
|
||||
function BattleManager:getBestAmmo(unit)
|
||||
|
||||
local ammo = unit:getAmmo()
|
||||
local ammo = unit:getAmmo() --[[@as table<number, table>]]
|
||||
|
||||
if not ammo then return 3221225470, 1 end -- Default ammo if no ammo is found
|
||||
|
||||
@@ -167,9 +163,9 @@ function BattleManager:getBestAmmo(unit)
|
||||
end
|
||||
end
|
||||
|
||||
local entry = Util.randomFromList(shells)
|
||||
local entry = Util.randomFromList(shells) --[[@as table]]
|
||||
if entry and entry.desc and entry.desc.warhead then
|
||||
local caliber = entry.desc.warhead.caliber
|
||||
local caliber = entry.desc.warhead.caliber --[[@as number]]
|
||||
if caliber > 50 then
|
||||
return 258503344128, 1
|
||||
else
|
||||
@@ -196,7 +192,7 @@ function BattleManager:IsUnitApplicable(unit)
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
|
||||
end
|
||||
|
||||
---@private
|
||||
@@ -214,6 +210,7 @@ function BattleManager:ToShootingHulls(groups)
|
||||
end
|
||||
|
||||
local hulls = Util.getSeparatedConvexHulls(points, 50)
|
||||
---@type Array<Array<Vec2>>
|
||||
local enlargedHulls = {}
|
||||
for _, hull in pairs(hulls) do
|
||||
local enlarged = Util.enlargeConvexHull(hull, 25)
|
||||
@@ -236,7 +233,7 @@ end
|
||||
---@param groupHulls Array<Array<Vec2>>
|
||||
---@return Vec2?
|
||||
function BattleManager:GetRandomPoint(origin, groupHulls)
|
||||
|
||||
|
||||
local hull = Util.randomFromList(groupHulls) --[[@as Array<Vec2>]]
|
||||
if not hull then return nil end
|
||||
local shootPoints = Util.GetTangentHullPointsFromOrigin(hull, origin)
|
||||
|
||||
@@ -7,7 +7,7 @@ local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHel
|
||||
local StageConfig = require("classes.configuration.StageConfig")
|
||||
|
||||
|
||||
---@class MissionCommandsHelper
|
||||
---@class MissionCommandsHelper : OnPlayerEnterUnitListener
|
||||
---@field missionsByCode table<string, Mission> @table of missions by their code
|
||||
---@field enabledByCode table<string, boolean> @table of enabled missions by their code
|
||||
---@field updateNeeded boolean @flag to indicate if an update is needed
|
||||
@@ -31,8 +31,6 @@ local function sortMissions(list, groupPos)
|
||||
end)
|
||||
end
|
||||
|
||||
local id = 0
|
||||
|
||||
local instance = nil
|
||||
|
||||
---@return MissionCommandsHelper
|
||||
@@ -57,16 +55,16 @@ function MissionCommandsHelper.getOrCreate()
|
||||
|
||||
instance._supplyUnitsTracker:AddOnSupplyUnitEventListener(
|
||||
{
|
||||
enteredSupplyHub = function(self, unit)
|
||||
enteredSupplyHub = function(_, unit)
|
||||
if unit == nil then return end
|
||||
instance.updateNeeded = true
|
||||
instance:updateCommandsForGroup(unit:getGroup():getID())
|
||||
end,
|
||||
exitedSupplyHub = function(self, unit)
|
||||
exitedSupplyHub = function(_, unit)
|
||||
instance.updateNeeded = true
|
||||
instance:updateCommandsForGroup(unit:getGroup():getID())
|
||||
end,
|
||||
supplyUnitSpawned = function(self, unit)
|
||||
supplyUnitSpawned = function(_, unit)
|
||||
instance.updateNeeded = true
|
||||
instance:updateCommandsForGroup(unit:getGroup():getID())
|
||||
end
|
||||
@@ -371,7 +369,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
|
||||
do --- secondary missions
|
||||
local count = 0
|
||||
local path = { [1] = folderNames.secondary }
|
||||
|
||||
---@type Array<Mission>
|
||||
local secondaryMissions = {}
|
||||
for code, enabled in pairs(self.enabledByCode) do
|
||||
if enabled == true then
|
||||
@@ -509,7 +507,6 @@ function MissionCommandsHelper:AddCargoCommands(groupID)
|
||||
local unloadCargoCommand = function(params)
|
||||
local unitID = params.unitID
|
||||
local crateType = params.crateType
|
||||
local supplyUnitsTracker = params.supplyUnitsTracker
|
||||
params.supplyUnitsTracker:UnloadRequested(unitID, crateType, params.commandHelper)
|
||||
end
|
||||
|
||||
@@ -518,7 +515,7 @@ function MissionCommandsHelper:AddCargoCommands(groupID)
|
||||
for cargoType, amount in pairs(cargo) do
|
||||
local cargoConfig = SupplyConfigHelper.getSupplyConfig(cargoType)
|
||||
if cargoConfig then
|
||||
for i = 1, amount do
|
||||
for _ = 1, amount do
|
||||
local path = { [1] = folderNames.cargo }
|
||||
---@type UnloadCargoCommandParams
|
||||
local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self
|
||||
|
||||
@@ -83,7 +83,8 @@ function SupplyConfigHelper.fromObjectName(name)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param type CrateType
|
||||
---@param type CrateType
|
||||
---@return SupplyConfig?
|
||||
function SupplyConfigHelper.getSupplyConfig(type)
|
||||
return SupplyConfig[type]
|
||||
end
|
||||
|
||||
@@ -29,7 +29,7 @@ local SupplyLoadConfig = {
|
||||
{ centerAngle = 270, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 },
|
||||
{ centerAngle = 90, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 },
|
||||
}
|
||||
},
|
||||
},
|
||||
["UH-1H"] = {
|
||||
maxInternalLoad = 2000,
|
||||
dropZones = {
|
||||
|
||||
@@ -10,7 +10,7 @@ local SupplyLoadConfig = require("classes.stageClasses.helpers.SupplyLoadConfig"
|
||||
---@field enteredSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil
|
||||
---@field exitedSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil
|
||||
|
||||
---@class SupplyUnitsTracker
|
||||
---@class SupplyUnitsTracker : OnPlayerEnterUnitListener
|
||||
---@field private _supplyUnitsByName table<string, Unit>
|
||||
---@field private _cargoInUnits table<string, table<CrateType, number>>
|
||||
---@field private _logger Logger
|
||||
@@ -43,6 +43,8 @@ function SupplyUnitsTracker.getOrCreate()
|
||||
SpearheadEvents.AddOnPlayerEnterUnitListener(singleton)
|
||||
|
||||
---@param selfA SupplyUnitsTracker
|
||||
---@param time number
|
||||
---@return number?
|
||||
local function updateTask(selfA, time)
|
||||
|
||||
selfA:Update()
|
||||
@@ -134,6 +136,7 @@ end
|
||||
|
||||
---@private
|
||||
---@param unit Unit
|
||||
---@return boolean
|
||||
function SupplyUnitsTracker:IsSupplyUnit(unit)
|
||||
if unit == nil then return false end
|
||||
|
||||
@@ -158,15 +161,17 @@ function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType)
|
||||
local unit = DcsUtil.GetPlayerUnitByID(unitID)
|
||||
if unit == nil then return end
|
||||
|
||||
if self._cargoInUnits[unitID] == nil then
|
||||
self._cargoInUnits[unitID] = {}
|
||||
local unitIdStr = tostring(unitID)
|
||||
|
||||
if self._cargoInUnits[unitIdStr] == nil then
|
||||
self._cargoInUnits[unitIdStr] = {}
|
||||
end
|
||||
|
||||
if self._cargoInUnits[unitID][crateType] == nil then
|
||||
self._cargoInUnits[unitID][crateType] = 0
|
||||
if self._cargoInUnits[unitIdStr][crateType] == nil then
|
||||
self._cargoInUnits[unitIdStr][crateType] = 0
|
||||
end
|
||||
|
||||
self._cargoInUnits[unitID][crateType] = self._cargoInUnits[unitID][crateType] + 1
|
||||
self._cargoInUnits[unitIdStr][crateType] = self._cargoInUnits[unitIdStr][crateType] + 1
|
||||
|
||||
end
|
||||
|
||||
@@ -185,7 +190,7 @@ function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType)
|
||||
self._cargoInUnits[unitIDStr][crateType] = self._cargoInUnits[unitIDStr][crateType] - 1
|
||||
|
||||
local hasCargo = false
|
||||
for type, count in pairs(self._cargoInUnits[unitIDStr]) do
|
||||
for _, count in pairs(self._cargoInUnits[unitIDStr]) do
|
||||
if count > 0 then
|
||||
hasCargo = true
|
||||
break
|
||||
@@ -216,13 +221,11 @@ end
|
||||
|
||||
function SupplyUnitsTracker:CheckUnitsInZones()
|
||||
|
||||
for name, unit in pairs(self._supplyUnitsByName) do
|
||||
for _, unit in pairs(self._supplyUnitsByName) do
|
||||
if unit ~= nil and unit:isExist() == true then
|
||||
self._logger:debug("Checking unit: " .. unit:getName())
|
||||
local pos = unit:getPoint()
|
||||
|
||||
local group = unit:getGroup()
|
||||
|
||||
for hub, enabled in pairs(self._registeredHubs) do
|
||||
if enabled == true then
|
||||
local zone = hub:GetZone()
|
||||
@@ -238,7 +241,7 @@ function SupplyUnitsTracker:CheckUnitsInZones()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
if self._unitInSupplyHub[tostring(unit:getID())] == true then
|
||||
self._unitInSupplyHub[tostring(unit:getID())] = false
|
||||
@@ -255,7 +258,7 @@ function SupplyUnitsTracker:CheckUnitsInZones()
|
||||
end
|
||||
end
|
||||
|
||||
self._unitPositions[unit:getID()] = pos
|
||||
self._unitPositions[tostring(unit:getID())] = pos
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -292,11 +295,11 @@ local cargoCount = 0
|
||||
---@param crateType CrateType
|
||||
---@param missionCommandsHelper MissionCommandsHelper
|
||||
function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHelper)
|
||||
|
||||
|
||||
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
|
||||
|
||||
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)
|
||||
return
|
||||
end
|
||||
@@ -305,7 +308,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe
|
||||
self._logger:warn("Unload requested for unit with no group: " .. unit:getName())
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local cargoConfig = SupplyConfigHelper.getSupplyConfig(crateType)
|
||||
|
||||
if cargoConfig == nil then
|
||||
@@ -325,6 +328,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe
|
||||
self:UpdateWeightForUnit(unit)
|
||||
|
||||
cargoCount = cargoCount + 1
|
||||
---@type {name: string, type: string, x: number, y: number}
|
||||
local cargoSpawnObject = {
|
||||
name = crateType .. "_" .. cargoCount,
|
||||
type = cargoConfig.staticType,
|
||||
@@ -362,11 +366,11 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType, missionC
|
||||
end
|
||||
|
||||
local unit = group:getUnit(1)
|
||||
|
||||
|
||||
if unit == nil then return end
|
||||
if unit:isExist() == false then return end
|
||||
|
||||
|
||||
|
||||
if unit:inAir() == true then
|
||||
trigger.action.outTextForUnit(unit:getID(), "Land first before crates can be loaded", 10)
|
||||
return
|
||||
@@ -383,7 +387,7 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType, missionC
|
||||
|
||||
---@param params LoadCargoParams
|
||||
local LoadCrateTask = function(params)
|
||||
|
||||
|
||||
local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType, params.commandHelper)
|
||||
if loaded ~= false then
|
||||
trigger.action.outTextForUnit(unit:getID(), "Loaded crate :" .. params.crateType, 10)
|
||||
@@ -410,7 +414,7 @@ end
|
||||
---@param commandHelper MissionCommandsHelper
|
||||
---@return boolean
|
||||
function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper)
|
||||
|
||||
|
||||
local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType)
|
||||
if crateConfigA == nil then
|
||||
trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5)
|
||||
@@ -444,7 +448,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper)
|
||||
if group == nil then return false end
|
||||
local groupID = group:getID()
|
||||
commandHelper:updateCommandsForGroup(groupID)
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -462,7 +466,7 @@ function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
@@ -484,11 +488,11 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
|
||||
if desc == nil or desc.box == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local objPos = foundObject:getPoint()
|
||||
local box = desc.box
|
||||
local box = desc.box --[[@type table]]
|
||||
local heading = 0
|
||||
|
||||
|
||||
-- Try to get object heading from position vector's forward direction
|
||||
-- This works for units and other objects that support getPosition
|
||||
pcall(function()
|
||||
@@ -497,13 +501,13 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
|
||||
heading = math.atan2(objPosition.x.z, objPosition.x.x)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- For rotated objects, we need to rotate the bounding box
|
||||
local minX = box.min.x
|
||||
local maxX = box.max.x
|
||||
local minZ = box.min.z
|
||||
local maxZ = box.max.z
|
||||
|
||||
local minX = box.min.x --[[@as number]]
|
||||
local maxX = box.max.x --[[@as number]]
|
||||
local minZ = box.min.z --[[@as number]]
|
||||
local maxZ = box.max.z --[[@as number]]
|
||||
|
||||
-- If object has significant rotation, apply rotation to bbox corners
|
||||
if math.abs(heading) > 0.1 then
|
||||
-- Get all 4 corners of bbox in local space
|
||||
@@ -513,21 +517,21 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
|
||||
{maxX, minZ},
|
||||
{maxX, maxZ}
|
||||
}
|
||||
|
||||
|
||||
-- Rotate corners and find new min/max
|
||||
minX, maxX = math.huge, -math.huge
|
||||
minZ, maxZ = math.huge, -math.huge
|
||||
|
||||
|
||||
for _, corner in ipairs(corners) do
|
||||
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 rotX = corner[1] * math.cos(heading) - corner[2] * math.sin(heading) --[[@as number]]
|
||||
local rotZ = corner[1] * math.sin(heading) + corner[2] * math.cos(heading) --[[@as number]]
|
||||
minX = math.min(minX, rotX)
|
||||
maxX = math.max(maxX, rotX)
|
||||
minZ = math.min(minZ, rotZ)
|
||||
maxZ = math.max(maxZ, rotZ)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Convert relative bbox to world space by adding object position
|
||||
---@type SupplyUnitsBoundingBox
|
||||
return {
|
||||
@@ -551,7 +555,7 @@ end
|
||||
---@param safetyMargin number Safety margin around objects
|
||||
---@return boolean True if collision detected
|
||||
function SupplyUnitsTracker:CheckBBoxCollision(crateBBox, objBBox, safetyMargin)
|
||||
|
||||
|
||||
-- Apply safety margin to object bbox
|
||||
local objMin = {
|
||||
x = objBBox.min.x - safetyMargin,
|
||||
@@ -563,7 +567,7 @@ function SupplyUnitsTracker:CheckBBoxCollision(crateBBox, objBBox, safetyMargin)
|
||||
y = objBBox.max.y + safetyMargin,
|
||||
z = objBBox.max.z + safetyMargin
|
||||
}
|
||||
|
||||
|
||||
-- AABB collision detection
|
||||
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
|
||||
@@ -577,7 +581,7 @@ end
|
||||
function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
||||
|
||||
local unitPos = unit:getPosition()
|
||||
|
||||
|
||||
-- Get unit's heading from the forward vector (x component)
|
||||
-- Heading is calculated as: atan2(forward.z, forward.x)
|
||||
local unitHeading = math.atan2(unitPos.x.z, unitPos.x.x)
|
||||
@@ -588,14 +592,14 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
||||
self._logger:error("Could not get bbox for crate type: " .. crateTypeName)
|
||||
return nil
|
||||
end
|
||||
|
||||
local crateRelativeBBox = crateDesc.box
|
||||
|
||||
local crateRelativeBBox = crateDesc.box --[[@as table]]
|
||||
|
||||
-- Get drop zone config for this unit
|
||||
local dropZones = {
|
||||
{ centerAngle = 180, angleWidth = 60, minRadius = 15, maxRadius = 50, spacing = 5 }
|
||||
}
|
||||
|
||||
|
||||
if SupplyLoadConfig[unit:getTypeName()] ~= nil then
|
||||
dropZones = SupplyLoadConfig[unit:getTypeName()].dropZones
|
||||
end
|
||||
@@ -612,9 +616,12 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
||||
radius = 100 -- Search a large area
|
||||
}
|
||||
}
|
||||
|
||||
---@type Array<{ pos: { x: number, y: number, z: number }, bbox: { min: { x: number, y: number, z: number }, max: { x: number, y: number, z: number } } }>
|
||||
local occupiedObjects = {}
|
||||
local found = function(foundItem, val)
|
||||
|
||||
---@param foundItem Object
|
||||
---@param _ any
|
||||
local found = function(foundItem, _)
|
||||
local bbox = self:GetBoundingBoxes(foundItem)
|
||||
if bbox then
|
||||
self._logger:debug("Found object: " .. foundItem:getTypeName() .. " at (" .. foundItem:getPoint().x .. ", " .. foundItem:getPoint().z .. ")")
|
||||
@@ -648,20 +655,21 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
||||
for distance = zone.minRadius, zone.maxRadius, zone.spacing do
|
||||
-- Check multiple positions within the angular slice
|
||||
local angleStep = math.min(15, zone.angleWidth / 3) -- Divide slice into sections
|
||||
|
||||
|
||||
for angle = minAngle, maxAngle, angleStep do
|
||||
local radians = math.rad(angle)
|
||||
|
||||
|
||||
-- Calculate position at this angle and distance, relative to unit's heading
|
||||
-- Angle 0 = forward, 90 = right, 180 = rear, 270 = left
|
||||
-- Apply unit heading to make angles relative to unit orientation
|
||||
local worldAngle = radians + unitHeading
|
||||
|
||||
|
||||
local candidateX = unitPos.p.x + distance * math.sin(worldAngle)
|
||||
local candidateZ = unitPos.p.z + distance * math.cos(worldAngle)
|
||||
local candidateY = land.getHeight({ x = candidateX, y = candidateZ })
|
||||
|
||||
-- Convert crate's relative bbox to world space at this position
|
||||
---@type { min: { x: number, y: number, z: number }, max: { x: number, y: number, z: number } }
|
||||
local crateBBoxWorldSpace = {
|
||||
min = {
|
||||
x = candidateX + crateRelativeBBox.min.x,
|
||||
|
||||
@@ -25,7 +25,9 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
|
||||
local BuildableMission = {}
|
||||
BuildableMission.__index = BuildableMission
|
||||
|
||||
|
||||
---@param siteType string
|
||||
---@param coords string
|
||||
---@return string
|
||||
local function getDefaultBriefing(siteType, coords)
|
||||
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." ..
|
||||
@@ -43,12 +45,13 @@ end
|
||||
---@param noLandingZone SpearheadTriggerZone?
|
||||
---@param logger Logger
|
||||
---@param briefing string?
|
||||
---@return BuildableMission
|
||||
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType, briefing)
|
||||
|
||||
setmetatable(BuildableMission, Mission)
|
||||
|
||||
local self = setmetatable({}, { __index = BuildableMission })
|
||||
|
||||
|
||||
self._targetZone = targetZone
|
||||
self._database = database
|
||||
self._requiredKilos = requiredKilos
|
||||
@@ -75,7 +78,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
|
||||
end
|
||||
|
||||
self.code = tostring(database:GetNewMissionCode())
|
||||
|
||||
|
||||
local splitTargetZoneName = Util.split_string(targetZone.name, "_")
|
||||
if splitTargetZoneName and splitTargetZoneName[3] and splitTargetZoneName[3] ~= "" then
|
||||
self.name = splitTargetZoneName[3]
|
||||
@@ -83,13 +86,6 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
|
||||
self.name = "Resupply"
|
||||
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._logger = logger
|
||||
self._onCrateDroppedOfListeners = {}
|
||||
@@ -124,6 +120,7 @@ function BuildableMission:ShowBriefing(groupID)
|
||||
|
||||
local unitType = DcsUtil.getUnitTypeFromGroup(group)
|
||||
local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
|
||||
if coords == nil then coords = "Could not make conversion" end
|
||||
|
||||
local siteType = "FARP"
|
||||
if self._crateType == "SAM_CRATE" then
|
||||
@@ -139,7 +136,7 @@ function BuildableMission:ShowBriefing(groupID)
|
||||
briefingPart = getDefaultBriefing(siteType, coords)
|
||||
end
|
||||
|
||||
local briefing = "Mission [" .. self.code .. "] " .. self.name ..
|
||||
local briefing = "Mission [" .. self.code .. "] " .. self.name ..
|
||||
"\n \n" ..
|
||||
briefingPart ..
|
||||
"\n\n" ..
|
||||
@@ -150,17 +147,20 @@ function BuildableMission:ShowBriefing(groupID)
|
||||
trigger.action.outTextForGroup(groupID, briefing, GlobalConfig:getBriefingTime())
|
||||
end
|
||||
|
||||
---@param groupID number
|
||||
function BuildableMission:MarkMissionAreaToGroup(groupID)
|
||||
|
||||
if self._markIDsPerGroup[groupID] then
|
||||
DcsUtil.RemoveMark(self._markIDsPerGroup[groupID])
|
||||
end
|
||||
local groupIdStr = tostring(groupID)
|
||||
|
||||
if self._markIDsPerGroup[groupIdStr] then
|
||||
DcsUtil.RemoveMark(self._markIDsPerGroup[groupIdStr])
|
||||
end
|
||||
---@type string
|
||||
local text = "[" .. self.code .. "] " .. self.name .. " | " .. self._crateType
|
||||
local location = { x= self.location.x, y=land.getHeight(self.location), z=self.location.y }
|
||||
local markID = DcsUtil.AddMarkToGroup(groupID, text, location)
|
||||
|
||||
self._markIDsPerGroup[groupID] = markID
|
||||
self._markIDsPerGroup[groupIdStr] = markID
|
||||
end
|
||||
|
||||
---@private
|
||||
@@ -201,9 +201,10 @@ function BuildableMission:SpawnActive()
|
||||
local fillColor2 = DrawingHelper.ColorTableToColorString({ 0, 0, 1, 0})
|
||||
self._dropOffZoneDrawing = CustomDrawing.FromZone(self._dropOffZone, lineColor2, fillColor2, 2, 6)
|
||||
self._dropOffZoneDrawing:Draw()
|
||||
|
||||
|
||||
---@param selfA BuildableMission
|
||||
---@param time number
|
||||
---@return number?
|
||||
local checkForCrateTasks = function (selfA, time)
|
||||
selfA:CheckCratesInZone()
|
||||
|
||||
@@ -266,8 +267,8 @@ function BuildableMission:CheckCratesInZone()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, foundCrate in pairs(foundCrates) do
|
||||
|
||||
for _, foundCrate in pairs(foundCrates) do
|
||||
local crateConfig = SupplyConfigHelper.fromObjectName(foundCrate:getName())
|
||||
if crateConfig then
|
||||
self._droppedKilos = self._droppedKilos + crateConfig.weight
|
||||
@@ -282,7 +283,7 @@ function BuildableMission:CheckCratesInZone()
|
||||
self:NotifyMissionComplete()
|
||||
self._state = "COMPLETED"
|
||||
end
|
||||
|
||||
|
||||
if self._state == "COMPLETED" then
|
||||
for groupID, markID in pairs(self._markIDsPerGroup) do
|
||||
if markID then
|
||||
|
||||
@@ -24,6 +24,7 @@ local RunwayStrikeMission = {}
|
||||
---@param runway Runway
|
||||
---@param database Database
|
||||
---@param logger Logger
|
||||
---@param airbaseName string
|
||||
---@param runwayBombingTracker RunwayBombingTracker
|
||||
---@return RunwayStrikeMission?
|
||||
function RunwayStrikeMission.new(runway, airbaseName, database, logger, runwayBombingTracker)
|
||||
@@ -42,7 +43,7 @@ function RunwayStrikeMission.new(runway, airbaseName, database, logger, runwayBo
|
||||
self._runwayZone = self:RunwayToSpearheadZone(runway)
|
||||
self._repairInProgress = false
|
||||
self._minKilosForDamage = 100
|
||||
|
||||
|
||||
local sections = self:ToSections(runway, 5)
|
||||
--[[
|
||||
+-----------+-----------+-----------+-----------+-----------+
|
||||
@@ -84,7 +85,6 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
|
||||
|
||||
self._logger:debug("Runway hit: " .. self._airportName .. ":" .. self._runway.Name)
|
||||
for _, section in pairs(self._runwaySections) do
|
||||
|
||||
local zone = self:SectionToSpearheadZone(section)
|
||||
if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
|
||||
if section.kilosHit == nil then
|
||||
@@ -96,7 +96,7 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
|
||||
end
|
||||
|
||||
---@param selfA RunwayStrikeMission
|
||||
local updateState = function(selfA, time)
|
||||
local updateState = function(selfA, _)
|
||||
selfA:UpdateState()
|
||||
end
|
||||
|
||||
@@ -154,7 +154,7 @@ function RunwayStrikeMission:Draw()
|
||||
|
||||
if runwaySection.drawID == nil then
|
||||
local zone = self:SectionToSpearheadZone(runwaySection)
|
||||
|
||||
|
||||
---@type Free
|
||||
local drawObject = {
|
||||
primitiveType = "Polygon",
|
||||
@@ -206,17 +206,18 @@ function RunwayStrikeMission:StartRepair()
|
||||
self._repairInProgress = true
|
||||
self._logger:debug("Starting repair of runway strike mission " .. self._airportName .. ":" .. self._runway.Name)
|
||||
---comment
|
||||
---@param selfA any
|
||||
---@return unknown
|
||||
---@param selfA RunwayStrikeMission
|
||||
---@param time number
|
||||
---@return number?
|
||||
local repairTask = function (selfA, time)
|
||||
local interval = selfA:DoRepairCycle()
|
||||
if interval == nil then return nil end
|
||||
return time + interval
|
||||
end
|
||||
|
||||
timer.scheduleFunction(repairTask, self, timer.getTime() + 5)
|
||||
end
|
||||
|
||||
---@return number?
|
||||
function RunwayStrikeMission:DoRepairCycle()
|
||||
|
||||
local interval = 5
|
||||
@@ -249,12 +250,14 @@ function RunwayStrikeMission:DoRepairCycle()
|
||||
|
||||
end
|
||||
|
||||
---@class StaticSpawn
|
||||
---@class StaticSpawn : table
|
||||
---@field category string
|
||||
---@field type string
|
||||
---@field y number
|
||||
---@field x number
|
||||
---@field heading number
|
||||
---@field name string?
|
||||
---@field hidden boolean?
|
||||
|
||||
|
||||
local counter = 1
|
||||
@@ -443,6 +446,7 @@ end
|
||||
|
||||
---@private
|
||||
---@param runway Runway
|
||||
---@param numSections number
|
||||
---@return Array<RunwaySection>
|
||||
function RunwayStrikeMission:ToSections(runway, numSections)
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ end
|
||||
---@return boolean
|
||||
function ZoneMission:AllDependenciesMet()
|
||||
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
|
||||
allDependenciesMet = false
|
||||
self._dependencies[missionName] = false
|
||||
@@ -227,10 +227,9 @@ end
|
||||
|
||||
---@internal
|
||||
---@param checkHealth boolean
|
||||
---@param messageIfDone boolean
|
||||
function ZoneMission:UpdateState(checkHealth, messageIfDone)
|
||||
---@param _ boolean
|
||||
function ZoneMission:UpdateState(checkHealth, _)
|
||||
if checkHealth == nil then checkHealth = false end
|
||||
if messageIfDone == false then messageIfDone = true end
|
||||
|
||||
|
||||
if checkHealth == true then
|
||||
@@ -238,7 +237,7 @@ function ZoneMission:UpdateState(checkHealth, messageIfDone)
|
||||
local staticObject = StaticObject.getByName(unitName)
|
||||
if staticObject then
|
||||
if staticObject:isExist() == true then
|
||||
local life0 = staticObject:getDesc().life
|
||||
local life0 = staticObject:getDesc().life --[[@as number]]
|
||||
if staticObject:getLife() / life0 < 0.3 then
|
||||
self._logger:debug("exploding unit")
|
||||
trigger.action.explosion(staticObject:getPoint(), 100)
|
||||
@@ -284,7 +283,9 @@ function ZoneMission:UpdateState(checkHealth, messageIfDone)
|
||||
end
|
||||
|
||||
if self._missionGroups.hasTargets == true then
|
||||
---@type number
|
||||
local total = 0
|
||||
---@type number
|
||||
local alive = 0
|
||||
|
||||
for _, units in pairs(self._missionGroups.targetsAlive) do
|
||||
@@ -297,13 +298,13 @@ function ZoneMission:UpdateState(checkHealth, messageIfDone)
|
||||
end
|
||||
|
||||
for _, sceneryObject in pairs(self._missionGroups.sceneryTargets) do
|
||||
total = total + 1
|
||||
total = total + 1 --[[@as number]]
|
||||
if sceneryObject:IsAlive() == true then
|
||||
alive = alive + 1
|
||||
end
|
||||
end
|
||||
|
||||
local deadRatio = (total - alive) / total
|
||||
local deadRatio = (total - alive) / total --[[@as number]]
|
||||
if deadRatio >= self._completeAtIndex then
|
||||
self._logger:debug("Dead ratio " .. self.zoneName .. deadRatio .. " >= " .. self._completeAtIndex)
|
||||
self._state = "COMPLETED"
|
||||
@@ -499,6 +500,7 @@ function ZoneMission:OnUnitLost(object)
|
||||
|
||||
local category = Object.getCategory(object)
|
||||
if category == Object.Category.UNIT then
|
||||
object = object --[[@as Unit]]
|
||||
local unitName = object:getName()
|
||||
self._logger:debug("UnitName:" .. unitName)
|
||||
|
||||
@@ -509,6 +511,7 @@ function ZoneMission:OnUnitLost(object)
|
||||
self._missionGroups.targetsAlive[groupName][unitName] = false
|
||||
end
|
||||
elseif category == Object.Category.STATIC then
|
||||
object = object --[[@as StaticObject]]
|
||||
local name = object:getName()
|
||||
self._missionGroups.unitsAlive[name][name] = false
|
||||
|
||||
@@ -525,12 +528,12 @@ end
|
||||
---@param unit Object
|
||||
function ZoneMission:MarkLastContact(unit)
|
||||
|
||||
if not unit then
|
||||
if not unit then
|
||||
self._logger:error("MarkLastContact called with nil unit")
|
||||
return
|
||||
end
|
||||
|
||||
local point = unit:getPoint()
|
||||
local point = unit:getPoint()
|
||||
if not point then
|
||||
self._logger:error("MarkLastContact called with unit without point")
|
||||
return
|
||||
|
||||
@@ -51,7 +51,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
|
||||
|
||||
self.location = database:GetLocationForMissionZone(zoneName)
|
||||
self.missionTypeDisplay = self.missionType
|
||||
|
||||
|
||||
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
||||
|
||||
return true, "success"
|
||||
@@ -73,9 +73,10 @@ function Mission:SpawnPersistedState() end
|
||||
function Mission:SpawnActive() end
|
||||
|
||||
---comment
|
||||
---@param checkHealth boolean
|
||||
---@param messageIfDone boolean
|
||||
function Mission:UpdateState(checkHealth, messageIfDone) end
|
||||
---@param _checkHealth boolean
|
||||
---@param _messageIfDone boolean
|
||||
---@diagnostic disable-next-line: unused-local
|
||||
function Mission:UpdateState(_checkHealth, _messageIfDone) end
|
||||
function Mission:StartCheckingContinuous() end
|
||||
|
||||
function Mission:PercentageComplete()
|
||||
@@ -90,6 +91,7 @@ function Mission:ShowBriefing(groupId)
|
||||
|
||||
local unitType = DcsUtil.getUnitTypeFromGroup(group)
|
||||
local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
|
||||
if coords == nil then coords = "Could not make conversion" end
|
||||
self._logger:debug("Coords converted: " .. coords)
|
||||
|
||||
local stateString = self:ToStateString()
|
||||
@@ -125,7 +127,7 @@ function Mission:NotifyMissionComplete()
|
||||
end)
|
||||
end
|
||||
|
||||
local success, err = pcall(function()
|
||||
local _, _ = pcall(function()
|
||||
SpearheadAPI.Internal.notifyMissionComplete(self.zoneName)
|
||||
end)
|
||||
|
||||
@@ -136,7 +138,9 @@ function Mission:ForceMissionComplete()
|
||||
self:NotifyMissionComplete()
|
||||
end
|
||||
|
||||
function Mission:MarkMissionAreaToGroup(groupId) end
|
||||
---@param _groupId number
|
||||
---@diagnostic disable-next-line: unused-local
|
||||
function Mission:MarkMissionAreaToGroup(_groupId) end
|
||||
|
||||
---endregion
|
||||
|
||||
@@ -148,30 +152,27 @@ function Mission:ToStateString() return "status: in progress" end
|
||||
|
||||
--endregion
|
||||
|
||||
do --aliases
|
||||
--- @alias MissionPriority
|
||||
--- | "none"
|
||||
--- | "primary"
|
||||
--- | "secondary"
|
||||
|
||||
--- @alias MissionPriority
|
||||
--- | "none"
|
||||
--- | "primary"
|
||||
--- | "secondary"
|
||||
--- @alias MissionType
|
||||
--- | "nil"
|
||||
--- | "STRIKE"
|
||||
--- | "DEEPSTRIKE"
|
||||
--- | "CAS"
|
||||
--- | "BAI"
|
||||
--- | "DEAD"
|
||||
--- | "SAM"
|
||||
--- | "OCA"
|
||||
--- | "LOGISTICS"
|
||||
---
|
||||
--- @alias MissionState
|
||||
--- | "NEW"
|
||||
--- | "WAITING"
|
||||
--- | "ACTIVE"
|
||||
--- | "COMPLETED"
|
||||
|
||||
--- @alias MissionType
|
||||
--- | "nil"
|
||||
--- | "STRIKE"
|
||||
--- | "DEEPSTRIKE"
|
||||
--- | "CAS"
|
||||
--- | "BAI"
|
||||
--- | "DEAD"
|
||||
--- | "SAM"
|
||||
--- | "OCA"
|
||||
--- | "LOGISTICS"
|
||||
|
||||
--- @alias MissionState
|
||||
--- | "NEW"
|
||||
--- | "WAITING"
|
||||
--- | "ACTIVE"
|
||||
--- | "COMPLETED"
|
||||
|
||||
end
|
||||
|
||||
return Mission
|
||||
Reference in New Issue
Block a user