Author SHA1 Message Date
dutchie031 77f4c41a6b changed releasentes lineending
Publish Beta Release / build (push) Successful in 26s
2026-09-21 22:59:48 +02:00
dutchie031 07a234489f changed releasentes lineending 2026-09-21 22:59:40 +02:00
dutchie031 e363b3fe94 changed releasentes lineending
Publish Beta Release / build (push) Successful in 24s
2026-09-21 22:53:39 +02:00
dutchie031 b68f95b748 update release notes
Publish Beta Release / build (push) Successful in 21s
2026-09-21 22:35:55 +02:00
dutchie031 8341a5fb6d update release notes 2026-09-21 22:34:14 +02:00
dutchie031 e32518f48d updated rleease notes
Publish Beta Release / build (push) Successful in 24s
2026-09-20 22:32:18 +02:00
dutchie031 2e7a930473 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>
2026-09-20 16:56:30 +00:00
16 changed files with 315 additions and 202 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>';
}
+83 -7
View File
@@ -26,14 +26,14 @@
<div class="content-wrapper">
<h1>Advanced Tutorial: Missions</h1>
<div id="strike-region">
<h2 id="strike">Strike</h2>
<p>
Naming: <span class="inline-lua"><span class="lua-variable">MISSION_STRIKE_[Name]</span></span> <br>
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>
@@ -41,18 +41,23 @@
</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
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.
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.
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>
@@ -60,6 +65,77 @@
<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>
+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;
+29 -39
View File
@@ -1,53 +1,43 @@
# Spearhead
Hello and welcome to Spearhead!
## Contributing.
If you're a developer that wants to contribute, look at the code or understand what we're doing, feel free to read on.
I'm always happy to see contributions! <br/>
Especially if you've found an issue, annoyance or feature that you know how to solve, fix or create! <br/>
If you're a mission maker that wants to see the documentation and build you mission with this framework. <br>
It's best to go to our fully fledged documentation website:
- Stable/Main: https://spearhead.rocks
- Beta/Develop: https://beta.spearhead.rocks
### Keep it small.
## Project Structure
Keeping your contributions small and to the point helps both me and yourself. <br/>
It'll help the PR review and the speed of improvements. <br/>
If you have a big feature in mind? Go for it! <br/>
Does it require an entire refactor of parts or all of the program? Maybe make sure to do step 0 first.
The Spearhead project is structured in a lot of separate files. <br>
While Lua isn't specifically created for it, most of it has been written with the standards of OOP in mind. <br>
Classes, Objects and helper methods.
### Steps (for first timers)
0. Reach out (Optional)
You're find in the `/src` folder all the files. <br>
They are separated in a somewhat logical order, but some things might feel out of place. <br>
If you want to let me know you want to do something to contribute, please do! <br/>
Maybe I'm already working on the thing you wanted to build, or someone else is. <br/>
Creating an issue is even better! <br/>
### Bundling, Intellisense, Linting
In DCS the easiest way to import a script is with 1 single file, however, that's not as "maintainable".
In order to keep it maintainable we're using a self-built tool to bundle, validate and rewrite some of the lua in order to get it into a single file.
The tool is open-source here: https://git.dutchie031.com/dutchie031/DcsMissionScriptingTools. <br>
It has a `VSCode` extensions and two `Github Action` steps. <br>
If your favorite IDE isn't `VSCode` (I respect that), it might be very easy to create an extension for it, if it does allow that.
1. Fork the repository
The `VSCode` extensions comes with Intellisense as well, in the form of a full file of DCS lua type annotations. <br>
This gives the Lua Language Server by Sumneko (which can be used in `VSCode`) all the type definitions it needs.
When writing Spearhead code it is also important to keep our own LuaLS Annotations up to date and complete. <br>
Read all about those annotations here: https://luals.github.io/wiki/annotations/
By forking you can create your own working set of code. <br/>
Whether you keep that fork public or private is up to you! <br/>
When a pull-request to the `develop` or `main` branches is created, before it can be merged it requires to pass the linter checks.
The first linting check is done by the Lua Language Server. It checks for whitespace and formatting, but it also checks if all types, type-checking and casts are in order. It marks unused variables etc. `.luarc.json` has all the config for those checks. <br>
The second linter job is with `luacheck` which first "bundles" the script, and then runs a full linter check over the bundled script. <br>
This is to make sure that also the bundled script does not contain any errors.
Keep in mind, forking is alright, but it's best with the intent to contribute back. <br/>
After all, instead of having 5 slightly different versions and products, it might be nicer to have 1 much better version. <br/>
## Contributing
2. Create a Draft PR as soon as possible!
### Setting up the project
As soon as you've found some times to create an initial version, please create a draft PR. <br/>
That way you can let me and everyone know it's being worked on and people can see what conflicts might arise with their own changes. <br/>
3. Test, test, test
With DCS and Lua there's a bunch of edge cases that are really hard to get to. <br/>
However, you kinda need to make sure most if not all cases are caught and tested. <br/>
Since automated testing for now doesn't seem feasible, please make sure to verify and test functionality after touching stuff. <br/>
4. Finalise and publish the PR.
Finalise the PR and let the maintainers know! <br/>
We can all have a look and discuss the changes. <br/>
# TODO:
- Would it be possible to generate optional markers on mission briefing locations for Tomcat / Phantom navigation? Then Jester can type it in for the pilots automatically.
If you've kept on reading I assume you either want to brwose
+10 -8
View File
@@ -5,19 +5,21 @@
### New Features
- Issue: #41
- Addresses #41
Adds Stage Complete debug methods for local testing of the Stage flow.
#40
- Issue: #35
PR #40
- Closes #35
Creating Lane option for stages.
Additionally to the primary stage lane, stages can now be created in different lanes.
These lanes can be used to create different stage progression flows and create different "side-stories".
#40
- Issue: #37
PR #40
- Closes #37
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
#44
PR #44
- Closes #42
DEEPSTRIKE now added as a possible feature.
This enables to have missions active in "Pre-Activated" Stages.
PR #48
### Bug Fixes
+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
@@ -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