Feature/deep strike (#48)
Update Docs / prepare-docs (push) Successful in 42s
Publish Beta Release / build (push) Successful in 41s
Update Docs / build-image (push) Successful in 19s
Update Docs / deploy-container (push) Successful in 21s

Closes #42

Reviewed-on: #48
Co-authored-by: dutchie031 <timrorije@gmail.com>
This commit was merged in pull request #48.
This commit is contained in:
2026-09-20 16:56:30 +00:00
committed by dutchie031
parent cbccedf710
commit 2e7a930473
15 changed files with 204 additions and 78 deletions
+1 -1
View File
@@ -169,7 +169,7 @@ class Sidebar extends HTMLElement {
render() {
var betaHtml = '';
if (true || window.location.hostname.toLowerCase() == 'beta.spearhead.rocks') {
if (window.location.hostname.toLowerCase() == 'beta.spearhead.rocks') {
betaHtml = '<note-box type="warning" title="Beta Version">You are looking at docs that are released with the beta version.</note-box>';
}
+106 -30
View File
@@ -26,42 +26,118 @@
<div class="content-wrapper">
<h1>Advanced Tutorial: Missions</h1>
<h2 id="strike">Strike</h2>
<p>
Naming: <span class="inline-lua"><span class="lua-variable">MISSION_STRIKE_[Name]</span></span> <br>
</p>
<h3 id="specific_targets">Specific Targets</h3>
<p>TBD</p>
<div id="strike-region">
<h2 id="strike">Strike</h2>
<p>
Naming: <code-inline>MISSION_STRIKE_[Name]</code-inline> <br>
</p>
<h3 id="specific_targets">Specific Targets</h3>
<p>TBD</p>
<h3 id="scenery_targets">Scenery Targets</h3>
<p>
Bridges, buildings or other type of targets that are baked into the map are awesome targets.<br>
We wanted to make sure they were easily integrated in Spearhead.
</p>
<h3 id="scenery_targets">Scenery Targets</h3>
<p>
Bridges, buildings or other type of targets that are baked into the map are awesome targets.<br>
We wanted to make sure they were easily integrated in Spearhead.
</p>
<note-box type="info" title="Implementation details">
Spearhead detects scenery targets by the trigger zone name. A zone named <strong>scenerytarget_[freeform]</strong> or
<strong>scenerytargets</strong> (case-insensitive) will be scanned and any scenery objects that has the attribute "Buildings" inside the zone
will be added as mission scenery targets.
</note-box>
<p>
Scenery targets are treated like mission targets: they are included in the mission completion calculation
(their `IsAlive()` state is checked) and their state is persisted/updated when missions spawn or resume.
</p>
<note-box type="warning" title="Changing IDs">
Due to Object IDs changing when maps are updated and edited the "Assign as..." functionality does not work for scenery targets.
Hence the need to use trigger zones to define them.
<note-box type="info" title="Implementation details">
Spearhead detects scenery targets by the trigger zone name. A zone named
<strong>scenerytarget_[freeform]</strong> or
<strong>scenerytargets</strong> (case-insensitive) will be scanned and any scenery objects that
has the attribute "Buildings" inside the zone
will be added as mission scenery targets.
</note-box>
<p>Here's two images showing how a bridge is added to a mission.</p>
<img src="/img/bridge.png" style="max-height: 35remrem;"/>
<p>
Scenery targets are treated like mission targets: they are included in the mission completion
calculation
(their `IsAlive()` state is checked) and their state is persisted/updated when missions spawn or
resume.
</p>
<note-box type="warning" title="Changing IDs">
Due to Object IDs changing when maps are updated and edited the "Assign as..." functionality
does not work for scenery targets.
Hence the need to use trigger zones to define them.
</note-box>
<p>Here's two images showing how a bridge is added to a mission.</p>
<img src="/img/bridge.png" style="max-height: 35remrem;" />
</div>
<div id="cas-region">
<h2 id="cas">CAS</h2>
<p>
Naming: <code-inline>MISSION_CAS_[Name]</code-inline> <br>
</p>
<h3 id="BattleManager">Battle Manager</h3>
<p>TBD</p>
</div>
<div id="dead-region">
<h2 id="dead">DEAD</h2>
<p>
Naming: <code-inline>MISSION_DEAD_[Name]</code-inline> <br>
Naming: <code-inline>MISSION_SAM_[Name]</code-inline> <br>
</p>
<h3 id="sam-vs-dead">SAM vs DEAD</h3>
<p>TBD</p>
</div>
<div id="bai-region">
<h2 id="bai">BAI</h2>
<p>
Naming: <code-inline>MISSION_BAI_[Name]</code-inline> <br>
</p>
</div>
<div id="deepstrike-region">
<h2 id="deepstrike">DEEPSTRIKE</h2>
<p>
Naming: <code-inline>MISSION_DEEPSTRIKE_[Name]</code-inline> <br>
<br>
When you, as a mission editor, want to create a mission that's further away from the current front-line you can use the Deep Strike mission type. <br>
This allows you to plan missions that require more strategic positioning and timing. <br>
</p>
<h3 id="activation">Activation</h3>
<p>
Deep Strike missions are activated when the Stage in which they are located becomes "PreActive" or "Active". <br>
"PreActive" is when either the current active StageNumber is a certain number of stages away OR the stage has "CAP" airbases active . <br>
<code-inline>SpearheadConfig.StageConfig.preactivateStage</code-inline> changes the configuration. <br>
<br>
For example: <br>
If the current active stage is <code-inline>MissionStage_2_[name]</code-inline> and preActivate config is set to 2. <br>
Then both <code-inline>MissionStage_3_[name]</code-inline> and <code-inline>MissionStage_4_[name]</code-inline> would become "PreActive".
</p>
<h3 id="primary-vs-secondary">Make Primary</h3>
<p>
By default DEEPSTRIKE missions are "Secondary" missions when spawned as a pre-activated stage.<br>
Then when the stage they are in is "Active", the mission becomes a "Primary" mission. <br>
<br>
If you want to make it a "Primary" mission from the start, you can overwrite it.<br>
Click the mission's trigger zone. Add a property named <code-inline>primary</code-inline> and set it to <code-inline>true</code-inline>.
This will make the mission "Primary" from the start. <br>
<br>
If you want it to "Secondary" even when its stage is the active stage, you can set "primary" to false. <br>
<br>
If you want to have it be "Secondary" when its stage is "Pre-Active" and "primary" when its stage is "Active" you don't have to do anything.
</p>
<h3 id="future-additions">Future</h3>
<p>
In the future we're thinking it would be cool to make the Deep Strike missions
be more integrated with the CAP airbases and/or logistics. <br>
Eg. Completing a DEEPSTRIKE mission would disable CAP for X amount of minutes after completion. <br>
This still needs to be refined, but we've already have ideas.
</p>
</div>
</div>
</div>
</main>
<footer>
<p>&copy; 2025 Spearhead Project</p>
+1 -1
View File
@@ -8,7 +8,7 @@
--color-bg-hover: rgba(120, 140, 255, 0.12); /* More visible hover */
--color-bg-note-dark: rgba(40, 35, 30, 0.85); /* Slightly darker note box */
--color-text-primary: #a5a5a5; /* Brighter text */
--color-text-primary: #b4b3b3; /* Brighter text */
--color-text-secondary: #bfc8d8; /* Lighter secondary text */
--color-text-accent: #7ecbff; /* Brighter accent */
--color-text-light: #ffffff;
+5
View File
@@ -19,6 +19,11 @@
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
#44
- Issue: #42
DEEPSTRIKE now added as a possible feature.
This enables to have missions active in "Pre-Activated" Stages.
#48
### Bug Fixes
## [0.13.0] 2026-08
+1 -1
View File
@@ -78,7 +78,7 @@ function FleetGroup.new(fleetGroupName, database, logger)
return nil, nil
end
if Util.startswith(namePart, "%[") == true then
if Util.startsWith(namePart, "%[") == true then
namePart = Util.split_string(namePart, "[")[1]
end
@@ -15,7 +15,7 @@ GlobalFleetManager.start = function(database)
local all_groups = MizGroupsManager.getAllGroupNames()
for _, groupName in pairs(all_groups) do
if Util.startswith(string.lower(groupName), "carriergroup" ) == true then
if Util.startsWith(string.lower(groupName), "carriergroup" ) == true then
logger:info("Registering " .. groupName .. " as a managed fleet")
local carrierGroup = FleetGroup.new(groupName, database, logger)
table.insert(fleetGroups, carrierGroup)
+1 -1
View File
@@ -150,7 +150,7 @@ do
for file in lfs.dir(dir) do
local split = Util.split_string(file, ".")
local doesStartWith = Util.startswith(file, startsWith, true)
local doesStartWith = Util.startsWith(file, startsWith, true)
if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
local numberString = split[#split-1]
local number = tonumber(numberString)
+23 -14
View File
@@ -69,6 +69,7 @@ local StageDrawing = require("classes.stageClasses.drawings.StageDrawing")
---@field descriptionLocation Vec2?
---@field dependsOn Array<string>
---@field completeAt number?
---@field primaryOverwrite boolean?
---@class FarpZoneData
---@field groups Array<string>
@@ -211,7 +212,7 @@ function Database.New(Logger)
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for _, layer_object in pairs(layer.objects) do
if Util.startswith(string.lower(layer_object.name), "buildable", true) == true then
if Util.startsWith(string.lower(layer_object.name), "buildable", true) == true then
local airbaseData = self:getAirbaseDataForDrawLayer(layer_object)
if airbaseData then
self._logger:debug("found airbase data for " .. layer_object.name)
@@ -234,7 +235,7 @@ function Database.New(Logger)
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for _, layer_object in pairs(layer.objects) do
if Util.startswith(layer_object.name, "drawing_", true) then
if Util.startsWith(layer_object.name, "drawing_", true) then
local object = layer_object --[[@as DrawingObject]]
local stageDrawing = StageDrawing.New(object)
if stageDrawing then
@@ -262,7 +263,7 @@ function Database.New(Logger)
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for _, layer_object in pairs(layer.objects) do
if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then
if Util.startsWith(string.lower(layer_object.name), "stagebriefing_", true) == true then
local zone = DcsUtil.getZoneByName(stageZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if zone and Util.is2dPointInZone(vec2, zone) == true then
@@ -491,7 +492,7 @@ end
local getAvailableCAPGroups = function()
local result = {}
for name, value in pairs(is_group_taken) do
if value == false and Util.startswith(name, "CAP") then
if value == false and Util.startsWith(name, "CAP") then
table.insert(result, name)
end
end
@@ -594,11 +595,11 @@ function Database:loadCapUnits()
for _, groupName in pairs(groups) do
is_group_taken[groupName] = true
if Util.startswith(groupName, "CAP_A", true) or Util.startswith(groupName, "CAP_B", true) then
if Util.startsWith(groupName, "CAP_A", true) or Util.startsWith(groupName, "CAP_B", true) then
table.insert(baseData.CapGroups, groupName)
elseif Util.startswith(groupName, "CAP_I", true) then
elseif Util.startsWith(groupName, "CAP_I", true) then
table.insert(baseData.InterceptGroups, groupName)
elseif Util.startswith(groupName, "CAP_S", true) then
elseif Util.startsWith(groupName, "CAP_S", true) then
table.insert(baseData.SweepGroups, groupName)
end
end
@@ -621,7 +622,7 @@ function Database:loadBlueSamUnits()
local triggerZone = DcsUtil.getZoneByName(blueSamZone)
if triggerZone then
for _, kvPair in pairs(triggerZone.properties) do
if kvPair.key and Util.startswith(kvPair.key, "buildable") then
if kvPair.key and Util.startsWith(kvPair.key, "buildable") then
local number = tonumber(kvPair.value)
if number and number > 0 then
samData.buildingKilos = number
@@ -633,7 +634,7 @@ function Database:loadBlueSamUnits()
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if layer_object.primitiveType == "TextBox" and triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
if layer_object.name and Util.startswith(layer_object.name, "supplybriefing_", true) then
if layer_object.name and Util.startsWith(layer_object.name, "supplybriefing_", true) then
layer_object = layer_object --[[@as TextBox]]
local description = layer_object.text
if description and description ~= "" then
@@ -710,9 +711,9 @@ function Database:LoadZoneData(missionZoneName)
if triggerZone and triggerZone.properties then
for _, kvPair in pairs(triggerZone.properties) do
local key = kvPair.key
if Util.startswith(key, "dependson", true) == true then
if Util.startsWith(key, "dependson", true) == true then
table.insert(self._tables.MissionZoneData[missionZoneName].dependsOn, kvPair.value)
elseif Util.startswith(key, "completeat") == true then
elseif Util.startsWith(key, "completeat") == true then
local value = tonumber(kvPair.value)
if value then
if value > 1 and value <= 100 then
@@ -722,6 +723,14 @@ function Database:LoadZoneData(missionZoneName)
end
self._tables.MissionZoneData[missionZoneName].completeAt = value
end
elseif Util.startsWith(key, "primary", true) == true then
if type(kvPair.value) == "string" then
if kvPair.value == "true" then
self._tables.MissionZoneData[missionZoneName].primaryOverwrite = true
elseif kvPair.value == "false" then
self._tables.MissionZoneData[missionZoneName].primaryOverwrite = false
end
end
end
end
end
@@ -734,7 +743,7 @@ function Database:LoadZoneData(missionZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if layer_object.primitiveType == "TextBox" and triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
if layer_object.name and Util.startswith(layer_object.name, "briefing_", true) then
if layer_object.name and Util.startsWith(layer_object.name, "briefing_", true) then
layer_object = layer_object --[[@as TextBox]]
local description = layer_object.text
if description and description ~= "" then
@@ -778,7 +787,7 @@ function Database:loadFarpData()
local triggerZone = DcsUtil.getZoneByName(farpZone)
if triggerZone then
for _, kvPair in pairs(triggerZone.properties) do
if kvPair.key and Util.startswith(kvPair.key, "buildable", true) == true then
if kvPair.key and Util.startsWith(kvPair.key, "buildable", true) == true then
local number = tonumber(kvPair.value)
if number and number > 0 then
farpzoneData.buildingKilos = number
@@ -792,7 +801,7 @@ function Database:loadFarpData()
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if layer_object.primitiveType == "TextBox" and triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
layer_object = layer_object --[[@as TextBox]]
if layer_object.name and Util.startswith(layer_object.name, "supplybriefing_", true) then
if layer_object.name and Util.startsWith(layer_object.name, "supplybriefing_", true) then
local description = layer_object.text
if description and description ~= "" then
farpzoneData.briefing = description
@@ -435,6 +435,12 @@ function Stage:PreActivate()
end
end
for _, mission in pairs(self._db.missions) do
if mission and mission.missionType == "DEEPSTRIKE" then
mission:SpawnInactive() --Not Really "inactive"
end
end
for _, airbase in pairs(self._db.airbases) do
airbase:ActivateRedStage()
end
@@ -210,7 +210,7 @@ function MissionCommandsHelper:OverviewToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do
if enabled == true then
local mission = self.missionsByCode[code]
if mission and mission:getState() == "ACTIVE" and mission.priority == "primary" then
if mission and mission:getState() == "ACTIVE" and mission:GetPriority() == "primary" then
table.insert(primaryMissions, mission)
end
end
@@ -230,7 +230,7 @@ function MissionCommandsHelper:OverviewToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do
if enabled == true then
local mission = self.missionsByCode[code]
if mission and mission:getState() == "ACTIVE" and mission.priority == "secondary" then
if mission and mission:getState() == "ACTIVE" and mission:GetPriority() == "secondary" then
table.insert(secondaryMissions, mission)
end
end
@@ -345,7 +345,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do
if enabled == true then
local mission = self.missionsByCode[code]
if mission and mission.priority == "primary" then
if mission and mission:GetPriority() == "primary" then
table.insert(primaryMissions, mission)
end
end
@@ -374,7 +374,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do
if enabled == true then
local mission = self.missionsByCode[code]
if mission and mission.priority == "secondary" then
if mission and mission:GetPriority() == "secondary" then
table.insert(secondaryMissions, mission)
end
end
@@ -416,7 +416,7 @@ function MissionCommandsHelper:addMissionCommands(groupId, path, mission)
end
local missionFolderName = "[" ..
mission.code .. "]" .. distance .. mission.name .. "( " .. mission.missionTypeDisplay .. " )"
mission.code .. "]" .. distance .. mission.name .. "(" .. mission.missionTypeDisplay .. ")"
missionCommands.addSubMenuForGroup(groupId, missionFolderName, path)
table.insert(path, missionFolderName)
@@ -76,7 +76,7 @@ local SupplyConfigHelper = {}
---@return SupplyConfig?
function SupplyConfigHelper.fromObjectName(name)
for configName, config in pairs(SupplyConfig) do
if Util.startswith(name, configName, true) == true then
if Util.startsWith(name, configName, true) == true then
return config
end
end
@@ -259,7 +259,7 @@ function BuildableMission:CheckCratesInZone()
local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
for _, staticObject in pairs(crates) do
if staticObject and staticObject:isExist() and Util.startswith(staticObject:getName(), self._crateType, true) then
if staticObject and staticObject:isExist() and Util.startsWith(staticObject:getName(), self._crateType, true) then
local pos = staticObject:getPoint()
if Util.is3dPointInZone(pos, self._dropOffZone) then
@@ -14,7 +14,7 @@ local DcsUtil = require("classes.util.DcsUtil")
---@field private _completeAtIndex number
---@field private _parentStage Stage
---@field private _battleManager? BattleManager
---@field private _lastContactMarkerID number;
---@field private _lastContactMarkerID number
local ZoneMission = {}
--- @class MissionGroups
@@ -24,7 +24,7 @@ local ZoneMission = {}
--- @field unitsAlive table<string, table<string, boolean>>
--- @field targetsAlive table<string, table<string, boolean>>
--- @field sceneryTargets Array<SpearheadSceneryObject>
--- @field groupNamesPerunit table<string,string>
--- @field groupNamesPerUnit table<string,string>
---@class ParsedMissionName
---@field missionName string
@@ -36,7 +36,7 @@ local ZoneMission = {}
local function ParseZoneName(input)
local split_name = Util.split_string(input, "_")
local split_length = Util.tableLength(split_name)
if Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
if Util.startsWith(input, "RANDOMMISSION") == true and split_length < 4 then
MissionEditorWarnings.Add("Random Mission with zonename " .. input .. " not in right format")
return nil
elseif split_length < 3 then
@@ -53,9 +53,10 @@ local function ParseZoneName(input)
if inputType == "cas" then parsedType = "CAS" end
if inputType == "bai" then parsedType = "BAI" end
if inputType == "sam" then parsedType = "SAM" end
if inputType == "deepstrike" then parsedType = "DEEPSTRIKE" end
if parsedType == "nil" then
MissionEditorWarnings.Add("Mission with zonename '" ..
MissionEditorWarnings.Add("Mission with zoneName '" ..
input .. "' has an unsupported type '" .. (type or "nil"))
return nil
end
@@ -110,7 +111,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
unitsAlive = {},
targetsAlive = {},
hasTargets = false,
groupNamesPerunit = {},
groupNamesPerUnit = {},
sceneryTargets = {}
}
@@ -148,17 +149,17 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._missionGroups.redGroups, spearheadGroup)
local isGroupTarget = Util.startswith(string.lower(groupName), "tgt_")
local isGroupTarget = Util.startsWith(string.lower(groupName), "tgt_")
for _, unit in pairs(spearheadGroup:GetObjects()) do
local unitName = unit:getName()
local isUnitTarget = Util.startswith(string.lower(unitName), "tgt_")
local isUnitTarget = Util.startsWith(string.lower(unitName), "tgt_")
if self._missionGroups.unitsAlive[groupName] == nil then
self._missionGroups.unitsAlive[groupName] = {}
end
self._missionGroups.unitsAlive[groupName][unitName] = true
self._missionGroups.groupNamesPerunit[unitName] = groupName
self._missionGroups.groupNamesPerUnit[unitName] = groupName
if isGroupTarget == true or isUnitTarget == true then
self._missionGroups.hasTargets = true
@@ -178,7 +179,6 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
if self.missionType == "CAS" then
self._battleManager = BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel)
end
self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups))
@@ -353,6 +353,19 @@ end
function ZoneMission:SpawnInactive()
self._logger:info("PreActivating " .. self.name)
if self.missionType == "DEEPSTRIKE" then
local missionData = self._database:getMissionDataForZone(self.zoneName)
---@type MissionPriority
local priority = "secondary"
if missionData and missionData.primaryOverwrite ~= nil then
if missionData.primaryOverwrite == true then
priority = "primary"
end
end
self._priority = priority
self._missionCommandsHelper:AddMissionToCommands(self)
end
for _, group in pairs(self._missionGroups.redGroups) do
group:Spawn()
end
@@ -389,6 +402,17 @@ function ZoneMission:SpawnActive()
self._battleManager:Start()
end
self._missionCommandsHelper:RemoveMissionToCommands(self) -- Remove first
if self.missionType == "DEEPSTRIKE" then
local missionData = self._database:getMissionDataForZone(self.zoneName)
local priority = "primary"
if missionData and missionData.primaryOverwrite ~= nil then
if missionData.primaryOverwrite == false then
priority = "secondary"
end
end
self._priority = priority
end
self._missionCommandsHelper:AddMissionToCommands(self)
self:StartCheckingContinuous()
@@ -480,7 +504,7 @@ function ZoneMission:OnUnitLost(object)
local unitName = object:getName()
self._logger:debug("UnitName:" .. unitName)
local groupName = self._missionGroups.groupNamesPerunit[unitName]
local groupName = self._missionGroups.groupNamesPerUnit[unitName]
self._missionGroups.unitsAlive[groupName][unitName] = false
if self._missionGroups.targetsAlive[groupName] and self._missionGroups.targetsAlive[groupName][unitName] then
@@ -9,7 +9,7 @@ local GlobalConfig = require("classes.configuration.GlobalConfig")
---@field zoneName string
---@field missionType MissionType
---@field missionTypeDisplay string
---@field priority MissionPriority
---@field protected _priority MissionPriority
---@field location Vec2?
---@field code string
---@field protected _state MissionState
@@ -40,7 +40,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
self.zoneName = zoneName
self.name = missionName
self.missionType = missionType
self.priority = priority
self._priority = priority
self._state = "NEW"
self._logger = logger
self._database = database
@@ -64,6 +64,11 @@ end
--region PUBLIC
---@return MissionPriority
function Mission:GetPriority()
return self._priority
end
function Mission:SpawnPersistedState() end
function Mission:SpawnActive() end
@@ -114,7 +119,7 @@ end
function Mission:NotifyMissionComplete()
self._missionCommandsHelper:RemoveMissionToCommands(self)
self._logger:info("Mission Completed: " .. self.zoneName)
trigger.action.outText("Mission " .. self.name .. " [" .. self.code .. "] was completed succesfully", 20)
trigger.action.outText("Mission " .. self.name .. " [" .. self.code .. "] was completed successfully", 20)
for _, listener in pairs(self._completeListeners) do
pcall(function()
@@ -155,13 +160,14 @@ function Mission:ToStateString() return "status: in progress" end
--- @alias MissionType
--- | "nil"
--- | "STRIKE"
--- | "DEEPSTRIKE"
--- | "CAS"
--- | "BAI"
--- | "DEAD"
--- | "SAM"
--- | "OCA"
--- | "LOGISTICS"
---
--- @alias MissionState
--- | "NEW"
--- | "WAITING"
+8 -8
View File
@@ -1,13 +1,13 @@
---@class Util
local UTIL = {}
do -- INIT UTIL
---splits a string in sub parts by seperator
---splits a string in sub parts by separator
---@param input string
---@param seperator string
---@param separator string
---@return Array<string> result list of strings
function UTIL.split_string(input, seperator)
if seperator == nil then
seperator = " "
function UTIL.split_string(input, separator)
if separator == nil then
separator = " "
end
local result = {}
@@ -15,7 +15,7 @@ do -- INIT UTIL
return result
end
for str in string.gmatch(input, "[^" .. seperator .. "]+") do
for str in string.gmatch(input, "[^" .. separator .. "]+") do
table.insert(result, str)
end
return result
@@ -134,7 +134,7 @@ do -- INIT UTIL
---@param findable string
---@param ignoreCase boolean?
---@return boolean
UTIL.startswith = function(str, findable, ignoreCase)
UTIL.startsWith = function(str, findable, ignoreCase)
if ignoreCase == true then
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
end
@@ -164,7 +164,7 @@ do -- INIT UTIL
---@return boolean
UTIL.startswithAny = function(str, findableTable)
for _, value in pairs(findableTable) do
if type(value) == "string" and UTIL.startswith(str, value) then return true end
if type(value) == "string" and UTIL.startsWith(str, value) then return true end
end
return false
end