Compare commits
29
Commits
v0.13.0
...
52f840c270
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52f840c270 | ||
|
|
d14a0dc78e | ||
|
|
a1355ff05d | ||
|
|
2e636b2ded | ||
|
|
488a492e39 | ||
|
|
772397250f | ||
|
|
0a7e8ea977 | ||
|
|
96507f1d34 | ||
|
|
96ada242a4 | ||
|
|
2bce1985e2 | ||
|
|
5601091b8f | ||
|
|
36ff621759 | ||
|
|
18eff4b85a | ||
|
|
952daa2897 | ||
|
|
7c0c5e4a85 | ||
|
|
970f049440 | ||
|
|
63a1ea44db | ||
|
|
c8329108dd | ||
|
|
a88520a413 | ||
|
|
d8764c6824 | ||
|
|
d0e7f2aae4 | ||
|
|
8c75ba5027 | ||
|
|
52f97f7c92 | ||
|
|
e5abfe19d6 | ||
|
|
b34918ff28 | ||
|
|
ab910b8faf | ||
|
|
f677a83c8e | ||
|
|
a1e36aa5d7 | ||
|
|
bb9144e3a0 |
Binary file not shown.
|
After Width: | Height: | Size: 223 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 345 KiB |
@@ -21,6 +21,9 @@
|
|||||||
<main>
|
<main>
|
||||||
<div class="reference-container">
|
<div class="reference-container">
|
||||||
<div class="side-nav">
|
<div class="side-nav">
|
||||||
|
|
||||||
|
<div id="beta-box-div"></div>
|
||||||
|
|
||||||
<h4>Quick Links</h4>
|
<h4>Quick Links</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/pages/tutorials.html" class="side-nav-h2">Tutorials</a></li>
|
<li><a href="/pages/tutorials.html" class="side-nav-h2">Tutorials</a></li>
|
||||||
@@ -59,6 +62,13 @@
|
|||||||
<footer>
|
<footer>
|
||||||
<p>© 2025 Spearhead Project</p>
|
<p>© 2025 Spearhead Project</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if (window.location.hostname.toLowerCase() == 'beta.spearhead.rocks') {
|
||||||
|
betaHtml = '<note-box type="warning" title="Beta Version">You are looking at docs that are released with the beta version.</note-box>';
|
||||||
|
}
|
||||||
|
document.getElementById('beta-box-div').innerHTML = betaHtml || '';
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -19,9 +19,10 @@ class Header extends HTMLElement {
|
|||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="/pages/include-script.html">Include the Script</a>
|
<a href="/pages/include-script.html">Include the Script</a>
|
||||||
<a href="/pages/first-start.html">First Start</a>
|
<a href="/pages/first-start.html">First Start</a>
|
||||||
|
<a href="/pages/advanced/map-markings.html">Map Markings</a>
|
||||||
<a href="/pages/advanced/CAP.html">Advanced: CAP</a>
|
<a href="/pages/advanced/CAP.html">Advanced: CAP</a>
|
||||||
<a href="/pages/advanced/missions.html">Advanced: Missions</a>
|
<a href="/pages/advanced/missions.html">Advanced: Missions</a>
|
||||||
<a href="/pages/advanced/map-markings.html">Map Markings</a>
|
<a href="/pages/advanced/lanes.html">Advanced: Lanes</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -49,17 +49,6 @@ class DownloadScript extends HTMLElement {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchReleaseAssets(url){
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
const data = await response.json();
|
|
||||||
return data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching release assets:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
|
|
||||||
@@ -72,10 +61,9 @@ class DownloadScript extends HTMLElement {
|
|||||||
if (latestRelease) {
|
if (latestRelease) {
|
||||||
version = latestRelease.tag_name;
|
version = latestRelease.tag_name;
|
||||||
timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA');
|
timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA');
|
||||||
|
const assets = latestRelease.assets;
|
||||||
const assets = await this.fetchReleaseAssets(latestRelease.assets_url);
|
|
||||||
if (assets && assets.length > 0) {
|
if (assets && assets.length > 0) {
|
||||||
const spearheadAsset = assets.find(asset => asset.name.includes('spearhead'));
|
const spearheadAsset = assets.find(asset => asset.name.includes('spearhead.v'));
|
||||||
if (spearheadAsset) {
|
if (spearheadAsset) {
|
||||||
size = (spearheadAsset.size / 1024).toFixed(2) + 'kb';
|
size = (spearheadAsset.size / 1024).toFixed(2) + 'kb';
|
||||||
href = spearheadAsset.browser_download_url;
|
href = spearheadAsset.browser_download_url;
|
||||||
@@ -105,7 +93,7 @@ class DownloadScript extends HTMLElement {
|
|||||||
<p>
|
<p>
|
||||||
<span class="version-text">${version}</span> (${timestamp}) <br>
|
<span class="version-text">${version}</span> (${timestamp}) <br>
|
||||||
<a style="text-decoration: underline;" target="_blank" href="${href}">Download</a> (${size}) <br>
|
<a style="text-decoration: underline;" target="_blank" href="${href}">Download</a> (${size}) <br>
|
||||||
<a style="text-decoration: underline;" target="_blank" href="https://git.dutchie031.com/api/v1/repos/Spearhead/spearhead/releases">See All Versions here</a>
|
<a style="text-decoration: underline;" target="_blank" href="https://git.dutchie031.com/Spearhead/spearhead/releases">See All Versions here</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -168,8 +168,17 @@ class Sidebar extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
var betaHtml = '';
|
||||||
|
if (window.location.hostname.toLowerCase() == 'beta.spearhead.rocks') {
|
||||||
|
betaHtml = '<note-box type="warning" title="Beta Version">You are looking at docs that are released with the beta version.</note-box>';
|
||||||
|
}
|
||||||
|
|
||||||
this.innerHTML = `
|
this.innerHTML = `
|
||||||
<div class="side-nav">
|
<div class="side-nav">
|
||||||
|
<div style="width:100%">
|
||||||
|
${betaHtml}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h4 class="side-nav-title"></h4>
|
<h4 class="side-nav-title"></h4>
|
||||||
<ul>
|
<ul>
|
||||||
<!-- Navigation items will be populated automatically -->
|
<!-- Navigation items will be populated automatically -->
|
||||||
|
|||||||
@@ -80,17 +80,12 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<note-box type="info" title="Combining Tasks">
|
<note-box type="info" title="Combining Tasks">
|
||||||
Currently a group can do 1 thing. Either be Primary, Seconday, Sweep or Intercept. <br>
|
Currently a group can do 1 thing. Either be Primary, Seconday, Sweep or Intercept. <br>
|
||||||
If you want to have different types during different stages, it's best to copy paste groups and
|
If you want to have different types during different stages, it's best to copy paste groups and
|
||||||
rename them accordingly. <br>
|
rename them accordingly. <br>
|
||||||
</note-box>
|
</note-box>
|
||||||
|
|
||||||
<note-box type="info" title="Airfield Defenses">
|
|
||||||
|
|
||||||
</note-box>
|
|
||||||
|
|
||||||
<h3 id="cap-zones-ref">Zones</h3>
|
<h3 id="cap-zones-ref">Zones</h3>
|
||||||
<table class="table-no-style">
|
<table class="table-no-style">
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -99,6 +94,7 @@
|
|||||||
<td>=></td>
|
<td>=></td>
|
||||||
<td>A Cap ROUTE. The two most outer points are used to define the fly-over points.</td>
|
<td>A Cap ROUTE. The two most outer points are used to define the fly-over points.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr><td>______________________</td><td></td><td></td> </tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code-inline>INTERCEPTZONE_[ZoneID]_[NAME]</code-inline></td>
|
<td><code-inline>INTERCEPTZONE_[ZoneID]_[NAME]</code-inline></td>
|
||||||
<td>=></td>
|
<td>=></td>
|
||||||
@@ -112,6 +108,19 @@
|
|||||||
</p>
|
</p>
|
||||||
<h2 id="setup">Setting up groups</h2>
|
<h2 id="setup">Setting up groups</h2>
|
||||||
|
|
||||||
|
<h3 id="airbases-grouping">Airbases</h3>
|
||||||
|
<p>
|
||||||
|
Before talking about the groups, it's good to mention that groups are currently logically grouped and managed by an Airbase. <br>
|
||||||
|
Meaning, Airbase A does not know how many flights Airbase B is sending out. <br>
|
||||||
|
This might change in the future where multiple Airbases can work together.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<note-box type="info" title="Airbase Defenses">
|
||||||
|
When an Airbase is "active" and thus has aircraft being scheduled for taskings, all RED groups at that airfield will also be spawned. <br>
|
||||||
|
Additionally, all missions of type <code-inline>SAM</code-inline> will be spawned (without being available in Primary Missions). <br>
|
||||||
|
This should hopefully give you all the tools to make distance CAP airbases still feel very well defended.
|
||||||
|
</note-box>
|
||||||
|
|
||||||
<h3 id="types-explained">Types explained</h3>
|
<h3 id="types-explained">Types explained</h3>
|
||||||
<p>
|
<p>
|
||||||
Before we go into depth on each type it's best to first look at the types briefly and what we
|
Before we go into depth on each type it's best to first look at the types briefly and what we
|
||||||
@@ -140,7 +149,7 @@
|
|||||||
The ground attack units mentioned are not in yet and thus escort groups are not either. <br>
|
The ground attack units mentioned are not in yet and thus escort groups are not either. <br>
|
||||||
|
|
||||||
<note-box type="warning" title="Overriding Taskings">
|
<note-box type="warning" title="Overriding Taskings">
|
||||||
Each tasking has very specific waypoints, both for departure, arrical and ofcourse all point in between. <br>
|
Each tasking has very specific waypoints, both for departure, arrival and ofcourse all point in between. <br>
|
||||||
Overriding the "tasking" will break part of the cap flow as "RTBInTen" and RTB events are triggered by waypoints. <br>
|
Overriding the "tasking" will break part of the cap flow as "RTBInTen" and RTB events are triggered by waypoints. <br>
|
||||||
Overriding these waypoints and taskings will break the management by Spearhead.
|
Overriding these waypoints and taskings will break the management by Spearhead.
|
||||||
</note-box>
|
</note-box>
|
||||||
@@ -154,7 +163,7 @@
|
|||||||
The way the trigger zone is mapped to a route is as follows: <br>
|
The way the trigger zone is mapped to a route is as follows: <br>
|
||||||
<br>
|
<br>
|
||||||
The 2 points that are furthest apart from each other are chosen as the "leg" of the route. <br>
|
The 2 points that are furthest apart from each other are chosen as the "leg" of the route. <br>
|
||||||
The other 2 points are discarded. <br>
|
The other 2 points are discarded at this time. <br>
|
||||||
<br>
|
<br>
|
||||||
The Orbit route is then created with an "Anchored" task. With the closest point being the starting
|
The Orbit route is then created with an "Anchored" task. With the closest point being the starting
|
||||||
point with an outbound leg to the furthest point. <br>
|
point with an outbound leg to the furthest point. <br>
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
<!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 Lanes</title>
|
||||||
|
<link rel="stylesheet" href="/style/style.css">
|
||||||
|
<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>Lanes</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If up to now you've used mission stages like <code-inline>MISSIONSTAGE_1_STAGE</code-inline> you've been using the main lane. <br>
|
||||||
|
This is the default or "main" lane, however, if you wanted to mix things up and create a more dynamic mission, you can use additional lanes. <br>
|
||||||
|
Each lane can have its own set of mission stages, allowing for parallel or branching mission objectives.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="how-it-works">How To</h2>
|
||||||
|
<h3 id="define-lanes">Define Lanes</h3>
|
||||||
|
<p>
|
||||||
|
Lanes are defined implicitly in the naming convention of the <code-inline>MISSIONSTAGE_</code-inline>. <br>
|
||||||
|
A lane is identified by a single character (alpha [A-Z]), which gives you 26 unique lanes. <br>
|
||||||
|
For example, <code-inline>MISSIONSTAGE_A1_[name]</code-inline> would belong to lane A. <br>
|
||||||
|
<br>
|
||||||
|
If there is no lane character specified, the stage belongs to the main lane. <br>
|
||||||
|
<br>
|
||||||
|
The 26 lane limit is only relevant if you want 26 lanes at the same time. <br>
|
||||||
|
But splitting up lanes (eg. (1-4 and 10-15) ) you can easily have more than 26 stages throughout your entire mission. <br>
|
||||||
|
<br>
|
||||||
|
Lanes don't have to start at index 1. as in the example lanes can easily start at any index. <br>
|
||||||
|
How lanes are then activated and activate other lanes is described below in the Dependencies Explained part.
|
||||||
|
</p>
|
||||||
|
<h3 id="dependencies">Dependencies Explained</h3>
|
||||||
|
<p>
|
||||||
|
When we are talking about lanes it's important to make the distinction between the main lane and additional lanes. <br>
|
||||||
|
The main lane is not only the default, but it's also the central lane that other lanes depend on. <br>
|
||||||
|
|
||||||
|
<h4>Chapters</h4>
|
||||||
|
Another term that we have used in the code is `Chapter` which refers to a grouping of mission stages within a lane. <br>
|
||||||
|
These are implied and cannot be directly controlled or altered. <br>
|
||||||
|
A chapter is a set of `MissionStage`s that are directly connected. <br>
|
||||||
|
For <code-inline>MissionStages</code-inline> <code-inline>[ A1, A2, A3, A5, A6 ]</code-inline> there are 2 chapters: <code-inline>[ A1, A2, A3 ]</code-inline> and <code-inline>[ A5, A6 ]</code-inline>. <br>
|
||||||
|
Within a chapter the stages are only directly dependent on each other. Meaning A2 will start when A1 is completed. <br>
|
||||||
|
<br>
|
||||||
|
However, the chapter start is where the inter-lane dependencies come into play. <br>
|
||||||
|
These operate a little differently. <br>
|
||||||
|
<br>
|
||||||
|
In the Visual example you can see the Stages grouped into chapters by the red boxes. <br>
|
||||||
|
|
||||||
|
<h4>Side Lane Chapter start</h4>
|
||||||
|
<p>
|
||||||
|
Side lane chapters are started when all are true:
|
||||||
|
<ul>
|
||||||
|
<li>The main lane is at or above the current stage lane number.</li>
|
||||||
|
<li>All previous chapters in the same side lane have been completed.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Main Lane Chapter start</h4>
|
||||||
|
<p>
|
||||||
|
Main lane chapters are started when all are true:
|
||||||
|
<ul>
|
||||||
|
<li>The previous chapter in the main lane has been completed.</li>
|
||||||
|
<li>All side lanes are at or above the current stage lane number or the side lane has no additional stages.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="visual">Visual</h2>
|
||||||
|
<p>
|
||||||
|
The image below illustrates how different lanes can be used within a mission. <br>
|
||||||
|
In the example you can see how the dependency between the main lane and side lanes works. <br>
|
||||||
|
An arrow means that the completion of one stage is required before the next stage can begin.
|
||||||
|
</p>
|
||||||
|
<img src="/img/lanes.png" alt="Example of mission lanes">
|
||||||
|
|
||||||
|
<h2 id="mermaid">Mermaid</h2>
|
||||||
|
<p>
|
||||||
|
Spearhead automatically exports a spearhead diagram to the logs on starting a mission. <br>
|
||||||
|
This can be handy to visualize the logic and lanes you've created and verify your mission flow will be the way you want it. <br>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="mermaid-log-example">Log Example</h3>
|
||||||
|
<code-block>
|
||||||
|
2026-09-01 17:53:13.213 INFO SCRIPTING (Main): [Spearhead][StageManager] ========== STAGE FLOW DIAGRAM ==========
|
||||||
|
graph TD
|
||||||
|
default_8["MAIN-2 (S8)"]
|
||||||
|
b_5["West Valley (S5)"]
|
||||||
|
default_1["MAIN-1 (S1)"]
|
||||||
|
a_5["WEST-2 (S5)"]
|
||||||
|
default_4["MAIN-2 (S4)"]
|
||||||
|
default_10["MAIN-1 (S10)"]
|
||||||
|
default_6["MAIN-2 (S6)"]
|
||||||
|
a_4["WEST-1 (S4)"]
|
||||||
|
default_3["MAIN-1 (S3)"]
|
||||||
|
default_9["MAIN-3 (S9)"]
|
||||||
|
a_7["WEST-2 (S7)"]
|
||||||
|
a_3["WEST (S3)"]
|
||||||
|
default_5["MAIN-3 (S5)"]
|
||||||
|
b_6["West Valley-1 (S6)"]
|
||||||
|
default_61["CAP-Maykop (S61)"]
|
||||||
|
default_7["MAIN-1 (S7)"]
|
||||||
|
default_0["START (S0)"]
|
||||||
|
a_6["WEST-3 (S6)"]
|
||||||
|
default_60["CAP-Nalchik (S60)"]
|
||||||
|
default_2["MAIN (S2)"]
|
||||||
|
a_2["WEST-1 (S2)"]
|
||||||
|
a_2 --> a_3
|
||||||
|
a_3 --> a_4
|
||||||
|
a_4 --> a_5
|
||||||
|
a_5 --> a_6
|
||||||
|
a_6 --> a_7
|
||||||
|
default_0 --> default_1
|
||||||
|
default_1 --> default_2
|
||||||
|
default_2 --> default_3
|
||||||
|
default_3 --> default_4
|
||||||
|
default_4 --> default_5
|
||||||
|
default_5 --> default_6
|
||||||
|
default_6 --> default_7
|
||||||
|
default_7 --> default_8
|
||||||
|
default_8 --> default_9
|
||||||
|
default_9 --> default_10
|
||||||
|
default_10 -->|chapter| default_60
|
||||||
|
default_60 --> default_61
|
||||||
|
b_5 --> b_6
|
||||||
|
default_2 -->|unlock| a_2
|
||||||
|
default_5 -->|unlock| b_5
|
||||||
|
a_7 -->|gate| default_60
|
||||||
|
b_6 -->|gate| default_60
|
||||||
|
========== END DIAGRAM ==========
|
||||||
|
|
||||||
|
</code-block>
|
||||||
|
|
||||||
|
<h3 id="mermaid-diagram-example">Diagram Example</h3>
|
||||||
|
<p>
|
||||||
|
If you want to then copy and paste everything between the <br>
|
||||||
|
<code-inline>========== STAGE FLOW DIAGRAM ==========</code-inline> and <br>
|
||||||
|
<code-inline>========== END DIAGRAM ==========</code-inline> <br>
|
||||||
|
you will be able to go to <a href="https://mermaid.live/">https://mermaid.live/</a> and paste the diagram code into the editor. <br>
|
||||||
|
|
||||||
|
It would look like this:
|
||||||
|
</p>
|
||||||
|
<img src="/img/mermaid-preview.png" alt="Example of mermaid diagram">
|
||||||
|
|
||||||
|
<h2 id="lane-limitations">Limitations</h2>
|
||||||
|
<h3 class="lane-limitations-cap">CAP</h3>
|
||||||
|
<p>
|
||||||
|
Due to the CAP naming conventions, we've opted to currently have CAP only react with the main lane. <br>
|
||||||
|
This might mean a bit less flexibility when using lanes for major mission logic, however, it's a limitation we've accepted for now. <br>
|
||||||
|
In the future we might be able to extend CAP's interaction with other lanes as well. <br>
|
||||||
|
Currently <a href="https://git.dutchie031.com/Spearhead/spearhead/issues/47">this issue</a> can be tracked here. <br>
|
||||||
|
|
||||||
|
This does not mean having both CAP and side-lanes is out of the question, since you can still have that side-lanes scouted by CAP aircraft.
|
||||||
|
It just needs a bit more pre-planning on where and how those CAP flights fly. <br>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Spearhead Project</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -26,42 +26,118 @@
|
|||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<h1>Advanced Tutorial: Missions</h1>
|
<h1>Advanced Tutorial: Missions</h1>
|
||||||
<h2 id="strike">Strike</h2>
|
<div id="strike-region">
|
||||||
<p>
|
<h2 id="strike">Strike</h2>
|
||||||
Naming: <span class="inline-lua"><span class="lua-variable">MISSION_STRIKE_[Name]</span></span> <br>
|
<p>
|
||||||
</p>
|
Naming: <code-inline>MISSION_STRIKE_[Name]</code-inline> <br>
|
||||||
<h3 id="specific_targets">Specific Targets</h3>
|
</p>
|
||||||
<p>TBD</p>
|
<h3 id="specific_targets">Specific Targets</h3>
|
||||||
|
<p>TBD</p>
|
||||||
|
|
||||||
|
<h3 id="scenery_targets">Scenery Targets</h3>
|
||||||
|
<p>
|
||||||
|
Bridges, buildings or other type of targets that are baked into the map are awesome targets.<br>
|
||||||
|
We wanted to make sure they were easily integrated in Spearhead.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 id="scenery_targets">Scenery Targets</h3>
|
<note-box type="info" title="Implementation details">
|
||||||
<p>
|
Spearhead detects scenery targets by the trigger zone name. A zone named
|
||||||
Bridges, buildings or other type of targets that are baked into the map are awesome targets.<br>
|
<strong>scenerytarget_[freeform]</strong> or
|
||||||
We wanted to make sure they were easily integrated in Spearhead.
|
<strong>scenerytargets</strong> (case-insensitive) will be scanned and any scenery objects that
|
||||||
</p>
|
has the attribute "Buildings" inside the zone
|
||||||
|
will be added as mission scenery targets.
|
||||||
<note-box type="info" title="Implementation details">
|
|
||||||
Spearhead detects scenery targets by the trigger zone name. A zone named <strong>scenerytarget_[freeform]</strong> or
|
|
||||||
<strong>scenerytargets</strong> (case-insensitive) will be scanned and any scenery objects that has the attribute "Buildings" inside the zone
|
|
||||||
will be added as mission scenery targets.
|
|
||||||
</note-box>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Scenery targets are treated like mission targets: they are included in the mission completion calculation
|
|
||||||
(their `IsAlive()` state is checked) and their state is persisted/updated when missions spawn or resume.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<note-box type="warning" title="Changing IDs">
|
|
||||||
Due to Object IDs changing when maps are updated and edited the "Assign as..." functionality does not work for scenery targets.
|
|
||||||
Hence the need to use trigger zones to define them.
|
|
||||||
</note-box>
|
</note-box>
|
||||||
|
|
||||||
|
<p>
|
||||||
<p>Here's two images showing how a bridge is added to a mission.</p>
|
Scenery targets are treated like mission targets: they are included in the mission completion
|
||||||
<img src="/img/bridge.png" style="max-height: 35remrem;"/>
|
calculation
|
||||||
|
(their `IsAlive()` state is checked) and their state is persisted/updated when missions spawn or
|
||||||
|
resume.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<note-box type="warning" title="Changing IDs">
|
||||||
|
Due to Object IDs changing when maps are updated and edited the "Assign as..." functionality
|
||||||
|
does not work for scenery targets.
|
||||||
|
Hence the need to use trigger zones to define them.
|
||||||
|
</note-box>
|
||||||
|
|
||||||
|
|
||||||
|
<p>Here's two images showing how a bridge is added to a mission.</p>
|
||||||
|
<img src="/img/bridge.png" style="max-height: 35remrem;" />
|
||||||
|
</div>
|
||||||
|
<div id="cas-region">
|
||||||
|
<h2 id="cas">CAS</h2>
|
||||||
|
<p>
|
||||||
|
Naming: <code-inline>MISSION_CAS_[Name]</code-inline> <br>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="BattleManager">Battle Manager</h3>
|
||||||
|
<p>TBD</p>
|
||||||
|
</div>
|
||||||
|
<div id="dead-region">
|
||||||
|
<h2 id="dead">DEAD</h2>
|
||||||
|
<p>
|
||||||
|
Naming: <code-inline>MISSION_DEAD_[Name]</code-inline> <br>
|
||||||
|
Naming: <code-inline>MISSION_SAM_[Name]</code-inline> <br>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="sam-vs-dead">SAM vs DEAD</h3>
|
||||||
|
<p>TBD</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bai-region">
|
||||||
|
<h2 id="bai">BAI</h2>
|
||||||
|
<p>
|
||||||
|
Naming: <code-inline>MISSION_BAI_[Name]</code-inline> <br>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="deepstrike-region">
|
||||||
|
<h2 id="deepstrike">DEEPSTRIKE</h2>
|
||||||
|
<p>
|
||||||
|
Naming: <code-inline>MISSION_DEEPSTRIKE_[Name]</code-inline> <br>
|
||||||
|
<br>
|
||||||
|
When you, as a mission editor, want to create a mission that's further away from the current front-line you can use the Deep Strike mission type. <br>
|
||||||
|
This allows you to plan missions that require more strategic positioning and timing. <br>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="activation">Activation</h3>
|
||||||
|
<p>
|
||||||
|
Deep Strike missions are activated when the Stage in which they are located becomes "PreActive" or "Active". <br>
|
||||||
|
"PreActive" is when either the current active StageNumber is a certain number of stages away OR the stage has "CAP" airbases active . <br>
|
||||||
|
<code-inline>SpearheadConfig.StageConfig.preactivateStage</code-inline> changes the configuration. <br>
|
||||||
|
<br>
|
||||||
|
For example: <br>
|
||||||
|
If the current active stage is <code-inline>MissionStage_2_[name]</code-inline> and preActivate config is set to 2. <br>
|
||||||
|
Then both <code-inline>MissionStage_3_[name]</code-inline> and <code-inline>MissionStage_4_[name]</code-inline> would become "PreActive".
|
||||||
|
</p>
|
||||||
|
<h3 id="primary-vs-secondary">Make Primary</h3>
|
||||||
|
<p>
|
||||||
|
By default DEEPSTRIKE missions are "Secondary" missions when spawned as a pre-activated stage.<br>
|
||||||
|
Then when the stage they are in is "Active", the mission becomes a "Primary" mission. <br>
|
||||||
|
<br>
|
||||||
|
If you want to make it a "Primary" mission from the start, you can overwrite it.<br>
|
||||||
|
Click the mission's trigger zone. Add a property named <code-inline>primary</code-inline> and set it to <code-inline>true</code-inline>.
|
||||||
|
This will make the mission "Primary" from the start. <br>
|
||||||
|
<br>
|
||||||
|
If you want it to "Secondary" even when its stage is the active stage, you can set "primary" to false. <br>
|
||||||
|
<br>
|
||||||
|
If you want to have it be "Secondary" when its stage is "Pre-Active" and "primary" when its stage is "Active" you don't have to do anything.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="future-additions">Future</h3>
|
||||||
|
<p>
|
||||||
|
In the future we're thinking it would be cool to make the Deep Strike missions
|
||||||
|
be more integrated with the CAP airbases and/or logistics. <br>
|
||||||
|
Eg. Completing a DEEPSTRIKE mission would disable CAP for X amount of minutes after completion. <br>
|
||||||
|
This still needs to be refined, but we've already have ideas.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<p>© 2025 Spearhead Project</p>
|
<p>© 2025 Spearhead Project</p>
|
||||||
|
|||||||
@@ -253,6 +253,13 @@
|
|||||||
To make an airbase buildable add a text box inside of the trigger zone and name it: <code-inline>buildable_[freeform]</code-inline> <br/>
|
To make an airbase buildable add a text box inside of the trigger zone and name it: <code-inline>buildable_[freeform]</code-inline> <br/>
|
||||||
Then in the text box type amount of kilo's you want to be transfered before the logistisc mission is complete. <br/>
|
Then in the text box type amount of kilo's you want to be transfered before the logistisc mission is complete. <br/>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
To add a custom briefing for a buildable zone, include a text box inside the trigger zone and name it: <code-inline>supplybriefing_[freeform]</code-inline>.
|
||||||
|
<code-inline>{{coords}}</code-inline> is highly recommended so the players can easily find their landing zone.<br>
|
||||||
|
Additionally, the amount of kilos required and delivered are always shown on the bottom, as well as a note to not land in the construction zone.
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
The base or zone will slowly build up with each crate giving both a nice view of it happening AND it's good for performance as there's not a big addition of objects at once. <br/>
|
The base or zone will slowly build up with each crate giving both a nice view of it happening AND it's good for performance as there's not a big addition of objects at once. <br/>
|
||||||
Right now a crate takes 15 seconds to unpack per 500kg. Meaning 2 crates of 1000kg will be faster than 1 crate of 2000kg. <br/>
|
Right now a crate takes 15 seconds to unpack per 500kg. Meaning 2 crates of 1000kg will be faster than 1 crate of 2000kg. <br/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
--color-bg-hover: rgba(120, 140, 255, 0.12); /* More visible hover */
|
--color-bg-hover: rgba(120, 140, 255, 0.12); /* More visible hover */
|
||||||
--color-bg-note-dark: rgba(40, 35, 30, 0.85); /* Slightly darker note box */
|
--color-bg-note-dark: rgba(40, 35, 30, 0.85); /* Slightly darker note box */
|
||||||
|
|
||||||
--color-text-primary: #a5a5a5; /* Brighter text */
|
--color-text-primary: #b4b3b3; /* Brighter text */
|
||||||
--color-text-secondary: #bfc8d8; /* Lighter secondary text */
|
--color-text-secondary: #bfc8d8; /* Lighter secondary text */
|
||||||
--color-text-accent: #7ecbff; /* Brighter accent */
|
--color-text-accent: #7ecbff; /* Brighter accent */
|
||||||
--color-text-light: #ffffff;
|
--color-text-light: #ffffff;
|
||||||
@@ -262,7 +262,7 @@ header nav a:hover::after {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
top: 100%; /* Position below the dropdown */
|
top: 100%; /* Position below the dropdown */
|
||||||
left: 0; /* Align left edge */
|
left: 0; /* Align left edge */
|
||||||
min-width: 160px;
|
min-width: 240px;
|
||||||
|
|
||||||
background-color: var(--color-bg-secondary);
|
background-color: var(--color-bg-secondary);
|
||||||
box-shadow: var(--shadow-medium);
|
box-shadow: var(--shadow-medium);
|
||||||
@@ -336,8 +336,8 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.side-nav {
|
.side-nav {
|
||||||
width: 200px;
|
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
|
max-width: 200px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- '.docs/**'
|
- '.docs/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DOCKER_REGISTRY: registry.dutchie031.net
|
DOCKER_REGISTRY: registry.dutchie031.net
|
||||||
DOCKER_IMAGE: spearhead-docs
|
DOCKER_IMAGE: spearhead-docs
|
||||||
DOCKER_TAG: latest
|
DOCKER_TAG: ${{ github.ref_name == 'main' && 'latest' || 'beta' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
## Run replacements and cleanups on the docs
|
## Run replacements and cleanups on the docs
|
||||||
@@ -36,7 +37,7 @@ jobs:
|
|||||||
sed -i '/@@API_CODE@@/d' .docs/web/pages/spearheadapi.html
|
sed -i '/@@API_CODE@@/d' .docs/web/pages/spearheadapi.html
|
||||||
rm .docs/web/pages/temp_api_code.html
|
rm .docs/web/pages/temp_api_code.html
|
||||||
|
|
||||||
- name: Highlight API code and update HTML
|
- name: Highlight Config code and update HTML
|
||||||
run: |
|
run: |
|
||||||
pygmentize -f html -l lua -O noclasses,style=monokai ./config.lua > .docs/web/pages/temp_config_code.html
|
pygmentize -f html -l lua -O noclasses,style=monokai ./config.lua > .docs/web/pages/temp_config_code.html
|
||||||
# Insert the highlighted code into the placeholder in spearheadapi.html
|
# Insert the highlighted code into the placeholder in spearheadapi.html
|
||||||
@@ -105,15 +106,30 @@ jobs:
|
|||||||
chmod 600 $HOME/.kube/config
|
chmod 600 $HOME/.kube/config
|
||||||
cat $HOME/.kube/config
|
cat $HOME/.kube/config
|
||||||
|
|
||||||
- name: Run Helm upgrade/install
|
- name: Run Helm upgrade/install spearhead-docs (main)
|
||||||
|
if: github.ref_name == 'main'
|
||||||
run: |
|
run: |
|
||||||
helm upgrade --install spearhead-docs .helm \
|
helm upgrade --install spearhead-docs .helm \
|
||||||
--namespace spearhead-docs \
|
--namespace spearhead-docs \
|
||||||
--create-namespace \
|
--create-namespace \
|
||||||
|
-f .helm/values.yaml \
|
||||||
--set image.repository=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }} \
|
--set image.repository=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }} \
|
||||||
--set image.tag=${{ env.DOCKER_TAG }} \
|
--set image.tag=${{ env.DOCKER_TAG }} \
|
||||||
--wait
|
--wait
|
||||||
|
|
||||||
|
- name: Run Helm upgrade/install spearhead-docs (develop)
|
||||||
|
if: github.ref_name == 'develop'
|
||||||
|
run: |
|
||||||
|
helm upgrade --install spearhead-docs-beta .helm \
|
||||||
|
--namespace spearhead-docs \
|
||||||
|
--create-namespace \
|
||||||
|
-f .helm/values.yaml \
|
||||||
|
-f .helm/values.beta.yaml \
|
||||||
|
--set image.repository=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }} \
|
||||||
|
--set image.tag=${{ env.DOCKER_TAG }} \
|
||||||
|
--wait
|
||||||
|
|
||||||
|
|
||||||
# - name: 'Deploy'
|
# - name: 'Deploy'
|
||||||
# uses: deliverybot/helm@v1
|
# uses: deliverybot/helm@v1
|
||||||
# with:
|
# with:
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ sources:
|
|||||||
- https://git.dutchie031.com/Spearhead/Spearhead
|
- https://git.dutchie031.com/Spearhead/Spearhead
|
||||||
maintainers:
|
maintainers:
|
||||||
- name: Spearhead Team
|
- name: Spearhead Team
|
||||||
|
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ spec:
|
|||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Values.app }}
|
- name: {{ .Values.app }}
|
||||||
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: {{ .Values.ports.containerPort }}
|
- containerPort: {{ .Values.ports.containerPort }}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Values.app }}-ingress
|
name: "{{ .Values.app }}-ingress"
|
||||||
namespace: spearhead-docs
|
namespace: spearhead-docs
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/tls-acme: "true"
|
kubernetes.io/tls-acme: "true"
|
||||||
@@ -22,28 +22,21 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
rules:
|
rules:
|
||||||
- host: spearhead.dutchie031.com
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- host: {{ . }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ .Values.app }}
|
name: {{ $.Values.app }}
|
||||||
port:
|
port:
|
||||||
number: {{ .Values.ports.servicePort }}
|
number: {{ $.Values.ports.servicePort }}
|
||||||
- host: spearhead.rocks
|
{{- end }}
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ .Values.app }}
|
|
||||||
port:
|
|
||||||
number: {{ .Values.ports.servicePort }}
|
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- spearhead.dutchie031.com
|
{{- range .Values.ingress.hosts }}
|
||||||
- spearhead.rocks
|
- {{ . }}
|
||||||
secretName: spearhead-docs-tls
|
{{- end }}
|
||||||
|
secretName: "{{ .Values.app }}-docs-tls"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
app: spearhead-docs-beta
|
||||||
|
|
||||||
|
image:
|
||||||
|
tag: "beta"
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
hosts:
|
||||||
|
- beta.spearhead.rocks
|
||||||
+15
-10
@@ -1,10 +1,15 @@
|
|||||||
|
|
||||||
app: spearhead-docs
|
app: spearhead-docs
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: registry.dutchie031.net/spearhead-docs
|
repository: registry.dutchie031.net/spearhead-docs
|
||||||
tag: latest
|
tag: latest
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
servicePort: 80
|
servicePort: 80
|
||||||
containerPort: 80
|
containerPort: 80
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
hosts:
|
||||||
|
- spearhead.dutchie031.com
|
||||||
|
- spearhead.rocks
|
||||||
+15
-1
@@ -5,11 +5,25 @@
|
|||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
|
- Issue: #41
|
||||||
|
Adds Stage Complete debug methods for local testing of the Stage flow.
|
||||||
|
#40
|
||||||
|
|
||||||
|
- Issue: #35
|
||||||
|
Creating Lane option for stages.
|
||||||
|
Additionally to the primary stage lane, stages can now be created in different lanes.
|
||||||
|
These lanes can be used to create different stage progression flows and create different "side-stories".
|
||||||
|
#40
|
||||||
|
|
||||||
|
- Issue: #37
|
||||||
|
Now enabled the mission editor to add a custom briefing to the "Buildable" missions.
|
||||||
|
#44
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
## [0.13.0] 2026-08
|
## [0.13.0] 2026-08
|
||||||
|
|
||||||
A good first release that finally has all major bugs fixed that were caused by the migration from both the underlying script transpiler and the migration to Gitea. <br>
|
A good first release that finally has all major bugs fixed that were caused by the migration from both the underlying script transpiler and the migration to Gitea.
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ SpearheadConfig = {
|
|||||||
---DEBUG LOGGING
|
---DEBUG LOGGING
|
||||||
debugEnabled = false, -- default false
|
debugEnabled = false, -- default false
|
||||||
|
|
||||||
|
--- DEBUG MENU
|
||||||
|
debugMenuEnabled = false, -- default false
|
||||||
|
|
||||||
--- The time briefings should be displayed by default.
|
--- The time briefings should be displayed by default.
|
||||||
--- Players can always "Clear Messages" through the F10 menu, so setting it to a high value can be
|
--- Players can always "Clear Messages" through the F10 menu, so setting it to a high value can be
|
||||||
briefingMessageDuration = 60, --default 60
|
briefingMessageDuration = 60, --default 60
|
||||||
@@ -73,6 +76,9 @@ SpearheadConfig = {
|
|||||||
--The location will continously update for the last killed unit.
|
--The location will continously update for the last killed unit.
|
||||||
markLastContact = false, -- default false
|
markLastContact = false, -- default false
|
||||||
|
|
||||||
|
--If enabled, the stage briefing including the current missions will be shown to players on spawn.
|
||||||
|
briefingOnSpawn = true, -- default true
|
||||||
|
|
||||||
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
|
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
|
||||||
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
|
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
|
||||||
--[[
|
--[[
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@ local MissionCompleteListeners = {}
|
|||||||
---@class SpearheadAPIInternal
|
---@class SpearheadAPIInternal
|
||||||
---@field notifyMissionComplete fun(zone_name: string)
|
---@field notifyMissionComplete fun(zone_name: string)
|
||||||
|
|
||||||
|
|
||||||
---@type FullSpearheadAPI
|
---@type FullSpearheadAPI
|
||||||
SpearheadAPI = {
|
SpearheadAPI = {
|
||||||
Stages = {
|
Stages = {
|
||||||
@@ -25,12 +24,12 @@ SpearheadAPI = {
|
|||||||
getCurrentStage = function()
|
getCurrentStage = function()
|
||||||
return GlobalStageManager.getCurrentStage() or nil
|
return GlobalStageManager.getCurrentStage() or nil
|
||||||
end,
|
end,
|
||||||
isStageComplete = function(stageNumber)
|
isStageComplete = function(stageNumber, stageLaneIdentifier)
|
||||||
if type(stageNumber) ~= "number" then
|
if type(stageNumber) ~= "number" then
|
||||||
return false, "stageNumber " .. stageNumber .. " is not a valid number"
|
return false, "stageNumber " .. stageNumber .. " is not a valid number"
|
||||||
end
|
end
|
||||||
|
|
||||||
local isComplete = GlobalStageManager.isStageComplete(stageNumber)
|
local isComplete = GlobalStageManager.isStageComplete(stageNumber, stageLaneIdentifier)
|
||||||
if isComplete == nil then
|
if isComplete == nil then
|
||||||
return nil, "no stage found with number " .. stageNumber
|
return nil, "no stage found with number " .. stageNumber
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ SpearheadAPI = SpearheadAPI
|
|||||||
---@class SpearheadStagesAPI
|
---@class SpearheadStagesAPI
|
||||||
---@field changeStage fun(stageNumber: number): boolean, string @Changes the active stage of spearhead. <br/> All other stages will change based on the normal logic. (CAP, BLUE etc.)
|
---@field changeStage fun(stageNumber: number): boolean, string @Changes the active stage of spearhead. <br/> All other stages will change based on the normal logic. (CAP, BLUE etc.)
|
||||||
---@field getCurrentStage fun(): number | nil @Returns the current stange number <br/> Returns nil when the stagenumber was not set before ever, which means Spearhead was not started.
|
---@field getCurrentStage fun(): number | nil @Returns the current stange number <br/> Returns nil when the stagenumber was not set before ever, which means Spearhead was not started.
|
||||||
---@field isStageComplete fun(stageNumber: number): boolean | nil, string @returns whether a stage (by index) is complete. <br/> @param stageNumber number <br/> @return boolean | nil <br/> @return string
|
---@field isStageComplete fun(stageNumber: number, stageLaneIdentifier: string?): boolean | nil, string @returns whether a stage (by index) is complete. <br/> @param stageNumber number <br/> @param stageLaneIdentifier string? nil for default lane <br/> @return boolean | nil <br/> @return string
|
||||||
|
|
||||||
---@class OnMissionCompleteListener
|
---@class OnMissionCompleteListener
|
||||||
---@field onMissionComplete fun(self: OnMissionCompleteListener, zone_name: string) @Called when a mission is completed. @return void
|
---@field onMissionComplete fun(self: OnMissionCompleteListener, zone_name: string) @Called when a mission is completed. @return void
|
||||||
@@ -14,4 +14,3 @@ SpearheadAPI = SpearheadAPI
|
|||||||
---@class MissionAPI
|
---@class MissionAPI
|
||||||
---@field addOnMissionCompleteListener fun(listener: OnMissionCompleteListener) @Adds a listener to the mission. <br/> @param listener OnMissionCompleteListener <br/> @return void
|
---@field addOnMissionCompleteListener fun(listener: OnMissionCompleteListener) @Adds a listener to the mission. <br/> @param listener OnMissionCompleteListener <br/> @return void
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -392,9 +392,12 @@ function CapBase:CheckAndScheduleIntercept()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
function CapBase:OnStageNumberChanged(number)
|
|
||||||
|
function CapBase:OnStageNumberChanged(number, laneIdentifier)
|
||||||
|
-- only react on "main" lane changes, ignore other lanes for now
|
||||||
|
if laneIdentifier ~= nil then return end
|
||||||
|
|
||||||
self.activeStage = number
|
self.activeStage = number
|
||||||
|
|
||||||
if self:IsBaseActiveWhenStageIsActive(number) == true then
|
if self:IsBaseActiveWhenStageIsActive(number) == true then
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ local SpearheadEvents = require("classes.spearhead_events")
|
|||||||
local RTBMission = require("classes.capClasses.taskings.RTB")
|
local RTBMission = require("classes.capClasses.taskings.RTB")
|
||||||
local Util = require("classes.util.Util")
|
local Util = require("classes.util.Util")
|
||||||
local DcsUtil = require("classes.util.DcsUtil")
|
local DcsUtil = require("classes.util.DcsUtil")
|
||||||
|
local GlobalConfig = require("classes.configuration.GlobalConfig")
|
||||||
|
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
||||||
|
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
|
||||||
|
|
||||||
---@class AirGroup : OnUnitLostListener
|
---@class AirGroup : OnUnitLostListener
|
||||||
---@field protected _logger Logger
|
---@field protected _logger Logger
|
||||||
@@ -12,9 +15,12 @@ local DcsUtil = require("classes.util.DcsUtil")
|
|||||||
---@field protected _config CapConfig
|
---@field protected _config CapConfig
|
||||||
---@field protected _checkLivenessNumber number
|
---@field protected _checkLivenessNumber number
|
||||||
---@field protected _spawnManager SpawnManager
|
---@field protected _spawnManager SpawnManager
|
||||||
|
---@field protected _routeDrawing CustomDrawing?
|
||||||
local AirGroup = {}
|
local AirGroup = {}
|
||||||
AirGroup.__index = AirGroup
|
AirGroup.__index = AirGroup
|
||||||
|
|
||||||
|
local globalConfig = GlobalConfig.New()
|
||||||
|
|
||||||
---@param logger Logger
|
---@param logger Logger
|
||||||
---@param groupName string
|
---@param groupName string
|
||||||
---@param groupType AirGroupType
|
---@param groupType AirGroupType
|
||||||
@@ -28,6 +34,7 @@ function AirGroup:New(groupName, groupType, config, logger, spawnManager)
|
|||||||
self._config = config
|
self._config = config
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
self._spawnManager = spawnManager
|
self._spawnManager = spawnManager
|
||||||
|
self._routeDrawings = {}
|
||||||
|
|
||||||
local group = Group.getByName(self._groupName)
|
local group = Group.getByName(self._groupName)
|
||||||
if group then
|
if group then
|
||||||
@@ -92,6 +99,27 @@ function AirGroup:SetMissionPrivate(mission)
|
|||||||
if group and mission then
|
if group and mission then
|
||||||
group:getController():setTask(mission)
|
group:getController():setTask(mission)
|
||||||
self._logger:debug("mission - Task set for group: " .. self._groupName)
|
self._logger:debug("mission - Task set for group: " .. self._groupName)
|
||||||
|
|
||||||
|
if globalConfig:isDebugMenuEnabled() == true then
|
||||||
|
-- draw the mission route for debugging purposes
|
||||||
|
if self._routeDrawing then
|
||||||
|
self._routeDrawing:Remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
local points = {}
|
||||||
|
if mission and mission.params and mission.params.route and mission.params.route.points then
|
||||||
|
for _, wp in pairs(mission.params.route.points) do
|
||||||
|
if wp.x and wp.y then
|
||||||
|
table.insert(points, { x = wp.x, y = wp.y })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local colorString = DrawingHelper.ColorTableToColorString({ 1, 0, 0, 1 })
|
||||||
|
self._routeDrawing = CustomDrawing.FromPoints(points, colorString, 5, 2)
|
||||||
|
self._routeDrawing:Draw()
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ local briefingMessageTime = nil
|
|||||||
---@class GlobalConfig
|
---@class GlobalConfig
|
||||||
---@field private _briefingTime number
|
---@field private _briefingTime number
|
||||||
---@field private _debugEnabled boolean
|
---@field private _debugEnabled boolean
|
||||||
|
---@field private _debugMenuEnabled boolean
|
||||||
local GlobalConfig = {}
|
local GlobalConfig = {}
|
||||||
GlobalConfig.__index = GlobalConfig;
|
GlobalConfig.__index = GlobalConfig;
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ function GlobalConfig.New()
|
|||||||
self._debugEnabled = false
|
self._debugEnabled = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self._debugMenuEnabled = SpearheadConfig.debugMenuEnabled == true
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -40,6 +43,10 @@ function GlobalConfig:isDebugEnabled()
|
|||||||
return self._debugEnabled or false
|
return self._debugEnabled or false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function GlobalConfig:isDebugMenuEnabled()
|
||||||
|
return self._debugMenuEnabled or false
|
||||||
|
end
|
||||||
|
|
||||||
return GlobalConfig
|
return GlobalConfig
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
---@class PersistenceConfig
|
||||||
|
---@field private _enabled boolean
|
||||||
|
---@field private directory string?
|
||||||
|
---@field private fileName string?
|
||||||
|
local PersistenceConfig = {}
|
||||||
|
|
||||||
|
function PersistenceConfig.new()
|
||||||
|
local self = setmetatable({}, { __index = PersistenceConfig })
|
||||||
|
|
||||||
|
if not SpearheadConfig then SpearheadConfig = {} end
|
||||||
|
if not SpearheadConfig.Persistence then SpearheadConfig.Persistence = {} end
|
||||||
|
self._enabled = SpearheadConfig.Persistence.enabled == true
|
||||||
|
self.directory = SpearheadConfig.Persistence.directory
|
||||||
|
self.fileName = SpearheadConfig.Persistence.fileName
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function PersistenceConfig:isEnabled()
|
||||||
|
return self._enabled == true
|
||||||
|
end
|
||||||
|
|
||||||
|
function PersistenceConfig:getDirectory()
|
||||||
|
return self.directory
|
||||||
|
end
|
||||||
|
|
||||||
|
function PersistenceConfig:getFileName()
|
||||||
|
return self.fileName
|
||||||
|
end
|
||||||
|
|
||||||
|
return PersistenceConfig
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
local Logger = require("classes.util.Logger")
|
||||||
|
local Util = require("classes.util.Util")
|
||||||
|
local StageRepository = require("classes.stageClasses.StageRepository")
|
||||||
|
|
||||||
|
---@class DebugMenu
|
||||||
|
---@field private _logger Logger
|
||||||
|
---@field private _stageRepository StageRepository
|
||||||
|
local DebugMenu = {}
|
||||||
|
DebugMenu.__index = DebugMenu
|
||||||
|
|
||||||
|
function DebugMenu.new()
|
||||||
|
local self = setmetatable({}, DebugMenu)
|
||||||
|
self._logger = Logger.new("DebugMenu")
|
||||||
|
self._stageRepository = StageRepository.getInstance()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
local menuName = "Spearhead Debug"
|
||||||
|
local debugMenuPath = { [1] = menuName }
|
||||||
|
function DebugMenu:RegisterMenus()
|
||||||
|
|
||||||
|
missionCommands.addSubMenu(menuName, {})
|
||||||
|
|
||||||
|
local refresh = function(params)
|
||||||
|
local selfA = params.self
|
||||||
|
selfA:RefreshMenu()
|
||||||
|
end
|
||||||
|
missionCommands.addCommand("Refresh Menu", debugMenuPath, refresh, { self = self })
|
||||||
|
|
||||||
|
self:AddStageOptions()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function DebugMenu:RefreshMenu()
|
||||||
|
|
||||||
|
missionCommands.removeItem(debugMenuPath)
|
||||||
|
self:RegisterMenus()
|
||||||
|
end
|
||||||
|
|
||||||
|
function DebugMenu:AddStageOptions()
|
||||||
|
local stageMenuTable = missionCommands.addSubMenu("Stages", debugMenuPath)
|
||||||
|
|
||||||
|
local stageLanes = self._stageRepository:getAllStageLanes()
|
||||||
|
for _, stageLane in pairs(stageLanes) do
|
||||||
|
|
||||||
|
local stages = stageLane:GetStagesAtIndex(stageLane:GetActiveStageIndex())
|
||||||
|
if stages then
|
||||||
|
for _, stage in pairs(stages) do
|
||||||
|
local stageLaneId = stageLane:GetStageLaneIdentifier() or ""
|
||||||
|
local stageIndex = stage:GetStageIndex()
|
||||||
|
local stageName = stage:GetStageName()
|
||||||
|
|
||||||
|
local stageMenuName = stageLaneId .. stageIndex .. "_" .. stageName
|
||||||
|
local currentStageMenuTable = missionCommands.addSubMenu(stageMenuName, stageMenuTable)
|
||||||
|
|
||||||
|
---@class CompleteStageParams
|
||||||
|
---@field self DebugMenu
|
||||||
|
---@field stage Stage
|
||||||
|
|
||||||
|
---@param params CompleteStageParams
|
||||||
|
local completeStage = function(params)
|
||||||
|
local stageA = params.stage
|
||||||
|
local missions = stageA:GetMissions()
|
||||||
|
for _, mission in pairs(missions) do
|
||||||
|
mission:ForceMissionComplete()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type CompleteStageParams
|
||||||
|
local params = { self = self, stage = stage }
|
||||||
|
missionCommands.addCommand("Complete Stage", currentStageMenuTable, completeStage, params)
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self._logger:info("No stages found for lane: " .. (stageLane:GetStageLaneIdentifier() or "default") .. " at index: " .. (stageLane:GetActiveStageIndex() or "nil"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return DebugMenu
|
||||||
@@ -72,7 +72,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if Util.startswith(namePart, "%[") == true then
|
if Util.startsWith(namePart, "%[") == true then
|
||||||
namePart = Util.split_string(namePart, "[")[1]
|
namePart = Util.split_string(namePart, "[")[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -127,7 +127,9 @@ function FleetGroup:new(fleetGroupName, database, logger)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
o.OnStageNumberChanged = function(self, number)
|
o.OnStageNumberChanged = function(self, number, laneIdentifier)
|
||||||
|
-- only react on "main" lane changes, ignore other lanes for now
|
||||||
|
if laneIdentifier ~= nil then return end
|
||||||
local targetZone = self.targetZonePerStage[tostring(number)]
|
local targetZone = self.targetZonePerStage[tostring(number)]
|
||||||
if targetZone and targetZone ~= self.currentTargetZone then
|
if targetZone and targetZone ~= self.currentTargetZone then
|
||||||
local points = self.pointsPerZone[targetZone]
|
local points = self.pointsPerZone[targetZone]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ GlobalFleetManager.start = function(database)
|
|||||||
|
|
||||||
local all_groups = MizGroupsManager.getAllGroupNames()
|
local all_groups = MizGroupsManager.getAllGroupNames()
|
||||||
for _, groupName in pairs(all_groups) do
|
for _, groupName in pairs(all_groups) do
|
||||||
if Util.startswith(string.lower(groupName), "carriergroup" ) == true then
|
if Util.startsWith(string.lower(groupName), "carriergroup" ) == true then
|
||||||
logger:info("Registering " .. groupName .. " as a managed fleet")
|
logger:info("Registering " .. groupName .. " as a managed fleet")
|
||||||
local carrierGroup = FleetGroup:new(groupName, database, logger)
|
local carrierGroup = FleetGroup:new(groupName, database, logger)
|
||||||
table.insert(fleetGroups, carrierGroup)
|
table.insert(fleetGroups, carrierGroup)
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ do
|
|||||||
---@field unitsStates table<string, UnitState>
|
---@field unitsStates table<string, UnitState>
|
||||||
---@field random_missions table<string, MissionState>
|
---@field random_missions table<string, MissionState>
|
||||||
---@field deliveredKilos table<string, number>
|
---@field deliveredKilos table<string, number>
|
||||||
---@field activeStage integer|nil
|
---@field activeStage number? required for backwards compatibility
|
||||||
|
---@field activeStageInStageLane table<string, number>
|
||||||
|
|
||||||
---@class UnitState
|
---@class UnitState
|
||||||
---@field isDead boolean
|
---@field isDead boolean
|
||||||
@@ -33,7 +34,8 @@ do
|
|||||||
unitsStates = {},
|
unitsStates = {},
|
||||||
random_missions = {},
|
random_missions = {},
|
||||||
deliveredKilos = {},
|
deliveredKilos = {},
|
||||||
activeStage = nil
|
activeStage = nil, -- Backwards compatibility for previous version without lanes
|
||||||
|
activeStageInStageLane = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
local logger = {}
|
local logger = {}
|
||||||
@@ -146,7 +148,7 @@ do
|
|||||||
for file in lfs.dir(dir) do
|
for file in lfs.dir(dir) do
|
||||||
|
|
||||||
local split = Util.split_string(file, ".")
|
local split = Util.split_string(file, ".")
|
||||||
local doesStartWith = Util.startswith(file, startsWith, true)
|
local doesStartWith = Util.startsWith(file, startsWith, true)
|
||||||
if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
|
if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
|
||||||
local numberString = split[#split-1]
|
local numberString = split[#split-1]
|
||||||
local number = tonumber(numberString)
|
local number = tonumber(numberString)
|
||||||
@@ -230,12 +232,29 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
---Sets the stage in the persistence table
|
---Sets the stage in the persistence table
|
||||||
|
---@param stageLane string?
|
||||||
---@param stageNumber number
|
---@param stageNumber number
|
||||||
Persistence.SetActiveStage = function(stageNumber)
|
Persistence.SetActiveStage = function(stageLane, stageNumber)
|
||||||
tables.activeStage = stageNumber
|
stageLane = stageLane or "nil"
|
||||||
|
tables.activeStageInStageLane[stageLane] = stageNumber
|
||||||
Persistence._updateRequired = true
|
Persistence._updateRequired = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Get the active stage as in the persistance file
|
||||||
|
---@param stageLane string?
|
||||||
|
---@return integer|nil
|
||||||
|
Persistence.GetActiveStage = function(stageLane)
|
||||||
|
stageLane = stageLane or "nil"
|
||||||
|
|
||||||
|
-- backwards compatibility for previous versions without stage lanes
|
||||||
|
if stageLane == "nil" and tables.activeStageInStageLane[stageLane] == nil then
|
||||||
|
tables.activeStageInStageLane[stageLane] = tables.activeStage
|
||||||
|
tables.activeStage = nil -- Clear the old activeStage so it's not used or confused later
|
||||||
|
end
|
||||||
|
|
||||||
|
return tables.activeStageInStageLane[stageLane]
|
||||||
|
end
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param missionName string
|
---@param missionName string
|
||||||
---@param pickedZone string
|
---@param pickedZone string
|
||||||
@@ -255,14 +274,6 @@ do
|
|||||||
return tables.random_missions[string.lower(missionName)]
|
return tables.random_missions[string.lower(missionName)]
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get the active stage as in the persistance file
|
|
||||||
---@return integer|nil
|
|
||||||
Persistence.GetActiveStage = function()
|
|
||||||
if tables.activeStage then
|
|
||||||
return tables.activeStage
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param zoneName string
|
---@param zoneName string
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ local Util = require("classes.util.Util")
|
|||||||
local DcsUtil = require("classes.util.DcsUtil")
|
local DcsUtil = require("classes.util.DcsUtil")
|
||||||
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
||||||
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
|
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
|
||||||
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
local StageDrawing = require("classes.stageClasses.drawings.StageDrawing")
|
||||||
|
|
||||||
|
|
||||||
---@class DatabaseTables
|
---@class DatabaseTables
|
||||||
@@ -27,7 +27,7 @@ local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
|||||||
---@field MissionZoneData table<string, MissionZoneData>
|
---@field MissionZoneData table<string, MissionZoneData>
|
||||||
---@field FarpZoneData table<string,FarpZoneData>
|
---@field FarpZoneData table<string,FarpZoneData>
|
||||||
---@field missionCodes table<string, boolean>
|
---@field missionCodes table<string, boolean>
|
||||||
---@field CustomDrawings Array<CustomDrawing>
|
---@field StageDrawings Array<StageDrawing>
|
||||||
|
|
||||||
---@class CapRoute
|
---@class CapRoute
|
||||||
---@field zones Array<SpearheadTriggerZone>
|
---@field zones Array<SpearheadTriggerZone>
|
||||||
@@ -58,6 +58,7 @@ local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
|||||||
---@class BlueSamData
|
---@class BlueSamData
|
||||||
---@field groups Array<string>
|
---@field groups Array<string>
|
||||||
---@field buildingKilos number?
|
---@field buildingKilos number?
|
||||||
|
---@field briefing string?
|
||||||
|
|
||||||
---@class MissionZoneData
|
---@class MissionZoneData
|
||||||
---@field ZoneName string
|
---@field ZoneName string
|
||||||
@@ -68,12 +69,14 @@ local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
|||||||
---@field descriptionLocation Vec2?
|
---@field descriptionLocation Vec2?
|
||||||
---@field dependsOn Array<string>
|
---@field dependsOn Array<string>
|
||||||
---@field completeAt number?
|
---@field completeAt number?
|
||||||
|
---@field primaryOverwrite boolean?
|
||||||
|
|
||||||
---@class FarpZoneData
|
---@class FarpZoneData
|
||||||
---@field groups Array<string>
|
---@field groups Array<string>
|
||||||
---@field padNames Array<string>
|
---@field padNames Array<string>
|
||||||
---@field buildingKilos number?
|
---@field buildingKilos number?
|
||||||
---@field supplyHubNames Array<string>
|
---@field supplyHubNames Array<string>
|
||||||
|
---@field briefing string?
|
||||||
|
|
||||||
---@class Database
|
---@class Database
|
||||||
---@field private _tables DatabaseTables
|
---@field private _tables DatabaseTables
|
||||||
@@ -107,7 +110,7 @@ function Database.New(Logger)
|
|||||||
FarpZoneData = {},
|
FarpZoneData = {},
|
||||||
missionCodes = {},
|
missionCodes = {},
|
||||||
SupplyHubZones = {},
|
SupplyHubZones = {},
|
||||||
CustomDrawings = {}
|
StageDrawings = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Database.__index = Database
|
Database.__index = Database
|
||||||
@@ -209,7 +212,7 @@ function Database.New(Logger)
|
|||||||
for i, layer in pairs(env.mission.drawings.layers) do
|
for i, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for key, layer_object in pairs(layer.objects) do
|
for key, layer_object in pairs(layer.objects) do
|
||||||
if Util.startswith(string.lower(layer_object.name), "buildable", true) == true then
|
if Util.startsWith(string.lower(layer_object.name), "buildable", true) == true then
|
||||||
local airbaseData = self:getAirbaseDataForDrawLayer(layer_object)
|
local airbaseData = self:getAirbaseDataForDrawLayer(layer_object)
|
||||||
if airbaseData then
|
if airbaseData then
|
||||||
self._logger:debug("found airbase data for " .. layer_object.name)
|
self._logger:debug("found airbase data for " .. layer_object.name)
|
||||||
@@ -229,10 +232,12 @@ function Database.New(Logger)
|
|||||||
for i, layer in pairs(env.mission.drawings.layers) do
|
for i, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for key, layer_object in pairs(layer.objects) do
|
for key, layer_object in pairs(layer.objects) do
|
||||||
if Util.startswith(layer_object.name, "drawing_", true) then
|
if Util.startsWith(layer_object.name, "drawing_", true) then
|
||||||
local object = layer_object --[[@as DrawingObject]]
|
local object = layer_object --[[@as DrawingObject]]
|
||||||
local customDrawing = CustomDrawing.New(object)
|
local stageDrawing = StageDrawing.New(object)
|
||||||
table.insert(self._tables.CustomDrawings, customDrawing)
|
if stageDrawing then
|
||||||
|
table.insert(self._tables.StageDrawings, stageDrawing)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -255,7 +260,7 @@ function Database.New(Logger)
|
|||||||
for i, layer in pairs(env.mission.drawings.layers) do
|
for i, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for key, layer_object in pairs(layer.objects) do
|
for key, layer_object in pairs(layer.objects) do
|
||||||
if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then
|
if Util.startsWith(string.lower(layer_object.name), "stagebriefing_", true) == true then
|
||||||
local zone = DcsUtil.getZoneByName(stageZoneName)
|
local zone = DcsUtil.getZoneByName(stageZoneName)
|
||||||
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
||||||
if zone and Util.is2dPointInZone(vec2, zone) == true then
|
if zone and Util.is2dPointInZone(vec2, zone) == true then
|
||||||
@@ -480,7 +485,7 @@ end
|
|||||||
local getAvailableCAPGroups = function()
|
local getAvailableCAPGroups = function()
|
||||||
local result = {}
|
local result = {}
|
||||||
for name, value in pairs(is_group_taken) do
|
for name, value in pairs(is_group_taken) do
|
||||||
if value == false and Util.startswith(name, "CAP") then
|
if value == false and Util.startsWith(name, "CAP") then
|
||||||
table.insert(result, name)
|
table.insert(result, name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -583,11 +588,11 @@ function Database:loadCapUnits()
|
|||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
|
|
||||||
if Util.startswith(groupName, "CAP_A", true) or Util.startswith(groupName, "CAP_B", true) then
|
if Util.startsWith(groupName, "CAP_A", true) or Util.startsWith(groupName, "CAP_B", true) then
|
||||||
table.insert(baseData.CapGroups, groupName)
|
table.insert(baseData.CapGroups, groupName)
|
||||||
elseif Util.startswith(groupName, "CAP_I", true) then
|
elseif Util.startsWith(groupName, "CAP_I", true) then
|
||||||
table.insert(baseData.InterceptGroups, groupName)
|
table.insert(baseData.InterceptGroups, groupName)
|
||||||
elseif Util.startswith(groupName, "CAP_S", true) then
|
elseif Util.startsWith(groupName, "CAP_S", true) then
|
||||||
table.insert(baseData.SweepGroups, groupName)
|
table.insert(baseData.SweepGroups, groupName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -610,10 +615,31 @@ function Database:loadBlueSamUnits()
|
|||||||
local triggerZone = DcsUtil.getZoneByName(blueSamZone)
|
local triggerZone = DcsUtil.getZoneByName(blueSamZone)
|
||||||
if triggerZone then
|
if triggerZone then
|
||||||
for _, kvPair in pairs(triggerZone.properties) do
|
for _, kvPair in pairs(triggerZone.properties) do
|
||||||
if kvPair.key and Util.startswith(kvPair.key, "buildable") then
|
if kvPair.key and Util.startsWith(kvPair.key, "buildable") then
|
||||||
local number = tonumber(kvPair.value)
|
local number = tonumber(kvPair.value)
|
||||||
if number and number > 0 then
|
if number and number > 0 then
|
||||||
samData.buildingKilos = number
|
samData.buildingKilos = number
|
||||||
|
|
||||||
|
if env.mission.drawings and env.mission.drawings.layers then
|
||||||
|
for i, layer in pairs(env.mission.drawings.layers) do
|
||||||
|
if string.lower(layer.name) == "author" then
|
||||||
|
for key, layer_object in pairs(layer.objects) do
|
||||||
|
|
||||||
|
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
||||||
|
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
|
||||||
|
if layer_object.name and Util.startsWith(layer_object.name, "supplybriefing_", true) then
|
||||||
|
local description = layer_object.text
|
||||||
|
if description and description ~= "" then
|
||||||
|
samData.briefing = description
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
MissionEditorWarnings.Add("Buildable number for " .. blueSamZone .. " is invalid")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -621,12 +647,12 @@ function Database:loadBlueSamUnits()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return Array<CustomDrawing>
|
---@return Array<StageDrawing>
|
||||||
function Database:getCustomDrawings()
|
function Database:getStageDrawings()
|
||||||
if self._tables.CustomDrawings == nil then
|
if self._tables.StageDrawings == nil then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
return self._tables.CustomDrawings
|
return self._tables.StageDrawings
|
||||||
end
|
end
|
||||||
|
|
||||||
---Loads all units, data and briefings
|
---Loads all units, data and briefings
|
||||||
@@ -677,9 +703,9 @@ function Database:LoadZoneData(missionZoneName)
|
|||||||
if triggerZone and triggerZone.properties then
|
if triggerZone and triggerZone.properties then
|
||||||
for _, kvPair in pairs(triggerZone.properties) do
|
for _, kvPair in pairs(triggerZone.properties) do
|
||||||
local key = kvPair.key
|
local key = kvPair.key
|
||||||
if Util.startswith(key, "dependson", true) == true then
|
if Util.startsWith(key, "dependson", true) == true then
|
||||||
table.insert(self._tables.MissionZoneData[missionZoneName].dependsOn, kvPair.value)
|
table.insert(self._tables.MissionZoneData[missionZoneName].dependsOn, kvPair.value)
|
||||||
elseif Util.startswith(key, "completeat") == true then
|
elseif Util.startsWith(key, "completeat") == true then
|
||||||
local value = tonumber(kvPair.value)
|
local value = tonumber(kvPair.value)
|
||||||
if value then
|
if value then
|
||||||
if value > 1 and value <= 100 then
|
if value > 1 and value <= 100 then
|
||||||
@@ -689,6 +715,14 @@ function Database:LoadZoneData(missionZoneName)
|
|||||||
end
|
end
|
||||||
self._tables.MissionZoneData[missionZoneName].completeAt = value
|
self._tables.MissionZoneData[missionZoneName].completeAt = value
|
||||||
end
|
end
|
||||||
|
elseif Util.startsWith(key, "primary", true) == true then
|
||||||
|
if type(kvPair.value) == "string" then
|
||||||
|
if kvPair.value == "true" then
|
||||||
|
self._tables.MissionZoneData[missionZoneName].primaryOverwrite = true
|
||||||
|
elseif kvPair.value == "false" then
|
||||||
|
self._tables.MissionZoneData[missionZoneName].primaryOverwrite = false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -701,7 +735,7 @@ function Database:LoadZoneData(missionZoneName)
|
|||||||
|
|
||||||
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
||||||
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
|
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
|
||||||
if layer_object.name and Util.startswith(layer_object.name, "briefing_", true) then
|
if layer_object.name and Util.startsWith(layer_object.name, "briefing_", true) then
|
||||||
local description = layer_object.text
|
local description = layer_object.text
|
||||||
if description and description ~= "" then
|
if description and description ~= "" then
|
||||||
self._tables.MissionZoneData[missionZoneName].description = description
|
self._tables.MissionZoneData[missionZoneName].description = description
|
||||||
@@ -744,11 +778,35 @@ function Database:loadFarpData()
|
|||||||
local triggerZone = DcsUtil.getZoneByName(farpZone)
|
local triggerZone = DcsUtil.getZoneByName(farpZone)
|
||||||
if triggerZone then
|
if triggerZone then
|
||||||
for _, kvPair in pairs(triggerZone.properties) do
|
for _, kvPair in pairs(triggerZone.properties) do
|
||||||
if kvPair.key and Util.startswith(kvPair.key, "buildable", true) == true then
|
if kvPair.key and Util.startsWith(kvPair.key, "buildable", true) == true then
|
||||||
local number = tonumber(kvPair.value)
|
local number = tonumber(kvPair.value)
|
||||||
if number and number > 0 then
|
if number and number > 0 then
|
||||||
farpzoneData.buildingKilos = number
|
farpzoneData.buildingKilos = number
|
||||||
|
|
||||||
|
-- check briefings
|
||||||
|
if env.mission.drawings and env.mission.drawings.layers then
|
||||||
|
for i, layer in pairs(env.mission.drawings.layers) do
|
||||||
|
if string.lower(layer.name) == "author" then
|
||||||
|
for key, layer_object in pairs(layer.objects) do
|
||||||
|
|
||||||
|
local vec2 = { x = layer_object.mapX, y = layer_object.mapY }
|
||||||
|
if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then
|
||||||
|
if layer_object.name and Util.startsWith(layer_object.name, "supplybriefing_", true) then
|
||||||
|
local description = layer_object.text
|
||||||
|
if description and description ~= "" then
|
||||||
|
farpzoneData.briefing = description
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
MissionEditorWarnings.Add("Buildable number for " .. farpZone .. " is invalid.")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -29,49 +29,44 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@class OnStageChangedListener
|
---@class OnStageChangedListener
|
||||||
---@field OnStageNumberChanged fun(self:OnStageChangedListener, number:integer)
|
---@field OnStageNumberChanged fun(self:OnStageChangedListener, number:integer, laneIdentifier:string?)
|
||||||
|
|
||||||
do -- STAGE NUMBER CHANGED
|
do -- STAGE NUMBER CHANGED
|
||||||
local OnStageNumberChangedListeners = {}
|
local OnStageNumberChangedListeners = {}
|
||||||
local OnStageNumberChangedHandlers = {}
|
local OnStageNumberChangedHandlers = {}
|
||||||
---Add a stage zone number changed listener
|
---Add a stage zone number changed listener
|
||||||
---@param listener OnStageChangedListener object with function OnStageNumberChanged(self, number)
|
---@param listener OnStageChangedListener object with function OnStageNumberChanged(self, number, stageLaneIdentifier)
|
||||||
SpearheadEvents.AddStageNumberChangedListener = function(listener)
|
SpearheadEvents.AddStageNumberChangedListener = function(listener)
|
||||||
|
if type(listener) ~= "table" or type(listener.OnStageNumberChanged) ~= "function" then
|
||||||
|
warn("Event handler not of type table/object with function OnStageNumberChanged(self, number, stageLaneIdentifier)")
|
||||||
|
return
|
||||||
|
end
|
||||||
table.insert(OnStageNumberChangedListeners, listener)
|
table.insert(OnStageNumberChangedListeners, listener)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Add a stage zone number changed listener
|
|
||||||
---@param handler function function(number)
|
|
||||||
SpearheadEvents.AddStageNumberChangedHandler = function(handler)
|
|
||||||
if type(handler) ~= "function" then
|
|
||||||
warn("Event handler not of type function, did you mean to use listener?")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
table.insert(OnStageNumberChangedHandlers, handler)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@class OnStageNumberChangeCompleteListener
|
---@class OnStageNumberChangeCompleteListener
|
||||||
---@field OnStageNumberChangeComplete fun(self:OnStageNumberChangeCompleteListener, number:integer)
|
---@field OnStageNumberChangeComplete fun(self:OnStageNumberChangeCompleteListener, number:integer, laneIdentifier:string?)
|
||||||
|
|
||||||
local OnStageNumberChangeCompleteListeners = {}
|
local OnStageNumberChangeCompleteListeners = {}
|
||||||
---@param listener OnStageNumberChangeCompleteListener
|
---@param listener OnStageNumberChangeCompleteListener
|
||||||
SpearheadEvents.AddStageNumberChangeCompleteListener = function(listener)
|
SpearheadEvents.AddStageNumberChangeCompleteListener = function(listener)
|
||||||
if type(listener) ~= "table" or type(listener.OnStageNumberChangeComplete) ~= "function" then
|
if type(listener) ~= "table" or type(listener.OnStageNumberChangeComplete) ~= "function" then
|
||||||
warn("Event handler not of type table/object with function OnStageNumberChangeComplete(self, number)")
|
warn("Event handler not of type table/object with function OnStageNumberChangeComplete(self, number, laneIdentifier)")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
table.insert(OnStageNumberChangeCompleteListeners, listener)
|
table.insert(OnStageNumberChangeCompleteListeners, listener)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param newStageNumber number
|
---@param newStageNumber number
|
||||||
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber)
|
---@param laneIdentifier string?
|
||||||
|
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber, laneIdentifier)
|
||||||
pcall(function ()
|
pcall(function ()
|
||||||
Persistence.SetActiveStage(newStageNumber)
|
Persistence.SetActiveStage(laneIdentifier, newStageNumber)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for _, callable in pairs(OnStageNumberChangedListeners) do
|
for _, callable in pairs(OnStageNumberChangedListeners) do
|
||||||
local succ, err = pcall(function()
|
local succ, err = pcall(function()
|
||||||
callable:OnStageNumberChanged(newStageNumber)
|
callable:OnStageNumberChanged(newStageNumber, laneIdentifier)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
logError(err)
|
logError(err)
|
||||||
@@ -79,7 +74,7 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _, callable in pairs(OnStageNumberChangedHandlers) do
|
for _, callable in pairs(OnStageNumberChangedHandlers) do
|
||||||
local succ, err = pcall(callable, newStageNumber)
|
local succ, err = pcall(callable, newStageNumber, laneIdentifier)
|
||||||
if err then
|
if err then
|
||||||
logError(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
@@ -87,7 +82,7 @@ do
|
|||||||
|
|
||||||
for _, callable in pairs(OnStageNumberChangeCompleteListeners) do
|
for _, callable in pairs(OnStageNumberChangeCompleteListeners) do
|
||||||
local succ, err = pcall(function()
|
local succ, err = pcall(function()
|
||||||
callable:OnStageNumberChangeComplete(newStageNumber)
|
callable:OnStageNumberChangeComplete(newStageNumber, laneIdentifier)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
logError(err)
|
logError(err)
|
||||||
|
|||||||
@@ -3,22 +3,14 @@ local Util = require("classes.util.Util")
|
|||||||
local DcsUtil = require("classes.util.DcsUtil")
|
local DcsUtil = require("classes.util.DcsUtil")
|
||||||
local Logger = require("classes.util.Logger")
|
local Logger = require("classes.util.Logger")
|
||||||
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
||||||
|
local PersistenceConfig = require("classes.configuration.PersistenceConfig")
|
||||||
local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
|
local ExtraStage = require("classes.stageClasses.Stages.ExtraStage")
|
||||||
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
|
local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage")
|
||||||
local WaitingStage = require("classes.stageClasses.Stages.WaitingStage")
|
local WaitingStage = require("classes.stageClasses.Stages.WaitingStage")
|
||||||
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
|
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
|
||||||
|
local StageRepository = require("classes.stageClasses.StageRepository")
|
||||||
local StagesByName = {}
|
local StageLane = require("classes.stageClasses.StageLane")
|
||||||
|
local Persistence = require("classes.persistence.Persistence")
|
||||||
---@type table<string, Array<Stage>>
|
|
||||||
local StagesByIndex = {}
|
|
||||||
|
|
||||||
---@type table<string, Array<Stage>>
|
|
||||||
local SideStageByIndex = {}
|
|
||||||
|
|
||||||
---@type table<string, Array<WaitingStage>>
|
|
||||||
local WaitingStagesByIndex = {}
|
|
||||||
|
|
||||||
local currentStage = -99
|
local currentStage = -99
|
||||||
|
|
||||||
@@ -26,25 +18,37 @@ local currentStage = -99
|
|||||||
---@field private database Database
|
---@field private database Database
|
||||||
---@field private logger Logger
|
---@field private logger Logger
|
||||||
---@field private stageConfig StageConfig
|
---@field private stageConfig StageConfig
|
||||||
|
---@field private _stageRepository StageRepository
|
||||||
---@field private _missionCommandsHelper MissionCommandsHelper
|
---@field private _missionCommandsHelper MissionCommandsHelper
|
||||||
local GlobalStageManager = {}
|
local GlobalStageManager = {}
|
||||||
GlobalStageManager.__index = GlobalStageManager
|
GlobalStageManager.__index = GlobalStageManager
|
||||||
|
|
||||||
GlobalStageManager.getCurrentStage = function() return currentStage end
|
GlobalStageManager.getCurrentStage = function() return currentStage end
|
||||||
|
|
||||||
|
---@type GlobalStageManager
|
||||||
|
local singletonInstance = nil
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param database Database
|
---@param database Database
|
||||||
---@param stageConfig StageConfig
|
---@param stageConfig StageConfig
|
||||||
---@param logLevel LogLevel
|
---@param logLevel LogLevel
|
||||||
---@param spawnManager SpawnManager
|
---@param spawnManager SpawnManager
|
||||||
---@return nil
|
---@return GlobalStageManager
|
||||||
function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnManager)
|
function GlobalStageManager.new(database, stageConfig, logLevel, spawnManager)
|
||||||
|
|
||||||
|
if singletonInstance ~= nil then
|
||||||
|
return singletonInstance
|
||||||
|
end
|
||||||
|
|
||||||
local logger = Logger.new("StageManager", logLevel)
|
local logger = Logger.new("StageManager", logLevel)
|
||||||
logger:info("Using Stage Log Level: " .. logLevel)
|
logger:info("Using Stage Log Level: " .. logLevel)
|
||||||
local self = setmetatable({}, GlobalStageManager)
|
local self = setmetatable({}, GlobalStageManager)
|
||||||
|
singletonInstance = self
|
||||||
|
|
||||||
self.database = database
|
self.database = database
|
||||||
self.stageConfig = stageConfig
|
self.stageConfig = stageConfig
|
||||||
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
||||||
|
self._stageRepository = StageRepository.getInstance()
|
||||||
|
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
if stageConfig.isAutoStages ~= true then
|
if stageConfig.isAutoStages ~= true then
|
||||||
@@ -55,170 +59,267 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
|
|||||||
|
|
||||||
for _, stageName in pairs(database:getStagezoneNames()) do
|
for _, stageName in pairs(database:getStagezoneNames()) do
|
||||||
logger:debug("Found stage zone with name: " .. stageName)
|
logger:debug("Found stage zone with name: " .. stageName)
|
||||||
|
|
||||||
|
local parseResult = self:ParseStageName(stageName)
|
||||||
|
if parseResult.isValid == false then
|
||||||
|
logger:warn("Stage zone with name " .. stageName .. " is not valid: " .. parseResult.invalidReason)
|
||||||
|
else
|
||||||
|
local initData = {
|
||||||
|
stageZoneName = parseResult.stageZoneName,
|
||||||
|
stageNumber = parseResult.orderNumber,
|
||||||
|
stageDisplayName = parseResult.stageDisplayName,
|
||||||
|
stageLaneIdentifier = parseResult.stageLaneIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
if Util.startswith(stageName, "missionstage", true) then
|
if parseResult.stageType == "PrimaryStage" then
|
||||||
local valid = true
|
local stage = PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
|
||||||
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 Util.tableLength(split) < 3 then
|
stage:AddStageCompleteListener(self)
|
||||||
MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a stage name")
|
self._stageRepository:AddStage(stage)
|
||||||
end
|
elseif parseResult.stageType == "ExtraStage" then
|
||||||
|
local stage = ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
|
||||||
local orderNumber = nil
|
stage:AddStageCompleteListener(self)
|
||||||
local isSideStage = false
|
self._stageRepository:AddStage(stage)
|
||||||
if valid == true then
|
elseif parseResult.stageType == "WaitingStage" then
|
||||||
local orderNumberString = string.lower(split[2])
|
local waitingStage = WaitingStage.New(database, stageConfig, logger, initData, parseResult.waitingStageSeconds, spawnManager)
|
||||||
if Util.startswith(orderNumberString, "x") == true then
|
waitingStage:AddStageCompleteListener(self)
|
||||||
isSideStage = true
|
self._stageRepository:AddStage(waitingStage)
|
||||||
|
|
||||||
orderNumberString = string.gsub(orderNumberString, "x", "")
|
|
||||||
orderNumber = tonumber(orderNumberString)
|
|
||||||
else
|
|
||||||
orderNumber = tonumber(split[2])
|
|
||||||
end
|
|
||||||
|
|
||||||
if orderNumber == nil then
|
|
||||||
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 = Logger.new(stageName, logLevel)
|
|
||||||
if valid == true and orderNumber then
|
|
||||||
|
|
||||||
---@type StageInitData
|
|
||||||
local initData = {
|
|
||||||
stageDisplayName = stageDisplayName,
|
|
||||||
stageNumber = orderNumber,
|
|
||||||
stageZoneName = stageName,
|
|
||||||
}
|
|
||||||
|
|
||||||
if isSideStage == true then
|
|
||||||
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 = PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager)
|
|
||||||
stage:AddStageCompleteListener(self)
|
|
||||||
|
|
||||||
if StagesByIndex[tostring(orderNumber)] == nil then StagesByIndex[tostring(orderNumber)] = {} end
|
|
||||||
table.insert(StagesByIndex[tostring(orderNumber)], stage)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Util.startswith(stageName, "waitingstage", true) then
|
|
||||||
local valid = true
|
|
||||||
|
|
||||||
local split = Util.split_string(stageName, "_")
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
if valid == true then
|
|
||||||
local stageIndexString = split[2]
|
|
||||||
local stageIndex = tonumber(stageIndexString)
|
|
||||||
|
|
||||||
if not stageIndex then
|
|
||||||
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
|
|
||||||
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 = Logger.new(stageName, logLevel)
|
|
||||||
|
|
||||||
---@type WaitingStageInitData
|
|
||||||
local initData = {
|
|
||||||
stageDisplayName = "Waiting Stage " .. stageIndex,
|
|
||||||
stageNumber = stageIndex or -99,
|
|
||||||
stageZoneName = stageName,
|
|
||||||
waitingSeconds = waitingSeconds --[[@as integer]]
|
|
||||||
}
|
|
||||||
local waitingStage = WaitingStage.New(database, stageConfig, stagelogger, initData, spawnManager)
|
|
||||||
|
|
||||||
if WaitingStagesByIndex[tostring(stageIndex)] == nil then
|
|
||||||
WaitingStagesByIndex[tostring(stageIndex)] = {}
|
|
||||||
end
|
|
||||||
table.insert(WaitingStagesByIndex[tostring(stageIndex)], waitingStage)
|
|
||||||
|
|
||||||
waitingStage:AddStageCompleteListener(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
singletonInstance = self
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function GlobalStageManager:Start()
|
||||||
|
self.logger:info("Starting GlobalStageManager")
|
||||||
|
|
||||||
|
local startingStage = self.stageConfig.startingStage or 1
|
||||||
|
local persistenceConfig = PersistenceConfig.new()
|
||||||
|
if persistenceConfig:isEnabled() == true then
|
||||||
|
self.logger:info("Persistence is enabled, loading stage state from persistence")
|
||||||
|
|
||||||
|
local stageLanes = self._stageRepository:getAllStageLanes()
|
||||||
|
for _, stageLane in pairs(stageLanes) do
|
||||||
|
|
||||||
|
local stageLaneIdentifier = stageLane:GetStageLaneIdentifier()
|
||||||
|
local persistedStage = Persistence.GetActiveStage(stageLaneIdentifier)
|
||||||
|
if persistedStage then
|
||||||
|
self.logger:info("Loaded persisted stage " .. persistedStage .. " for lane " .. (stageLaneIdentifier or "default"))
|
||||||
|
stageLane:SetActiveStageIndex(persistedStage)
|
||||||
|
Events.PublishStageNumberChanged(persistedStage, stageLaneIdentifier)
|
||||||
|
else
|
||||||
|
stageLane:SetActiveStageIndex(startingStage)
|
||||||
|
Events.PublishStageNumberChanged(startingStage, stageLaneIdentifier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.logger:info("Persistence is disabled, starting at stage " .. startingStage)
|
||||||
|
local stageLanes = self._stageRepository:getAllStageLanes()
|
||||||
|
for _, stageLane in pairs(stageLanes) do
|
||||||
|
stageLane:SetActiveStageIndex(startingStage)
|
||||||
|
Events.PublishStageNumberChanged(startingStage, stageLane:GetStageLaneIdentifier())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class StageNameParseResult
|
||||||
|
---@field isValid boolean
|
||||||
|
---@field orderNumber integer
|
||||||
|
---@field stageLaneIdentifier string?
|
||||||
|
---@field stageDisplayName string
|
||||||
|
---@field stageZoneName string
|
||||||
|
---@field stageType StageType
|
||||||
|
---@field invalidReason string?
|
||||||
|
---@field waitingStageSeconds integer?
|
||||||
|
|
||||||
|
---@param self GlobalStageManager
|
||||||
|
---@param stageName string
|
||||||
|
---@return StageNameParseResult
|
||||||
|
function GlobalStageManager:ParseStageName(stageName)
|
||||||
|
|
||||||
|
local split = Util.split_string(stageName, "_")
|
||||||
|
|
||||||
|
if Util.tableLength(split) < 3 then
|
||||||
|
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a order number or valid format" }
|
||||||
|
end
|
||||||
|
|
||||||
|
local typePart = string.lower(split[1])
|
||||||
|
|
||||||
|
if typePart == "missionstage" then
|
||||||
|
|
||||||
|
local orderNumberString = string.lower(split[2])
|
||||||
|
---@type StageType
|
||||||
|
local stageType = "PrimaryStage"
|
||||||
|
if Util.endsWith(orderNumberString, "x", true) == true then
|
||||||
|
stageType = "ExtraStage"
|
||||||
|
orderNumberString = orderNumberString:sub(1, -1)
|
||||||
|
end
|
||||||
|
local stageLaneIdentifier = nil
|
||||||
|
local first = orderNumberString:sub(1, 1)
|
||||||
|
if tonumber(first) == nil then -- first character is the lane identifier only if it's not a number
|
||||||
|
stageLaneIdentifier = string.lower(first)
|
||||||
|
|
||||||
|
orderNumberString = orderNumberString:sub(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local orderNumber = tonumber(orderNumberString)
|
||||||
|
if orderNumber == nil then
|
||||||
|
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString }
|
||||||
|
end
|
||||||
|
|
||||||
|
local stageDisplayName = split[3]
|
||||||
|
---@type StageNameParseResult
|
||||||
|
local result = {
|
||||||
|
isValid = true,
|
||||||
|
orderNumber = orderNumber,
|
||||||
|
stageLaneIdentifier = stageLaneIdentifier,
|
||||||
|
stageDisplayName = stageDisplayName,
|
||||||
|
stageZoneName = stageName,
|
||||||
|
stageType = stageType
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
elseif typePart == "waitingstage" then
|
||||||
|
|
||||||
|
local stageType = "WaitingStage"
|
||||||
|
local orderNumberString = split[2]
|
||||||
|
local orderNumber = tonumber(orderNumberString)
|
||||||
|
if orderNumber == nil then
|
||||||
|
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid order number : " .. orderNumberString }
|
||||||
|
end
|
||||||
|
|
||||||
|
local waitingSecondsString = split[3]
|
||||||
|
local waitingSeconds = tonumber(waitingSecondsString)
|
||||||
|
if waitingSeconds == nil then
|
||||||
|
return { isValid = false, invalidReason = "Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter : " .. waitingSecondsString }
|
||||||
|
end
|
||||||
|
|
||||||
|
local stageDisplayName = "Waiting Stage " .. orderNumber
|
||||||
|
|
||||||
|
---@type StageNameParseResult
|
||||||
|
local result = {
|
||||||
|
isValid = true,
|
||||||
|
orderNumber = orderNumber,
|
||||||
|
stageLaneIdentifier = nil,
|
||||||
|
stageDisplayName = stageDisplayName,
|
||||||
|
stageZoneName = stageName,
|
||||||
|
stageType = stageType,
|
||||||
|
waitingStageSeconds = waitingSeconds
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
||||||
|
end
|
||||||
|
return { isValid = false, invalidReason = "Stage zone with name " .. stageName .. " has an unrecognized type: " .. typePart }
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stage Stage
|
||||||
function GlobalStageManager:OnStageComplete(stage)
|
function GlobalStageManager:OnStageComplete(stage)
|
||||||
self.logger:debug("Receiving stage complete event from: " .. stage.zoneName)
|
self.logger:debug("Receiving stage complete event from: " .. stage.zoneName)
|
||||||
|
|
||||||
local anyIncomplete = false
|
local stageIndex = stage:GetStageIndex()
|
||||||
self.logger:debug("Checking stages for index: " .. tostring(currentStage))
|
local laneIdentifier = stage:GetStageLaneIdentifier()
|
||||||
for index, stage in pairs(StagesByIndex[tostring(currentStage)]) do
|
|
||||||
if stage:IsComplete() == false then
|
local stageLane = self._stageRepository:getStageLane(laneIdentifier)
|
||||||
anyIncomplete = true
|
if not stageLane or not stageLane:IsCurrentStageIndexComplete() then
|
||||||
self.logger:debug("Need to wait for Stage " .. stage.zoneName .. " to be completed")
|
self.logger:debug("Stage lane " .. (laneIdentifier or "default") .. " is not complete for stage index " .. stageIndex)
|
||||||
else
|
return
|
||||||
self.logger:debug("Stage verified to be completed: " .. stage.zoneName)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if anyIncomplete == false and self.stageConfig.isAutoStages == true then
|
|
||||||
|
|
||||||
-- CHECK WAITING STAGES
|
local nextStageIndex = stageIndex + 1
|
||||||
local nextStage = currentStage + 1
|
|
||||||
|
Events.PublishStageNumberChanged(nextStageIndex, laneIdentifier)
|
||||||
if WaitingStagesByIndex[tostring(nextStage)] then
|
stageLane:SetActiveStageIndex(nextStageIndex)
|
||||||
for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do
|
|
||||||
if waitingStage:IsActive() == false then
|
if stageLane:IsDefaultStageLane() then
|
||||||
waitingStage:ActivateStage()
|
-- COMPLETION IN THE DEFAULT LANE
|
||||||
|
|
||||||
|
-- 1. Check if any lane is at status "BetweenChapters" and the next chapter start is "next stage index"
|
||||||
|
local allLanes = self._stageRepository:getAllStageLanes()
|
||||||
|
for _, lane in ipairs(allLanes) do
|
||||||
|
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey and lane:GetStageLaneState() == "BetweenChapters" then
|
||||||
|
local nextChapterStart = lane:GetNextChapterStart()
|
||||||
|
if nextChapterStart == nextStageIndex then
|
||||||
|
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is at chapter start for next stage index " .. nextStageIndex)
|
||||||
|
Events.PublishStageNumberChanged(nextStageIndex, lane:GetStageLaneIdentifier())
|
||||||
|
lane:SetActiveStageIndex(nextStageIndex)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
local anyWaiting = false
|
-- COMPLETION IN A SIDE LANE
|
||||||
if WaitingStagesByIndex[tostring(nextStage)] then
|
|
||||||
for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do
|
-- 1. Check if the default lane is at status "BetweenChapters"
|
||||||
if waitingStage:IsComplete() == false then
|
local defaultStageLane = self._stageRepository:getStageLane(StageLane.DefaultLaneKey)
|
||||||
anyWaiting = true
|
if defaultStageLane and defaultStageLane:GetStageLaneState() == "BetweenChapters" then
|
||||||
|
local nextChapterStart = defaultStageLane:GetNextChapterStart()
|
||||||
|
if nextChapterStart == nextStageIndex then
|
||||||
|
|
||||||
|
-- 2. Check if all side lanes are at or beyond the next chapter starts
|
||||||
|
local allLanes = self._stageRepository:getAllStageLanes()
|
||||||
|
local allSideLanesReady = true
|
||||||
|
for _, lane in ipairs(allLanes) do
|
||||||
|
if lane:GetStageLaneIdentifier() ~= StageLane.DefaultLaneKey then
|
||||||
|
-- Check if the lane is at or beyond the next chapter starts when "InChapter" eq "Active"
|
||||||
|
if lane:GetStageLaneState() == "InChapter" and lane:GetActiveStageIndex() < nextStageIndex then
|
||||||
|
allSideLanesReady = false
|
||||||
|
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex)
|
||||||
|
break
|
||||||
|
|
||||||
|
-- When in between chapters, check if the next chapter start is less than the next stage index, meaning there is still stages to be completed before the main lane can be activated again
|
||||||
|
elseif lane:GetStageLaneState() == "BetweenChapters" then
|
||||||
|
local nextChapter = lane:GetNextChapterStart()
|
||||||
|
if nextChapter and nextChapter < nextStageIndex then
|
||||||
|
allSideLanesReady = false
|
||||||
|
self.logger:debug("Lane " .. (lane:GetStageLaneIdentifier() or "default") .. " is not ready for next stage index " .. nextStageIndex)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if allSideLanesReady then
|
||||||
|
self.logger:debug("All side lanes are ready for next stage index " .. nextStageIndex .. ", activating default lane")
|
||||||
|
Events.PublishStageNumberChanged(nextStageIndex, nil)
|
||||||
|
defaultStageLane:SetActiveStageIndex(nextStageIndex)
|
||||||
|
else
|
||||||
|
self.logger:debug("Not all side lanes are ready for next stage index " .. nextStageIndex .. ", default lane will not be activated")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if anyWaiting == false then
|
|
||||||
local newStageNumber = currentStage + 1
|
|
||||||
self.logger:debug("Setting next stage to: " .. tostring(newStageNumber))
|
|
||||||
Events.PublishStageNumberChanged(newStageNumber)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param stageNumber number
|
||||||
|
---@param stageLaneIdentifier string?
|
||||||
|
---@return boolean?
|
||||||
|
function GlobalStageManager:IsStageComplete(stageNumber, stageLaneIdentifier)
|
||||||
|
local stageLane = self._stageRepository:getStageLane(stageLaneIdentifier)
|
||||||
|
if not stageLane then
|
||||||
|
self.logger:warn("Stage lane " .. (stageLaneIdentifier or "default") .. " does not exist")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return stageLane:IsStageIndexComplete(stageNumber)
|
||||||
|
end
|
||||||
|
|
||||||
---@public
|
---@public
|
||||||
function GlobalStageManager:OnStageNumberChanged(stageNumber)
|
function GlobalStageManager:OnStageNumberChanged(stageNumber, stageLaneIdentifier)
|
||||||
|
|
||||||
|
-- only react on "main" lane changes, ignore other lanes for now
|
||||||
|
if stageLaneIdentifier ~= nil then return end
|
||||||
|
|
||||||
self.logger:debug("Stage number changed to: " .. tostring(stageNumber))
|
self.logger:debug("Stage number changed to: " .. tostring(stageNumber))
|
||||||
currentStage = stageNumber
|
|
||||||
|
|
||||||
self:UpdateDrawings(stageNumber)
|
self:UpdateDrawings(stageNumber)
|
||||||
end
|
end
|
||||||
|
|
||||||
function GlobalStageManager:OnStageNumberChangeComplete(stageNumber)
|
function GlobalStageManager:OnStageNumberChangeComplete(stageNumber, stageLaneIdentifier)
|
||||||
|
-- only react on "main" lane changes, ignore other lanes for now
|
||||||
|
if stageLaneIdentifier ~= nil then return end
|
||||||
|
|
||||||
self.logger:debug("Stage number change complete to: " .. tostring(stageNumber))
|
self.logger:debug("Stage number change complete to: " .. tostring(stageNumber))
|
||||||
|
|
||||||
local groups = {}
|
local groups = {}
|
||||||
@@ -236,83 +337,267 @@ function GlobalStageManager:OnStageNumberChangeComplete(stageNumber)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
function GlobalStageManager:UpdateDrawings(stageNumber)
|
---@param stageNumber number
|
||||||
|
---@param stageLaneIdentifier string?
|
||||||
|
function GlobalStageManager:UpdateDrawings(stageNumber, stageLaneIdentifier)
|
||||||
self.logger:debug("Updating custom drawings for stage number: " .. tostring(stageNumber))
|
self.logger:debug("Updating custom drawings for stage number: " .. tostring(stageNumber))
|
||||||
local drawings = self.database:getCustomDrawings()
|
local drawings = self.database:getStageDrawings()
|
||||||
for _, drawing in pairs(drawings) do
|
for _, drawing in pairs(drawings) do
|
||||||
local startStage, stopStage = drawing:GetStartAndStop()
|
local startStage = drawing:GetStartingStage()
|
||||||
if stageNumber >= startStage and stageNumber < stopStage then
|
local stopStage = drawing:GetRemoveAtStage()
|
||||||
self.logger:debug("Drawing " .. drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber))
|
local laneIdentifier = drawing:GetStageLaneIdentifier()
|
||||||
drawing:Draw()
|
|
||||||
else
|
if laneIdentifier == stageLaneIdentifier then
|
||||||
self.logger:debug("Drawing " .. drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber))
|
if stageNumber >= startStage and stageNumber < stopStage then
|
||||||
drawing:Remove()
|
self.logger:debug("Drawing " .. drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber))
|
||||||
|
drawing:Draw()
|
||||||
|
else
|
||||||
|
self.logger:debug("Drawing " .. drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber))
|
||||||
|
drawing:Remove()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
GlobalStageManager.printFullOverview = function ()
|
function GlobalStageManager:PrintMermaidStage()
|
||||||
|
local lanes = self._stageRepository:getAllStageLanes()
|
||||||
|
|
||||||
local logger = Logger.new("StageOverview", "INFO")
|
local nodes = {}
|
||||||
logger:info("Stage overview:")
|
local edges = {}
|
||||||
|
local stageIndicesByLane = {} -- Track all stage indices per lane
|
||||||
local max = 0
|
local mainLaneId = "default"
|
||||||
local lines = {}
|
|
||||||
for stageIndex, stages in pairs(StagesByIndex) do
|
-- Color palette for lanes (26 vibrant colors optimized for dark mode)
|
||||||
|
local laneColors = {
|
||||||
|
"#FF6B6B", -- 1: Red
|
||||||
|
"#4ECDC4", -- 2: Teal
|
||||||
|
"#45B7D1", -- 3: Blue
|
||||||
|
"#FFA502", -- 4: Orange
|
||||||
|
"#95E1D3", -- 5: Mint
|
||||||
|
"#F38181", -- 6: Coral
|
||||||
|
"#AA96DA", -- 7: Purple
|
||||||
|
"#FCBAD3", -- 8: Pink
|
||||||
|
"#A8E6CF", -- 9: Light green
|
||||||
|
"#FFD3B6", -- 10: Peach
|
||||||
|
"#FFAAA5", -- 11: Light red
|
||||||
|
"#FF8B94", -- 12: Rose
|
||||||
|
"#FFEAA7", -- 13: Butter
|
||||||
|
"#DFE6E9", -- 14: Gray
|
||||||
|
"#00B894", -- 15: Emerald
|
||||||
|
"#0984E3", -- 16: Cobalt
|
||||||
|
"#6C5CE7", -- 17: Indigo
|
||||||
|
"#A29BFE", -- 18: Lavender
|
||||||
|
"#FD79A8", -- 19: Magenta
|
||||||
|
"#FDCB6E", -- 20: Gold
|
||||||
|
"#6C757D", -- 21: Slate
|
||||||
|
"#20C997", -- 22: Seafoam
|
||||||
|
"#E74C3C", -- 23: Scarlet
|
||||||
|
"#3498DB", -- 24: Dodger blue
|
||||||
|
"#9B59B6", -- 25: Amethyst
|
||||||
|
"#1ABC9C", -- 26: Turquoise
|
||||||
|
}
|
||||||
|
local laneColorMap = {} -- Map lane ID to color
|
||||||
|
local colorIndex = 1
|
||||||
|
|
||||||
|
-- Build nodes and collect all stage indices per lane
|
||||||
|
for _, lane in ipairs(lanes) do
|
||||||
|
local laneId = lane:GetStageLaneIdentifier() or "default"
|
||||||
|
local stageIndices = lane:GetAllStageIndices()
|
||||||
|
stageIndicesByLane[laneId] = stageIndices
|
||||||
|
|
||||||
local totalStrike = 0
|
-- Assign color to this lane
|
||||||
local totalbai = 0
|
laneColorMap[laneId] = laneColors[colorIndex]
|
||||||
local totaldead = 0
|
colorIndex = colorIndex + 1
|
||||||
local totalMissions = 0
|
if colorIndex > #laneColors then
|
||||||
local totalCas = 0
|
colorIndex = 1
|
||||||
|
end
|
||||||
for _, stage in pairs(stages) do
|
|
||||||
|
-- Create nodes for each stage index in this lane
|
||||||
|
for _, stageIndex in ipairs(stageIndices) do
|
||||||
|
local stages = lane:GetStagesAtIndex(stageIndex)
|
||||||
|
|
||||||
local strike, dead, bai, cas = stage:GetStageStats()
|
if stages then
|
||||||
|
for _, stage in ipairs(stages) do
|
||||||
totalStrike = totalStrike + strike
|
local nodeId = laneId .. "_" .. stageIndex
|
||||||
totalbai = totalbai + bai
|
local stageType = stage:GetStageType()
|
||||||
totaldead = totaldead + dead
|
local stageName = stage.stageName or stage.zoneName
|
||||||
totalCas = totalCas + cas
|
|
||||||
totalMissions = totalMissions + strike + dead + bai + cas
|
-- Generate bracket label: [1], [2] for default, [w1], [e2] for other lanes
|
||||||
end
|
local bracketLabel
|
||||||
|
if laneId == "default" then
|
||||||
local index = tonumber(stageIndex)
|
bracketLabel = "[" .. stageIndex .. "]"
|
||||||
if index then
|
else
|
||||||
if index > max then
|
bracketLabel = "[" .. laneId .. stageIndex .. "]"
|
||||||
max = index
|
end
|
||||||
|
|
||||||
|
-- Combined label: [bracket] Name
|
||||||
|
local label = bracketLabel .. " " .. stageName
|
||||||
|
|
||||||
|
-- Different node shapes for different stage types
|
||||||
|
local nodeShape = "["
|
||||||
|
local nodeEnd = "]"
|
||||||
|
if stageType == "ExtraStage" then
|
||||||
|
nodeShape = "(["
|
||||||
|
nodeEnd = "])"
|
||||||
|
elseif stageType == "WaitingStage" then
|
||||||
|
nodeShape = "[["
|
||||||
|
nodeEnd = "]]"
|
||||||
|
end
|
||||||
|
|
||||||
|
nodes[nodeId] = string.format(' %s%s"%s"%s',
|
||||||
|
nodeId, nodeShape, label, nodeEnd)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
lines[index] ="Stage# " .. tostring(stageIndex).. " | " .. totalStrike .. " strikes | " .. totaldead .. " dead | " .. totalbai .. " BAI | " .. totalCas .. " CAS | Total:" .. totalMissions
|
|
||||||
else
|
|
||||||
logger:warn("Stage index is not a number: " .. stageIndex)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, max do
|
-- Add edges for sequential stages within the same lane
|
||||||
if lines[i] then
|
for laneId, stageIndices in pairs(stageIndicesByLane) do
|
||||||
logger:info(lines[i])
|
for i = 1, #stageIndices - 1 do
|
||||||
|
local fromIdx = stageIndices[i]
|
||||||
|
local toIdx = stageIndices[i + 1]
|
||||||
|
local fromNodeId = laneId .. "_" .. fromIdx
|
||||||
|
local toNodeId = laneId .. "_" .. toIdx
|
||||||
|
|
||||||
|
-- Check if this is a chapter boundary (gap)
|
||||||
|
local isChapter = false
|
||||||
|
for _, lane in ipairs(lanes) do
|
||||||
|
local normalizedLaneId = lane:GetStageLaneIdentifier() or "default"
|
||||||
|
if normalizedLaneId == laneId then
|
||||||
|
isChapter = lane:IsChapterStart(toIdx)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local label = isChapter and "|chapter|" or ""
|
||||||
|
table.insert(edges, string.format(' %s -->%s %s', fromNodeId, label, toNodeId))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get main lane reference
|
||||||
|
local mainStageLane = nil
|
||||||
|
for _, lane in ipairs(lanes) do
|
||||||
|
if lane:IsDefaultStageLane() then
|
||||||
|
mainStageLane = lane
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add dependencies from DEFAULT lane to SIDE lanes at chapter starts
|
||||||
|
-- Side lanes activate when main completes the stage BEFORE the chapter start
|
||||||
|
if mainStageLane then
|
||||||
|
for _, lane in ipairs(lanes) do
|
||||||
|
if lane:IsDefaultStageLane() == false then
|
||||||
|
local sideId = lane:GetStageLaneIdentifier()
|
||||||
|
local sideIndices = stageIndicesByLane[sideId]
|
||||||
|
|
||||||
|
-- For each stage in the side lane
|
||||||
|
for _, stageIdx in ipairs(sideIndices) do
|
||||||
|
-- Check if this is a chapter start in the side lane
|
||||||
|
if lane:IsChapterStart(stageIdx) then
|
||||||
|
-- Main lane must complete (stageIdx - 1) to activate side lane at stageIdx
|
||||||
|
local fromNodeId = mainLaneId .. "_" .. (stageIdx - 1)
|
||||||
|
local toNodeId = sideId .. "_" .. stageIdx
|
||||||
|
-- Side lane activates when main lane completes the prior stage
|
||||||
|
table.insert(edges, string.format(' %s -->|unlock| %s', fromNodeId, toNodeId))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add dependencies from SIDE lanes gating the MAIN lane at chapter boundaries
|
||||||
|
-- Main cannot advance to the next chapter until ALL side lanes are at or above that chapter index
|
||||||
|
if mainStageLane then
|
||||||
|
local mainIndices = stageIndicesByLane[mainLaneId]
|
||||||
|
|
||||||
|
-- For each chapter start in main lane (skip first one)
|
||||||
|
for i = 2, #mainIndices do
|
||||||
|
local nextIdx = mainIndices[i]
|
||||||
|
|
||||||
|
-- Check if nextIdx is a chapter start (there's a gap before it)
|
||||||
|
if mainStageLane:IsChapterStart(nextIdx) then
|
||||||
|
-- Before main can progress to nextIdx, all side lanes must be at >= nextIdx
|
||||||
|
for _, lane in ipairs(lanes) do
|
||||||
|
if lane:IsDefaultStageLane() == false then
|
||||||
|
local sideId = lane:GetStageLaneIdentifier()
|
||||||
|
local sideIndices = stageIndicesByLane[sideId]
|
||||||
|
|
||||||
|
-- Find the appropriate gate node for this side lane
|
||||||
|
-- Use first stage >= nextIdx if it exists, otherwise use the highest stage
|
||||||
|
local gateStageIdx = nil
|
||||||
|
|
||||||
|
for _, idx in ipairs(sideIndices) do
|
||||||
|
if idx >= nextIdx then
|
||||||
|
gateStageIdx = idx
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If no stage >= nextIdx, use the highest stage in this lane
|
||||||
|
if gateStageIdx == nil and #sideIndices > 0 then
|
||||||
|
gateStageIdx = sideIndices[#sideIndices]
|
||||||
|
end
|
||||||
|
|
||||||
|
if gateStageIdx then
|
||||||
|
local fromNodeId = sideId .. "_" .. gateStageIdx
|
||||||
|
local toNodeId = mainLaneId .. "_" .. nextIdx
|
||||||
|
table.insert(edges, string.format(' %s -->|gate| %s', fromNodeId, toNodeId))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Build complete Mermaid diagram
|
||||||
|
local diagramLines = {"graph TD"}
|
||||||
|
|
||||||
|
-- Add class definitions for each lane with colors and contrasting text
|
||||||
|
for laneId, color in pairs(laneColorMap) do
|
||||||
|
table.insert(diagramLines, string.format(' classDef lane_%s fill:%s,stroke:#333,stroke-width:2px,color:#000', laneId, color))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add nodes
|
||||||
|
for _, nodeStr in pairs(nodes) do
|
||||||
|
table.insert(diagramLines, nodeStr)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add edges
|
||||||
|
for _, edgeStr in pairs(edges) do
|
||||||
|
table.insert(diagramLines, edgeStr)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Apply classes to nodes
|
||||||
|
for nodeId in pairs(nodes) do
|
||||||
|
local laneId = nodeId:match("(.+)_[0-9]+$")
|
||||||
|
if laneId then
|
||||||
|
table.insert(diagramLines, string.format(' class %s lane_%s', nodeId, laneId))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Print as single multi-line message
|
||||||
|
local diagram = "========== STAGE FLOW DIAGRAM ==========\n" .. table.concat(diagramLines, "\n") .. "\n========== END DIAGRAM =========="
|
||||||
|
self.logger:info(diagram)
|
||||||
end
|
end
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param stageNumber number
|
---@param stageNumber number
|
||||||
|
---@param stageLaneIdentifier string? nil for default lan
|
||||||
---@return boolean | nil
|
---@return boolean | nil
|
||||||
GlobalStageManager.isStageComplete = function (stageNumber)
|
GlobalStageManager.isStageComplete = function (stageNumber, stageLaneIdentifier)
|
||||||
|
|
||||||
local stageIndex = tostring(stageNumber)
|
|
||||||
|
|
||||||
if StagesByIndex[stageIndex] == nil then return nil end
|
|
||||||
|
|
||||||
for _, stage in ipairs(StagesByIndex[stageIndex]) do
|
if singletonInstance == nil then
|
||||||
if stage:IsComplete() == false then
|
Logger.new("StageManager", "INFO"):warn("GlobalStageManager.isStageComplete called before GlobalStageManager was initialized. Returning nil")
|
||||||
return false
|
return nil
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
if stageLaneIdentifier then
|
||||||
|
stageLaneIdentifier = string.lower(stageLaneIdentifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
return singletonInstance:IsStageComplete(stageNumber, stageLaneIdentifier)
|
||||||
end
|
end
|
||||||
|
|
||||||
return GlobalStageManager
|
return GlobalStageManager
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ function BlueSam.New(database, logger, zoneName, spawnManager)
|
|||||||
|
|
||||||
local zone = DcsUtil.getZoneByName(zoneName)
|
local zone = DcsUtil.getZoneByName(zoneName)
|
||||||
if zone then
|
if zone then
|
||||||
BuildableZone.New(self, zone, self._buildableCrateKilos or 0, "SAM_CRATE", self._blueGroups, logger, database)
|
BuildableZone.New(self, zone, self._buildableCrateKilos or 0, "SAM_CRATE", self._blueGroups, logger, database, blueSamData.briefing)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager)
|
|||||||
local zone = DcsUtil.getZoneByName(zoneName)
|
local zone = DcsUtil.getZoneByName(zoneName)
|
||||||
if zone then
|
if zone then
|
||||||
self._logger:debug("Creating Buildable zone: " .. zoneName .. " with " .. (farpData.buildingKilos or "nil") .. " kilos")
|
self._logger:debug("Creating Buildable zone: " .. zoneName .. " with " .. (farpData.buildingKilos or "nil") .. " kilos")
|
||||||
BuildableZone.New(self, zone, farpData.buildingKilos or 0, "FARP_CRATE", self._groups, logger, database)
|
BuildableZone.New(self, zone, farpData.buildingKilos or 0, "FARP_CRATE", self._groups, logger, database, farpData.briefing)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:Deactivate()
|
self:Deactivate()
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ BuildableZone.__index = BuildableZone
|
|||||||
---@param database Database
|
---@param database Database
|
||||||
---@param crateType SupplyType
|
---@param crateType SupplyType
|
||||||
---@param logger Logger
|
---@param logger Logger
|
||||||
function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroups, logger, database)
|
---@param briefing string?
|
||||||
|
function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroups, logger, database, briefing)
|
||||||
self._targetZone = targetZone
|
self._targetZone = targetZone
|
||||||
self._requiredKilos = kilosRequired or 0
|
self._requiredKilos = kilosRequired or 0
|
||||||
self._buildableGroups = buildableGroups or {}
|
self._buildableGroups = buildableGroups or {}
|
||||||
@@ -69,7 +70,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup
|
|||||||
|
|
||||||
local noLandingZone = self:GetNoLandingZone()
|
local noLandingZone = self:GetNoLandingZone()
|
||||||
if kilosRequired and kilosRequired > 0 then
|
if kilosRequired and kilosRequired > 0 then
|
||||||
self._buildableMission = BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType)
|
self._buildableMission = BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType, briefing)
|
||||||
self._buildableMission:AddOnCrateDroppedOfListener(self)
|
self._buildableMission:AddOnCrateDroppedOfListener(self)
|
||||||
else
|
else
|
||||||
self._buildableMission = nil
|
self._buildableMission = nil
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---@class StageLane
|
||||||
|
---@field private _isDefaultStageLane boolean
|
||||||
|
---@field private _stageLaneIdentifier string
|
||||||
|
---@field private _activeStageIndex number
|
||||||
|
---@field private _stagesInLaneByIndex table<number, Array<Stage>>
|
||||||
|
---@field private _chapterStarts table<number, boolean>?
|
||||||
|
---@field private _stageLaneState StageLaneState
|
||||||
|
---@field private _maxStageIndex number
|
||||||
|
local StageLane = {}
|
||||||
|
StageLane.__index = StageLane
|
||||||
|
|
||||||
|
---@alias StageLaneState
|
||||||
|
---| "InChapter"
|
||||||
|
---| "BetweenChapters"
|
||||||
|
---| "Completed"
|
||||||
|
|
||||||
|
StageLane.DefaultLaneKey = nil
|
||||||
|
|
||||||
|
function StageLane.New(laneIdentifier)
|
||||||
|
|
||||||
|
if not laneIdentifier then
|
||||||
|
laneIdentifier = StageLane.DefaultLaneKey
|
||||||
|
end
|
||||||
|
|
||||||
|
local self = setmetatable({}, { __index = StageLane }) --[[@as StageLane]]
|
||||||
|
self._isDefaultStageLane = laneIdentifier == StageLane.DefaultLaneKey
|
||||||
|
self._stageLaneIdentifier = laneIdentifier
|
||||||
|
self._activeStageIndex = nil
|
||||||
|
self._stagesInLaneByIndex = {}
|
||||||
|
self._chapterStarts = nil
|
||||||
|
self._stageLaneState = "BetweenChapters"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stage Stage
|
||||||
|
function StageLane:AddStage(stage)
|
||||||
|
local stageIndex = stage:GetStageIndex()
|
||||||
|
if not self._stagesInLaneByIndex[stageIndex] then
|
||||||
|
self._stagesInLaneByIndex[stageIndex] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self._maxStageIndex or stageIndex > self._maxStageIndex then
|
||||||
|
self._maxStageIndex = stageIndex
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self._stagesInLaneByIndex[stageIndex], stage)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stageNumber number
|
||||||
|
---@return boolean?
|
||||||
|
function StageLane:IsStageIndexComplete(stageNumber)
|
||||||
|
local stages = self._stagesInLaneByIndex[stageNumber]
|
||||||
|
if not stages then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, stage in ipairs(stages) do
|
||||||
|
if not stage:IsComplete() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function StageLane:IsCurrentStageIndexComplete()
|
||||||
|
return self:IsStageIndexComplete(self._activeStageIndex) == true
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function StageLane:GetStageLaneIdentifier()
|
||||||
|
return self._stageLaneIdentifier
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function StageLane:IsDefaultStageLane()
|
||||||
|
return self._isDefaultStageLane
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return number
|
||||||
|
function StageLane:GetActiveStageIndex()
|
||||||
|
return self._activeStageIndex
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stageNumber number
|
||||||
|
function StageLane:SetActiveStageIndex(stageNumber)
|
||||||
|
|
||||||
|
if self._stagesInLaneByIndex[stageNumber] == nil then
|
||||||
|
self._stageLaneState = "BetweenChapters" -- stage number is not in this lane, so we are between chapters
|
||||||
|
elseif stageNumber > self._maxStageIndex then
|
||||||
|
self._stageLaneState = "Completed" -- stage number is beyond the max stage index
|
||||||
|
else
|
||||||
|
self._stageLaneState = "InChapter"
|
||||||
|
end
|
||||||
|
|
||||||
|
self._activeStageIndex = stageNumber
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return StageLaneState
|
||||||
|
function StageLane:GetStageLaneState()
|
||||||
|
return self._stageLaneState
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks if the given stage number is a chapter start. <br>
|
||||||
|
--- Chapter starts are the first StageIndex after a gap in stage numbers. <br>
|
||||||
|
--- For example, if the stage numbers are 1, 2, 3, 5, 6, 7, then stage number 5 is a chapter start because there is a gap between 3 and 5. <br>
|
||||||
|
---@param stageNumber number
|
||||||
|
function StageLane:IsChapterStart(stageNumber)
|
||||||
|
|
||||||
|
if self._chapterStarts == nil then
|
||||||
|
self:FillChapterStarts()
|
||||||
|
end
|
||||||
|
|
||||||
|
return self._chapterStarts[tostring(stageNumber)] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- fills chapter starts, chapter starts are where there's a gap in between stage numbers.
|
||||||
|
--- The first stage in a lane is always a chapter start.
|
||||||
|
---@private
|
||||||
|
function StageLane:FillChapterStarts()
|
||||||
|
|
||||||
|
local previousIndex = nil
|
||||||
|
local stageIndices = {}
|
||||||
|
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
|
||||||
|
table.insert(stageIndices, tonumber(stageIndex))
|
||||||
|
end
|
||||||
|
table.sort(stageIndices)
|
||||||
|
|
||||||
|
self._chapterStarts = self._chapterStarts or {}
|
||||||
|
for _, stageIndex in ipairs(stageIndices) do
|
||||||
|
if previousIndex == nil then
|
||||||
|
-- First stage is always a chapter start
|
||||||
|
self._chapterStarts[tostring(stageIndex)] = true
|
||||||
|
elseif stageIndex > previousIndex + 1 then
|
||||||
|
-- Gap before this stage means it's a chapter start
|
||||||
|
self._chapterStarts[tostring(stageIndex)] = true
|
||||||
|
else
|
||||||
|
-- Consecutive stage, not a chapter start
|
||||||
|
self._chapterStarts[tostring(stageIndex)] = false
|
||||||
|
end
|
||||||
|
previousIndex = stageIndex
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Array<number>
|
||||||
|
function StageLane:GetAllStageIndices()
|
||||||
|
local indices = {}
|
||||||
|
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
|
||||||
|
table.insert(indices, tonumber(stageIndex))
|
||||||
|
end
|
||||||
|
table.sort(indices)
|
||||||
|
return indices
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stageIndex number
|
||||||
|
---@return Array<Stage>?
|
||||||
|
function StageLane:GetStagesAtIndex(stageIndex)
|
||||||
|
return self._stagesInLaneByIndex[stageIndex]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return number?
|
||||||
|
function StageLane:GetNextChapterStart()
|
||||||
|
if self._chapterStarts == nil then
|
||||||
|
self:FillChapterStarts()
|
||||||
|
end
|
||||||
|
|
||||||
|
for stageIndex, _ in pairs(self._stagesInLaneByIndex) do
|
||||||
|
if tonumber(stageIndex) > self._activeStageIndex and self._chapterStarts[tostring(stageIndex)] == true then
|
||||||
|
return tonumber(stageIndex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return StageLane
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
local StageLane = require("classes.stageClasses.StageLane")
|
||||||
|
|
||||||
|
---@class StageRepository
|
||||||
|
---@field private StageLanes table<string, StageLane>
|
||||||
|
local StageRepository = {}
|
||||||
|
StageRepository.__index = StageRepository
|
||||||
|
|
||||||
|
local instance = nil
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@return StageRepository
|
||||||
|
function StageRepository.new()
|
||||||
|
local self = setmetatable({}, StageRepository)
|
||||||
|
self.StageLanes = {}
|
||||||
|
self.StageLanes[tostring(StageLane.DefaultLaneKey)] = StageLane.New(StageLane.DefaultLaneKey)
|
||||||
|
instance = self
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return StageRepository
|
||||||
|
StageRepository.getInstance = function()
|
||||||
|
if instance == nil then
|
||||||
|
instance = StageRepository.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
return instance
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param laneName string? `nil` for default lane
|
||||||
|
---@return StageLane?
|
||||||
|
function StageRepository:getStageLane(laneName)
|
||||||
|
if not laneName then
|
||||||
|
laneName = StageLane.DefaultLaneKey
|
||||||
|
end
|
||||||
|
return self.StageLanes[tostring(laneName)]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Array<StageLane>
|
||||||
|
function StageRepository:getAllStageLanes()
|
||||||
|
local lanes = {}
|
||||||
|
for _, lane in pairs(self.StageLanes) do
|
||||||
|
table.insert(lanes, lane)
|
||||||
|
end
|
||||||
|
return lanes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stage Stage
|
||||||
|
function StageRepository:AddStage(stage)
|
||||||
|
|
||||||
|
local stageLaneIdentifier = stage:GetStageLaneIdentifier() or StageLane.DefaultLaneKey
|
||||||
|
|
||||||
|
if self.StageLanes[tostring(stageLaneIdentifier)] == nil then
|
||||||
|
self.StageLanes[tostring(stageLaneIdentifier)] = StageLane.New(stageLaneIdentifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.StageLanes[tostring(stageLaneIdentifier)]:AddStage(stage)
|
||||||
|
end
|
||||||
|
|
||||||
|
return StageRepository
|
||||||
@@ -13,12 +13,19 @@ local Events = require("classes.spearhead_events")
|
|||||||
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
||||||
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
||||||
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
|
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
|
||||||
|
local StageRepository = require("classes.stageClasses.StageRepository")
|
||||||
|
local StageState = require("classes.stageClasses.Stages.BaseStage.StageState")
|
||||||
|
|
||||||
---@alias StageColor
|
---@alias StageColor
|
||||||
---| "RED"
|
---| "RED"
|
||||||
---| "BLUE"
|
---| "BLUE"
|
||||||
---| "GRAY"
|
---| "GRAY"
|
||||||
|
|
||||||
|
---@alias StageType
|
||||||
|
---| "PrimaryStage"
|
||||||
|
---| "ExtraStage"
|
||||||
|
---| "WaitingStage"
|
||||||
|
|
||||||
--- @class StageData
|
--- @class StageData
|
||||||
--- @field stageBriefing string?
|
--- @field stageBriefing string?
|
||||||
--- @field missionsByCode table<string, Mission>
|
--- @field missionsByCode table<string, Mission>
|
||||||
@@ -33,9 +40,11 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
|
|||||||
--- @field supplyHubs Array<SupplyHub>
|
--- @field supplyHubs Array<SupplyHub>
|
||||||
|
|
||||||
--- @class StageInitData
|
--- @class StageInitData
|
||||||
|
--- @field type StageType
|
||||||
--- @field stageZoneName string
|
--- @field stageZoneName string
|
||||||
--- @field stageNumber integer
|
--- @field stageNumber integer
|
||||||
--- @field stageDisplayName string
|
--- @field stageDisplayName string
|
||||||
|
--- @field stageLaneIdentifier string?
|
||||||
|
|
||||||
|
|
||||||
--- @class StageCompleteListener
|
--- @class StageCompleteListener
|
||||||
@@ -45,13 +54,15 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
|
|||||||
--- @field zoneName string
|
--- @field zoneName string
|
||||||
--- @field stageName string?
|
--- @field stageName string?
|
||||||
--- @field stageNumber number
|
--- @field stageNumber number
|
||||||
|
--- @field protected _stageLaneIdentifier string?
|
||||||
|
--- @field protected _stageType StageType
|
||||||
--- @field protected _currentStageState CurrentStageState
|
--- @field protected _currentStageState CurrentStageState
|
||||||
--- @field protected _missionCommandsHelper MissionCommandsHelper
|
--- @field protected _missionCommandsHelper MissionCommandsHelper
|
||||||
--- @field protected _missionPriority MissionPriority
|
--- @field protected _missionPriority MissionPriority
|
||||||
--- @field protected _database Database
|
--- @field protected _database Database
|
||||||
|
--- @field protected _stageRepository StageRepository
|
||||||
--- @field protected _db StageData
|
--- @field protected _db StageData
|
||||||
--- @field protected _logger Logger
|
--- @field protected _logger Logger
|
||||||
--- @field protected _activeStage integer
|
|
||||||
--- @field protected _stageConfig StageConfig
|
--- @field protected _stageConfig StageConfig
|
||||||
--- @field protected _customDrawing CustomDrawing
|
--- @field protected _customDrawing CustomDrawing
|
||||||
--- @field protected _spawnedGroups Array<string>
|
--- @field protected _spawnedGroups Array<string>
|
||||||
@@ -63,14 +74,6 @@ local Stage = {}
|
|||||||
|
|
||||||
Stage.__index = Stage
|
Stage.__index = Stage
|
||||||
|
|
||||||
---@enum CurrentStageState
|
|
||||||
Stage.CurrentStageState = {
|
|
||||||
INACTIVE = 0,
|
|
||||||
PREACTIVATED = 1,
|
|
||||||
ACTIVE = 2,
|
|
||||||
BLUE = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
Stage.StageColors = {
|
Stage.StageColors = {
|
||||||
INVISIBLE = { 0, 0, 0, 0 },
|
INVISIBLE = { 0, 0, 0, 0 },
|
||||||
RED_ACTIVE = { 1, 0, 0, 0.20 },
|
RED_ACTIVE = { 1, 0, 0, 0.20 },
|
||||||
@@ -86,15 +89,22 @@ Stage.StageColors = {
|
|||||||
---@param initData StageInitData
|
---@param initData StageInitData
|
||||||
---@param missionPriority MissionPriority
|
---@param missionPriority MissionPriority
|
||||||
---@param spawnManager SpawnManager
|
---@param spawnManager SpawnManager
|
||||||
|
---@param stageType StageType
|
||||||
---@return Stage
|
---@return Stage
|
||||||
function Stage:superNew(database, stageConfig, logger, initData, missionPriority, spawnManager)
|
function Stage:superNew(database, stageConfig, logger, initData, stageType, missionPriority, spawnManager)
|
||||||
|
|
||||||
logger:debug("[BaseStage] Initiating stage with name: " .. initData.stageZoneName)
|
logger:debug("[BaseStage] Initiating stage with name: " .. initData.stageZoneName)
|
||||||
self._currentStageState = Stage.CurrentStageState.INACTIVE
|
self._currentStageState = StageState.Inactive
|
||||||
self.zoneName = initData.stageZoneName
|
self.zoneName = initData.stageZoneName
|
||||||
self.stageNumber = initData.stageNumber
|
self.stageNumber = initData.stageNumber
|
||||||
|
self._stageRepository = StageRepository.getInstance()
|
||||||
|
if initData.stageLaneIdentifier then
|
||||||
|
self._stageLaneIdentifier = string.lower(initData.stageLaneIdentifier)
|
||||||
|
else
|
||||||
|
self._stageLaneIdentifier = nil
|
||||||
|
end
|
||||||
self.stageName = initData.stageDisplayName
|
self.stageName = initData.stageDisplayName
|
||||||
self._currentStageState = Stage.CurrentStageState.INACTIVE
|
self._stageType = stageType
|
||||||
|
|
||||||
self.OnPostStageComplete = nil
|
self.OnPostStageComplete = nil
|
||||||
self.OnPostBlueActivated = nil
|
self.OnPostBlueActivated = nil
|
||||||
@@ -115,7 +125,6 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
|
|||||||
supplyHubs = {}
|
supplyHubs = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self._activeStage = -99
|
|
||||||
self._stageConfig = stageConfig or {}
|
self._stageConfig = stageConfig or {}
|
||||||
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
self._missionCommandsHelper = MissionCommandsHelper.getOrCreate()
|
||||||
|
|
||||||
@@ -273,7 +282,7 @@ end
|
|||||||
|
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function Stage:IsComplete()
|
function Stage:IsComplete()
|
||||||
if self._currentStageState == Stage.CurrentStageState.BLUE then return true end
|
if self._currentStageState >= StageState.Blue then return true end
|
||||||
|
|
||||||
for i, mission in pairs(self._db.sams) do
|
for i, mission in pairs(self._db.sams) do
|
||||||
local state = mission:getState()
|
local state = mission:getState()
|
||||||
@@ -288,14 +297,44 @@ function Stage:IsComplete()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self._currentStageState = Stage.CurrentStageState.BLUE
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function Stage:IsActive()
|
function Stage:IsActive()
|
||||||
return self._currentStageState == Stage.CurrentStageState.ACTIVE
|
return self._currentStageState == StageState.Activated
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return StageType
|
||||||
|
function Stage:GetStageType()
|
||||||
|
return self._stageType
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function Stage:GetStageLaneIdentifier()
|
||||||
|
return self._stageLaneIdentifier
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return number
|
||||||
|
function Stage:GetStageIndex()
|
||||||
|
return self.stageNumber
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string
|
||||||
|
function Stage:GetStageName()
|
||||||
|
return self.stageName
|
||||||
|
end
|
||||||
|
|
||||||
|
function Stage:GetMissions()
|
||||||
|
local missions = {}
|
||||||
|
for _, mission in pairs(self._db.missions) do
|
||||||
|
table.insert(missions, mission)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, sam in pairs(self._db.sams) do
|
||||||
|
table.insert(missions, sam)
|
||||||
|
end
|
||||||
|
return missions
|
||||||
end
|
end
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
@@ -358,7 +397,7 @@ function Stage:IsMissionComplete(missionName)
|
|||||||
return mission:getState() == "COMPLETED"
|
return mission:getState() == "COMPLETED"
|
||||||
end
|
end
|
||||||
|
|
||||||
---private use only
|
-- private usage only
|
||||||
function Stage:NotifyComplete()
|
function Stage:NotifyComplete()
|
||||||
|
|
||||||
self._logger:info("Stage complete: " .. (self.stageName or self.stageNumber or "unknown"))
|
self._logger:info("Stage complete: " .. (self.stageName or self.stageNumber or "unknown"))
|
||||||
@@ -381,19 +420,28 @@ end
|
|||||||
|
|
||||||
---Activates all SAMS, Airbase units etc all at once.
|
---Activates all SAMS, Airbase units etc all at once.
|
||||||
function Stage:PreActivate()
|
function Stage:PreActivate()
|
||||||
if self._currentStageState == Stage.CurrentStageState.INACTIVE then
|
|
||||||
self._currentStageState = Stage.CurrentStageState.PREACTIVATED
|
|
||||||
for key, mission in pairs(self._db.sams) do
|
|
||||||
if mission then
|
|
||||||
mission:SpawnInactive()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, airbase in pairs(self._db.airbases) do
|
if self._currentStageState >= StageState.PreActivated then
|
||||||
airbase:ActivateRedStage()
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self._currentStageState = StageState.PreActivated
|
||||||
|
for key, mission in pairs(self._db.sams) do
|
||||||
|
if mission then
|
||||||
|
mission:SpawnInactive()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for key, mission in pairs(self._db.missions) do
|
||||||
|
if mission and mission.missionType == "DEEPSTRIKE" then
|
||||||
|
mission:SpawnActive()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, airbase in pairs(self._db.airbases) do
|
||||||
|
airbase:ActivateRedStage()
|
||||||
|
end
|
||||||
|
|
||||||
self:MarkStage()
|
self:MarkStage()
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -407,22 +455,22 @@ function Stage:MarkStage()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self._stageConfig.isDrawStagesEnabled == false then return end
|
if self._stageConfig.isDrawStagesEnabled == false then return end
|
||||||
if self._stageConfig.isDrawPreActivatedEnabled == false and self._currentStageState == Stage.CurrentStageState.PREACTIVATED then
|
if self._stageConfig.isDrawPreActivatedEnabled == false and self._currentStageState == StageState.PreActivated then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if self._customDrawing then
|
if self._customDrawing then
|
||||||
self._customDrawing:UpdateDrawingObject(function(drawingObject)
|
self._customDrawing:UpdateDrawingObject(function(drawingObject)
|
||||||
local drawing = drawingObject --[[@as Polygon]]
|
local drawing = drawingObject --[[@as Polygon]]
|
||||||
if self._currentStageState == Stage.CurrentStageState.ACTIVE then
|
if self._currentStageState == StageState.Activated then
|
||||||
drawing.fillColorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_ACTIVE)
|
drawing.fillColorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_ACTIVE)
|
||||||
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_ACTIVE)
|
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_ACTIVE)
|
||||||
drawing.style = "dot dash"
|
drawing.style = "dot dash"
|
||||||
elseif self._currentStageState == Stage.CurrentStageState.PREACTIVATED then
|
elseif self._currentStageState == StageState.PreActivated then
|
||||||
drawing.fillColorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_PREACTIVE)
|
drawing.fillColorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_PREACTIVE)
|
||||||
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_PREACTIVE)
|
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.RED_PREACTIVE)
|
||||||
drawing.style = "no line"
|
drawing.style = "no line"
|
||||||
elseif self._currentStageState == Stage.CurrentStageState.BLUE then
|
elseif self._currentStageState == StageState.Blue then
|
||||||
drawing.fillColorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.BLUE)
|
drawing.fillColorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.BLUE)
|
||||||
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.BLUE)
|
drawing.colorString = DrawingHelper.ColorTableToColorString(Stage.StageColors.BLUE)
|
||||||
drawing.style = "two dash"
|
drawing.style = "two dash"
|
||||||
@@ -440,10 +488,13 @@ function Stage:MarkStage()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Stage:ActivateStage()
|
function Stage:ActivateStage()
|
||||||
self._currentStageState = Stage.CurrentStageState.ACTIVE
|
|
||||||
|
if self._currentStageState >= StageState.Activated then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
self:PreActivate()
|
self:PreActivate()
|
||||||
|
self._currentStageState = StageState.Activated
|
||||||
pcall(function()
|
pcall(function()
|
||||||
self:MarkStage()
|
self:MarkStage()
|
||||||
end)
|
end)
|
||||||
@@ -471,6 +522,10 @@ function Stage:ActivateStage()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self._db and self._db.stageBriefing then
|
||||||
|
self._missionCommandsHelper:AddStageBriefing(self.zoneName, self._db.stageBriefing)
|
||||||
|
end
|
||||||
|
|
||||||
timer.scheduleFunction(self.CheckContinuousAsync, self, timer.getTime() + 3)
|
timer.scheduleFunction(self.CheckContinuousAsync, self, timer.getTime() + 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -483,43 +538,60 @@ end
|
|||||||
---comment
|
---comment
|
||||||
---@param self Stage
|
---@param self Stage
|
||||||
---@param number integer
|
---@param number integer
|
||||||
function Stage:OnStageNumberChanged(number)
|
---@param stageLaneIdentifier string?
|
||||||
|
function Stage:OnStageNumberChanged(number, stageLaneIdentifier)
|
||||||
|
|
||||||
if self._activeStage == number then --only activate once for a stage
|
---@return boolean
|
||||||
return
|
local needsPreActivation = function()
|
||||||
|
if self._stageLaneIdentifier == stageLaneIdentifier then
|
||||||
|
if self.stageNumber <= number + self._stageConfig.AmountPreactivateStage then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
elseif self._stageLaneIdentifier == nil then
|
||||||
|
--main lane
|
||||||
|
--Check if this stage is a chapter stage
|
||||||
|
if self._stageRepository:getStageLane(self._stageLaneIdentifier):IsChapterStart(self.stageNumber)
|
||||||
|
and self.stageNumber < number + self._stageConfig.AmountPreactivateStage
|
||||||
|
then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if GlobalCapManager.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local previousActive = self._activeStage
|
---@return boolean
|
||||||
self._activeStage = number
|
local needsActivation = function()
|
||||||
|
if self._stageLaneIdentifier == stageLaneIdentifier and self.stageNumber == number then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then
|
---@return boolean
|
||||||
self._logger:debug("Pre-activating stage: " .. self.zoneName .. " with number: " .. number)
|
local needsBlueActivation = function()
|
||||||
self:PreActivate()
|
if self._stageLaneIdentifier == stageLaneIdentifier and self.stageNumber < number then
|
||||||
elseif GlobalCapManager.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if needsPreActivation() == true then
|
||||||
self:PreActivate()
|
self:PreActivate()
|
||||||
end
|
end
|
||||||
|
|
||||||
if number == self.stageNumber then
|
if needsActivation() == true then
|
||||||
self:ActivateStage()
|
self:ActivateStage()
|
||||||
|
|
||||||
if self._db and self._db.stageBriefing then
|
|
||||||
self._missionCommandsHelper:AddStageBriefing(self.zoneName, self._db.stageBriefing)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if previousActive <= self.stageNumber then
|
if needsBlueActivation() == true then
|
||||||
if number > self.stageNumber then
|
self:ActivateBlueStage()
|
||||||
self:ActivateBlueStage()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if number > self.stageNumber then
|
|
||||||
self._missionCommandsHelper:RemoveStageBriefing(self.zoneName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Stage:GetBriefing()
|
|
||||||
return "Briefing For "
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self Stage
|
---@param self Stage
|
||||||
@@ -528,7 +600,6 @@ Stage.OnMissionComplete = function(self, mission)
|
|||||||
self:CheckAndUpdateSelf()
|
self:CheckAndUpdateSelf()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---private use only
|
---private use only
|
||||||
function Stage:ActivateBlueGroups()
|
function Stage:ActivateBlueGroups()
|
||||||
|
|
||||||
@@ -590,8 +661,12 @@ end
|
|||||||
|
|
||||||
function Stage:ActivateBlueStage()
|
function Stage:ActivateBlueStage()
|
||||||
|
|
||||||
|
if self._currentStageState >= StageState.Blue then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
self._logger:debug("Setting stage '" .. Util.toString(self.zoneName) .. "' to blue")
|
self._logger:debug("Setting stage '" .. Util.toString(self.zoneName) .. "' to blue")
|
||||||
self._currentStageState = Stage.CurrentStageState.BLUE
|
self._currentStageState = StageState.Blue
|
||||||
for _, mission in pairs(self._db.missions) do
|
for _, mission in pairs(self._db.missions) do
|
||||||
mission:SpawnPersistedState()
|
mission:SpawnPersistedState()
|
||||||
end
|
end
|
||||||
@@ -615,6 +690,8 @@ function Stage:ActivateBlueStage()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self._missionCommandsHelper:RemoveStageBriefing(self.zoneName)
|
||||||
|
|
||||||
timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3)
|
timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
--- StageState enumeration for the current stage state.
|
||||||
|
--- Chronological order, meaning that higher values should represent later stages in the sequence.
|
||||||
|
---@enum CurrentStageState
|
||||||
|
local StageState = {
|
||||||
|
Inactive = 0,
|
||||||
|
PreActivated = 1,
|
||||||
|
Activated = 2,
|
||||||
|
Blue = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return StageState
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
|
local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
|
||||||
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
||||||
|
local StageState = require("classes.stageClasses.Stages.StageState")
|
||||||
|
|
||||||
---@class ExtraStage : Stage
|
---@class ExtraStage : Stage
|
||||||
local ExtraStage = {}
|
local ExtraStage = {}
|
||||||
@@ -19,7 +20,7 @@ function ExtraStage.New(database, stageConfig, logger, initData, spawnManager)
|
|||||||
setmetatable(ExtraStage, Stage)
|
setmetatable(ExtraStage, Stage)
|
||||||
|
|
||||||
local self = setmetatable({}, { __index = ExtraStage }) --[[@as ExtraStage]]
|
local self = setmetatable({}, { __index = ExtraStage }) --[[@as ExtraStage]]
|
||||||
self:superNew(database, stageConfig, logger, initData, "secondary", spawnManager)
|
self:superNew(database, stageConfig, logger, initData, "ExtraStage", "secondary", spawnManager)
|
||||||
|
|
||||||
self.OnPostBlueActivated = function (selfStage)
|
self.OnPostBlueActivated = function (selfStage)
|
||||||
|
|
||||||
@@ -36,7 +37,12 @@ end
|
|||||||
---comment
|
---comment
|
||||||
---@param self ExtraStage
|
---@param self ExtraStage
|
||||||
---@param number integer
|
---@param number integer
|
||||||
function ExtraStage:OnStageNumberChanged(number)
|
---@param stageLaneIdentifier string?
|
||||||
|
function ExtraStage:OnStageNumberChanged(number, stageLaneIdentifier)
|
||||||
|
|
||||||
|
if stageLaneIdentifier ~= self._stageLaneIdentifier then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if self._activeStage == number then --only activate once for a stage
|
if self._activeStage == number then --only activate once for a stage
|
||||||
return
|
return
|
||||||
@@ -56,7 +62,7 @@ function ExtraStage:OnStageNumberChanged(number)
|
|||||||
self:ActivateStage()
|
self:ActivateStage()
|
||||||
end
|
end
|
||||||
|
|
||||||
if self._currentStageState == Stage.CurrentStageState.BLUE then
|
if self._currentStageState == StageState.BLUE then
|
||||||
self:ActivateBlueStage()
|
self:ActivateBlueStage()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
|
|||||||
setmetatable(PrimaryStage, Stage)
|
setmetatable(PrimaryStage, Stage)
|
||||||
|
|
||||||
local self = setmetatable({}, { __index = PrimaryStage }) --[[@as PrimaryStage]]
|
local self = setmetatable({}, { __index = PrimaryStage }) --[[@as PrimaryStage]]
|
||||||
self:superNew(database, stageConfig, logger, initData, "primary", spawnManager)
|
self:superNew(database, stageConfig, logger, initData, "PrimaryStage", "primary", spawnManager)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,28 +4,24 @@ local Stage = require("classes.stageClasses.Stages.BaseStage.Stage")
|
|||||||
---@field private _waitTimeSeconds integer
|
---@field private _waitTimeSeconds integer
|
||||||
---@field private _startTime number
|
---@field private _startTime number
|
||||||
local WaitingStage = {}
|
local WaitingStage = {}
|
||||||
|
|
||||||
WaitingStage.__index = WaitingStage
|
WaitingStage.__index = WaitingStage
|
||||||
|
|
||||||
---@class WaitingStageInitData : StageInitData
|
|
||||||
---@field waitingSeconds integer
|
|
||||||
local WaitingStageInitData = {}
|
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param database Database
|
---@param database Database
|
||||||
---@param stageConfig StageConfig
|
---@param stageConfig StageConfig
|
||||||
---@param logger any
|
---@param logger any
|
||||||
---@param initData WaitingStageInitData
|
---@param initData StageInitData
|
||||||
---@param spawnManager SpawnManager
|
---@param spawnManager SpawnManager
|
||||||
|
---@param waitingSeconds integer
|
||||||
---@return WaitingStage
|
---@return WaitingStage
|
||||||
function WaitingStage.New(database, stageConfig, logger, initData, spawnManager)
|
function WaitingStage.New(database, stageConfig, logger, initData, waitingSeconds, spawnManager)
|
||||||
setmetatable(WaitingStage, Stage)
|
setmetatable(WaitingStage, Stage)
|
||||||
|
|
||||||
local self = setmetatable({}, { __index = WaitingStage }) --[[@as WaitingStage]]
|
local self = setmetatable({}, { __index = WaitingStage }) --[[@as WaitingStage]]
|
||||||
self:superNew(database, stageConfig, logger, initData, "none", spawnManager)
|
self:superNew(database, stageConfig, logger, initData, "WaitingStage", "none", spawnManager)
|
||||||
|
|
||||||
self._waitTimeSeconds = 5
|
self._waitTimeSeconds = 5
|
||||||
if initData.waitingSeconds and initData.waitingSeconds > 5 then self._waitTimeSeconds = initData.waitingSeconds end
|
if waitingSeconds and waitingSeconds > 5 then self._waitTimeSeconds = waitingSeconds end
|
||||||
self._startTime = nil
|
self._startTime = nil
|
||||||
|
|
||||||
self.CheckContinuousAsync = function (selfA, time)
|
self.CheckContinuousAsync = function (selfA, time)
|
||||||
|
|||||||
@@ -1,38 +1,58 @@
|
|||||||
|
|
||||||
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
|
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
|
||||||
local Util = require("classes.util.Util")
|
local Util = require("classes.util.Util")
|
||||||
local Logger = require("classes.util.Logger")
|
local Logger = require("classes.util.Logger")
|
||||||
|
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
||||||
|
|
||||||
local drawingLogger = Logger.new("CustomDrawing")
|
local drawingLogger = Logger.new("CustomDrawing")
|
||||||
|
|
||||||
|
|
||||||
---@class CustomDrawing
|
---@class CustomDrawing
|
||||||
---@field private _id integer?
|
---@field protected _IDs Array<integer>?
|
||||||
---@field private _drawingObject DrawingObject
|
---@field protected _name string?
|
||||||
---@field private _startingStage number
|
---@field protected _drawingObject DrawingObject
|
||||||
---@field private _removeAtStage number
|
|
||||||
---@field private _logger Logger
|
|
||||||
local CustomDrawing = {}
|
local CustomDrawing = {}
|
||||||
CustomDrawing.__index = CustomDrawing
|
CustomDrawing.__index = CustomDrawing
|
||||||
|
|
||||||
|
---@protected
|
||||||
---@param drawingObject DrawingObject
|
---@param drawingObject DrawingObject
|
||||||
---@return CustomDrawing
|
---@return CustomDrawing?
|
||||||
function CustomDrawing.New(drawingObject)
|
function CustomDrawing.New(drawingObject)
|
||||||
local self = setmetatable({}, CustomDrawing)
|
local self = setmetatable({}, CustomDrawing)
|
||||||
self._drawingObject = drawingObject
|
self._drawingObject = drawingObject
|
||||||
self._id = nil -- initiate ID as nil, when drawn it will be set to the ID returned by DrawingHelper.Draw
|
self._IDs = {} -- initiate IDs as an empty array, when drawn it will be set to the IDs returned by DrawingHelper.Draw
|
||||||
self._logger = drawingLogger
|
self._name = drawingObject.name
|
||||||
|
|
||||||
local name = drawingObject.name
|
|
||||||
local split = Util.split_string(name or "", "_")
|
|
||||||
local secondPart = split[2] or "1"
|
|
||||||
local splitPart = Util.split_string(secondPart, ":")
|
|
||||||
self._startingStage = tonumber(splitPart[1]) or 1
|
|
||||||
self._removeAtStage = tonumber(splitPart[2]) or math.huge
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param points Array<Vec2>
|
||||||
|
---@param colorString string
|
||||||
|
---@param lineStyle LineType
|
||||||
|
---@param lineThickness number
|
||||||
|
---@return CustomDrawing?
|
||||||
|
function CustomDrawing.FromPoints(points, colorString, lineStyle, lineThickness)
|
||||||
|
if points == nil or #points < 3 then
|
||||||
|
drawingLogger:warn("CustomDrawing.FromPoints called with nil or less than 3 points")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type FreeLine
|
||||||
|
local drawingObject = {
|
||||||
|
mapX = 0,
|
||||||
|
mapY = 0,
|
||||||
|
points = points,
|
||||||
|
lineMode = "free",
|
||||||
|
name = "custom_drawing_" .. tostring(math.random(1000000)),
|
||||||
|
primitiveType = "Polygon",
|
||||||
|
polygonMode = "free",
|
||||||
|
visible = true,
|
||||||
|
style = DrawingHelper.ToLineStyleString(lineStyle),
|
||||||
|
colorString = colorString,
|
||||||
|
thickness = lineThickness,
|
||||||
|
closed = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomDrawing.New(drawingObject)
|
||||||
|
end
|
||||||
|
|
||||||
---@param zone SpearheadTriggerZone
|
---@param zone SpearheadTriggerZone
|
||||||
---@param colorString string
|
---@param colorString string
|
||||||
---@param fillColorString string
|
---@param fillColorString string
|
||||||
@@ -65,7 +85,6 @@ function CustomDrawing.FromZone(zone, colorString, fillColorString, lineStyle, l
|
|||||||
end
|
end
|
||||||
|
|
||||||
if zone.zone_type == "Polygon" then
|
if zone.zone_type == "Polygon" then
|
||||||
|
|
||||||
---@type Free
|
---@type Free
|
||||||
local drawingObject = {
|
local drawingObject = {
|
||||||
mapX = 0,
|
mapX = 0,
|
||||||
@@ -80,7 +99,7 @@ function CustomDrawing.FromZone(zone, colorString, fillColorString, lineStyle, l
|
|||||||
fillColorString = fillColorString,
|
fillColorString = fillColorString,
|
||||||
thickness = lineThickness,
|
thickness = lineThickness,
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomDrawing.New(drawingObject)
|
return CustomDrawing.New(drawingObject)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -90,27 +109,25 @@ function CustomDrawing:GetName()
|
|||||||
return self._drawingObject.name
|
return self._drawingObject.name
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return number start
|
|
||||||
---@return number stop
|
|
||||||
function CustomDrawing:GetStartAndStop()
|
|
||||||
return self._startingStage, self._removeAtStage
|
|
||||||
end
|
|
||||||
|
|
||||||
function CustomDrawing:Draw()
|
function CustomDrawing:Draw()
|
||||||
self._logger:debug("Drawing custom drawing with ID " .. tostring(self._id))
|
drawingLogger:debug("Drawing custom drawing with IDs " .. table.concat(self._IDs, ", "))
|
||||||
|
|
||||||
if self._id ~= nil then
|
if self._IDs ~= nil then
|
||||||
DrawingHelper.Remove(self._id)
|
for _, id in ipairs(self._IDs) do
|
||||||
|
DrawingHelper.Remove(id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self._id = DrawingHelper.Draw(self._drawingObject, self._logger)
|
self._IDs = DrawingHelper.Draw(self._drawingObject)
|
||||||
end
|
end
|
||||||
|
|
||||||
function CustomDrawing:Remove()
|
function CustomDrawing:Remove()
|
||||||
if self._id ~= nil then
|
if self._IDs ~= nil then
|
||||||
self._logger:debug("Removing custom drawing with ID " .. tostring(self._id))
|
for _, id in ipairs(self._IDs) do
|
||||||
DrawingHelper.Remove(self._id)
|
drawingLogger:debug("Removing custom drawing with ID " .. tostring(id))
|
||||||
self._id = nil
|
DrawingHelper.Remove(id)
|
||||||
|
end
|
||||||
|
self._IDs = {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -119,4 +136,4 @@ function CustomDrawing:UpdateDrawingObject(updateFunc)
|
|||||||
self._drawingObject = updateFunc(self._drawingObject)
|
self._drawingObject = updateFunc(self._drawingObject)
|
||||||
end
|
end
|
||||||
|
|
||||||
return CustomDrawing
|
return CustomDrawing
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing")
|
||||||
|
local Util = require("classes.util.Util")
|
||||||
|
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
|
||||||
|
---@class StageDrawing : CustomDrawing
|
||||||
|
---@field private _startingStage number
|
||||||
|
---@field private _removeAtStage number
|
||||||
|
---@field private _stageLaneIdentifier string?
|
||||||
|
local StageDrawing = {}
|
||||||
|
StageDrawing.__index = StageDrawing
|
||||||
|
setmetatable(StageDrawing, CustomDrawing)
|
||||||
|
|
||||||
|
|
||||||
|
---@param drawingObject DrawingObject
|
||||||
|
---@return StageDrawing?
|
||||||
|
function StageDrawing.New(drawingObject)
|
||||||
|
|
||||||
|
local super = CustomDrawing.New(drawingObject)
|
||||||
|
if not super then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local self = setmetatable(super, StageDrawing) --[[@as StageDrawing]]
|
||||||
|
|
||||||
|
local split = Util.split_string(self._name or "", "_")
|
||||||
|
local secondPart = split[2] or "1"
|
||||||
|
local splitPart = Util.split_string(secondPart, ":")
|
||||||
|
if tonumber(splitPart[1]) == nil then
|
||||||
|
local laneIdentifier = string.sub(splitPart[1], 1, 1)
|
||||||
|
if tonumber(laneIdentifier) == nil then
|
||||||
|
self._stageLaneIdentifier = laneIdentifier
|
||||||
|
end
|
||||||
|
local startNumber = string.sub(splitPart[1], 2)
|
||||||
|
local startNumberVal = tonumber(startNumber)
|
||||||
|
if startNumberVal == nil then
|
||||||
|
MissionEditorWarnings.Add("Start number for " ..
|
||||||
|
self._name .. " is not a valid number: " .. tostring(startNumber))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
self._startingStage = startNumberVal
|
||||||
|
else
|
||||||
|
local startNumber = tonumber(splitPart[1])
|
||||||
|
if startNumber == nil then
|
||||||
|
MissionEditorWarnings.Add("Start number for " ..
|
||||||
|
self._name .. " is not a valid number: " .. tostring(splitPart[1]))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
self._startingStage = startNumber
|
||||||
|
end
|
||||||
|
|
||||||
|
if tonumber(splitPart[2]) == nil then
|
||||||
|
local laneIdentifier = string.sub(splitPart[2], 1, 1)
|
||||||
|
if tonumber(laneIdentifier) == nil then
|
||||||
|
if self._stageLaneIdentifier and self._stageLaneIdentifier ~= laneIdentifier then
|
||||||
|
MissionEditorWarnings.Add("Lane identifiers do not match for " ..
|
||||||
|
self._name ..
|
||||||
|
": " ..
|
||||||
|
self._stageLaneIdentifier ..
|
||||||
|
" vs " .. laneIdentifier .. ". Will only use stage " .. self._stageLaneIdentifier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local stopNumber = string.sub(splitPart[2], 2)
|
||||||
|
local stopNumberVal = tonumber(stopNumber)
|
||||||
|
if stopNumberVal == nil then
|
||||||
|
MissionEditorWarnings.Add("Stop number for " ..
|
||||||
|
self._name .. " is not a valid number: " .. tostring(stopNumber))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
self._removeAtStage = stopNumberVal
|
||||||
|
else
|
||||||
|
local stopNumber = tonumber(splitPart[2])
|
||||||
|
if stopNumber == nil then
|
||||||
|
MissionEditorWarnings.Add("Stop number for " ..
|
||||||
|
self._name .. " is not a valid number: " .. tostring(splitPart[2]))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
self._removeAtStage = stopNumber
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function StageDrawing:GetStageLaneIdentifier()
|
||||||
|
return self._stageLaneIdentifier
|
||||||
|
end
|
||||||
|
|
||||||
|
function StageDrawing:GetStartingStage()
|
||||||
|
return self._startingStage
|
||||||
|
end
|
||||||
|
|
||||||
|
function StageDrawing:GetRemoveAtStage()
|
||||||
|
return self._removeAtStage
|
||||||
|
end
|
||||||
|
|
||||||
|
return StageDrawing
|
||||||
@@ -1,57 +1,45 @@
|
|||||||
local Util = require("classes.util.Util")
|
local Util = require("classes.util.Util")
|
||||||
|
local DcsUtil = require("classes.util.DcsUtil")
|
||||||
local Logger = require("classes.util.Logger")
|
local Logger = require("classes.util.Logger")
|
||||||
local GlobalConfig = require("classes.configuration.GlobalConfig")
|
local GlobalConfig = require("classes.configuration.GlobalConfig")
|
||||||
|
|
||||||
---@type LogLevel
|
---@type LogLevel
|
||||||
local level = "INFO"
|
local level = "INFO"
|
||||||
if GlobalConfig:isDebugEnabled() then
|
if GlobalConfig.New():isDebugEnabled() then
|
||||||
level = "DEBUG"
|
level = "DEBUG"
|
||||||
end
|
end
|
||||||
|
|
||||||
local drawingLogger = Logger.new("DrawingHelper", level)
|
local logger = Logger.new("DrawingHelper", level)
|
||||||
|
|
||||||
---@class DrawingHelper
|
---@class DrawingHelper
|
||||||
local DrawingHelper = {}
|
local DrawingHelper = {}
|
||||||
DrawingHelper.__index = DrawingHelper
|
DrawingHelper.__index = DrawingHelper
|
||||||
|
|
||||||
|
|
||||||
local customDrawingIdIncrementer = 4210
|
|
||||||
|
|
||||||
---@param object DrawingObject
|
---@param object DrawingObject
|
||||||
---@param logger Logger?
|
---@return Array<integer> id
|
||||||
---@return integer? id
|
function DrawingHelper.Draw(object)
|
||||||
function DrawingHelper.Draw(object, logger)
|
|
||||||
if object == nil then
|
if object == nil then
|
||||||
if logger then
|
if logger then
|
||||||
logger:warn("DrawingHelper.Draw called with nil object")
|
logger:warn("DrawingHelper.Draw called with nil object")
|
||||||
end
|
end
|
||||||
return nil
|
return {}
|
||||||
end
|
|
||||||
|
|
||||||
local id = DrawingHelper.GetAndAddId()
|
|
||||||
|
|
||||||
if logger then
|
|
||||||
logger:debug("Drawing object with ID " .. tostring(id) .. " and primitive type: " .. tostring(object.primitiveType))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if(object.primitiveType == "Polygon") then
|
if(object.primitiveType == "Polygon") then
|
||||||
DrawingHelper.DrawPolygon(object--[[@as Polygon]], id, logger)
|
return DrawingHelper.DrawPolygon(object--[[@as Polygon]])
|
||||||
elseif(object.primitiveType == "Line") then
|
elseif(object.primitiveType == "Line") then
|
||||||
DrawingHelper.DrawLine(object--[[@as Line]], id, logger)
|
return DrawingHelper.DrawLine(object--[[@as Line]])
|
||||||
elseif(object.primitiveType == "TextBox") then
|
elseif(object.primitiveType == "TextBox") then
|
||||||
DrawingHelper.DrawTextBox(object--[[@as TextBox]], id)
|
return DrawingHelper.DrawTextBox(object--[[@as TextBox]])
|
||||||
else
|
else
|
||||||
if logger then
|
logger:warn("Unknown primitive type: " .. tostring(object.primitiveType))
|
||||||
logger:warn("Unknown primitive type: " .. tostring(object.primitiveType))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return id
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
function DrawingHelper.GetAndAddId()
|
function DrawingHelper.GetAndAddId()
|
||||||
customDrawingIdIncrementer = customDrawingIdIncrementer + 1
|
return DcsUtil.GetNextDrawID()
|
||||||
return customDrawingIdIncrementer
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@@ -62,8 +50,7 @@ end
|
|||||||
---@param lineColor table
|
---@param lineColor table
|
||||||
---@param lineStyle LineType
|
---@param lineStyle LineType
|
||||||
---@param lineThickness number
|
---@param lineThickness number
|
||||||
---@param logger Logger?
|
local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineStyle, lineThickness)
|
||||||
local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineStyle, lineThickness, logger)
|
|
||||||
|
|
||||||
if lineThickness == nil or lineThickness <= 0 then
|
if lineThickness == nil or lineThickness <= 0 then
|
||||||
lineStyle = 0
|
lineStyle = 0
|
||||||
@@ -76,23 +63,19 @@ local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineS
|
|||||||
end
|
end
|
||||||
functionString = functionString .. "{0,1,0,1}, {0,1,0,1}, " .. lineStyle .. ")"
|
functionString = functionString .. "{0,1,0,1}, {0,1,0,1}, " .. lineStyle .. ")"
|
||||||
|
|
||||||
if logger then
|
logger:debug("Drawing complex drawing with ID " .. tostring(drawID) .. " and function string: " .. functionString)
|
||||||
logger:debug("Drawing complex drawing with ID " .. tostring(drawID) .. " and function string: " .. functionString)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@diagnostic disable-next-line: deprecated
|
---@diagnostic disable-next-line: deprecated
|
||||||
local f, err = loadstring(functionString)
|
local f, err = loadstring(functionString)
|
||||||
if f then
|
if f then
|
||||||
f()
|
f()
|
||||||
else
|
else
|
||||||
env.error("Something failed when drawing complex drawing" .. err)
|
logger:error("Something failed when drawing complex drawing" .. err)
|
||||||
end
|
|
||||||
|
|
||||||
if logger then
|
|
||||||
logger:debug("Drawing with fill color: " .. table.concat(fillColor, ",") .. " and line color: " .. table.concat(lineColor, ",") .. " and line style: " .. tostring(lineStyle) .. " and line thickness: " .. tostring(lineThickness))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
trigger.action.setMarkupColorFill(drawID, fillColor)
|
if fillColor then
|
||||||
|
trigger.action.setMarkupColorFill(drawID, fillColor)
|
||||||
|
end
|
||||||
trigger.action.setMarkupColor(drawID, lineColor)
|
trigger.action.setMarkupColor(drawID, lineColor)
|
||||||
trigger.action.setMarkupTypeLine(drawID, lineStyle)
|
trigger.action.setMarkupTypeLine(drawID, lineStyle)
|
||||||
|
|
||||||
@@ -100,13 +83,14 @@ end
|
|||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param object Polygon
|
---@param object Polygon
|
||||||
---@param id integer
|
---@return Array<integer>
|
||||||
---@param logger Logger?
|
function DrawingHelper.DrawPolygon(object)
|
||||||
function DrawingHelper.DrawPolygon(object, id, logger)
|
|
||||||
if object == nil then
|
if object == nil then
|
||||||
return
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local id = DrawingHelper.GetAndAddId()
|
||||||
|
|
||||||
---@param circle Circle
|
---@param circle Circle
|
||||||
local function DrawCircle(circle)
|
local function DrawCircle(circle)
|
||||||
local vec3 = { x = circle.mapX, y = 0, z = circle.mapY }
|
local vec3 = { x = circle.mapX, y = 0, z = circle.mapY }
|
||||||
@@ -115,15 +99,15 @@ function DrawingHelper.DrawPolygon(object, id, logger)
|
|||||||
local style = DrawingHelper.ToLineStyleInteger(circle.style)
|
local style = DrawingHelper.ToLineStyleInteger(circle.style)
|
||||||
trigger.action.circleToAll(-1, id, vec3, circle.radius, colorString, fillColor, style, true)
|
trigger.action.circleToAll(-1, id, vec3, circle.radius, colorString, fillColor, style, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param oval Oval
|
---@param oval Oval
|
||||||
---@param logger Logger?
|
---@return integer?, Array<CustomDrawing>?
|
||||||
local function DrawOval(oval, logger)
|
local function DrawOval(oval)
|
||||||
---@type Array<Vec3>
|
---@type Array<Vec3>
|
||||||
local points = {}
|
local points = {}
|
||||||
local pointsNo = 30
|
local pointsNo = 30
|
||||||
local angleStep = (2 * math.pi) / pointsNo
|
local angleStep = (2 * math.pi) / pointsNo
|
||||||
|
|
||||||
local fillColor = DrawingHelper.ColorToColorTable(oval.fillColorString)
|
local fillColor = DrawingHelper.ColorToColorTable(oval.fillColorString)
|
||||||
local color = DrawingHelper.ColorToColorTable(oval.colorString)
|
local color = DrawingHelper.ColorToColorTable(oval.colorString)
|
||||||
local lineStyle = DrawingHelper.ToLineStyleInteger(oval.style)
|
local lineStyle = DrawingHelper.ToLineStyleInteger(oval.style)
|
||||||
@@ -134,12 +118,11 @@ function DrawingHelper.DrawPolygon(object, id, logger)
|
|||||||
local y = oval.mapY + (oval.r2 * math.sin(angle))
|
local y = oval.mapY + (oval.r2 * math.sin(angle))
|
||||||
table.insert(points, { x = x, y = 0, z = y } )
|
table.insert(points, { x = x, y = 0, z = y } )
|
||||||
end
|
end
|
||||||
MarkupToAll(7, id, points, fillColor, color, lineStyle, oval.thickness, logger)
|
MarkupToAll(7, id, points, fillColor, color, lineStyle, oval.thickness)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param free Free
|
---@param free Free
|
||||||
---@param logger Logger?
|
local function DrawFree(free)
|
||||||
local function DrawFree(free, logger)
|
|
||||||
local fillColor = DrawingHelper.ColorToColorTable(free.fillColorString)
|
local fillColor = DrawingHelper.ColorToColorTable(free.fillColorString)
|
||||||
local color = DrawingHelper.ColorToColorTable(free.colorString)
|
local color = DrawingHelper.ColorToColorTable(free.colorString)
|
||||||
local lineStyle = DrawingHelper.ToLineStyleInteger(free.style)
|
local lineStyle = DrawingHelper.ToLineStyleInteger(free.style)
|
||||||
@@ -163,7 +146,7 @@ function DrawingHelper.DrawPolygon(object, id, logger)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
MarkupToAll(7, id, points, fillColor, color, lineStyle, free.thickness, logger)
|
MarkupToAll(7, id, points, fillColor, color, lineStyle, free.thickness)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param rect Rect
|
---@param rect Rect
|
||||||
@@ -175,19 +158,16 @@ function DrawingHelper.DrawPolygon(object, id, logger)
|
|||||||
local pointA = { x = rect.mapX, y = 0, z = rect.mapY }
|
local pointA = { x = rect.mapX, y = 0, z = rect.mapY }
|
||||||
local pointB = { x = rect.mapX + rect.width, y = 0, z = rect.mapY + rect.height }
|
local pointB = { x = rect.mapX + rect.width, y = 0, z = rect.mapY + rect.height }
|
||||||
trigger.action.rectToAll(-1, id, pointA, pointB, color, fillColor, lineStyle, true)
|
trigger.action.rectToAll(-1, id, pointA, pointB, color, fillColor, lineStyle, true)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param arrow Arrow
|
---@param arrow Arrow
|
||||||
---@param logger Logger?
|
local function DrawArrow(arrow)
|
||||||
local function DrawArrow(arrow, logger)
|
|
||||||
local fillColor = DrawingHelper.ColorToColorTable(arrow.fillColorString)
|
local fillColor = DrawingHelper.ColorToColorTable(arrow.fillColorString)
|
||||||
local color = DrawingHelper.ColorToColorTable(arrow.colorString)
|
local color = DrawingHelper.ColorToColorTable(arrow.colorString)
|
||||||
local lineStyle = DrawingHelper.ToLineStyleInteger(arrow.style)
|
local lineStyle = DrawingHelper.ToLineStyleInteger(arrow.style)
|
||||||
|
|
||||||
if logger then
|
logger:debug("Drawing arrow with start point: " .. tostring(arrow.mapX) .. ", " .. tostring(arrow.mapY) .. " and angle: " .. tostring(arrow.angle) .. " and length: " .. tostring(arrow.length))
|
||||||
logger:debug("Drawing arrow with start point: " .. tostring(arrow.mapX) .. ", " .. tostring(arrow.mapY) .. " and angle: " .. tostring(arrow.angle) .. " and length: " .. tostring(arrow.length))
|
|
||||||
end
|
|
||||||
|
|
||||||
local endPoint = { x = arrow.mapX, y = 0, z = arrow.mapY }
|
local endPoint = { x = arrow.mapX, y = 0, z = arrow.mapY }
|
||||||
local rad = math.rad(arrow.angle or 0)
|
local rad = math.rad(arrow.angle or 0)
|
||||||
@@ -203,44 +183,56 @@ function DrawingHelper.DrawPolygon(object, id, logger)
|
|||||||
if object.polygonMode == "circle" then
|
if object.polygonMode == "circle" then
|
||||||
DrawCircle(object--[[@as Circle]])
|
DrawCircle(object--[[@as Circle]])
|
||||||
elseif object.polygonMode == "oval" then
|
elseif object.polygonMode == "oval" then
|
||||||
DrawOval(object--[[@as Oval]], logger)
|
DrawOval(object--[[@as Oval]])
|
||||||
elseif object.polygonMode == "free" then
|
elseif object.polygonMode == "free" then
|
||||||
DrawFree(object--[[@as Free]], logger)
|
DrawFree(object--[[@as Free]])
|
||||||
elseif object.polygonMode == "rect" then
|
elseif object.polygonMode == "rect" then
|
||||||
DrawRect(object--[[@as Rect]])
|
DrawRect(object--[[@as Rect]])
|
||||||
elseif object.polygonMode == "arrow" then
|
elseif object.polygonMode == "arrow" then
|
||||||
DrawArrow(object--[[@as Arrow]], logger)
|
DrawArrow(object--[[@as Arrow]])
|
||||||
end
|
end
|
||||||
|
return {id}
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param object Line
|
---@param object Line
|
||||||
---@param id integer
|
---@return Array<integer>
|
||||||
---@param logger Logger?
|
function DrawingHelper.DrawLine(object)
|
||||||
function DrawingHelper.DrawLine(object, id, logger)
|
|
||||||
|
|
||||||
---@type Array<Vec3>
|
---@type Array<Vec3>
|
||||||
local points = {}
|
local points = {}
|
||||||
|
local ids = {}
|
||||||
|
|
||||||
for _, point in ipairs(object.points) do
|
for _, point in ipairs(object.points) do
|
||||||
table.insert(points, { x = point.x, y = 0, z = point.y } )
|
table.insert(points, { x = object.mapX + point.x, y = 0, z = object.mapY + point.y } )
|
||||||
end
|
end
|
||||||
|
|
||||||
local color = DrawingHelper.ColorToColorTable(object.colorString)
|
local color = DrawingHelper.ColorToColorTable(object.colorString)
|
||||||
local lineStyle = DrawingHelper.ToLineStyleInteger(object.style)
|
local lineStyle = DrawingHelper.ToLineStyleInteger(object.style)
|
||||||
MarkupToAll(1, id, points, color, color, lineStyle, object.thickness, logger)
|
|
||||||
|
for i = 1, #points - 1 do
|
||||||
|
local id = DrawingHelper.GetAndAddId()
|
||||||
|
trigger.action.lineToAll(-1, id, points[i], points[i + 1], color, lineStyle, true)
|
||||||
|
table.insert(ids, id)
|
||||||
|
end
|
||||||
|
return ids
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param object TextBox
|
---@param object TextBox
|
||||||
---@param id integer
|
---@return Array<integer>
|
||||||
function DrawingHelper.DrawTextBox(object, id)
|
function DrawingHelper.DrawTextBox(object)
|
||||||
|
|
||||||
|
local id = DrawingHelper.GetAndAddId()
|
||||||
|
|
||||||
trigger.action.textToAll(-1, id, { x= object.mapX, y = 0, z = object.mapY },
|
trigger.action.textToAll(-1, id, { x= object.mapX, y = 0, z = object.mapY },
|
||||||
DrawingHelper.ColorToColorTable(object.colorString),
|
DrawingHelper.ColorToColorTable(object.colorString),
|
||||||
DrawingHelper.ColorToColorTable(object.fillColorString),
|
DrawingHelper.ColorToColorTable(object.fillColorString),
|
||||||
object.fontSize or 12,
|
object.fontSize or 12,
|
||||||
true,
|
true,
|
||||||
object.text or "")
|
object.text or "")
|
||||||
|
|
||||||
|
return {id}
|
||||||
end
|
end
|
||||||
|
|
||||||
function DrawingHelper.Remove(id)
|
function DrawingHelper.Remove(id)
|
||||||
@@ -252,7 +244,7 @@ end
|
|||||||
function DrawingHelper.ColorToColorTable(hexStr)
|
function DrawingHelper.ColorToColorTable(hexStr)
|
||||||
|
|
||||||
if hexStr == nil then
|
if hexStr == nil then
|
||||||
drawingLogger:warn("ColorToColorTable called with nil hexStr, returning default color {0, 0, 0, 0}")
|
logger:warn("ColorToColorTable called with nil hexStr, returning default color {0, 0, 0, 0}")
|
||||||
return { 0, 0, 0, 0 }
|
return { 0, 0, 0, 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -266,11 +258,11 @@ function DrawingHelper.ColorToColorTable(hexStr)
|
|||||||
return { r, g , b , a }
|
return { r, g , b , a }
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param rgba table
|
---@param rgba table { r, g, b, a }
|
||||||
---@return string
|
---@return string
|
||||||
function DrawingHelper.ColorTableToColorString(rgba)
|
function DrawingHelper.ColorTableToColorString(rgba)
|
||||||
if rgba == nil or #rgba < 4 or rgba[1] == nil or rgba[2] == nil or rgba[3] == nil or rgba[4] == nil then
|
if rgba == nil or #rgba < 4 or rgba[1] == nil or rgba[2] == nil or rgba[3] == nil or rgba[4] == nil then
|
||||||
drawingLogger:warn("ColorTableToColorString called with invalid rgba table, returning default color string '0x00000000'")
|
logger:warn("ColorTableToColorString called with invalid rgba table, returning default color string '0x00000000'")
|
||||||
return "0x00000000"
|
return "0x00000000"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -293,11 +285,11 @@ function DrawingHelper.ToLineStyleInteger(lineStyle)
|
|||||||
return 2
|
return 2
|
||||||
elseif lineStyle == "dotted" then
|
elseif lineStyle == "dotted" then
|
||||||
return 3
|
return 3
|
||||||
elseif lineStyle == "dot dash" then
|
elseif lineStyle == "dotdash" then
|
||||||
return 4
|
return 4
|
||||||
elseif lineStyle == "long dash" then
|
elseif lineStyle == "longdash" then
|
||||||
return 5
|
return 5
|
||||||
elseif lineStyle == "two dash" then
|
elseif lineStyle == "twodash" then
|
||||||
return 6
|
return 6
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
@@ -314,11 +306,11 @@ function DrawingHelper.ToLineStyleString(lineStyle)
|
|||||||
elseif lineStyle == 3 then
|
elseif lineStyle == 3 then
|
||||||
return "dotted"
|
return "dotted"
|
||||||
elseif lineStyle == 4 then
|
elseif lineStyle == 4 then
|
||||||
return "dot dash"
|
return "dotdash"
|
||||||
elseif lineStyle == 5 then
|
elseif lineStyle == 5 then
|
||||||
return "long dash"
|
return "longdash"
|
||||||
elseif lineStyle == 6 then
|
elseif lineStyle == 6 then
|
||||||
return "two dash"
|
return "twodash"
|
||||||
else
|
else
|
||||||
return "no line"
|
return "no line"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ function MissionCommandsHelper:OverviewToGroup(groupID)
|
|||||||
for code, enabled in pairs(self.enabledByCode) do
|
for code, enabled in pairs(self.enabledByCode) do
|
||||||
if enabled == true then
|
if enabled == true then
|
||||||
local mission = self.missionsByCode[code]
|
local mission = self.missionsByCode[code]
|
||||||
if mission and mission:getState() == "ACTIVE" and mission.priority == "primary" then
|
if mission and mission:getState() == "ACTIVE" and mission:GetPriority() == "primary" then
|
||||||
table.insert(primaryMissions, mission)
|
table.insert(primaryMissions, mission)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -232,7 +232,7 @@ function MissionCommandsHelper:OverviewToGroup(groupID)
|
|||||||
for code, enabled in pairs(self.enabledByCode) do
|
for code, enabled in pairs(self.enabledByCode) do
|
||||||
if enabled == true then
|
if enabled == true then
|
||||||
local mission = self.missionsByCode[code]
|
local mission = self.missionsByCode[code]
|
||||||
if mission and mission:getState() == "ACTIVE" and mission.priority == "secondary" then
|
if mission and mission:getState() == "ACTIVE" and mission:GetPriority() == "secondary" then
|
||||||
table.insert(secondaryMissions, mission)
|
table.insert(secondaryMissions, mission)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -347,7 +347,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
|
|||||||
for code, enabled in pairs(self.enabledByCode) do
|
for code, enabled in pairs(self.enabledByCode) do
|
||||||
if enabled == true then
|
if enabled == true then
|
||||||
local mission = self.missionsByCode[code]
|
local mission = self.missionsByCode[code]
|
||||||
if mission and mission.priority == "primary" then
|
if mission and mission:GetPriority() == "primary" then
|
||||||
table.insert(primaryMissions, mission)
|
table.insert(primaryMissions, mission)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -376,7 +376,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
|
|||||||
for code, enabled in pairs(self.enabledByCode) do
|
for code, enabled in pairs(self.enabledByCode) do
|
||||||
if enabled == true then
|
if enabled == true then
|
||||||
local mission = self.missionsByCode[code]
|
local mission = self.missionsByCode[code]
|
||||||
if mission and mission.priority == "secondary" then
|
if mission and mission:GetPriority() == "secondary" then
|
||||||
table.insert(secondaryMissions, mission)
|
table.insert(secondaryMissions, mission)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -418,7 +418,7 @@ function MissionCommandsHelper:addMissionCommands(groupId, path, mission)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local missionFolderName = "[" ..
|
local missionFolderName = "[" ..
|
||||||
mission.code .. "]" .. distance .. mission.name .. "( " .. mission.missionTypeDisplay .. " )"
|
mission.code .. "]" .. distance .. mission.name .. "(" .. mission.missionTypeDisplay .. ")"
|
||||||
missionCommands.addSubMenuForGroup(groupId, missionFolderName, path)
|
missionCommands.addSubMenuForGroup(groupId, missionFolderName, path)
|
||||||
table.insert(path, missionFolderName)
|
table.insert(path, missionFolderName)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ local SupplyConfigHelper = {}
|
|||||||
---@return SupplyConfig?
|
---@return SupplyConfig?
|
||||||
function SupplyConfigHelper.fromObjectName(name)
|
function SupplyConfigHelper.fromObjectName(name)
|
||||||
for configName, config in pairs(SupplyConfig) do
|
for configName, config in pairs(SupplyConfig) do
|
||||||
if Util.startswith(name, configName, true) == true then
|
if Util.startsWith(name, configName, true) == true then
|
||||||
return config
|
return config
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -21,9 +21,18 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelpe
|
|||||||
---@field private _dropOffZone SpearheadTriggerZone?
|
---@field private _dropOffZone SpearheadTriggerZone?
|
||||||
---@field private _noLandingZoneDrawing CustomDrawing?
|
---@field private _noLandingZoneDrawing CustomDrawing?
|
||||||
---@field private _dropOffZoneDrawing CustomDrawing?
|
---@field private _dropOffZoneDrawing CustomDrawing?
|
||||||
|
---@field private _briefing string?
|
||||||
local BuildableMission = {}
|
local BuildableMission = {}
|
||||||
BuildableMission.__index = BuildableMission
|
BuildableMission.__index = BuildableMission
|
||||||
|
|
||||||
|
|
||||||
|
local function getDefaultBriefing(siteType, coords)
|
||||||
|
return "We've dispatched forward units to find a proper spot for a new " .. siteType .. "." ..
|
||||||
|
"\nYou will need to drop off supplies so they can start building." ..
|
||||||
|
"\nThe coords are: " .. coords ..
|
||||||
|
"\n\n"
|
||||||
|
end
|
||||||
|
|
||||||
---@class OnCrateDroppedListener
|
---@class OnCrateDroppedListener
|
||||||
---@field OnCrateDroppedOff fun(self:OnCrateDroppedListener, mission:BuildableMission, kilos:number)
|
---@field OnCrateDroppedOff fun(self:OnCrateDroppedListener, mission:BuildableMission, kilos:number)
|
||||||
|
|
||||||
@@ -33,7 +42,8 @@ BuildableMission.__index = BuildableMission
|
|||||||
---@param requiredCrateType SupplyType
|
---@param requiredCrateType SupplyType
|
||||||
---@param noLandingZone SpearheadTriggerZone?
|
---@param noLandingZone SpearheadTriggerZone?
|
||||||
---@param logger Logger
|
---@param logger Logger
|
||||||
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType)
|
---@param briefing string?
|
||||||
|
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType, briefing)
|
||||||
|
|
||||||
setmetatable(BuildableMission, Mission)
|
setmetatable(BuildableMission, Mission)
|
||||||
|
|
||||||
@@ -43,6 +53,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
|
|||||||
self._database = database
|
self._database = database
|
||||||
self._requiredKilos = requiredKilos
|
self._requiredKilos = requiredKilos
|
||||||
self._droppedKilos = 0
|
self._droppedKilos = 0
|
||||||
|
self._briefing = briefing
|
||||||
|
|
||||||
self._noLandingZone = noLandingZone
|
self._noLandingZone = noLandingZone
|
||||||
|
|
||||||
@@ -64,7 +75,13 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.code = tostring(database:GetNewMissionCode())
|
self.code = tostring(database:GetNewMissionCode())
|
||||||
self.name = "Resupply"
|
|
||||||
|
local splitTargetZoneName = Util.split_string(targetZone.name, "_")
|
||||||
|
if splitTargetZoneName and splitTargetZoneName[3] and splitTargetZoneName[3] ~= "" then
|
||||||
|
self.name = splitTargetZoneName[3]
|
||||||
|
else
|
||||||
|
self.name = "Resupply"
|
||||||
|
end
|
||||||
|
|
||||||
local type = "site"
|
local type = "site"
|
||||||
if requiredCrateType == "SAM_CRATE" then
|
if requiredCrateType == "SAM_CRATE" then
|
||||||
@@ -81,7 +98,6 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
|
|||||||
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate()
|
self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate()
|
||||||
self._state = "NEW"
|
self._state = "NEW"
|
||||||
|
|
||||||
|
|
||||||
self.location = targetZone.location
|
self.location = targetZone.location
|
||||||
|
|
||||||
self.missionType = "LOGISTICS"
|
self.missionType = "LOGISTICS"
|
||||||
@@ -116,11 +132,16 @@ function BuildableMission:ShowBriefing(groupID)
|
|||||||
siteType = "airbase"
|
siteType = "airbase"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local briefingPart = self._briefing
|
||||||
|
if briefingPart then
|
||||||
|
briefingPart = Util.replaceString(briefingPart, "{{coords}}", coords)
|
||||||
|
else
|
||||||
|
briefingPart = getDefaultBriefing(siteType, coords)
|
||||||
|
end
|
||||||
|
|
||||||
local briefing = "Mission [" .. self.code .. "] " .. self.name ..
|
local briefing = "Mission [" .. self.code .. "] " .. self.name ..
|
||||||
"\n \n" ..
|
"\n \n" ..
|
||||||
"We've dispatched forward units to find a proper spot for a new " .. siteType .. "." ..
|
briefingPart ..
|
||||||
"\nYou will need to drop off supplies so they can start building." ..
|
|
||||||
"\nThe coords are: " .. coords ..
|
|
||||||
"\n\n" ..
|
"\n\n" ..
|
||||||
"\nKilos still required: " .. self._requiredKilos - self._droppedKilos ..
|
"\nKilos still required: " .. self._requiredKilos - self._droppedKilos ..
|
||||||
"\n\n" ..
|
"\n\n" ..
|
||||||
@@ -237,7 +258,7 @@ function BuildableMission:CheckCratesInZone()
|
|||||||
|
|
||||||
local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
|
local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
|
||||||
for _, staticObject in pairs(crates) do
|
for _, staticObject in pairs(crates) do
|
||||||
if staticObject and staticObject:isExist() and Util.startswith(staticObject:getName(), self._crateType, true) then
|
if staticObject and staticObject:isExist() and Util.startsWith(staticObject:getName(), self._crateType, true) then
|
||||||
local pos = staticObject:getPoint()
|
local pos = staticObject:getPoint()
|
||||||
|
|
||||||
if Util.is3dPointInZone(pos, self._dropOffZone) then
|
if Util.is3dPointInZone(pos, self._dropOffZone) then
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ local RunwayStrikeMission = {}
|
|||||||
---@field corners Array<Vec2>
|
---@field corners Array<Vec2>
|
||||||
---@field kilosHit number
|
---@field kilosHit number
|
||||||
---@field repairGroups Array<string>
|
---@field repairGroups Array<string>
|
||||||
---@field drawID number?
|
---@field drawID Array<number>
|
||||||
|
|
||||||
|
|
||||||
---@param runway Runway
|
---@param runway Runway
|
||||||
@@ -172,8 +172,10 @@ function RunwayStrikeMission:Draw()
|
|||||||
|
|
||||||
runwaySection.drawID = DrawingHelper.Draw(drawObject)
|
runwaySection.drawID = DrawingHelper.Draw(drawObject)
|
||||||
else
|
else
|
||||||
DcsUtil.SetFillColor(runwaySection.drawID, fillColor)
|
for _, drawID in pairs(runwaySection.drawID) do
|
||||||
DcsUtil.SetLineColor(runwaySection.drawID, lineColor)
|
DcsUtil.SetFillColor(drawID, fillColor)
|
||||||
|
DcsUtil.SetLineColor(drawID, lineColor)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -497,6 +499,7 @@ function RunwayStrikeMission:ToSections(runway, numSections)
|
|||||||
corners = corners,
|
corners = corners,
|
||||||
kilosHit = 0,
|
kilosHit = 0,
|
||||||
repairGroups = {},
|
repairGroups = {},
|
||||||
|
drawID = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
table.insert(sections, section)
|
table.insert(sections, section)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ local DcsUtil = require("classes.util.DcsUtil")
|
|||||||
---@field private _completeAtIndex number
|
---@field private _completeAtIndex number
|
||||||
---@field private _parentStage Stage
|
---@field private _parentStage Stage
|
||||||
---@field private _battleManager? BattleManager
|
---@field private _battleManager? BattleManager
|
||||||
---@field private _lastContactMarkerID number;
|
---@field private _lastContactMarkerID number
|
||||||
local ZoneMission = {}
|
local ZoneMission = {}
|
||||||
|
|
||||||
--- @class MissionGroups
|
--- @class MissionGroups
|
||||||
@@ -24,7 +24,7 @@ local ZoneMission = {}
|
|||||||
--- @field unitsAlive table<string, table<string, boolean>>
|
--- @field unitsAlive table<string, table<string, boolean>>
|
||||||
--- @field targetsAlive table<string, table<string, boolean>>
|
--- @field targetsAlive table<string, table<string, boolean>>
|
||||||
--- @field sceneryTargets Array<SpearheadSceneryObject>
|
--- @field sceneryTargets Array<SpearheadSceneryObject>
|
||||||
--- @field groupNamesPerunit table<string,string>
|
--- @field groupNamesPerUnit table<string,string>
|
||||||
|
|
||||||
---@class ParsedMissionName
|
---@class ParsedMissionName
|
||||||
---@field missionName string
|
---@field missionName string
|
||||||
@@ -36,7 +36,7 @@ local ZoneMission = {}
|
|||||||
local function ParseZoneName(input)
|
local function ParseZoneName(input)
|
||||||
local split_name = Util.split_string(input, "_")
|
local split_name = Util.split_string(input, "_")
|
||||||
local split_length = Util.tableLength(split_name)
|
local split_length = Util.tableLength(split_name)
|
||||||
if Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
|
if Util.startsWith(input, "RANDOMMISSION") == true and split_length < 4 then
|
||||||
MissionEditorWarnings.Add("Random Mission with zonename " .. input .. " not in right format")
|
MissionEditorWarnings.Add("Random Mission with zonename " .. input .. " not in right format")
|
||||||
return nil
|
return nil
|
||||||
elseif split_length < 3 then
|
elseif split_length < 3 then
|
||||||
@@ -53,9 +53,10 @@ local function ParseZoneName(input)
|
|||||||
if inputType == "cas" then parsedType = "CAS" end
|
if inputType == "cas" then parsedType = "CAS" end
|
||||||
if inputType == "bai" then parsedType = "BAI" end
|
if inputType == "bai" then parsedType = "BAI" end
|
||||||
if inputType == "sam" then parsedType = "SAM" end
|
if inputType == "sam" then parsedType = "SAM" end
|
||||||
|
if inputType == "deepstrike" then parsedType = "DEEPSTRIKE" end
|
||||||
|
|
||||||
if parsedType == "nil" then
|
if parsedType == "nil" then
|
||||||
MissionEditorWarnings.Add("Mission with zonename '" ..
|
MissionEditorWarnings.Add("Mission with zoneName '" ..
|
||||||
input .. "' has an unsupported type '" .. (type or "nil"))
|
input .. "' has an unsupported type '" .. (type or "nil"))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@@ -66,8 +67,6 @@ local function ParseZoneName(input)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
MINIMAL_UNITS_ALIVE_RATIO = 0.21
|
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param zoneName string
|
---@param zoneName string
|
||||||
---@param priority MissionPriority
|
---@param priority MissionPriority
|
||||||
@@ -112,7 +111,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
|
|||||||
unitsAlive = {},
|
unitsAlive = {},
|
||||||
targetsAlive = {},
|
targetsAlive = {},
|
||||||
hasTargets = false,
|
hasTargets = false,
|
||||||
groupNamesPerunit = {},
|
groupNamesPerUnit = {},
|
||||||
sceneryTargets = {}
|
sceneryTargets = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,17 +149,17 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
|
|||||||
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
|
local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true)
|
||||||
table.insert(self._missionGroups.redGroups, spearheadGroup)
|
table.insert(self._missionGroups.redGroups, spearheadGroup)
|
||||||
|
|
||||||
local isGroupTarget = Util.startswith(string.lower(groupName), "tgt_")
|
local isGroupTarget = Util.startsWith(string.lower(groupName), "tgt_")
|
||||||
for _, unit in pairs(spearheadGroup:GetObjects()) do
|
for _, unit in pairs(spearheadGroup:GetObjects()) do
|
||||||
local unitName = unit:getName()
|
local unitName = unit:getName()
|
||||||
local isUnitTarget = Util.startswith(string.lower(unitName), "tgt_")
|
local isUnitTarget = Util.startsWith(string.lower(unitName), "tgt_")
|
||||||
|
|
||||||
if self._missionGroups.unitsAlive[groupName] == nil then
|
if self._missionGroups.unitsAlive[groupName] == nil then
|
||||||
self._missionGroups.unitsAlive[groupName] = {}
|
self._missionGroups.unitsAlive[groupName] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
self._missionGroups.unitsAlive[groupName][unitName] = true
|
self._missionGroups.unitsAlive[groupName][unitName] = true
|
||||||
self._missionGroups.groupNamesPerunit[unitName] = groupName
|
self._missionGroups.groupNamesPerUnit[unitName] = groupName
|
||||||
|
|
||||||
if isGroupTarget == true or isUnitTarget == true then
|
if isGroupTarget == true or isUnitTarget == true then
|
||||||
self._missionGroups.hasTargets = true
|
self._missionGroups.hasTargets = true
|
||||||
@@ -180,7 +179,6 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw
|
|||||||
|
|
||||||
if self.missionType == "CAS" then
|
if self.missionType == "CAS" then
|
||||||
self._battleManager = BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel)
|
self._battleManager = BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups))
|
self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups))
|
||||||
@@ -354,6 +352,19 @@ end
|
|||||||
function ZoneMission:SpawnInactive()
|
function ZoneMission:SpawnInactive()
|
||||||
self._logger:info("PreActivating " .. self.name)
|
self._logger:info("PreActivating " .. self.name)
|
||||||
|
|
||||||
|
if self.missionType == "DEEPSTRIKE" then
|
||||||
|
local missionData = self._database:getMissionDataForZone(self.zoneName)
|
||||||
|
---@type MissionPriority
|
||||||
|
local priority = "secondary"
|
||||||
|
if missionData and missionData.primaryOverwrite ~= nil then
|
||||||
|
if missionData.primaryOverwrite == true then
|
||||||
|
priority = "primary"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self._priority = priority
|
||||||
|
self._missionCommandsHelper:AddMissionToCommands(self)
|
||||||
|
end
|
||||||
|
|
||||||
for _, group in pairs(self._missionGroups.redGroups) do
|
for _, group in pairs(self._missionGroups.redGroups) do
|
||||||
group:Spawn()
|
group:Spawn()
|
||||||
end
|
end
|
||||||
@@ -390,6 +401,17 @@ function ZoneMission:SpawnActive()
|
|||||||
self._battleManager:Start()
|
self._battleManager:Start()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self._missionCommandsHelper:RemoveMissionToCommands(self) -- Remove first
|
||||||
|
if self.missionType == "DEEPSTRIKE" then
|
||||||
|
local missionData = self._database:getMissionDataForZone(self.zoneName)
|
||||||
|
local priority = "primary"
|
||||||
|
if missionData and missionData.primaryOverwrite ~= nil then
|
||||||
|
if missionData.primaryOverwrite == false then
|
||||||
|
priority = "secondary"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self._priority = priority
|
||||||
|
end
|
||||||
self._missionCommandsHelper:AddMissionToCommands(self)
|
self._missionCommandsHelper:AddMissionToCommands(self)
|
||||||
|
|
||||||
self:StartCheckingContinuous()
|
self:StartCheckingContinuous()
|
||||||
@@ -480,7 +502,7 @@ function ZoneMission:OnUnitLost(object)
|
|||||||
local unitName = object:getName()
|
local unitName = object:getName()
|
||||||
self._logger:debug("UnitName:" .. unitName)
|
self._logger:debug("UnitName:" .. unitName)
|
||||||
|
|
||||||
local groupName = self._missionGroups.groupNamesPerunit[unitName]
|
local groupName = self._missionGroups.groupNamesPerUnit[unitName]
|
||||||
self._missionGroups.unitsAlive[groupName][unitName] = false
|
self._missionGroups.unitsAlive[groupName][unitName] = false
|
||||||
|
|
||||||
if self._missionGroups.targetsAlive[groupName] and self._missionGroups.targetsAlive[groupName][unitName] then
|
if self._missionGroups.targetsAlive[groupName] and self._missionGroups.targetsAlive[groupName][unitName] then
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ local GlobalConfig = require("classes.configuration.GlobalConfig")
|
|||||||
---@field zoneName string
|
---@field zoneName string
|
||||||
---@field missionType MissionType
|
---@field missionType MissionType
|
||||||
---@field missionTypeDisplay string
|
---@field missionTypeDisplay string
|
||||||
---@field priority MissionPriority
|
---@field protected _priority MissionPriority
|
||||||
---@field location Vec2?
|
---@field location Vec2?
|
||||||
---@field code string
|
---@field code string
|
||||||
---@field protected _state MissionState
|
---@field protected _state MissionState
|
||||||
@@ -40,7 +40,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
|
|||||||
self.zoneName = zoneName
|
self.zoneName = zoneName
|
||||||
self.name = missionName
|
self.name = missionName
|
||||||
self.missionType = missionType
|
self.missionType = missionType
|
||||||
self.priority = priority
|
self._priority = priority
|
||||||
self._state = "NEW"
|
self._state = "NEW"
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
self._database = database
|
self._database = database
|
||||||
@@ -64,6 +64,11 @@ end
|
|||||||
|
|
||||||
--region PUBLIC
|
--region PUBLIC
|
||||||
|
|
||||||
|
---@return MissionPriority
|
||||||
|
function Mission:GetPriority()
|
||||||
|
return self._priority
|
||||||
|
end
|
||||||
|
|
||||||
function Mission:SpawnPersistedState() end
|
function Mission:SpawnPersistedState() end
|
||||||
function Mission:SpawnActive() end
|
function Mission:SpawnActive() end
|
||||||
|
|
||||||
@@ -112,7 +117,7 @@ end
|
|||||||
function Mission:NotifyMissionComplete()
|
function Mission:NotifyMissionComplete()
|
||||||
self._missionCommandsHelper:RemoveMissionToCommands(self)
|
self._missionCommandsHelper:RemoveMissionToCommands(self)
|
||||||
self._logger:info("Mission Completed: " .. self.zoneName)
|
self._logger:info("Mission Completed: " .. self.zoneName)
|
||||||
trigger.action.outText("Mission " .. self.name .. " [" .. self.code .. "] was completed succesfully", 20)
|
trigger.action.outText("Mission " .. self.name .. " [" .. self.code .. "] was completed successfully", 20)
|
||||||
|
|
||||||
for _, listener in pairs(self._completeListeners) do
|
for _, listener in pairs(self._completeListeners) do
|
||||||
pcall(function()
|
pcall(function()
|
||||||
@@ -120,12 +125,17 @@ function Mission:NotifyMissionComplete()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local succ, err = pcall(function()
|
local success, err = pcall(function()
|
||||||
SpearheadAPI.Internal.notifyMissionComplete(self.zoneName)
|
SpearheadAPI.Internal.notifyMissionComplete(self.zoneName)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Mission:ForceMissionComplete()
|
||||||
|
self._state = "COMPLETED"
|
||||||
|
self:NotifyMissionComplete()
|
||||||
|
end
|
||||||
|
|
||||||
function Mission:MarkMissionAreaToGroup(groupId) end
|
function Mission:MarkMissionAreaToGroup(groupId) end
|
||||||
|
|
||||||
---endregion
|
---endregion
|
||||||
@@ -148,6 +158,7 @@ do --aliases
|
|||||||
--- @alias MissionType
|
--- @alias MissionType
|
||||||
--- | "nil"
|
--- | "nil"
|
||||||
--- | "STRIKE"
|
--- | "STRIKE"
|
||||||
|
--- | "DEEPSTRIKE"
|
||||||
--- | "CAS"
|
--- | "CAS"
|
||||||
--- | "BAI"
|
--- | "BAI"
|
||||||
--- | "DEAD"
|
--- | "DEAD"
|
||||||
|
|||||||
@@ -632,13 +632,19 @@ do -- INIT DCS_UTIL
|
|||||||
---@field b number
|
---@field b number
|
||||||
---@field a number
|
---@field a number
|
||||||
|
|
||||||
|
local drawID = 4210
|
||||||
|
|
||||||
|
function DCS_UTIL.GetNextDrawID()
|
||||||
|
drawID = drawID + 1
|
||||||
|
return drawID
|
||||||
|
end
|
||||||
|
|
||||||
---@param groupID number
|
---@param groupID number
|
||||||
---@param text string
|
---@param text string
|
||||||
---@param location Vec3
|
---@param location Vec3
|
||||||
---@return number markID
|
---@return number markID
|
||||||
function DCS_UTIL.AddMarkToGroup(groupID, text, location)
|
function DCS_UTIL.AddMarkToGroup(groupID, text, location)
|
||||||
drawID = drawID + 1
|
local drawID = DCS_UTIL.GetNextDrawID()
|
||||||
trigger.action.markToGroup(drawID, text, location, groupID, true, nil)
|
trigger.action.markToGroup(drawID, text, location, groupID, true, nil)
|
||||||
return drawID
|
return drawID
|
||||||
end
|
end
|
||||||
@@ -648,7 +654,7 @@ do -- INIT DCS_UTIL
|
|||||||
---@param location Vec3
|
---@param location Vec3
|
||||||
---@return integer
|
---@return integer
|
||||||
function DCS_UTIL.AddMarkToAll(text, location)
|
function DCS_UTIL.AddMarkToAll(text, location)
|
||||||
drawID = drawID + 1
|
local drawID = DCS_UTIL.GetNextDrawID()
|
||||||
trigger.action.markToAll(drawID, text, location, true, nil)
|
trigger.action.markToAll(drawID, text, location, true, nil)
|
||||||
return drawID
|
return drawID
|
||||||
end
|
end
|
||||||
@@ -677,6 +683,10 @@ do -- INIT DCS_UTIL
|
|||||||
---@param drawID number
|
---@param drawID number
|
||||||
---@param fillColor DrawColor
|
---@param fillColor DrawColor
|
||||||
function DCS_UTIL.SetFillColor(drawID, fillColor)
|
function DCS_UTIL.SetFillColor(drawID, fillColor)
|
||||||
|
if fillColor == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local lineColorMapped = {
|
local lineColorMapped = {
|
||||||
fillColor.r or 0,
|
fillColor.r or 0,
|
||||||
fillColor.g or 0,
|
fillColor.g or 0,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
---@class Util
|
---@class Util
|
||||||
local UTIL = {}
|
local UTIL = {}
|
||||||
do -- INIT UTIL
|
do -- INIT UTIL
|
||||||
---splits a string in sub parts by seperator
|
---splits a string in sub parts by separator
|
||||||
---@param input string
|
---@param input string
|
||||||
---@param seperator string
|
---@param separator string
|
||||||
---@return table result list of strings
|
---@return table result list of strings
|
||||||
function UTIL.split_string(input, seperator)
|
function UTIL.split_string(input, separator)
|
||||||
if seperator == nil then
|
if separator == nil then
|
||||||
seperator = " "
|
separator = " "
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
@@ -15,7 +15,7 @@ do -- INIT UTIL
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
for str in string.gmatch(input, "[^" .. seperator .. "]+") do
|
for str in string.gmatch(input, "[^" .. separator .. "]+") do
|
||||||
table.insert(result, str)
|
table.insert(result, str)
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
@@ -120,7 +120,7 @@ do -- INIT UTIL
|
|||||||
---@param findable string
|
---@param findable string
|
||||||
---@param ignoreCase boolean?
|
---@param ignoreCase boolean?
|
||||||
---@return boolean
|
---@return boolean
|
||||||
UTIL.startswith = function(str, findable, ignoreCase)
|
UTIL.startsWith = function(str, findable, ignoreCase)
|
||||||
if ignoreCase == true then
|
if ignoreCase == true then
|
||||||
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
|
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
|
||||||
end
|
end
|
||||||
@@ -128,6 +128,14 @@ do -- INIT UTIL
|
|||||||
return str:find('^' .. findable) ~= nil
|
return str:find('^' .. findable) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
UTIL.endsWith = 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
|
---comment
|
||||||
---@param str string
|
---@param str string
|
||||||
---@param findable string
|
---@param findable string
|
||||||
@@ -142,7 +150,7 @@ do -- INIT UTIL
|
|||||||
---@return boolean
|
---@return boolean
|
||||||
UTIL.startswithAny = function(str, findableTable)
|
UTIL.startswithAny = function(str, findableTable)
|
||||||
for key, value in pairs(findableTable) do
|
for key, value in pairs(findableTable) do
|
||||||
if type(value) == "string" and UTIL.startswith(str, value) then return true end
|
if type(value) == "string" and UTIL.startsWith(str, value) then return true end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
+21
-20
@@ -5,9 +5,11 @@ local DcsUtil = require("classes.util.DcsUtil")
|
|||||||
local Database = require("classes.spearhead_db")
|
local Database = require("classes.spearhead_db")
|
||||||
local SpearheadEvents = require("classes.spearhead_events")
|
local SpearheadEvents = require("classes.spearhead_events")
|
||||||
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
|
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
|
||||||
|
local GlobalConfig = require("classes.configuration.GlobalConfig")
|
||||||
local CapConfig = require("classes.configuration.CapConfig")
|
local CapConfig = require("classes.configuration.CapConfig")
|
||||||
local StageConfig = require("classes.configuration.StageConfig")
|
local StageConfig = require("classes.configuration.StageConfig")
|
||||||
local Persistence = require("classes.persistence.Persistence")
|
local Persistence = require("classes.persistence.Persistence")
|
||||||
|
local PersistenceConfig = require("classes.configuration.PersistenceConfig")
|
||||||
local SpawnManager = require("classes.helpers.SpawnManager")
|
local SpawnManager = require("classes.helpers.SpawnManager")
|
||||||
local DetectionManager = require("classes.capClasses.detection.DetectionManager")
|
local DetectionManager = require("classes.capClasses.detection.DetectionManager")
|
||||||
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
|
||||||
@@ -33,19 +35,11 @@ MissionCommandsHelper.getOrCreate() -- initiate
|
|||||||
local capConfig = CapConfig:new();
|
local capConfig = CapConfig:new();
|
||||||
local stageConfig = StageConfig:getInstance();
|
local stageConfig = StageConfig:getInstance();
|
||||||
|
|
||||||
local startingStage = stageConfig.startingStage or 1
|
local persistenceConfig = PersistenceConfig.new()
|
||||||
if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then
|
|
||||||
standardLogger:info("Persistence enabled")
|
if persistenceConfig and persistenceConfig:isEnabled() == true then
|
||||||
local persistenceLogger = Logger.new("Persistence", defaultLogLevel)
|
local persistenceLogger = Logger.new("Persistence", defaultLogLevel)
|
||||||
Persistence.Init(persistenceLogger)
|
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
|
end
|
||||||
|
|
||||||
local spawnLogger = Logger.new("SpawnManager", defaultLogLevel)
|
local spawnLogger = Logger.new("SpawnManager", defaultLogLevel)
|
||||||
@@ -54,21 +48,28 @@ local detectionLogger = Logger.new("DetectionManager", defaultLogLevel)
|
|||||||
local detectionManager = DetectionManager.New(detectionLogger)
|
local detectionManager = DetectionManager.New(detectionLogger)
|
||||||
|
|
||||||
GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
|
GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
|
||||||
GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
|
local globalStageManager = GlobalStageManager.new(databaseManager, stageConfig, defaultLogLevel, spawnManager)
|
||||||
GlobalFleetManager.start(databaseManager)
|
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")
|
env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms")
|
||||||
local duration = (timer.getTime() * 1000) - startTime
|
local duration = (timer.getTime() * 1000) - startTime
|
||||||
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
|
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
|
||||||
|
|
||||||
local missionEditorWarningsLogger = Logger.new("MissionEditorWarnings", defaultLogLevel)
|
local missionEditorWarningsLogger = Logger.new("MissionEditorWarnings", defaultLogLevel)
|
||||||
MissionEditorWarnings.WriteAll(missionEditorWarningsLogger)
|
MissionEditorWarnings.WriteAll(missionEditorWarningsLogger)
|
||||||
GlobalStageManager:printFullOverview()
|
globalStageManager:PrintMermaidStage()
|
||||||
|
|
||||||
|
local startDelayed = function()
|
||||||
|
globalStageManager:Start()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(startDelayed, nil, timer.getTime() + 5) -- delay start by 5 seconds so every deleted item it first removed entirely
|
||||||
|
|
||||||
|
local globalConfig = GlobalConfig.New()
|
||||||
|
if globalConfig and globalConfig:isDebugMenuEnabled() == true then
|
||||||
|
local DebugMenu = require("classes.debug.DebugMenu")
|
||||||
|
local debugMenu = DebugMenu.new()
|
||||||
|
debugMenu:RegisterMenus()
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user