Feature/deep strike (#48)
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:
@@ -169,7 +169,7 @@ class Sidebar extends HTMLElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
var betaHtml = '';
|
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>';
|
betaHtml = '<note-box type="warning" title="Beta Version">You are looking at docs that are released with the beta version.</note-box>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>© 2025 Spearhead Project</p>
|
<p>© 2025 Spearhead Project</p>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
|
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
|
||||||
#44
|
#44
|
||||||
|
|
||||||
|
- Issue: #42
|
||||||
|
DEEPSTRIKE now added as a possible feature.
|
||||||
|
This enables to have missions active in "Pre-Activated" Stages.
|
||||||
|
#48
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
## [0.13.0] 2026-08
|
## [0.13.0] 2026-08
|
||||||
|
|||||||
@@ -78,7 +78,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)
|
||||||
|
|||||||
@@ -150,7 +150,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)
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ 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>
|
||||||
@@ -211,7 +212,7 @@ function Database.New(Logger)
|
|||||||
for _, layer in pairs(env.mission.drawings.layers) do
|
for _, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for _, layer_object in pairs(layer.objects) do
|
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)
|
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)
|
||||||
@@ -234,7 +235,7 @@ function Database.New(Logger)
|
|||||||
for _, layer in pairs(env.mission.drawings.layers) do
|
for _, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for _, layer_object in pairs(layer.objects) do
|
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 object = layer_object --[[@as DrawingObject]]
|
||||||
local stageDrawing = StageDrawing.New(object)
|
local stageDrawing = StageDrawing.New(object)
|
||||||
if stageDrawing then
|
if stageDrawing then
|
||||||
@@ -262,7 +263,7 @@ function Database.New(Logger)
|
|||||||
for _, layer in pairs(env.mission.drawings.layers) do
|
for _, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for _, layer_object in pairs(layer.objects) do
|
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 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
|
||||||
@@ -491,7 +492,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
|
||||||
@@ -594,11 +595,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
|
||||||
@@ -621,7 +622,7 @@ 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
|
||||||
@@ -633,7 +634,7 @@ function Database:loadBlueSamUnits()
|
|||||||
|
|
||||||
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
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.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]]
|
layer_object = layer_object --[[@as TextBox]]
|
||||||
local description = layer_object.text
|
local description = layer_object.text
|
||||||
if description and description ~= "" then
|
if description and description ~= "" then
|
||||||
@@ -710,9 +711,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
|
||||||
@@ -722,6 +723,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
|
||||||
@@ -734,7 +743,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 layer_object.primitiveType == "TextBox" and triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
|
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]]
|
layer_object = layer_object --[[@as TextBox]]
|
||||||
local description = layer_object.text
|
local description = layer_object.text
|
||||||
if description and description ~= "" then
|
if description and description ~= "" then
|
||||||
@@ -778,7 +787,7 @@ 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
|
||||||
@@ -792,7 +801,7 @@ function Database:loadFarpData()
|
|||||||
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
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.primitiveType == "TextBox" and triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
|
||||||
layer_object = layer_object --[[@as TextBox]]
|
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
|
local description = layer_object.text
|
||||||
if description and description ~= "" then
|
if description and description ~= "" then
|
||||||
farpzoneData.briefing = description
|
farpzoneData.briefing = description
|
||||||
|
|||||||
@@ -435,6 +435,12 @@ function Stage:PreActivate()
|
|||||||
end
|
end
|
||||||
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
|
for _, airbase in pairs(self._db.airbases) do
|
||||||
airbase:ActivateRedStage()
|
airbase:ActivateRedStage()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -210,7 +210,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
|
||||||
@@ -230,7 +230,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
|
||||||
@@ -345,7 +345,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
|
||||||
@@ -374,7 +374,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
|
||||||
@@ -416,7 +416,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
|
||||||
|
|||||||
@@ -259,7 +259,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
|
||||||
@@ -110,7 +111,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
|
|||||||
unitsAlive = {},
|
unitsAlive = {},
|
||||||
targetsAlive = {},
|
targetsAlive = {},
|
||||||
hasTargets = false,
|
hasTargets = false,
|
||||||
groupNamesPerunit = {},
|
groupNamesPerUnit = {},
|
||||||
sceneryTargets = {}
|
sceneryTargets = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,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
|
||||||
@@ -178,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))
|
||||||
@@ -353,6 +353,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
|
||||||
@@ -389,6 +402,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 +504,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
|
||||||
|
|
||||||
@@ -114,7 +119,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()
|
||||||
@@ -155,13 +160,14 @@ function Mission:ToStateString() return "status: in progress" end
|
|||||||
--- @alias MissionType
|
--- @alias MissionType
|
||||||
--- | "nil"
|
--- | "nil"
|
||||||
--- | "STRIKE"
|
--- | "STRIKE"
|
||||||
|
--- | "DEEPSTRIKE"
|
||||||
--- | "CAS"
|
--- | "CAS"
|
||||||
--- | "BAI"
|
--- | "BAI"
|
||||||
--- | "DEAD"
|
--- | "DEAD"
|
||||||
--- | "SAM"
|
--- | "SAM"
|
||||||
--- | "OCA"
|
--- | "OCA"
|
||||||
--- | "LOGISTICS"
|
--- | "LOGISTICS"
|
||||||
|
---
|
||||||
--- @alias MissionState
|
--- @alias MissionState
|
||||||
--- | "NEW"
|
--- | "NEW"
|
||||||
--- | "WAITING"
|
--- | "WAITING"
|
||||||
|
|||||||
@@ -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 Array<string> result list of strings
|
---@return Array<string> 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
|
||||||
@@ -134,7 +134,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
|
||||||
@@ -164,7 +164,7 @@ do -- INIT UTIL
|
|||||||
---@return boolean
|
---@return boolean
|
||||||
UTIL.startswithAny = function(str, findableTable)
|
UTIL.startswithAny = function(str, findableTable)
|
||||||
for _, value in pairs(findableTable) do
|
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
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user