Compare commits

...
10 Commits
Author SHA1 Message Date
dutchie031 1447f5d489 merged DcsTools 2026-07-24 13:58:44 +02:00
dutchie031 6e96bc7562 wip 2026-07-24 13:56:56 +02:00
DutchieGitHubex61widutchie031 <dutchie031>
14115c53a0 Dcs lua tool (#66)
* custom drawings and animations for video

* wip

* Work in progress refactor for toolkit

* wip

* Refactored everything for Lua Compile Tool

* Trying out the github action

* updated the github action ref

* initial working version

---------

Co-authored-by: ex61wi <tim.rorije@ing.com>
Co-authored-by: dutchie031 <dutchie031>
2026-06-06 19:26:32 +02:00
dutchie031 7cd9a9f4bb initial working version 2026-06-06 19:10:03 +02:00
dutchie031 d61dd78f08 updated the github action ref 2026-04-25 21:54:28 +02:00
ex61wi 7d6cb61ca9 Trying out the github action 2026-04-07 22:48:15 +02:00
ex61wi b381ec8688 Refactored everything for Lua Compile Tool 2026-04-07 13:05:58 +02:00
ex61wi 9634dfc03b wip 2026-04-04 16:52:51 +02:00
ex61wi a6814a5255 Work in progress refactor for toolkit 2026-03-06 19:07:54 +01:00
ex61wi 64eaf570d7 wip 2026-01-20 15:58:40 +01:00
75 changed files with 3576 additions and 4430 deletions
Vendored
BIN
View File
Binary file not shown.
+5 -4
View File
@@ -12,10 +12,11 @@ jobs:
with:
fetch-depth: 0
- name: Run SpearheadCompile
run: |
mkdir -p ./output
python3 SpearheadCompile.py . ./output/spearhead.lua
- name: Compile Spearhead
uses: dutchie031/DcsMissionScriptingTools/github-action@actions/v1
with:
source-root: ./src
output-file: ./output/spearhead.lua
- name: Upload spearhead.lua artifact
uses: actions/upload-artifact@v4
+128 -131
View File
@@ -1,131 +1,128 @@
name: Publish Release
on:
workflow_dispatch:
inputs:
release_candidate:
description: 'Is this a release candidate?'
required: true
default: true
type: boolean
change_size:
type: choice
description: Version Bump
options:
- patch
- minor
- major
default: patch
branches:
- main
- automation
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for all branches and tags
- name: Run SpearheadCompile
run: |
mkdir -p ./output
python3 SpearheadCompile.py . ./output/spearhead.lua
- name: Calculate Tag
id: calculate_tag
run: |
# Get latest tag with 'v' prefix
latest_tag=$(git tag --sort=-v:refname | grep -E '^v' | head -n 1)
if [ -z "$latest_tag" ]; then
latest_tag="v0.0.0"
fi
echo "Latest tag: $latest_tag"
# Remove 'v' prefix for version parsing
version=${latest_tag#v}
IFS='.' read -r major minor patch <<< "$version"
bump="${{ inputs.change_size }}"
if [ "$bump" = "major" ]; then
major=$((major+1))
minor=0
patch=0
elif [ "$bump" = "minor" ]; then
minor=$((minor+1))
patch=0
else
patch=$((patch+1))
fi
new_tag="v${major}.${minor}.${patch}"
if [ "${{ inputs.release_candidate }}" = "true" ]; then
new_tag="$new_tag-rc"
fi
echo "New tag: $new_tag"
echo "new_tag=$new_tag" >> "$GITHUB_OUTPUT"
- name: Check for existing tag
id: check_tag
run: |
if git tag | grep -q "^${{ steps.calculate_tag.outputs.new_tag }}$"; then
echo "Tag already exists. Exiting."
exit 1
fi
- name: Generate Release Notes
id: generate_release_notes
run: |
if [ "${{ inputs.release_candidate }}" != "true" ]; then
git log $(git describe --tags --abbrev=0 $(git tag --sort=-v:refname | grep -v '-rc' | head -n 1))..HEAD --pretty=format:'- %s%n %b' > release_notes.txt
else
git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'- %s%n %b' > release_notes.txt
fi
awk -i inplace '!seen[$0]++' release_notes.txt
cat release_notes.txt
shell: bash
- name: Apply Tag to Current Commit
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git tag ${{ steps.calculate_tag.outputs.new_tag }}
git push origin ${{ steps.calculate_tag.outputs.new_tag }}
- name: Copy Release to Versioned File
run: |
cp ./output/spearhead.lua ./output/spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua
echo "Versioned file created: spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua"
- name: Create GitHub Release
uses: actions/create-release@v1
id: create_release
with:
tag_name: ${{ steps.calculate_tag.outputs.new_tag }}
release_name: Release ${{ steps.calculate_tag.outputs.new_tag }}
draft: true
body_path: release_notes.txt
prerelease: ${{ inputs.release_candidate }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Spearhead.lua Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/spearhead.lua
asset_name: spearhead.lua
asset_content_type: application/octet-stream
- name: Upload Spearhead.lua Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua
asset_name: spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua
asset_content_type: application/octet-stream
name: Publish Release
on:
workflow_dispatch:
inputs:
release_candidate:
description: 'Is this a release candidate?'
required: true
default: true
type: boolean
change_size:
type: choice
description: Version Bump
options:
- patch
- minor
- major
default: patch
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for all branches and tags
- name: Compile Spearhead
uses: dutchie031/DcsMissionScriptingTools/github-action@actions/v1
with:
source-root: ./src
output-file: ./output/spearhead.lua
- name: Calculate Tag
id: calculate_tag
run: |
# Get latest tag with 'v' prefix
latest_tag=$(git tag --sort=-v:refname | grep -E '^v' | head -n 1)
if [ -z "$latest_tag" ]; then
latest_tag="v0.0.0"
fi
echo "Latest tag: $latest_tag"
# Remove 'v' prefix for version parsing
version=${latest_tag#v}
IFS='.' read -r major minor patch <<< "$version"
bump="${{ inputs.change_size }}"
if [ "$bump" = "major" ]; then
major=$((major+1))
minor=0
patch=0
elif [ "$bump" = "minor" ]; then
minor=$((minor+1))
patch=0
else
patch=$((patch+1))
fi
new_tag="v${major}.${minor}.${patch}"
if [ "${{ inputs.release_candidate }}" = "true" ]; then
new_tag="$new_tag-rc"
fi
echo "New tag: $new_tag"
echo "new_tag=$new_tag" >> "$GITHUB_OUTPUT"
- name: Check for existing tag
id: check_tag
run: |
if git tag | grep -q "^${{ steps.calculate_tag.outputs.new_tag }}$"; then
echo "Tag already exists. Exiting."
exit 1
fi
- name: Generate Release Notes
id: generate_release_notes
run: |
if [ "${{ inputs.release_candidate }}" != "true" ]; then
git log $(git describe --tags --abbrev=0 $(git tag --sort=-v:refname | grep -v '-rc' | head -n 1))..HEAD --pretty=format:'- %s%n %b' > release_notes.txt
else
git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'- %s%n %b' > release_notes.txt
fi
awk -i inplace '!seen[$0]++' release_notes.txt
cat release_notes.txt
shell: bash
- name: Apply Tag to Current Commit
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git tag ${{ steps.calculate_tag.outputs.new_tag }}
git push origin ${{ steps.calculate_tag.outputs.new_tag }}
- name: Copy Release to Versioned File
run: |
cp ./output/spearhead.lua ./output/spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua
echo "Versioned file created: spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua"
- name: Create GitHub Release
uses: actions/create-release@v1
id: create_release
with:
tag_name: ${{ steps.calculate_tag.outputs.new_tag }}
release_name: Release ${{ steps.calculate_tag.outputs.new_tag }}
draft: true
body_path: release_notes.txt
prerelease: ${{ inputs.release_candidate }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Spearhead.lua Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/spearhead.lua
asset_name: spearhead.lua
asset_content_type: application/octet-stream
- name: Upload Spearhead.lua Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua
asset_name: spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua
asset_content_type: application/octet-stream
+5 -1
View File
@@ -1,2 +1,6 @@
**/.DS_Store
.DS_Store
.DS_Store
/dist
.vscode/settings.json
+6 -1
View File
@@ -16,5 +16,10 @@
"disableFolding": false
}
},
"livePreview.serverRoot": "/_docs"
"livePreview.serverRoot": "/_docs",
"Lua.workspace.library": [
"/Users/ex61wi/Developer/personal/DcsMissionScriptingTools/dutchies-dcs-scripting-tools/lua-addons",
"c:\\Users\\Tim\\.vscode\\extensions\\dutchie031.dutchies-dcs-scripting-tools-0.0.3\\lua-addons"
],
"dutchies-dcs-scripting-tools.compileAt": "onSave",
}
-1349
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

+18 -11
View File
@@ -32,21 +32,28 @@
<div class="content-wrapper">
<h1 id="welcome">Welcome to Spearhead!</h1>
<h1 id="welcome">Welcome to Spearhead!</h1>
<p>Welcome to the story teller friendly mission framework in DCS (at least, that's what we hope to create).</p>
<p>Together with mission editors we've tried to build a framework that makes creating multiplayer missions as easy as possible.
With the help of naming conventions, trigger zones, pre-configured logic and custom configuration items, it gives a huge amount of versatility.
we're trying to strike a balance between no-code and low-code with the possibility to hook into it for the hard core scripters (like ourselves).
<note-box type="info">
<b>For the scripters</b>: If you yourself are a scripter, and you're missing interfaces, please reach out to us! We want to make sure that the framework is as easy to use for scripters as it is for non-scripters, and we can't do that without your help!
</note-box>
</p>
<p>
Whether this is your first mission or your hundredth, we hope Spearhead will make your life easier.
</p>
<download-spearhead></download-spearhead>
<p>Welcome to the story teller friendly mission framework in DCS (at least, that's what we hope to create).</p>
<p>Together with mission editors we've tried to build a framework that
makes creating multiplayer missions as easy as possible.
With the help of naming conventions, trigger zones, pre-configured
logic and custom configuration items, it gives a huge amount of versatility.
we're trying to strike a balance between no-code and low-code with the possibility to hook into it for
the hard core scripters.</p>
<p id="latest-version">Latest Version: #{VERSION}# created at: #{VERSION_DATE}#</p>
<p id="latest-version">Latest Beta Version: #{BETA_VERSION}# created at: #{BETA_VERSION_DATE}#</p>
<a style="text-decoration: underline;" target="_blank" href="https://github.com/dutchie031/Spearhead/releases">See All releases here</a>
</div>
</div>
</main>
<footer>
+1
View File
@@ -3,3 +3,4 @@ import * as sidebar from "./components/sidebar.js";
import './components/code-block.js';
import './components/code-inline.js';
import './components/note.js';
import './components/latest-version-download.js';
+6 -5
View File
@@ -6,7 +6,7 @@ class Header extends HTMLElement {
}
connectedCallback() {
this.innerHTML = `
this.innerHTML = /*html*/`
<div class="header-box">
<a class="logo" href="/index.html">Spearhead</a>
<span class="subTitle">mission making, made easy</span>
@@ -17,10 +17,11 @@ class Header extends HTMLElement {
<div class="dropdown">
<a href="/pages/tutorials.html">Tutorials</a>
<div class="dropdown-content">
<a href="/pages/tutorials.html">Quick Starts</a>
<a href="/pages/advanced/CAP.html">Advanced: CAP</a>
<a href="/pages/advanced/missions.html">Advanced: Missions</a>
<a href="/pages/advanced/custom-drawings.html">Custom Drawings</a>
<a href="/pages/include-script.html">Include the Script</a>
<a href="/pages/first-start.html">First Start</a>
<a href="/pages/advanced/CAP.html">Advanced: CAP</a>
<a href="/pages/advanced/missions.html">Advanced: Missions</a>
<a href="/pages/advanced/map-markings.html">Map Markings</a>
</div>
</div>
@@ -0,0 +1,119 @@
class DownloadScript extends HTMLElement {
constructor(){
super();
this.cacheKey = 'spearhead-latest-release';
this.cacheExpiryMinutes = 15;
}
getCachedData() {
const cached = localStorage.getItem(this.cacheKey);
if (!cached) return null;
const { data, timestamp } = JSON.parse(cached);
const now = Date.now();
const expiry = this.cacheExpiryMinutes * 60 * 1000; // 15 minutes in ms
if (now - timestamp > expiry) {
localStorage.removeItem(this.cacheKey);
return null;
}
return data;
}
setCachedData(data) {
const cacheItem = {
data: data,
timestamp: Date.now()
};
localStorage.setItem(this.cacheKey, JSON.stringify(cacheItem));
}
async fetchLatestRelease() {
// Check cache first
const cachedData = this.getCachedData();
if (cachedData) {
return cachedData;
}
try {
const response = await fetch('https://api.github.com/repos/dutchie031/Spearhead/releases/latest');
const data = await response.json();
// Cache the data
this.setCachedData(data);
return data;
} catch (error) {
console.error('Error fetching latest release:', error);
return null;
}
}
async fetchReleaseAssets(url){
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching release assets:', error);
return null;
}
}
async connectedCallback() {
var version = "?";
var timestamp = "?";
var size = "?";
var href = "https://github.com/dutchie031/Spearhead/releases"
const latestRelease = await this.fetchLatestRelease();
if (latestRelease) {
version = latestRelease.tag_name;
timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA');
const assets = await this.fetchReleaseAssets(latestRelease.assets_url);
if (assets && assets.length > 0) {
const spearheadAsset = assets.find(asset => asset.name.includes('spearhead'));
if (spearheadAsset) {
size = (spearheadAsset.size / 1024).toFixed(2) + 'kb';
href = spearheadAsset.browser_download_url;
}
}
}
this.innerHTML = /*html*/`
<div class="latest-version-download-box">
<style>
.latest-version-download-box {
display: flex;
}
.latest-version-download-content {
border: 1px solid #ccc;
border-radius: 8px;
padding: 0px 16px;
}
.version-text {
font-weight: bold;
color: #f5efefff
}
</style>
<div class="latest-version-download-content">
<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://github.com/dutchie031/Spearhead/releases">See All Versions here</a>
</p>
</div>
</div>
`;
}
}
customElements.define('download-spearhead', DownloadScript);
+2 -4
View File
@@ -37,10 +37,8 @@
Even though, at the time of writing this, I've written every single line of code myself, I could not have done it without the help of these members.<br>
Therefore it's something WE created.
Our goal was very simple, make it possible to make big dynamic missions, without having to either give away all controll or having to get knee deep into scripting. <br>
We struck a balance.
Our goal was very simple, make it possible to make big dynamic missions, without having to either give away all control or having to get knee deep into scripting. <br>
We struck a balance.
</p>
@@ -25,24 +25,46 @@
<app-sidebar></app-sidebar>
<div class="content-wrapper">
<h1>Advanced Tutorial: Custom Drawings</h1>
<h1>Map Markings</h1>
<p>
Drawings can be very powerful in DCS missions. <br>
They can guide players towards the right objectives or warn them of dangers ahead. <br>
In Spearhead, we provide an easy way to manage and update these drawings dynamically as the mission progresses. <br>
In Spearhead, we have both automatic markers and drawings as well as the possibility to integrate custom drawings.<br>
</p>
<h2 id="custom_drawings_how_to">How to</h2>
<h2 id="automatic_drawings">Automated</h2>
<h3 id="stage_drawings">Stages</h3>
<p>
Spearhead automatically draws stage markings on the map for players to see. <br>
These are drawn when a stage becomes active and removed when the stage is completed. <br>
This gives players a clear visual indication of where they need to go next. <br>
<br>
These can however be disabled in the configuration if desired. <br>
<code-inline>SpearheadConfig.StageConfig.drawStages</code-inline> <br>
<code-inline>SpearheadConfig.StageConfig.drawPreActived</code-inline> <br>
</p>
<h3 id="lastcontact_markings">Last Contact</h3>
<p>
If enabled, Spearhead will draw a marker on the map where the enemy was last killed. <br>
This might help in big multiplayer missions where players log off without sharing intel. <br>
This can be enabled or disabled in the configuration. <br>
<code-inline>SpearheadConfig.StageConfig.markLastContact</code-inline>
</p>
<h2 id="custom_drawings">Custom</h2>
<p>
Adding a custom drawing is easy. <br>
Simply create the drawing and then name it according to the following convention: <br>
<code-inline>DRAWING_[a]:[b]_[FreeForm]</starting></code-inline> <br>
Where:
<ul>
<li><strong>[a]</strong> is the stage number at which the drawing should appear.</li>
<li><strong>[b]</strong> is the stage number at which the drawing should be removed again.</li>
<li><strong>[FreeForm]</strong> is any name you want to make it unique or findables.</li>
<li><code-inline>[a]</code-inline> is the stage number at which the drawing should appear.</li>
<li><code-inline>[b]</code-inline> is the stage number at which the drawing should be removed again.</li>
<li><code-inline>[FreeForm]</code-inline> is any name you want to make it unique or findables.</li>
</ul>
</p>
@@ -51,8 +73,6 @@
This is due to other layers being visible by default and cannot be as easily removed when not needed.
The author layer is not visible by default in multiplayer, making it perfect for this use case.
</note-box>
</div>
</div>
+208
View File
@@ -0,0 +1,208 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spearhead Tutorials</title>
<link rel="stylesheet" href="/style/prism.css">
<link rel="stylesheet" href="/style/style.css">
<script src="/js/prism.js"></script>
<script src="/js/site.js"></script>
<style>
.side-nav a.active {
font-weight: bold;
color: #4fc3f7;
}
</style>
<script type="module" src="../js/components.js"></script>
</head>
<body>
<header>
<app-header></app-header>
</header> <main>
<div class="reference-container">
<app-sidebar></app-sidebar>
<div class="content-wrapper">
<h1>Tutorials</h1>
<p>
This guide is to get you started building your first Spearhead mission. <br />
Spearhead was created to enable the mission maker to worry as little about the running, timing and scripting
and most about the setting and looks and feel of the mission. <br />
In the example we'll show how you can create a simple island hopping mission
</p>
<h2>Include the script</h2>
<p>
Read <a href="/pages/include-script.html">here</a> how to include the Spearhead script in your mission first. <br />
</p>
<h2 id="stages">Stages</h2>
<p>
In general, stages are the backbone of a Spearhead mission. <br>
They define the flow of the mission and what is active at what time. <br>
Don't worry, it can be changed and adapted later on, but it's best to translate your mission idea into stages first. <br>
</p>
<p>
Stages are defined by trigger zones. <br>
They'll be picked up by Spearhead when named according to the naming convention: <br>
<code-inline>MISSIONSTAGE_[OrderNumber]_[FreeForm]</code-inline> <br>
<br>
Stages are divided in primary stages: <code-inline>MISSIONSTAGE_[number]</code-inline> <br>
or secondary stages: <code-inline>MISSIONSTAGE_x[number]</code-inline> <br>
Note the x before the number. This marks it as "extra" <br>
Secondary stages will follow the order number, but are not required to be completed before moving on to the next stage numbers. <br>
</p>
<p>
For this Demo mission we'll create 3 stages. <br>
<code-inline>MissionStage_1_start</code-inline><br>
<code-inline>MissionStage_2_continuation</code-inline><br>
<code-inline>MissionStage_3_finale</code-inline> <br>
Each stage will have it's own trigger zone. <br/>
</p>
<img src="../img/first-start/1.png" alt="The first stages displayed" style="width: 100%;"></img>
<p>
There's 3 stages now. <br>
The first <code-inline>MissionStage_1_start</code-inline> is the first stage and will be active at mission start. <br>
The second <code-inline>MissionStage_2_continuation</code-inline> will be pre-activated when stage 1 is active. <br>
But will be fully activated when stage 1 is complete. <br>
Pre-activated means that the SAM missions and Airbase units will be activated. <br>
This is the difference between SAM and DEAD missions, but let's not get into too much detail at this time. <br>
You can ofcourse create as many stages as you want. <br>
You can also change the amount of stages that get pre-activated. <br>
By default Spearhead will pre-activate 1 stage ahead. <br/>
This can be changed in the configuration. <br/>
</p>
<h2 id="create-missions">Create Missions</h2>
<p>
Now that we've created the stages we can start adding missions to them. <br>
Missions are also defined by trigger zones. <br>
They'll be picked up by Spearhead when named according to the naming convention: <br>
<code-inline>MISSION_[Type]_[FreeForm]</code-inline> <br>
Where <code-inline>[Type]</code-inline> is the type of mission. <a href="./reference.html#mission-zones">See all</a><br>
</p>
<h3 id="mission-cas">CAS</h3>
<p>
Personally CAS is one of my favorite mission types. <br>
Mostly because it's easy to set up and creates a truly immersive experience. <br>
</p>
<h3 id="mission-sam">SAM</h3>
<p>
</p>
<h3 id="mission-strike">STRIKE</h3>
<p>
</p>
<h2 id="setting-up-cap">Setting up CAP</h2>
<p>
If you don't want to use the CAP managers withing Spearhead you can skip this and continue to <a href="#setting-up-the-missions">Setting up Missions</a>. <br />
However CAP is one of the painpoints in a lot of missions and setting up a dynamic feeling airspace can be
quite the challenge. <br />
With the CAP managers we've tried to make this a lot easier. <br />
A CAP group needs to follow the following naming convention: <code-inline>CAP_[A|B][CONFIG]_[Free Form]</code-inline>
For details on config read this: <code-inline>CAP Group Config</code-inline>
For now I set up 3 groups with the following names. <code-inline>CAP_A[1]1_Rota1</code-inline>, <code-inline>CAP_A[1]1_Rota1-1</code-inline>,
<code-inline>CAP_B[1]1_Rota1</code-inline> <br />
The first two are marked with <code-inline>A</code-inline> and will therefore be primary CAP units. They will be
scheduled and make up for the total count. <br />
Meaning that for this airbase there is 2 CAP units max at a time flying out. <br />
In this case all groups have <code-inline>[1]1</code-inline> in the name, (This would be the same as <code-inline>[1]A</code-inline>) which means
that when stage 1 is active the groups will activate and fly out to stage 1.
I also set up a few groups further back. One example: <code-inline>CAP_A[1-3]3_Group1</code-inline>. This group will
protect zone 3 when zones 1 through 3 are active.
CAP units fly out, fly their CAP zone for x amount of minutes and will then RTB. <br />
Before they actually RTB an event is triggered 10 minutes before the actual RTB task. This event
will trigger a backup unit to startup and fly out to take over. <br />
</p>
<note-box type="info" title="Tip">
You can start a mission, speed up the simulation and make the CAP fly out to see what happens
</note-box>
<h3 id="creating-cap-routes">Creating CAP Routes</h3>
<p>
Creating CAP routes is not needed per se, but with a multi-stage stage (we have 2 stages with <code-inline>_1_</code-inline>) it is recommended. <br/>
Similarly with huge stages. <br/>
If there is multiple zones it will "round-robin" over them. <br/>
</p>
<p>
If no CAP route is present the unit will fly a route generated differently per zone: <br/>
<code-inline>quad zone</code-inline> => race-track between the corner closest to the origin airbase to the center point of the zone <br/>
<code-inline>circle zone</code-inline> => race-track between the closest point on circle to the origin airbase to the center <br/>
</p>
<p>
If you want to create your own CAP Routes you can! <br/>
For this example I created 2 CAP routes inside of the 2 <code-inline>_1_</code-inline> stages. <br/>
</p>
<p>
As you can see below there's a nice feature you can exploit. As long as the <code-inline>X</code-inline> of the zone is inside of the the <code-inline>CAPROUTE</code-inline> will be used for that stage!
</p>
<img src="../img/cap_routes.png" alt="CAP Routes Image" style="width: 100%;"></img>
<p>
Well, nice, we're done setting up the initial CAP effort. <br/>
If you want to change values for the CAP routes please read about how to configure it here: <a href="./Reference.html#cap-config">Cap Config</a>
</p>
<h2 id="mission-briefings">Mission Briefings</h2>
<p>
So now we've created some missions we also want to add briefings to them. This is pretty easy with Spearhead. <br/>
</p>
<p>
To do so click on <code-inline>draw</code-inline> on the left hand pane in the mission editor. This opens up the drawing tools in the editor. <br/>
On the right click <code-inline>TextBox</code-inline> and click somewhere inside the zone to which you want to add the briefing. <br/>
Give the briefing a name (It's not used, but can be nice to use to reference the briefing later) and add the briefing. <br/>
The text box is quite small, but can have a lot of text. Easiest is to edit the text in an editor of choice and paste it into the box afterwards. <br/>
</p>
<p>
Keep the binding layer to "Author" only. That way it doesn't show up for anyone other than in the mission editor.
</p>
<p>
See the two images below. The left shows the <code-inline>Text Box</code-inline> drawing. The right shows the briefing as shown in the mission.
</p>
<div style="display: flex">
<div style="flex: 50%">
<img src="../img/briefing_me.png"/>
</div>
<div style="flex: 50%">
<img src="../img/briefing_mission.png"/>
</div>
</div>
<p>
We are now done creating a first mission. Hit fly and test it. <br/>
Check all references for way more features and keep up to date with the latest changes as they come along! <br/>
</p>
</div>
</div>
</main>
<footer>
<p>&copy; 2025 Spearhead Project</p>
</footer>
</body>
</html>
+69
View File
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spearhead Tutorials</title>
<link rel="stylesheet" href="/style/prism.css">
<link rel="stylesheet" href="/style/style.css">
<script src="/js/prism.js"></script>
<script src="/js/site.js"></script>
<style>
.side-nav a.active {
font-weight: bold;
color: #4fc3f7;
}
</style>
<script type="module" src="../js/components.js"></script>
</head>
<body>
<header>
<app-header></app-header>
</header> <main>
<div class="reference-container">
<app-sidebar></app-sidebar>
<div class="content-wrapper">
<h1>Include the Spearhead Script</h1>
<h2 id="include-the-script">Include the Script</h2>
<p>
Download the latest version of Spearhead. You can choose which version you feel comfortable with. <br />
Versions that end in -rc are not stable. Versions that do not have the "release candidate" (rc) tag are. <br>
Stable does not mean bug-free, so if you find any issues let us know!
<br />
Here is the latest stable version:
<download-spearhead></download-spearhead>
</p>
<p>
Then run the script in the mission. <br />
Please do exactly as it's done below. <br />
</p>
<div style="width: 100%;">
<img style="width: 100%;" src="../img/script_install.png"></img>
</div>
<note-box type="info" title="Note">
Spearhead does not require any dependencies (eg. MIST or MOOSE). Compatibility with other frameworks is
not tested at this time, so cannot be guaranteed, but there should be no conflicts if they are not
controlling the same units.
</note-box>
<div class="next-steps">
<a href="/pages/first-start.html">Next: Getting started with Spearhead &rarr;</a>
</div>
</div>
</div>
</main>
<footer>
<p>&copy; 2025 Spearhead Project</p>
</footer>
</body>
</html>
+12 -1
View File
@@ -80,11 +80,22 @@
<strong>Example:</strong> <code-inline>MISSION_DEAD_BYRON</code-inline>
</p>
<p>
Missions are completable objectives with specific types, such as DEAD, BAI, STRIKE, or SAM. <br />
Missions are completable objectives with specific types. <br />
Randomized missions can be defined using the format: <span
class="inline-lua"><span class="lua-variable">RANDOMMISSION_[Type]_[Name]_[Index]</span></span>.
</p>
<p>
Valid Types:
<ul>
<li>SAM</li>
<li>DEAD</li>
<li>BAI</li>
<li>CAS</li>
<li>STRIKE</li>
</ul>
</p>
<h3 id="cap-routes">CAP Routes</h3>
<p>
<strong>Format:</strong> <code-inline>CAPROUTE_[routeID]_[Name]</code-inline> <br />
+35 -37
View File
@@ -66,24 +66,23 @@
These are logically ordered zones that will activate one by one based on the mission status in them. <br />
There is a little more to it, but you'll find out. <br />
Stages need to be named according to the convention: <span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_[OrderNumber]_[FreeForm]</span></span> <br />
The first stage will be called <span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_1</span></span> or <span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_1_EAST</span></span> for example. <br />
Stages need to be named according to the convention: <code-inline>MISSIONSTAGE_[OrderNumber]_[FreeForm]</code-inline> <br />
The first stage will be called <code-inline>MISSIONSTAGE_1</code-inline> or <code-inline>MISSIONSTAGE_1_EAST</code-inline> for example. <br />
<br /><br />
Stages are divided in primary stages: <span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_[number]</span></span> <br />
or secondary stages: <span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_x[number]</span></span> <br />
Stages are divided in primary stages: <code-inline>MISSIONSTAGE_[number]</code-inline> <br />
or secondary stages: <code-inline>MISSIONSTAGE_x[number]</code-inline> <br />
Note the x before the number. This marks it as "extra"
</p>
<p>
For this mission we started with the three stages: <span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_1_GROUND</span></span>,
<span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_2_WATER</span></span> and <span
class="inline-lua"><span class="lua-variable">MISSIONSTAGE_1_AIRBASE</span></span> as you see in the image.
For this mission we started with the three stages: <code-inline>MISSIONSTAGE_1_GROUND</code-inline>,
<code-inline>MISSIONSTAGE_2_WATER</code-inline> and <code-inline>MISSIONSTAGE_1_AIRBASE</code-inline> as you see in the image.
</p>
<img src="../img/starting_stages.png" style="width: 100%;"></img>
<p>
<span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_1_GROUND</span></span> will be activated when the mission starts <br />
<span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_1_WATER</span></span> will be also be activated <br />
<span class="inline-lua"><span class="lua-variable">MISSIONSTAGE_2_AIRBASE</span></span> will be activated when the player enters the zone.
<code-inline>MISSIONSTAGE_1_GROUND</code-inline>will be activated when the mission starts <br />
<code-inline>MISSIONSTAGE_1_WATER</code-inline> will be also be activated <br />
<code-inline>MISSIONSTAGE_2_AIRBASE</code-inline> will be activated when the player enters the zone.
<br />
</p>
@@ -94,20 +93,19 @@
quite the challenge. <br />
With the CAP managers we've tried to make this a lot easier. <br />
A CAP group needs to follow the following naming convention: <span
class="inline-lua"><span class="lua-variable">CAP_[A|B][CONFIG]_[Free Form]</span></span>
A CAP group needs to follow the following naming convention: <code-inline>CAP_[A|B][CONFIG]_[Free Form]</code-inline>
For details on config read this: <span class="inline-ref">CAP Group Config</span>
For details on config read this: <code-inline>CAP Group Config</code-inline>
For now I set up 3 groups with the following names. <span class="inline-lua"><span class="lua-variable">CAP_A[1]1_Rota1</span></span>, <span class="inline-lua"><span class="lua-variable">CAP_A[1]1_Rota1-1</span></span>,
<span class="inline-lua"><span class="lua-variable">CAP_B[1]1_Rota1</span></span> <br />
The first two are marked with <span class="inline-lua"><span class="lua-variable">A</span></span> and will therefore be primary CAP units. They will be
For now I set up 3 groups with the following names. <code-inline>CAP_A[1]1_Rota1</code-inline>, <code-inline>CAP_A[1]1_Rota1-1</code-inline>,
<code-inline>CAP_B[1]1_Rota1</code-inline> <br />
The first two are marked with <code-inline>A</code-inline> and will therefore be primary CAP units. They will be
scheduled and make up for the total count. <br />
Meaning that for this airbase there is 2 CAP units max at a time flying out. <br />
In this case all groups have <span class="inline-lua"><span class="lua-variable">[1]1</span></span> in the name, (This would be the same as <span class="inline-lua"><span class="lua-variable">[1]A</span></span>) which means
In this case all groups have <code-inline>[1]1</code-inline> in the name, (This would be the same as <code-inline>[1]A</code-inline>) which means
that when stage 1 is active the groups will activate and fly out to stage 1.
I also set up a few groups further back. One example: <span class="inline-lua"><span class="lua-variable">CAP_A[1-3]3_Group1</span></span>. This group will
I also set up a few groups further back. One example: <code-inline>CAP_A[1-3]3_Group1</code-inline>. This group will
protect zone 3 when zones 1 through 3 are active.
CAP units fly out, fly their CAP zone for x amount of minutes and will then RTB. <br />
@@ -121,21 +119,21 @@
<h3 id="creating-cap-routes">Creating CAP Routes</h3>
<p>
Creating CAP routes is not needed per se, but with a multi-stage stage (we have 2 stages with <span class="inline-lua"><span class="lua-variable">_1_</span></span>) it is recommended. <br/>
Creating CAP routes is not needed per se, but with a multi-stage stage (we have 2 stages with <code-inline>_1_</code-inline>) it is recommended. <br/>
Similarly with huge stages. <br/>
If there is multiple zones it will "round-robin" over them. <br/>
</p>
<p>
If no CAP route is present the unit will fly a route generated differently per zone: <br/>
<span class="inline-lua"><span class="lua-variable">quad zone</span></span> => race-track between the corner closest to the origin airbase to the center point of the zone <br/>
<span class="inline-lua"><span class="lua-variable">circle zone</span></span> => race-track between the closest point on circle to the origin airbase to the center <br/>
<code-inline>quad zone</code-inline> => race-track between the corner closest to the origin airbase to the center point of the zone <br/>
<code-inline>circle zone</code-inline> => race-track between the closest point on circle to the origin airbase to the center <br/>
</p>
<p>
If you want to create your own CAP Routes you can! <br/>
For this example I created 2 CAP routes inside of the 2 <span class="inline-lua"><span class="lua-variable">_1_</span></span> stages. <br/>
For this example I created 2 CAP routes inside of the 2 <code-inline>_1_</code-inline> stages. <br/>
</p>
<p>
As you can see below there's a nice feature you can exploit. As long as the <span class="inline-lua"><span class="lua-variable">X</span></span> of the zone is inside of the the <span class="inline-lua"><span class="lua-variable">CAPROUTE</span></span> will be used for that stage!
As you can see below there's a nice feature you can exploit. As long as the <code-inline>X</code-inline> of the zone is inside of the the <code-inline>CAPROUTE</code-inline> will be used for that stage!
</p>
<img src="../img/cap_routes.png" alt="CAP Routes Image" style="width: 100%;"></img>
@@ -155,7 +153,7 @@
By default DCS will always keep 1 static object in 1 groups.
</note-box>
<p>
For this example I'll set up two missions. The first one is <span class="inline-lua"><span class="lua-variable">DEAD</span></span> mission and will consist of an SA-2 site with an additional "control center".
For this example I'll set up two missions. The first one is <code-inline>DEAD</code-inline> mission and will consist of an SA-2 site with an additional "control center".
</p>
<h3 id="mission-1-dead">Mission: DEAD</h3>
@@ -173,32 +171,32 @@
</div>
</div>
<p>
Important to note. It's all inside the triggerzone <span class="inline-lua"><span class="lua-variable">MISSION_DEAD_BYRON</span></span>. Which means it's a <span class="inline-lua"><span class="lua-variable">MISSION</span></span> of type <span class="inline-lua"><span class="lua-variable">DEAD</span></span> and with name <span class="inline-lua"><span class="lua-variable">BYRON</span></span>. <br/>
Important to note. It's all inside the triggerzone <code-inline>MISSION_DEAD_BYRON</code-inline>. Which means it's a <code-inline>MISSION</code-inline> of type <code-inline>DEAD</code-inline> and with name <code-inline>BYRON</code-inline>. <br/>
At the start Spearhead will detect the triggerzone, take all units and despawn them and only spawn when needed for better performance. <br/>
</p>
<p>
The current list of mission types are:
<span class="inline-lua"><span class="lua-variable">DEAD</span></span>
<span class="inline-lua"><span class="lua-variable">STRIKE</span></span>
<span class="inline-lua"><span class="lua-variable">BAI</span></span>
<span class="inline-lua"><span class="lua-variable">SAM</span></span> <br/>
<code-inline>DEAD</code-inline>
<code-inline>STRIKE</code-inline>
<code-inline>BAI</code-inline>
<code-inline>SAM</code-inline> <br/>
For specific differences, please check the reference page.
</p>
<p>
Each type has some additional completion logic to it. <br/>
<span class="inline-lua"><span class="lua-variable">DEAD</span></span> and <span class="inline-lua"><span class="lua-variable">SAM</span></span> missions will be marked complete when all air defences are destroyed. This includes Tracking Radars, Self tracking launchers and AAA guns if they are inside the zone. <br/>
If you want to add the Search radar or another random unit like the command tent to the target list you can add a <span class="inline-lua"><span class="lua-variable">TGT_</span></span> prefix to the unit or group you want destroyed. <br/>
Please be aware that adding <span class="inline-lua"><span class="lua-variable">TGT_</span></span> to a group will make the entire group a target and therefore each unit needs to be destroyed. <br/>
<code-inline>DEAD</code-inline> and <code-inline>SAM</code-inline> missions will be marked complete when all air defences are destroyed. This includes Tracking Radars, Self tracking launchers and AAA guns if they are inside the zone. <br/>
If you want to add the Search radar or another random unit like the command tent to the target list you can add a <code-inline>TGT_</code-inline> prefix to the unit or group you want destroyed. <br/>
Please be aware that adding <code-inline>TGT_</code-inline> to a group will make the entire group a target and therefore each unit needs to be destroyed. <br/>
</p>
<h3 id="mission-2-strike">Mission: STRIKE</h3>
<p>
To show the power of <span class="inline-lua"><span class="lua-variable">TGT_</span></span> targets I'll create a strike mission next.
To show the power of <code-inline>TGT_</code-inline> targets I'll create a strike mission next.
</p>
<p>
A nice supply strike mission will do. Add a ship, some containers and some additional units. <br/>
Even some SHORADS to spice the whole thing up. <br/>
In the picture below all units that are selected and who show up as white (they are actually red) have the prefix <span class="inline-lua"><span class="lua-variable">TGT_</span></span> in front of their name. <br/>
In the picture below all units that are selected and who show up as white (they are actually red) have the prefix <code-inline>TGT_</code-inline> in front of their name. <br/>
</p>
<p>
@@ -261,8 +259,8 @@
So now we've created some missions we also want to add briefings to them. This is pretty easy with Spearhead. <br/>
</p>
<p>
To do so click on <span class="inline-ref">draw</span> on the left hand pane in the mission editor. This opens up the drawing tools in the editor. <br/>
On the right click <span class="inline-ref">TextBox</span> and click somewhere inside the zone to which you want to add the briefing. <br/>
To do so click on <code-inline>draw</code-inline> on the left hand pane in the mission editor. This opens up the drawing tools in the editor. <br/>
On the right click <code-inline>TextBox</code-inline> and click somewhere inside the zone to which you want to add the briefing. <br/>
Give the briefing a name (It's not used, but can be nice to use to reference the briefing later) and add the briefing. <br/>
The text box is quite small, but can have a lot of text. Easiest is to edit the text in an editor of choice and paste it into the box afterwards. <br/>
</p>
@@ -270,7 +268,7 @@
Keep the binding layer to "Author" only. That way it doesn't show up for anyone other than in the mission editor.
</p>
<p>
See the two images below. The left shows the <span class="inline-ref">Text Box</span> drawing. The right shows the briefing as shown in the mission.
See the two images below. The left shows the <code-inline>Text Box</code-inline> drawing. The right shows the briefing as shown in the mission.
</p>
<div style="display: flex">
<div style="flex: 50%">
@@ -1,22 +0,0 @@
local GlobalFleetManager = {}
local fleetGroups = {}
GlobalFleetManager.start = function(database)
local logger = Spearhead.LoggerTemplate.new("CARRIERFLEET", "INFO")
local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames()
for _, groupName in pairs(all_groups) do
if Spearhead.Util.startswith(string.lower(groupName), "carriergroup" ) == true then
logger:info("Registering " .. groupName .. " as a managed fleet")
local carrierGroup = Spearhead.internal.FleetGroup:new(groupName, database, logger)
table.insert(fleetGroups, carrierGroup)
end
end
end
if not Spearhead.internal then Spearhead.internal = {} end
Spearhead.internal.GlobalFleetManager = GlobalFleetManager
+621 -621
View File
File diff suppressed because it is too large Load Diff
-82
View File
@@ -1,82 +0,0 @@
--Single player purpose
local defaultLogLevel = "INFO"
if SpearheadConfig and SpearheadConfig.debugEnabled == true then
defaultLogLevel = "DEBUG"
end
local startTime = timer.getTime() * 1000
Spearhead.Events.Init(defaultLogLevel)
local dbLogger = Spearhead.LoggerTemplate.new("database", defaultLogLevel)
local standardLogger = Spearhead.LoggerTemplate.new("", defaultLogLevel)
local databaseManager = Spearhead.DB.New(dbLogger)
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(defaultLogLevel) -- initiate
local capConfig = Spearhead.internal.configuration.CapConfig:new();
local stageConfig = Spearhead.internal.configuration.StageConfig:new();
local startingStage = stageConfig.startingStage or 1
if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then
standardLogger:info("Persistence enabled")
local persistenceLogger = Spearhead.LoggerTemplate.new("Persistence", defaultLogLevel)
Spearhead.classes.persistence.Persistence.Init(persistenceLogger)
local persistanceStage = Spearhead.classes.persistence.Persistence.GetActiveStage()
if persistanceStage then
standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage)
startingStage = persistanceStage
end
else
standardLogger:info("Persistence disabled")
end
local spawnLogger = Spearhead.LoggerTemplate.new("SpawnManager", defaultLogLevel)
local spawnManager = Spearhead.classes.helpers.SpawnManager.new(spawnLogger)
local detectionLogger = Spearhead.LoggerTemplate.new("DetectionManager", defaultLogLevel)
local detectionManager = Spearhead.classes.capClasses.detection.DetectionManager.New(detectionLogger)
Spearhead.classes.capClasses.GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
Spearhead.internal.GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
Spearhead.internal.GlobalFleetManager.start(databaseManager)
local SetStageDelayed = function(number, time)
Spearhead.Events.PublishStageNumberChanged(number)
return nil
end
timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3)
env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms")
local duration = (timer.getTime() * 1000) - startTime
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
Spearhead.LoadingDone()
Spearhead.internal.GlobalStageManager:printFullOverview()
--Check lines of code in directory per file:
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
-- find . -name '*.lua' | xargs wc -l
--- ==================== DEBUG ORDER OR ZONE VEC ===========================
-- local zone = Spearhead.DcsUtil.getZoneByName("MISSIONSTAGE_99")
-- local count = Spearhead.Util.tableLength(zone.verts)
-- for i = 1, count - 1 do
-- local a = zone.verts[i]
-- local b = zone.verts[i+1]
-- local color = {0,0,0,1}
-- color[i] = 1
-- trigger.action.textToAll(-1, 46+i , { x= a.x, y = 0, z = a.z } , color, {0,0,0}, 24 , true , "" .. i )
-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true)
-- end
@@ -44,7 +44,4 @@ function Queue:toList()
return items
end
if Spearhead == nil then Spearhead = {} end
if Spearhead._baseClasses == nil then Spearhead._baseClasses = {} end
Spearhead._baseClasses.Queue = Queue
return Queue
@@ -1,3 +1,6 @@
local SpearheadEvents = require("classes.spearhead_events")
local GlobalStageManager = require("classes.stageClasses.GlobalStageManager")
---@type Array<OnMissionCompleteListener>
local MissionCompleteListeners = {}
@@ -16,18 +19,18 @@ SpearheadAPI = {
return false, "stageNumber " .. stageNumber .. " is not a valid number"
end
Spearhead.Events.PublishStageNumberChanged(stageNumber)
SpearheadEvents.PublishStageNumberChanged(stageNumber)
return true, ""
end,
getCurrentStage = function()
return Spearhead.StageNumber or nil
return GlobalStageManager.getCurrentStage() or nil
end,
isStageComplete = function(stageNumber)
if type(stageNumber) ~= "number" then
return false, "stageNumber " .. stageNumber .. " is not a valid number"
end
local isComplete = Spearhead.internal.GlobalStageManager.isStageComplete(stageNumber)
local isComplete = GlobalStageManager.isStageComplete(stageNumber)
if isComplete == nil then
return nil, "no stage found with number " .. stageNumber
end
@@ -1,3 +1,11 @@
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")
---@class CapBase : OnStageChangedListener
---@field private airbaseName string
---@field private logger table
@@ -54,7 +62,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
local capGroup = Spearhead.classes.capClasses.airGroups.CapGroup.New(name, capConfig, logger, spawnManager)
local capGroup = CapGroup.New(name, capConfig, logger, spawnManager)
if capGroup then
self.capGroupsByName[name] = capGroup
end
@@ -63,7 +71,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
if baseData and baseData.SweepGroups then
for key, name in pairs(baseData.SweepGroups) do
local sweepGroup = Spearhead.classes.capClasses.airGroups.SweepGroup.New(name, capConfig, logger, spawnManager)
local sweepGroup = SweepGroup.New(name, capConfig, logger, spawnManager)
if sweepGroup then
self.sweepGroupsByName[name] = sweepGroup
end
@@ -72,22 +80,21 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
if baseData and baseData.InterceptGroups then
for key, name in pairs(baseData.InterceptGroups) do
local interceptGroup = Spearhead.classes.capClasses.airGroups.InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager)
local interceptGroup = InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager)
if interceptGroup then
self.interceptGroupsByName[name] = interceptGroup
end
end
end
local capFlights = Spearhead.Util.tableLength(self.capGroupsByName)
local sweepFlights = Spearhead.Util.tableLength(self.sweepGroupsByName)
local interceptFlights = Spearhead.Util.tableLength(self.interceptGroupsByName)
local capFlights = Util.tableLength(self.capGroupsByName)
local sweepFlights = Util.tableLength(self.sweepGroupsByName)
local interceptFlights = Util.tableLength(self.interceptGroupsByName)
logger:info(airbaseName .. " : " .. capFlights .." CAP | " .. sweepFlights .. " SWEEP | " .. interceptFlights .. " INTERCEPT")
self:CreateRunwayStrikeMission(database)
Spearhead.Events.AddStageNumberChangedListener(self)
SpearheadEvents.AddStageNumberChangedListener(self)
timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15)
@@ -111,7 +118,7 @@ function CapBase:CreateRunwayStrikeMission(database)
" at airbase " ..
self.airbaseName ..
" with heading " .. runway.course .. " and length " .. runway.length .. " and width " .. runway.width)
local mission = Spearhead.classes.stageClasses.missions.RunwayStrikeMission.new(runway, self.airbaseName,
local mission = RunwayStrikeMission.new(runway, self.airbaseName,
database, self.logger, self.runwayBombingTracker)
self.runwayStrikeMissions[runway.Name] = mission
end
@@ -338,7 +345,7 @@ function CapBase:CheckAndScheduleIntercept()
local unit = Unit.getByName(unitName)
if unit and unit:isExist() then
local unitPos = unit:getPoint()
if Spearhead.Util.is3dPointInZone(unitPos, zone) == true then
if Util.is3dPointInZone(unitPos, zone) == true then
if not unitsToInterceptPerZone[zone.name] then
unitsToInterceptPerZone[zone.name] = {}
end
@@ -356,7 +363,7 @@ function CapBase:CheckAndScheduleIntercept()
for name, targets in pairs(unitsToInterceptPerZone) do
local required = 0
local nrTargets = Spearhead.Util.tableLength(targets)
local nrTargets = Util.tableLength(targets)
if targets and nrTargets > 0 then
required = math.ceil(nrTargets / ratio)
end
@@ -424,7 +431,4 @@ function CapBase:IsBaseActiveWhenStageIsActive(stageNumber)
return false
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
Spearhead.classes.capClasses.CapAirbase = CapBase
return CapBase
@@ -1,3 +1,9 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker")
local CapAirbase = require("classes.capClasses.CapAirbase")
---@class GlobalCapManager
local GlobalCapManager = {}
do
local airbasesPerStage = {}
@@ -17,9 +23,9 @@ do
function GlobalCapManager.start(database, capConfig, detectionManager, stageConfig, logLevel, spawnManager)
if initiated == true then return end
local logger = Spearhead.LoggerTemplate.new("AirbaseManager", logLevel)
local bombTrackLogger = Spearhead.LoggerTemplate.new("RunwayBombingTracker", logLevel)
local runwayBombingTracker = Spearhead.classes.capClasses.runwayBombing.RunwayBombingTracker.new(bombTrackLogger)
local logger = Logger.new("AirbaseManager", logLevel)
local bombTrackLogger = Logger.new("RunwayBombingTracker", logLevel)
local runwayBombingTracker = RunwayBombingTracker.new(bombTrackLogger)
local zones = database:getStagezoneNames()
if zones then
@@ -32,9 +38,9 @@ do
if airbaseNames then
for _, airbaseName in pairs(airbaseNames) do
if airbaseName then
local airbaseSpecificLogger = Spearhead.LoggerTemplate.new("CAP_" .. airbaseName, logLevel)
local airbaseSpecificLogger = Logger.new("CAP_" .. airbaseName, logLevel)
local airbase = Spearhead.classes.capClasses.CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager)
local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager)
if airbase then
table.insert(airbasesPerStage[stageName], airbase)
@@ -46,16 +52,14 @@ do
end
end
logger:info("Initiated " .. Spearhead.Util.tableLength(allAirbasesByName) .. " airbases for cap")
logger:info("Initiated " .. Util.tableLength(allAirbasesByName) .. " airbases for cap")
initiated = true
local InfoFunctions = {}
---returns if there is CAP active
---@param zoneName any
---@param activeZoneNumber number
---@return boolean
InfoFunctions.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber)
GlobalCapManager.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber)
for _, airbase in pairs(airbasesPerStage[zoneName]) do
if airbase:IsBaseActiveWhenStageIsActive(activeZoneNumber) == true then
return true
@@ -63,14 +67,7 @@ do
end
return false
end
Spearhead.capInfo = InfoFunctions
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
Spearhead.classes.capClasses.GlobalCapManager = GlobalCapManager
return GlobalCapManager
@@ -1,3 +1,8 @@
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")
---@class AirGroup : OnUnitLostListener
---@field protected _logger Logger
---@field protected _groupName string
@@ -26,13 +31,13 @@ function AirGroup:New(groupName, groupType, config, logger, spawnManager)
local group = Group.getByName(self._groupName)
if group then
Spearhead.Events.addOnGroupRTBListener(self._groupName, self)
Spearhead.Events.addOnGroupRTBInTenListener(self._groupName, self)
Spearhead.Events.addOnGroupOnStationListener(self._groupName, self)
SpearheadEvents.addOnGroupRTBListener(self._groupName, self)
SpearheadEvents.addOnGroupRTBInTenListener(self._groupName, self)
SpearheadEvents.addOnGroupOnStationListener(self._groupName, self)
for _, unit in pairs(group:getUnits()) do
Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self)
Spearhead.Events.addOnUnitLandEventListener(unit:getName(), self)
SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self)
SpearheadEvents.addOnUnitLandEventListener(unit:getName(), self)
end
spawnManager:DestroyGroup(groupName)
@@ -105,7 +110,7 @@ function AirGroup:SendRTB(airbase)
end
end
if location then
local mission = Spearhead.classes.capClasses.taskings.RTB.getAsMission(airbase, { x= location.x, y= location.z }, self._config)
local mission = RTBMission.getAsMission(airbase, { x= location.x, y= location.z }, self._config)
group:getController():setTask(mission)
self._logger:debug("AirGroup:SendRTB - Task set for group: " .. self._groupName)
end
@@ -248,7 +253,7 @@ function AirGroup:OnLastUnitLanded()
return time + 5
end
if pos and Spearhead.Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then
if pos and Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then
data.lastChangeTime = time
end
data.lastLocations[unit:getName()] = pos
@@ -426,14 +431,10 @@ do --EVENT LISTENERS
end
function AirGroup:Destroy()
Spearhead.DcsUtil.DestroyGroup(self._groupName)
self._spawnManager:DestroyGroup(self._groupName)
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.AirGroup = AirGroup
return AirGroup
---@alias AirGroupState
---| "UnSpawned"
@@ -1,3 +1,7 @@
local AirGroup = require("classes.capClasses.airGroups.AirGroup")
local CAP = require("classes.capClasses.taskings.CAP")
local Util = require("classes.util.Util")
local MissionEditorWarner = require("classes.util.MissionEditorWarnings")
---@class CapGroup : AirGroup
---@field private _targetZoneIdPerStage table<string, string>
@@ -14,9 +18,9 @@ CapGroup.__index = CapGroup
---@return CapGroup
function CapGroup.New(groupName, config, logger, spawnManager)
setmetatable(CapGroup, Spearhead.classes.capClasses.airGroups.AirGroup)
setmetatable(CapGroup, AirGroup)
local self = setmetatable({}, CapGroup) --[[@as CapGroup]]
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "CAP", config, logger, spawnManager)
AirGroup.New(self, groupName, "CAP", config, logger, spawnManager)
self._targetZoneIdPerStage = {}
@@ -65,10 +69,10 @@ function CapGroup:SendToZone(zone, targetZoneID, airbase)
end
if isInAir == true then
local mission = Spearhead.classes.capClasses.taskings.CAP.getAsMission(self._groupName, airbase, zone, self._config)
local mission = CAP.getAsMission(self._groupName, airbase, zone, self._config)
self:SetMission(mission)
else
local mission = Spearhead.classes.capClasses.taskings.CAP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
local mission = CAP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
if mission then
self:SetMission(mission)
else
@@ -80,8 +84,8 @@ end
---@private
function CapGroup:InitWithName(groupName)
local split_string = Spearhead.Util.split_string(groupName, "_")
local partCount = Spearhead.Util.tableLength(split_string)
local split_string = Util.split_string(groupName, "_")
local partCount = Util.tableLength(split_string)
if partCount >= 3 then
local configPart = split_string[2]
@@ -95,34 +99,34 @@ function CapGroup:InitWithName(groupName)
elseif first == "[" then
self._isBackup = false
else
Spearhead.AddMissionEditorWarning("Could not parse the CAP config for group: " .. groupName)
MissionEditorWarner.Add("Could not parse the CAP config for group: " .. groupName)
return
end
local subsplit = Spearhead.Util.split_string(configPart, "|")
local subsplit = Util.split_string(configPart, "|")
if subsplit then
for key, value in pairs(subsplit) do
local keySplit = Spearhead.Util.split_string(value, "]")
local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do
local dashSeperated = Spearhead.Util.split_string(value, "-")
if Spearhead.Util.tableLength(dashSeperated) > 1 then
local dashSeperated = Util.split_string(value, "-")
if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2])
for i = from, till do
if Spearhead.Util.strContains(targetZone, "A") == true then
if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
else
self._targetZoneIdPerStage[tostring(i)] = targetZone
end
end
else
if Spearhead.Util.strContains(targetZone, "A") == true then
if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
else
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
@@ -132,15 +136,11 @@ function CapGroup:InitWithName(groupName)
end
end
env.info("Capgroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
env.info("Capgroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage))
else
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
MissionEditorWarner.Add("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.CapGroup = CapGroup
return CapGroup
@@ -1,3 +1,9 @@
local AirGroup = require("classes.capClasses.airGroups.AirGroup")
local INTERCEPT = require("classes.capClasses.taskings.INTERCEPT")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local MissionEditorWarner = require("classes.util.MissionEditorWarnings")
---@class InterceptGroup : AirGroup
---@field private _targetNames Array<string>
---@field private _targetZoneName string
@@ -22,9 +28,9 @@ InterceptGroup.__index = InterceptGroup
---@return InterceptGroup?
function InterceptGroup.New(groupName, config, logger, detectionManager, spawnManager)
setmetatable(InterceptGroup, Spearhead.classes.capClasses.airGroups.AirGroup)
setmetatable(InterceptGroup, AirGroup)
local self = setmetatable({}, InterceptGroup)
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "INTERCEPT", config, logger, spawnManager)
AirGroup.New(self, groupName, "INTERCEPT", config, logger, spawnManager)
local group = Group.getByName(groupName)
if not group then
@@ -80,7 +86,9 @@ function InterceptGroup:SetTargetUnits(unitNames)
self:UpdateTask()
if self._updateTaskID then
timer.removeFunction(self._updateTaskID)
pcall(function()
timer.removeFunction(self._updateTaskID)
end)
end
local updateContinous = function(selfA, time)
@@ -133,7 +141,7 @@ function InterceptGroup:GetClosestTarget()
local targetUnit = Unit.getByName(targetName)
if targetUnit and targetUnit:isExist() then
local pos = targetUnit:getPoint()
local distance = Spearhead.Util.VectorDistance3d(groupPoint, pos)
local distance = Util.VectorDistance3d(groupPoint, pos)
if distance < closestDistance then
closestDistance = distance
closestUnit = targetUnit
@@ -177,7 +185,7 @@ function InterceptGroup:UpdateTask()
end
end
if Spearhead.DcsUtil.IsBingoFuel(self._groupName) then
if DcsUtil.IsBingoFuel(self._groupName) then
self._logger:debug("InterceptGroup: " .. self._groupName .. " is at bingo fuel, returning to base")
self:SendRTB(self._airbase)
return nil -- Return to base if bingo fuel
@@ -185,7 +193,7 @@ function InterceptGroup:UpdateTask()
if selfDetected and self:IsInAir() == true then
if self._currentTargetName and self._currentTargetName == closestUnit:getName() then
local distance = Spearhead.Util.VectorDistance3d(closestUnit:getPoint(), groupPoint)
local distance = Util.VectorDistance3d(closestUnit:getPoint(), groupPoint)
if distance < 30 * 1852 then
-- If the target is within 10 nautical miles, continue attacking
self._logger:debug("InterceptGroup: " .. self._groupName .. " continues attacking target " .. closestUnit:getName())
@@ -197,7 +205,7 @@ function InterceptGroup:UpdateTask()
local vec3 = closestUnit:getPoint()
local vec2 = { x = vec3.x, y = vec3.z }
local groupPointVec2 = { x = groupPoint.x, y = groupPoint.z }
local mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt)
local mission = INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt)
self:SetMission(mission)
self._currentTargetName = closestUnit:getName()
return 15 -- Just continue attacking the detected target
@@ -217,7 +225,7 @@ function InterceptGroup:UpdateTask()
local mission = nil
if self:IsInAir() == true then
-- If the group is in the air, create an intercept mission
mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromInAir(
mission = INTERCEPT.getMissionFromInAir(
self._groupName,
{ x = groupPoint.x, y = groupPoint.z },
interceptPoint,
@@ -227,7 +235,7 @@ function InterceptGroup:UpdateTask()
alt
)
else
mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromAirbase(
mission = INTERCEPT.getMissionFromAirbase(
self._groupName,
interceptPoint,
self._airbase,
@@ -308,33 +316,33 @@ end
---@private
function InterceptGroup:InitWithName(groupName)
local split_string = Spearhead.Util.split_string(groupName, "_")
local partCount = Spearhead.Util.tableLength(split_string)
local split_string = Util.split_string(groupName, "_")
local partCount = Util.tableLength(split_string)
if partCount >= 3 then
local configPart = split_string[2]
configPart = string.sub(configPart, 2, #configPart)
local subsplit = Spearhead.Util.split_string(configPart, "|")
local subsplit = Util.split_string(configPart, "|")
if subsplit then
for key, value in pairs(subsplit) do
local keySplit = Spearhead.Util.split_string(value, "]")
local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do
local dashSeperated = Spearhead.Util.split_string(value, "-")
if Spearhead.Util.tableLength(dashSeperated) > 1 then
local dashSeperated = Util.split_string(value, "-")
if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2])
for i = from, till do
if Spearhead.Util.strContains(targetZone, "A") == true then
if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
else
self._targetZoneIdPerStage[tostring(i)] = targetZone
end
end
else
if Spearhead.Util.strContains(targetZone, "A") == true then
if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
else
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
@@ -344,16 +352,12 @@ function InterceptGroup:InitWithName(groupName)
end
end
env.info("interceptGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
env.info("interceptGroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage))
else
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
MissionEditorWarner.Add("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.InterceptGroup = InterceptGroup
return InterceptGroup
@@ -1,3 +1,8 @@
local AirGroup = require("classes.capClasses.airGroups.AirGroup")
local SWEEP = require("classes.capClasses.taskings.SWEEP")
local Util = require("classes.util.Util")
local MissionEditorWarner = require("classes.util.MissionEditorWarnings")
---@class SweepGroup : AirGroup
---@field _targetZoneIdPerStage table<number, string>
---@field _currentTargetZoneID string?
@@ -11,9 +16,9 @@ SweepGroup.__index = SweepGroup
---@param spawnManager SpawnManager
---@return SweepGroup
function SweepGroup.New(groupName, config, logger, spawnManager)
setmetatable(SweepGroup, Spearhead.classes.capClasses.airGroups.AirGroup)
setmetatable(SweepGroup, AirGroup)
local self = setmetatable({}, SweepGroup) --[[@as SweepGroup]]
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "SWEEP", config, logger, spawnManager)
AirGroup.New(self, groupName, "SWEEP", config, logger, spawnManager)
self._targetZoneIdPerStage = {}
self:InitWithName(groupName)
@@ -47,7 +52,7 @@ function SweepGroup:SendToZone(zone, targetZoneID, airbase)
local group = Group.getByName(self._groupName)
local mission = Spearhead.classes.capClasses.taskings.SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
local mission = SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
if mission then
---@type SetTaskParams
local params = {
@@ -68,35 +73,35 @@ end
---@private
function SweepGroup:InitWithName(groupName)
local split_string = Spearhead.Util.split_string(groupName, "_")
local partCount = Spearhead.Util.tableLength(split_string)
local split_string = Util.split_string(groupName, "_")
local partCount = Util.tableLength(split_string)
if partCount >= 3 then
local configPart = split_string[2]
configPart = string.sub(configPart, 2, #configPart)
local subsplit = Spearhead.Util.split_string(configPart, "|")
local subsplit = Util.split_string(configPart, "|")
if subsplit then
for key, value in pairs(subsplit) do
local keySplit = Spearhead.Util.split_string(value, "]")
local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do
local dashSeperated = Spearhead.Util.split_string(value, "-")
if Spearhead.Util.tableLength(dashSeperated) > 1 then
local dashSeperated = Util.split_string(value, "-")
if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2])
for i = from, till do
if Spearhead.Util.strContains(targetZone, "A") == true then
if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
else
self._targetZoneIdPerStage[tostring(i)] = targetZone
end
end
else
if Spearhead.Util.strContains(targetZone, "A") == true then
if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A",
tostring(dashSeperated[1]))
else
@@ -107,15 +112,11 @@ function SweepGroup:InitWithName(groupName)
end
end
env.info("SweepGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
env.info("SweepGroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage))
else
Spearhead.AddMissionEditorWarning("SWEEP Group with name: " ..
MissionEditorWarner.Add("SWEEP Group with name: " ..
groupName .. "should have at least 3 parts, but has " .. partCount)
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.SweepGroup = SweepGroup
return SweepGroup
@@ -161,8 +161,4 @@ function DetectionManager:UpdateDetectedUnitsByCoalition(coalitionSide)
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.detection then Spearhead.classes.capClasses.detection = {} end
Spearhead.classes.capClasses.detection.DetectionManager = DetectionManager
return DetectionManager
@@ -1,3 +1,6 @@
local SpearheadEvents = require("classes.spearhead_events")
local Util = require("classes.util.Util")
---@class RunwayBombingTracker : OnWeaponFiredListener
---@field trackedRunways table<Runway, RunwayStrikeMission>
---@field private _logger Logger
@@ -11,8 +14,7 @@ function RunwayBombingTracker.new(logger)
local self = setmetatable({}, RunwayBombingTracker)
self._logger = logger
Spearhead.Events.AddWeaponFiredListener(self)
SpearheadEvents.AddWeaponFiredListener(self)
return self
end
@@ -111,15 +113,10 @@ function RunwayBombingTracker:OnWeaponImpact(weaponDesc, impactPoint)
local zone= strikeMission:GetRunwayZone()
if Spearhead.Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
strikeMission:RunwayHit(impactPoint, explosiveMass)
end
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.runwayBombing then Spearhead.classes.capClasses.runwayBombing = {} end
Spearhead.classes.capClasses.runwayBombing.RunwayBombingTracker = RunwayBombingTracker
return RunwayBombingTracker
@@ -1,3 +1,6 @@
local Util = require("classes.util.Util")
local RTB = require("classes.capClasses.taskings.RTB")
---@class CAPTasking
local CAP = {}
@@ -38,7 +41,7 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
for indexA, pointA in ipairs(capZone.verts) do
for indexB, pointB in ipairs(capZone.verts) do
if pointA ~= pointB then
local distance = Spearhead.Util.VectorDistance2d(pointA, pointB)
local distance = Util.VectorDistance2d(pointA, pointB)
if distance > furthestDistance then
furthestDistance = distance
furthestA = indexA
@@ -58,12 +61,12 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
local furthest = pointA
local closest = pointB
local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB)
local heading = Util.vectorHeadingFromTo(pointA, pointB)
if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then
if Util.VectorDistance2d(baseVec2, pointB) > Util.VectorDistance2d(baseVec2, pointA) then
furthest = pointB
closest = pointA
heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA)
heading = Util.vectorHeadingFromTo(pointB, pointA)
end
local distance = furthestDistance
@@ -87,8 +90,8 @@ end
local GetOutboundTask = function(airbase, capZone, capConfig)
local airbaseVec3 = airbase:getPoint()
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520)
local heading = Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
local point = Util.vectorMove(airbaseVec2, heading, 18520)
return {
alt = 2000,
@@ -130,9 +133,9 @@ function CAP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig)
[1] = GetOutboundTask(airbase, capZone, capConfig),
[2] = GetOutboundTask(airbase, capZone, capConfig),
[3] = CAP.getAsTasking(groupName, airbase, capZone, capConfig),
[4] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig),
[5] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
[6] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
[4] = RTB.getApproachPoint(airbase, capZone.location, capConfig),
[5] = RTB.getInitialPoint(airbase),
[6] = RTB.getLandingPoint(airbase)
}
local mission = {
@@ -155,9 +158,9 @@ end
function CAP.getAsMission(groupName, airbase, capZone, capConfig)
local points = {
[1] = CAP.getAsTasking(groupName, airbase, capZone, capConfig),
[2] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig),
[3] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
[4] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
[2] = RTB.getApproachPoint(airbase, capZone.location, capConfig),
[3] = RTB.getInitialPoint(airbase),
[4] = RTB.getLandingPoint(airbase)
}
local mission = {
@@ -320,8 +323,4 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig)
}
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.CAP = CAP
return CAP
@@ -1,4 +1,5 @@
local Util = require("classes.util.Util")
local RTB = require("classes.capClasses.taskings.RTB")
---@class InterceptTasking
local INTERCEPT = {}
@@ -14,11 +15,11 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con
local airbaseVec3 = airbase:getPoint()
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, interceptPoint)
local heading = Util.vectorHeadingFromTo(airbaseVec2, interceptPoint)
env.info("BLAAT: heading from runway to first point: " .. heading)
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 3*1852)
local point = Util.vectorMove(airbaseVec2, heading, 3*1852)
local pointA, pointB, pointC, pointD = INTERCEPT.getInterceptTaskPoint(groupName, point, interceptPoint, airbaseVec2, config, speed, alt)
@@ -28,9 +29,9 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con
[3] = pointB,
[4] = pointC,
[5] = pointD,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
[6] = RTB.getApproachPoint(airbase, interceptPoint, config),
[7] = RTB.getInitialPoint(airbase),
[8] = RTB.getLandingPoint(airbase)
}
local mission = {
@@ -67,9 +68,9 @@ function INTERCEPT.getMissionFromInAir(groupName, currentPosition, interceptPoin
[3] = pointB,
[4] = pointC,
[5] = pointD,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
[6] = RTB.getApproachPoint(airbase, interceptPoint, config),
[7] = RTB.getInitialPoint(airbase),
[8] = RTB.getLandingPoint(airbase)
}
local mission = {
@@ -109,9 +110,9 @@ function INTERCEPT.getUnitInterceptMissionFromAir(groupName, currentPoint, targe
[3] = pointB,
[4] = pointC,
[5] = pointD,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, currentPoint, config),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
[6] = RTB.getApproachPoint(airbase, currentPoint, config),
[7] = RTB.getInitialPoint(airbase),
[8] = RTB.getLandingPoint(airbase)
}
local mission = {
@@ -392,8 +393,4 @@ function INTERCEPT.getUnitInterceptTaskPoint(groupName, currentPoint, targetPosi
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.INTERCEPT = INTERCEPT
return INTERCEPT
@@ -1,3 +1,4 @@
local Util = require("classes.util.Util")
---@class RTBTasking
local RTB = {}
@@ -33,7 +34,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
local runways = airbase:getRunways()
local windVec = atmosphere.getWind(airbase:getPoint())
local mPerS = Spearhead.Util.vectorMagnitude(windVec)
local mPerS = Util.vectorMagnitude(windVec)
if mPerS > 0 then
for i = 1, #runways do
@@ -48,7 +49,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
end
local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0}
local alignment = Spearhead.Util.vectorAlignment(windVec, runwayVec)
local alignment = Util.vectorAlignment(windVec, runwayVec)
if minAlignment == nil or alignment < minAlignment then
activeCourse = i
@@ -68,7 +69,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
end
local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0}
local alignment = Spearhead.Util.vectorAlignment(windVec, runwayVec)
local alignment = Util.vectorAlignment(windVec, runwayVec)
if minAlignment == nil or alignment < minAlignment then
activeCourse = i
@@ -102,7 +103,7 @@ local function calcInitialPoint(airbase)
local flipped = (heading + 180) % 360
local basePoint = airbase:getPoint()
local basePointVec2 = {x = basePoint.x, y = basePoint.z}
local point = Spearhead.Util.vectorMove(basePointVec2, flipped, 22000)
local point = Util.vectorMove(basePointVec2, flipped, 22000)
return point, flipped
@@ -116,10 +117,10 @@ end
function RTB.getApproachPoint(airbase, missionPoint, capConfig)
local initialPoint, headingFromRunway = calcInitialPoint(airbase)
local pointA = Spearhead.Util.vectorMove(initialPoint, headingFromRunway - 45, 9000)
local pointB = Spearhead.Util.vectorMove(initialPoint, headingFromRunway + 45, 9000)
local pointA = Util.vectorMove(initialPoint, headingFromRunway - 45, 9000)
local pointB = Util.vectorMove(initialPoint, headingFromRunway + 45, 9000)
if Spearhead.Util.VectorDistance2d(missionPoint, pointA) > Spearhead.Util.VectorDistance2d(missionPoint, pointB) then
if Util.VectorDistance2d(missionPoint, pointA) > Util.VectorDistance2d(missionPoint, pointB) then
pointA = pointB
end
@@ -194,8 +195,4 @@ function RTB.getLandingPoint(airbase)
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.RTB = RTB
return RTB
@@ -1,3 +1,6 @@
local Util = require("classes.util.Util")
local RTB = require("classes.capClasses.taskings.RTB")
---@class SWEEPTasking
local SWEEP = {}
@@ -31,7 +34,7 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
for indexA, pointA in ipairs(capZone.verts) do
for indexB, pointB in ipairs(capZone.verts) do
if pointA ~= pointB then
local distance = Spearhead.Util.VectorDistance2d(pointA, pointB)
local distance = Util.VectorDistance2d(pointA, pointB)
if distance > furthestDistance then
furthestDistance = distance
furthestA = indexA
@@ -51,12 +54,12 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
local furthest = pointA
local closest = pointB
local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB)
local heading = Util.vectorHeadingFromTo(pointA, pointB)
if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then
if Util.VectorDistance2d(baseVec2, pointB) > Util.VectorDistance2d(baseVec2, pointA) then
furthest = pointB
closest = pointA
heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA)
heading = Util.vectorHeadingFromTo(pointB, pointA)
end
local distance = furthestDistance
@@ -80,8 +83,8 @@ end
local GetOutboundTask = function(airbase, capZone, capConfig)
local airbaseVec3 = airbase:getPoint()
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520)
local heading = Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
local point = Util.vectorMove(airbaseVec2, heading, 18520)
return {
alt = 2000,
@@ -128,9 +131,9 @@ function SWEEP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig)
[3] = pointA,
[4] = pointB,
[5] = pointC,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
[6] = RTB.getApproachPoint(airbase, capZone.location, capConfig),
[7] = RTB.getInitialPoint(airbase),
[8] = RTB.getLandingPoint(airbase)
}
local mission = {
@@ -280,8 +283,4 @@ function SWEEP.getAsTasking(groupName, airbase, capZone, capConfig)
return pointA, pointB, pointC
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.SWEEP = SWEEP
return SWEEP
@@ -95,6 +95,4 @@ function CapConfig:getDeathDelay()
return self._deathDelay
end
if not Spearhead.internal then Spearhead.internal = {} end
if not Spearhead.internal.configuration then Spearhead.internal.configuration = {} end
Spearhead.internal.configuration.CapConfig = CapConfig;
return CapConfig
@@ -29,6 +29,5 @@ function GlobalConfig:getBriefingTime()
end
if Spearhead == nil then Spearhead = {} end
Spearhead.GlobalConfig = GlobalConfig.New()
return GlobalConfig
@@ -7,8 +7,6 @@
--- @field startingStage integer
--- @field maxMissionsPerStage integer
--- @field AmountPreactivateStage integer
local StageConfig = {};
---comment
@@ -34,6 +32,4 @@ function StageConfig:new()
return o;
end
if not Spearhead.internal then Spearhead.internal = {} end
if not Spearhead.internal.configuration then Spearhead.internal.configuration = {} end
Spearhead.internal.configuration.StageConfig = StageConfig;
return StageConfig
@@ -1,3 +1,10 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local MissionEditorWarning = require("classes.util.MissionEditorWarnings")
local RouteUtil = require("classes.spearhead_routeutil")
local SpearheadEvents = require("classes.spearhead_events")
---@class FleetGroup
local FleetGroup = {}
---comment
@@ -13,9 +20,9 @@ function FleetGroup:new(fleetGroupName, database, logger)
o.fleetGroupName = fleetGroupName
o.logger = logger
local split_name = Spearhead.Util.split_string(fleetGroupName, "_")
if Spearhead.Util.tableLength(split_name) < 2 then
Spearhead.AddMissionEditorWarning("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_<fleetname>")
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]
@@ -27,12 +34,12 @@ function FleetGroup:new(fleetGroupName, database, logger)
do --INIT
local carrierRouteZones = database:getCarrierRouteZones()
for _, zoneName in pairs(carrierRouteZones) do
if Spearhead.Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then
local zone = Spearhead.DcsUtil.getZoneByName(zoneName)
if zone and zone.zone_type == Spearhead.DcsUtil.ZoneType.Polygon then
local split_string = Spearhead.Util.split_string(zoneName, "_")
if Spearhead.Util.tableLength(split_string) < 3 then
Spearhead.AddMissionEditorWarning(
if Util.strContains(string.lower(zoneName), "_".. string.lower(o.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, "_")
if Util.tableLength(split_string) < 3 then
MissionEditorWarning.Add(
"CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName)
else
@@ -48,7 +55,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
for ii = i + 1, 4 do
local a = zone.verts[i]
local b = zone.verts[ii]
local dist = Spearhead.Util.VectorDistance2d(a, b)
local dist = Util.VectorDistance2d(a, b)
if biggest == nil or dist > biggest then
biggestA = a
@@ -65,17 +72,17 @@ function FleetGroup:new(fleetGroupName, database, logger)
return nil, nil
end
if Spearhead.Util.startswith(namePart, "%[") == true then
namePart = Spearhead.Util.split_string(namePart, "[")[1]
if Util.startswith(namePart, "%[") == true then
namePart = Util.split_string(namePart, "[")[1]
end
if Spearhead.Util.strContains(namePart, "%]") == true then
namePart = Spearhead.Util.split_string(namePart, "]")[1]
if Util.strContains(namePart, "%]") == true then
namePart = Util.split_string(namePart, "]")[1]
end
local split_numbers = Spearhead.Util.split_string(namePart, "-")
if Spearhead.Util.tableLength(split_numbers) < 2 then
Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
local split_numbers = Util.split_string(namePart, "-")
if Util.tableLength(split_numbers) < 2 then
MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
return nil, nil
end
@@ -83,7 +90,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
local second = tonumber(split_numbers[2])
if first == nil or second == nil then
Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
return nil, nil
end
return first, second
@@ -97,11 +104,11 @@ function FleetGroup:new(fleetGroupName, database, logger)
end
o.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} }
else
Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
end
end
else
Spearhead.AddMissionEditorWarning("CARRIERROUTE cannot be a cilinder: " .. zoneName)
MissionEditorWarning.Add("CARRIERROUTE cannot be a cilinder: " .. zoneName)
end
end
end
@@ -115,7 +122,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
local group = Group.getByName(groupName)
if group then
logger:info("Sending " .. fleetGroupName .. " to " .. targetZone)
logger:info("Sending " .. groupName .. " to " .. targetZone)
group:getController():setTask(task)
end
end
@@ -124,14 +131,13 @@ function FleetGroup:new(fleetGroupName, database, logger)
local targetZone = self.targetZonePerStage[tostring(number)]
if targetZone and targetZone ~= self.currentTargetZone then
local points = self.pointsPerZone[targetZone]
local task = Spearhead.RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB)
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
end
Spearhead.Events.AddStageNumberChangedListener(o)
SpearheadEvents.AddStageNumberChangedListener(o)
return o
end
if not Spearhead.internal then Spearhead.internal = {} end
Spearhead.internal.FleetGroup = FleetGroup
return FleetGroup
@@ -0,0 +1,26 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local FleetGroup = require("classes.fleetClasses.FleetGroup")
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
---@class GlobalFleetManager
local GlobalFleetManager = {}
local fleetGroups = {}
GlobalFleetManager.start = function(database)
local logger = Logger.new("CARRIERFLEET", "INFO")
local all_groups = MizGroupsManager.getAllGroupNames()
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)
table.insert(fleetGroups, carrierGroup)
end
end
end
return GlobalFleetManager
@@ -1,3 +1,5 @@
local DcsUtil = require("classes.util.DcsUtil")
---@class SpawnData
---@field groupTemplate table
---@field isStatic boolean
@@ -14,17 +16,17 @@ MizGroupsManager._spawnTemplateData = {}
do --init
for coalition_name, coalition_data in pairs(env.mission.coalition) do
local coalition_nr = Spearhead.DcsUtil.stringToCoalition(coalition_name)
local coalition_nr = DcsUtil.stringToCoalition(coalition_name)
if coalition_data.country then
for country_index, country_data in pairs(coalition_data.country) do
for category_name, categorydata in pairs(country_data) do
local category_id = Spearhead.DcsUtil.stringToGroupCategory(category_name)
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
local name = group.name
local skippable = false
local isStatic = false
if category_id == Spearhead.DcsUtil.GroupCategory.STATIC then
if category_id == DcsUtil.GroupCategory.STATIC then
isStatic = true
local unit = group.units[1]
if unit and unit.category == "Heliports" then
@@ -75,7 +77,4 @@ function MizGroupsManager.getSpawnTemplateData(groupName)
return MizGroupsManager._spawnTemplateData[groupName]
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.helpers then Spearhead.classes.helpers = {} end
Spearhead.classes.helpers.MizGroupsManager = MizGroupsManager
return MizGroupsManager
@@ -1,3 +1,8 @@
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
local Persistence = require("classes.persistence.Persistence")
local SpearheadEvents = require("classes.spearhead_events")
local Util = require("classes.util.Util")
---@class SpawnManager : OnUnitLostListener
---@field private _persistedUnits table<string,boolean>
---@field private _logger Logger
@@ -29,7 +34,7 @@ end
---@return boolean isStatic
function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant)
local spawnData = Spearhead.classes.helpers.MizGroupsManager.getSpawnTemplateData(groupName)
local spawnData = MizGroupsManager.getSpawnTemplateData(groupName)
if spawnData == nil then
env.error("SpawnManager:SpawnGroup - No spawn template found for group: " .. groupName)
@@ -67,7 +72,7 @@ end
---@param groupName string
---@return boolean
function SpawnManager:IsGroupStatic(groupName)
local isStatic = Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName)
local isStatic = MizGroupsManager.IsGroupStatic(groupName)
if isStatic ~= nil then return isStatic end
return StaticObject.getByName(groupName) ~= nil
@@ -89,7 +94,7 @@ function SpawnManager:OnUnitLost(object)
heading = heading
end
Spearhead.classes.persistence.Persistence.UnitKilled(
Persistence.UnitKilled(
name,
object:getPoint(),
heading,
@@ -119,7 +124,7 @@ do --- privates
country = override.countryID or spawnData.country
end
local spawnTemplate = Spearhead.Util.deepCopyTable(spawnData.groupTemplate)
local spawnTemplate = Util.deepCopyTable(spawnData.groupTemplate)
---@type Array<string>
local removeableUnitNames = {}
--[[
@@ -131,8 +136,8 @@ do --- privates
for _, unit in pairs(spawnTemplate["units"]) do
local name = unit["name"]
Spearhead.Events.addOnUnitLostEventListener(name, self)
local state = Spearhead.classes.persistence.Persistence.UnitState(name)
SpearheadEvents.addOnUnitLostEventListener(name, self)
local state = Persistence.UnitState(name)
if state then
if state.isDead == true then
removeableUnitNames[#removeableUnitNames+1] = name
@@ -170,7 +175,7 @@ do --- privates
if isPersistent == true then
self._persistedUnits[unit:getName()] = true
end
Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self)
SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self)
end
return group
@@ -195,10 +200,10 @@ do --- privates
country = overrides.countryID or spawnData.country
end
local spawnTemplate = Spearhead.Util.deepCopyTable(spawnData.groupTemplate)
local spawnTemplate = Util.deepCopyTable(spawnData.groupTemplate)
--for static Objecst groupNames and unit names are the same and is always 1:1
local persistentState = Spearhead.classes.persistence.Persistence.UnitState(groupName)
local persistentState = Persistence.UnitState(groupName)
if persistentState then
if persistentState.isDead == true then
spawnTemplate["dead"] = true
@@ -226,7 +231,7 @@ do --- privates
if isPersistent == true then
self._persistedUnits[groupName] = true
Spearhead.Events.addOnUnitLostEventListener(groupName, self)
SpearheadEvents.addOnUnitLostEventListener(groupName, self)
end
return object
@@ -238,7 +243,7 @@ do --- privates
if not unit then return end
local deadState = Spearhead.classes.persistence.Persistence.UnitState(unit:getName())
local deadState = Persistence.UnitState(unit:getName())
if deadState and deadState.isDead == true then
unit:destroy() -- Destroy the unit if it is dead
local staticObject = {
@@ -255,10 +260,7 @@ do --- privates
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.helpers then Spearhead.classes.helpers = {} end
Spearhead.classes.helpers.SpawnManager = SpawnManager
return SpawnManager
--Old Spawn Method
@@ -1,4 +1,6 @@
local Util = require("classes.util.Util")
---@class Perstistence
---@field private _path string
---@field private _updateRequired boolean
@@ -81,7 +83,7 @@ do
if lua then
tables = lua
logger:debug("Loaded persistence data: " .. Spearhead.Util.toString(lua))
logger:debug("Loaded persistence data: " .. Util.toString(lua))
else
logger:error("Could not load persistence file, using default tables")
end
@@ -143,8 +145,8 @@ do
local latestFile, lastNumber = default, 0
for file in lfs.dir(dir) do
local split = Spearhead.Util.split_string(file, ".")
local doesStartWith = Spearhead.Util.startswith(file, startsWith, true)
local split = Util.split_string(file, ".")
local doesStartWith = Util.startswith(file, startsWith, true)
if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
local numberString = split[#split-1]
local number = tonumber(numberString)
@@ -179,7 +181,7 @@ do
if SpearheadConfig.Persistence.fileName then
local userFileName = SpearheadConfig.Persistence.fileName
local split = Spearhead.Util.split_string(userFileName, ".")
local split = Util.split_string(userFileName, ".")
if not split and #split < 3 then
split[#split+1] = "0"
@@ -199,12 +201,12 @@ do
end
end
local split = Spearhead.Util.split_string(fileName, ".")
local matchingPart = table.concat(Spearhead.Util.sublist(split, 1, #split-2), ".")
local split = Util.split_string(fileName, ".")
local matchingPart = table.concat(Util.sublist(split, 1, #split-2), ".")
local lastFile = getLastFileOrDefault(dir--[[@as string]], matchingPart, fileName)
local fileSplit = Spearhead.Util.split_string(lastFile, ".")
local fileSplit = Util.split_string(lastFile, ".")
fileSplit[#fileSplit-1] = tostring(tonumber(fileSplit[#fileSplit-1]) + 1)
fileName = table.concat(fileSplit, ".")
@@ -329,7 +331,4 @@ do
end
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.persistence == nil then Spearhead.classes.persistence = {} end
Spearhead.classes.persistence.Persistence = Persistence
return Persistence
@@ -1,3 +1,10 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
---@class DatabaseTables
---@field AllZoneNames Array<string> All Zone Names
---@field StageZoneNames Array<string> All Stage Zone Names
@@ -112,13 +119,13 @@ function Database.New(Logger)
self._logger:debug("Initiating tables")
do -- INIT ZONE TABLES
for zone_ind, zone_data in pairs(Spearhead.DcsUtil.__trigger_zones) do
for zone_ind, zone_data in pairs(DcsUtil.__trigger_zones) do
local zone_name = zone_data.name
---@type Vec2
local zoneLocation = { x = zone_data.location.x, y = zone_data.location.y }
local split_string = Spearhead.Util.split_string(zone_name, "_")
local split_string = Util.split_string(zone_name, "_")
table.insert(self._tables.AllZoneNames, zone_name)
if string.lower(split_string[1]) == "missionstage" then
@@ -188,7 +195,7 @@ function Database.New(Logger)
end
if lowered == "scenerytarget" or lowered == "scenerytargets" then
local sceneryObjects = Spearhead.DcsUtil.getSceneryObjectsInZone(zone_data)
local sceneryObjects = DcsUtil.getSceneryObjectsInZone(zone_data)
for _, sceneryObject in pairs(sceneryObjects) do
table.insert(self._tables.AllSceneryObjects, sceneryObject)
end
@@ -202,7 +209,7 @@ function Database.New(Logger)
for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
if Spearhead.Util.startswith(string.lower(layer_object.name), "buildable", true) == true then
if Util.startswith(string.lower(layer_object.name), "buildable", true) == true then
local airbaseData = self:getAirbaseDataForDrawLayer(layer_object)
if airbaseData then
self._logger:debug("found airbase data for " .. layer_object.name)
@@ -222,9 +229,9 @@ function Database.New(Logger)
for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
if Spearhead.Util.startswith(layer_object.name, "drawing_", true) then
if Util.startswith(layer_object.name, "drawing_", true) then
local object = layer_object --[[@as DrawingObject]]
local customDrawing = Spearhead.classes.stageClasses.drawings.CustomDrawing.New(object)
local customDrawing = CustomDrawing.New(object)
table.insert(self._tables.CustomDrawings, customDrawing)
end
end
@@ -248,10 +255,10 @@ function Database.New(Logger)
for i, layer in pairs(env.mission.drawings.layers) do
if string.lower(layer.name) == "author" then
for key, layer_object in pairs(layer.objects) do
if Spearhead.Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then
local zone = Spearhead.DcsUtil.getZoneByName(stageZoneName)
if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then
local zone = DcsUtil.getZoneByName(stageZoneName)
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if zone and Spearhead.Util.is2dPointInZone(vec2, zone) == true then
if zone and Util.is2dPointInZone(vec2, zone) == true then
local description = layer_object.text
if description and description ~= "" then
stageData.StageBriefing = description
@@ -265,18 +272,18 @@ function Database.New(Logger)
-- fill blue sams
for _, blueSamStageName in pairs(self._tables.BlueSams) do
if Spearhead.DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then
if DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then
table.insert(stageData.BlueSamZones, blueSamStageName)
end
end
--- fill farp zones
for _, farpZoneName in pairs(self._tables.AllFarpZones) do
if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then
if DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then
table.insert(stageData.FarpZones, farpZoneName)
for hubZoneName, available in pairs(availableSupplyHubs) do
if available == true and Spearhead.DcsUtil.isZoneInZone(hubZoneName, farpZoneName) == true then
if available == true and DcsUtil.isZoneInZone(hubZoneName, farpZoneName) == true then
local farpZoneData = self:getOrCreateFarpDataForZone(farpZoneName)
if farpZoneData then
table.insert(farpZoneData.supplyHubNames, hubZoneName)
@@ -291,15 +298,15 @@ function Database.New(Logger)
for _, airbase in pairs(world.getAirbases()) do
local point = airbase:getPoint()
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then
if DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then
if airbase:getDesc().category == 0 then
table.insert(stageData.AirbaseNames, airbase:getName())
local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName())
local airbaseZone = DcsUtil.getAirbaseZoneByName(airbase:getName())
for hubZoneName, available in pairs(availableSupplyHubs) do
local zone = Spearhead.DcsUtil.getZoneByName(hubZoneName)
local zone = DcsUtil.getZoneByName(hubZoneName)
if zone and airbaseZone then
if available == true and Spearhead.Util.is2dPointInZone(zone.location, airbaseZone) == true then
if available == true and Util.is2dPointInZone(zone.location, airbaseZone) == true then
local airbaseData = self:getOrCreateAirbaseData(airbase:getName())
if airbaseData then
table.insert(airbaseData.supplyHubNames, hubZoneName)
@@ -314,14 +321,14 @@ function Database.New(Logger)
-- fill supply hubs
for supplyHubZone, available in pairs(availableSupplyHubs) do
if available == true and Spearhead.DcsUtil.isZoneInZone(supplyHubZone, stageZoneName) == true then
if available == true and DcsUtil.isZoneInZone(supplyHubZone, stageZoneName) == true then
table.insert(stageData.SupplyHubZones, supplyHubZone)
end
end
for _, farpZoneName in pairs(stageData.FarpZones) do
for _, supplyHubZone in pairs(self._tables.SupplyHubZones) do
if Spearhead.DcsUtil.isZoneInZone(supplyHubZone, farpZoneName) == true then
if DcsUtil.isZoneInZone(supplyHubZone, farpZoneName) == true then
stageData.SupplyHubZonesInFarp[supplyHubZone] = farpZoneName
end
end
@@ -329,14 +336,14 @@ function Database.New(Logger)
-- fill missions
for key, missionZone in pairs(self._tables.MissionZones) do
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
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
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
table.insert(stageData.RandomMissionZones, missionZone)
end
end
@@ -345,13 +352,13 @@ function Database.New(Logger)
for _, missionZone in pairs(self._tables.MissionZones) do
if self._tables.MissionZoneData[missionZone] == nil or self._tables.MissionZoneData[missionZone].description == nil then
Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZone .. " does not have a briefing")
MissionEditorWarnings.Add("Mission with zonename: " .. missionZone .. " does not have a briefing")
end
end
for _, missionZone in pairs(self._tables.RandomMissionZones) do
if self._tables.MissionZoneData[missionZone] == nil or self._tables.MissionZoneData[missionZone].description == nil then
Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZone .. " does not have a briefing")
MissionEditorWarnings.Add("Mission with zonename: " .. missionZone .. " does not have a briefing")
end
end
@@ -368,7 +375,7 @@ function Database.New(Logger)
}
end
local position = airbase:getPoint()
if Spearhead.DcsUtil.isPositionInZone(position.x, position.z, farpZoneName) == true then
if DcsUtil.isPositionInZone(position.x, position.z, farpZoneName) == true then
table.insert(self._tables.FarpZoneData[farpZoneName].padNames, name)
end
end
@@ -387,7 +394,7 @@ function Database.New(Logger)
for _, cap_route_zone in pairs(self._tables.AllCapRoutes) do
local split = Spearhead.Util.split_string(cap_route_zone, "_")
local split = Util.split_string(cap_route_zone, "_")
local zoneID = split[2]
if zoneID then
@@ -398,7 +405,7 @@ function Database.New(Logger)
}
end
local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone)
local zone = DcsUtil.getZoneByName(cap_route_zone)
if zone then
table.insert(tables.capZonesByCapZoneID[zoneID].zones, zone)
end
@@ -406,7 +413,7 @@ function Database.New(Logger)
end
for _, interceptZone in pairs(self._tables.AllInterceptZones) do
local split = Spearhead.Util.split_string(interceptZone, "_")
local split = Util.split_string(interceptZone, "_")
local zoneID = split[2]
if zoneID then
@@ -414,7 +421,7 @@ function Database.New(Logger)
tables.interceptZonesByZoneID[zoneID] = {}
end
local zone = Spearhead.DcsUtil.getZoneByName(interceptZone)
local zone = DcsUtil.getZoneByName(interceptZone)
if zone then
table.insert(tables.interceptZonesByZoneID[zoneID], zone)
end
@@ -443,12 +450,12 @@ function Database.New(Logger)
if missions == 0 then missions = 1 end
self._logger:info("initiated the database with amount of zones: ")
self._logger:info("Stages: " .. Spearhead.Util.tableLength(self._tables.StageZones))
self._logger:info("Total Missions: " .. Spearhead.Util.tableLength(self._tables.MissionZoneData))
self._logger:info("Stages: " .. Util.tableLength(self._tables.StageZones))
self._logger:info("Total Missions: " .. Util.tableLength(self._tables.MissionZoneData))
self._logger:info("Average units per mission: " .. totalUnits / missions)
self._logger:info("Random Missions: " .. Spearhead.Util.tableLength(self._tables.RandomMissionZones))
self._logger:info("Farps: " .. Spearhead.Util.tableLength(self._tables.AllFarpZones))
self._logger:info("Airbases: " .. Spearhead.Util.tableLength(self._tables.AirbaseDataPerAirfield))
self._logger:info("Random Missions: " .. Util.tableLength(self._tables.RandomMissionZones))
self._logger:info("Farps: " .. Util.tableLength(self._tables.AllFarpZones))
self._logger:info("Airbases: " .. Util.tableLength(self._tables.AirbaseDataPerAirfield))
return self
@@ -470,7 +477,7 @@ end
local getAvailableCAPGroups = function()
local result = {}
for name, value in pairs(is_group_taken) do
if value == false and Spearhead.Util.startswith(name, "CAP") then
if value == false and Util.startswith(name, "CAP") then
table.insert(result, name)
end
end
@@ -480,7 +487,7 @@ end
---@private
function Database:initAvailableUnits()
do
local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames()
local all_groups = MizGroupsManager.getAllGroupNames()
for _, value in pairs(all_groups) do
is_group_taken[value] = false
end
@@ -492,9 +499,9 @@ end
---@return AirbaseData?
function Database:getAirbaseDataForDrawLayer(layer_object)
for _, airbase in pairs(world.getAirbases()) do
local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName())
local zone = DcsUtil.getAirbaseZoneByName(airbase:getName())
if zone and Spearhead.Util.is2dPointInZone({ x = layer_object.mapX, y = layer_object.mapY }, zone) == true then
if zone and Util.is2dPointInZone({ x = layer_object.mapX, y = layer_object.mapY }, zone) == true then
return self:getOrCreateAirbaseData(airbase:getName())
end
end
@@ -555,7 +562,7 @@ function Database:loadCapUnits()
local point = airbase:getPoint()
---@type SpearheadTriggerZone?
local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName())
local zone = DcsUtil.getAirbaseZoneByName(airbase:getName())
if zone == nil then
zone = {
@@ -569,15 +576,15 @@ function Database:loadCapUnits()
local baseData = self:getOrCreateAirbaseData(airbase:getName())
local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, zone)
local groups = DcsUtil.areGroupsInCustomZone(all_groups, zone)
for _, groupName in pairs(groups) do
is_group_taken[groupName] = true
if Spearhead.Util.startswith(groupName, "CAP_A", true) or Spearhead.Util.startswith(groupName, "CAP_B", true) then
if Util.startswith(groupName, "CAP_A", true) or Util.startswith(groupName, "CAP_B", true) then
table.insert(baseData.CapGroups, groupName)
elseif Spearhead.Util.startswith(groupName, "CAP_I", true) then
elseif Util.startswith(groupName, "CAP_I", true) then
table.insert(baseData.InterceptGroups, groupName)
elseif Spearhead.Util.startswith(groupName, "CAP_S", true) then
elseif Util.startswith(groupName, "CAP_S", true) then
table.insert(baseData.SweepGroups, groupName)
end
end
@@ -588,19 +595,19 @@ end
---@private
function Database:loadBlueSamUnits()
local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames()
local all_groups = MizGroupsManager.getAllGroupNames()
for _, blueSamZone in pairs(self._tables.BlueSams) do
local samData = self:getOrCreateBlueSamDataForZone(blueSamZone)
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, blueSamZone)
local groups = DcsUtil.getGroupsInZone(all_groups, blueSamZone)
for _, groupName in pairs(groups) do
is_group_taken[groupName] = true
table.insert(samData.groups, groupName)
end
local triggerZone = Spearhead.DcsUtil.getZoneByName(blueSamZone)
local triggerZone = DcsUtil.getZoneByName(blueSamZone)
if triggerZone then
for _, kvPair in pairs(triggerZone.properties) do
if kvPair.key and Spearhead.Util.startswith(kvPair.key, "buildable") then
if kvPair.key and Util.startswith(kvPair.key, "buildable") then
local number = tonumber(kvPair.value)
if number and number > 0 then
samData.buildingKilos = number
@@ -631,9 +638,9 @@ function Database:LoadZoneData(missionZoneName)
dependsOn = {}
}
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName)
local groups = DcsUtil.getGroupsInZone(all_groups, missionZoneName)
for _, groupName in pairs(groups) do
if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then
if MizGroupsManager.IsGroupStatic(groupName) == true then
local object = StaticObject.getByName(groupName)
if object and object:getCoalition() == coalition.side.RED then
@@ -656,26 +663,26 @@ function Database:LoadZoneData(missionZoneName)
for _, sceneryObject in pairs(self._tables.AllSceneryObjects) do
local point = sceneryObject:GetPoint()
if point then
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, missionZoneName) == true then
if DcsUtil.isPositionInZone(point.x, point.z, missionZoneName) == true then
table.insert(self._tables.MissionZoneData[missionZoneName].SceneryTargets, sceneryObject)
end
end
end
-- Check for properties and adds the settings to the mission data
local triggerZone = Spearhead.DcsUtil.getZoneByName(missionZoneName)
local triggerZone = DcsUtil.getZoneByName(missionZoneName)
if triggerZone and triggerZone.properties then
for _, kvPair in pairs(triggerZone.properties) do
local key = kvPair.key
if Spearhead.Util.startswith(key, "dependson", true) == true then
if Util.startswith(key, "dependson", true) == true then
table.insert(self._tables.MissionZoneData[missionZoneName].dependsOn, kvPair.value)
elseif Spearhead.Util.startswith(key, "completeat") == true then
elseif Util.startswith(key, "completeat") == true then
local value = tonumber(kvPair.value)
if value then
if value > 1 and value <= 100 then
value = value / 100
elseif value > 100 then
Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZoneName .. " has a complete at value of " .. value .. " which is higher than 100, this will not work as intended")
MissionEditorWarnings.Add("Mission with zonename: " .. missionZoneName .. " has a complete at value of " .. value .. " which is higher than 100, this will not work as intended")
end
self._tables.MissionZoneData[missionZoneName].completeAt = value
end
@@ -690,8 +697,8 @@ function Database:LoadZoneData(missionZoneName)
for key, layer_object in pairs(layer.objects) do
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
if triggerZone and Spearhead.Util.is2dPointInZone(vec2, triggerZone) then
if layer_object.name and Spearhead.Util.startswith(layer_object.name, "briefing_", true) then
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
if layer_object.name and Util.startswith(layer_object.name, "briefing_", true) then
local description = layer_object.text
if description and description ~= "" then
self._tables.MissionZoneData[missionZoneName].description = description
@@ -725,16 +732,16 @@ function Database:loadFarpData()
for _, farpZone in pairs(self._tables.AllFarpZones) do
local farpzoneData = self:getOrCreateFarpDataForZone(farpZone)
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone)
local groups = DcsUtil.getGroupsInZone(all_groups, farpZone)
for _, groupName in pairs(groups) do
is_group_taken[groupName] = true
table.insert(farpzoneData.groups, groupName)
end
local triggerZone = Spearhead.DcsUtil.getZoneByName(farpZone)
local triggerZone = DcsUtil.getZoneByName(farpZone)
if triggerZone then
for _, kvPair in pairs(triggerZone.properties) do
if kvPair.key and Spearhead.Util.startswith(kvPair.key, "buildable", true) == true then
if kvPair.key and Util.startswith(kvPair.key, "buildable", true) == true then
local number = tonumber(kvPair.value)
if number and number > 0 then
farpzoneData.buildingKilos = number
@@ -754,7 +761,7 @@ function Database:loadAirbaseGroups()
if base then
local basedata = self:getOrCreateAirbaseData(baseName)
local point = base:getPoint()
local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(baseName)
local airbaseZone = DcsUtil.getAirbaseZoneByName(baseName)
if airbaseZone == nil then
airbaseZone = {
@@ -768,9 +775,9 @@ function Database:loadAirbaseGroups()
if airbaseZone and base:getDesc().category == Airbase.Category.AIRDROME then
local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone)
local groups = DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone)
for _, groupName in pairs(groups) do
if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then
if MizGroupsManager.IsGroupStatic(groupName) == true then
local object = StaticObject.getByName(groupName)
if object then
if object:getCoalition() == coalition.side.RED then
@@ -806,9 +813,9 @@ function Database:loadMiscGroupsInStages()
for _, stageZone in pairs(self._tables.StageZones) do
stageZone.MiscGroups = {}
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, stageZone.StageZoneName)
local groups = DcsUtil.getGroupsInZone(all_groups, stageZone.StageZoneName)
for _, groupName in pairs(groups) do
if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then
if MizGroupsManager.IsGroupStatic(groupName) == true then
local object = StaticObject.getByName(groupName)
if object and object:getCoalition() ~= coalition.side.NEUTRAL then
is_group_taken[groupName] = true
@@ -846,14 +853,14 @@ function Database:GetCapZoneForZoneID(zoneID)
if capZonesForID and capZonesForID.zones then
local count = Spearhead.Util.tableLength(capZonesForID.zones)
local count = Util.tableLength(capZonesForID.zones)
if count == 0 then
self._logger:warn("Tried to get cap zone for zoneID: " .. zoneID .. " but cap zones were empty for this ID")
return nil
end
capZonesForID.current = capZonesForID.current + 1
if Spearhead.Util.tableLength(capZonesForID.zones) < capZonesForID.current then
if Util.tableLength(capZonesForID.zones) < capZonesForID.current then
capZonesForID.current = 1
end
return capZonesForID.zones[capZonesForID.current]
@@ -1009,5 +1016,4 @@ function Database:GetNewMissionCode()
]]
end
if not Spearhead then Spearhead = {} end
Spearhead.DB = Database
return Database
@@ -1,4 +1,8 @@
local Logger = require("classes.util.Logger")
local Persistence = require("classes.persistence.Persistence")
local DcsUtil = require("classes.util.DcsUtil")
---@class SpearheadEvents
local SpearheadEvents = {}
do
@@ -7,10 +11,9 @@ do
---@param logLevel LogLevel
SpearheadEvents.Init = function(logLevel)
logger = Spearhead.LoggerTemplate.new("Events", logLevel)
logger = Logger.new("Events", logLevel)
end
local warn = function(text)
if logger then
logger:warn(text)
@@ -50,7 +53,7 @@ do
---@param newStageNumber number
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber)
pcall(function ()
Spearhead.classes.persistence.Persistence.SetActiveStage(newStageNumber)
Persistence.SetActiveStage(newStageNumber)
end)
for _, callable in pairs(OnStageNumberChangedListeners) do
@@ -68,8 +71,7 @@ do
logError(err)
end
end
Spearhead.LoggerTemplate.new("Events", "INFO"):info("Published stage number changed to: " .. tostring(newStageNumber))
Spearhead.StageNumber = newStageNumber
Logger.new("Events", "INFO"):info("Published stage number changed to: " .. tostring(newStageNumber))
end
end
@@ -349,7 +351,7 @@ do
end
if event.id == world.event.S_EVENT_MISSION_END then
Spearhead.classes.persistence.Persistence.UpdateNow()
Persistence.UpdateNow()
end
local AI_GROUPS = {}
@@ -370,7 +372,7 @@ do
return false
end
local players = Spearhead.DcsUtil.getAllPlayerUnits()
local players = DcsUtil.getAllPlayerUnits()
local unitName = unit:getName()
for i, unit in pairs(players) do
if unit:getName() == unitName then
@@ -398,5 +400,4 @@ do
world.addEventHandler(e)
end
if Spearhead == nil then Spearhead = {} end
Spearhead.Events = SpearheadEvents
return SpearheadEvents
@@ -1,3 +1,7 @@
local DcsUtil = require("classes.util.DcsUtil")
local Util = require("classes.util.Util")
---@class SpearheadRouteUtil
local ROUTE_UTIL = {}
do --setup route util
---comment
@@ -22,7 +26,7 @@ do --setup route util
---@return table task
local RtbTask = function(airdromeId, basePoint, speed)
if basePoint == nil then
basePoint = Spearhead.Util.getAirbaseById(airdromeId):getPoint()
basePoint = DcsUtil.getAirbaseById(airdromeId):getPoint()
end
return {
@@ -214,7 +218,7 @@ do --setup route util
---@param deviationDistance number
---@return table? route
ROUTE_UTIL.createCapMission = function(groupName, airdromeId, capPoint, racetrackSecondPoint, altitude, speed, durationOnStation, attackHelos, deviationDistance)
local baseName = Spearhead.DcsUtil.getAirbaseName(airdromeId)
local baseName = DcsUtil.getAirbaseName(airdromeId)
if baseName == nil then
return nil
end
@@ -294,7 +298,7 @@ do --setup route util
TODO: Test the creation and pubishing of event and the timing of said event
]] --
local base = Spearhead.DcsUtil.getAirbaseById(airdromeId)
local base = DcsUtil.getAirbaseById(airdromeId)
if base == nil then
return nil, "No airbase found for ID " .. tostring(airdromeId)
end
@@ -307,7 +311,7 @@ do --setup route util
end
local units = group:getUnits()
while pos == nil and i <= Spearhead.Util.tableLength(units) do
while pos == nil and i <= Util.tableLength(units) do
local unit = units[i]
if unit and unit:isExist() == true and unit:inAir() == true then
pos = unit:getPoint()
@@ -461,5 +465,4 @@ do --setup route util
end
end
if Spearhead == nil then Spearhead = {} end
Spearhead.RouteUtil = ROUTE_UTIL
return ROUTE_UTIL
@@ -1,4 +1,11 @@
local Events = require("classes.spearhead_events")
local Util = require("classes.util.Util")
local Logger = require("classes.util.Logger")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
local WaitingStage = require("classes.stageClasses.Stages.WaitingStage")
local StagesByName = {}
@@ -20,6 +27,8 @@ local currentStage = -99
local GlobalStageManager = {}
GlobalStageManager.__index = GlobalStageManager
GlobalStageManager.getCurrentStage = function() return currentStage end
---comment
---@param database Database
---@param stageConfig StageConfig
@@ -27,7 +36,7 @@ GlobalStageManager.__index = GlobalStageManager
---@param spawnManager SpawnManager
---@return nil
function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnManager)
local logger = Spearhead.LoggerTemplate.new("StageManager", logLevel)
local logger = Logger.new("StageManager", logLevel)
logger:info("Using Stage Log Level: " .. logLevel)
local self = setmetatable({}, GlobalStageManager)
self.database = database
@@ -45,28 +54,29 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
end
}
Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener)
Events.AddStageNumberChangedListener(OnStageNumberChangedListener)
for _, stageName in pairs(database:getStagezoneNames()) do
logger:debug("Found stage zone with name: " .. stageName)
if Spearhead.Util.startswith(stageName, "missionstage", true) then
if Util.startswith(stageName, "missionstage", true) then
local valid = true
local split = Spearhead.Util.split_string(stageName, "_")
if Spearhead.Util.tableLength(split) < 2 then
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format")
local split = Util.split_string(stageName, "_")
if Util.tableLength(split) < 2 then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a order number or valid format")
valid = false
end
if Spearhead.Util.tableLength(split) < 3 then
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a stage name")
if Util.tableLength(split) < 3 then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a stage name")
end
local orderNumber = nil
local isSideStage = false
if valid == true then
local orderNumberString = string.lower(split[2])
if Spearhead.Util.startswith(orderNumberString, "x") == true then
if Util.startswith(orderNumberString, "x") == true then
isSideStage = true
orderNumberString = string.gsub(orderNumberString, "x", "")
@@ -76,13 +86,13 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
end
if orderNumber == nil then
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a valid order number : " .. split[2])
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a valid order number : " .. split[2])
valid = false
end
end
local stageDisplayName = split[3]
local stagelogger = Spearhead.LoggerTemplate.new(stageName, logLevel)
local stagelogger = Logger.new(stageName, logLevel)
if valid == true and orderNumber then
---@type StageInitData
@@ -93,13 +103,13 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
}
if isSideStage == true then
local stage = Spearhead.classes.stageClasses.Stages.ExtraStage.New(database, stageConfig, stagelogger, initData, spawnManager)
local stage = ExtraStage.New(database, stageConfig, stagelogger, initData, spawnManager)
stage:AddStageCompleteListener(self)
if SideStageByIndex[tostring(orderNumber)] == nil then SideStageByIndex[tostring(orderNumber)] = {} end
table.insert(SideStageByIndex[tostring(orderNumber)], stage)
else
local stage = Spearhead.classes.stageClasses.Stages.PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager)
local stage = PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager)
stage:AddStageCompleteListener(self)
if StagesByIndex[tostring(orderNumber)] == nil then StagesByIndex[tostring(orderNumber)] = {} end
@@ -108,13 +118,13 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
end
end
if Spearhead.Util.startswith(stageName, "waitingstage", true) then
if Util.startswith(stageName, "waitingstage", true) then
local valid = true
local split = Spearhead.Util.split_string(stageName, "_")
local split = Util.split_string(stageName, "_")
if Spearhead.Util.tableLength(split) < 3 then
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format")
if Util.tableLength(split) < 3 then
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a order number or valid format")
valid = false
end
@@ -123,19 +133,19 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
local stageIndex = tonumber(stageIndexString)
if not stageIndex then
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a valid order number")
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a valid order number")
valid = false
end
local waitingSecondsString = split[3]
local waitingSeconds = tonumber(waitingSecondsString)
if not waitingSeconds then
Spearhead.AddMissionEditorWarning("Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter")
MissionEditorWarnings.Add("Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter")
valid = false
end
if valid == true then
local stagelogger = Spearhead.LoggerTemplate.new(stageName, logLevel)
local stagelogger = Logger.new(stageName, logLevel)
---@type WaitingStageInitData
local initData = {
@@ -144,7 +154,7 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
stageZoneName = stageName,
waitingSeconds = waitingSeconds --[[@as integer]]
}
local waitingStage = Spearhead.classes.stageClasses.Stages.WaitingStage.New(database, stageConfig, stagelogger, initData, spawnManager)
local waitingStage = WaitingStage.New(database, stageConfig, stagelogger, initData, spawnManager)
if WaitingStagesByIndex[tostring(stageIndex)] == nil then
WaitingStagesByIndex[tostring(stageIndex)] = {}
@@ -200,7 +210,7 @@ function GlobalStageManager:OnStageComplete(stage)
local newStageNumber = currentStage + 1
self:UpdateDrawings(newStageNumber)
self.logger:debug("Setting next stage to: " .. tostring(newStageNumber))
Spearhead.Events.PublishStageNumberChanged(newStageNumber)
Events.PublishStageNumberChanged(newStageNumber)
end
end
end
@@ -221,7 +231,7 @@ end
GlobalStageManager.printFullOverview = function ()
local logger = Spearhead.LoggerTemplate.new("StageOverview", "INFO")
local logger = Logger.new("StageOverview", "INFO")
logger:info("Stage overview:")
local max = 0
@@ -282,5 +292,4 @@ GlobalStageManager.isStageComplete = function (stageNumber)
return true
end
if not Spearhead.internal then Spearhead.internal = {} end
Spearhead.internal.GlobalStageManager = GlobalStageManager
return GlobalStageManager
@@ -1,5 +1,5 @@
local DcsUtil = require("classes.util.DcsUtil")
---@class SpearheadGroup : OnUnitLostListener
---@field private _groupName string
@@ -149,7 +149,7 @@ end
function SpearheadGroup:SetInvisible()
if self._isStatic == true then
local country = Spearhead.DcsUtil.GetNeutralCountry()
local country = DcsUtil.GetNeutralCountry()
---@type SpawnOverrides
local overrides = {
@@ -187,7 +187,4 @@ function SpearheadGroup:SetVisible()
end
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end
Spearhead.classes.stageClasses.Groups.SpearheadGroup = SpearheadGroup
return SpearheadGroup
@@ -1,3 +1,5 @@
local Persistence = require("classes.persistence.Persistence")
---@class SpearheadSceneryObject
---@field private persistentName string The persistent name of the scenery object
---@field private objectID number The ID of the scenery object
@@ -47,7 +49,7 @@ end
function SpearheadSceneryObject:MarkDead()
if self.isDead == true then return end
self.isDead = true
Spearhead.classes.persistence.Persistence.UnitKilled(self.persistentName, self:GetPoint(), 0, "Scenery")
Persistence.UnitKilled(self.persistentName, self:GetPoint(), 0, "Scenery")
end
function SpearheadSceneryObject:UpdateStatePersistently()
@@ -55,7 +57,7 @@ function SpearheadSceneryObject:UpdateStatePersistently()
return
end
local state = Spearhead.classes.persistence.Persistence.UnitState(self.persistentName)
local state = Persistence.UnitState(self.persistentName)
if state and state.isDead == true then
trigger.action.explosion(self:GetPoint(), 1000)
self.isDead = true
@@ -71,7 +73,4 @@ function SpearheadSceneryObject:GetPoint()
return Object.getPoint(self.internalObj)
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end
Spearhead.classes.stageClasses.Groups.SpearheadSceneryObject = SpearheadSceneryObject
return SpearheadSceneryObject
@@ -1,3 +1,7 @@
local BuildableZone = require("classes.stageClasses.SpecialZones.abstract.BuildableZone")
local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
---@class BlueSam : BuildableZone
---@field Activate fun(self: BlueSam)
@@ -20,7 +24,7 @@ BlueSam.__index = BlueSam
---@return BlueSam?
function BlueSam.New(database, logger, zoneName, spawnManager)
setmetatable(BlueSam, Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone)
setmetatable(BlueSam, BuildableZone)
local self = setmetatable({}, BlueSam)
self._database = database
@@ -53,39 +57,39 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
end
for _, groupName in pairs(blueSamData.groups) do
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true)
if SpearheadGroup then
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
if spearheadGroup then
if SpearheadGroup:GetCoalition() == 2 or SpearheadGroup:GetCoalition() == 0 then
table.insert(self._blueGroups, SpearheadGroup)
if spearheadGroup:GetCoalition() == 2 or spearheadGroup:GetCoalition() == 0 then
table.insert(self._blueGroups, spearheadGroup)
end
for _, unit in pairs(SpearheadGroup:GetObjects()) do
if SpearheadGroup:GetCoalition() == 1 then
for _, unit in pairs(spearheadGroup:GetObjects()) do
if spearheadGroup:GetCoalition() == 1 then
table.insert(blueUnitsPos, unit:getPoint())
elseif SpearheadGroup:GetCoalition() == 2 then
elseif spearheadGroup:GetCoalition() == 2 then
table.insert(redUnitsPos, unit:getPoint())
end
end
end
SpearheadGroup:Destroy()
spearheadGroup:Destroy()
end
--Cleanup units
local cleanup_distance = 5
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
for redUnitName, redUnitPos in pairs(redUnitsPos) do
local distance = Spearhead.Util.VectorDistance3d(blueUnitPos, redUnitPos)
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
if distance <= cleanup_distance then
self._cleanupUnits[redUnitName] = true
end
end
end
local zone = Spearhead.DcsUtil.getZoneByName(zoneName)
local zone = DcsUtil.getZoneByName(zoneName)
if zone then
Spearhead.classes.stageClasses.SpecialZones.abstract.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)
end
return self
@@ -104,9 +108,9 @@ function BlueSam:GetNoLandingZone()
end
end
local vecs = Spearhead.Util.getConvexHull(points)
local vecs = Util.getConvexHull(points)
local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName)
local zone = DcsUtil.getZoneByName(self._zoneName)
if zone == nil then
self._logger:error("Zone not found: " .. self._zoneName)
return nil
@@ -136,7 +140,7 @@ end
function BlueSam:SpawnGroups()
for unitName, needsCleanup in pairs(self._cleanupUnits) do
Spearhead.DcsUtil.DestroyUnit(unitName)
DcsUtil.DestroyUnit(unitName)
end
for _, group in pairs(self._blueGroups) do
@@ -148,10 +152,4 @@ function BlueSam:OnBuildingComplete()
self:SpawnGroups()
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.BlueSam = BlueSam
return BlueSam
@@ -1,4 +1,8 @@
local BuildableZone = require("classes.stageClasses.SpecialZones.abstract.BuildableZone")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local SupplyHub = require("classes.stageClasses.SpecialZones.SupplyHub")
local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup")
---@class FarpZone: BuildableZone
---@field private _startingFarp boolean
@@ -18,14 +22,14 @@ FarpZone.__index = FarpZone
---@param spawnManager SpawnManager
---@return FarpZone
function FarpZone.New(database, logger, zoneName, spawnManager)
setmetatable(FarpZone, Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone)
setmetatable(FarpZone, BuildableZone)
local self = setmetatable({}, FarpZone)
self._database = database
self._logger = logger
self._zoneName = zoneName
local split = Spearhead.Util.split_string(zoneName, "_")
local split = Util.split_string(zoneName, "_")
if string.lower(split[2]) == "a" then
self._startingFarp = true
else
@@ -44,7 +48,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
self._padNames = farpData.padNames
for _, supplyHubName in pairs(farpData.supplyHubNames) do
local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(database, logger, supplyHubName)
local supplyHub = SupplyHub.new(database, logger, supplyHubName)
if supplyHub then
table.insert(self._supplyHubs, supplyHub)
end
@@ -52,15 +56,15 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
for _, groupName in pairs(farpData.groups) do
local group = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true)
local group = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._groups, group)
group:Destroy()
end
local zone = Spearhead.DcsUtil.getZoneByName(zoneName)
local zone = DcsUtil.getZoneByName(zoneName)
if zone then
self._logger:debug("Creating Buildable zone: " .. zoneName .. " with " .. (farpData.buildingKilos or "nil") .. " kilos")
Spearhead.classes.stageClasses.SpecialZones.abstract.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)
end
end
self:Deactivate()
@@ -132,8 +136,4 @@ function FarpZone:SetPadsBlue()
end
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.FarpZone = FarpZone
return FarpZone
@@ -1,3 +1,9 @@
local BuildableZone = require("classes.stageClasses.SpecialZones.abstract.BuildableZone")
local DcsUtil = require("classes.util.DcsUtil")
local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup")
local SupplyHub = require("classes.stageClasses.SpecialZones.SupplyHub")
local Util = require("classes.util.Util")
---@class StageBase : BuildableZone
---@field private _database Database
---@field private _logger Logger
@@ -21,7 +27,7 @@ StageBase.__index = StageBase
---@param spawnManager SpawnManager
---@return StageBase?
function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
setmetatable(StageBase, Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone)
setmetatable(StageBase, BuildableZone)
local self = setmetatable({}, StageBase)
self._database = databaseManager
@@ -33,7 +39,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
self._supplyHubs = {}
self._airbase = Airbase.getByName(airbaseName)
self._initialSide = Spearhead.DcsUtil.getStartingCoalition(self._airbase)
self._initialSide = DcsUtil.getStartingCoalition(self._airbase)
do --init
local airbaseData = databaseManager:getAirbaseDataForZone(airbaseName)
@@ -49,7 +55,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
local blueUnitsPos = {}
for _, groupName in pairs(airbaseData.RedGroups) do
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true)
local shGroup = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._red_groups, shGroup)
for _, unit in pairs(shGroup:GetObjects()) do
@@ -60,7 +66,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
end
for _, groupName in pairs(airbaseData.BlueGroups) do
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true)
local shGroup = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._blue_groups, shGroup)
for _, unit in pairs(shGroup:GetObjects()) do
@@ -71,7 +77,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
end
for _, supplyHubName in pairs(airbaseData.supplyHubNames) do
local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(databaseManager, logger,
local supplyHub = SupplyHub.new(databaseManager, logger,
supplyHubName)
if supplyHub then
table.insert(self._supplyHubs, supplyHub)
@@ -86,7 +92,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
for redUnitName, redUnitPos in pairs(redUnitsPos) do
local distance = Spearhead.Util.VectorDistance3d(blueUnitPos, redUnitPos)
local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos)
if distance <= cleanup_distance then
self._cleanup_units[redUnitName] = true
end
@@ -94,9 +100,9 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager)
end
end
local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbaseName)
local zone = DcsUtil.getAirbaseZoneByName(airbaseName)
if zone then
Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone.New(self, zone, airbaseData.buildingKilos or 0, "AIRBASE_CRATE", self._blue_groups, logger, databaseManager)
BuildableZone.New(self, zone, airbaseData.buildingKilos or 0, "AIRBASE_CRATE", self._blue_groups, logger, databaseManager)
end
end
@@ -126,8 +132,8 @@ function StageBase:CleanRedUnits()
for unitName, shouldClean in pairs(self._cleanup_units) do
if shouldClean == true then
Spearhead.DcsUtil.DestroyUnit(unitName)
Spearhead.DcsUtil.CleanCorpse(unitName)
DcsUtil.DestroyUnit(unitName)
DcsUtil.CleanCorpse(unitName)
end
end
end
@@ -187,10 +193,4 @@ function StageBase:OnBuildingComplete()
self:FinaliseBlueStage()
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.StageBase = StageBase
return StageBase
@@ -1,3 +1,8 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
---@class SupplyHub
---@field private _database Database
---@field private _logger Logger
@@ -26,18 +31,18 @@ function SupplyHub.new(database, logger, zoneName)
self._logger = logger
self._zoneName = zoneName
local split = Spearhead.Util.split_string(zoneName, "_")
local split = Util.split_string(zoneName, "_")
if string.lower(split[2]) == "a" then
self._activeAtStart = true
else
self._activeAtStart = false
end
self._zone = Spearhead.DcsUtil.getZoneByName(zoneName)
self._zone = DcsUtil.getZoneByName(zoneName)
self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel)
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logger.LogLevel)
self._inZone = {}
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel)
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel)
self._logger:debug("Creating Supply Hub zone: " .. self._zoneName)
@@ -66,23 +71,18 @@ function SupplyHub:Activate()
self._logger:debug("Activating Supply Hub zone: " .. self._zoneName)
local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName)
local zone = DcsUtil.getZoneByName(self._zoneName)
if zone and self._drawID == nil then
---@type DrawColor
local fillColor = { r=0, g=1, b=0, a=0.2 }
---@type DrawColor
local lineColor = { r=0, g=1, b=0, a=1}
local lineStyle = 1
self._drawID = Spearhead.DcsUtil.DrawZone(zone, lineColor, fillColor, lineStyle)
self._drawID = DcsUtil.DrawZone(zone, lineColor, fillColor, lineStyle)
end
self._supplyUnitsTracker:RegisterHub(self)
end
if not Spearhead then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.SupplyHub = SupplyHub
return SupplyHub
@@ -1,3 +1,6 @@
local Util = require("classes.util.Util")
local Persistence = require("classes.persistence.Persistence")
local BuildableMission = require("classes.stageClasses.missions.BuildableMission")
---@class BuildableZone : OnCrateDroppedListener
---@field protected _targetZone SpearheadTriggerZone
@@ -22,11 +25,11 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
self._requiredKilos = kilosRequired or 0
self._buildableGroups = buildableGroups or {}
self._buildableLogger = logger
local totalGroups = Spearhead.Util.tableLength(self._buildableGroups)
local totalGroups = Util.tableLength(self._buildableGroups)
self._groupsPerKilo = totalGroups / self._requiredKilos
self._receivedBuildingKilos = 0
local persistedKilos = Spearhead.classes.persistence.Persistence.GetZoneDeliveredKilos(targetZone.name)
local persistedKilos = Persistence.GetZoneDeliveredKilos(targetZone.name)
if persistedKilos and persistedKilos > 0 then
self._buildableLogger:debug("Zone " .. targetZone.name .. " already has " .. persistedKilos .. " kilos delivered")
self._receivedBuildingKilos = persistedKilos
@@ -66,7 +69,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
local noLandingZone = self:GetNoLandingZone()
if kilosRequired and kilosRequired > 0 then
self._buildableMission = Spearhead.classes.stageClasses.missions.BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType)
self._buildableMission = BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType)
self._buildableMission:AddOnCrateDroppedOfListener(self)
else
self._buildableMission = nil
@@ -138,7 +141,7 @@ end
---@param kilos number
function BuildableZone:FinaliseCrate(kilos)
self._receivedBuildingKilos = self._receivedBuildingKilos + kilos
Spearhead.classes.persistence.Persistence.SetZoneDeliveredKilos(self._targetZone.name, self._receivedBuildingKilos)
Persistence.SetZoneDeliveredKilos(self._targetZone.name, self._receivedBuildingKilos)
if self._receivedBuildingKilos >= self._requiredKilos then
self:OnBuildingComplete()
end
@@ -157,7 +160,7 @@ function BuildableZone:GetNoLandingZone()
end
end
local vecs = Spearhead.Util.getConvexHull(points)
local vecs = Util.getConvexHull(points)
---@type SpearheadTriggerZone
local spearheadZone = {
@@ -197,9 +200,4 @@ function BuildableZone:SpawnAmount(amount)
return true
end
if not Spearhead then Spearhead = {} end
Spearhead.classes = Spearhead.classes or {}
Spearhead.classes.stageClasses = Spearhead.classes.stageClasses or {}
Spearhead.classes.stageClasses.SpecialZones = Spearhead.classes.stageClasses.SpecialZones or {}
Spearhead.classes.stageClasses.SpecialZones.abstract = Spearhead.classes.stageClasses.SpecialZones.abstract or {}
Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone = BuildableZone
return BuildableZone
@@ -1,3 +1,16 @@
local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local DcsUtil = require("classes.util.DcsUtil")
local FarpZone = require("classes.stageClasses.SpecialZones.FarpZone")
local SupplyHub = require("classes.stageClasses.SpecialZones.SupplyHub")
local Util = require("classes.util.Util")
local ZoneMission = require("classes.stageClasses.missions.ZoneMission")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local Persistence = require("classes.persistence.Persistence")
local StageBase = require("classes.stageClasses.SpecialZones.StageBase")
local BlueSam = require("classes.stageClasses.SpecialZones.BlueSam")
local Events = require("classes.spearhead_events")
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
---@alias StageColor
---| "RED"
@@ -71,8 +84,6 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
logger:debug("[BaseStage] Initiating stage with name: " .. initData.stageZoneName)
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup
self.zoneName = initData.stageZoneName
self.stageNumber = initData.stageNumber
self._isActive = false
@@ -101,11 +112,11 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
self._activeStage = -99
self._preActivated = false
self._stageConfig = stageConfig or {}
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel)
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel)
local zone = Spearhead.DcsUtil.getZoneByName(self.zoneName)
local zone = DcsUtil.getZoneByName(self.zoneName)
if zone then
self._stageDrawingId = Spearhead.DcsUtil.DrawZone(zone, Stage.StageColors.INVISIBLE, Stage.StageColors.INVISIBLE, 4)
self._stageDrawingId = DcsUtil.DrawZone(zone, Stage.StageColors.INVISIBLE, Stage.StageColors.INVISIBLE, 4)
end
self._spawnedGroups = {}
@@ -114,13 +125,13 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
local farpNames = database:getFarpNamesInStage(self.zoneName)
for _, farpName in pairs(farpNames) do
local farp = Spearhead.classes.stageClasses.SpecialZones.FarpZone.New(database, logger, farpName, spawnManager)
local farp = FarpZone.New(database, logger, farpName, spawnManager)
table.insert(self._db.farps, farp)
end
local supplyHubNames = database:getSupplyHubsInStage(self.zoneName)
for _, supplyHubName in pairs(supplyHubNames) do
local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(database, logger, supplyHubName)
local supplyHub = SupplyHub.new(database, logger, supplyHubName)
table.insert(self._db.supplyHubs, supplyHub)
end
@@ -144,17 +155,17 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
do -- load tables
local missionZones = database:getMissionsForStage(self.zoneName)
self._logger:debug("Found " .. Spearhead.Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName)
self._logger:debug("Found " .. Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName)
for _, missionZone in pairs(missionZones) do
local mission = Spearhead.classes.stageClasses.missions.ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager)
local mission = ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager)
if mission then
self._db.missionsByCode[mission.code] = mission
if mission.name and self._db.missionsByName[mission.name] == nil then
self._db.missionsByName[mission.name] = mission
else
Spearhead.AddMissionEditorWarning("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName)
MissionEditorWarnings.Add("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName)
end
if mission.missionType == "SAM" then
@@ -170,7 +181,7 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
---@type table<string, Array<Mission>>
local randomMissionByName = {}
for _, missionZoneName in pairs(randomMissionNames) do
local mission = Spearhead.classes.stageClasses.missions.ZoneMission.new(missionZoneName, self._missionPriority, database, logger, self, spawnManager)
local mission = ZoneMission.new(missionZoneName, self._missionPriority, database, logger, self, spawnManager)
if mission then
if randomMissionByName[mission.name] == nil then
randomMissionByName[mission.name] = {}
@@ -181,18 +192,18 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
for missionName, missions in pairs(randomMissionByName) do
local missionZonePicked = Spearhead.classes.persistence.Persistence.GetPickedRandomMission(missionName)
local missionZonePicked = Persistence.GetPickedRandomMission(missionName)
if missionZonePicked == nil then
local mission = Spearhead.Util.randomFromList(missions) --[[@as Mission]]
local mission = Util.randomFromList(missions) --[[@as Mission]]
if mission then
Spearhead.classes.persistence.Persistence.RegisterPickedRandomMission(mission.name, mission.zoneName)
Persistence.RegisterPickedRandomMission(mission.name, mission.zoneName)
self._db.missionsByCode[mission.code] = mission
if mission.name and self._db.missionsByName[mission.name] == nil then
self._db.missionsByName[mission.name] = mission
else
Spearhead.AddMissionEditorWarning("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName)
MissionEditorWarnings.Add("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName)
end
if mission.missionType == "SAM" then
@@ -223,13 +234,13 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
local airbaseNames = database:getAirbaseNamesInStage(self.zoneName)
if airbaseNames ~= nil and type(airbaseNames) == "table" then
for _, airbaseName in pairs(airbaseNames) do
local airbase = Spearhead.classes.stageClasses.SpecialZones.StageBase.New(database, logger, airbaseName, spawnManager)
local airbase = StageBase.New(database, logger, airbaseName, spawnManager)
table.insert(self._db.airbases, airbase)
end
end
for _, samZoneName in pairs(database:getBlueSamsInStage(self.zoneName)) do
local blueSam = Spearhead.classes.stageClasses.SpecialZones.BlueSam.New(database, logger, samZoneName, spawnManager)
local blueSam = BlueSam.New(database, logger, samZoneName, spawnManager)
table.insert(self._db.blueSams, blueSam)
end
@@ -242,7 +253,7 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
end
end
Spearhead.Events.AddStageNumberChangedListener(self)
Events.AddStageNumberChangedListener(self)
return self
end
@@ -304,14 +315,14 @@ function Stage:CheckAndUpdateSelf()
end
local max = dbTables.maxMissions
local availableMissionsCount = Spearhead.Util.tableLength(getAvailableMissions())
local availableMissionsCount = Util.tableLength(getAvailableMissions())
local activeCount = getActiveMissionsCount()
if activeCount < max and availableMissionsCount > 0 then
for i = activeCount+1, max do
if availableMissionsCount == 0 then
i = max+1 --exits this loop
else
local mission = Spearhead.Util.randomFromList(getAvailableMissions()) --[[@as Mission]]
local mission = Util.randomFromList(getAvailableMissions()) --[[@as Mission]]
if mission then
mission:SpawnActive()
activeCount = activeCount + 1;
@@ -391,8 +402,8 @@ function Stage:MarkStage(stageColor)
end
if self._stageDrawingId and self._stageConfig.isDrawStagesEnabled == true then
Spearhead.DcsUtil.SetLineColor(self._stageDrawingId, lineColor)
Spearhead.DcsUtil.SetFillColor(self._stageDrawingId, fillColor)
DcsUtil.SetLineColor(self._stageDrawingId, lineColor)
DcsUtil.SetFillColor(self._stageDrawingId, fillColor)
end
end
@@ -405,7 +416,7 @@ function Stage:ActivateStage()
self:PreActivate(false)
self._logger:debug("Activating Misc groups for zone. Count: " .. Spearhead.Util.tableLength(self._db.miscGroups))
self._logger:debug("Activating Misc groups for zone. Count: " .. Util.tableLength(self._db.miscGroups))
for _, miscGroup in pairs(self._db.miscGroups) do
miscGroup:Spawn()
end
@@ -452,7 +463,7 @@ function Stage:OnStageNumberChanged(number)
if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then
self._logger:debug("Pre-activating stage: " .. self.zoneName .. " with number: " .. number)
self:PreActivate(true)
elseif Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
elseif GlobalCapManager.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
self:PreActivate(false)
end
@@ -547,7 +558,7 @@ end
function Stage:ActivateBlueStage()
self._logger:debug("Setting stage '" .. Spearhead.Util.toString(self.zoneName) .. "' to blue")
self._logger:debug("Setting stage '" .. Util.toString(self.zoneName) .. "' to blue")
for _, mission in pairs(self._db.missions) do
mission:SpawnPersistedState()
@@ -575,11 +586,7 @@ function Stage:ActivateBlueStage()
timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3)
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
if not Spearhead.classes.stageClasses.Stages.BaseStage then Spearhead.classes.stageClasses.Stages.BaseStage = {} end
Spearhead.classes.stageClasses.Stages.BaseStage.Stage = Stage
return Stage
@@ -1,4 +1,7 @@
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
---@class ExtraStage : Stage
local ExtraStage = {}
ExtraStage.__index = ExtraStage
@@ -13,7 +16,6 @@ ExtraStage.__index = ExtraStage
---@return ExtraStage
function ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage
setmetatable(ExtraStage, Stage)
local self = setmetatable({}, { __index = ExtraStage }) --[[@as ExtraStage]]
@@ -46,7 +48,7 @@ function ExtraStage:OnStageNumberChanged(number)
if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then
self._logger:debug("Pre-activating stage: " .. self.zoneName .. " with number: " .. number)
self:PreActivate(true)
elseif Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
elseif GlobalCapManager.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
self:PreActivate(false)
end
@@ -61,9 +63,6 @@ function ExtraStage:OnStageNumberChanged(number)
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
Spearhead.classes.stageClasses.Stages.ExtraStage = ExtraStage
return ExtraStage
@@ -1,4 +1,6 @@
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
---@class PrimaryStage : Stage
local PrimaryStage = {}
@@ -13,7 +15,6 @@ PrimaryStage.__index = PrimaryStage
---@return PrimaryStage
function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage
setmetatable(PrimaryStage, Stage)
local self = setmetatable({}, { __index = PrimaryStage }) --[[@as PrimaryStage]]
@@ -22,9 +23,6 @@ function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
Spearhead.classes.stageClasses.Stages.PrimaryStage = PrimaryStage
return PrimaryStage
@@ -1,3 +1,4 @@
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
---@class WaitingStage : Stage
---@field private _waitTimeSeconds integer
@@ -18,8 +19,6 @@ local WaitingStageInitData = {}
---@param spawnManager SpawnManager
---@return WaitingStage
function WaitingStage.New(database, stageConfig, logger, initData, spawnManager)
local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage
setmetatable(WaitingStage, Stage)
local self = setmetatable({}, { __index = WaitingStage }) --[[@as WaitingStage]]
@@ -69,9 +68,6 @@ function WaitingStage:GetExpectedTime()
return self._startTime + self._waitTimeSeconds
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
Spearhead.classes.stageClasses.Stages.WaitingStage = WaitingStage
return WaitingStage
@@ -1,9 +1,10 @@
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
local Util = require("classes.util.Util")
---@class CustomDrawing
---@field private _id integer?
---@field private _drawingObject DrawingObject
---@field private _helper DrawingHelper
---@field private _startingStage number
---@field private _removeAtStage number
local CustomDrawing = {}
@@ -17,12 +18,11 @@ function CustomDrawing.New(drawingObject, id)
local self = setmetatable({}, CustomDrawing)
self._drawingObject = drawingObject
self._id = id
self._helper = Spearhead.classes.stageClasses.drawings.helper.DrawingHelper
local name = drawingObject.name
local split = Spearhead.Util.split_string(name or "", "_")
local split = Util.split_string(name or "", "_")
local secondPart = split[2] or "1"
local splitPart = Spearhead.Util.split_string(secondPart, ":")
local splitPart = Util.split_string(secondPart, ":")
self._startingStage = tonumber(splitPart[1]) or 1
self._removeAtStage = tonumber(splitPart[2]) or math.huge
return self
@@ -35,18 +35,14 @@ function CustomDrawing:GetStartAndStop()
end
function CustomDrawing:Draw()
self._id = self._helper.Draw(self._drawingObject)
self._id = DrawingHelper.Draw(self._drawingObject)
end
function CustomDrawing:Remove()
if self._id ~= nil then
self._helper.Remove(self._id)
DrawingHelper.Remove(self._id)
self._id = nil
end
end
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end
Spearhead.classes.stageClasses.drawings.CustomDrawing = CustomDrawing
return CustomDrawing
@@ -220,9 +220,4 @@ end
---@field public b number
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end
if not Spearhead.classes.stageClasses.drawings.helper then Spearhead.classes.stageClasses.drawings.helper = {} end
Spearhead.classes.stageClasses.drawings.helper.DrawingHelper = DrawingHelper
return DrawingHelper
@@ -1,3 +1,7 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
---@class BattleManager
---@field private _name string
---@field private _logger Logger
@@ -21,7 +25,7 @@ function BattleManager.New(redGroups, blueGroups, name, logLevel)
self._isActive = false
self._name = name
self._logger = Spearhead.LoggerTemplate.new("BattleManager_" .. name, logLevel)
self._logger = Logger.new("BattleManager_" .. name, logLevel)
self._redGroups = redGroups
self._blueGroups = blueGroups
@@ -167,7 +171,7 @@ function BattleManager:getBestAmmo(unit)
end
end
local entry = Spearhead.Util.randomFromList(shells)
local entry = Util.randomFromList(shells)
if entry and entry.desc and entry.desc.warhead then
local caliber = entry.desc.warhead.caliber
if caliber > 50 then
@@ -213,10 +217,10 @@ function BattleManager:ToShootingHulls(groups)
end
end
local hulls = Spearhead.Util.getSeparatedConvexHulls(points, 50)
local hulls = Util.getSeparatedConvexHulls(points, 50)
local enlargedHulls = {}
for _, hull in pairs(hulls) do
local enlarged = Spearhead.Util.enlargeConvexHull(hull, 25)
local enlarged = Util.enlargeConvexHull(hull, 25)
if enlarged then
table.insert(enlargedHulls, enlarged)
end
@@ -237,15 +241,15 @@ end
---@return Vec2?
function BattleManager:GetRandomPoint(origin, groupHulls)
local hull = Spearhead.Util.randomFromList(groupHulls) --[[@as Array<Vec2>]]
local hull = Util.randomFromList(groupHulls) --[[@as Array<Vec2>]]
if not hull then return nil end
local shootPoints = Spearhead.Util.GetTangentHullPointsFromOrigin(hull, origin)
local shootPoints = Util.GetTangentHullPointsFromOrigin(hull, origin)
if debugDrawing == true then
self:DrawDebugZone({ hull })
end
return Spearhead.Util.randomFromList(shootPoints) --[[@as Vec2]]
return Util.randomFromList(shootPoints) --[[@as Vec2]]
end
do --DEBUG
@@ -258,7 +262,7 @@ do --DEBUG
color = {r = 0, g = 0, b = 1, a = 1}
end
Spearhead.DcsUtil.DrawLine(unit:getPoint(), {x = target.x, y = 0, z = target.y}, color, 1)
DcsUtil.DrawLine(unit:getPoint(), {x = target.x, y = 0, z = target.y}, color, 1)
end
---@param hulls Array<Array<Vec2>>
@@ -274,13 +278,9 @@ do --DEBUG
location = { x=drawHull[1].x, y=drawHull[1].y },
}
Spearhead.DcsUtil.DrawZone(zone, {r =0, g=0, b =1, a = 0.5} ,{r =0, g= 0, b =1, a = 0}, 1)
DcsUtil.DrawZone(zone, {r =0, g=0, b =1, a = 0.5} ,{r =0, g= 0, b =1, a = 0}, 1)
end
end
end --DEBUG
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
Spearhead.classes.stageClasses.helpers.BattleManager = BattleManager
return BattleManager
@@ -0,0 +1,20 @@
---@class MaxLoadConfig
---@field maxInternalLoad number
---@type table<string, MaxLoadConfig>
local MaxLoadConfig = {
["Mi-8MT"] = {
maxInternalLoad = 4000,
},
["CH-47Fbl1"] = {
maxInternalLoad = 10000
},
["Mi-24P"] = {
maxInternalLoad = 2000
},
["UH-1H"] = {
maxInternalLoad = 2000
}
}
return MaxLoadConfig
@@ -1,3 +1,10 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local Logger = require("classes.util.Logger")
local SpearheadEvents = require("classes.spearhead_events")
local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker")
local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper")
---@class MissionCommandsHelper
---@field missionsByCode table<string, Mission> @table of missions by their code
@@ -17,8 +24,8 @@ MissionCommandsHelper.__index = MissionCommandsHelper
---@param groupPos Vec2
local function sortMissions(list, groupPos)
table.sort(list, function(a, b)
local distA = Spearhead.Util.VectorDistance2d(groupPos, a.location or {x=0, y=0})
local distB = Spearhead.Util.VectorDistance2d(groupPos, b.location or {x=0, y=0})
local distA = Util.VectorDistance2d(groupPos, a.location or {x=0, y=0})
local distB = Util.VectorDistance2d(groupPos, b.location or {x=0, y=0})
return distA < distB;
end)
end
@@ -33,7 +40,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
if instance == nil then
instance = setmetatable({}, MissionCommandsHelper)
instance._logger = Spearhead.LoggerTemplate.new("MissionCommandsHelper", logLevel)
instance._logger = Logger.new("MissionCommandsHelper", logLevel)
instance._logger:info("Creating MissionCommandsHelper instance")
@@ -45,7 +52,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
instance._supplyHubGroups = {}
instance._stageBriefings = {}
instance._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logLevel)
instance._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logLevel)
---comment
---@param selfA MissionCommandsHelper
@@ -56,7 +63,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
return time + 10
end
for _, unit in pairs(Spearhead.DcsUtil.getAllPlayerUnits()) do
for _, unit in pairs(DcsUtil.getAllPlayerUnits()) do
if unit and unit:isExist() then
local group = unit:getGroup()
if group then
@@ -71,7 +78,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
end
timer.scheduleFunction(instance.updateContinuous, instance, timer.getTime() + 5)
Spearhead.Events.AddOnPlayerEnterUnitListener(instance)
SpearheadEvents.AddOnPlayerEnterUnitListener(instance)
end
@@ -174,7 +181,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID)
local text = "Missions Overview\n\n"
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(id)
local group = DcsUtil.GetPlayerGroupByGroupID(id)
---@type Vec2
local groupPos = { x=0, y=0 }
if group then
@@ -193,7 +200,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID)
if lead and lead:isExist() == true then
local pos = lead:getPoint()
local Vec2Pos = { x= pos.x, y=pos.z }
local distance = Spearhead.Util.VectorDistance2d(Vec2Pos, mission.location) / 1852
local distance = Util.VectorDistance2d(Vec2Pos, mission.location) / 1852
distanceText = string.format("~%d", math.floor(distance))
end
end
@@ -323,7 +330,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
local perFolder = 9
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
---@type Vec2
local groupPos = { x=0, y=0 }
if group then
@@ -351,7 +358,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for _, mission in pairs(primaryMissions) do
count = count + 1
if count <= perFolder then
local copied = Spearhead.Util.deepCopyTable(path)
local copied = Util.deepCopyTable(path)
self:addMissionCommands(groupID, copied, mission)
else
local name = "Next Menu ..."
@@ -380,7 +387,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for _, mission in pairs(secondaryMissions) do
count = count + 1
if count <= perFolder then
local copied = Spearhead.Util.deepCopyTable(path)
local copied = Util.deepCopyTable(path)
self:addMissionCommands(groupID, copied, mission)
else
local name = "Next Menu ..."
@@ -401,14 +408,14 @@ function MissionCommandsHelper:addMissionCommands(groupId, path, mission)
if path then
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId)
local group = DcsUtil.GetPlayerGroupByGroupID(groupId)
local distance = "[?]"
if group then
local lead = group:getUnit(1)
if lead and lead:isExist() == true then
local pos = lead:getPoint()
local Vec2Pos = { x= pos.x, y=pos.z }
local dist = Spearhead.Util.VectorDistance2d(Vec2Pos, mission.location) / 1852
local dist = Util.VectorDistance2d(Vec2Pos, mission.location) / 1852
distance = "[" .. string.format("~%dnM", math.floor(dist)) .. "]"
end
end
@@ -435,7 +442,7 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID)
self._logger:debug("Adding supply hub commands for group: " .. tostring(groupID))
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then return end
local unit = group:getUnit(1)
@@ -446,6 +453,7 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID)
---@field groupID number
---@field crateType CrateType
---@field supplyUnitsTracker SupplyUnitsTracker
---@field commandHelper MissionCommandsHelper
---comment
---@param params LoadCargoCommandParams
@@ -453,36 +461,36 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID)
local crateType = params.crateType
local supplyUnitsTracker = params.supplyUnitsTracker
if supplyUnitsTracker then
supplyUnitsTracker:UnitRequestCrateLoading(params.groupID, crateType)
supplyUnitsTracker:UnitRequestCrateLoading(params.groupID, crateType, params.commandHelper)
end
end
local path = { [1] = folderNames.supplyHub }
---@type LoadCargoCommandParams
local farpParams1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_1000", supplyUnitsTracker = self._supplyUnitsTracker }
local farpParams1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_1000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self }
missionCommands.addCommandForGroup(groupID, "Load FARP Crate (1000)", path, loadCargoCommand, farpParams1000)
---@type LoadCargoCommandParams
local farpParams2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker }
local farpParams2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self }
missionCommands.addCommandForGroup(groupID, "Load FARP Crate (2000)", path, loadCargoCommand, farpParams2000)
---@type LoadCargoCommandParams
local samParms1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker }
local samParms1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self }
missionCommands.addCommandForGroup(groupID, "Load SAM Crate (1000)", path, loadCargoCommand, samParms1000)
---@type LoadCargoCommandParams
local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker }
local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self }
missionCommands.addCommandForGroup(groupID, "Load SAM Crate (2000)", path, loadCargoCommand, samParms2000)
---@type LoadCargoCommandParams
local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "AIRBASE_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker }
missionCommands.addCommandForGroup(groupID, "Airbase Crate (2000)", path, loadCargoCommand, samParms2000)
local airbaseParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "AIRBASE_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self }
missionCommands.addCommandForGroup(groupID, "Airbase Crate (2000)", path, loadCargoCommand, airbaseParms2000)
end
function MissionCommandsHelper:AddCargoCommands(groupID)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then return end
local unit = group:getUnit(1)
@@ -492,24 +500,26 @@ function MissionCommandsHelper:AddCargoCommands(groupID)
---@field unitID number
---@field crateType CrateType
---@field supplyUnitsTracker SupplyUnitsTracker
---@field commandHelper MissionCommandsHelper
---comment
---@param params UnloadCargoCommandParams
local unloadCargoCommand = function(params)
local unitID = params.unitID
local crateType = params.crateType
params.supplyUnitsTracker:UnloadRequested(unitID, crateType)
local supplyUnitsTracker = params.supplyUnitsTracker
params.supplyUnitsTracker:UnloadRequested(unitID, crateType, params.commandHelper)
end
local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID())
if cargo then
for cargoType, amount in pairs(cargo) do
local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(cargoType)
local cargoConfig = SupplyConfigHelper.getSupplyConfig(cargoType)
if cargoConfig then
for i = 1, amount do
local path = { [1] = folderNames.cargo }
---@type UnloadCargoCommandParams
local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self._supplyUnitsTracker }
local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self }
missionCommands.addCommandForGroup(groupID, "Unload " .. cargoConfig.displayName, path, unloadCargoCommand, params)
end
end
@@ -531,7 +541,7 @@ function MissionCommandsHelper:addMissionFolders(groupId)
missionCommands.addSubMenuForGroup(groupId, folderNames.supplyHub)
end
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId)
local group = DcsUtil.GetPlayerGroupByGroupID(groupId)
if group == nil then return end
local unit = group:getUnit(1)
@@ -561,8 +571,4 @@ function MissionCommandsHelper:ResetFolders(groupID)
self:addMissionFolders(groupID)
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper = MissionCommandsHelper
return MissionCommandsHelper
@@ -1,4 +1,6 @@
local Util = require("classes.util.Util")
---@alias SupplyType
---| "FARP_CRATE"
---| "SAM_CRATE"
@@ -65,24 +67,6 @@ local SupplyConfig = {
},
}
---@class MaxLoadConfig
---@field maxInternalLoad number
---@type table<string, MaxLoadConfig>
MaxLoadConfig = {
["Mi-8MT"] = {
maxInternalLoad = 4000,
},
["CH-47Fbl1"] = {
maxInternalLoad = 10000
},
["Mi-24P"] = {
maxInternalLoad = 2000
},
["UH-1H"] = {
maxInternalLoad = 2000
}
}
---@class SupplyConfigHelper
local SupplyConfigHelper = {}
@@ -92,7 +76,7 @@ local SupplyConfigHelper = {}
---@return SupplyConfig?
function SupplyConfigHelper.fromObjectName(name)
for configName, config in pairs(SupplyConfig) do
if Spearhead.Util.startswith(name, configName, true) == true then
if Util.startswith(name, configName, true) == true then
return config
end
end
@@ -104,11 +88,6 @@ function SupplyConfigHelper.getSupplyConfig(type)
return SupplyConfig[type]
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
if Spearhead.classes.stageClasses.helpers.supplies == nil then Spearhead.classes.stageClasses.helpers.supplies = {} end
Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig = MaxLoadConfig
Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper = SupplyConfigHelper
return SupplyConfigHelper
@@ -1,15 +1,24 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local SpearheadEvents = require("classes.spearhead_events")
local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper")
local MaxLoadConfig = require("classes.stageClasses.helpers.MaxLoadConfig")
env.info("Spearhead SupplyUnitsTracker loaded")
---@class SupplyUnitEventListener
---@field supplyUnitSpawned fun(self:SupplyUnitEventListener, unit:Unit) | nil
---@field enteredSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil
---@field exitedSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil
---@class SupplyUnitsTracker
---@field private _supplyUnitsByName table<string, Unit>
---@field private _cargoInUnits table<string, table<CrateType, number>>
---@field private _logger Logger
---@field private _unitPositions table<string, Vec3>
---@field private _commandsHelper MissionCommandsHelper
---@field private _unitInSupplyHub table<string, boolean>
---@field private _droppedCrates table<string, StaticObject>
---@field private _registeredHubs table<SupplyHub, boolean>
---@field private _supplyUnitSpawnedListener Array<SupplyUnitSpawnedListener>
---@field private _supplyUnitEventsListeners Array<SupplyUnitEventListener>
local SupplyUnitsTracker = {}
SupplyUnitsTracker.__index = SupplyUnitsTracker
@@ -23,17 +32,16 @@ function SupplyUnitsTracker.getOrCreate(logLevel)
if singleton == nil then
singleton = setmetatable({}, SupplyUnitsTracker)
singleton._logger = Spearhead.LoggerTemplate.new("SupplyUnitsTracker", logLevel)
singleton._logger = Logger.new("SupplyUnitsTracker", logLevel)
singleton._unitPositions = {}
singleton._cargoInUnits = {}
singleton._supplyUnitsByName = {}
singleton._droppedCrates = {}
singleton._registeredHubs = {}
singleton._supplyUnitSpawnedListener = {}
singleton._supplyUnitEventsListeners = {}
singleton._unitInSupplyHub = {}
singleton._commandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel)
Spearhead.Events.AddOnPlayerEnterUnitListener(singleton)
SpearheadEvents.AddOnPlayerEnterUnitListener(singleton)
---@param selfA SupplyUnitsTracker
local function updateTask(selfA, time)
@@ -70,33 +78,34 @@ function SupplyUnitsTracker:OnPlayerEntersUnit(unit)
if self:IsSupplyUnit(unit) == true then
self._supplyUnitsByName[unit:getName()] = unit
self._cargoInUnits[tostring(unit:getID())] = nil
self._unitInSupplyHub[tostring(unit:getID())] = false
self._unitPositions[tostring(unit:getID())] = unit:getPoint()
end
for _, listener in pairs(self._supplyUnitSpawnedListener) do
for _, listener in pairs(self._supplyUnitEventsListeners) do
pcall(function()
listener:SupplyUnitSpawned(unit)
if listener.supplyUnitSpawned then
listener:supplyUnitSpawned(unit)
end
end)
end
end
---@class SupplyUnitSpawnedListener
---@field SupplyUnitSpawned fun(self:SupplyUnitSpawnedListener, unit:Unit)
---@param listener SupplyUnitSpawnedListener
---@param listener SupplyUnitEventListener
function SupplyUnitsTracker:AddOnSupplyUnitSpawnedListener(listener)
if listener == nil then return end
if self._supplyUnitSpawnedListener == nil then
self._supplyUnitSpawnedListener = {}
if self._supplyUnitEventsListeners == nil then
self._supplyUnitEventsListeners = {}
end
table.insert(self._supplyUnitSpawnedListener, listener)
table.insert(self._supplyUnitEventsListeners, listener)
end
function SupplyUnitsTracker:Update()
local players = Spearhead.DcsUtil.getAllPlayerUnits()
local players = DcsUtil.getAllPlayerUnits()
for _, player in pairs(players) do
if player ~= nil and player:isExist() and self:IsSupplyUnit(player) == true then
self._supplyUnitsByName[player:getName()] = player
@@ -127,7 +136,7 @@ function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType)
if unitID == nil or crateType == nil then return end
local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID)
local unit = DcsUtil.GetPlayerUnitByID(unitID)
if unit == nil then return end
if self._cargoInUnits[unitID] == nil then
@@ -142,6 +151,7 @@ function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType)
end
---@private
---@param unitID number
---@param crateType CrateType
function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType)
@@ -168,13 +178,14 @@ function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType)
end
---@private
---@param unit Unit
function SupplyUnitsTracker:UpdateWeightForUnit(unit)
local weight = 0
if self._cargoInUnits[tostring(unit:getID())] then
for crateType, count in pairs(self._cargoInUnits[tostring(unit:getID())]) do
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfig and count then
weight = weight + (crateConfig.weight * count)
end
@@ -197,10 +208,30 @@ function SupplyUnitsTracker:CheckUnitsInZones()
if enabled == true then
local zone = hub:GetZone()
if zone ~= nil then
if Spearhead.Util.is3dPointInZone(pos, zone) then
self._commandsHelper:MarkUnitInSupplyHub(group:getID())
if Util.is3dPointInZone(pos, zone) then
if self._unitInSupplyHub[tostring(unit:getID())] ~= true then
self._unitInSupplyHub[tostring(unit:getID())] = true
for _, listener in pairs(self._supplyUnitEventsListeners) do
pcall(function()
if listener.enteredSupplyHub then
listener:enteredSupplyHub(unit, hub)
end
end)
end
end
else
self._commandsHelper:MarkUnitOutsideSupplyHub(group:getID())
if self._unitInSupplyHub[tostring(unit:getID())] == true then
self._unitInSupplyHub[tostring(unit:getID())] = false
for _, listener in pairs(self._supplyUnitEventsListeners) do
pcall(function()
if listener.exitedSupplyHub then
listener:exitedSupplyHub(unit, hub)
end
end)
end
end
end
end
end
@@ -237,11 +268,16 @@ function SupplyUnitsTracker:GetUnits()
end
local cargoCount = 0
function SupplyUnitsTracker:UnloadRequested(unitID, crateType)
---comment
---@param unitID number
---@param crateType CrateType
---@param missionCommandsHelper MissionCommandsHelper
function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHelper)
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID)
local unit = DcsUtil.GetPlayerUnitByID(unitID)
if unit == nil or unit:isExist() == false then return end
local group = unit:getGroup()
if group == nil then
@@ -251,7 +287,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType)
self:RemoveCargoFromUnit(unitID, crateType)
self:UpdateWeightForUnit(unit)
local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
local cargoConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if cargoConfig == nil then
self._logger:error("Invalid crate type: " .. crateType)
@@ -270,7 +306,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType)
local spawned = coalition.addStaticObject(unit:getCoalition(), cargoSpawnObject)
self._droppedCrates[cargoSpawnObject.name] = spawned
self._commandsHelper:updateCommandsForGroup(group:getID())
missionCommandsHelper:updateCommandsForGroup(group:getID())
end
---@return table<string,StaticObject>
@@ -281,14 +317,15 @@ end
---Loads a crate directly into the unit
---@param groupID number
---@param crateType CrateType
function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType)
---@param missionCommandsHelper MissionCommandsHelper
function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType, missionCommandsHelper)
self._logger:debug("UnitRequestCrateLoading called with groupID: " .. groupID .. " and crateType: " .. crateType)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group ~= nil then
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfig == nil then
self._logger:error("Invalid crate type: " .. crateType)
return
@@ -312,11 +349,12 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType)
---@field unit Unit
---@field groupID number
---@field crateType CrateType
---@field commandHelper MissionCommandsHelper
---@param params LoadCargoParams
local LoadCrateTask = function(params)
local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType)
local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType, params.commandHelper)
if loaded ~= false then
trigger.action.outTextForUnit(unit:getID(), "Loaded crate :" .. params.crateType, 10)
end
@@ -327,7 +365,8 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType)
self = self,
unit = unit,
crateType = crateType,
groupID = groupID
groupID = groupID,
commandHelper = missionCommandsHelper
}
timer.scheduleFunction(LoadCrateTask, params, timer.getTime() + 15)
@@ -338,10 +377,11 @@ end
---comment
---@param unit Unit
---@param crateType CrateType
---@param commandHelper MissionCommandsHelper
---@return boolean
function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType)
function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper)
local crateConfigA = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfigA == nil then
trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5)
return false
@@ -354,7 +394,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType)
end
end
local unitConfig = Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig[unit:getTypeName()]
local unitConfig = MaxLoadConfig[unit:getTypeName()]
if unitConfig == nil then
trigger.action.outTextForUnit(unit:getID(), "Your unit type is not configured for logistics: " .. crateType, 5)
self._logger:error("Invalid unit type: " .. unit:getTypeName())
@@ -373,7 +413,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType)
local group = unit:getGroup()
if group == nil then return false end
local groupID = group:getID()
self._commandsHelper:updateCommandsForGroup(groupID)
commandHelper:updateCommandsForGroup(groupID)
return true
end
@@ -383,10 +423,10 @@ end
---@param crateType CrateType
function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfig == nil then
self._logger:error("Invalid crate type: " .. crateType)
return
@@ -404,19 +444,19 @@ end
function SupplyUnitsTracker:GetCargoPlacePosition(unit)
local pos = unit:getPosition()
local preferedPos = {
local preferredPos = {
x = pos.p.x - 10 * pos.x.x,
y = pos.p.y - 10 * pos.x.y,
z = pos.p.z - 10 * pos.x.z
}
return preferedPos
return preferredPos
-- local volume = {
-- id = world.VolumeType.SPHERE,
-- params = {
-- point = preferedPos,
-- point = preferredPos,
-- radius = 10
-- }
-- }
@@ -446,9 +486,4 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit)
end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker = SupplyUnitsTracker
return SupplyUnitsTracker
@@ -1,6 +1,12 @@
local Mission = require("classes.stageClasses.missions.baseMissions.Mission")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local GlobalConfig = require("classes.configuration.GlobalConfig")
local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper")
---@class BuildableMission : Mission, SupplyUnitSpawnedListener
---@class BuildableMission : Mission, SupplyUnitEventListener
---@field private _requiredKilos number
---@field private _droppedKilos number
---@field private _crateType SupplyType
@@ -27,7 +33,6 @@ BuildableMission.__index = BuildableMission
---@param logger Logger
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType)
local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission
setmetatable(BuildableMission, Mission)
local self = setmetatable({}, { __index = BuildableMission })
@@ -42,7 +47,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
if noLandingZone then
local verts = noLandingZone.verts
local enlarged = Spearhead.Util.enlargeConvexHull(verts, 300)
local enlarged = Util.enlargeConvexHull(verts, 300)
---@type SpearheadTriggerZone
local dropOfZone = {
@@ -71,7 +76,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self._onCrateDroppedOfListeners = {}
self._completeListeners = {}
self._markIDsPerGroup = {}
self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel)
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logger.LogLevel)
self._state = "NEW"
@@ -82,7 +87,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self.priority = "secondary"
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(self._logger.LogLevel)
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(self._logger.LogLevel)
self._crateType = requiredCrateType
@@ -96,11 +101,11 @@ end
function BuildableMission:ShowBriefing(groupID)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then return end
local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group)
local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
local unitType = DcsUtil.getUnitTypeFromGroup(group)
local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
local siteType = "FARP"
if self._crateType == "SAM_CRATE" then
@@ -119,18 +124,18 @@ function BuildableMission:ShowBriefing(groupID)
"\n\n" ..
"NOTE: Do not land in the orange construction zone!"
trigger.action.outTextForGroup(groupID, briefing, Spearhead.GlobalConfig:getBriefingTime())
trigger.action.outTextForGroup(groupID, briefing, GlobalConfig:getBriefingTime())
end
function BuildableMission:MarkMissionAreaToGroup(groupID)
if self._markIDsPerGroup[groupID] then
Spearhead.DcsUtil.RemoveMark(self._markIDsPerGroup[groupID])
DcsUtil.RemoveMark(self._markIDsPerGroup[groupID])
end
local text = "[" .. self.code .. "] " .. self.name .. " | " .. self._crateType
local location = { x= self.location.x, y=land.getHeight(self.location), z=self.location.y }
local markID = Spearhead.DcsUtil.AddMarkToGroup(groupID, text, location)
local markID = DcsUtil.AddMarkToGroup(groupID, text, location)
self._markIDsPerGroup[groupID] = markID
end
@@ -163,7 +168,7 @@ function BuildableMission:SpawnActive()
local lineColor = { r=230/255, g=93/255, b=49/255, a=1}
---@type DrawColor
local fillColor = { r=230/255, g=93/255, b=49/255, a=0.2}
self._noLandingZoneId = Spearhead.DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6)
self._noLandingZoneId = DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6)
if self._dropOffZone == nil then
self._logger:error("No drop off zone found for mission: " .. self.code)
@@ -172,7 +177,7 @@ function BuildableMission:SpawnActive()
local lineColor2 = { r=0, g=0, b=1, a=1}
local fillColor2 = { r=0, g=0, b=1, a=0}
self._dropOffZoneId = Spearhead.DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6)
self._dropOffZoneId = DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6)
---@param selfA BuildableMission
---@param time number
@@ -230,17 +235,17 @@ function BuildableMission:CheckCratesInZone()
local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
for _, staticObject in pairs(crates) do
if staticObject and staticObject:isExist() and Spearhead.Util.startswith(staticObject:getName(), self._crateType, true) then
if staticObject and staticObject:isExist() and Util.startswith(staticObject:getName(), self._crateType, true) then
local pos = staticObject:getPoint()
if Spearhead.Util.is3dPointInZone(pos, self._dropOffZone) then
if Util.is3dPointInZone(pos, self._dropOffZone) then
table.insert(foundCrates, staticObject)
end
end
end
for _, foundCrate in pairs(foundCrates) do
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.fromObjectName(foundCrate:getName())
local crateConfig = SupplyConfigHelper.fromObjectName(foundCrate:getName())
if crateConfig then
self._droppedKilos = self._droppedKilos + crateConfig.weight
foundCrate:destroy()
@@ -249,8 +254,8 @@ function BuildableMission:CheckCratesInZone()
end
if self._droppedKilos >= self._requiredKilos then
Spearhead.DcsUtil.RemoveMark(self._noLandingZoneId)
Spearhead.DcsUtil.RemoveMark(self._dropOffZoneId)
DcsUtil.RemoveMark(self._noLandingZoneId)
DcsUtil.RemoveMark(self._dropOffZoneId)
self:NotifyMissionComplete()
self._state = "COMPLETED"
end
@@ -258,7 +263,7 @@ function BuildableMission:CheckCratesInZone()
if self._state == "COMPLETED" then
for groupID, markID in pairs(self._markIDsPerGroup) do
if markID then
Spearhead.DcsUtil.RemoveMark(markID)
DcsUtil.RemoveMark(markID)
self._markIDsPerGroup[groupID] = nil
end
end
@@ -267,7 +272,4 @@ function BuildableMission:CheckCratesInZone()
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
Spearhead.classes.stageClasses.missions.BuildableMission = BuildableMission
return BuildableMission
@@ -1,3 +1,7 @@
local Mission = require("classes.stageClasses.missions.baseMissions.Mission")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
---@class RunwayStrikeMission : Mission
---@field runwayBombingTracker RunwayBombingTracker
---@field private _runway Runway
@@ -22,8 +26,7 @@ local RunwayStrikeMission = {}
---@param runwayBombingTracker RunwayBombingTracker
---@return RunwayStrikeMission?
function RunwayStrikeMission.new(runway, airbaseName, database, logger, runwayBombingTracker)
local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission
RunwayStrikeMission.__index = RunwayStrikeMission
setmetatable(RunwayStrikeMission, Mission)
local self = setmetatable({}, RunwayStrikeMission)
@@ -82,7 +85,7 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass)
for _, section in pairs(self._runwaySections) do
local zone = self:SectionToSpearheadZone(section)
if Spearhead.Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
if section.kilosHit == nil then
section.kilosHit = 0
end
@@ -153,10 +156,10 @@ function RunwayStrikeMission:Draw()
local color = { r=0, g=1, b=0, a=0.5 }
runwaySection.drawID = Spearhead.DcsUtil.DrawZone(zone, color, color, 5)
runwaySection.drawID = DcsUtil.DrawZone(zone, color, color, 5)
else
Spearhead.DcsUtil.SetFillColor(runwaySection.drawID, fillColor)
Spearhead.DcsUtil.SetLineColor(runwaySection.drawID, lineColor)
DcsUtil.SetFillColor(runwaySection.drawID, fillColor)
DcsUtil.SetLineColor(runwaySection.drawID, lineColor)
end
end
@@ -342,7 +345,7 @@ local repairStaticConfigs = {
---@private
---@param section RunwaySection
function RunwayStrikeMission:AddOrUpdateRepairStatics(section)
if Spearhead.Util.tableLength(section.repairGroups) > 0 then
if Util.tableLength(section.repairGroups) > 0 then
return
end
@@ -351,7 +354,7 @@ function RunwayStrikeMission:AddOrUpdateRepairStatics(section)
y = section.center.y,
}
local repairGroup = Spearhead.Util.randomFromList(repairStaticConfigs)
local repairGroup = Util.randomFromList(repairStaticConfigs)
for _, repairStatic in pairs(repairGroup) do
repairStatic.x = location.x + repairStatic.x
@@ -369,7 +372,7 @@ end
---@private
---@param section RunwaySection
function RunwayStrikeMission:RemoveRepairStatics(section)
if Spearhead.Util.tableLength(section.repairGroups) == 0 then
if Util.tableLength(section.repairGroups) == 0 then
return
end
@@ -537,9 +540,4 @@ function RunwayStrikeMission:RunwayToSpearheadZone(runway)
}
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
Spearhead.classes.stageClasses.missions.RunwayStrikeMission = RunwayStrikeMission
return RunwayStrikeMission
@@ -1,3 +1,11 @@
local Util = require("classes.util.Util")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local Mission = require("classes.stageClasses.missions.baseMissions.Mission")
local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup")
local Events = require("classes.spearhead_events")
local BattleManager = require("classes.stageClasses.helpers.BattleManager")
local DcsUtil = require("classes.util.DcsUtil")
--- ZoneMission is missions that are defined by zones in the ME
---@class ZoneMission : Mission, OnUnitLostListener
---@field private _state MissionState
@@ -26,13 +34,13 @@ local ZoneMission = {}
---@param input string
---@return ParsedMissionName?
local function ParseZoneName(input)
local split_name = Spearhead.Util.split_string(input, "_")
local split_length = Spearhead.Util.tableLength(split_name)
if Spearhead.Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
Spearhead.AddMissionEditorWarning("Random Mission with zonename " .. input .. " not in right format")
local split_name = Util.split_string(input, "_")
local split_length = Util.tableLength(split_name)
if Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
MissionEditorWarnings.Add("Random Mission with zonename " .. input .. " not in right format")
return nil
elseif split_length < 3 then
Spearhead.AddMissionEditorWarning("Mission with zonename" .. input .. " not in right format")
MissionEditorWarnings.Add("Mission with zonename" .. input .. " not in right format")
return nil
end
@@ -47,7 +55,7 @@ local function ParseZoneName(input)
if inputType == "sam" then parsedType = "SAM" end
if parsedType == "nil" then
Spearhead.AddMissionEditorWarning("Mission with zonename '" ..
MissionEditorWarnings.Add("Mission with zonename '" ..
input .. "' has an unsupported type '" .. (type or "nil"))
return nil
end
@@ -69,7 +77,6 @@ MINIMAL_UNITS_ALIVE_RATIO = 0.21
---@param spawnManager SpawnManager
---@return ZoneMission?
function ZoneMission.new(zoneName, priority, database, logger, parentStage, spawnManager)
local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission
ZoneMission.__index = ZoneMission
setmetatable(ZoneMission, Mission)
@@ -112,8 +119,6 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
self._parentStage = parentStage
self._dependencies = {}
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup
if missionData.dependsOn then
for _, dependency in pairs(missionData.dependsOn) do
self._dependencies[dependency] = false
@@ -129,7 +134,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
end
self._missionGroups.sceneryTargets = missionData.SceneryTargets or {}
if Spearhead.Util.tableLength(self._missionGroups.sceneryTargets) > 0 then
if Util.tableLength(self._missionGroups.sceneryTargets) > 0 then
self._missionGroups.hasTargets = true
end
@@ -145,10 +150,10 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
table.insert(self._missionGroups.redGroups, spearheadGroup)
local isGroupTarget = Spearhead.Util.startswith(string.lower(groupName), "tgt_")
local isGroupTarget = Util.startswith(string.lower(groupName), "tgt_")
for _, unit in pairs(spearheadGroup:GetObjects()) do
local unitName = unit:getName()
local isUnitTarget = Spearhead.Util.startswith(string.lower(unitName), "tgt_")
local isUnitTarget = Util.startswith(string.lower(unitName), "tgt_")
if self._missionGroups.unitsAlive[groupName] == nil then
self._missionGroups.unitsAlive[groupName] = {}
@@ -167,18 +172,18 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
self._missionGroups.targetsAlive[groupName][unitName] = true
end
Spearhead.Events.addOnUnitLostEventListener(unitName, self)
Events.addOnUnitLostEventListener(unitName, self)
end
spearheadGroup:Destroy()
end
if self.missionType == "CAS" then
self._battleManager = Spearhead.classes.stageClasses.helpers.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
self._logger:debug("Mission " .. self.name .. " group count: " .. Spearhead.Util.tableLength(missionData.RedGroups))
self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups))
return self
end
@@ -327,7 +332,7 @@ function ZoneMission:UpdateState(checkHealth, messageIfDone)
end
if self._state == "COMPLETED" and self._lastContactMarkerID then
Spearhead.DcsUtil.RemoveMark(self._lastContactMarkerID)
DcsUtil.RemoveMark(self._lastContactMarkerID)
end
if self._state == "COMPLETED" and self._battleManager then
@@ -511,13 +516,10 @@ function ZoneMission:MarkLastContact(unit)
if self._lastContactMarkerID then
Spearhead.DcsUtil.RemoveMark(self._lastContactMarkerID)
DcsUtil.RemoveMark(self._lastContactMarkerID)
end
self._lastContactMarkerID = Spearhead.DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point)
self._lastContactMarkerID = DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point)
end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
Spearhead.classes.stageClasses.missions.ZoneMission = ZoneMission
return ZoneMission
@@ -1,5 +1,8 @@
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local DcsUtil = require("classes.util.DcsUtil")
local Util = require("classes.util.Util")
local GlobalConfig = require("classes.configuration.GlobalConfig")
---@class Mission
---@field name string
@@ -49,7 +52,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
self.location = database:GetLocationForMissionZone(zoneName)
self.missionTypeDisplay = self.missionType
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel)
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel)
return true, "success"
end
@@ -77,11 +80,11 @@ end
---@param groupId number
function Mission:ShowBriefing(groupId)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId)
local group = DcsUtil.GetPlayerGroupByGroupID(groupId)
if group == nil then return end
local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group)
local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
local unitType = DcsUtil.getUnitTypeFromGroup(group)
local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
self._logger:debug("Coords converted: " .. coords)
local stateString = self:ToStateString()
@@ -89,12 +92,12 @@ function Mission:ShowBriefing(groupId)
local briefing = self._missionBriefing
briefing = Spearhead.Util.replaceString(briefing, "{{coords}}", coords)
briefing = Spearhead.Util.replaceString(briefing, "{{ coords }}", coords)
briefing = Util.replaceString(briefing, "{{coords}}", coords)
briefing = Util.replaceString(briefing, "{{ coords }}", coords)
local text = "Mission [" ..
self.code .. "] " .. self.name .. "\n \n" .. briefing .. " \n \n" .. stateString
trigger.action.outTextForGroup(groupId, text, Spearhead.GlobalConfig:getBriefingTime());
trigger.action.outTextForGroup(groupId, text, GlobalConfig:getBriefingTime());
end
@@ -135,14 +138,6 @@ function Mission:ToStateString() return "status: in progress" end
--endregion
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
if not Spearhead.classes.stageClasses.missions.baseMissions then Spearhead.classes.stageClasses.missions.baseMissions = {} end
Spearhead.classes.stageClasses.missions.baseMissions.Mission = Mission
do --aliases
--- @alias MissionPriority
@@ -168,4 +163,4 @@ do --aliases
end
return Mission
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
local Util = require("classes.util.Util")
--- @class Logger
--- @field LoggerName string the name of the logger
--- @field LogLevel string the log level of the logger
local LOGGER = {}
do
local PreFix = "Spearhead"
---comment
---@param logger_name any
---@param logLevel LogLevel
---@return Logger
function LOGGER.new(logger_name, logLevel)
LOGGER.__index = LOGGER
local self = setmetatable({}, LOGGER)
self.LoggerName = logger_name or "(loggername not set)"
self.LogLevel = logLevel or "INFO"
return self
end
---@param message any the message
function LOGGER:info(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" then
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
end
end
---comment
---@param message string
function LOGGER:warn(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then
env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
end
end
---@param message any -- the message
function LOGGER:error(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.LogLevel == "ERROR" then
env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
end
end
---@param message any the message
function LOGGER:debug(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "DEBUG" then
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message)
end
end
end
return LOGGER
@@ -0,0 +1,26 @@
---@class MissionEditingWarnings
local MissionEditingWarnings = {}
function MissionEditingWarnings.Add(warningMessage)
table.insert(MissionEditingWarnings, warningMessage or "skip")
end
---@param logger Logger
function MissionEditingWarnings.WriteAll(logger)
if not logger then
return
end
if not MissionEditingWarnings or #MissionEditingWarnings == 0 then
return
end
logger:warn("Mission Editor Warnings:")
for _, warning in ipairs(MissionEditingWarnings) do
logger:warn("- " .. warning)
end
end
return MissionEditingWarnings
+503
View File
@@ -0,0 +1,503 @@
---@class Util
local UTIL = {}
do -- INIT UTIL
---splits a string in sub parts by seperator
---@param input string
---@param seperator string
---@return table result list of strings
function UTIL.split_string(input, seperator)
if seperator == nil then
seperator = " "
end
local result = {}
if input == nil then
return result
end
for str in string.gmatch(input, "[^" .. seperator .. "]+") do
table.insert(result, str)
end
return result
end
---comment
---@param table any
---@return number
function UTIL.tableLength(table)
if table == nil then return 0 end
local count = 0
for _ in pairs(table) do count = count + 1 end
return count
end
---@param orig table
---@return table copy
function UTIL.deepCopyTable(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[UTIL.deepCopyTable(orig_key)] = UTIL.deepCopyTable(orig_value)
end
setmetatable(copy, UTIL.deepCopyTable(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
---Gets a random from the list
---@param list Array
---@return any @random element from the list
function UTIL.randomFromList(list)
local max = #list
if max == 0 or max == nil then
return nil
end
local random = math.random(0, max)
if random == 0 then random = 1 end
return list[random]
end
---@param list Array
---@param start number start
---@param n number length
---@return Array
function UTIL.sublist(list, start, n)
local result = {}
for i = start, n do
result[#result + 1] = list[i]
end
return result
end
---@param str string
---@param find string
---@param replace string
---@return string
function UTIL.replaceString(str, find, replace)
if str == nil then return "" end
if find == nil or replace == nil then return str end
local result = str:gsub(find, replace)
return result
end
local function table_print(tt, indent, done)
done = done or {}
indent = indent or 0
if type(tt) == "table" then
local sb = {}
for key, value in pairs(tt) do
table.insert(sb, string.rep(" ", indent)) -- indent it
if type(value) == "table" and not done[value] then
done[value] = true
table.insert(sb, "[\"" ..key .. "\"]" .. " = {\n");
table.insert(sb, table_print(value, indent + 2, done))
table.insert(sb, string.rep(" ", indent)) -- indent it
table.insert(sb, "},\n");
elseif "number" == type(key) then
table.insert(sb, string.format("\"%s\",\n", tostring(value)))
else
table.insert(sb, string.format(
"[\"%s\"] = \"%s\",\n", tostring(key), tostring(value)))
end
end
return table.concat(sb)
else
return tt .. "\n"
end
end
---comment
---@param str string
---@param findable string
---@param ignoreCase boolean?
---@return boolean
UTIL.startswith = function(str, findable, ignoreCase)
if ignoreCase == true then
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
end
return str:find('^' .. findable) ~= nil
end
---comment
---@param str string
---@param findable string
---@return boolean
UTIL.strContains = function(str, findable)
return str:find(findable) ~= nil
end
---comment
---@param str string
---@param findableTable table
---@return boolean
UTIL.startswithAny = function(str, findableTable)
for key, value in pairs(findableTable) do
if type(value) == "string" and UTIL.startswith(str, value) then return true end
end
return false
end
function UTIL.toString(something)
if something == nil then
return "nil"
elseif "table" == type(something) then
return table_print(something)
elseif "string" == type(something) then
return something
else
return tostring(something)
end
end
---comment
---@param a Vec2
---@param b Vec2
---@return number
function UTIL.VectorDistance2d(a, b)
return math.sqrt((b.x - a.x) ^ 2 + (b.y - a.y) ^ 2)
end
---comment
---@param a Vec3
---@param b Vec3
---@return number
function UTIL.VectorDistance3d(a, b)
return UTIL.vectorMagnitude({ x = a.x - b.x, y = a.y - b.y, z = a.z - b.z })
end
---comment
---@param vec Vec3
---@return number
function UTIL.vectorMagnitude(vec)
return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5
end
---@param vec Vec3
---@return Vec3
function UTIL.vectorNormalize(vec)
local magnitude = UTIL.vectorMagnitude(vec)
if magnitude == 0 then
return { x = 0, y = 0, z = 0 }
end
return { x = vec.x / magnitude, y = vec.y / magnitude, z = vec.z / magnitude }
end
---@param vec Vec2
---@param direction number @in degrees
---@param distance number
---@return Vec2
function UTIL.vectorMove(vec, direction, distance)
local rad = math.rad(direction)
local x = vec.x + (math.cos(rad) * distance)
local y = vec.y + (math.sin(rad) * distance)
return { x = x, y = y}
end
---comment
---@param vec1 Vec2
---@param vec2 Vec2
---@return number in degrees
function UTIL.vectorHeadingFromTo(vec1, vec2)
local dx = vec2.x - vec1.x
local dy = vec2.y - vec1.y
local heading = math.deg(math.atan2(dy, dx))
if heading < 0 then
heading = heading + 360
end
return heading
end
---@param vec1 Vec3
---@param vec2 Vec3
---@return number alignment a number in range [-1,1]. > 0 same direction. < 0 opposite direction
function UTIL.vectorAlignment(vec1, vec2)
local vec1Norm = UTIL.vectorNormalize(vec1)
local vec2Norm = UTIL.vectorNormalize(vec2)
return ((vec1Norm.x * vec2Norm.x) + (vec1Norm.y * vec2Norm.y) + (vec1Norm.z * vec2Norm.z))
end
local function isInComplexPolygon(polygon, x, y)
local function getEdges(poly)
local result = {}
for i = 1, #poly do
local point1 = poly[i]
local point2Index = i + 1
if point2Index > #poly then point2Index = 1 end
local point2 = poly[point2Index]
local edge = { x1 = point1.x, z1 = point1.y, x2 = point2.x, z2 = point2.y }
table.insert(result, edge)
end
return result
end
local edges = getEdges(polygon)
local count = 0;
for _, edge in pairs(edges) do
if (x < edge.x1) ~= (x < edge.x2) and y < edge.z1 + ((x - edge.x1) / (edge.x2 - edge.x1)) * (edge.z2 - edge.z1) then
count = count + 1
-- if (yp < y1) != (yp < y2) and xp < x1 + ((yp-y1)/(y2-y1))*(x2-x1) then
-- count = count + 1
end
end
return count % 2 == 1
end
---comment
---@param polygon Array<Vec2> of pairs { x, y }
---@param x number X location
---@param y number Y location
---@return boolean
function UTIL.IsPointInPolygon(polygon, x, y)
return isInComplexPolygon(polygon, x, y)
end
---@param point Vec3
---@param zone SpearheadTriggerZone
function UTIL.is3dPointInZone(point, zone)
if zone.zone_type == "Polygon" and zone.verts then
if UTIL.IsPointInPolygon(zone.verts, point.x, point.z) == true then
return true
end
else
if (((point.x - zone.location.x) ^ 2 + (point.z - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then
return true
end
end
return false
end
---@param point Vec2
---@param zone SpearheadTriggerZone
function UTIL.is2dPointInZone(point, zone)
if zone.zone_type == "Polygon" and zone.verts then
if UTIL.IsPointInPolygon(zone.verts, point.x, point.y) == true then
return true
end
else
if (((point.x - zone.location.x) ^ 2 + (point.y - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then
return true
end
end
return false
end
---comment
---@param points Array<Vec2> points
---@return Array<Vec2> hullPoints
function UTIL.getConvexHull(points)
if #points == 0 then
return {}
end
---comment
---@param a Vec2
---@param b Vec2
---@param c Vec2
---@return boolean
local function ccw(a, b, c)
return (b.y - a.y) * (c.x - a.x) > (b.x - a.x) * (c.y - a.y)
end
table.sort(points, function(left, right)
return left.y < right.y
end)
local hull = {}
-- lower hull
for _, point in pairs(points) do
while #hull >= 2 and not ccw(hull[#hull - 1], hull[#hull], point) do
table.remove(hull, #hull)
end
table.insert(hull, point)
end
-- upper hull
local t = #hull + 1
for i = #points, 1, -1 do
local point = points[i]
while #hull >= t and not ccw(hull[#hull - 1], hull[#hull], point) do
table.remove(hull, #hull)
end
table.insert(hull, point)
end
table.remove(hull, #hull)
return hull
end
---Splits points into clusters with at least minSeparation between clusters, returns convex hull for each cluster
---@param points Array<Vec2>
---@param minSeparation number
---@return Array<Array<Vec2>> hulls
function UTIL.getSeparatedConvexHulls(points, minSeparation)
if #points == 0 then return {} end
-- Simple clustering: group points that are within minSeparation of each other
local clusters = {}
local assigned = {}
for i, p in ipairs(points) do
if not assigned[i] then
local cluster = { p }
assigned[i] = true
-- Find all points close to p (BFS)
local queue = { i }
while #queue > 0 do
local idx = table.remove(queue)
local base = points[idx]
for j, q in ipairs(points) do
if not assigned[j] then
local dx = base.x - q.x
local dy = base.y - q.y
if (dx * dx + dy * dy) <= (minSeparation * minSeparation) then
table.insert(cluster, q)
assigned[j] = true
table.insert(queue, j)
end
end
end
end
table.insert(clusters, cluster)
end
end
-- Compute convex hull for each cluster
local hulls = {}
for _, cluster in ipairs(clusters) do
local hull = UTIL.getConvexHull(cluster)
if #hull > 0 then
table.insert(hulls, hull)
end
end
return hulls
end
---@param points Array<Vec2>
function UTIL.enlargeConvexHull(points, meters)
if points == nil or #points == 0 then
return {}
end
---@type Array<Vec2>
local allpoints = {}
for _, point in pairs(points) do
table.insert(allpoints, point)
allpoints[#allpoints + 1] = point
allpoints[#allpoints+1] = { x = point.x + meters, y = point.y, }
allpoints[#allpoints+1] = { x = point.x - meters, y = point.y, }
allpoints[#allpoints+1] = { x = point.x, y = point.y + meters, }
allpoints[#allpoints+1] = { x = point.x, y = point.y - meters, }
allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, }
allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, }
allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, }
allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, }
end
return UTIL.getConvexHull(allpoints)
end
---Returns hull points visible from origin (not blocked by hull edges)
---@param hull Array<Vec2>
---@param origin Vec2
---@return Array<Vec2>
function UTIL.GetVisibleHullPointsFromOrigin(hull, origin)
local function segmentsIntersect(a, b, c, d)
-- Helper: returns true if segment ab intersects cd (excluding endpoints)
local function ccw(p1, p2, p3)
return (p3.y - p1.y) * (p2.x - p1.x) > (p2.y - p1.y) * (p3.x - p1.x)
end
return (ccw(a, c, d) ~= ccw(b, c, d)) and (ccw(a, b, c) ~= ccw(a, b, d))
end
local n = #hull
local visible = {}
for i = 1, n do
local p = hull[i]
local isVisible = true
-- Check against all hull edges except those incident to p
for j = 1, n do
local a = hull[j]
local b = hull[(j % n) + 1]
-- skip edges incident to p
if (a ~= p and b ~= p) then
if segmentsIntersect(origin, p, a, b) then
isVisible = false
break
end
end
end
if isVisible then
table.insert(visible, p)
end
end
return visible
end
---Returns the two tangent points ("scratch points") from origin to the convex hull
---@param hull Array<Vec2>
---@param origin Vec2
---@return Array<Vec2>
function UTIL.GetTangentHullPointsFromOrigin(hull, origin)
if hull == nil or #hull <= 0 then
return {}
end
local function orientation(a, b, c)
-- Returns >0 if c is to the left of ab, <0 if to the right, 0 if colinear
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
end
local n = #hull
if n == 0 then return {} end
if n == 1 then return { hull[1] } end
-- Find left tangent: the hull point where all other points are to the right of the line from origin to that point
local function findTangent(isLeft)
local best = 1
for i = 2, n do
local o = orientation(origin, hull[best], hull[i])
if (isLeft and o < 0) or (not isLeft and o > 0) then
best = i
end
end
return hull[best]
end
local leftTangent = findTangent(true)
local rightTangent = findTangent(false)
-- If tangents are the same (can happen if origin is colinear), only return one
if leftTangent.x == rightTangent.x and leftTangent.y == rightTangent.y then
return { leftTangent }
else
return { leftTangent, rightTangent }
end
end
end
return UTIL
+96
View File
@@ -0,0 +1,96 @@
--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")
local CapConfig = require("classes.configuration.CapConfig")
local StageConfig = require("classes.configuration.StageConfig")
local Persistence = require("classes.persistence.Persistence")
local SpawnManager = require("classes.helpers.SpawnManager")
local DetectionManager = require("classes.capClasses.detection.DetectionManager")
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
local GlobalStageManager = require("classes.stageClasses.GlobalStageManager")
local GlobalFleetManager = require("classes.fleetClasses.GlobalFleetManager")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local defaultLogLevel = "INFO"
if SpearheadConfig and SpearheadConfig.debugEnabled == true then
defaultLogLevel = "DEBUG"
end
local startTime = timer.getTime() * 1000
SpearheadEvents.Init(defaultLogLevel)
local dbLogger = Logger.new("database", defaultLogLevel)
local standardLogger = Logger.new("", defaultLogLevel)
local databaseManager = Database.New(dbLogger)
MissionCommandsHelper.getOrCreate(defaultLogLevel) -- initiate
local capConfig = CapConfig:new();
local stageConfig = StageConfig:new();
local startingStage = stageConfig.startingStage or 1
if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then
standardLogger:info("Persistence enabled")
local persistenceLogger = Logger.new("Persistence", defaultLogLevel)
Persistence.Init(persistenceLogger)
local persistanceStage = Persistence.GetActiveStage()
if persistanceStage then
standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage)
startingStage = persistanceStage
end
else
standardLogger:info("Persistence disabled")
end
local spawnLogger = Logger.new("SpawnManager", defaultLogLevel)
local spawnManager = SpawnManager.new(spawnLogger)
local detectionLogger = Logger.new("DetectionManager", defaultLogLevel)
local detectionManager = DetectionManager.New(detectionLogger)
GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
GlobalFleetManager.start(databaseManager)
local SetStageDelayed = function(number, time)
SpearheadEvents.PublishStageNumberChanged(number)
return nil
end
timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3)
env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms")
local duration = (timer.getTime() * 1000) - startTime
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
local missionEditorWarningsLogger = Logger.new("MissionEditorWarnings", defaultLogLevel)
MissionEditorWarnings.WriteAll(missionEditorWarningsLogger)
GlobalStageManager:printFullOverview()
--Check lines of code in directory per file:
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
-- find . -name '*.lua' | xargs wc -l
--- ==================== DEBUG ORDER OR ZONE VEC ===========================
-- local zone = Spearhead.DcsUtil.getZoneByName("MISSIONSTAGE_99")
-- local count = Spearhead.Util.tableLength(zone.verts)
-- for i = 1, count - 1 do
-- local a = zone.verts[i]
-- local b = zone.verts[i+1]
-- local color = {0,0,0,1}
-- color[i] = 1
-- trigger.action.textToAll(-1, 46+i , { x= a.x, y = 0, z = a.z } , color, {0,0,0}, 24 , true , "" .. i )
-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true)
-- end