Sweep cap (#54)
* Added initial implementation of Sweep groups * Added initial implementation of Sweep groups * Added initial implementation of Sweep groups * Added initial implementation of Sweep groups * fixed issues when testing * Intercept cap (#55) * First intial WIP for intercept units * First intial WIP for intercept units * major updates --------- Co-authored-by: ex61wi <tim.rorije@ing.com> Co-authored-by: dutchie032 <dutchie032> --------- Co-authored-by: ex61wi <tim.rorije@ing.com> Co-authored-by: dutchie032 <dutchie032>
This commit is contained in:
committed by
GitHub
co-authored by
ex61wi
dutchie032 <dutchie032>
parent
bdf7154333
commit
ef65f5a353
Vendored
+2
-1
@@ -15,5 +15,6 @@
|
|||||||
"foldStartRegex": "[\\s]*--region[\\s]*(.*)",
|
"foldStartRegex": "[\\s]*--region[\\s]*(.*)",
|
||||||
"disableFolding": false
|
"disableFolding": false
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"livePreview.serverRoot": "/_docs"
|
||||||
}
|
}
|
||||||
@@ -544,8 +544,24 @@ do -- Controller
|
|||||||
---@class Controller
|
---@class Controller
|
||||||
---@field setTask fun(self:Controller, task: Task) Sets the task of the controller to the passed task
|
---@field setTask fun(self:Controller, task: Task) Sets the task of the controller to the passed task
|
||||||
---@field setCommand function
|
---@field setCommand function
|
||||||
|
---@field getDetectedTargets fun(self:Controller, type1: DetectionType?, type2: DetectionType?, type3: DetectionType?) : Array<DetectedObject> Returns a list of units that are detected by the controller. <br>Only works for unit controllers, not group controllers.
|
||||||
Controller = Controller
|
Controller = Controller
|
||||||
|
|
||||||
|
---@enum DetectionType
|
||||||
|
Controller.Detection = {
|
||||||
|
VISUAL = 1,
|
||||||
|
OPTIC = 2,
|
||||||
|
RADAR = 4,
|
||||||
|
IRST = 8,
|
||||||
|
RWR = 16,
|
||||||
|
DLINK = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
---@class DetectedObject
|
||||||
|
---@field object Object
|
||||||
|
---@field visible boolean
|
||||||
|
---@field type boolean
|
||||||
|
---@field distance boolean
|
||||||
|
|
||||||
---@alias TaskType
|
---@alias TaskType
|
||||||
---| "NoTask"
|
---| "NoTask"
|
||||||
|
|||||||
+8
-28
@@ -5,46 +5,26 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Spearhead</title>
|
<title>Spearhead</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="style/style.css">
|
<link rel="stylesheet" href="style/style.css">
|
||||||
|
|
||||||
<script src="js/site.js"></script>
|
<script src="js/site.js"></script>
|
||||||
|
<script type="module" src="js/components.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="header-box">
|
<app-header></app-header>
|
||||||
<a class="logo" href="index.html">Spearhead</a>
|
|
||||||
<div class="header-right">
|
|
||||||
<nav>
|
|
||||||
<a href="index.html">Home</a>
|
|
||||||
<a href="pages/tutorials.html">Tutorials</a>
|
|
||||||
<a href="pages/persistence.html">Persistence</a>
|
|
||||||
<a href="pages/reference.html">Reference</a>
|
|
||||||
<a href="pages/spearheadapi.html">API</a>
|
|
||||||
</nav>
|
|
||||||
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
|
|
||||||
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
|
|
||||||
</svg>
|
|
||||||
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="display: none;">
|
|
||||||
<path d="M6.995 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007-2.246-5.007-5.007-5.007S6.995 9.239 6.995 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"/>
|
|
||||||
<path transform="rotate(-45.017 5.986 18.01)" d="M4.986 17.01H6.986V19.01H4.986z"/>
|
|
||||||
<path transform="rotate(-45.001 18.008 5.99)" d="M17.008 4.99H19.008V6.99H17.008z"/>
|
|
||||||
<path transform="rotate(-134.983 5.988 5.99)" d="M4.988 4.99H6.988V6.99H4.988z"/>
|
|
||||||
<path transform="rotate(134.999 18.008 18.01)" d="M17.008 17.01H19.008V19.01H17.008z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div class="reference-container">
|
<div class="reference-container">
|
||||||
<div class="side-nav">
|
<div class="side-nav">
|
||||||
<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>
|
||||||
<li><a href="pages/persistence.html" class="side-nav-h2">Persistence</a></li>
|
<li><a href="/pages/persistence.html" class="side-nav-h2">Persistence</a></li>
|
||||||
<li><a href="pages/reference.html" class="side-nav-h2">Reference</a></li>
|
<li><a href="/pages/reference.html" class="side-nav-h2">Reference</a></li>
|
||||||
<li><a href="pages/spearheadapi.html" class="side-nav-h2">API</a></li>
|
<li><a href="/pages/spearheadapi.html" class="side-nav-h2">API</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<style>
|
|
||||||
.side-nav a.active {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #4fc3f7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<!-- Removed inline script: sidenav highlight now handled by site.js -->
|
|
||||||
@@ -4,69 +4,36 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Spearhead Persistence</title>
|
<title>Spearhead Persistence</title> <!-- Google Fonts -->
|
||||||
<link rel="stylesheet" href="../style/style.css">
|
|
||||||
<script src="../js/site.js"></script>
|
<link rel="stylesheet" href="/style/style.css">
|
||||||
|
<script src="/js/site.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.side-nav a.active {
|
.side-nav a.active {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #4fc3f7;
|
color: #4fc3f7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script type="module" src="/js/components.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="header-box">
|
<app-header></app-header>
|
||||||
<a class="logo" href="../index.html">Spearhead</a>
|
</header> <main>
|
||||||
<div class="header-right">
|
|
||||||
<nav>
|
|
||||||
<a href="../index.html">Home</a>
|
|
||||||
<a href="../pages/tutorials.html">Tutorials</a>
|
|
||||||
<a href="../pages/persistence.html">Persistence</a>
|
|
||||||
<a href="../pages/reference.html">Reference</a>
|
|
||||||
<a href="../pages/spearheadapi.html">API</a>
|
|
||||||
</nav>
|
|
||||||
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
|
|
||||||
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
|
|
||||||
</svg>
|
|
||||||
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="display: none;">
|
|
||||||
<path d="M6.995 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007-2.246-5.007-5.007-5.007S6.995 9.239 6.995 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"/>
|
|
||||||
<path transform="rotate(-45.017 5.986 18.01)" d="M4.986 17.01H6.986V19.01H4.986z"/>
|
|
||||||
<path transform="rotate(-45.001 18.008 5.99)" d="M17.008 4.99H19.008V6.99H17.008z"/>
|
|
||||||
<path transform="rotate(-134.983 5.988 5.99)" d="M4.988 4.99H6.988V6.99H4.988z"/>
|
|
||||||
<path transform="rotate(134.999 18.008 18.01)" d="M17.008 17.01H19.008V19.01H17.008z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div class="reference-container">
|
<div class="reference-container">
|
||||||
<div class="side-nav">
|
<app-sidebar></app-sidebar>
|
||||||
<h4 class="side-nav-title"></h4>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#settings" class="side-nav-h2">Settings</a></li>
|
|
||||||
<li><a href="#feedback" class="side-nav-h2">Feedback</a></li>
|
|
||||||
<li><a href="#basic-behavior" class="side-nav-h2">Basic Behavior</a></li>
|
|
||||||
<li><a href="#misc-units" class="side-nav-h2">Misc Units</a></li>
|
|
||||||
<li><a href="#missions" class="side-nav-h2">Missions</a></li>
|
|
||||||
<li><a href="#blue-sams" class="side-nav-h2">Blue SAMs</a></li>
|
|
||||||
<li><a href="#airbases" class="side-nav-h2">Airbases</a></li>
|
|
||||||
<li><a href="#warehouses" class="side-nav-h2">Warehouses</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<h1>Persistence</h1>
|
<h1>Persistence</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Spearhead comes with a custom Persistence option. <br/>
|
Spearhead comes with a custom Persistence option. <br />
|
||||||
It will even save burned out vehicles to give players a consistent battlefield even after the restart. <br/>
|
It will even save burned out vehicles to give players a consistent battlefield even after the
|
||||||
|
restart. <br />
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Most is pretty straightforward, however, some zones are somewhat special. <br/>
|
Most is pretty straightforward, however, some zones are somewhat special. <br />
|
||||||
Underneath you'll see all special zones listed.
|
Underneath you'll see all special zones listed.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -77,9 +44,10 @@
|
|||||||
|
|
||||||
<h2 id="feedback">Feedback</h2>
|
<h2 id="feedback">Feedback</h2>
|
||||||
<p>
|
<p>
|
||||||
Since this feature is still very much in development, please let any issues be known as soon as possible and as concise as possible so a fix can be made quickly. <br/>
|
Since this feature is still very much in development, please let any issues be known as soon as
|
||||||
Currently implemented is local file storage. <br/>
|
possible and as concise as possible so a fix can be made quickly. <br />
|
||||||
If enough interest is expressed, cloud-based persistence would be possible. <br/>
|
Currently implemented is local file storage. <br />
|
||||||
|
If enough interest is expressed, cloud-based persistence would be possible. <br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@@ -102,40 +70,46 @@
|
|||||||
|
|
||||||
<h2 id="basic-behavior">Basic Behavior</h2>
|
<h2 id="basic-behavior">Basic Behavior</h2>
|
||||||
<p>
|
<p>
|
||||||
While playing the mission, Spearhead is keeping track of all units killed. <br/>
|
While playing the mission, Spearhead is keeping track of all units killed. <br />
|
||||||
These units are stored in memory internally and written to file. <br/>
|
These units are stored in memory internally and written to file. <br />
|
||||||
This happens every 2 minutes AND during the "onMissionStop" event to make sure the mission is as up-to-date as possible without having to call IO methods on each event.<br/>
|
This happens every 2 minutes AND during the "onMissionStop" event to make sure the mission is as
|
||||||
|
up-to-date as possible without having to call IO methods on each event.<br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="misc-units">Misc Units</h2>
|
<h2 id="misc-units">Misc Units</h2>
|
||||||
<p>
|
<p>
|
||||||
Miscellaneous units will follow basic behavior. <br/>
|
Miscellaneous units will follow basic behavior. <br />
|
||||||
These are units that are part of a stage but are not in a mission or airbase. <br/>
|
These are units that are part of a stage but are not in a mission or airbase. <br />
|
||||||
These units will be replaced by a static "DEAD" unit after a mission restart at the location it was killed. <br/>
|
These units will be replaced by a static "DEAD" unit after a mission restart at the location it was
|
||||||
Due to blue units spawning afterwards, it's generally best to not have these units move through or over areas where BLUESAMS and Airbase units will spawn after a stage completion.
|
killed. <br />
|
||||||
|
Due to blue units spawning afterwards, it's generally best to not have these units move through or
|
||||||
|
over areas where BLUESAMS and Airbase units will spawn after a stage completion.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="missions">Missions</h2>
|
<h2 id="missions">Missions</h2>
|
||||||
<p>
|
<p>
|
||||||
Missions follow the same logic as Misc Units. <br/>
|
Missions follow the same logic as Misc Units. <br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="blue-sams">Blue SAMs</h2>
|
<h2 id="blue-sams">Blue SAMs</h2>
|
||||||
<p>
|
<p>
|
||||||
For Blue SAMs, due to placements easily overlapping between red and blue units within a BLUESAM trigger zone, red units that overlap with blue units will be deleted. <br/>
|
For Blue SAMs, due to placements easily overlapping between red and blue units within a BLUESAM
|
||||||
This will ensure that the blue units are placed as needed. <br/>
|
trigger zone, red units that overlap with blue units will be deleted. <br />
|
||||||
|
This will ensure that the blue units are placed as needed. <br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="airbases">Airbases</h2>
|
<h2 id="airbases">Airbases</h2>
|
||||||
<p>
|
<p>
|
||||||
Airbase units will also be checked for overlap. As the blue units will be spawned after the RED unit. <br/>
|
Airbase units will also be checked for overlap. As the blue units will be spawned after the RED
|
||||||
Units that were alive when the stage was completed will be removed. Units that died will have corpses spawned. <br/>
|
unit. <br />
|
||||||
|
Units that were alive when the stage was completed will be removed. Units that died will have
|
||||||
|
corpses spawned. <br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="warehouses">Warehouses</h2>
|
<h2 id="warehouses">Warehouses</h2>
|
||||||
<p>
|
<p>
|
||||||
Currently, warehouses are not implemented and therefore warehouses are not persisted. <br/>
|
Currently, warehouses are not implemented and therefore warehouses are not persisted. <br />
|
||||||
When supply missions and logistics get implemented, warehouses will be persisted as well. <br/>
|
When supply missions and logistics get implemented, warehouses will be persisted as well. <br />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,4 +120,4 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Spearhead Reference</title>
|
<title>Spearhead Reference</title> <!-- Google Fonts -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="../style/style.css">
|
<link rel="stylesheet" href="../style/style.css">
|
||||||
<script src="../js/site.js"></script>
|
<script src="../js/site.js"></script>
|
||||||
<style>
|
<style>
|
||||||
@@ -12,78 +13,17 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #4fc3f7;
|
color: #4fc3f7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script type="module" src="../js/components.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="header-box">
|
<app-header></app-header>
|
||||||
<a class="logo" href="../index.html">Spearhead</a>
|
</header>
|
||||||
<div class="header-right">
|
|
||||||
<nav>
|
|
||||||
<a href="../index.html">Home</a>
|
|
||||||
<a href="../pages/tutorials.html">Tutorials</a>
|
|
||||||
<a href="../pages/persistence.html">Persistence</a>
|
|
||||||
<a href="../pages/reference.html">Reference</a>
|
|
||||||
<a href="../pages/spearheadapi.html">API</a>
|
|
||||||
</nav>
|
|
||||||
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
|
|
||||||
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
|
|
||||||
</svg>
|
|
||||||
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="display: none;">
|
|
||||||
<path d="M6.995 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007-2.246-5.007-5.007-5.007S6.995 9.239 6.995 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"/>
|
|
||||||
<path transform="rotate(-45.017 5.986 18.01)" d="M4.986 17.01H6.986V19.01H4.986z"/>
|
|
||||||
<path transform="rotate(-45.001 18.008 5.99)" d="M17.008 4.99H19.008V6.99H17.008z"/>
|
|
||||||
<path transform="rotate(-134.983 5.988 5.99)" d="M4.988 4.99H6.988V6.99H4.988z"/>
|
|
||||||
<path transform="rotate(134.999 18.008 18.01)" d="M17.008 17.01H19.008V19.01H17.008z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main>
|
<main>
|
||||||
<div class="reference-container">
|
<div class="reference-container">
|
||||||
<div class="side-nav">
|
<app-sidebar></app-sidebar>
|
||||||
<h4 class="side-nav-title"></h4>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#configuration" class="side-nav-h2">Configuration</a></li>
|
|
||||||
<li><a href="#general-naming-conventions" class="side-nav-h2">Naming Conventions</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#stage-zones" class="side-nav-h3">Stage Zones</a></li>
|
|
||||||
<li><a href="#waiting-stages" class="side-nav-h3">Waiting Stages</a></li>
|
|
||||||
<li><a href="#mission-zones" class="side-nav-h3">Mission Zones</a></li>
|
|
||||||
<li><a href="#cap-routes" class="side-nav-h3">CAP Routes</a></li>
|
|
||||||
<li><a href="#farp-zones" class="side-nav-h3">FARP Zones</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a href="#mission-types" class="side-nav-h2">Mission Types</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#sam" class="side-nav-h3">SAM</a></li>
|
|
||||||
<li><a href="#dead" class="side-nav-h3">DEAD</a></li>
|
|
||||||
<li><a href="#bai" class="side-nav-h3">BAI</a></li>
|
|
||||||
<li><a href="#cas" class="side-nav-h3">BAI</a></li>
|
|
||||||
<li><a href="#strike" class="side-nav-h3">STRIKE</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a href="#mission-briefings" class="side-nav-h2">Mission Briefings</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#special-fields" class="side-nav-h3">Special Fields</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a href="#cap-configuration" class="side-nav-h2">CAP Configuration</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#cap-group-naming" class="side-nav-h3">CAP Group Naming</a></li>
|
|
||||||
<li><a href="#cap-group-config" class="side-nav-h3">CAP Group Config</a></li>
|
|
||||||
<li><a href="#active-and-backup-cap" class="side-nav-h3">Active and Backup CAP</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a href="#randomization" class="side-nav-h2">Randomization</a></li>
|
|
||||||
<li><a href="#in-stage-dependencies" class="side-nav-h2">In stage dependencies</a></li>
|
|
||||||
<li><a href="#buildable" class="side-nav-h2">Buildables</a></li>
|
|
||||||
<li><a href="#runway-bombing" class="side-nav-h2">Runway Bombing</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<h1>Reference</h1>
|
<h1>Reference</h1>
|
||||||
|
|||||||
@@ -5,46 +5,18 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Spearhead API</title>
|
<title>Spearhead API</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="../style/style.css">
|
<link rel="stylesheet" href="../style/style.css">
|
||||||
<script src="../js/site.js"></script>
|
<script src="../js/site.js"></script>
|
||||||
|
<script type="module" src="../js/components.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="header-box">
|
<app-header></app-header>
|
||||||
<a class="logo" href="../index.html">Spearhead</a>
|
</header> <main>
|
||||||
<div class="header-right">
|
|
||||||
<nav>
|
|
||||||
<a href="../index.html">Home</a>
|
|
||||||
<a href="../pages/tutorials.html">Tutorials</a>
|
|
||||||
<a href="../pages/persistence.html">Persistence</a>
|
|
||||||
<a href="../pages/reference.html">Reference</a>
|
|
||||||
<a href="../pages/spearheadapi.html">API</a>
|
|
||||||
</nav>
|
|
||||||
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
|
|
||||||
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
|
|
||||||
</svg>
|
|
||||||
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="display: none;">
|
|
||||||
<path d="M6.995 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007-2.246-5.007-5.007-5.007S6.995 9.239 6.995 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"/>
|
|
||||||
<path transform="rotate(-45.017 5.986 18.01)" d="M4.986 17.01H6.986V19.01H4.986z"/>
|
|
||||||
<path transform="rotate(-45.001 18.008 5.99)" d="M17.008 4.99H19.008V6.99H17.008z"/>
|
|
||||||
<path transform="rotate(-134.983 5.988 5.99)" d="M4.988 4.99H6.988V6.99H4.988z"/>
|
|
||||||
<path transform="rotate(134.999 18.008 18.01)" d="M17.008 17.01H19.008V19.01H17.008z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div class="reference-container">
|
<div class="reference-container">
|
||||||
<div class="side-nav">
|
<app-sidebar></app-sidebar>
|
||||||
<h4 class="side-nav-title"></h4>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#introduction" class="side-nav-h2">Introduction</a></li>
|
|
||||||
<li><a href="#stages" class="side-nav-h2">Stages</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<h1>Spearhead API</h1>
|
<h1>Spearhead API</h1>
|
||||||
|
|||||||
@@ -5,65 +5,24 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Spearhead Tutorials</title>
|
<title>Spearhead Tutorials</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="../style/style.css">
|
<link rel="stylesheet" href="../style/style.css">
|
||||||
<script src="../js/site.js"></script>
|
<script src="../js/site.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.side-nav a.active {
|
.side-nav a.active {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #4fc3f7;
|
color: #4fc3f7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script type="module" src="../js/components.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="header-box">
|
<app-header></app-header>
|
||||||
<a class="logo" href="../index.html">Spearhead</a>
|
</header> <main>
|
||||||
<div class="header-right">
|
|
||||||
<nav>
|
|
||||||
<a href="../index.html">Home</a>
|
|
||||||
<a href="../pages/tutorials.html">Tutorials</a>
|
|
||||||
<a href="../pages/persistence.html">Persistence</a>
|
|
||||||
<a href="../pages/reference.html">Reference</a>
|
|
||||||
<a href="../pages/spearheadapi.html">API</a>
|
|
||||||
</nav>
|
|
||||||
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
|
|
||||||
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
|
|
||||||
</svg>
|
|
||||||
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="display: none;">
|
|
||||||
<path d="M6.995 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007-2.246-5.007-5.007-5.007S6.995 9.239 6.995 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"/>
|
|
||||||
<path transform="rotate(-45.017 5.986 18.01)" d="M4.986 17.01H6.986V19.01H4.986z"/>
|
|
||||||
<path transform="rotate(-45.001 18.008 5.99)" d="M17.008 4.99H19.008V6.99H17.008z"/>
|
|
||||||
<path transform="rotate(-134.983 5.988 5.99)" d="M4.988 4.99H6.988V6.99H4.988z"/>
|
|
||||||
<path transform="rotate(134.999 18.008 18.01)" d="M17.008 17.01H19.008V19.01H17.008z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div class="reference-container">
|
<div class="reference-container">
|
||||||
<div class="side-nav">
|
<app-sidebar></app-sidebar>
|
||||||
<h4 class="side-nav-title"></h4>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#include-the-script" class="side-nav-h2">Include the Script</a></li>
|
|
||||||
<li><a href="#stages" class="side-nav-h2">Stages</a></li>
|
|
||||||
<li><a href="#setting-up-cap" class="side-nav-h2">Setting up CAP</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#creating-cap-routes" class="side-nav-h3">Creating CAP Routes</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a href="#setting-up-the-missions" class="side-nav-h2">Setting up Missions</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#mission-1-dead" class="side-nav-h3">Mission: DEAD</a></li>
|
|
||||||
<li><a href="#mission-2-strike" class="side-nav-h3">Mission: STRIKE</a></li>
|
|
||||||
<li><a href="#mission-cas" class="side-nav-h3">Mission: CAS</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a href="#mission-briefings" class="side-nav-h2">Mission Briefings</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<h1>Tutorials</h1>
|
<h1>Tutorials</h1>
|
||||||
|
|||||||
+99
-37
@@ -1,16 +1,17 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Dark Theme (Default) - Slightly lighter */
|
/* Dark Theme (Default) - Slightly lighter */
|
||||||
--color-bg-primary: #0a0b10; /* Much darker primary background */
|
--color-bg-primary: #1b1d27; /* Much darker primary background */
|
||||||
--color-bg-secondary: #13141a; /* Darker secondary background */
|
--color-bg-secondary: #2d303f; /* Darker secondary background */
|
||||||
--color-bg-tertiary: #181a22; /* Darker tertiary */
|
--color-bg-tertiary: #2c3041; /* Darker tertiary */
|
||||||
--color-bg-header: #090a0f; /* Darker header */
|
--color-bg-header: #10121b; /* Darker header */
|
||||||
--color-bg-code: #10121a; /* Almost black code block background */
|
--color-bg-code: #10121a; /* Almost black code block background */
|
||||||
--color-bg-note: rgba(30, 32, 40, 0.85); /* Slightly darker note background */
|
--color-bg-note: rgba(30, 32, 40, 0.85); /* Slightly darker note background */
|
||||||
--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-inline-lua: #181a22; /* Match code block background */
|
--color-bg-inline-lua: #181a22; /* Match code block background */
|
||||||
--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: #f6f8fa; /* Brighter text */
|
--color-text-primary: #a5a5a5; /* 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;
|
||||||
@@ -32,10 +33,11 @@
|
|||||||
/* Shadow and transparency values - unchanged */
|
/* Shadow and transparency values - unchanged */
|
||||||
--shadow-small: 0 2px 5px rgba(0, 0, 0, 0.3);
|
--shadow-small: 0 2px 5px rgba(0, 0, 0, 0.3);
|
||||||
--shadow-medium: 0 3px 8px rgba(0, 0, 0, 0.5);
|
--shadow-medium: 0 3px 8px rgba(0, 0, 0, 0.5);
|
||||||
--shadow-large: 0 4px 12px rgba(0, 0, 0, 0.6);
|
--shadow-large: 0 4px 12px rgba(0, 0, 0, 0.6); /* Common opacity values */
|
||||||
|
--opacity-sidenav: 0.85; /* Increased opacity for better contrast */ /* Font families with improved Inter setup */
|
||||||
/* Common opacity values */
|
--font-family-primary: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
--opacity-sidenav: 0.85; /* Increased opacity for better contrast */
|
--font-family-code: 'JetBrains Mono', 'Monaco', 'Cascadia Code', monospace;
|
||||||
|
--font-family-heading: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
|
||||||
/* Responsive margins - unchanged */
|
/* Responsive margins - unchanged */
|
||||||
--margin-left: 20%;
|
--margin-left: 20%;
|
||||||
@@ -58,17 +60,23 @@
|
|||||||
--color-bg-header-accent: #2a2620;
|
--color-bg-header-accent: #2a2620;
|
||||||
--color-logo-gradient: linear-gradient(120deg, #5caaff, #a0e8ff, #ffffff);
|
--color-logo-gradient: linear-gradient(120deg, #5caaff, #a0e8ff, #ffffff);
|
||||||
--color-nav-underline: #b0a890;
|
--color-nav-underline: #b0a890;
|
||||||
--color-nav-underline-glow: rgba(176, 168, 144, 0.6);
|
--color-nav-underline-glow: rgba(176, 168, 144, 0.6); --color-nav-hover-bg: var(--color-bg-hover);
|
||||||
--color-nav-hover-bg: var(--color-bg-hover);
|
|
||||||
--color-nav-hover-text: #ffffff;
|
--color-nav-hover-text: #ffffff;
|
||||||
--color-nav-hover-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
--color-nav-hover-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
/* Heading colors */
|
||||||
|
--color-heading-h1: #8fc6ff;
|
||||||
|
--color-heading-h2: #b3e0ff;
|
||||||
|
--color-heading-h3: #c9e6ff;
|
||||||
|
--color-heading-h1-border: #8fc6ff;
|
||||||
|
--color-heading-h2-border: #b3e0ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] {
|
[data-theme="light"] {
|
||||||
--color-bg-primary: #f5f7ff;
|
--color-bg-primary: #e0e0e0;
|
||||||
--color-bg-secondary: #e8ecf7;
|
--color-bg-secondary: #b6b6b6;
|
||||||
--color-bg-tertiary: #dce2f0;
|
--color-bg-tertiary: #b6b6b6;
|
||||||
--color-bg-header: #f0f2fa;
|
--color-bg-header: #d3d3d3;
|
||||||
--color-bg-code: #e3e6eb; /* Medium-light gray for code block background */
|
--color-bg-code: #e3e6eb; /* Medium-light gray for code block background */
|
||||||
--color-bg-note: rgba(215, 228, 245, 0.75);
|
--color-bg-note: rgba(215, 228, 245, 0.75);
|
||||||
--color-bg-hover: rgba(100, 120, 200, 0.1);
|
--color-bg-hover: rgba(100, 120, 200, 0.1);
|
||||||
@@ -80,8 +88,8 @@
|
|||||||
--color-text-accent: #3050b0;
|
--color-text-accent: #3050b0;
|
||||||
--color-text-light: #2a2e40;
|
--color-text-light: #2a2e40;
|
||||||
|
|
||||||
--color-border-primary: #a0a8b8;
|
--color-border-primary: #496cb3;
|
||||||
--color-border-accent: #5c75d9;
|
--color-border-accent: #1d46ee;
|
||||||
|
|
||||||
--color-link: #3050b0;
|
--color-link: #3050b0;
|
||||||
--color-link-hover: #4060c0;
|
--color-link-hover: #4060c0;
|
||||||
@@ -117,10 +125,16 @@
|
|||||||
--color-bg-header-accent: #f5e9d2;
|
--color-bg-header-accent: #f5e9d2;
|
||||||
--color-logo-gradient: linear-gradient(120deg, #225a9e, #3578c7, #70aaff);
|
--color-logo-gradient: linear-gradient(120deg, #225a9e, #3578c7, #70aaff);
|
||||||
--color-nav-underline: #e0cfa0;
|
--color-nav-underline: #e0cfa0;
|
||||||
--color-nav-underline-glow: rgba(224, 207, 160, 0.4);
|
--color-nav-underline-glow: rgba(224, 207, 160, 0.4); --color-nav-hover-bg: var(--color-bg-hover);
|
||||||
--color-nav-hover-bg: var(--color-bg-hover);
|
|
||||||
--color-nav-hover-text: var(--color-text-primary);
|
--color-nav-hover-text: var(--color-text-primary);
|
||||||
--color-nav-hover-shadow: 0 4px 8px rgba(48, 80, 176, 0.15);
|
--color-nav-hover-shadow: 0 4px 8px rgba(48, 80, 176, 0.15);
|
||||||
|
|
||||||
|
/* Heading colors for light theme */
|
||||||
|
--color-heading-h1: #225a9e;
|
||||||
|
--color-heading-h2: #3578c7;
|
||||||
|
--color-heading-h3: #4a90e2;
|
||||||
|
--color-heading-h1-border: #225a9e;
|
||||||
|
--color-heading-h2-border: #3578c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -131,14 +145,17 @@ html {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
font-family: Arial, sans-serif;
|
font-family: var(--font-family-primary);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.6;
|
line-height: 1.65; /* Increased for better Inter readability */
|
||||||
|
font-weight: 500; /* Medium weight for substantial feel */
|
||||||
|
letter-spacing: 0.015em; /* Slightly more spacing for better readability */
|
||||||
|
font-feature-settings: "cv02", "cv03", "cv04", "cv11"; /* Better Inter alternates */
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +260,35 @@ header nav a:hover::after {
|
|||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
top: 100%; /* Position below the dropdown */
|
||||||
|
left: 0; /* Align left edge */
|
||||||
|
min-width: 160px;
|
||||||
|
|
||||||
|
background-color: var(--color-bg-secondary);
|
||||||
|
box-shadow: var(--shadow-medium);
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
max-height: 0px;
|
||||||
|
transition: all 1.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown:hover .dropdown-content {
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
header.collapsible {
|
header.collapsible {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -292,13 +338,13 @@ main {
|
|||||||
|
|
||||||
/* Side navigation styles */
|
/* Side navigation styles */
|
||||||
.reference-container {
|
.reference-container {
|
||||||
display: flex;
|
display: block; /* Change from flex since sidebar is now fixed */
|
||||||
align-items: flex-start;
|
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
margin-left: var(--margin-left);
|
margin-left: var(--margin-left);
|
||||||
flex-direction: row;
|
|
||||||
gap: 2em;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
/* Ensure the container doesn't interfere with sticky positioning */
|
||||||
|
overflow: visible;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-nav {
|
.side-nav {
|
||||||
@@ -307,9 +353,14 @@ main {
|
|||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
position: sticky;
|
position: fixed;
|
||||||
top: 2em;
|
top: 7em; /* Account for header height (5em) + spacing (2em) */
|
||||||
align-self: flex-start;
|
left: var(--margin-left);
|
||||||
|
max-height: calc(100vh - 9em); /* Account for header + top spacing */
|
||||||
|
z-index: 10;
|
||||||
|
/* Ensure proper stacking context */
|
||||||
|
transform: translateZ(0);
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-nav h4 {
|
.side-nav h4 {
|
||||||
@@ -366,7 +417,7 @@ main {
|
|||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
margin-left: 0;
|
margin-left: 220px; /* Account for fixed sidebar width + gap */
|
||||||
border-left: 2px solid var(--color-border-accent);
|
border-left: 2px solid var(--color-border-accent);
|
||||||
padding-left: 1.5em;
|
padding-left: 1.5em;
|
||||||
padding-bottom: 20vh; /* Allow last section to scroll to top */
|
padding-bottom: 20vh; /* Allow last section to scroll to top */
|
||||||
@@ -377,33 +428,34 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
margin-top: 1.5em;
|
margin-top: 1.5em;
|
||||||
margin-bottom: 0.8em;
|
margin-bottom: 0.8em;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
|
font-family: var(--font-family-heading);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2.2em;
|
font-size: 2.2em;
|
||||||
color: #8fc6ff;
|
color: var(--color-heading-h1);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-shadow: 0 1px 4px rgba(143,198,255,0.08);
|
text-shadow: 0 1px 4px rgba(143,198,255,0.08);
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
border-bottom: 1.5px solid #8fc6ff;
|
border-bottom: 1.5px solid var(--color-heading-h1-border);
|
||||||
padding-bottom: 0.3em;
|
padding-bottom: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.8em;
|
font-size: 1.8em;
|
||||||
color: #b3e0ff;
|
color: var(--color-heading-h2);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-shadow: 0 1px 2px rgba(179,224,255,0.07);
|
text-shadow: 0 1px 2px rgba(179,224,255,0.07);
|
||||||
letter-spacing: 0.005em;
|
letter-spacing: 0.005em;
|
||||||
border-bottom: 1px solid #b3e0ff;
|
border-bottom: 1px solid var(--color-heading-h2-border);
|
||||||
padding-bottom: 0.2em;
|
padding-bottom: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
color: #c9e6ff;
|
color: var(--color-heading-h3);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-shadow: 0 1px 1px rgba(201,230,255,0.06);
|
text-shadow: 0 1px 1px rgba(201,230,255,0.06);
|
||||||
letter-spacing: 0.005em;
|
letter-spacing: 0.005em;
|
||||||
@@ -455,7 +507,7 @@ th {
|
|||||||
.inline-lua {
|
.inline-lua {
|
||||||
background-color: var(--color-bg-inline-lua);
|
background-color: var(--color-bg-inline-lua);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-family: 'Courier New', monospace;
|
font-family: var(--font-family-code);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
color: #23272e;
|
color: #23272e;
|
||||||
@@ -469,7 +521,7 @@ pre {
|
|||||||
padding: 1.2em;
|
padding: 1.2em;
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
font-family: 'Courier New', monospace;
|
font-family: var(--font-family-code);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.08);
|
||||||
color: #23272e;
|
color: #23272e;
|
||||||
@@ -590,6 +642,10 @@ img {
|
|||||||
}
|
}
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.reference-container {
|
||||||
|
margin-left: var(--margin-small);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,6 +661,12 @@ img {
|
|||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
width: 100%; /* Make content take full width */
|
width: 100%; /* Make content take full width */
|
||||||
|
margin-left: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-container {
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@@ -667,4 +729,4 @@ img {
|
|||||||
|
|
||||||
.theme-toggle:hover svg {
|
.theme-toggle:hover svg {
|
||||||
fill: var(--color-text-accent);
|
fill: var(--color-text-accent);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -2,16 +2,25 @@
|
|||||||
---@field private airbaseName string
|
---@field private airbaseName string
|
||||||
---@field private logger table
|
---@field private logger table
|
||||||
---@field private database Database
|
---@field private database Database
|
||||||
|
---@field private detectionManager DetectionManager
|
||||||
---@field private activeStage number
|
---@field private activeStage number
|
||||||
---@field private capConfig table
|
---@field private capConfig table
|
||||||
---@field private capGroupsByName table<string, CapGroup>
|
---@field private capGroupsByName table<string, CapGroup>
|
||||||
|
---@field private sweepGroupsByName table<string, SweepGroup>
|
||||||
|
---@field private interceptGroupsByName table<string, InterceptGroup>
|
||||||
---@field private runwayBombingTracker RunwayBombingTracker
|
---@field private runwayBombingTracker RunwayBombingTracker
|
||||||
---@field private runwayStrikeMissions table<string, RunwayStrikeMission>
|
---@field private runwayStrikeMissions table<string, RunwayStrikeMission>
|
||||||
local CapBase = {}
|
local CapBase = {}
|
||||||
|
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param self CapBase
|
||||||
|
---@param time number
|
||||||
|
---@return number?
|
||||||
local CheckStateContinuous = function(self, time)
|
local CheckStateContinuous = function(self, time)
|
||||||
self:CheckAndScheduleCAP()
|
self:CheckAndScheduleCAP()
|
||||||
|
self:CheckAndScheduleSweep()
|
||||||
|
self:CheckAndScheduleIntercept()
|
||||||
return time + 15
|
return time + 15
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -22,8 +31,9 @@ end
|
|||||||
---@param capConfig table
|
---@param capConfig table
|
||||||
---@param stageConfig table
|
---@param stageConfig table
|
||||||
---@param runwayBombingTracker RunwayBombingTracker
|
---@param runwayBombingTracker RunwayBombingTracker
|
||||||
|
---@param detectionManager DetectionManager
|
||||||
---@return CapBase
|
---@return CapBase
|
||||||
function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runwayBombingTracker)
|
function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runwayBombingTracker, detectionManager)
|
||||||
CapBase.__index = CapBase
|
CapBase.__index = CapBase
|
||||||
local self = setmetatable({}, { __index = CapBase }) --[[@as CapBase]]
|
local self = setmetatable({}, { __index = CapBase }) --[[@as CapBase]]
|
||||||
|
|
||||||
@@ -36,6 +46,9 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
|
|||||||
self.capConfig = capConfig
|
self.capConfig = capConfig
|
||||||
self.database = database
|
self.database = database
|
||||||
self.capGroupsByName = {}
|
self.capGroupsByName = {}
|
||||||
|
self.sweepGroupsByName = {}
|
||||||
|
self.interceptGroupsByName = {}
|
||||||
|
self.detectionManager = detectionManager
|
||||||
|
|
||||||
local baseData = database:getAirbaseDataForZone(airbaseName)
|
local baseData = database:getAirbaseDataForZone(airbaseName)
|
||||||
if baseData and baseData.CapGroups then
|
if baseData and baseData.CapGroups then
|
||||||
@@ -47,14 +60,36 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
logger:info("Airbase with name '" ..
|
if baseData and baseData.SweepGroups then
|
||||||
airbaseName .. "' has a total of " .. Spearhead.Util.tableLength(self.capGroupsByName) .. " cap flights registered")
|
for key, name in pairs(baseData.SweepGroups) do
|
||||||
|
local sweepGroup = Spearhead.classes.capClasses.airGroups.SweepGroup.New(name, capConfig, logger)
|
||||||
|
if sweepGroup then
|
||||||
|
self.sweepGroupsByName[name] = sweepGroup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if baseData and baseData.InterceptGroups then
|
||||||
|
for key, name in pairs(baseData.InterceptGroups) do
|
||||||
|
local interceptGroup = Spearhead.classes.capClasses.airGroups.InterceptGroup.New(name, capConfig, logger, detectionManager)
|
||||||
|
if interceptGroup then
|
||||||
|
self.interceptGroupsByName[name] = interceptGroup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local capFlights = Spearhead.Util.tableLength(self.capGroupsByName)
|
||||||
|
local sweepFlights = Spearhead.Util.tableLength(self.sweepGroupsByName)
|
||||||
|
local interceptFlights = Spearhead.Util.tableLength(self.interceptGroupsByName)
|
||||||
|
|
||||||
|
logger:info(airbaseName .. " : " .. capFlights .." CAP | " .. sweepFlights .. " SWEEP | " .. interceptFlights .. " INTERCEPT")
|
||||||
|
|
||||||
self:CreateRunwayStrikeMission(database)
|
self:CreateRunwayStrikeMission(database)
|
||||||
|
|
||||||
Spearhead.Events.AddStageNumberChangedListener(self)
|
Spearhead.Events.AddStageNumberChangedListener(self)
|
||||||
|
|
||||||
timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15)
|
timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15)
|
||||||
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -91,6 +126,22 @@ function CapBase:SpawnIfApplicable()
|
|||||||
capGroup:Spawn()
|
capGroup:Spawn()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for groupName, sweepGroup in pairs(self.sweepGroupsByName) do
|
||||||
|
local targetStage = sweepGroup:GetZoneIDWhenStageID(tostring(self.activeStage))
|
||||||
|
|
||||||
|
if targetStage ~= nil and sweepGroup:GetState() == "UnSpawned" then
|
||||||
|
sweepGroup:Spawn()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for groupName, interceptGroup in pairs(self.interceptGroupsByName) do
|
||||||
|
local targetStage = interceptGroup:GetZoneIDWhenStageID(tostring(self.activeStage))
|
||||||
|
|
||||||
|
if targetStage ~= nil and interceptGroup:GetState() == "UnSpawned" then
|
||||||
|
interceptGroup:Spawn()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function CapBase:CheckAndScheduleCAP()
|
function CapBase:CheckAndScheduleCAP()
|
||||||
@@ -223,6 +274,116 @@ function CapBase:CheckAndScheduleCAP()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function CapBase:CheckAndScheduleSweep()
|
||||||
|
self.logger:debug("Check sweep taskings for airbase " .. self.airbaseName)
|
||||||
|
|
||||||
|
local airbase = Airbase.getByName(self.airbaseName)
|
||||||
|
if not airbase then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local activeStageID = tostring(self.activeStage)
|
||||||
|
|
||||||
|
for _, group in pairs(self.sweepGroupsByName) do
|
||||||
|
local targetZoneID = group:GetZoneIDWhenStageID(activeStageID)
|
||||||
|
if targetZoneID then
|
||||||
|
local triggerZone = self.database:GetCapZoneForZoneID(targetZoneID)
|
||||||
|
if triggerZone then
|
||||||
|
if group:GetState() == "ReadyOnTheRamp" then
|
||||||
|
group:SendToZone(triggerZone, targetZoneID, airbase)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.logger:debug("SweepGroup " .. group:GetName() .. " has no trigger zone for stage " .. activeStageID)
|
||||||
|
group:SendRTB(airbase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function CapBase:CheckAndScheduleIntercept()
|
||||||
|
|
||||||
|
self.logger:debug("Check intercept taskings for airbase " .. self.airbaseName)
|
||||||
|
|
||||||
|
local interceptZoneIDs = {}
|
||||||
|
|
||||||
|
local airbase = Airbase.getByName(self.airbaseName)
|
||||||
|
if not airbase then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, group in pairs(self.interceptGroupsByName) do
|
||||||
|
local targetZoneID = group:GetZoneIDWhenStageID(tostring(self.activeStage))
|
||||||
|
if targetZoneID then
|
||||||
|
interceptZoneIDs[targetZoneID] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type table<string, Array<string>>
|
||||||
|
local unitsToInterceptPerZone = {}
|
||||||
|
|
||||||
|
local detectedUnits = self.detectionManager:GetDetectedUnitsBy(coalition.side.RED)
|
||||||
|
for targetZoneID, _ in pairs(interceptZoneIDs) do
|
||||||
|
local zones = self.database:GetInterceptZonesForZoneID(targetZoneID)
|
||||||
|
if zones then
|
||||||
|
for _, zone in pairs(zones) do
|
||||||
|
|
||||||
|
self.logger:debug("Check intercept zone " .. zone.name .. " for airbase " .. self.airbaseName)
|
||||||
|
|
||||||
|
for _, unitName in pairs(detectedUnits) do
|
||||||
|
self.logger:debug("Check unit " .. unitName .. " for intercept in zone " .. zone.name)
|
||||||
|
|
||||||
|
local unit = Unit.getByName(unitName)
|
||||||
|
if unit and unit:isExist() then
|
||||||
|
local unitPos = unit:getPoint()
|
||||||
|
if Spearhead.Util.is3dPointInZone(unitPos, zone) == true then
|
||||||
|
if not unitsToInterceptPerZone[zone.name] then
|
||||||
|
unitsToInterceptPerZone[zone.name] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(unitsToInterceptPerZone[zone.name], unitName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---RATIO. Amount of enemy fighters required per zone before another group gets added.
|
||||||
|
local ratio = 4 -- Ratio of units to intercept per zone, can be adjusted
|
||||||
|
|
||||||
|
for name, targets in pairs(unitsToInterceptPerZone) do
|
||||||
|
local required = 0
|
||||||
|
local nrTargets = Spearhead.Util.tableLength(targets)
|
||||||
|
if targets and nrTargets > 0 then
|
||||||
|
required = math.ceil(nrTargets / ratio)
|
||||||
|
end
|
||||||
|
|
||||||
|
local total = 0
|
||||||
|
for _, group in pairs(self.interceptGroupsByName) do
|
||||||
|
if group:GetState() == "OnStation" and group:GetCurrentTargetZone() == name then
|
||||||
|
group:SetTargetUnits(targets)
|
||||||
|
total = total + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if total < required then
|
||||||
|
for _, group in pairs(self.interceptGroupsByName) do
|
||||||
|
if total < required then
|
||||||
|
local zoneID = group:GetZoneIDWhenStageID(tostring(self.activeStage))
|
||||||
|
if group:GetState() == "ReadyOnTheRamp" then
|
||||||
|
group:SendToInterceptUnits(targets, name, airbase)
|
||||||
|
total = total + 1
|
||||||
|
|
||||||
|
self.logger:debug("Intercept group " .. group:GetName() .. " sent to intercept zone " .. name )
|
||||||
|
elseif group:GetState() == "InTransit" or group:GetState() == "OnStation" then
|
||||||
|
group:SetTargetUnits(targets)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
function CapBase:OnStageNumberChanged(number)
|
function CapBase:OnStageNumberChanged(number)
|
||||||
self.activeStage = number
|
self.activeStage = number
|
||||||
|
|
||||||
@@ -243,6 +404,20 @@ function CapBase:IsBaseActiveWhenStageIsActive(stageNumber)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, group in pairs(self.sweepGroupsByName) do
|
||||||
|
local target = group:GetZoneIDWhenStageID(tostring(stageNumber))
|
||||||
|
if target ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, group in pairs(self.interceptGroupsByName) do
|
||||||
|
local target = group:GetZoneIDWhenStageID(tostring(stageNumber))
|
||||||
|
if target ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ do
|
|||||||
---@param database Database
|
---@param database Database
|
||||||
---@param capConfig table
|
---@param capConfig table
|
||||||
---@param stageConfig StageConfig
|
---@param stageConfig StageConfig
|
||||||
|
---@param detectionManager DetectionManager
|
||||||
---@param logLevel LogLevel
|
---@param logLevel LogLevel
|
||||||
function GlobalCapManager.start(database, capConfig, stageConfig, logLevel)
|
function GlobalCapManager.start(database, capConfig, detectionManager, stageConfig, logLevel)
|
||||||
if initiated == true then return end
|
if initiated == true then return end
|
||||||
|
|
||||||
local logger = Spearhead.LoggerTemplate.new("AirbaseManager", logLevel)
|
local logger = Spearhead.LoggerTemplate.new("AirbaseManager", logLevel)
|
||||||
@@ -31,7 +32,7 @@ do
|
|||||||
for _, airbaseName in pairs(airbaseNames) do
|
for _, airbaseName in pairs(airbaseNames) do
|
||||||
if airbaseName then
|
if airbaseName then
|
||||||
local airbaseSpecificLogger = Spearhead.LoggerTemplate.new("CAP_" .. airbaseName, logLevel)
|
local airbaseSpecificLogger = Spearhead.LoggerTemplate.new("CAP_" .. airbaseName, logLevel)
|
||||||
local airbase = Spearhead.classes.capClasses.CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker)
|
local airbase = Spearhead.classes.capClasses.CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager)
|
||||||
if airbase then
|
if airbase then
|
||||||
table.insert(airbasesPerStage[stageName], airbase)
|
table.insert(airbasesPerStage[stageName], airbase)
|
||||||
allAirbasesByName[airbaseName] = airbase
|
allAirbasesByName[airbaseName] = airbase
|
||||||
|
|||||||
@@ -113,6 +113,19 @@ function AirGroup:Spawn()
|
|||||||
self:SpawnInternal(false)
|
self:SpawnInternal(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function AirGroup:IsInAir()
|
||||||
|
local group = Group.getByName(self._groupName)
|
||||||
|
if group then
|
||||||
|
local units = group:getUnits()
|
||||||
|
for _, unit in pairs(units) do
|
||||||
|
if unit:inAir() == true then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
---@param force boolean
|
---@param force boolean
|
||||||
---@param withoutLoadout boolean?
|
---@param withoutLoadout boolean?
|
||||||
---@protected
|
---@protected
|
||||||
@@ -423,6 +436,8 @@ Spearhead.classes.capClasses.airGroups.AirGroup = AirGroup
|
|||||||
|
|
||||||
---@alias AirGroupType
|
---@alias AirGroupType
|
||||||
---| "CAP"
|
---| "CAP"
|
||||||
|
---| "SWEEP"
|
||||||
|
---| "INTERCEPT"
|
||||||
|
|
||||||
---| "CAS"
|
---| "CAS"
|
||||||
---| "SEAD"
|
---| "SEAD"
|
||||||
|
|||||||
@@ -80,63 +80,62 @@ end
|
|||||||
---@private
|
---@private
|
||||||
function CapGroup:InitWithName(groupName)
|
function CapGroup:InitWithName(groupName)
|
||||||
local split_string = Spearhead.Util.split_string(groupName, "_")
|
local split_string = Spearhead.Util.split_string(groupName, "_")
|
||||||
local partCount = Spearhead.Util.tableLength(split_string)
|
local partCount = Spearhead.Util.tableLength(split_string)
|
||||||
if partCount >= 3 then
|
if partCount >= 3 then
|
||||||
|
|
||||||
local configPart = split_string[2]
|
local configPart = split_string[2]
|
||||||
local first = configPart:sub(1, 1)
|
local first = configPart:sub(1, 1)
|
||||||
if first == "A" then
|
if first == "A" then
|
||||||
self._isBackup = false
|
self._isBackup = false
|
||||||
configPart = string.sub(configPart, 2, #configPart)
|
configPart = string.sub(configPart, 2, #configPart)
|
||||||
elseif first == "B" then
|
elseif first == "B" then
|
||||||
configPart = string.sub(configPart, 2, #configPart)
|
configPart = string.sub(configPart, 2, #configPart)
|
||||||
self._isBackup = true
|
self._isBackup = true
|
||||||
elseif first == "[" then
|
elseif first == "[" then
|
||||||
self._isBackup = false
|
self._isBackup = false
|
||||||
else
|
else
|
||||||
Spearhead.AddMissionEditorWarning("Could not parse the CAP config for group: " .. groupName)
|
Spearhead.AddMissionEditorWarning("Could not parse the CAP config for group: " .. groupName)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local subsplit = Spearhead.Util.split_string(configPart, "|")
|
local subsplit = Spearhead.Util.split_string(configPart, "|")
|
||||||
if subsplit then
|
if subsplit then
|
||||||
for key, value in pairs(subsplit) do
|
for key, value in pairs(subsplit) do
|
||||||
local keySplit = Spearhead.Util.split_string(value, "]")
|
local keySplit = Spearhead.Util.split_string(value, "]")
|
||||||
local targetZone = keySplit[2]
|
local targetZone = keySplit[2]
|
||||||
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
|
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
|
||||||
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
|
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
|
||||||
for _, value in pairs(commaSeperated) do
|
for _, value in pairs(commaSeperated) do
|
||||||
local dashSeperated = Spearhead.Util.split_string(value, "-")
|
local dashSeperated = Spearhead.Util.split_string(value, "-")
|
||||||
if Spearhead.Util.tableLength(dashSeperated) > 1 then
|
if Spearhead.Util.tableLength(dashSeperated) > 1 then
|
||||||
local from = tonumber(dashSeperated[1])
|
local from = tonumber(dashSeperated[1])
|
||||||
local till = tonumber(dashSeperated[2])
|
local till = tonumber(dashSeperated[2])
|
||||||
|
|
||||||
for i = from, till do
|
for i = from, till do
|
||||||
if Spearhead.Util.strContains(targetZone, "A") == true then
|
|
||||||
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
|
|
||||||
else
|
|
||||||
self._targetZoneIdPerStage[tostring(i)] = targetZone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if Spearhead.Util.strContains(targetZone, "A") == true then
|
if Spearhead.Util.strContains(targetZone, "A") == true then
|
||||||
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
|
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
|
||||||
else
|
else
|
||||||
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
|
self._targetZoneIdPerStage[tostring(i)] = targetZone
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if Spearhead.Util.strContains(targetZone, "A") == true then
|
||||||
|
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
|
||||||
|
else
|
||||||
|
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info("Capgroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
|
|
||||||
|
|
||||||
else
|
|
||||||
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
env.info("Capgroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
|
||||||
|
|
||||||
|
else
|
||||||
|
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not Spearhead then Spearhead = {} end
|
if not Spearhead then Spearhead = {} end
|
||||||
|
|||||||
@@ -0,0 +1,358 @@
|
|||||||
|
---@class InterceptGroup : AirGroup
|
||||||
|
---@field private _targetNames Array<string>
|
||||||
|
---@field private _targetZoneName string
|
||||||
|
---@field private _detectionManager DetectionManager
|
||||||
|
---@field private _coalitionSide CoalitionSide
|
||||||
|
---@field private _lastKnownTargetAt number
|
||||||
|
---@field private _currentTargetName string?
|
||||||
|
---@field private _targetZoneIdPerStage table<string, string>
|
||||||
|
---@field private _config CapConfig
|
||||||
|
---@field private _airbase Airbase
|
||||||
|
---@field private _maxSpeed number?
|
||||||
|
---@field private _updateTaskID number|nil
|
||||||
|
local InterceptGroup = {}
|
||||||
|
InterceptGroup.__index = InterceptGroup
|
||||||
|
|
||||||
|
|
||||||
|
---@param groupName string
|
||||||
|
---@param config CapConfig
|
||||||
|
---@param logger Logger
|
||||||
|
---@param detectionManager DetectionManager
|
||||||
|
---@return InterceptGroup?
|
||||||
|
function InterceptGroup.New(groupName, config, logger, detectionManager)
|
||||||
|
|
||||||
|
setmetatable(InterceptGroup, Spearhead.classes.capClasses.airGroups.AirGroup)
|
||||||
|
local self = setmetatable({}, InterceptGroup)
|
||||||
|
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "INTERCEPT", config, logger)
|
||||||
|
|
||||||
|
local group = Group.getByName(groupName)
|
||||||
|
if not group then
|
||||||
|
logger:error("InterceptGroup: Group " .. groupName .. " does not exist")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local unit = group:getUnit(1)
|
||||||
|
if unit then
|
||||||
|
local desc = unit:getDesc()
|
||||||
|
if desc["speedMax10K"] then
|
||||||
|
self._maxSpeed = desc["speedMax10K"] * 0.75
|
||||||
|
self._logger:debug("InterceptGroup: Max speed for group " .. groupName .. " is set to " .. self._maxSpeed .. " m/s")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self._coalitionSide = group:getCoalition()
|
||||||
|
self._detectionManager = detectionManager
|
||||||
|
self._config = config
|
||||||
|
self._targetNames = {}
|
||||||
|
self._targetZoneIdPerStage = {}
|
||||||
|
|
||||||
|
self:InitWithName(groupName)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param units Array<string>
|
||||||
|
---@param homeAirbase Airbase
|
||||||
|
function InterceptGroup:SendToInterceptUnits(units, zoneName, homeAirbase)
|
||||||
|
|
||||||
|
self._airbase = homeAirbase
|
||||||
|
self._targetZoneName = zoneName
|
||||||
|
self:SetTargetUnits(units)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function InterceptGroup:GetZoneIDWhenStageID(stageID)
|
||||||
|
return self._targetZoneIdPerStage[stageID]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function InterceptGroup:GetCurrentTargetZone()
|
||||||
|
return self._targetZoneName
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param unitNames Array<string>
|
||||||
|
function InterceptGroup:SetTargetUnits(unitNames)
|
||||||
|
|
||||||
|
self._targetNames = unitNames
|
||||||
|
self:UpdateTask()
|
||||||
|
|
||||||
|
if self._updateTaskID then
|
||||||
|
timer.removeFunction(self._updateTaskID)
|
||||||
|
end
|
||||||
|
|
||||||
|
local updateContinous = function(selfA, time)
|
||||||
|
local next = selfA:UpdateTask()
|
||||||
|
if next then
|
||||||
|
return time + next
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self._updateTaskID = timer.scheduleFunction(updateContinous, self, timer.getTime() + 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
function InterceptGroup:RemoveTargetUnit(unit)
|
||||||
|
|
||||||
|
if not unit then return end
|
||||||
|
|
||||||
|
local name = unit:getName()
|
||||||
|
for key, value in pairs(self._targetNames) do
|
||||||
|
if value == "name" then
|
||||||
|
table.remove(self._targetNames, key)
|
||||||
|
self._logger:debug("InterceptGroup: Removed target unit " .. name .. " from group " .. self._groupName)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@return Unit?
|
||||||
|
---@return Vec3? groupPoint
|
||||||
|
function InterceptGroup:GetClosestTarget()
|
||||||
|
|
||||||
|
local group = Group.getByName(self._groupName)
|
||||||
|
if not group then return nil end
|
||||||
|
|
||||||
|
local groupPoint = nil
|
||||||
|
for _, unit in pairs(group:getUnits()) do
|
||||||
|
if unit and unit:isExist() then
|
||||||
|
groupPoint = unit:getPoint()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not groupPoint then return nil end
|
||||||
|
|
||||||
|
local closestUnit = nil
|
||||||
|
local closestDistance = math.huge
|
||||||
|
|
||||||
|
for _, targetName in pairs(self._targetNames) do
|
||||||
|
local targetUnit = Unit.getByName(targetName)
|
||||||
|
if targetUnit and targetUnit:isExist() then
|
||||||
|
local pos = targetUnit:getPoint()
|
||||||
|
local distance = Spearhead.Util.VectorDistance3d(groupPoint, pos)
|
||||||
|
if distance < closestDistance then
|
||||||
|
closestDistance = distance
|
||||||
|
closestUnit = targetUnit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return closestUnit, groupPoint
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return number? @interval or null if no retry required
|
||||||
|
function InterceptGroup:UpdateTask()
|
||||||
|
local group = Group.getByName(self._groupName)
|
||||||
|
if not group then return end
|
||||||
|
|
||||||
|
|
||||||
|
local closestUnit, groupPoint = self:GetClosestTarget()
|
||||||
|
if not closestUnit then return 15 end
|
||||||
|
if not groupPoint then return 15 end
|
||||||
|
|
||||||
|
local closesUnitVec = closestUnit:getPoint()
|
||||||
|
local alt = closesUnitVec.y
|
||||||
|
if alt < 1000 then
|
||||||
|
alt = 1000 -- Ensure minimum altitude for intercept
|
||||||
|
end
|
||||||
|
|
||||||
|
local selfDetected = false
|
||||||
|
for _, unit in pairs(group:getUnits()) do
|
||||||
|
if unit and unit:isExist() then
|
||||||
|
local controller = unit:getController()
|
||||||
|
if controller then
|
||||||
|
for _, detected in pairs(controller:getDetectedTargets(Controller.Detection.VISUAL, Controller.Detection.OPTIC, Controller.Detection.RADAR)) do
|
||||||
|
if detected and detected.object and detected.distance == true then
|
||||||
|
if detected.object:getName() == closestUnit:getName() then
|
||||||
|
selfDetected = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Spearhead.DcsUtil.IsBingoFuel(self._groupName) then
|
||||||
|
self._logger:debug("InterceptGroup: " .. self._groupName .. " is at bingo fuel, returning to base")
|
||||||
|
self:SendRTB(self._airbase)
|
||||||
|
return nil -- Return to base if bingo fuel
|
||||||
|
end
|
||||||
|
|
||||||
|
if selfDetected and self:IsInAir() == true then
|
||||||
|
if self._currentTargetName and self._currentTargetName == closestUnit:getName() then
|
||||||
|
local distance = Spearhead.Util.VectorDistance3d(closestUnit:getPoint(), groupPoint)
|
||||||
|
if distance < 30 * 1852 then
|
||||||
|
-- If the target is within 10 nautical miles, continue attacking
|
||||||
|
self._logger:debug("InterceptGroup: " .. self._groupName .. " continues attacking target " .. closestUnit:getName())
|
||||||
|
return 15 -- Continue attacking the detected target
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self._logger:debug("InterceptGroup: " .. self._groupName .. " has detected target " .. closestUnit:getName() .. ", creating intercept mission")
|
||||||
|
local vec3 = closestUnit:getPoint()
|
||||||
|
local vec2 = { x = vec3.x, y = vec3.z }
|
||||||
|
local groupPointVec2 = { x = groupPoint.x, y = groupPoint.z }
|
||||||
|
local mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt)
|
||||||
|
self:SetMission(mission)
|
||||||
|
self._currentTargetName = closestUnit:getName()
|
||||||
|
return 15 -- Just continue attacking the detected target
|
||||||
|
else
|
||||||
|
self._currentTargetName = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local speed = self._config:getMaxSpeed()
|
||||||
|
local interceptPoint = self:GetInterceptPoint(groupPoint, speed, closestUnit)
|
||||||
|
|
||||||
|
if interceptPoint == nil then
|
||||||
|
return 30 -- Return to base if no intercept point could be calculated
|
||||||
|
end
|
||||||
|
|
||||||
|
local mission = nil
|
||||||
|
if self:IsInAir() == true then
|
||||||
|
-- If the group is in the air, create an intercept mission
|
||||||
|
mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromInAir(
|
||||||
|
self._groupName,
|
||||||
|
{ x = groupPoint.x, y = groupPoint.z },
|
||||||
|
interceptPoint,
|
||||||
|
self._airbase,
|
||||||
|
self._config,
|
||||||
|
self._maxSpeed,
|
||||||
|
alt
|
||||||
|
)
|
||||||
|
else
|
||||||
|
mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromAirbase(
|
||||||
|
self._groupName,
|
||||||
|
interceptPoint,
|
||||||
|
self._airbase,
|
||||||
|
self._config,
|
||||||
|
self._maxSpeed,
|
||||||
|
alt
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
if mission then
|
||||||
|
self:SetMission(mission)
|
||||||
|
else
|
||||||
|
self._logger:warn("InterceptGroup: Could not create mission for group " .. self._groupName)
|
||||||
|
return 30 -- Return to base if mission could not be created
|
||||||
|
end
|
||||||
|
|
||||||
|
return 15
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param originatingUnit Vec3
|
||||||
|
---@param speed number
|
||||||
|
---@param targetUnit Unit
|
||||||
|
---@return Vec2? @Returns the intercept point as a Vec2 or nil if no intercept point could be calculated
|
||||||
|
function InterceptGroup:GetInterceptPoint(originatingUnit, speed, targetUnit)
|
||||||
|
|
||||||
|
-- Calculate intercept point for a moving target
|
||||||
|
-- originatingUnit: Vec3 (our position)
|
||||||
|
-- speed: our speed (scalar, m/s)
|
||||||
|
-- targetUnit: Unit (target)
|
||||||
|
|
||||||
|
if not targetUnit or not targetUnit:isExist() then return nil end
|
||||||
|
|
||||||
|
local targetPos = targetUnit:getPoint()
|
||||||
|
local targetVel = targetUnit:getVelocity() -- Vec3
|
||||||
|
local relPos = {
|
||||||
|
x = targetPos.x - originatingUnit.x,
|
||||||
|
y = targetPos.y - originatingUnit.y,
|
||||||
|
z = targetPos.z - originatingUnit.z
|
||||||
|
}
|
||||||
|
local relVel = {
|
||||||
|
x = targetVel.x,
|
||||||
|
y = targetVel.y,
|
||||||
|
z = targetVel.z
|
||||||
|
}
|
||||||
|
local relPos2 = relPos.x^2 + relPos.y^2 + relPos.z^2
|
||||||
|
local relVel2 = relVel.x^2 + relVel.y^2 + relVel.z^2
|
||||||
|
local speed2 = speed^2
|
||||||
|
local dot = relPos.x * relVel.x + relPos.y * relVel.y + relPos.z * relVel.z
|
||||||
|
|
||||||
|
-- Quadratic formula: a*t^2 + b*t + c = 0
|
||||||
|
local a = relVel2 - speed2
|
||||||
|
local b = 2 * dot
|
||||||
|
local c = relPos2
|
||||||
|
local discriminant = b^2 - 4*a*c
|
||||||
|
if discriminant < 0 or a == 0 then
|
||||||
|
-- No solution, just head to current position
|
||||||
|
return { x = targetPos.x, y = targetPos.z }
|
||||||
|
end
|
||||||
|
local sqrtDisc = math.sqrt(discriminant)
|
||||||
|
local t1 = (-b + sqrtDisc) / (2*a)
|
||||||
|
local t2 = (-b - sqrtDisc) / (2*a)
|
||||||
|
local t = math.min(t1, t2)
|
||||||
|
if t < 0 then t = math.max(t1, t2) end
|
||||||
|
if t < 0 then
|
||||||
|
-- No valid intercept time, just head to current position
|
||||||
|
return { x = targetPos.x, y = targetPos.z }
|
||||||
|
end
|
||||||
|
-- Intercept point
|
||||||
|
local intercept = {
|
||||||
|
x = targetPos.x + targetVel.x * t,
|
||||||
|
y = targetPos.z + targetVel.z * t
|
||||||
|
}
|
||||||
|
return intercept
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function InterceptGroup:InitWithName(groupName)
|
||||||
|
local split_string = Spearhead.Util.split_string(groupName, "_")
|
||||||
|
local partCount = Spearhead.Util.tableLength(split_string)
|
||||||
|
if partCount >= 3 then
|
||||||
|
local configPart = split_string[2]
|
||||||
|
configPart = string.sub(configPart, 2, #configPart)
|
||||||
|
local subsplit = Spearhead.Util.split_string(configPart, "|")
|
||||||
|
if subsplit then
|
||||||
|
for key, value in pairs(subsplit) do
|
||||||
|
local keySplit = Spearhead.Util.split_string(value, "]")
|
||||||
|
local targetZone = keySplit[2]
|
||||||
|
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
|
||||||
|
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
|
||||||
|
for _, value in pairs(commaSeperated) do
|
||||||
|
local dashSeperated = Spearhead.Util.split_string(value, "-")
|
||||||
|
if Spearhead.Util.tableLength(dashSeperated) > 1 then
|
||||||
|
local from = tonumber(dashSeperated[1])
|
||||||
|
local till = tonumber(dashSeperated[2])
|
||||||
|
|
||||||
|
for i = from, till do
|
||||||
|
if Spearhead.Util.strContains(targetZone, "A") == true then
|
||||||
|
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
|
||||||
|
else
|
||||||
|
self._targetZoneIdPerStage[tostring(i)] = targetZone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Spearhead.Util.strContains(targetZone, "A") == true then
|
||||||
|
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
|
||||||
|
else
|
||||||
|
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("interceptGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
|
||||||
|
|
||||||
|
else
|
||||||
|
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if not Spearhead then Spearhead = {} end
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
|
||||||
|
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
|
||||||
|
Spearhead.classes.capClasses.airGroups.InterceptGroup = InterceptGroup
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
---@class SweepGroup : AirGroup
|
||||||
|
---@field _targetZoneIdPerStage table<number, string>
|
||||||
|
---@field _currentTargetZoneID string?
|
||||||
|
local SweepGroup = {}
|
||||||
|
SweepGroup.__index = SweepGroup
|
||||||
|
|
||||||
|
|
||||||
|
---@param groupName string
|
||||||
|
---@param config CapConfig
|
||||||
|
---@param logger Logger
|
||||||
|
---@return SweepGroup
|
||||||
|
function SweepGroup.New(groupName, config, logger)
|
||||||
|
setmetatable(SweepGroup, Spearhead.classes.capClasses.airGroups.AirGroup)
|
||||||
|
local self = setmetatable({}, SweepGroup) --[[@as SweepGroup]]
|
||||||
|
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "SWEEP", config, logger)
|
||||||
|
|
||||||
|
self._targetZoneIdPerStage = {}
|
||||||
|
self:InitWithName(groupName)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function SweepGroup:GetZoneIDWhenStageID(stageID)
|
||||||
|
return self._targetZoneIdPerStage[stageID]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string?
|
||||||
|
function SweepGroup:GetCurrentTargetZoneID()
|
||||||
|
return self._currentTargetZoneID
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class SetTaskParams
|
||||||
|
---@field task table
|
||||||
|
---@field self SweepGroup
|
||||||
|
|
||||||
|
---@param params SetTaskParams
|
||||||
|
local setMissionDelayedTask = function(params, time)
|
||||||
|
params.self:SetMissionPrivate(params.task)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SweepGroup:SendToZone(zone, targetZoneID, airbase)
|
||||||
|
self._logger:debug("Airgroup " .. self._groupName .. " called to zone: " .. zone.name)
|
||||||
|
|
||||||
|
self._currentTargetZoneID = targetZoneID
|
||||||
|
|
||||||
|
local group = Group.getByName(self._groupName)
|
||||||
|
|
||||||
|
local mission = Spearhead.classes.capClasses.taskings.SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
|
||||||
|
if mission then
|
||||||
|
---@type SetTaskParams
|
||||||
|
local params = {
|
||||||
|
task = mission,
|
||||||
|
self = self
|
||||||
|
}
|
||||||
|
local delay = math.random(120, 600)
|
||||||
|
timer.scheduleFunction(setMissionDelayedTask, params, timer.getTime() + delay)
|
||||||
|
else
|
||||||
|
self._logger:error("SweepGroup:SendToZone - Mission could not be created for group: " .. self._groupName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function SweepGroup:SendToZoneInternal()
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function SweepGroup:InitWithName(groupName)
|
||||||
|
local split_string = Spearhead.Util.split_string(groupName, "_")
|
||||||
|
local partCount = Spearhead.Util.tableLength(split_string)
|
||||||
|
if partCount >= 3 then
|
||||||
|
|
||||||
|
local configPart = split_string[2]
|
||||||
|
configPart = string.sub(configPart, 2, #configPart)
|
||||||
|
|
||||||
|
local subsplit = Spearhead.Util.split_string(configPart, "|")
|
||||||
|
if subsplit then
|
||||||
|
for key, value in pairs(subsplit) do
|
||||||
|
local keySplit = Spearhead.Util.split_string(value, "]")
|
||||||
|
local targetZone = keySplit[2]
|
||||||
|
local allActives = string.sub(keySplit[1], 2, #keySplit[1])
|
||||||
|
local commaSeperated = Spearhead.Util.split_string(allActives, ",")
|
||||||
|
for _, value in pairs(commaSeperated) do
|
||||||
|
local dashSeperated = Spearhead.Util.split_string(value, "-")
|
||||||
|
if Spearhead.Util.tableLength(dashSeperated) > 1 then
|
||||||
|
local from = tonumber(dashSeperated[1])
|
||||||
|
local till = tonumber(dashSeperated[2])
|
||||||
|
|
||||||
|
for i = from, till do
|
||||||
|
if Spearhead.Util.strContains(targetZone, "A") == true then
|
||||||
|
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
|
||||||
|
else
|
||||||
|
self._targetZoneIdPerStage[tostring(i)] = targetZone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Spearhead.Util.strContains(targetZone, "A") == true then
|
||||||
|
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A",
|
||||||
|
tostring(dashSeperated[1]))
|
||||||
|
else
|
||||||
|
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info("SweepGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage))
|
||||||
|
else
|
||||||
|
Spearhead.AddMissionEditorWarning("SWEEP Group with name: " ..
|
||||||
|
groupName .. "should have at least 3 parts, but has " .. partCount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead then Spearhead = {} end
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
|
||||||
|
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
|
||||||
|
Spearhead.classes.capClasses.airGroups.SweepGroup = SweepGroup
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
|
||||||
|
---@class DetectionManager
|
||||||
|
---@field private _logger Logger
|
||||||
|
---@field private _detectedUnits table<string, table<string, number>>
|
||||||
|
---@field private _detectingUnits table<string, table<string, Unit>>
|
||||||
|
local DetectionManager = {}
|
||||||
|
DetectionManager.__index = DetectionManager
|
||||||
|
|
||||||
|
|
||||||
|
---@param logger Logger
|
||||||
|
function DetectionManager.New(logger)
|
||||||
|
local self = setmetatable({}, DetectionManager)
|
||||||
|
|
||||||
|
self._logger = logger
|
||||||
|
self._detectedUnits = {
|
||||||
|
[tostring(coalition.side.RED)] = {},
|
||||||
|
[tostring(coalition.side.BLUE)] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self._detectingUnits = {
|
||||||
|
[tostring(coalition.side.RED)] = {},
|
||||||
|
[tostring(coalition.side.BLUE)] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
---@param selfA DetectionManager
|
||||||
|
---@param time number
|
||||||
|
local updateDetectingUnitsTask = function(selfA, time)
|
||||||
|
selfA:UpdateDetectingUnits()
|
||||||
|
return time + 120
|
||||||
|
end
|
||||||
|
timer.scheduleFunction(updateDetectingUnitsTask, self, timer.getTime() + 120)
|
||||||
|
|
||||||
|
---@param selfA DetectionManager
|
||||||
|
---@param time number
|
||||||
|
local updateDetected = function(selfA, time)
|
||||||
|
selfA:UpdateDetectedUnits()
|
||||||
|
return time + 10
|
||||||
|
end
|
||||||
|
timer.scheduleFunction(updateDetected, self, timer.getTime() + 130)
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param unitName string
|
||||||
|
---@param coalitionSide CoalitionSide
|
||||||
|
function DetectionManager:IsUnitDetectedBy(unitName, coalitionSide)
|
||||||
|
local coalitionString = tostring(coalitionSide)
|
||||||
|
if not self._detectedUnits[coalitionString] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self._detectedUnits[coalitionString][unitName] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return timer.getTime() - self._detectedUnits[coalitionString][unitName] < 20
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Array<string>
|
||||||
|
function DetectionManager:GetDetectedUnitsBy(coalitionSide)
|
||||||
|
local coalitionString = tostring(coalitionSide)
|
||||||
|
if not self._detectedUnits[coalitionString] then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local detectedUnits = {}
|
||||||
|
for unitName, _ in pairs(self._detectedUnits[coalitionString]) do
|
||||||
|
if self:IsUnitDetectedBy(unitName, coalitionSide) then
|
||||||
|
table.insert(detectedUnits, unitName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return detectedUnits
|
||||||
|
end
|
||||||
|
|
||||||
|
function DetectionManager:UpdateDetectingUnits()
|
||||||
|
self:UpdateDetectingInCoalition(coalition.side.RED)
|
||||||
|
self:UpdateDetectingInCoalition(coalition.side.BLUE)
|
||||||
|
self._logger:debug("Updated detecting units")
|
||||||
|
end
|
||||||
|
|
||||||
|
function DetectionManager:UpdateDetectedUnits()
|
||||||
|
self:UpdateDetectedUnitsByCoalition(coalition.side.RED)
|
||||||
|
self:UpdateDetectedUnitsByCoalition(coalition.side.BLUE)
|
||||||
|
self._logger:debug("Updated detected units")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param unit Unit
|
||||||
|
---@return boolean
|
||||||
|
function DetectionManager:IsDetectingType(unit)
|
||||||
|
return unit:hasAttribute("EWR") or unit:hasAttribute("AWACS") or unit:hasAttribute("SAM SR")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param coalitionSide CoalitionSide
|
||||||
|
function DetectionManager:UpdateDetectingInCoalition(coalitionSide)
|
||||||
|
|
||||||
|
local coalitionString = tostring(coalitionSide)
|
||||||
|
|
||||||
|
local airGroups = coalition.getGroups(coalitionSide, Group.Category.AIRPLANE)
|
||||||
|
for _, group in ipairs(airGroups) do
|
||||||
|
if group and group:isExist() then
|
||||||
|
for _, unit in ipairs(group:getUnits()) do
|
||||||
|
if unit and self:IsDetectingType(unit) == true then
|
||||||
|
self._detectingUnits[coalitionString][unit:getName()] = unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local groundGroups = coalition.getGroups(coalitionSide, Group.Category.GROUND)
|
||||||
|
for _, group in ipairs(groundGroups) do
|
||||||
|
if group and group:isExist() then
|
||||||
|
for _, unit in ipairs(group:getUnits()) do
|
||||||
|
if unit and self:IsDetectingType(unit) == true then
|
||||||
|
self._detectingUnits[coalitionString][unit:getName()] = unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ships = coalition.getGroups(coalitionSide, Group.Category.SHIP)
|
||||||
|
for _, group in ipairs(ships) do
|
||||||
|
if group and group:isExist() then
|
||||||
|
for _, unit in ipairs(group:getUnits()) do
|
||||||
|
if unit and self:IsDetectingType(unit) == true then
|
||||||
|
self._detectingUnits[coalitionString][unit:getName()] = unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param coalitionSide CoalitionSide
|
||||||
|
function DetectionManager:UpdateDetectedUnitsByCoalition(coalitionSide)
|
||||||
|
|
||||||
|
local coalitionString = tostring(coalitionSide)
|
||||||
|
if not self._detectingUnits[coalitionString] then return end
|
||||||
|
if not self._detectedUnits[coalitionString] then
|
||||||
|
self._detectedUnits[coalitionString] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, detectingUnit in pairs(self._detectingUnits[coalitionString]) do
|
||||||
|
if detectingUnit and detectingUnit:isExist() then
|
||||||
|
local controller = detectingUnit:getController()
|
||||||
|
if controller then
|
||||||
|
local targets = controller:getDetectedTargets(Controller.Detection.RADAR)
|
||||||
|
for _, target in pairs(targets) do
|
||||||
|
if target and target.object ~= nil and target.distance == true then
|
||||||
|
local targetUnit = target.object
|
||||||
|
local name = targetUnit:getName()
|
||||||
|
self._detectedUnits[coalitionString][name] = timer.getTime()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead then Spearhead = {} end
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
|
||||||
|
if not Spearhead.classes.capClasses.detection then Spearhead.classes.capClasses.detection = {} end
|
||||||
|
Spearhead.classes.capClasses.detection.DetectionManager = DetectionManager
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---@class InterceptTasking
|
||||||
|
local INTERCEPT = {}
|
||||||
|
|
||||||
|
|
||||||
|
---@param groupName string
|
||||||
|
---@param interceptPoint Vec2
|
||||||
|
---@param airbase Airbase
|
||||||
|
---@param speed number?
|
||||||
|
---@param alt number?
|
||||||
|
---@param config CapConfig
|
||||||
|
function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, config, speed, alt)
|
||||||
|
|
||||||
|
local airbaseVec3 = airbase:getPoint()
|
||||||
|
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
|
||||||
|
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, interceptPoint)
|
||||||
|
|
||||||
|
env.info("BLAAT: heading from runway to first point: " .. heading)
|
||||||
|
|
||||||
|
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 3*1852)
|
||||||
|
|
||||||
|
local pointA, pointB, pointC, pointD = INTERCEPT.getInterceptTaskPoint(groupName, point, interceptPoint, airbaseVec2, config, speed, alt)
|
||||||
|
|
||||||
|
local points = {
|
||||||
|
[1] = pointA,
|
||||||
|
[2] = pointA,
|
||||||
|
[3] = pointB,
|
||||||
|
[4] = pointC,
|
||||||
|
[5] = pointD,
|
||||||
|
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config),
|
||||||
|
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
|
||||||
|
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
|
||||||
|
}
|
||||||
|
|
||||||
|
local mission = {
|
||||||
|
id = 'Mission',
|
||||||
|
params = {
|
||||||
|
airborne = true,
|
||||||
|
route = {
|
||||||
|
points = points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mission
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param groupName string
|
||||||
|
---@param currentPosition Vec2
|
||||||
|
---@param interceptPoint Vec2
|
||||||
|
---@param airbase Airbase
|
||||||
|
---@param speed number?
|
||||||
|
---@param alt number?
|
||||||
|
---@param config CapConfig
|
||||||
|
---@return table
|
||||||
|
function INTERCEPT.getMissionFromInAir(groupName, currentPosition, interceptPoint, airbase, config, speed, alt)
|
||||||
|
|
||||||
|
local pointAirbasev3 = airbase:getPoint()
|
||||||
|
local airbase2 = { x = pointAirbasev3.x, y = pointAirbasev3.z }
|
||||||
|
|
||||||
|
local pointA, pointB, pointC, pointD = INTERCEPT.getInterceptTaskPoint(groupName, currentPosition, interceptPoint, airbase2, config, speed, alt)
|
||||||
|
|
||||||
|
local points = {
|
||||||
|
[1] = pointA,
|
||||||
|
[2] = pointA,
|
||||||
|
[3] = pointB,
|
||||||
|
[4] = pointC,
|
||||||
|
[5] = pointD,
|
||||||
|
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config),
|
||||||
|
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
|
||||||
|
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
|
||||||
|
}
|
||||||
|
|
||||||
|
local mission = {
|
||||||
|
id = 'Mission',
|
||||||
|
params = {
|
||||||
|
airborne = true,
|
||||||
|
route = {
|
||||||
|
points = points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mission
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param groupName string
|
||||||
|
---@param currentPoint Vec2
|
||||||
|
---@param targetPosition Vec2
|
||||||
|
---@param targetUnit Unit
|
||||||
|
---@param airbase any
|
||||||
|
---@param config any
|
||||||
|
---@param speed number?
|
||||||
|
---@param alt number?
|
||||||
|
---@return table
|
||||||
|
function INTERCEPT.getUnitInterceptMissionFromAir(groupName, currentPoint, targetPosition, targetUnit, airbase, config, speed, alt)
|
||||||
|
|
||||||
|
local pointAirbasev3 = airbase:getPoint()
|
||||||
|
local airbase2 = { x = pointAirbasev3.x, y = pointAirbasev3.z }
|
||||||
|
|
||||||
|
local pointA, pointB, pointC, pointD = INTERCEPT.getUnitInterceptTaskPoint(groupName, currentPoint, targetPosition, targetUnit, airbase2, config, speed, alt)
|
||||||
|
|
||||||
|
local points = {
|
||||||
|
[1] = pointA,
|
||||||
|
[2] = pointA,
|
||||||
|
[3] = pointB,
|
||||||
|
[4] = pointC,
|
||||||
|
[5] = pointD,
|
||||||
|
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, currentPoint, config),
|
||||||
|
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
|
||||||
|
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
|
||||||
|
}
|
||||||
|
|
||||||
|
local mission = {
|
||||||
|
id = 'Mission',
|
||||||
|
params = {
|
||||||
|
airborne = true,
|
||||||
|
route = {
|
||||||
|
points = points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mission
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param groupName string
|
||||||
|
---@param currentPoint Vec2
|
||||||
|
---@param targetPoint Vec2
|
||||||
|
---@param airbasePoint Vec2
|
||||||
|
---@param config CapConfig
|
||||||
|
---@param speed number?
|
||||||
|
---@param alt number?
|
||||||
|
---@return table pointA @Starts the task right away
|
||||||
|
---@return table pointB @The actual target point to fly to and search, but searching started at pointA
|
||||||
|
---@return table pointC A fly over point halfway between the target and the airbase, if nothing it found then the unit will fly it's route till here
|
||||||
|
---@return table pointD THe point after Point C where the unit will execute the RTB command
|
||||||
|
function INTERCEPT.getInterceptTaskPoint(groupName, currentPoint, targetPoint, airbasePoint, config, speed, alt)
|
||||||
|
|
||||||
|
local pointA = {
|
||||||
|
alt = alt or config:getMinAlt(),
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed or config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = currentPoint.x,
|
||||||
|
y = currentPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
id = 'EngageTargetsInZone',
|
||||||
|
params = {
|
||||||
|
point = targetPoint,
|
||||||
|
zoneRadius = 10 * 1852, -- 10 NM, point will be updated, so target should be in this zone.
|
||||||
|
targetTypes = {
|
||||||
|
[1] = "Planes",
|
||||||
|
},
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointB = {
|
||||||
|
alt = alt or config:getMinAlt(),
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed or config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = targetPoint.x,
|
||||||
|
y = targetPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
id = 'EngageTargetsInZone',
|
||||||
|
params = {
|
||||||
|
point = targetPoint,
|
||||||
|
zoneRadius = 10 * 1852, -- 10 NM, point will be updated, so target should be in this zone.
|
||||||
|
targetTypes = {
|
||||||
|
[1] = "Planes",
|
||||||
|
},
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local midPoint = {
|
||||||
|
x = (targetPoint.x + airbasePoint.x) / 2,
|
||||||
|
y = (targetPoint.y + airbasePoint.y) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointC = {
|
||||||
|
alt = config:getMinAlt(),
|
||||||
|
action = "Fly Over Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = midPoint.x,
|
||||||
|
y = midPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointD = {
|
||||||
|
alt = config:getMinAlt(),
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = midPoint.x,
|
||||||
|
y = midPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
number = 1,
|
||||||
|
auto = false,
|
||||||
|
id = "WrappedAction",
|
||||||
|
enabled = "true",
|
||||||
|
params = {
|
||||||
|
action = {
|
||||||
|
id = "Script",
|
||||||
|
params = {
|
||||||
|
command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointA, pointB, pointC, pointD
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param groupName string
|
||||||
|
---@param currentPoint Vec2
|
||||||
|
---@param targetPosition Vec2
|
||||||
|
---@param targetUnit Unit
|
||||||
|
---@param airbasePoint Vec2
|
||||||
|
---@param config CapConfig
|
||||||
|
---@param speed number?
|
||||||
|
---@param alt number?
|
||||||
|
---@return table pointA @Starts the task right away
|
||||||
|
---@return table pointB @The actual target point to fly to and search, but searching started at pointA
|
||||||
|
---@return table pointC A fly over point halfway between the target and the airbase, if nothing it found then the unit will fly it's route till here
|
||||||
|
---@return table pointD THe point after Point C where the unit will execute the RTB command
|
||||||
|
function INTERCEPT.getUnitInterceptTaskPoint(groupName, currentPoint, targetPosition, targetUnit, airbasePoint, config, speed, alt)
|
||||||
|
|
||||||
|
local pointA = {
|
||||||
|
alt = alt or config:getMinAlt(),
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed or config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = currentPoint.x,
|
||||||
|
y = currentPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
id = 'EngageUnit',
|
||||||
|
params = {
|
||||||
|
unitId = targetUnit:getID(),
|
||||||
|
groupAttack = true,
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointB = {
|
||||||
|
alt = alt or config:getMinAlt(),
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed or config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = targetPosition.x,
|
||||||
|
y = targetPosition.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
id = 'EngageUnit',
|
||||||
|
params = {
|
||||||
|
unitId = targetUnit:getID(),
|
||||||
|
groupAttack = true,
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local midPoint = {
|
||||||
|
x = (targetPosition.x + airbasePoint.x) / 2,
|
||||||
|
y = (targetPosition.y + airbasePoint.y) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointC = {
|
||||||
|
alt = config:getMinAlt(),
|
||||||
|
action = "Fly Over Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = midPoint.x,
|
||||||
|
y = midPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointD = {
|
||||||
|
alt = config:getMinAlt(),
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = config:getMaxSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = midPoint.x,
|
||||||
|
y = midPoint.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
number = 1,
|
||||||
|
auto = false,
|
||||||
|
id = "WrappedAction",
|
||||||
|
enabled = "true",
|
||||||
|
params = {
|
||||||
|
action = {
|
||||||
|
id = "Script",
|
||||||
|
params = {
|
||||||
|
command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointA, pointB, pointC, pointD
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if not Spearhead then Spearhead = {} end
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
|
||||||
|
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
|
||||||
|
Spearhead.classes.capClasses.taskings.INTERCEPT = INTERCEPT
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
---@class SWEEPTasking
|
||||||
|
local SWEEP = {}
|
||||||
|
|
||||||
|
---@param attackHelos boolean
|
||||||
|
---@return table
|
||||||
|
local function GetCAPTargetTypes(attackHelos)
|
||||||
|
local targetTypes = {
|
||||||
|
[1] = "Planes",
|
||||||
|
}
|
||||||
|
|
||||||
|
if attackHelos then
|
||||||
|
targetTypes[2] = "Helicopters"
|
||||||
|
end
|
||||||
|
|
||||||
|
return targetTypes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class SWEEPTaskingOptions
|
||||||
|
---@field furthest Vec2
|
||||||
|
---@field closest Vec2
|
||||||
|
|
||||||
|
---@param capZone SpearheadTriggerZone
|
||||||
|
---@param airBase Airbase
|
||||||
|
---@return SWEEPTaskingOptions
|
||||||
|
local function GetCAPPointFromTriggerZone(airBase, capZone)
|
||||||
|
local furthestA = nil
|
||||||
|
local furthestB = nil
|
||||||
|
|
||||||
|
local furthestDistance = 0
|
||||||
|
|
||||||
|
for indexA, pointA in ipairs(capZone.verts) do
|
||||||
|
for indexB, pointB in ipairs(capZone.verts) do
|
||||||
|
if pointA ~= pointB then
|
||||||
|
local distance = Spearhead.Util.VectorDistance2d(pointA, pointB)
|
||||||
|
if distance > furthestDistance then
|
||||||
|
furthestDistance = distance
|
||||||
|
furthestA = indexA
|
||||||
|
furthestB = indexB
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local baseVec3 = airBase:getPoint()
|
||||||
|
|
||||||
|
---@type Vec2
|
||||||
|
local baseVec2 = { x = baseVec3.x, y = baseVec3.z }
|
||||||
|
|
||||||
|
local pointA = capZone.verts[furthestA]
|
||||||
|
local pointB = capZone.verts[furthestB]
|
||||||
|
local furthest = pointA
|
||||||
|
local closest = pointB
|
||||||
|
|
||||||
|
local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB)
|
||||||
|
|
||||||
|
if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then
|
||||||
|
furthest = pointB
|
||||||
|
closest = pointA
|
||||||
|
heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA)
|
||||||
|
end
|
||||||
|
|
||||||
|
local distance = furthestDistance
|
||||||
|
if distance > 15000 then
|
||||||
|
distance = distance - 10000
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
width = 10000,
|
||||||
|
furthest = furthest,
|
||||||
|
closest = closest,
|
||||||
|
legLength = distance,
|
||||||
|
hotLegDir = math.rad(heading),
|
||||||
|
orbitOriginPoint = closest
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param airbase Airbase
|
||||||
|
---@param capZone SpearheadTriggerZone
|
||||||
|
---@param capConfig CapConfig
|
||||||
|
local GetOutboundTask = function(airbase, capZone, capConfig)
|
||||||
|
local airbaseVec3 = airbase:getPoint()
|
||||||
|
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
|
||||||
|
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
|
||||||
|
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520)
|
||||||
|
|
||||||
|
return {
|
||||||
|
alt = 2000,
|
||||||
|
action = "Fly Over Point",
|
||||||
|
type = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = capConfig:getMinSpeed(),
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = point.x,
|
||||||
|
y = point.y,
|
||||||
|
speed_locked = false,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
id = 'EngageTargets',
|
||||||
|
params = {
|
||||||
|
maxDist = capConfig:getMaxDeviationRange(),
|
||||||
|
maxDistEnabled = capConfig:getMaxDeviationRange() > 0, -- required to check maxDist
|
||||||
|
targetTypes = GetCAPTargetTypes(false),
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param groupName string
|
||||||
|
---@param airbase Airbase
|
||||||
|
---@param capZone SpearheadTriggerZone
|
||||||
|
---@param capConfig CapConfig
|
||||||
|
function SWEEP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig)
|
||||||
|
|
||||||
|
local pointA, pointB, pointC = SWEEP.getAsTasking(groupName, airbase, capZone, capConfig)
|
||||||
|
|
||||||
|
local points = {
|
||||||
|
[1] = GetOutboundTask(airbase, capZone, capConfig),
|
||||||
|
[2] = GetOutboundTask(airbase, capZone, capConfig),
|
||||||
|
[3] = pointA,
|
||||||
|
[4] = pointB,
|
||||||
|
[5] = pointC,
|
||||||
|
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig),
|
||||||
|
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase),
|
||||||
|
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase)
|
||||||
|
}
|
||||||
|
|
||||||
|
local mission = {
|
||||||
|
id = 'Mission',
|
||||||
|
params = {
|
||||||
|
airborne = true,
|
||||||
|
route = {
|
||||||
|
points = points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mission
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param groupName string
|
||||||
|
---@param airbase Airbase
|
||||||
|
---@param capZone SpearheadTriggerZone
|
||||||
|
---@param capConfig CapConfig
|
||||||
|
---@return table TurningPointA
|
||||||
|
---@return table TurningPointB
|
||||||
|
---@return table TurningPointC
|
||||||
|
function SWEEP.getAsTasking(groupName, airbase, capZone, capConfig)
|
||||||
|
|
||||||
|
local capTaskingOptions = GetCAPPointFromTriggerZone(airbase, capZone)
|
||||||
|
|
||||||
|
|
||||||
|
local alt = math.random(capConfig:getMinAlt(), capConfig:getMaxAlt())
|
||||||
|
local speed = capConfig:getMaxSpeed()
|
||||||
|
|
||||||
|
local pointA = {
|
||||||
|
alt = alt,
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed,
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = capTaskingOptions.closest.x,
|
||||||
|
y = capTaskingOptions.closest.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
number = 1,
|
||||||
|
auto = false,
|
||||||
|
id = "WrappedAction",
|
||||||
|
enabled = "true",
|
||||||
|
params = {
|
||||||
|
action = {
|
||||||
|
id = "Script",
|
||||||
|
params = {
|
||||||
|
command = "pcall(Spearhead.Events.PublishOnStation, \"" .. groupName .. "\")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
id = 'EngageTargets',
|
||||||
|
params = {
|
||||||
|
maxDist = capConfig:getMaxDeviationRange(),
|
||||||
|
maxDistEnabled = capConfig:getMaxDeviationRange() >= 0, -- required to check maxDist
|
||||||
|
targetTypes = GetCAPTargetTypes(false),
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointB = {
|
||||||
|
alt = alt,
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed,
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = capTaskingOptions.furthest.x,
|
||||||
|
y = capTaskingOptions.furthest.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
id = 'EngageTargets',
|
||||||
|
params = {
|
||||||
|
maxDist = capConfig:getMaxDeviationRange(),
|
||||||
|
maxDistEnabled = capConfig:getMaxDeviationRange() >= 0, -- required to check maxDist
|
||||||
|
targetTypes = GetCAPTargetTypes(false),
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local pointC = {
|
||||||
|
alt = alt,
|
||||||
|
action = "Turning Point",
|
||||||
|
alt_type = "BARO",
|
||||||
|
speed = speed,
|
||||||
|
ETA = 0,
|
||||||
|
ETA_locked = false,
|
||||||
|
x = capTaskingOptions.closest.x,
|
||||||
|
y = capTaskingOptions.closest.y,
|
||||||
|
speed_locked = true,
|
||||||
|
formation_template = "",
|
||||||
|
task = {
|
||||||
|
id = "ComboTask",
|
||||||
|
params = {
|
||||||
|
tasks = {
|
||||||
|
[1] = {
|
||||||
|
id = 'EngageTargets',
|
||||||
|
params = {
|
||||||
|
maxDist = capConfig:getMaxDeviationRange(),
|
||||||
|
maxDistEnabled = capConfig:getMaxDeviationRange() >= 0, -- required to check maxDist
|
||||||
|
targetTypes = GetCAPTargetTypes(false),
|
||||||
|
priority = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
number = 2,
|
||||||
|
auto = false,
|
||||||
|
id = "WrappedAction",
|
||||||
|
enabled = "true",
|
||||||
|
params = {
|
||||||
|
action = {
|
||||||
|
id = "Script",
|
||||||
|
params = {
|
||||||
|
command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointA, pointB, pointC
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead then Spearhead = {} end
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
|
||||||
|
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
|
||||||
|
Spearhead.classes.capClasses.taskings.SWEEP = SWEEP
|
||||||
+63
-14
@@ -7,8 +7,10 @@
|
|||||||
---@field MissionZonesLocations table<string, Vec2> All Mission Zone Locations
|
---@field MissionZonesLocations table<string, Vec2> All Mission Zone Locations
|
||||||
---@field StageZonesByNumber table<string, Array<string>> Stage zones grouped by index number
|
---@field StageZonesByNumber table<string, Array<string>> Stage zones grouped by index number
|
||||||
---@field AllFarpZones Array<string>
|
---@field AllFarpZones Array<string>
|
||||||
---@field CapRoutes Array<string> All Cap route zone names
|
---@field AllCapRoutes Array<string> All Cap route zone names
|
||||||
---@field capZonesByCapZoneID table<string, CapRoute> table<StageNumber, table>
|
---@field AllInterceptZones Array<string> All intercept zones
|
||||||
|
---@field capZonesByCapZoneID table<string, CapRoute>
|
||||||
|
---@field interceptZonesByZoneID table<string, Array<SpearheadTriggerZone>>
|
||||||
---@field CarrierRouteZones Array<string> All Carrier routes zones
|
---@field CarrierRouteZones Array<string> All Carrier routes zones
|
||||||
---@field BlueSams Array<string> All blue sam zones
|
---@field BlueSams Array<string> All blue sam zones
|
||||||
---@field SupplyHubZones Array<string> All supply hub zones
|
---@field SupplyHubZones Array<string> All supply hub zones
|
||||||
@@ -43,6 +45,8 @@
|
|||||||
|
|
||||||
---@class AirbaseData
|
---@class AirbaseData
|
||||||
---@field CapGroups Array<string>
|
---@field CapGroups Array<string>
|
||||||
|
---@field SweepGroups Array<string>
|
||||||
|
---@field InterceptGroups Array<string>
|
||||||
---@field RedGroups Array<string>
|
---@field RedGroups Array<string>
|
||||||
---@field BlueGroups Array<string>
|
---@field BlueGroups Array<string>
|
||||||
---@field supplyHubNames Array<string>
|
---@field supplyHubNames Array<string>
|
||||||
@@ -78,8 +82,10 @@ function Database.New(Logger)
|
|||||||
local tables = {
|
local tables = {
|
||||||
AllZoneNames = {},
|
AllZoneNames = {},
|
||||||
BlueSams = {},
|
BlueSams = {},
|
||||||
CapRoutes = {},
|
AllCapRoutes = {},
|
||||||
capZonesByCapZoneID = {},
|
capZonesByCapZoneID = {},
|
||||||
|
AllInterceptZones = {},
|
||||||
|
interceptZonesByZoneID = {},
|
||||||
CarrierRouteZones = {},
|
CarrierRouteZones = {},
|
||||||
MissionZones = {},
|
MissionZones = {},
|
||||||
MissionZonesLocations = {},
|
MissionZonesLocations = {},
|
||||||
@@ -141,37 +147,43 @@ function Database.New(Logger)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "waitingstage" then
|
local lowered = string.lower(split_string[1])
|
||||||
|
|
||||||
|
if lowered == "waitingstage" then
|
||||||
table.insert(self._tables.StageZoneNames, zone_name)
|
table.insert(self._tables.StageZoneNames, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "mission" then
|
if lowered == "mission" then
|
||||||
table.insert(self._tables.MissionZones, zone_name)
|
table.insert(self._tables.MissionZones, zone_name)
|
||||||
self._tables.MissionZonesLocations[zone_name] = zoneLocation
|
self._tables.MissionZonesLocations[zone_name] = zoneLocation
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "randommission" then
|
if lowered == "randommission" then
|
||||||
table.insert(self._tables.RandomMissionZones, zone_name)
|
table.insert(self._tables.RandomMissionZones, zone_name)
|
||||||
self._tables.MissionZonesLocations[zone_name] = zoneLocation
|
self._tables.MissionZonesLocations[zone_name] = zoneLocation
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "farp" then
|
if lowered == "farp" then
|
||||||
table.insert(self._tables.AllFarpZones, zone_name)
|
table.insert(self._tables.AllFarpZones, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "caproute" then
|
if lowered == "caproute" then
|
||||||
table.insert(self._tables.CapRoutes, zone_name)
|
table.insert(self._tables.AllCapRoutes, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "carrierroute" then
|
if lowered == "interceptzone" then
|
||||||
|
table.insert(self._tables.AllInterceptZones, zone_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if lowered == "carrierroute" then
|
||||||
table.insert(self._tables.CarrierRouteZones, zone_name)
|
table.insert(self._tables.CarrierRouteZones, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "bluesam" then
|
if lowered == "bluesam" then
|
||||||
table.insert(self._tables.BlueSams, zone_name)
|
table.insert(self._tables.BlueSams, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "supplyhub" then
|
if lowered == "supplyhub" then
|
||||||
table.insert(self._tables.SupplyHubZones, zone_name)
|
table.insert(self._tables.SupplyHubZones, zone_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -364,7 +376,7 @@ function Database.New(Logger)
|
|||||||
self:loadMiscGroupsInStages()
|
self:loadMiscGroupsInStages()
|
||||||
|
|
||||||
|
|
||||||
for _, cap_route_zone in pairs(self._tables.CapRoutes) do
|
for _, cap_route_zone in pairs(self._tables.AllCapRoutes) do
|
||||||
local split = Spearhead.Util.split_string(cap_route_zone, "_")
|
local split = Spearhead.Util.split_string(cap_route_zone, "_")
|
||||||
local zoneID = split[2]
|
local zoneID = split[2]
|
||||||
|
|
||||||
@@ -383,6 +395,22 @@ function Database.New(Logger)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, interceptZone in pairs(self._tables.AllInterceptZones) do
|
||||||
|
local split = Spearhead.Util.split_string(interceptZone, "_")
|
||||||
|
local zoneID = split[2]
|
||||||
|
|
||||||
|
if zoneID then
|
||||||
|
if tables.interceptZonesByZoneID[zoneID] == nil then
|
||||||
|
tables.interceptZonesByZoneID[zoneID] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local zone = Spearhead.DcsUtil.getZoneByName(interceptZone)
|
||||||
|
if zone then
|
||||||
|
table.insert(tables.interceptZonesByZoneID[zoneID], zone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local totalUnits = 0
|
local totalUnits = 0
|
||||||
local missions = 0
|
local missions = 0
|
||||||
for _, data in pairs(self._tables.MissionZoneData) do
|
for _, data in pairs(self._tables.MissionZoneData) do
|
||||||
@@ -525,6 +553,8 @@ function Database:getOrCreateAirbaseData(baseName)
|
|||||||
if baseData == nil then
|
if baseData == nil then
|
||||||
baseData = {
|
baseData = {
|
||||||
CapGroups = {},
|
CapGroups = {},
|
||||||
|
InterceptGroups = {},
|
||||||
|
SweepGroups = {},
|
||||||
RedGroups = {},
|
RedGroups = {},
|
||||||
BlueGroups = {},
|
BlueGroups = {},
|
||||||
supplyHubNames = {}
|
supplyHubNames = {}
|
||||||
@@ -591,7 +621,14 @@ function Database:loadCapUnits()
|
|||||||
local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, zone)
|
local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, zone)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(baseData.CapGroups, groupName)
|
|
||||||
|
if Spearhead.Util.startswith(groupName, "CAP_A", true) or Spearhead.Util.startswith(groupName, "CAP_B", true) then
|
||||||
|
table.insert(baseData.CapGroups, groupName)
|
||||||
|
elseif Spearhead.Util.startswith(groupName, "CAP_I", true) then
|
||||||
|
table.insert(baseData.InterceptGroups, groupName)
|
||||||
|
elseif Spearhead.Util.startswith(groupName, "CAP_S", true) then
|
||||||
|
table.insert(baseData.SweepGroups, groupName)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self._tables.AirbaseDataPerAirfield[airbase:getName()] = baseData
|
self._tables.AirbaseDataPerAirfield[airbase:getName()] = baseData
|
||||||
@@ -832,6 +869,18 @@ function Database:GetCapZoneForZoneID(zoneID)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Database:GetInterceptZonesForZoneID(zoneID)
|
||||||
|
zoneID = tostring(zoneID) or "nothing"
|
||||||
|
|
||||||
|
local interceptZonesForID = self._tables.interceptZonesByZoneID[zoneID]
|
||||||
|
|
||||||
|
if not interceptZonesForID then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return interceptZonesForID
|
||||||
|
end
|
||||||
|
|
||||||
---@return Array<string> result a list of stage zone names
|
---@return Array<string> result a list of stage zone names
|
||||||
function Database:getStagezoneNames()
|
function Database:getStagezoneNames()
|
||||||
return self._tables.StageZoneNames
|
return self._tables.StageZoneNames
|
||||||
|
|||||||
@@ -46,8 +46,13 @@ assert(loadfile(classPath .. "stageClasses\\SpecialZones\\SupplyHub.lua"))()
|
|||||||
|
|
||||||
assert(loadfile(classPath .. "capClasses\\taskings\\RTB.lua"))()
|
assert(loadfile(classPath .. "capClasses\\taskings\\RTB.lua"))()
|
||||||
assert(loadfile(classPath .. "capClasses\\taskings\\CAP.lua"))()
|
assert(loadfile(classPath .. "capClasses\\taskings\\CAP.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\taskings\\SWEEP.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\taskings\\INTERCEPT.lua"))()
|
||||||
assert(loadfile(classPath .. "capClasses\\airGroups\\AirGroup.lua"))()
|
assert(loadfile(classPath .. "capClasses\\airGroups\\AirGroup.lua"))()
|
||||||
assert(loadfile(classPath .. "capClasses\\airGroups\\CapGroup.lua"))()
|
assert(loadfile(classPath .. "capClasses\\airGroups\\CapGroup.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\airGroups\\SweepGroup.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\airGroups\\InterceptGroup.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\detection\\DetectionManager.lua"))()
|
||||||
assert(loadfile(classPath .. "capClasses\\GlobalCapManager.lua"))()
|
assert(loadfile(classPath .. "capClasses\\GlobalCapManager.lua"))()
|
||||||
assert(loadfile(classPath .. "capClasses\\CapAirbase.lua"))()
|
assert(loadfile(classPath .. "capClasses\\CapAirbase.lua"))()
|
||||||
assert(loadfile(classPath .. "capClasses\\runwayBombing\\RunwayBombingTracker.lua"))()
|
assert(loadfile(classPath .. "capClasses\\runwayBombing\\RunwayBombingTracker.lua"))()
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ else
|
|||||||
standardLogger:info("Persistence disabled")
|
standardLogger:info("Persistence disabled")
|
||||||
end
|
end
|
||||||
|
|
||||||
Spearhead.classes.capClasses.GlobalCapManager.start(databaseManager, capConfig, stageConfig, defaultLogLevel)
|
local detectionLogger = Spearhead.LoggerTemplate.new("DetectionManager", defaultLogLevel)
|
||||||
|
local detectionManager = Spearhead.classes.capClasses.detection.DetectionManager.New(detectionLogger)
|
||||||
|
|
||||||
|
Spearhead.classes.capClasses.GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel)
|
||||||
Spearhead.internal.GlobalStageManager:NewAndStart(databaseManager, stageConfig, defaultLogLevel)
|
Spearhead.internal.GlobalStageManager:NewAndStart(databaseManager, stageConfig, defaultLogLevel)
|
||||||
Spearhead.internal.GlobalFleetManager.start(databaseManager)
|
Spearhead.internal.GlobalFleetManager.start(databaseManager)
|
||||||
|
|
||||||
@@ -74,3 +77,8 @@ Spearhead.internal.GlobalStageManager:printFullOverview()
|
|||||||
-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true)
|
-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true)
|
||||||
|
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
|
|
||||||
|
local unit = Unit.getByName("CAP_B[1-2]10A_sochi-2-1")
|
||||||
|
local desc = unit:getDesc()
|
||||||
|
env.info("Blaat desc: " .. Spearhead.Util.toString(desc))
|
||||||
Reference in New Issue
Block a user