Author SHA1 Message Date
dutchie031 c4683fb80c updated CI pipeline
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 4s
PR Prerequisites / Lua Check (pull_request) Failing after 11s
2026-09-10 17:11:54 +02:00
dutchie031 60a4d6e88c Merge branch 'develop' into CI 2026-09-09 22:09:07 +02:00
dutchie031 9b2540d461 Fixed an issue
Publish Release / build (push) Successful in 30s
2026-09-09 22:06:29 +02:00
dutchie031 a351c45b73 wip
PR Prerequisites / Lua Check (pull_request) Failing after 14s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 28s
2026-09-09 22:04:15 +02:00
dutchie031 e081b8e446 updated luals version
PR Prerequisites / Lua Check (pull_request) Successful in 10s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 30s
2026-09-09 15:55:11 +02:00
dutchie031 4b3d87f589 updated luals version
PR Prerequisites / Lua Check (pull_request) Failing after 10s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 24s
2026-09-09 15:49:32 +02:00
dutchie031 1fde8ec3b1 updated luals version 2026-09-09 15:49:22 +02:00
dutchie031 68e8d1c9ac updated luals version
PR Prerequisites / Lua Check (pull_request) Failing after 12s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 23s
2026-09-09 15:49:05 +02:00
dutchie031 ccd38078f3 updated luals version
PR Prerequisites / Lua Check (pull_request) Failing after 11s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 24s
2026-09-09 15:47:50 +02:00
dutchie031 de1e6ffae9 updated luals version
PR Prerequisites / Lua Check (pull_request) Failing after 11s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 29s
2026-09-09 15:37:39 +02:00
dutchie031 2a76df1e21 updated
PR Prerequisites / Lua Check (pull_request) Failing after 13s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 27s
2026-09-09 15:36:02 +02:00
dutchie031 a55cd2bf2a trying other check
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 24s
PR Prerequisites / Lua Check (pull_request) Failing after 1m21s
2026-09-09 12:36:29 +02:00
dutchie031 d16e25531a non important trailing whitespace ignored
PR Prerequisites / Lua Check (pull_request) Failing after 24s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 23s
2026-09-09 11:27:23 +02:00
dutchie031 30c7124a86 non important trailing whitespace ignored
PR Prerequisites / Lua Check (pull_request) Failing after 24s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 26s
2026-09-09 11:25:23 +02:00
dutchie031 514a9a9c55 non important trailing whitespace ignored
PR Prerequisites / Lua Check (pull_request) Failing after 24s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 24s
2026-09-09 11:24:09 +02:00
dutchie031 96fab864c3 non important trailing whitespace ignored
PR Prerequisites / Lua Check (pull_request) Failing after 21s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 23s
2026-09-09 11:22:21 +02:00
dutchie031 0346f8a7a8 linter fixes
PR Prerequisites / Lua Check (pull_request) Failing after 20s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 28s
2026-09-09 11:12:58 +02:00
dutchie031 ba240db310 fixing linting
PR Prerequisites / Lua Check (pull_request) Failing after 22s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 22s
2026-09-08 21:04:40 +02:00
dutchie031 50f2a0eea0 added CI for all source files
PR Prerequisites / Lua Check (pull_request) Failing after 20s
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 24s
2026-09-08 17:35:21 +02:00
dutchie031 70d18f4ac3 updated luacheck rc
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 24s
2026-09-08 17:07:41 +02:00
dutchie031 341590e981 fixed minor bug
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 49s
2026-09-08 16:54:00 +02:00
dutchie031 233310209b changed checkout version
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 39s
2026-09-08 16:44:52 +02:00
dutchie031 2cae676a10 changed checkout version
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 6s
2026-09-08 16:41:35 +02:00
dutchie031 de81fdf419 added lua check CI step
PR Prerequisites / Bundles Without Errors (pull_request) Failing after 8s
2026-09-08 16:40:04 +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
59 changed files with 658 additions and 651 deletions
+1 -1
View File
@@ -64,7 +64,7 @@
</footer>
<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>';
}
document.getElementById('beta-box-div').innerHTML = betaHtml || '';
@@ -50,17 +50,6 @@ class DownloadScript extends HTMLElement {
}
}
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() {
var version = "?";
@@ -72,10 +61,9 @@ class DownloadScript extends HTMLElement {
if (latestRelease) {
version = latestRelease.tag_name;
timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA');
const assets = await this.fetchReleaseAssets(latestRelease.assets_url);
const assets = latestRelease.assets;
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) {
size = (spearheadAsset.size / 1024).toFixed(2) + 'kb';
href = spearheadAsset.browser_download_url;
@@ -105,7 +93,7 @@ class DownloadScript extends HTMLElement {
<p>
<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="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>
</div>
+5 -2
View File
@@ -169,13 +169,16 @@ class Sidebar extends HTMLElement {
render() {
var betaHtml = '';
if (window.location.hostname.toLowerCase() == 'beta.spearhead.rocks') {
if (true || 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>';
}
this.innerHTML = `
<div class="side-nav">
${betaHtml}
<div style="width:100%">
${betaHtml}
</div>
<h4 class="side-nav-title"></h4>
<ul>
<!-- Navigation items will be populated automatically -->
+16 -7
View File
@@ -80,17 +80,12 @@
</tbody>
</table>
<note-box type="info" title="Combining Tasks">
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
rename them accordingly. <br>
</note-box>
<note-box type="info" title="Airfield Defenses">
</note-box>
<h3 id="cap-zones-ref">Zones</h3>
<table class="table-no-style">
<tbody>
@@ -99,6 +94,7 @@
<td>=></td>
<td>A Cap ROUTE. The two most outer points are used to define the fly-over points.</td>
</tr>
<tr><td>______________________</td><td></td><td></td> </tr>
<tr>
<td><code-inline>INTERCEPTZONE_[ZoneID]_[NAME]</code-inline></td>
<td>=></td>
@@ -112,6 +108,19 @@
</p>
<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>
<p>
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>
<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 these waypoints and taskings will break the management by Spearhead.
</note-box>
@@ -154,7 +163,7 @@
The way the trigger zone is mapped to a route is as follows: <br>
<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>
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>
+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/>
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>
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/>
+1
View File
@@ -337,6 +337,7 @@ main {
.side-nav {
min-width: 200px;
max-width: 200px;
padding-left: 0;
overflow-y: auto;
border-right: none;
+76
View File
@@ -0,0 +1,76 @@
name: PR Prerequisites
on:
pull_request:
branches:
- main
- develop
env:
LUA_VERSION: "5.1"
LUA_LANGUAGE_SERVER_VERSION: "3.19.1"
jobs:
bundles_without_error:
runs-on: ubuntu-latest
name: Bundles Without Errors
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Lua
uses: leafo/gh-actions-lua@v13
with:
luaVersion: ${{ env.LUA_VERSION }}
- name: Compile Spearhead
uses: https://git.dutchie031.com/dutchie031/DcsMissionScriptingTools/github-action@action/v0
with:
source-root: ./src
output-file: ./output/spearhead.lua
- name: Verify Compiled Lua Syntax
run: lua -e "assert(loadfile('./output/spearhead.lua'))"
- name: Set up Luarocks
uses: leafo/gh-actions-luarocks@v4
- name: Install luacheck
run: luarocks install luacheck
- name: Lint Compiled Output
run: luacheck ./output/spearhead.lua
lua_check:
runs-on: ubuntu-latest
name: Lua Check
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Lua
uses: leafo/gh-actions-lua@v13
with:
luaVersion: ${{ env.LUA_VERSION }}
- name: Install Dcs Plugins
uses: https://git.dutchie031.com/dutchie031/DcsMissionScriptingTools/github-actions/install-lua-addon@install-lua-addon/v1
with:
destination-path: "./.vscode/lua-addons" ##same as local so .luarc.json can be the same
- name: Install lua-language-server
run: |
mkdir -p ./luals
# For Ubuntu/Debian
wget https://github.com/LuaLS/lua-language-server/releases/download/${{ env.LUA_LANGUAGE_SERVER_VERSION }}/lua-language-server-${{ env.LUA_LANGUAGE_SERVER_VERSION }}-linux-x64.tar.gz
tar -xzf lua-language-server-*.tar.gz -C ./luals
rm lua-language-server-*.tar.gz
- name: Verify Lua Language Server Installation
run: ./luals/bin/lua-language-server --version
- name: Run Lua Language Server
run: ./luals/bin/lua-language-server --check ./src --checklevel=Warning
+1
View File
@@ -3,5 +3,6 @@
/dist
.vscode/lua-addons
.vscode/settings.json
**\settings.json
+40
View File
@@ -0,0 +1,40 @@
{
"Lua": {
"runtime": {
"version": "Lua 5.1"
},
"workspace": {
"checkThirdParty": false
},
"diagnostics": {
"enable": true,
"globals": [
"lfs"
],
"severity": {
"undefined-field": "Warning",
"unused-local": "Warning",
"unused-function": "Warning"
},
"groupFileStatus": {
"ambiguity": "Any",
"await": "Any",
"duplicate": "Any",
"global": "Any",
"luadoc": "Any",
"redefined": "Any",
"strict": "Any",
"type-check": "Any",
"unbalanced": "Any",
"unused": "Any"
}
},
"hint": {
"enable": false
},
"completion": {
"enable": true
}
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
{
"recommendations": [
"sumneko.lua"
"dutchie031.dutchies-dcs-scripting-tools",
"sumneko.lua",
]
}
+4
View File
@@ -15,6 +15,10 @@
These lanes can be used to create different stage progression flows and create different "side-stories".
#40
- Issue: #37
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
#44
### Bug Fixes
## [0.13.0] 2026-08
-73
View File
@@ -1,73 +0,0 @@
--assert(loadfile("C:\\Repos\\DCS\\Spearhead\\dev\\dev_classes.lua"))()
local basePath = "C:\\Repos\\DCS\\Spearhead\\"
local classPath = basePath .. "classes\\"
assert(loadfile(classPath .. "_baseClasses\\Queue.lua"))()
assert(loadfile(classPath .. "spearhead_base.lua"))()
assert(loadfile(classPath .. "spearhead_routeutil.lua"))()
assert(loadfile(classPath .. "spearhead_events.lua"))()
assert(loadfile(classPath .. "spearhead_db.lua"))()
assert(loadfile(classPath .. "fleetClasses\\FleetGroup.lua"))()
assert(loadfile(classPath .. "fleetClasses\\GlobalFleetManager.lua"))()
assert(loadfile(classPath .. "helpers\\MizGroupsManager.lua"))()
assert(loadfile(classPath .. "helpers\\SpawnManager.lua"))()
assert(loadfile(classPath .. "configuration\\CapConfig.lua"))()
assert(loadfile(classPath .. "configuration\\StageConfig.lua"))()
assert(loadfile(classPath .. "stageClasses\\GlobalStageManager.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\baseMissions\\Mission.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\ZoneMission.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\RunwayStrikeMission.lua"))()
assert(loadfile(classPath .. "stageClasses\\missions\\BuildableMission.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\BaseStage\\Stage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\PrimaryStage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\ExtraStage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Stages\\WaitingStage.lua"))()
assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadGroup.lua"))()
assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadSceneryObject.lua"))()
assert(loadfile(classPath .. "stageClasses\\helpers\\MissionCommandsHelper.lua"))()
assert(loadfile(classPath .. "stageClasses\\helpers\\SupplyConfig.lua"))()
assert(loadfile(classPath .. "stageClasses\\helpers\\SupplyUnitsTracker.lua"))()
assert(loadfile(classPath .. "stageClasses\\helpers\\BattleManager.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\abstract\\BuildableZone.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\StageBase.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\BlueSam.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\FarpZone.lua"))()
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\SupplyHub.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\RTB.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\CAP.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\SWEEP.lua"))()
assert(loadfile(classPath .. "capClasses\\taskings\\INTERCEPT.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\AirGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\CapGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\SweepGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\airGroups\\InterceptGroup.lua"))()
assert(loadfile(classPath .. "capClasses\\detection\\DetectionManager.lua"))()
assert(loadfile(classPath .. "capClasses\\GlobalCapManager.lua"))()
assert(loadfile(classPath .. "capClasses\\CapAirbase.lua"))()
assert(loadfile(classPath .. "capClasses\\runwayBombing\\RunwayBombingTracker.lua"))()
assert(loadfile(classPath .. "persistence\\Persistence.lua"))()
-- Startup:
assert(loadfile(basePath .. "main.lua"))()
-79
View File
@@ -1,79 +0,0 @@
SpearheadConfig = {
debugEnabled = true,
CapConfig = {
--quickly enable of disable the entire CAP Logic
--(you can also just rename all units to not be named "CAP_")
enabled = true, -- default true
--min ground speed for CAP aircraft during patrol
-- unit: knots
minSpeed = 400, -- default 400
--max speed for CAP aircraft during patrol
-- unit: knots
maxSpeed = 500, -- default 500
--minAlt for aircraft on patrol
-- unit: feet
minAlt = 18000, -- default 18000
--maxAlt for aircraft on patrol
-- unit: feet
maxAlt = 28000, -- default 28000
--Delay for aircraft from touchdown to off the chocks.
-- unit: seconds
rearmDelay = 180, -- default 600
repairDelay = 600, -- default 600
--Delay for aircraft from death to takeoff.
--When the seconds remaining is the same at the rearmDelay it will be spawned on the ramp and follow the rearm logic.
-- !! Can not be lower than rearmDelay
-- unit: seconds
deathDelay = 1800, -- default 1800
},
StageConfig = {
-- management of stages and its missions.
-- This is not related to CAP managers which will continue to work even if stage management is disabled
enabled = true, -- default true
--Will draw the active and the next stage
drawStages = true, -- default true
drawPreActivated = true,
markLastContact = true,
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
--[[
TODO: Add manual stage transition documentation
]]
autoStages = true, --default true
--Maximum missions per stage (includes all types of missions)
maxMissionStage = 100,
--Stage starting number
startingStage = 1,
---DEBUG logging. Consider keeping this disabled
debugEnabled = true
},
Persistence = {
--- io and lfs cannot be sanitized in the MissionScripting.lua
--- enables or disables the persistence logic in spearhead
enabled = true,
--- sets the directory where the persistence file is stored
--- if nil then lfs.writedir() will be used.
--- which will
directory = nil ,
--- the filename of the persistence file. Should end with .json for convention, but any text extension should do.
fileName = "Spearhead_Persistence_Dev"
}
}
+11 -15
View File
@@ -2,7 +2,6 @@ local Util = require("classes.util.Util")
local CapGroup = require("classes.capClasses.airGroups.CapGroup")
local SweepGroup = require("classes.capClasses.airGroups.SweepGroup")
local InterceptGroup = require("classes.capClasses.airGroups.InterceptGroup")
local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker")
local SpearheadEvents = require("classes.spearhead_events")
local RunwayStrikeMission = require("classes.stageClasses.missions.RunwayStrikeMission")
@@ -37,12 +36,11 @@ end
---@param database Database
---@param logger table
---@param capConfig table
---@param stageConfig table
---@param runwayBombingTracker RunwayBombingTracker
---@param detectionManager DetectionManager
---@param spawnManager SpawnManager
---@return CapBase
function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager)
function CapBase.new(airbaseName, database, logger, capConfig, runwayBombingTracker, detectionManager, spawnManager)
CapBase.__index = CapBase
local self = setmetatable({}, { __index = CapBase }) --[[@as CapBase]]
@@ -61,7 +59,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
local baseData = database:getAirbaseDataForZone(airbaseName)
if baseData and baseData.CapGroups then
for key, name in pairs(baseData.CapGroups) do
for _, name in pairs(baseData.CapGroups) do
local capGroup = CapGroup.New(name, capConfig, logger, spawnManager)
if capGroup then
self.capGroupsByName[name] = capGroup
@@ -70,7 +68,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
end
if baseData and baseData.SweepGroups then
for key, name in pairs(baseData.SweepGroups) do
for _, name in pairs(baseData.SweepGroups) do
local sweepGroup = SweepGroup.New(name, capConfig, logger, spawnManager)
if sweepGroup then
self.sweepGroupsByName[name] = sweepGroup
@@ -79,7 +77,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
end
if baseData and baseData.InterceptGroups then
for key, name in pairs(baseData.InterceptGroups) do
for _, name in pairs(baseData.InterceptGroups) do
local interceptGroup = InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager)
if interceptGroup then
self.interceptGroupsByName[name] = interceptGroup
@@ -98,7 +96,6 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15)
return self
end
@@ -127,7 +124,7 @@ end
function CapBase:SpawnIfApplicable()
self.logger:debug("Check spawns for airbase " .. self.airbaseName)
for groupName, capGroup in pairs(self.capGroupsByName) do
for _, capGroup in pairs(self.capGroupsByName) do
local targetStage = capGroup:GetZoneIDWhenStageID(tostring(self.activeStage))
if targetStage ~= nil and capGroup:GetState() == "UnSpawned" then
@@ -135,7 +132,7 @@ function CapBase:SpawnIfApplicable()
end
end
for groupName, sweepGroup in pairs(self.sweepGroupsByName) do
for _, sweepGroup in pairs(self.sweepGroupsByName) do
local targetStage = sweepGroup:GetZoneIDWhenStageID(tostring(self.activeStage))
if targetStage ~= nil and sweepGroup:GetState() == "UnSpawned" then
@@ -143,7 +140,7 @@ function CapBase:SpawnIfApplicable()
end
end
for groupName, interceptGroup in pairs(self.interceptGroupsByName) do
for _, interceptGroup in pairs(self.interceptGroupsByName) do
local targetStage = interceptGroup:GetZoneIDWhenStageID(tostring(self.activeStage))
if targetStage ~= nil and interceptGroup:GetState() == "UnSpawned" then
@@ -154,8 +151,9 @@ end
function CapBase:CheckAndScheduleCAP()
self.logger:debug("Check taskings for airbase " .. self.airbaseName)
---@type table<string, number>
local countPerStage = {}
---@type table<string, number>
local requiredPerStage = {}
local airbase = Airbase.getByName(self.airbaseName)
@@ -312,8 +310,7 @@ function CapBase:CheckAndScheduleIntercept()
self.logger:debug("Check intercept taskings for airbase " .. self.airbaseName)
local interceptZoneIDs = {}
---@type table<string, boolean>
local interceptZoneIDs = {}
local airbase = Airbase.getByName(self.airbaseName)
@@ -321,7 +318,7 @@ function CapBase:CheckAndScheduleIntercept()
return nil
end
for name, group in pairs(self.interceptGroupsByName) do
for _, group in pairs(self.interceptGroupsByName) do
local targetZoneID = group:GetZoneIDWhenStageID(tostring(self.activeStage))
if targetZoneID then
interceptZoneIDs[targetZoneID] = true
@@ -379,7 +376,6 @@ function CapBase:CheckAndScheduleIntercept()
if total < required then
for _, group in pairs(self.interceptGroupsByName) do
if total < required then
local zoneID = group:GetZoneIDWhenStageID(tostring(self.activeStage))
if group:GetState() == "ReadyOnTheRamp" then
group:SendToInterceptUnits(targets, name, airbase)
total = total + 1
+3 -8
View File
@@ -8,19 +8,15 @@ local GlobalCapManager = {}
do
local airbasesPerStage = {}
local allAirbasesByName = {}
local activeAirbasesPerActiveStage = {}
local unitsPerzonePerStage = {}
local initiated = false
---comment
---@param database Database
---@param capConfig table
---@param stageConfig StageConfig
---@param detectionManager DetectionManager
---@param logLevel LogLevel
---@param spawnManager SpawnManager
function GlobalCapManager.start(database, capConfig, detectionManager, stageConfig, logLevel, spawnManager)
function GlobalCapManager.start(database, capConfig, detectionManager, logLevel, spawnManager)
if initiated == true then return end
local logger = Logger.new("AirbaseManager", logLevel)
@@ -29,7 +25,7 @@ do
local zones = database:getStagezoneNames()
if zones then
for key, stageName in pairs(zones) do
for _, stageName in pairs(zones) do
if airbasesPerStage[stageName] == nil then
airbasesPerStage[stageName] = {}
end
@@ -40,8 +36,7 @@ do
if airbaseName then
local airbaseSpecificLogger = Logger.new("CAP_" .. airbaseName, logLevel)
local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager)
local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, runwayBombingTracker, detectionManager, spawnManager)
if airbase then
table.insert(airbasesPerStage[stageName], airbase)
allAirbasesByName[airbaseName] = airbase
@@ -1,7 +1,6 @@
local SpearheadEvents = require("classes.spearhead_events")
local RTBMission = require("classes.capClasses.taskings.RTB")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local GlobalConfig = require("classes.configuration.GlobalConfig")
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
@@ -81,7 +80,7 @@ function AirGroup:SetMission(mission)
end
end
local setMissionDelayed = function(data, time)
local setMissionDelayed = function(data, _)
data.self:SetMissionPrivate(data.mission)
end
@@ -357,7 +356,7 @@ do --- RESPAWN FUNCTIONS
self:SetState("Dead")
---@param selfA AirGroup
local respawnTask = function(selfA, time)
local respawnTask = function(selfA, _)
selfA:StartRepair()
end
@@ -376,8 +375,7 @@ do --- RESPAWN FUNCTIONS
end
---comment
---@param selfA AirGroup
local rearmTask = function(selfA, time)
local rearmTask = function(_, _)
self:StartRearm()
end
@@ -402,7 +400,7 @@ do --- RESPAWN FUNCTIONS
end
---@param selfA AirGroup
local rearmTask = function(selfA, time)
local rearmTask = function(selfA, _)
selfA:MarkRearmComplete()
end
@@ -415,6 +413,7 @@ end
do --EVENT LISTENERS
---@param unit Unit
function AirGroup:OnUnitLost(unit)
if unit == nil then return end
self:CheckLiveness()
end
@@ -434,7 +433,7 @@ do --EVENT LISTENERS
end
end
function AirGroup:OnUnitLanded(unit, airbase)
function AirGroup:OnUnitLanded(_, _)
local anyInAir = false
local group = Group.getByName(self._groupName)
if group then
@@ -107,13 +107,13 @@ function CapGroup:InitWithName(groupName)
local subsplit = Util.split_string(configPart, "|")
if subsplit then
for key, value in pairs(subsplit) do
for _, value in pairs(subsplit) do
local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do
local dashSeperated = Util.split_string(value, "-")
for _, childValue in pairs(commaSeperated) do
local dashSeperated = Util.split_string(childValue, "-")
if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2])
@@ -213,8 +213,6 @@ function InterceptGroup:UpdateTask()
self._currentTargetName = nil
end
local speed = self._config:getMaxSpeed()
local interceptPoint = self:GetInterceptPoint(groupPoint, speed, closestUnit)
@@ -323,13 +321,13 @@ function InterceptGroup:InitWithName(groupName)
configPart = string.sub(configPart, 2, #configPart)
local subsplit = Util.split_string(configPart, "|")
if subsplit then
for key, value in pairs(subsplit) do
for _, value in pairs(subsplit) do
local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do
local dashSeperated = Util.split_string(value, "-")
for _, childValue in pairs(commaSeperated) do
local dashSeperated = Util.split_string(childValue, "-")
if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2])
@@ -41,7 +41,7 @@ end
---@field self SweepGroup
---@param params SetTaskParams
local setMissionDelayedTask = function(params, time)
local setMissionDelayedTask = function(params, _)
params.self:SetMissionPrivate(params.task)
end
@@ -50,8 +50,6 @@ function SweepGroup:SendToZone(zone, targetZoneID, airbase)
self._currentTargetZoneID = targetZoneID
local group = Group.getByName(self._groupName)
local mission = SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
if mission then
---@type SetTaskParams
@@ -82,13 +80,13 @@ function SweepGroup:InitWithName(groupName)
local subsplit = Util.split_string(configPart, "|")
if subsplit then
for key, value in pairs(subsplit) do
for _, value in pairs(subsplit) do
local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do
local dashSeperated = Util.split_string(value, "-")
for _, childValue in pairs(commaSeperated) do
local dashSeperated = Util.split_string(childValue, "-")
if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2])
@@ -38,7 +38,6 @@ function DetectionManager.New(logger)
end
timer.scheduleFunction(updateDetected, self, timer.getTime() + 130)
return self
end
@@ -21,7 +21,7 @@ end
---comment
---@param weapon Weapon
function RunwayBombingTracker:OnWeaponFired(unit, weapon, target)
function RunwayBombingTracker:OnWeaponFired(_, weapon, _)
if weapon == nil then
return
@@ -83,11 +83,7 @@ function RunwayBombingTracker.trackWeaponTask(weaponTrackingArgs, time)
x = pos.x + velocity.x * nextInterval,
y = pos.z + velocity.z * nextInterval
}
self:OnWeaponImpact(weapon:getDesc(), impactPoint)
return nil
end
@@ -109,7 +105,7 @@ function RunwayBombingTracker:OnWeaponImpact(weaponDesc, impactPoint)
local warhead = weaponDesc.warhead
local explosiveMass = warhead.explosiveMass or warhead.shapedExplosiveMass
for runway, strikeMission in pairs(self.trackedRunways) do
for _, strikeMission in pairs(self.trackedRunways) do
local zone= strikeMission:GetRunwayZone()
-3
View File
@@ -33,9 +33,6 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
local furthestA = nil
local furthestB = nil
local furthestFromBase = nil
local furthestFromBaseDistance = 0
local furthestDistance = 0
for indexA, pointA in ipairs(capZone.verts) do
@@ -1,8 +1,4 @@
local briefingMessageTime = nil
---@class GlobalConfig
---@field private _briefingTime number
---@field private _debugEnabled boolean
-1
View File
@@ -1,5 +1,4 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local StageRepository = require("classes.stageClasses.StageRepository")
---@class DebugMenu
+48 -42
View File
@@ -4,37 +4,43 @@ local MissionEditorWarning = require("classes.util.MissionEditorWarnings")
local RouteUtil = require("classes.spearhead_routeutil")
local SpearheadEvents = require("classes.spearhead_events")
---@class FleetGroup
---@class FleetGroup : OnStageChangedListener
---@field private fleetGroupName string
---@field private logger Logger
---@field private fleetNameIdentifier string
---@field private targetZonePerStage table<string, string>
---@field private currentTargetZone string
---@field private pointsPerZone table<string, {pointA: Vec2, pointB: Vec2}>
local FleetGroup = {}
FleetGroup.__index = FleetGroup
---comment
---@param fleetGroupName string
---@param database Database
---@param logger Logger
---@return nil
function FleetGroup:new(fleetGroupName, database, logger)
local o = {}
---@return FleetGroup?
function FleetGroup.new(fleetGroupName, database, logger)
setmetatable(o, { __index = self })
local self = setmetatable({}, FleetGroup)
o.fleetGroupName = fleetGroupName
o.logger = logger
self.fleetGroupName = fleetGroupName
self.logger = logger
local split_name = Util.split_string(fleetGroupName, "_")
if Util.tableLength(split_name) < 2 then
MissionEditorWarning.Add("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_<fleetname>")
return nil
end
o.fleetNameIdentifier = split_name[2]
self.fleetNameIdentifier = split_name[2]
o.targetZonePerStage = {}
o.currentTargetZone = nil
o.pointsPerZone = {}
self.targetZonePerStage = {}
self.currentTargetZone = nil
self.pointsPerZone = {}
do --INIT
local carrierRouteZones = database:getCarrierRouteZones()
for _, zoneName in pairs(carrierRouteZones) do
if Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then
if Util.strContains(string.lower(zoneName), "_".. string.lower(self.fleetNameIdentifier) .. "_" ) == true then
local zone = DcsUtil.getZoneByName(zoneName)
if zone and zone.zone_type == DcsUtil.ZoneType.Polygon then
local split_string = Util.split_string(zoneName, "_")
@@ -43,18 +49,18 @@ function FleetGroup:new(fleetGroupName, database, logger)
"CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName)
else
---@param zone SpearheadTriggerZone
---@param zoneA SpearheadTriggerZone
---@return Vec2, Vec2
local function GetTwoFurthestPoints(zone)
local function GetTwoFurthestPoints(zoneA)
local biggest = nil
local biggestA = zone.verts[1]
local biggestB = zone.verts[2]
local biggestA = zoneA.verts[1]
local biggestB = zoneA.verts[2]
for i = 1, 3 do
for ii = i + 1, 4 do
local a = zone.verts[i]
local b = zone.verts[ii]
local a = zoneA.verts[i]
local b = zoneA.verts[ii]
local dist = Util.VectorDistance2d(a, b)
if biggest == nil or dist > biggest then
@@ -100,9 +106,9 @@ function FleetGroup:new(fleetGroupName, database, logger)
local first, second = getMinMaxStage(split_string[3])
if first ~= nil and second ~= nil then
for i = first, second do
o.targetZonePerStage[tostring(i)] = zoneName
self.targetZonePerStage[tostring(i)] = zoneName
end
o.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} }
self.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} }
else
MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
end
@@ -114,32 +120,32 @@ function FleetGroup:new(fleetGroupName, database, logger)
end
end
local SetTaskAsync = function(input, time)
local targetZone = input.targetZone
local task = input.task
local groupName = input.groupName
local logger = input.logger
SpearheadEvents.AddStageNumberChangedListener(self)
return self
end
local group = Group.getByName(groupName)
if group then
logger:info("Sending " .. groupName .. " to " .. targetZone)
group:getController():setTask(task)
end
local SetTaskAsync = function(input, _)
local targetZone = input.targetZone
local task = input.task
local groupName = input.groupName
local l_logger = input.logger
local group = Group.getByName(groupName)
if group then
l_logger:info("Sending " .. groupName .. " to " .. targetZone)
group:getController():setTask(task)
end
end
o.OnStageNumberChanged = function(self, number, laneIdentifier)
-- only react on "main" lane changes, ignore other lanes for now
if laneIdentifier ~= nil then return end
local targetZone = self.targetZonePerStage[tostring(number)]
if targetZone and targetZone ~= self.currentTargetZone then
local points = self.pointsPerZone[targetZone]
local task = RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB)
timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5)
end
function FleetGroup:OnStageNumberChanged(number, laneIdentifier)
-- only react on "main" lane changes, ignore other lanes for now
if laneIdentifier ~= nil then return end
local targetZone = self.targetZonePerStage[tostring(number)]
if targetZone and targetZone ~= self.currentTargetZone then
local points = self.pointsPerZone[targetZone]
local task = RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB)
timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5)
end
SpearheadEvents.AddStageNumberChangedListener(o)
return o
end
return FleetGroup
@@ -17,7 +17,7 @@ GlobalFleetManager.start = function(database)
for _, groupName in pairs(all_groups) do
if Util.startswith(string.lower(groupName), "carriergroup" ) == true then
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)
end
end
+3 -4
View File
@@ -15,14 +15,13 @@ MizGroupsManager._groupNames = {}
MizGroupsManager._spawnTemplateData = {}
do --init
for coalition_name, coalition_data in pairs(env.mission.coalition) do
local coalition_nr = DcsUtil.stringToCoalition(coalition_name)
for _, coalition_data in pairs(env.mission.coalition) do
if coalition_data.country then
for country_index, country_data in pairs(coalition_data.country) do
for _, country_data in pairs(coalition_data.country) do
for category_name, categorydata in pairs(country_data) do
local category_id = DcsUtil.stringToGroupCategory(category_name)
if category_id ~= nil and type(categorydata) == "table" and categorydata.group ~= nil and type(categorydata.group) == "table" then
for group_index, group in pairs(categorydata.group) do
for _, group in pairs(categorydata.group) do
local name = group.name
local skippable = false
local isStatic = false
+4 -5
View File
@@ -44,7 +44,7 @@ function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant)
if spawnData.isStatic == true then
return self:SpawnStaticInternal(groupName, spawnData, overrides, isGroupPersistant), true
else
return self:SpawnGroupInternal(groupName, spawnData, overrides, isGroupPersistant), false
return self:SpawnGroupInternal(spawnData, overrides, isGroupPersistant), false
end
end
@@ -103,20 +103,20 @@ function SpawnManager:OnUnitLost(object)
end
end
---@param groupName string
function SpawnManager:SpawnCorpsesOnly(groupName)
if groupName == nil then return end
end
do --- privates
---@private
---@param groupName string
---@param spawnData SpawnData
---@param override SpawnOverrides?
---@param isPersistent boolean?
---@return Group|nil
function SpawnManager:SpawnGroupInternal(groupName, spawnData, override, isPersistent)
function SpawnManager:SpawnGroupInternal(spawnData, override, isPersistent)
if not spawnData then return end
local country = spawnData.country
@@ -181,7 +181,6 @@ do --- privates
return group
end
return nil
end
+5 -3
View File
@@ -112,7 +112,7 @@ do
logger:info("Wrote persistence data to file")
end
local UpdateContinuous = function(null, time)
local UpdateContinuous = function(_, time)
env.info("[Spearhead][Persistence] Checking up on persistence state...")
if Persistence._updateRequired == true then
local status, result = pcall(writeToFile)
@@ -135,13 +135,15 @@ do
end
local warnForNonPersistenceContinous = function(null, time)
local warnForNonPersistenceContinous = function(_, time)
trigger.action.outText("Persistence was enabeld, however, io and lfs are not available and no persistence will be done. Make sure to either disable persistence or fix the issues before continuing.", 10)
return time + 9
end
---@param dir string @BaseDirectory
---@return string
---@param startsWith string
---@param default string?
---@return string?
local getLastFileOrDefault = function(dir, startsWith, default)
local latestFile, lastNumber = default, 0
+56 -11
View File
@@ -58,6 +58,7 @@ local StageDrawing = require("classes.stageClasses.drawings.StageDrawing")
---@class BlueSamData
---@field groups Array<string>
---@field buildingKilos number?
---@field briefing string?
---@class MissionZoneData
---@field ZoneName string
@@ -74,6 +75,7 @@ local StageDrawing = require("classes.stageClasses.drawings.StageDrawing")
---@field padNames Array<string>
---@field buildingKilos number?
---@field supplyHubNames Array<string>
---@field briefing string?
---@class Database
---@field private _tables DatabaseTables
@@ -119,7 +121,7 @@ function Database.New(Logger)
self._logger:debug("Initiating tables")
do -- INIT ZONE TABLES
for zone_ind, zone_data in pairs(DcsUtil.__trigger_zones) do
for _, zone_data in pairs(DcsUtil.__trigger_zones) do
local zone_name = zone_data.name
---@type Vec2
@@ -206,9 +208,9 @@ function Database.New(Logger)
self._logger:debug("initiated zone tables, continuing with descriptions")
do --load markers
if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, 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
local airbaseData = self:getAirbaseDataForDrawLayer(layer_object)
if airbaseData then
@@ -226,9 +228,9 @@ function Database.New(Logger)
---@type table<integer, boolean>
do -- custom drawings
if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
for _, layer_object in pairs(layer.objects) do
if Util.startswith(layer_object.name, "drawing_", true) then
local object = layer_object --[[@as DrawingObject]]
local stageDrawing = StageDrawing.New(object)
@@ -254,9 +256,9 @@ function Database.New(Logger)
-- Find and add Briefing
if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, 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
local zone = DcsUtil.getZoneByName(stageZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
@@ -337,14 +339,14 @@ function Database.New(Logger)
end
-- fill missions
for key, missionZone in pairs(self._tables.MissionZones) do
for _, missionZone in pairs(self._tables.MissionZones) do
if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
table.insert(stageData.MissionZones, missionZone)
end
end
-- fill random missions
for key, missionZone in pairs(self._tables.RandomMissionZones) do
for _, missionZone in pairs(self._tables.RandomMissionZones) do
if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
table.insert(stageData.RandomMissionZones, missionZone)
end
@@ -616,6 +618,27 @@ function Database:loadBlueSamUnits()
local number = tonumber(kvPair.value)
if number and number > 0 then
samData.buildingKilos = number
if env.mission.drawings and env.mission.drawings.layers then
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for _, 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
@@ -697,9 +720,9 @@ function Database:LoadZoneData(missionZoneName)
---Adds Briefings
if env.mission.drawings and env.mission.drawings.layers then
for i, layer in pairs(env.mission.drawings.layers) do
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
for _, 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
@@ -750,6 +773,28 @@ function Database:loadFarpData()
local number = tonumber(kvPair.value)
if number and number > 0 then
farpzoneData.buildingKilos = number
-- check briefings
if env.mission.drawings and env.mission.drawings.layers then
for _, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for _, 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
+20 -21
View File
@@ -65,7 +65,7 @@ do
end)
for _, callable in pairs(OnStageNumberChangedListeners) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnStageNumberChanged(newStageNumber, laneIdentifier)
end)
if err then
@@ -74,14 +74,14 @@ do
end
for _, callable in pairs(OnStageNumberChangedHandlers) do
local succ, err = pcall(callable, newStageNumber, laneIdentifier)
local _, err = pcall(callable, newStageNumber, laneIdentifier)
if err then
logError(err)
end
end
for _, callable in pairs(OnStageNumberChangeCompleteListeners) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnStageNumberChangeComplete(newStageNumber, laneIdentifier)
end)
if err then
@@ -112,7 +112,7 @@ do
local triggerWeaponFired = function(unit, weapon, target)
for _, callable in pairs(onWeaponFiredListeners) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnWeaponFired(unit, weapon, target)
end)
@@ -186,7 +186,7 @@ do
if groupName ~= nil then
if OnGroupRTBListeners[groupName] then
for _, callable in pairs(OnGroupRTBListeners[groupName]) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnGroupRTB(groupName)
end)
if err then
@@ -221,7 +221,7 @@ do
if groupName ~= nil then
if OnGroupRTBInTenListeners[groupName] then
for _, callable in pairs(OnGroupRTBInTenListeners[groupName]) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnGroupRTBInTen(groupName)
end)
if err then
@@ -257,7 +257,7 @@ do
if groupName ~= nil then
if OnGroupOnStationListeners[groupName] then
for _, callable in pairs(OnGroupOnStationListeners[groupName]) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnGroupOnStation(groupName)
end)
if err then
@@ -286,7 +286,7 @@ do
if unit ~= nil then
if playerEnterUnitListeners then
for _, callable in pairs(playerEnterUnitListeners) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnPlayerEntersUnit(unit)
end)
if err then
@@ -321,7 +321,7 @@ do
local name = unit:getName()
if onLandEventListeners[name] then
for _, callable in pairs(onLandEventListeners[name]) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnUnitLanded(unit, airbase)
end)
if err then
@@ -344,7 +344,7 @@ do
if object and object.getName and OnUnitLostListeners[object:getName()] then
for _, callable in pairs(OnUnitLostListeners[object:getName()]) do
local succ, err = pcall(function()
local _, err = pcall(function()
callable:OnUnitLost(object)
end)
@@ -374,26 +374,26 @@ do
local AI_GROUPS = {}
local function CheckAndTriggerSpawnAsync(unit, time)
local function CheckAndTriggerSpawnAsync(unit, _)
local function isPlayer(unit)
if unit == nil then return false, "unit is nil" end
if unit.getGroup == nil then return false, 'no get group function in unit object, most likely static' end
if Object.getCategory(unit) ~= Object.Category.UNIT then
local function isPlayer(checkUnit)
if checkUnit == nil then return false, "unit is nil" end
if checkUnit.getGroup == nil then return false, 'no get group function in unit object, most likely static' end
if Object.getCategory(checkUnit) ~= Object.Category.UNIT then
return false, "object is not a unit"
end
if unit:isExist() ~= true then return false, "unit does not exist" end
local group = unit:getGroup()
if checkUnit:isExist() ~= true then return false, "unit does not exist" end
local group = checkUnit:getGroup()
if group ~= nil then
if AI_GROUPS[group:getName()] == true then
return false
end
local players = DcsUtil.getAllPlayerUnits()
local unitName = unit:getName()
for i, unit in pairs(players) do
if unit:getName() == unitName then
local unitName = checkUnit:getName()
for _, playerUnit in pairs(players) do
if playerUnit:getName() == unitName then
return true
end
end
@@ -403,7 +403,6 @@ do
end
if isPlayer(unit) == true then
local groupId = unit:getGroup():getID()
SpearheadEvents.TriggerPlayerEntersUnit(unit)
end
+70 -62
View File
@@ -2,7 +2,6 @@ local Events = require("classes.spearhead_events")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local Logger = require("classes.util.Logger")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local PersistenceConfig = require("classes.configuration.PersistenceConfig")
local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
@@ -35,7 +34,6 @@ local singletonInstance = nil
---@param spawnManager SpawnManager
---@return GlobalStageManager
function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
if singletonInstance ~= nil then
return singletonInstance
end
@@ -52,7 +50,8 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
self.logger = logger
if stageConfig.isAutoStages ~= true then
logger:warn("Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message")
logger:warn(
"Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message")
end
Events.AddStageNumberChangedListener(self)
@@ -81,12 +80,12 @@ function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
stage:AddStageCompleteListener(self)
self._stageRepository:AddStage(stage)
elseif parseResult.stageType == "WaitingStage" then
local waitingStage = WaitingStage.New(database, stageConfig, logger, initData, parseResult.waitingStageSeconds, spawnManager)
local waitingStage = WaitingStage.New(database, stageConfig, logger, initData,
parseResult.waitingStageSeconds, spawnManager)
waitingStage:AddStageCompleteListener(self)
self._stageRepository:AddStage(waitingStage)
end
end
end
singletonInstance = self
@@ -103,11 +102,11 @@ function GlobalStageManager:Start()
local stageLanes = self._stageRepository:getAllStageLanes()
for _, stageLane in pairs(stageLanes) do
local stageLaneIdentifier = stageLane:GetStageLaneIdentifier()
local persistedStage = Persistence.GetActiveStage(stageLaneIdentifier)
if persistedStage then
self.logger:info("Loaded persisted stage " .. persistedStage .. " for lane " .. (stageLaneIdentifier or "default"))
self.logger:info("Loaded persisted stage " ..
persistedStage .. " for lane " .. (stageLaneIdentifier or "default"))
stageLane:SetActiveStageIndex(persistedStage)
Events.PublishStageNumberChanged(persistedStage, stageLaneIdentifier)
else
@@ -139,17 +138,16 @@ end
---@param stageName string
---@return StageNameParseResult
function GlobalStageManager:ParseStageName(stageName)
local split = Util.split_string(stageName, "_")
if Util.tableLength(split) < 3 then
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a order number or valid format" }
return { isValid = false, invalidReason = "Stage zone with name " ..
stageName .. " does not have a order number or valid format" }
end
local typePart = string.lower(split[1])
if typePart == "missionstage" then
local orderNumberString = string.lower(split[2])
---@type StageType
local stageType = "PrimaryStage"
@@ -167,7 +165,8 @@ function GlobalStageManager:ParseStageName(stageName)
local orderNumber = tonumber(orderNumberString)
if orderNumber == nil then
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString }
return { isValid = false, invalidReason = "Stage zone with name " ..
stageName .. " does not have a valid order number : " .. orderNumberString }
end
local stageDisplayName = split[3]
@@ -182,18 +181,19 @@ function GlobalStageManager:ParseStageName(stageName)
}
return result
elseif typePart == "waitingstage" then
local stageType = "WaitingStage"
local orderNumberString = split[2]
local orderNumber = tonumber(orderNumberString)
if orderNumber == nil then
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString }
return { isValid = false, invalidReason = "Waiting Stage zone with name " ..
stageName .. " does not have a valid order number : " .. orderNumberString }
end
local waitingSecondsString = split[3]
local waitingSeconds = tonumber(waitingSecondsString)
if waitingSeconds == nil then
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter : " .. waitingSecondsString }
return { isValid = false, invalidReason = "Waiting Stage zone with name " ..
stageName .. " does not have a valid amount of seconds parameter : " .. waitingSecondsString }
end
local stageDisplayName = "Waiting Stage " .. orderNumber
@@ -209,9 +209,9 @@ function GlobalStageManager:ParseStageName(stageName)
waitingStageSeconds = waitingSeconds
}
return result
end
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " has an unrecognized type: " .. typePart }
return { isValid = false, invalidReason = "Stage zone with name " ..
stageName .. " has an unrecognized type: " .. typePart }
end
---@param stage Stage
@@ -223,7 +223,8 @@ function GlobalStageManager:OnStageComplete(stage)
local stageLane = self._stageRepository:getStageLane(laneIdentifier)
if not stageLane or not stageLane:IsCurrentStageIndexComplete() then
self.logger:debug("Stage lane " .. (laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex)
self.logger:debug("Stage lane " ..
(laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex)
return
end
@@ -242,7 +243,9 @@ function GlobalStageManager:OnStageComplete(stage)
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey and lane:GetStageLaneState() == "BetweenChapters" then
local nextChapterStart = lane:GetNextChapterStart()
if nextChapterStart == nextStageIndex then
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is at chapter start for next stage index " .. nextStageIndex)
self.logger:debug("Lane " ..
(lane:GetStageLaneIdentifier() or "default") ..
" is at chapter start for next stage index " .. nextStageIndex)
Events.PublishStageNumberChanged(nextStageIndex, lane:GetStageLaneIdentifier())
lane:SetActiveStageIndex(nextStageIndex)
end
@@ -256,7 +259,6 @@ function GlobalStageManager:OnStageComplete(stage)
if defaultStageLane and defaultStageLane:GetStageLaneState() == "BetweenChapters" then
local nextChapterStart = defaultStageLane:GetNextChapterStart()
if nextChapterStart == nextStageIndex then
-- 2. Check if all side lanes are at or beyond the next chapter starts
local allLanes = self._stageRepository:getAllStageLanes()
local allSideLanesReady = true
@@ -265,15 +267,19 @@ function GlobalStageManager:OnStageComplete(stage)
-- Check if the lane is at or beyond the next chapter starts when "InChapter" eq "Active"
if lane:GetStageLaneState() == "InChapter" and lane:GetActiveStageIndex() < nextStageIndex then
allSideLanesReady = false
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex)
self.logger:debug("Lane " ..
(lane:GetStageLaneIdentifier() or "default") ..
" is not ready for next stage index " .. nextStageIndex)
break
-- When in between chapters, check if the next chapter start is less than the next stage index, meaning there is still stages to be completed before the main lane can be activated again
-- When in between chapters, check if the next chapter start is less than the next stage index, meaning there is still stages to be completed before the main lane can be activated again
elseif lane:GetStageLaneState() == "BetweenChapters" then
local nextChapter = lane:GetNextChapterStart()
if nextChapter and nextChapter < nextStageIndex then
allSideLanesReady = false
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex)
self.logger:debug("Lane " ..
(lane:GetStageLaneIdentifier() or "default") ..
" is not ready for next stage index " .. nextStageIndex)
break
end
end
@@ -281,13 +287,14 @@ function GlobalStageManager:OnStageComplete(stage)
end
if allSideLanesReady then
self.logger:debug("All side lanes are ready for next stage index " .. nextStageIndex .. ", activating default lane")
self.logger:debug("All side lanes are ready for next stage index " ..
nextStageIndex .. ", activating default lane")
Events.PublishStageNumberChanged(nextStageIndex, nil)
defaultStageLane:SetActiveStageIndex(nextStageIndex)
else
self.logger:debug("Not all side lanes are ready for next stage index " .. nextStageIndex .. ", default lane will not be activated")
self.logger:debug("Not all side lanes are ready for next stage index " ..
nextStageIndex .. ", default lane will not be activated")
end
end
end
end
@@ -308,7 +315,6 @@ end
---@public
function GlobalStageManager:OnStageNumberChanged(stageNumber, stageLaneIdentifier)
-- only react on "main" lane changes, ignore other lanes for now
if stageLaneIdentifier ~= nil then return end
@@ -321,7 +327,6 @@ function GlobalStageManager:OnStageNumberChangeComplete(stageNumber, stageLaneId
if stageLaneIdentifier ~= nil then return end
self.logger:debug("Stage number change complete to: " .. tostring(stageNumber))
local groups = {}
for _, player in pairs(DcsUtil.getAllPlayerUnits()) do
@@ -349,17 +354,18 @@ function GlobalStageManager:UpdateDrawings(stageNumber, stageLaneIdentifier)
if laneIdentifier == stageLaneIdentifier then
if stageNumber >= startStage and stageNumber < stopStage then
self.logger:debug("Drawing " .. drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber))
self.logger:debug("Drawing " ..
drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber))
drawing:Draw()
else
self.logger:debug("Drawing " .. drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber))
self.logger:debug("Drawing " ..
drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber))
drawing:Remove()
end
end
end
end
function GlobalStageManager:PrintMermaidStage()
local lanes = self._stageRepository:getAllStageLanes()
@@ -370,32 +376,32 @@ function GlobalStageManager:PrintMermaidStage()
-- Color palette for lanes (26 vibrant colors optimized for dark mode)
local laneColors = {
"#FF6B6B", -- 1: Red
"#4ECDC4", -- 2: Teal
"#45B7D1", -- 3: Blue
"#FFA502", -- 4: Orange
"#95E1D3", -- 5: Mint
"#F38181", -- 6: Coral
"#AA96DA", -- 7: Purple
"#FCBAD3", -- 8: Pink
"#A8E6CF", -- 9: Light green
"#FFD3B6", -- 10: Peach
"#FFAAA5", -- 11: Light red
"#FF8B94", -- 12: Rose
"#FFEAA7", -- 13: Butter
"#DFE6E9", -- 14: Gray
"#00B894", -- 15: Emerald
"#0984E3", -- 16: Cobalt
"#6C5CE7", -- 17: Indigo
"#A29BFE", -- 18: Lavender
"#FD79A8", -- 19: Magenta
"#FDCB6E", -- 20: Gold
"#6C757D", -- 21: Slate
"#20C997", -- 22: Seafoam
"#E74C3C", -- 23: Scarlet
"#3498DB", -- 24: Dodger blue
"#9B59B6", -- 25: Amethyst
"#1ABC9C", -- 26: Turquoise
"#FF6B6B", -- 1: Red
"#4ECDC4", -- 2: Teal
"#45B7D1", -- 3: Blue
"#FFA502", -- 4: Orange
"#95E1D3", -- 5: Mint
"#F38181", -- 6: Coral
"#AA96DA", -- 7: Purple
"#FCBAD3", -- 8: Pink
"#A8E6CF", -- 9: Light green
"#FFD3B6", -- 10: Peach
"#FFAAA5", -- 11: Light red
"#FF8B94", -- 12: Rose
"#FFEAA7", -- 13: Butter
"#DFE6E9", -- 14: Gray
"#00B894", -- 15: Emerald
"#0984E3", -- 16: Cobalt
"#6C5CE7", -- 17: Indigo
"#A29BFE", -- 18: Lavender
"#FD79A8", -- 19: Magenta
"#FDCB6E", -- 20: Gold
"#6C757D", -- 21: Slate
"#20C997", -- 22: Seafoam
"#E74C3C", -- 23: Scarlet
"#3498DB", -- 24: Dodger blue
"#9B59B6", -- 25: Amethyst
"#1ABC9C", -- 26: Turquoise
}
local laneColorMap = {} -- Map lane ID to color
local colorIndex = 1
@@ -552,11 +558,12 @@ function GlobalStageManager:PrintMermaidStage()
end
-- Build complete Mermaid diagram
local diagramLines = {"graph TD"}
local diagramLines = { "graph TD" }
-- Add class definitions for each lane with colors and contrasting text
for laneId, color in pairs(laneColorMap) do
table.insert(diagramLines, string.format(' classDef lane_%s fill:%s,stroke:#333,stroke-width:2px,color:#000', laneId, color))
table.insert(diagramLines,
string.format(' classDef lane_%s fill:%s,stroke:#333,stroke-width:2px,color:#000', laneId, color))
end
-- Add nodes
@@ -578,7 +585,8 @@ function GlobalStageManager:PrintMermaidStage()
end
-- Print as single multi-line message
local diagram = "========== STAGE FLOW DIAGRAM ==========\n" .. table.concat(diagramLines, "\n") .. "\n========== END DIAGRAM =========="
local diagram = "========== STAGE FLOW DIAGRAM ==========\n" ..
table.concat(diagramLines, "\n") .. "\n========== END DIAGRAM =========="
self.logger:info(diagram)
end
@@ -586,10 +594,10 @@ end
---@param stageNumber number
---@param stageLaneIdentifier string? nil for default lan
---@return boolean | nil
GlobalStageManager.isStageComplete = function (stageNumber, stageLaneIdentifier)
GlobalStageManager.isStageComplete = function(stageNumber, stageLaneIdentifier)
if singletonInstance == nil then
Logger.new("StageManager", "INFO"):warn("GlobalStageManager.isStageComplete called before GlobalStageManager was initialized. Returning nil")
Logger.new("StageManager", "INFO"):warn(
"GlobalStageManager.isStageComplete called before GlobalStageManager was initialized. Returning nil")
return nil
end
@@ -57,7 +57,7 @@ function SpearheadGroup:Spawn(lateStart)
uncontrolled = lateStart,
}
local spawnedObject, isStatic = self._spawnManager:SpawnGroup(self._groupName, overrides, self._isPersistent)
local _, isStatic = self._spawnManager:SpawnGroup(self._groupName, overrides, self._isPersistent)
self._isStatic = isStatic
self._isSpawned = true
end
@@ -50,12 +50,6 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
---@type table<string, Vec3>
local redUnitsPos = {}
local buildable = false
if self._buildableCrateKilos and self._buildableCrateKilos > 0 then
buildable = true
end
for _, groupName in pairs(blueSamData.groups) do
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
if spearheadGroup then
@@ -78,7 +72,7 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
--Cleanup units
local cleanup_distance = 5
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
for _, blueUnitPos in pairs(blueUnitsPos) do
for redUnitName, redUnitPos in pairs(redUnitsPos) do
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
if distance <= cleanup_distance then
@@ -89,7 +83,7 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
local zone = DcsUtil.getZoneByName(zoneName)
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
return self
@@ -140,7 +134,9 @@ end
function BlueSam:SpawnGroups()
for unitName, needsCleanup in pairs(self._cleanupUnits) do
DcsUtil.DestroyUnit(unitName)
if needsCleanup then
DcsUtil.DestroyUnit(unitName)
end
end
for _, group in pairs(self._blueGroups) do
@@ -64,7 +64,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
local zone = DcsUtil.getZoneByName(zoneName)
if zone then
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
self:Deactivate()
@@ -90,7 +90,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
local cleanup_distance = 5
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
for _, blueUnitPos in pairs(blueUnitsPos) do
for redUnitName, redUnitPos in pairs(redUnitsPos) do
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
if distance <= cleanup_distance then
@@ -20,7 +20,8 @@ BuildableZone.__index = BuildableZone
---@param database Database
---@param crateType SupplyType
---@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._requiredKilos = kilosRequired or 0
self._buildableGroups = buildableGroups or {}
@@ -69,14 +70,12 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
local noLandingZone = self:GetNoLandingZone()
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)
else
self._buildableMission = nil
end
if self._buildableMission == nil then
self._buildableLogger:debug("No buildable mission for zone: " .. targetZone.name)
end
@@ -98,23 +97,11 @@ function BuildableZone:OnBuildingComplete() end
---@field unpackedItems number
---@field unpackedKilos number
---@param mission BuildableMission?
---@param _ BuildableMission?
---@param kilos number
function BuildableZone:OnCrateDroppedOff(mission, kilos)
function BuildableZone:OnCrateDroppedOff(_, kilos)
self._buildableLogger:debug("Crate dropped off in zone: " .. self._targetZone.name)
local timeToUnpack = (kilos / 500) * 15
---@type UnpackCrateParam
local params = {
self = self,
groupsPerKilo = self._groupsPerKilo,
unpackedItems = 0,
kilosPerSecond = kilos/timeToUnpack,
unpackedKilos = 0,
kilos = kilos
}
---@param params UnpackCrateParam
---@param time number
local startUnpackingCrate = function(params, time)
@@ -135,6 +122,17 @@ function BuildableZone:OnCrateDroppedOff(mission, kilos)
return time + 2
end
local timeToUnpack = (kilos / 500) * 15
---@type UnpackCrateParam
local params = {
self = self,
groupsPerKilo = self._groupsPerKilo,
unpackedItems = 0,
kilosPerSecond = kilos/timeToUnpack,
unpackedKilos = 0,
kilos = kilos
}
timer.scheduleFunction(startUnpackingCrate, params, timer.getTime() + 2)
end
@@ -189,7 +187,7 @@ function BuildableZone:SpawnAmount(amount)
return nil
end
for i = 1, amount do
for _ = 1, amount do
local spawned = spawnOne()
if spawned ~= true then
self._buildableLogger:debug("No more groups to spawn in zone: " .. self._targetZone.name)
@@ -49,7 +49,6 @@ end
function StageRepository:AddStage(stage)
local stageLaneIdentifier = stage:GetStageLaneIdentifier() or StageLane.DefaultLaneKey
if self.StageLanes[tostring(stageLaneIdentifier)] == nil then
self.StageLanes[tostring(stageLaneIdentifier)] = StageLane.New(stageLaneIdentifier)
end
@@ -160,13 +160,13 @@ function Stage:superNew(database, stageConfig, logger, initData, stageType, miss
self._logger:info("Initiating new Stage with name: " .. self.zoneName)
---comment
---@param self Stage
---@param selfA Stage
---@param time number?
self.CheckContinuousAsync = function (self, time)
self.CheckContinuousAsync = function (selfA, time)
self:CheckAndUpdateSelf()
if self:IsComplete() == true then
self:NotifyComplete()
selfA:CheckAndUpdateSelf()
if selfA:IsComplete() == true then
selfA:NotifyComplete()
return nil
end
@@ -284,14 +284,14 @@ end
function Stage:IsComplete()
if self._currentStageState >= StageState.Blue then return true end
for i, mission in pairs(self._db.sams) do
for _, mission in pairs(self._db.sams) do
local state = mission:getState()
if state == "ACTIVE" or state == "NEW" or state =="WAITING" then
return false
end
end
for i, mission in pairs(self._db.missions) do
for _, mission in pairs(self._db.missions) do
local state = mission:getState()
if state == "ACTIVE" or state == "NEW" then
return false
@@ -370,9 +370,9 @@ function Stage:CheckAndUpdateSelf()
local availableMissionsCount = Util.tableLength(getAvailableMissions())
local activeCount = getActiveMissionsCount()
if activeCount < max and availableMissionsCount > 0 then
for i = activeCount+1, max do
for _ = activeCount+1, max do
if availableMissionsCount == 0 then
i = max+1 --exits this loop
break
else
local mission = Util.randomFromList(getAvailableMissions()) --[[@as Mission]]
if mission then
@@ -426,7 +426,7 @@ function Stage:PreActivate()
end
self._currentStageState = StageState.PreActivated
for key, mission in pairs(self._db.sams) do
for _, mission in pairs(self._db.sams) do
if mission then
mission:SpawnInactive()
end
@@ -589,8 +589,8 @@ function Stage:OnStageNumberChanged(number, stageLaneIdentifier)
end
---@param self Stage
---@param mission Mission
Stage.OnMissionComplete = function(self, mission)
---@param _ Mission
Stage.OnMissionComplete = function(self, _)
self:CheckAndUpdateSelf()
end
@@ -645,7 +645,7 @@ function Stage:GetStageStats()
end
end
for _, mission in pairs(self._db.sams) do
for _, _ in pairs(self._db.sams) do
dead = dead + 1
end
@@ -673,13 +673,13 @@ function Stage:ActivateBlueStage()
miscGroup:Spawn()
end
---@param self Stage
local ActivateBlueAsync = function(self)
---@param selfA Stage
local ActivateBlueAsync = function(selfA)
pcall(function()
self:MarkStage()
selfA:MarkStage()
end)
self:ActivateBlueGroups()
selfA:ActivateBlueGroups()
return nil
end
@@ -1,6 +1,7 @@
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
local StageState = require("classes.stageClasses.Stages.BaseStage.StageState")
---@class ExtraStage : Stage
local ExtraStage = {}
@@ -22,7 +23,6 @@ function ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
self:superNew(database, stageConfig, logger, initData, "ExtraStage", "secondary", spawnManager)
self.OnPostBlueActivated = function (selfStage)
selfStage:MarkStage()
end
@@ -47,7 +47,6 @@ function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
return
end
local previousActive = self._activeStage
self._activeStage = number
if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then
@@ -61,7 +60,7 @@ function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
self:ActivateStage()
end
if self._currentStageState == Stage.CurrentStageState.BLUE then
if self._currentStageState == StageState.Blue then
self:ActivateBlueStage()
end
@@ -56,7 +56,7 @@ function WaitingStage:OnStageNumberChanged()
self._logger:debug("Waiting Stage OnStageNumberChanged override")
end
function WaitingStage:MarkStage(stageColor)
function WaitingStage:MarkStage(_)
self._logger:debug("Waiting Stage MarkStage override")
end
@@ -1,7 +1,5 @@
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
local Util = require("classes.util.Util")
local Logger = require("classes.util.Logger")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local drawingLogger = Logger.new("CustomDrawing")
@@ -1,4 +1,3 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local Logger = require("classes.util.Logger")
local GlobalConfig = require("classes.configuration.GlobalConfig")
@@ -15,7 +14,6 @@ local logger = Logger.new("DrawingHelper", level)
local DrawingHelper = {}
DrawingHelper.__index = DrawingHelper
---@param object DrawingObject
---@return Array<integer> id
function DrawingHelper.Draw(object)
@@ -1,6 +1,5 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
---@class BattleManager
---@field private _name string
@@ -13,8 +12,6 @@ local DcsUtil = require("classes.util.DcsUtil")
local BattleManager = {}
BattleManager.__index = BattleManager
local debugDrawing = false
---@param redGroups Array<SpearheadGroup>
---@param blueGroups Array<SpearheadGroup>
---@param name string
@@ -90,8 +87,6 @@ function BattleManager:Update()
self._logger:debug("BattleManager Update called for " .. self._name)
local shootChance = 1 -- Adjust this value to control the shooting probability (0.0 to 1.0)
self:LetUnitsShoot(self._redGroups, self._blueGroups)
self:LetUnitsShoot(self._blueGroups, self._redGroups)
@@ -31,8 +31,6 @@ local function sortMissions(list, groupPos)
end)
end
local id = 0
local instance = nil
---@return MissionCommandsHelper
@@ -57,16 +55,16 @@ function MissionCommandsHelper.getOrCreate()
instance._supplyUnitsTracker:AddOnSupplyUnitEventListener(
{
enteredSupplyHub = function(self, unit)
enteredSupplyHub = function(_, unit)
if unit == nil then return end
instance.updateNeeded = true
instance:updateCommandsForGroup(unit:getGroup():getID())
end,
exitedSupplyHub = function(self, unit)
exitedSupplyHub = function(_, unit)
instance.updateNeeded = true
instance:updateCommandsForGroup(unit:getGroup():getID())
end,
supplyUnitSpawned = function(self, unit)
supplyUnitSpawned = function(_, unit)
instance.updateNeeded = true
instance:updateCommandsForGroup(unit:getGroup():getID())
end
@@ -509,7 +507,6 @@ function MissionCommandsHelper:AddCargoCommands(groupID)
local unloadCargoCommand = function(params)
local unitID = params.unitID
local crateType = params.crateType
local supplyUnitsTracker = params.supplyUnitsTracker
params.supplyUnitsTracker:UnloadRequested(unitID, crateType, params.commandHelper)
end
@@ -518,7 +515,7 @@ function MissionCommandsHelper:AddCargoCommands(groupID)
for cargoType, amount in pairs(cargo) do
local cargoConfig = SupplyConfigHelper.getSupplyConfig(cargoType)
if cargoConfig then
for i = 1, amount do
for _ = 1, amount do
local path = { [1] = folderNames.cargo }
---@type UnloadCargoCommandParams
local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self
@@ -185,7 +185,7 @@ function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType)
self._cargoInUnits[unitIDStr][crateType] = self._cargoInUnits[unitIDStr][crateType] - 1
local hasCargo = false
for type, count in pairs(self._cargoInUnits[unitIDStr]) do
for _, count in pairs(self._cargoInUnits[unitIDStr]) do
if count > 0 then
hasCargo = true
break
@@ -216,13 +216,11 @@ end
function SupplyUnitsTracker:CheckUnitsInZones()
for name, unit in pairs(self._supplyUnitsByName) do
for _, unit in pairs(self._supplyUnitsByName) do
if unit ~= nil and unit:isExist() == true then
self._logger:debug("Checking unit: " .. unit:getName())
local pos = unit:getPoint()
local group = unit:getGroup()
for hub, enabled in pairs(self._registeredHubs) do
if enabled == true then
local zone = hub:GetZone()
@@ -614,7 +612,7 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
}
local occupiedObjects = {}
local found = function(foundItem, val)
local found = function(foundItem, _)
local bbox = self:GetBoundingBoxes(foundItem)
if bbox then
self._logger:debug("Found object: " .. foundItem:getTypeName() .. " at (" .. foundItem:getPoint().x .. ", " .. foundItem:getPoint().z .. ")")
@@ -21,9 +21,18 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
---@field private _dropOffZone SpearheadTriggerZone?
---@field private _noLandingZoneDrawing CustomDrawing?
---@field private _dropOffZoneDrawing CustomDrawing?
---@field private _briefing string?
local BuildableMission = {}
BuildableMission.__index = BuildableMission
---@return string
local function getDefaultBriefing(siteType, coords)
return "We've dispatched forward units to find a proper spot for a new " .. siteType .. "." ..
"\nYou will need to drop off supplies so they can start building." ..
"\nThe coords are: " .. coords ..
"\n\n"
end
---@class OnCrateDroppedListener
---@field OnCrateDroppedOff fun(self:OnCrateDroppedListener, mission:BuildableMission, kilos:number)
@@ -33,7 +42,8 @@ BuildableMission.__index = BuildableMission
---@param requiredCrateType SupplyType
---@param noLandingZone SpearheadTriggerZone?
---@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)
@@ -43,6 +53,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self._database = database
self._requiredKilos = requiredKilos
self._droppedKilos = 0
self._briefing = briefing
self._noLandingZone = noLandingZone
@@ -64,13 +75,12 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
end
self.code = tostring(database:GetNewMissionCode())
self.name = "Resupply"
local type = "site"
if requiredCrateType == "SAM_CRATE" then
type = "SAM site"
elseif requiredCrateType == "FARP_CRATE" then
type = "FARP"
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
self.zoneName = targetZone.name .. "_supply"
@@ -81,7 +91,6 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate()
self._state = "NEW"
self.location = targetZone.location
self.missionType = "LOGISTICS"
@@ -116,11 +125,16 @@ function BuildableMission:ShowBriefing(groupID)
siteType = "airbase"
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 ..
"\n \n" ..
"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 ..
briefingPart ..
"\n\n" ..
"\nKilos still required: " .. self._requiredKilos - self._droppedKilos ..
"\n\n" ..
@@ -84,7 +84,6 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
self._logger:debug("Runway hit: " .. self._airportName .. ":" .. self._runway.Name)
for _, section in pairs(self._runwaySections) do
local zone = self:SectionToSpearheadZone(section)
if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
if section.kilosHit == nil then
@@ -96,7 +95,7 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
end
---@param selfA RunwayStrikeMission
local updateState = function(selfA, time)
local updateState = function(selfA, _)
selfA:UpdateState()
end
@@ -213,7 +212,6 @@ function RunwayStrikeMission:StartRepair()
if interval == nil then return nil end
return time + interval
end
timer.scheduleFunction(repairTask, self, timer.getTime() + 5)
end
@@ -211,7 +211,7 @@ end
---@return boolean
function ZoneMission:AllDependenciesMet()
local allDependenciesMet = true
for missionName, value in pairs(self._dependencies) do
for missionName, _ in pairs(self._dependencies) do
if self._parentStage:IsMissionComplete(missionName) == false then
allDependenciesMet = false
self._dependencies[missionName] = false
@@ -70,6 +70,7 @@ function Mission:SpawnActive() end
---comment
---@param checkHealth boolean
---@param messageIfDone boolean
---@diagnostic disable-next-line: unused-local
function Mission:UpdateState(checkHealth, messageIfDone) end
function Mission:StartCheckingContinuous() end
@@ -120,7 +121,7 @@ function Mission:NotifyMissionComplete()
end)
end
local succ, err = pcall(function()
local _, _ = pcall(function()
SpearheadAPI.Internal.notifyMissionComplete(self.zoneName)
end)
@@ -131,6 +132,8 @@ function Mission:ForceMissionComplete()
self:NotifyMissionComplete()
end
---@param groupId number
---@diagnostic disable-next-line: unused-local
function Mission:MarkMissionAreaToGroup(groupId) end
---endregion
+21 -22
View File
@@ -77,7 +77,7 @@ do -- INIT DCS_UTIL
do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment
do --init trigger zones
for i, trigger_zone in pairs(env.mission.triggers.zones) do
for _, trigger_zone in pairs(env.mission.triggers.zones) do
-- reorder verts as they are not ordered correctly in the ME
local verts = {}
if Util.tableLength(trigger_zone.verticies) >= 4 then
@@ -192,7 +192,7 @@ do -- INIT DCS_UTIL
BLUE = 2
}
]] --
local input = string.lower(input)
input = string.lower(input)
if input == 'neutrals' or input == "neutral" or input == "0" then
return DCS_UTIL.Coalition.NEUTRAL
end
@@ -235,7 +235,7 @@ do -- INIT DCS_UTIL
end
end
for index, zone_name in pairs(zone_names) do
for _, zone_name in pairs(zone_names) do
local zone = DCS_UTIL.__trigger_zones[zone_name]
if zone then
zones[#zones + 1] = zone
@@ -247,7 +247,7 @@ do -- INIT DCS_UTIL
local lUnit = units[units_ind]
local unit_pos = lUnit:getPosition().p
local lCat = Object.getCategory(lUnit)
for zone_name, zone in pairs(zones) do
for _, zone in pairs(zones) do
if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit
local isInZone = Util.is3dPointInZone(unit_pos, zone)
if isInZone == true then
@@ -313,7 +313,7 @@ do -- INIT DCS_UTIL
function DCS_UTIL.isPositionInZones(x, z, zone_names)
---@type Array<SpearheadTriggerZone>
local zones = {}
for index, zone_name in pairs(zone_names) do
for _, zone_name in pairs(zone_names) do
local zone = DCS_UTIL.__trigger_zones[zone_name]
if zone then
zones[#zones + 1] = zone
@@ -321,7 +321,7 @@ do -- INIT DCS_UTIL
end
local result_zones = {}
for zone_name, zone in pairs(zones) do
for _, zone in pairs(zones) do
if Util.is3dPointInZone({ x = x, z = z, y = 0 }, zone) == true then
result_zones[#result_zones + 1] = zone.name
end
@@ -414,7 +414,7 @@ do -- INIT DCS_UTIL
local height = land.getHeight(location)
local vec3 = { x = location.x, y = height, z = location.y }
local unitType = string.lower(unitType or "")
unitType = string.lower(unitType or "")
local conversionType = config[unitType]
if not conversionType then conversionType = "DDM" end
@@ -574,7 +574,7 @@ do -- INIT DCS_UTIL
local units = {}
for i = 0, 2 do
local players = coalition.getPlayers(i)
for key, unit in pairs(players) do
for _, unit in pairs(players) do
units[#units + 1] = unit
end
end
@@ -617,8 +617,7 @@ do -- INIT DCS_UTIL
end
function DCS_UTIL.CleanCorpse(unitName)
local unitName = "dead_" .. unitName
unitName = "dead_" .. unitName
local object = StaticObject.getByName(unitName)
if object then
@@ -632,11 +631,11 @@ do -- INIT DCS_UTIL
---@field b number
---@field a number
local drawID = 4210
local __drawID = 4210
function DCS_UTIL.GetNextDrawID()
drawID = drawID + 1
return drawID
__drawID = __drawID + 1
return __drawID
end
---@param groupID number
@@ -644,9 +643,9 @@ do -- INIT DCS_UTIL
---@param location Vec3
---@return number markID
function DCS_UTIL.AddMarkToGroup(groupID, text, location)
drawID = drawID + 1
trigger.action.markToGroup(drawID, text, location, groupID, true, nil)
return drawID
local nextId = DCS_UTIL.GetNextDrawID()
trigger.action.markToGroup(nextId, text, location, groupID, true, nil)
return nextId
end
---comment
@@ -654,9 +653,9 @@ do -- INIT DCS_UTIL
---@param location Vec3
---@return integer
function DCS_UTIL.AddMarkToAll(text, location)
drawID = drawID + 1
trigger.action.markToAll(drawID, text, location, true, nil)
return drawID
local nextId = DCS_UTIL.GetNextDrawID()
trigger.action.markToAll(nextId, text, location, true, nil)
return nextId
end
---@param markId number
@@ -705,7 +704,7 @@ do -- INIT DCS_UTIL
---@return number? id
function DCS_UTIL.GetNeutralCountry()
for name, id in pairs(country.id) do
for _, id in pairs(country.id) do
if coalition.getCountryCoalition(id) == DCS_UTIL.Coalition.NEUTRAL then
return id
end
@@ -759,7 +758,7 @@ do -- INIT DCS_UTIL
function DCS_UTIL.GetPlayerGroupByGroupID(groupId)
for i = 0, 2 do
local players = coalition.getPlayers(i)
for key, unit in pairs(players) do
for _, unit in pairs(players) do
if unit and unit:isExist() == true then
local group = unit:getGroup()
if group and group:getID() == groupId then
@@ -775,7 +774,7 @@ do -- INIT DCS_UTIL
function DCS_UTIL.GetPlayerUnitByID(unitID)
for i = 0, 2 do
local players = coalition.getPlayers(i)
for key, unit in pairs(players) do
for _, unit in pairs(players) do
if unit and unit:getID() == unitID then
return unit
end
+8 -1
View File
@@ -149,7 +149,7 @@ do -- INIT UTIL
---@param findableTable table
---@return boolean
UTIL.startswithAny = function(str, findableTable)
for key, value in pairs(findableTable) do
for _, value in pairs(findableTable) do
if type(value) == "string" and UTIL.startswith(str, value) then return true end
end
return false
@@ -238,7 +238,12 @@ do -- INIT UTIL
return ((vec1Norm.x * vec2Norm.x) + (vec1Norm.y * vec2Norm.y) + (vec1Norm.z * vec2Norm.z))
end
---@param polygon Array<Vec2> of pairs { x, y }
---@param x number X location
---@param y number Y location
---@return boolean
local function isInComplexPolygon(polygon, x, y)
---@param poly Array<Vec2> of pairs { x, y }
local function getEdges(poly)
local result = {}
for i = 1, #poly do
@@ -399,7 +404,9 @@ do -- INIT UTIL
if #points == 0 then return {} end
-- Simple clustering: group points that are within minSeparation of each other
---@type Array<Array<Vec2>>
local clusters = {}
---@type table<integer, boolean>
local assigned = {}
for i, p in ipairs(points) do
+1 -2
View File
@@ -1,7 +1,6 @@
--Single player purpose
local Logger = require("classes.util.Logger")
local DcsUtil = require("classes.util.DcsUtil")
local Database = require("classes.spearhead_db")
local SpearheadEvents = require("classes.spearhead_events")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
@@ -47,7 +46,7 @@ local spawnManager = SpawnManager.new(spawnLogger)
local detectionLogger = Logger.new("DetectionManager", defaultLogLevel)
local detectionManager = DetectionManager.New(detectionLogger)
GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
GlobalCapManager.start(databaseManager, capConfig, detectionManager, defaultLogLevel, spawnManager)
local globalStageManager = GlobalStageManager.new(databaseManager, stageConfig, defaultLogLevel, spawnManager)
GlobalFleetManager.start(databaseManager)