Author SHA1 Message Date
dutchie031 52f840c270 Initial Deep Strike implementation 2026-09-08 15:47:11 +02:00
dutchie031 d14a0dc78e Added mission type deepstrike 2026-09-08 08:13:17 +02:00
dutchie031 a1355ff05d fixed minor bug
Publish Release / build (push) Failing after 12s
2026-09-08 07:44:24 +02:00
dutchie031 2e636b2ded minor udpdate to docs
Update Docs / prepare-docs (push) Successful in 30s
Update Docs / build-image (push) Successful in 9s
Update Docs / deploy-container (push) Successful in 13s
2026-09-08 07:25:29 +02:00
dutchie031 488a492e39 Custom briefings for BlueSam and FARPS (#44)
Update Docs / prepare-docs (push) Successful in 32s
Publish Release / build (push) Successful in 34s
Update Docs / build-image (push) Successful in 15s
Update Docs / deploy-container (push) Successful in 12s
Fixes #37

<img width="761" alt="image.png" src="attachments/bdc9d61a-8bbc-41f3-8de4-b97edfae28da">
<img width="1095" alt="image.png" src="attachments/9c77798d-f371-4035-84dd-7c60cb79d91f">

Reviewed-on: #44
Co-authored-by: dutchie031 <timrorije@gmail.com>
2026-09-07 17:23:30 +00:00
25 changed files with 315 additions and 115 deletions
+1 -1
View File
@@ -64,7 +64,7 @@
</footer> </footer>
<script> <script>
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>'; betaHtml = '<note-box type="warning" title="Beta Version">You are looking at docs that are released with the beta version.</note-box>';
} }
document.getElementById('beta-box-div').innerHTML = betaHtml || ''; document.getElementById('beta-box-div').innerHTML = betaHtml || '';
@@ -49,17 +49,6 @@ class DownloadScript extends HTMLElement {
return null; return null;
} }
} }
async fetchReleaseAssets(url){
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching release assets:', error);
return null;
}
}
async connectedCallback() { async connectedCallback() {
@@ -72,10 +61,9 @@ class DownloadScript extends HTMLElement {
if (latestRelease) { if (latestRelease) {
version = latestRelease.tag_name; version = latestRelease.tag_name;
timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA'); timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA');
const assets = latestRelease.assets;
const assets = await this.fetchReleaseAssets(latestRelease.assets_url);
if (assets && assets.length > 0) { if (assets && assets.length > 0) {
const spearheadAsset = assets.find(asset => asset.name.includes('spearhead')); const spearheadAsset = assets.find(asset => asset.name.includes('spearhead.v'));
if (spearheadAsset) { if (spearheadAsset) {
size = (spearheadAsset.size / 1024).toFixed(2) + 'kb'; size = (spearheadAsset.size / 1024).toFixed(2) + 'kb';
href = spearheadAsset.browser_download_url; href = spearheadAsset.browser_download_url;
@@ -105,7 +93,7 @@ class DownloadScript extends HTMLElement {
<p> <p>
<span class="version-text">${version}</span> (${timestamp}) <br> <span class="version-text">${version}</span> (${timestamp}) <br>
<a style="text-decoration: underline;" target="_blank" href="${href}">Download</a> (${size}) <br> <a style="text-decoration: underline;" target="_blank" href="${href}">Download</a> (${size}) <br>
<a style="text-decoration: underline;" target="_blank" href="https://git.dutchie031.com/api/v1/repos/Spearhead/spearhead/releases">See All Versions here</a> <a style="text-decoration: underline;" target="_blank" href="https://git.dutchie031.com/Spearhead/spearhead/releases">See All Versions here</a>
</p> </p>
</div> </div>
+4 -1
View File
@@ -175,7 +175,10 @@ class Sidebar extends HTMLElement {
this.innerHTML = ` this.innerHTML = `
<div class="side-nav"> <div class="side-nav">
${betaHtml} <div style="width:100%">
${betaHtml}
</div>
<h4 class="side-nav-title"></h4> <h4 class="side-nav-title"></h4>
<ul> <ul>
<!-- Navigation items will be populated automatically --> <!-- Navigation items will be populated automatically -->
+16 -7
View File
@@ -80,17 +80,12 @@
</tbody> </tbody>
</table> </table>
<note-box type="info" title="Combining Tasks"> <note-box type="info" title="Combining Tasks">
Currently a group can do 1 thing. Either be Primary, Seconday, Sweep or Intercept. <br> Currently a group can do 1 thing. Either be Primary, Seconday, Sweep or Intercept. <br>
If you want to have different types during different stages, it's best to copy paste groups and If you want to have different types during different stages, it's best to copy paste groups and
rename them accordingly. <br> rename them accordingly. <br>
</note-box> </note-box>
<note-box type="info" title="Airfield Defenses">
</note-box>
<h3 id="cap-zones-ref">Zones</h3> <h3 id="cap-zones-ref">Zones</h3>
<table class="table-no-style"> <table class="table-no-style">
<tbody> <tbody>
@@ -99,6 +94,7 @@
<td>=></td> <td>=></td>
<td>A Cap ROUTE. The two most outer points are used to define the fly-over points.</td> <td>A Cap ROUTE. The two most outer points are used to define the fly-over points.</td>
</tr> </tr>
<tr><td>______________________</td><td></td><td></td> </tr>
<tr> <tr>
<td><code-inline>INTERCEPTZONE_[ZoneID]_[NAME]</code-inline></td> <td><code-inline>INTERCEPTZONE_[ZoneID]_[NAME]</code-inline></td>
<td>=></td> <td>=></td>
@@ -112,6 +108,19 @@
</p> </p>
<h2 id="setup">Setting up groups</h2> <h2 id="setup">Setting up groups</h2>
<h3 id="airbases-grouping">Airbases</h3>
<p>
Before talking about the groups, it's good to mention that groups are currently logically grouped and managed by an Airbase. <br>
Meaning, Airbase A does not know how many flights Airbase B is sending out. <br>
This might change in the future where multiple Airbases can work together.
</p>
<note-box type="info" title="Airbase Defenses">
When an Airbase is "active" and thus has aircraft being scheduled for taskings, all RED groups at that airfield will also be spawned. <br>
Additionally, all missions of type <code-inline>SAM</code-inline> will be spawned (without being available in Primary Missions). <br>
This should hopefully give you all the tools to make distance CAP airbases still feel very well defended.
</note-box>
<h3 id="types-explained">Types explained</h3> <h3 id="types-explained">Types explained</h3>
<p> <p>
Before we go into depth on each type it's best to first look at the types briefly and what we Before we go into depth on each type it's best to first look at the types briefly and what we
@@ -140,7 +149,7 @@
The ground attack units mentioned are not in yet and thus escort groups are not either. <br> The ground attack units mentioned are not in yet and thus escort groups are not either. <br>
<note-box type="warning" title="Overriding Taskings"> <note-box type="warning" title="Overriding Taskings">
Each tasking has very specific waypoints, both for departure, arrical and ofcourse all point in between. <br> Each tasking has very specific waypoints, both for departure, arrival and ofcourse all point in between. <br>
Overriding the "tasking" will break part of the cap flow as "RTBInTen" and RTB events are triggered by waypoints. <br> Overriding the "tasking" will break part of the cap flow as "RTBInTen" and RTB events are triggered by waypoints. <br>
Overriding these waypoints and taskings will break the management by Spearhead. Overriding these waypoints and taskings will break the management by Spearhead.
</note-box> </note-box>
@@ -154,7 +163,7 @@
The way the trigger zone is mapped to a route is as follows: <br> The way the trigger zone is mapped to a route is as follows: <br>
<br> <br>
The 2 points that are furthest apart from each other are chosen as the "leg" of the route. <br> The 2 points that are furthest apart from each other are chosen as the "leg" of the route. <br>
The other 2 points are discarded. <br> The other 2 points are discarded at this time. <br>
<br> <br>
The Orbit route is then created with an "Anchored" task. With the closest point being the starting The Orbit route is then created with an "Anchored" task. With the closest point being the starting
point with an outbound leg to the furthest point. <br> point with an outbound leg to the furthest point. <br>
+106 -30
View File
@@ -26,42 +26,118 @@
<div class="content-wrapper"> <div class="content-wrapper">
<h1>Advanced Tutorial: Missions</h1> <h1>Advanced Tutorial: Missions</h1>
<h2 id="strike">Strike</h2> <div id="strike-region">
<p> <h2 id="strike">Strike</h2>
Naming: <span class="inline-lua"><span class="lua-variable">MISSION_STRIKE_[Name]</span></span> <br> <p>
</p> Naming: <code-inline>MISSION_STRIKE_[Name]</code-inline> <br>
<h3 id="specific_targets">Specific Targets</h3> </p>
<p>TBD</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> <note-box type="info" title="Implementation details">
<p> Spearhead detects scenery targets by the trigger zone name. A zone named
Bridges, buildings or other type of targets that are baked into the map are awesome targets.<br> <strong>scenerytarget_[freeform]</strong> or
We wanted to make sure they were easily integrated in Spearhead. <strong>scenerytargets</strong> (case-insensitive) will be scanned and any scenery objects that
</p> has the attribute "Buildings" inside the zone
will be added as mission scenery targets.
<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> </note-box>
<p>
<p>Here's two images showing how a bridge is added to a mission.</p> Scenery targets are treated like mission targets: they are included in the mission completion
<img src="/img/bridge.png" style="max-height: 35remrem;"/> 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>
</div> </div>
</main> </main>
<footer> <footer>
<p>&copy; 2025 Spearhead Project</p> <p>&copy; 2025 Spearhead Project</p>
+7
View File
@@ -253,6 +253,13 @@
To make an airbase buildable add a text box inside of the trigger zone and name it: <code-inline>buildable_[freeform]</code-inline> <br/> To make an airbase buildable add a text box inside of the trigger zone and name it: <code-inline>buildable_[freeform]</code-inline> <br/>
Then in the text box type amount of kilo's you want to be transfered before the logistisc mission is complete. <br/> Then in the text box type amount of kilo's you want to be transfered before the logistisc mission is complete. <br/>
<br>
To add a custom briefing for a buildable zone, include a text box inside the trigger zone and name it: <code-inline>supplybriefing_[freeform]</code-inline>.
<code-inline>{{coords}}</code-inline> is highly recommended so the players can easily find their landing zone.<br>
Additionally, the amount of kilos required and delivered are always shown on the bottom, as well as a note to not land in the construction zone.
<br/>
<br> <br>
The base or zone will slowly build up with each crate giving both a nice view of it happening AND it's good for performance as there's not a big addition of objects at once. <br/> The base or zone will slowly build up with each crate giving both a nice view of it happening AND it's good for performance as there's not a big addition of objects at once. <br/>
Right now a crate takes 15 seconds to unpack per 500kg. Meaning 2 crates of 1000kg will be faster than 1 crate of 2000kg. <br/> Right now a crate takes 15 seconds to unpack per 500kg. Meaning 2 crates of 1000kg will be faster than 1 crate of 2000kg. <br/>
+2 -1
View File
@@ -8,7 +8,7 @@
--color-bg-hover: rgba(120, 140, 255, 0.12); /* More visible hover */ --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-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-secondary: #bfc8d8; /* Lighter secondary text */
--color-text-accent: #7ecbff; /* Brighter accent */ --color-text-accent: #7ecbff; /* Brighter accent */
--color-text-light: #ffffff; --color-text-light: #ffffff;
@@ -337,6 +337,7 @@ main {
.side-nav { .side-nav {
min-width: 200px; min-width: 200px;
max-width: 200px;
padding-left: 0; padding-left: 0;
overflow-y: auto; overflow-y: auto;
border-right: none; border-right: none;
+4
View File
@@ -15,6 +15,10 @@
These lanes can be used to create different stage progression flows and create different "side-stories". These lanes can be used to create different stage progression flows and create different "side-stories".
#40 #40
- Issue: #37
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
#44
### Bug Fixes ### Bug Fixes
## [0.13.0] 2026-08 ## [0.13.0] 2026-08
+1 -1
View File
@@ -72,7 +72,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
return nil, nil return nil, nil
end end
if Util.startswith(namePart, "%[") == true then if Util.startsWith(namePart, "%[") == true then
namePart = Util.split_string(namePart, "[")[1] namePart = Util.split_string(namePart, "[")[1]
end end
@@ -15,7 +15,7 @@ GlobalFleetManager.start = function(database)
local all_groups = MizGroupsManager.getAllGroupNames() local all_groups = MizGroupsManager.getAllGroupNames()
for _, groupName in pairs(all_groups) do for _, groupName in pairs(all_groups) do
if Util.startswith(string.lower(groupName), "carriergroup" ) == true then if Util.startsWith(string.lower(groupName), "carriergroup" ) == true then
logger:info("Registering " .. groupName .. " as a managed fleet") logger:info("Registering " .. groupName .. " as a managed fleet")
local carrierGroup = FleetGroup:new(groupName, database, logger) local carrierGroup = FleetGroup:new(groupName, database, logger)
table.insert(fleetGroups, carrierGroup) table.insert(fleetGroups, carrierGroup)
+1 -1
View File
@@ -148,7 +148,7 @@ do
for file in lfs.dir(dir) do for file in lfs.dir(dir) do
local split = Util.split_string(file, ".") 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 if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
local numberString = split[#split-1] local numberString = split[#split-1]
local number = tonumber(numberString) local number = tonumber(numberString)
+68 -12
View File
@@ -58,6 +58,7 @@ local StageDrawing = require("classes.stageClasses.drawings.StageDrawing")
---@class BlueSamData ---@class BlueSamData
---@field groups Array<string> ---@field groups Array<string>
---@field buildingKilos number? ---@field buildingKilos number?
---@field briefing string?
---@class MissionZoneData ---@class MissionZoneData
---@field ZoneName string ---@field ZoneName string
@@ -68,12 +69,14 @@ local StageDrawing = require("classes.stageClasses.drawings.StageDrawing")
---@field descriptionLocation Vec2? ---@field descriptionLocation Vec2?
---@field dependsOn Array<string> ---@field dependsOn Array<string>
---@field completeAt number? ---@field completeAt number?
---@field primaryOverwrite boolean?
---@class FarpZoneData ---@class FarpZoneData
---@field groups Array<string> ---@field groups Array<string>
---@field padNames Array<string> ---@field padNames Array<string>
---@field buildingKilos number? ---@field buildingKilos number?
---@field supplyHubNames Array<string> ---@field supplyHubNames Array<string>
---@field briefing string?
---@class Database ---@class Database
---@field private _tables DatabaseTables ---@field private _tables DatabaseTables
@@ -209,7 +212,7 @@ function Database.New(Logger)
for i, layer in pairs(env.mission.drawings.layers) do for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for key, 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) local airbaseData = self:getAirbaseDataForDrawLayer(layer_object)
if airbaseData then if airbaseData then
self._logger:debug("found airbase data for " .. layer_object.name) self._logger:debug("found airbase data for " .. layer_object.name)
@@ -229,7 +232,7 @@ function Database.New(Logger)
for i, layer in pairs(env.mission.drawings.layers) do for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for key, 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 object = layer_object --[[@as DrawingObject]]
local stageDrawing = StageDrawing.New(object) local stageDrawing = StageDrawing.New(object)
if stageDrawing then if stageDrawing then
@@ -257,7 +260,7 @@ function Database.New(Logger)
for i, layer in pairs(env.mission.drawings.layers) do for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do for key, layer_object in pairs(layer.objects) do
if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then if Util.startsWith(string.lower(layer_object.name), "stagebriefing_", true) == true then
local zone = DcsUtil.getZoneByName(stageZoneName) local zone = DcsUtil.getZoneByName(stageZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY } local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if zone and Util.is2dPointInZone(vec2, zone) == true then if zone and Util.is2dPointInZone(vec2, zone) == true then
@@ -482,7 +485,7 @@ end
local getAvailableCAPGroups = function() local getAvailableCAPGroups = function()
local result = {} local result = {}
for name, value in pairs(is_group_taken) do 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) table.insert(result, name)
end end
end end
@@ -585,11 +588,11 @@ function Database:loadCapUnits()
for _, groupName in pairs(groups) do for _, groupName in pairs(groups) do
is_group_taken[groupName] = true 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) 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) 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) table.insert(baseData.SweepGroups, groupName)
end end
end end
@@ -612,10 +615,31 @@ function Database:loadBlueSamUnits()
local triggerZone = DcsUtil.getZoneByName(blueSamZone) local triggerZone = DcsUtil.getZoneByName(blueSamZone)
if triggerZone then if triggerZone then
for _, kvPair in pairs(triggerZone.properties) do 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) local number = tonumber(kvPair.value)
if number and number > 0 then if number and number > 0 then
samData.buildingKilos = number samData.buildingKilos = number
if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
if layer_object.name and Util.startsWith(layer_object.name, "supplybriefing_", true) then
local description = layer_object.text
if description and description ~= "" then
samData.briefing = description
end
end
end
end
end
end
end
else
MissionEditorWarnings.Add("Buildable number for " .. blueSamZone .. " is invalid")
end end
end end
end end
@@ -679,9 +703,9 @@ function Database:LoadZoneData(missionZoneName)
if triggerZone and triggerZone.properties then if triggerZone and triggerZone.properties then
for _, kvPair in pairs(triggerZone.properties) do for _, kvPair in pairs(triggerZone.properties) do
local key = kvPair.key 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) 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) local value = tonumber(kvPair.value)
if value then if value then
if value > 1 and value <= 100 then if value > 1 and value <= 100 then
@@ -691,6 +715,14 @@ function Database:LoadZoneData(missionZoneName)
end end
self._tables.MissionZoneData[missionZoneName].completeAt = value self._tables.MissionZoneData[missionZoneName].completeAt = value
end 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 end
end end
@@ -703,7 +735,7 @@ function Database:LoadZoneData(missionZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY } local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
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
local description = layer_object.text local description = layer_object.text
if description and description ~= "" then if description and description ~= "" then
self._tables.MissionZoneData[missionZoneName].description = description self._tables.MissionZoneData[missionZoneName].description = description
@@ -746,11 +778,35 @@ function Database:loadFarpData()
local triggerZone = DcsUtil.getZoneByName(farpZone) local triggerZone = DcsUtil.getZoneByName(farpZone)
if triggerZone then if triggerZone then
for _, kvPair in pairs(triggerZone.properties) do 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) local number = tonumber(kvPair.value)
if number and number > 0 then if number and number > 0 then
farpzoneData.buildingKilos = number farpzoneData.buildingKilos = number
-- check briefings
if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) 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
end
end
end
end
end
end
end
else
MissionEditorWarnings.Add("Buildable number for " .. farpZone .. " is invalid.")
end end
end end
end end
end end
@@ -89,7 +89,7 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
local zone = DcsUtil.getZoneByName(zoneName) local zone = DcsUtil.getZoneByName(zoneName)
if zone then if zone then
BuildableZone.New(self, zone, self._buildableCrateKilos or 0, "SAM_CRATE", self._blueGroups, logger, database) BuildableZone.New(self, zone, self._buildableCrateKilos or 0, "SAM_CRATE", self._blueGroups, logger, database, blueSamData.briefing)
end end
return self return self
@@ -64,7 +64,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
local zone = DcsUtil.getZoneByName(zoneName) local zone = DcsUtil.getZoneByName(zoneName)
if zone then if zone then
self._logger:debug("Creating Buildable zone: " .. zoneName .. " with " .. (farpData.buildingKilos or "nil") .. " kilos") self._logger:debug("Creating Buildable zone: " .. zoneName .. " with " .. (farpData.buildingKilos or "nil") .. " kilos")
BuildableZone.New(self, zone, farpData.buildingKilos or 0, "FARP_CRATE", self._groups, logger, database) BuildableZone.New(self, zone, farpData.buildingKilos or 0, "FARP_CRATE", self._groups, logger, database, farpData.briefing)
end end
end end
self:Deactivate() self:Deactivate()
@@ -20,7 +20,8 @@ BuildableZone.__index = BuildableZone
---@param database Database ---@param database Database
---@param crateType SupplyType ---@param crateType SupplyType
---@param logger Logger ---@param logger Logger
function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroups, logger, database) ---@param briefing string?
function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroups, logger, database, briefing)
self._targetZone = targetZone self._targetZone = targetZone
self._requiredKilos = kilosRequired or 0 self._requiredKilos = kilosRequired or 0
self._buildableGroups = buildableGroups or {} self._buildableGroups = buildableGroups or {}
@@ -69,7 +70,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
local noLandingZone = self:GetNoLandingZone() local noLandingZone = self:GetNoLandingZone()
if kilosRequired and kilosRequired > 0 then if kilosRequired and kilosRequired > 0 then
self._buildableMission = BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType) self._buildableMission = BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType, briefing)
self._buildableMission:AddOnCrateDroppedOfListener(self) self._buildableMission:AddOnCrateDroppedOfListener(self)
else else
self._buildableMission = nil self._buildableMission = nil
@@ -432,6 +432,12 @@ function Stage:PreActivate()
end end
end end
for key, mission in pairs(self._db.missions) do
if mission and mission.missionType == "DEEPSTRIKE" then
mission:SpawnActive()
end
end
for _, airbase in pairs(self._db.airbases) do for _, airbase in pairs(self._db.airbases) do
airbase:ActivateRedStage() airbase:ActivateRedStage()
end end
@@ -1,6 +1,7 @@
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage") local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
local GlobalCapManager = require("classes.capClasses.GlobalCapManager") local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
local StageState = require("classes.stageClasses.Stages.StageState")
---@class ExtraStage : Stage ---@class ExtraStage : Stage
local ExtraStage = {} local ExtraStage = {}
@@ -61,7 +62,7 @@ function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
self:ActivateStage() self:ActivateStage()
end end
if self._currentStageState == Stage.CurrentStageState.BLUE then if self._currentStageState == StageState.BLUE then
self:ActivateBlueStage() self:ActivateBlueStage()
end end
@@ -15,7 +15,6 @@ local logger = Logger.new("DrawingHelper", level)
local DrawingHelper = {} local DrawingHelper = {}
DrawingHelper.__index = DrawingHelper DrawingHelper.__index = DrawingHelper
---@param object DrawingObject ---@param object DrawingObject
---@return Array<integer> id ---@return Array<integer> id
function DrawingHelper.Draw(object) function DrawingHelper.Draw(object)
@@ -212,7 +212,7 @@ function MissionCommandsHelper:OverviewToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do for code, enabled in pairs(self.enabledByCode) do
if enabled == true then if enabled == true then
local mission = self.missionsByCode[code] 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) table.insert(primaryMissions, mission)
end end
end end
@@ -232,7 +232,7 @@ function MissionCommandsHelper:OverviewToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do for code, enabled in pairs(self.enabledByCode) do
if enabled == true then if enabled == true then
local mission = self.missionsByCode[code] 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) table.insert(secondaryMissions, mission)
end end
end end
@@ -347,7 +347,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do for code, enabled in pairs(self.enabledByCode) do
if enabled == true then if enabled == true then
local mission = self.missionsByCode[code] local mission = self.missionsByCode[code]
if mission and mission.priority == "primary" then if mission and mission:GetPriority() == "primary" then
table.insert(primaryMissions, mission) table.insert(primaryMissions, mission)
end end
end end
@@ -376,7 +376,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for code, enabled in pairs(self.enabledByCode) do for code, enabled in pairs(self.enabledByCode) do
if enabled == true then if enabled == true then
local mission = self.missionsByCode[code] local mission = self.missionsByCode[code]
if mission and mission.priority == "secondary" then if mission and mission:GetPriority() == "secondary" then
table.insert(secondaryMissions, mission) table.insert(secondaryMissions, mission)
end end
end end
@@ -418,7 +418,7 @@ function MissionCommandsHelper:addMissionCommands(groupId, path, mission)
end end
local missionFolderName = "[" .. local missionFolderName = "[" ..
mission.code .. "]" .. distance .. mission.name .. "( " .. mission.missionTypeDisplay .. " )" mission.code .. "]" .. distance .. mission.name .. "(" .. mission.missionTypeDisplay .. ")"
missionCommands.addSubMenuForGroup(groupId, missionFolderName, path) missionCommands.addSubMenuForGroup(groupId, missionFolderName, path)
table.insert(path, missionFolderName) table.insert(path, missionFolderName)
@@ -76,7 +76,7 @@ local SupplyConfigHelper = {}
---@return SupplyConfig? ---@return SupplyConfig?
function SupplyConfigHelper.fromObjectName(name) function SupplyConfigHelper.fromObjectName(name)
for configName, config in pairs(SupplyConfig) do 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 return config
end end
end end
@@ -21,9 +21,18 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
---@field private _dropOffZone SpearheadTriggerZone? ---@field private _dropOffZone SpearheadTriggerZone?
---@field private _noLandingZoneDrawing CustomDrawing? ---@field private _noLandingZoneDrawing CustomDrawing?
---@field private _dropOffZoneDrawing CustomDrawing? ---@field private _dropOffZoneDrawing CustomDrawing?
---@field private _briefing string?
local BuildableMission = {} local BuildableMission = {}
BuildableMission.__index = BuildableMission BuildableMission.__index = BuildableMission
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." ..
"\nThe coords are: " .. coords ..
"\n\n"
end
---@class OnCrateDroppedListener ---@class OnCrateDroppedListener
---@field OnCrateDroppedOff fun(self:OnCrateDroppedListener, mission:BuildableMission, kilos:number) ---@field OnCrateDroppedOff fun(self:OnCrateDroppedListener, mission:BuildableMission, kilos:number)
@@ -33,7 +42,8 @@ BuildableMission.__index = BuildableMission
---@param requiredCrateType SupplyType ---@param requiredCrateType SupplyType
---@param noLandingZone SpearheadTriggerZone? ---@param noLandingZone SpearheadTriggerZone?
---@param logger Logger ---@param logger Logger
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType) ---@param briefing string?
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType, briefing)
setmetatable(BuildableMission, Mission) setmetatable(BuildableMission, Mission)
@@ -43,6 +53,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self._database = database self._database = database
self._requiredKilos = requiredKilos self._requiredKilos = requiredKilos
self._droppedKilos = 0 self._droppedKilos = 0
self._briefing = briefing
self._noLandingZone = noLandingZone self._noLandingZone = noLandingZone
@@ -64,7 +75,13 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
end end
self.code = tostring(database:GetNewMissionCode()) self.code = tostring(database:GetNewMissionCode())
self.name = "Resupply"
local splitTargetZoneName = Util.split_string(targetZone.name, "_")
if splitTargetZoneName and splitTargetZoneName[3] and splitTargetZoneName[3] ~= "" then
self.name = splitTargetZoneName[3]
else
self.name = "Resupply"
end
local type = "site" local type = "site"
if requiredCrateType == "SAM_CRATE" then if requiredCrateType == "SAM_CRATE" then
@@ -81,7 +98,6 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate() self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate()
self._state = "NEW" self._state = "NEW"
self.location = targetZone.location self.location = targetZone.location
self.missionType = "LOGISTICS" self.missionType = "LOGISTICS"
@@ -116,11 +132,16 @@ function BuildableMission:ShowBriefing(groupID)
siteType = "airbase" siteType = "airbase"
end end
local briefingPart = self._briefing
if briefingPart then
briefingPart = Util.replaceString(briefingPart, "{{coords}}", coords)
else
briefingPart = getDefaultBriefing(siteType, coords)
end
local briefing = "Mission [" .. self.code .. "] " .. self.name .. local briefing = "Mission [" .. self.code .. "] " .. self.name ..
"\n \n" .. "\n \n" ..
"We've dispatched forward units to find a proper spot for a new " .. siteType .. "." .. briefingPart ..
"\nYou will need to drop off supplies so they can start building." ..
"\nThe coords are: " .. coords ..
"\n\n" .. "\n\n" ..
"\nKilos still required: " .. self._requiredKilos - self._droppedKilos .. "\nKilos still required: " .. self._requiredKilos - self._droppedKilos ..
"\n\n" .. "\n\n" ..
@@ -237,7 +258,7 @@ function BuildableMission:CheckCratesInZone()
local crates = self._supplyUnitsTracker:GetCargoCratesDropped() local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
for _, staticObject in pairs(crates) do 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() local pos = staticObject:getPoint()
if Util.is3dPointInZone(pos, self._dropOffZone) then if Util.is3dPointInZone(pos, self._dropOffZone) then
@@ -14,7 +14,7 @@ local DcsUtil = require("classes.util.DcsUtil")
---@field private _completeAtIndex number ---@field private _completeAtIndex number
---@field private _parentStage Stage ---@field private _parentStage Stage
---@field private _battleManager? BattleManager ---@field private _battleManager? BattleManager
---@field private _lastContactMarkerID number; ---@field private _lastContactMarkerID number
local ZoneMission = {} local ZoneMission = {}
--- @class MissionGroups --- @class MissionGroups
@@ -24,7 +24,7 @@ local ZoneMission = {}
--- @field unitsAlive table<string, table<string, boolean>> --- @field unitsAlive table<string, table<string, boolean>>
--- @field targetsAlive table<string, table<string, boolean>> --- @field targetsAlive table<string, table<string, boolean>>
--- @field sceneryTargets Array<SpearheadSceneryObject> --- @field sceneryTargets Array<SpearheadSceneryObject>
--- @field groupNamesPerunit table<string,string> --- @field groupNamesPerUnit table<string,string>
---@class ParsedMissionName ---@class ParsedMissionName
---@field missionName string ---@field missionName string
@@ -36,7 +36,7 @@ local ZoneMission = {}
local function ParseZoneName(input) local function ParseZoneName(input)
local split_name = Util.split_string(input, "_") local split_name = Util.split_string(input, "_")
local split_length = Util.tableLength(split_name) 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") MissionEditorWarnings.Add("Random Mission with zonename " .. input .. " not in right format")
return nil return nil
elseif split_length < 3 then elseif split_length < 3 then
@@ -53,9 +53,10 @@ local function ParseZoneName(input)
if inputType == "cas" then parsedType = "CAS" end if inputType == "cas" then parsedType = "CAS" end
if inputType == "bai" then parsedType = "BAI" end if inputType == "bai" then parsedType = "BAI" end
if inputType == "sam" then parsedType = "SAM" end if inputType == "sam" then parsedType = "SAM" end
if inputType == "deepstrike" then parsedType = "DEEPSTRIKE" end
if parsedType == "nil" then if parsedType == "nil" then
MissionEditorWarnings.Add("Mission with zonename '" .. MissionEditorWarnings.Add("Mission with zoneName '" ..
input .. "' has an unsupported type '" .. (type or "nil")) input .. "' has an unsupported type '" .. (type or "nil"))
return nil return nil
end end
@@ -66,8 +67,6 @@ local function ParseZoneName(input)
} }
end end
MINIMAL_UNITS_ALIVE_RATIO = 0.21
---comment ---comment
---@param zoneName string ---@param zoneName string
---@param priority MissionPriority ---@param priority MissionPriority
@@ -112,7 +111,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
unitsAlive = {}, unitsAlive = {},
targetsAlive = {}, targetsAlive = {},
hasTargets = false, hasTargets = false,
groupNamesPerunit = {}, groupNamesPerUnit = {},
sceneryTargets = {} sceneryTargets = {}
} }
@@ -150,17 +149,17 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true) local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._missionGroups.redGroups, spearheadGroup) 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 for _, unit in pairs(spearheadGroup:GetObjects()) do
local unitName = unit:getName() 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 if self._missionGroups.unitsAlive[groupName] == nil then
self._missionGroups.unitsAlive[groupName] = {} self._missionGroups.unitsAlive[groupName] = {}
end end
self._missionGroups.unitsAlive[groupName][unitName] = true self._missionGroups.unitsAlive[groupName][unitName] = true
self._missionGroups.groupNamesPerunit[unitName] = groupName self._missionGroups.groupNamesPerUnit[unitName] = groupName
if isGroupTarget == true or isUnitTarget == true then if isGroupTarget == true or isUnitTarget == true then
self._missionGroups.hasTargets = true self._missionGroups.hasTargets = true
@@ -180,7 +179,6 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
if self.missionType == "CAS" then if self.missionType == "CAS" then
self._battleManager = BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel) self._battleManager = BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel)
end end
self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups)) self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups))
@@ -354,6 +352,19 @@ end
function ZoneMission:SpawnInactive() function ZoneMission:SpawnInactive()
self._logger:info("PreActivating " .. self.name) 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 for _, group in pairs(self._missionGroups.redGroups) do
group:Spawn() group:Spawn()
end end
@@ -390,6 +401,17 @@ function ZoneMission:SpawnActive()
self._battleManager:Start() self._battleManager:Start()
end 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._missionCommandsHelper:AddMissionToCommands(self)
self:StartCheckingContinuous() self:StartCheckingContinuous()
@@ -480,7 +502,7 @@ function ZoneMission:OnUnitLost(object)
local unitName = object:getName() local unitName = object:getName()
self._logger:debug("UnitName:" .. unitName) self._logger:debug("UnitName:" .. unitName)
local groupName = self._missionGroups.groupNamesPerunit[unitName] local groupName = self._missionGroups.groupNamesPerUnit[unitName]
self._missionGroups.unitsAlive[groupName][unitName] = false self._missionGroups.unitsAlive[groupName][unitName] = false
if self._missionGroups.targetsAlive[groupName] and self._missionGroups.targetsAlive[groupName][unitName] then 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 zoneName string
---@field missionType MissionType ---@field missionType MissionType
---@field missionTypeDisplay string ---@field missionTypeDisplay string
---@field priority MissionPriority ---@field protected _priority MissionPriority
---@field location Vec2? ---@field location Vec2?
---@field code string ---@field code string
---@field protected _state MissionState ---@field protected _state MissionState
@@ -40,7 +40,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
self.zoneName = zoneName self.zoneName = zoneName
self.name = missionName self.name = missionName
self.missionType = missionType self.missionType = missionType
self.priority = priority self._priority = priority
self._state = "NEW" self._state = "NEW"
self._logger = logger self._logger = logger
self._database = database self._database = database
@@ -64,6 +64,11 @@ end
--region PUBLIC --region PUBLIC
---@return MissionPriority
function Mission:GetPriority()
return self._priority
end
function Mission:SpawnPersistedState() end function Mission:SpawnPersistedState() end
function Mission:SpawnActive() end function Mission:SpawnActive() end
@@ -112,7 +117,7 @@ end
function Mission:NotifyMissionComplete() function Mission:NotifyMissionComplete()
self._missionCommandsHelper:RemoveMissionToCommands(self) self._missionCommandsHelper:RemoveMissionToCommands(self)
self._logger:info("Mission Completed: " .. self.zoneName) 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 for _, listener in pairs(self._completeListeners) do
pcall(function() pcall(function()
@@ -120,7 +125,7 @@ function Mission:NotifyMissionComplete()
end) end)
end end
local succ, err = pcall(function() local success, err = pcall(function()
SpearheadAPI.Internal.notifyMissionComplete(self.zoneName) SpearheadAPI.Internal.notifyMissionComplete(self.zoneName)
end) end)
@@ -153,6 +158,7 @@ do --aliases
--- @alias MissionType --- @alias MissionType
--- | "nil" --- | "nil"
--- | "STRIKE" --- | "STRIKE"
--- | "DEEPSTRIKE"
--- | "CAS" --- | "CAS"
--- | "BAI" --- | "BAI"
--- | "DEAD" --- | "DEAD"
+2 -2
View File
@@ -644,7 +644,7 @@ do -- INIT DCS_UTIL
---@param location Vec3 ---@param location Vec3
---@return number markID ---@return number markID
function DCS_UTIL.AddMarkToGroup(groupID, text, location) function DCS_UTIL.AddMarkToGroup(groupID, text, location)
drawID = drawID + 1 local drawID = DCS_UTIL.GetNextDrawID()
trigger.action.markToGroup(drawID, text, location, groupID, true, nil) trigger.action.markToGroup(drawID, text, location, groupID, true, nil)
return drawID return drawID
end end
@@ -654,7 +654,7 @@ do -- INIT DCS_UTIL
---@param location Vec3 ---@param location Vec3
---@return integer ---@return integer
function DCS_UTIL.AddMarkToAll(text, location) function DCS_UTIL.AddMarkToAll(text, location)
drawID = drawID + 1 local drawID = DCS_UTIL.GetNextDrawID()
trigger.action.markToAll(drawID, text, location, true, nil) trigger.action.markToAll(drawID, text, location, true, nil)
return drawID return drawID
end end
+8 -8
View File
@@ -1,13 +1,13 @@
---@class Util ---@class Util
local UTIL = {} local UTIL = {}
do -- INIT UTIL do -- INIT UTIL
---splits a string in sub parts by seperator ---splits a string in sub parts by separator
---@param input string ---@param input string
---@param seperator string ---@param separator string
---@return table result list of strings ---@return table result list of strings
function UTIL.split_string(input, seperator) function UTIL.split_string(input, separator)
if seperator == nil then if separator == nil then
seperator = " " separator = " "
end end
local result = {} local result = {}
@@ -15,7 +15,7 @@ do -- INIT UTIL
return result return result
end end
for str in string.gmatch(input, "[^" .. seperator .. "]+") do for str in string.gmatch(input, "[^" .. separator .. "]+") do
table.insert(result, str) table.insert(result, str)
end end
return result return result
@@ -120,7 +120,7 @@ do -- INIT UTIL
---@param findable string ---@param findable string
---@param ignoreCase boolean? ---@param ignoreCase boolean?
---@return boolean ---@return boolean
UTIL.startswith = function(str, findable, ignoreCase) UTIL.startsWith = function(str, findable, ignoreCase)
if ignoreCase == true then if ignoreCase == true then
return string.lower(str):find('^' .. string.lower(findable)) ~= nil return string.lower(str):find('^' .. string.lower(findable)) ~= nil
end end
@@ -150,7 +150,7 @@ do -- INIT UTIL
---@return boolean ---@return boolean
UTIL.startswithAny = function(str, findableTable) UTIL.startswithAny = function(str, findableTable)
for key, value in pairs(findableTable) do for key, value in pairs(findableTable) do
if type(value) == "string" and UTIL.startswith(str, value) then return true end if type(value) == "string" and UTIL.startsWith(str, value) then return true end
end end
return false return false
end end