Develop (#16)
* Initial Methods for SpearheadAPI * Added Spearhead API * Added Spearhead API * fixed compile for api * Added notes for API in beta * Added notes for API in beta * Added Menu to all doc pages * Added Menu to all doc pages * added it for real * added it for real * fixed develop * fixed develop * fixed develop * wip * wip * updated * work in progress * work in progress * added empty docs page * wip * removed SAR files from persistence branch * Added Blue Sams as seperate class * added initial persistance * added initial persistance * added initial persistance * added initial persistance * added initial persistance * added menu to persistence markdown * added menu to persistence markdown * added menu to persistence markdown * docs update * wip * wip * Refactor of Stage classes. * wip * wip * wip * reworked bluesam and stagebase * Added waiting stage functionality * updated docs * wip * wip * wip * updated * wip * wip * wip * refactored without issues, needs testing --------- Co-authored-by: Dutchie <54616262+dutchie032@users.noreply.github.com> Co-authored-by: dutchie032 <dutchie032> Co-authored-by: ex61wi <tim.rorije@ing.com>
This commit is contained in:
committed by
GitHub
co-authored by
Dutchie
dutchie032 <dutchie032>
ex61wi
parent
9c6a5ed217
commit
ff392e0e7e
@@ -15,7 +15,6 @@ jobs:
|
|||||||
branch: main
|
branch: main
|
||||||
username: dutchie032
|
username: dutchie032
|
||||||
repo: dutchie032.github.io
|
repo: dutchie032.github.io
|
||||||
sourcefileA: Reference
|
|
||||||
sourcefileB: GetStarted
|
sourcefileB: GetStarted
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -34,9 +33,12 @@ jobs:
|
|||||||
run: git config --global user.name "automated action"
|
run: git config --global user.name "automated action"
|
||||||
- name: clone
|
- name: clone
|
||||||
run: git clone --single-branch --branch $branch https://github.com/$username/$repo
|
run: git clone --single-branch --branch $branch https://github.com/$username/$repo
|
||||||
|
|
||||||
|
|
||||||
- name: commit
|
- name: commit
|
||||||
run: git pull --rebase
|
run: git pull --rebase
|
||||||
working-directory: ./dutchie032.github.io
|
working-directory: ./dutchie032.github.io
|
||||||
|
|
||||||
- name: ls
|
- name: ls
|
||||||
run: ls
|
run: ls
|
||||||
- name: Replace strings in file
|
- name: Replace strings in file
|
||||||
@@ -56,15 +58,26 @@ jobs:
|
|||||||
replace("./spearhead/_docs/Reference.md", "##!config!##", luaConfig)
|
replace("./spearhead/_docs/Reference.md", "##!config!##", luaConfig)
|
||||||
|
|
||||||
lastUpdateTime = datetime.datetime.now().isoformat()
|
lastUpdateTime = datetime.datetime.now().isoformat()
|
||||||
replace("./spearhead/_docs/Reference.md", "##!lastupdatetime!##", lastUpdateTime)
|
menu = ""
|
||||||
replace("./spearhead/_docs/GetStarted.md", "##!lastupdatetime!##", lastUpdateTime)
|
with open("./spearhead/_docs/menu.html", "r") as menuFile:
|
||||||
|
menu = menuFile.read()
|
||||||
|
|
||||||
|
files = [ "Reference.md", "GetStarted.md", "SpearheadApi.md", "Persistence.md" ]
|
||||||
|
for file in files:
|
||||||
|
replace(f"./spearhead/_docs/{file}", "##!lastupdatetime!##", lastUpdateTime)
|
||||||
|
with open(f"./spearhead/_docs/{file}", "a") as f:
|
||||||
|
f.write(menu)
|
||||||
|
|
||||||
|
|
||||||
shell: python
|
shell: python
|
||||||
|
|
||||||
- name: mkdir
|
- name: mkdir
|
||||||
run: mkdir -p ./$repo/Spearhead
|
run: mkdir -p ./$repo/Spearhead
|
||||||
- run: npm i markdown-to-html-cli -g
|
- run: npm i markdown-to-html-cli -g
|
||||||
- run: markdown-to-html --title 'Spearhead' --dark-mode auto --keywords "dcs,spearhead,tdcs,mission,campaign,tactical dcs,tactical" --no-dark-mode --markdown-style-theme dark --source ./spearhead/_docs/$sourcefileA.md --output ./$repo/Spearhead/$sourcefileA.html
|
- run: markdown-to-html --title 'Spearhead' --dark-mode auto --keywords "dcs,spearhead,tdcs,mission,campaign,tactical dcs,tactical" --no-dark-mode --markdown-style-theme dark --source ./spearhead/_docs/Reference.md --output ./$repo/Spearhead/Reference.html
|
||||||
- run: markdown-to-html --title 'Spearhead' --dark-mode auto --keywords "dcs,spearhead,tdcs,mission,campaign,tactical dcs,tactical" --no-dark-mode --markdown-style-theme dark --source ./spearhead/_docs/$sourcefileB.md --output ./$repo/Spearhead/$sourcefileB.html
|
- run: markdown-to-html --title 'Spearhead' --dark-mode auto --keywords "dcs,spearhead,tdcs,mission,campaign,tactical dcs,tactical" --no-dark-mode --markdown-style-theme dark --source ./spearhead/_docs/GetStarted.md --output ./$repo/Spearhead/GetStarted.html
|
||||||
|
- run: markdown-to-html --title 'Spearhead' --dark-mode auto --keywords "dcs,spearhead,tdcs,mission,campaign,tactical dcs,tactical" --no-dark-mode --markdown-style-theme dark --source ./spearhead/_docs/SpearheadApi.md --output ./$repo/Spearhead/SpearheadApi.html
|
||||||
|
- run: markdown-to-html --title 'Spearhead' --dark-mode auto --keywords "dcs,spearhead,tdcs,mission,campaign,tactical dcs,tactical" --no-dark-mode --markdown-style-theme dark --source ./spearhead/_docs/Persistence.md --output ./$repo/Spearhead/Persistence.html
|
||||||
|
|
||||||
- run: python3 ./spearhead/SpearheadCompile.py "./spearhead" "./$repo/Spearhead/spearhead.lua"
|
- run: python3 ./spearhead/SpearheadCompile.py "./spearhead" "./$repo/Spearhead/spearhead.lua"
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
@@ -87,4 +100,5 @@ jobs:
|
|||||||
working-directory: ./dutchie032.github.io
|
working-directory: ./dutchie032.github.io
|
||||||
- name: push
|
- name: push
|
||||||
run: git push
|
run: git push
|
||||||
|
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
|
||||||
working-directory: ./dutchie032.github.io
|
working-directory: ./dutchie032.github.io
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"sumneko.lua"
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+7
-1
@@ -12,6 +12,12 @@
|
|||||||
"coalition",
|
"coalition",
|
||||||
"timer",
|
"timer",
|
||||||
"AI",
|
"AI",
|
||||||
"net"
|
"net",
|
||||||
|
"lfs"
|
||||||
|
],
|
||||||
|
"cSpell.words": [
|
||||||
|
"farp",
|
||||||
|
"Sams",
|
||||||
|
"SEAD"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+1251
File diff suppressed because it is too large
Load Diff
@@ -26,8 +26,17 @@ def compile(root, target):
|
|||||||
|
|
||||||
mainFileName = os.path.join(root, "main.lua")
|
mainFileName = os.path.join(root, "main.lua")
|
||||||
with open(mainFileName, 'r') as mainFile:
|
with open(mainFileName, 'r') as mainFile:
|
||||||
|
compiled += "do --Main\n"
|
||||||
string = mainFile.read()
|
string = mainFile.read()
|
||||||
compiled += string
|
compiled += string
|
||||||
|
compiled += "\nend --Main\n"
|
||||||
|
|
||||||
|
apiFileName = os.path.join(root, "api" , "SpearheadApi.lua")
|
||||||
|
with open(apiFileName, 'r') as apiFile:
|
||||||
|
part = "do --Spearhead API\n"
|
||||||
|
part += apiFile.read()
|
||||||
|
part += "\nend --Spearhead API\n"
|
||||||
|
compiled += part
|
||||||
|
|
||||||
with open(target, "w") as targetFile:
|
with open(target, "w") as targetFile:
|
||||||
targetFile.write(compiled)
|
targetFile.write(compiled)
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Spearhead Persistence
|
||||||
|
|
||||||
|
> **_NOTE:_** Persistence is only released in the Beta branch at the moment.
|
||||||
|
|
||||||
|
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/>
|
||||||
|
|
||||||
|
Most is pretty straight forward, however, some zones are somewhat special. <br/>
|
||||||
|
Underneath you'll see all special zones listed.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
To see the full config check: [Reference](./Reference.html#Configuration)
|
||||||
|
|
||||||
|
## Feedback
|
||||||
|
|
||||||
|
Since this feature is still very much in development, please let any issues know as soon as possible and as consice as possible so a fix can be made quickly.
|
||||||
|
|
||||||
|
Currently implemented is local file storage. <br/>
|
||||||
|
If enough interest is expressed cloud based persistence would be possible. <br/>
|
||||||
|
|
||||||
|
|
||||||
|
```lua
|
||||||
|
... Rest of config
|
||||||
|
Persistence = {
|
||||||
|
--- io and lfs cannot be sanitized in the MissionScripting.lua
|
||||||
|
|
||||||
|
--- enables or disables the persistence logic in spearhead
|
||||||
|
enabled = false,
|
||||||
|
|
||||||
|
--- sets the directory where the persistence file is stored
|
||||||
|
--- if nil then lfs.writedir() will be used.
|
||||||
|
--- which will
|
||||||
|
directory = nil ,
|
||||||
|
|
||||||
|
--- the filename of the persistence file. Should end with .json for convention, but any text extension should do.
|
||||||
|
fileName = "Spearhead_Persistence.json"
|
||||||
|
}
|
||||||
|
... Rest of config
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Basic behavior
|
||||||
|
|
||||||
|
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/>
|
||||||
|
This happens every 2 minutes AND during "onMissionStop" event to make sure the mission is as up to date as possible without having to call IO methods on each event.<br/>
|
||||||
|
|
||||||
|
## Misc units
|
||||||
|
|
||||||
|
Miscelaneous units will follow basic behaviour. <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/>
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Missions
|
||||||
|
|
||||||
|
Missions follow the same logic as Misc Units. <br/>
|
||||||
|
|
||||||
|
## Blue SAMs
|
||||||
|
|
||||||
|
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/>
|
||||||
|
This will ensure that the blue units are placed as needed. <br/>
|
||||||
|
|
||||||
|
|
||||||
|
## Airbases
|
||||||
|
|
||||||
|
Airbase units will also be checked for overlap. As the blue units will be spawned after the RED unit. <br/>
|
||||||
|
Units that were alive when the stage was completed will be removed. Units that died will have corpses spawned. <br/>
|
||||||
|
|
||||||
|
## Warehouses
|
||||||
|
|
||||||
|
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/>
|
||||||
+23
-47
@@ -19,51 +19,6 @@ If you however want to go right to the Get Started guide click here: [here](./Ge
|
|||||||
You can always come back later here. <br/>
|
You can always come back later here. <br/>
|
||||||
The Get Started guide will not show all details and reasoning. <br/>
|
The Get Started guide will not show all details and reasoning. <br/>
|
||||||
|
|
||||||
## Release Notes
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>01-10-2024 Initial Version </summary>
|
|
||||||
The initial version with basic functionality
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Feature / TODO list
|
|
||||||
|
|
||||||
- [ ] CAP Manager
|
|
||||||
- [x] RTB flow. Flying out before primary flies back.
|
|
||||||
- [ ] Out of Missile flow
|
|
||||||
- [ ] Stage
|
|
||||||
- [ ] Pre Activate
|
|
||||||
- [x] All SAM Sites
|
|
||||||
- [x] All Red airbase units
|
|
||||||
- [ ] OCA Missions
|
|
||||||
- [ ] Activate
|
|
||||||
- [ ] Activate all DEAD sites. (No surprise pop ups)
|
|
||||||
- [ ] Activate other random mission till 10 max
|
|
||||||
- [ ] Completion Logic
|
|
||||||
- [ ] ?? Custom Conditions ??
|
|
||||||
- [ ] ?? Airbases as final mission ??
|
|
||||||
- [ ] Required Mission types
|
|
||||||
- [ ] Missions
|
|
||||||
- [x] BAI
|
|
||||||
- [x] DEAD
|
|
||||||
- [x] STRIKE
|
|
||||||
- [ ] INTERCEPT
|
|
||||||
- [ ] LOGISTIC
|
|
||||||
- [ ] EXTRACTION | MEDEVAC
|
|
||||||
- [ ] More?
|
|
||||||
|
|
||||||
- [ ] Warehouses
|
|
||||||
- [ ] Logistics [OPTIONAL]
|
|
||||||
- [ ]
|
|
||||||
|
|
||||||
- [ ] Team creation
|
|
||||||
- [ ] Airbases
|
|
||||||
- [ ] Farps
|
|
||||||
- [ ] Carrier/Fleet routes
|
|
||||||
- [ ] Fleet tracks
|
|
||||||
- [ ] Persistance
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Spearhead will always try to be as configurable as possible. <br/>
|
Spearhead will always try to be as configurable as possible. <br/>
|
||||||
@@ -77,6 +32,14 @@ Overwrite the values in your own script (before spearhead runs) or <a download="
|
|||||||
##!config!##
|
##!config!##
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## The Spearhead API
|
||||||
|
|
||||||
|
> **_NOTE:_** The Spearhead.API space is only release in the Beta branch at the moment.
|
||||||
|
|
||||||
|
You can not only use Spearhead, but you can also interact with it via your own scripts.
|
||||||
|
|
||||||
|
See all the reference, explanation here: [SpearheadAPI](./SpearheadApi.html)
|
||||||
|
|
||||||
## All Trigger zone name options:
|
## All Trigger zone name options:
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -84,6 +47,21 @@ Overwrite the values in your own script (before spearhead runs) or <a download="
|
|||||||
A mission stage that encapsulates multiple missions, airbases and other objects.
|
A mission stage that encapsulates multiple missions, airbases and other objects.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>WAITINGSTAGE_</summary>
|
||||||
|
A stage that functions as a logic delay or timer. <br/>
|
||||||
|
With this you can add a wait before or in between stages.
|
||||||
|
|
||||||
|
Naming Convention: `WAITINGSTAGE_<order>_<amount of seconds>`
|
||||||
|
Example: `WAITINGSTAGE_2_180`
|
||||||
|
|
||||||
|
A waiting stage can be part of the same stage order as other stages. <br/>
|
||||||
|
This will first activate the DELAYSTAGE and only when the DELAYSTAGE is completed will the other stages be activated. <br/>
|
||||||
|
This is so you can easily add delays later in your mission development, without havint to rename all subsequent stages.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>MISSION_</summary>
|
<summary>MISSION_</summary>
|
||||||
</details>
|
</details>
|
||||||
@@ -106,8 +84,6 @@ Overwrite the values in your own script (before spearhead runs) or <a download="
|
|||||||
A stage is a logical part of a mission. It's isn't anything special per se, but everything revolves around stages in Spearhead. <br/>
|
A stage is a logical part of a mission. It's isn't anything special per se, but everything revolves around stages in Spearhead. <br/>
|
||||||
Everything is tied to at a stage. <br/>
|
Everything is tied to at a stage. <br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Mission
|
## Mission
|
||||||
|
|
||||||
A mission is a completable objective with a state and a continuous check to see if itself is completed. <br/>
|
A mission is a completable objective with a state and a continuous check to see if itself is completed. <br/>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
# Spearhead.API
|
||||||
|
|
||||||
|
> **_NOTE:_** The Spearhead.API space is only release in the Beta branch at the moment.
|
||||||
|
|
||||||
|
the `Spearhead.API` space is specifically created to make sure mission makers can interact with the framework.
|
||||||
|
|
||||||
|
Simply alter logic, get the current state in Spearhead, and give the whole ME more control.
|
||||||
|
|
||||||
|
eg. Late activate the entire framework by calling `Spearhead.API.Stages.changeStage(1)` later or on demand and setting the starting config stage to -1 in the Spearhead configuration file
|
||||||
|
|
||||||
|
|
||||||
|
## Stages
|
||||||
|
|
||||||
|
```lua
|
||||||
|
|
||||||
|
---Changes the active stage of spearhead.
|
||||||
|
--- All other stages will change based on the normal logic. (CAP, BLUE etc.)
|
||||||
|
---@param stageNumber number the stage number you want changed
|
||||||
|
---@return boolean success indicator of success
|
||||||
|
---@return string message error message
|
||||||
|
Spearhead.API.Stages.changeStage = function(stageNumber) end
|
||||||
|
|
||||||
|
---Returns the current stange number
|
||||||
|
---Returns nil when the stagenumber was not set before ever, which means Spearhead was not started.
|
||||||
|
---@return number | nil
|
||||||
|
Spearhead.API.Stages.getCurrentStage = function() end
|
||||||
|
|
||||||
|
|
||||||
|
---returns whether a stage (by index) is complete.
|
||||||
|
---@param stageNumber number
|
||||||
|
---@return boolean | nil
|
||||||
|
---@return string
|
||||||
|
Spearhead.API.Stages.isStageComplete = function(stageNumber) end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
<div id="menu">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<div style="position:fixed;
|
||||||
|
left:80%;
|
||||||
|
top:80%;
|
||||||
|
height:15%;
|
||||||
|
width:15%;
|
||||||
|
padding:10px;
|
||||||
|
background-color: #00053B;
|
||||||
|
border-radius: 8px;">
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
<a href="/Spearhead/Reference.html" style="color: white; font-size: 18px; ">
|
||||||
|
<i class="bi bi-file-earmark-richtext"></i>
|
||||||
|
 Reference
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
<a href="/Spearhead/GetStarted.html" style="color: white; font-size: 18px;">
|
||||||
|
<i class="bi bi-rocket-takeoff"></i>
|
||||||
|
 Get Started
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
<a href="/Spearhead/Persistence.html" style="color: white; font-size: 18px;">
|
||||||
|
<i class="bi bi-database-check"></i>
|
||||||
|
 Persistence
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
<a href="/Spearhead/SpearheadApi.html" style="color: white; font-size: 18px;">
|
||||||
|
<i class="bi bi-gear"></i>
|
||||||
|
 API
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local SpearheadAPI = {}
|
||||||
|
do
|
||||||
|
|
||||||
|
SpearheadAPI.Stages = {}
|
||||||
|
|
||||||
|
--- Changes the active stage of spearhead.
|
||||||
|
--- All other stages will change based on the normal logic. (CAP, BLUE etc.)
|
||||||
|
--- @param stageNumber number the stage number you want changed
|
||||||
|
--- @return boolean success indicator of success
|
||||||
|
--- @return string message error message
|
||||||
|
SpearheadAPI.Stages.changeStage = function(stageNumber)
|
||||||
|
if type(stageNumber) ~= "number" then
|
||||||
|
return false, "stageNumber " .. stageNumber .. " is not a valid number"
|
||||||
|
end
|
||||||
|
|
||||||
|
Spearhead.Events.PublishStageNumberChanged(stageNumber)
|
||||||
|
return true, ""
|
||||||
|
end
|
||||||
|
|
||||||
|
---Returns the current stange number
|
||||||
|
---Returns nil when the stagenumber was not set before ever, which means Spearhead was not started.
|
||||||
|
---@return number | nil
|
||||||
|
SpearheadAPI.Stages.getCurrentStage = function()
|
||||||
|
return Spearhead.StageNumber or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---returns whether a stage (by index) is complete.
|
||||||
|
---@param stageNumber number
|
||||||
|
---@return boolean | nil
|
||||||
|
---@return string
|
||||||
|
SpearheadAPI.isStageComplete = function(stageNumber)
|
||||||
|
if type(stageNumber) ~= "number" then
|
||||||
|
return false, "stageNumber " .. stageNumber .. " is not a valid number"
|
||||||
|
end
|
||||||
|
|
||||||
|
local isComplete = Spearhead.internal.GlobalStageManager.isStageComplete(stageNumber)
|
||||||
|
if isComplete == nil then
|
||||||
|
return nil, "no stage found with number " .. stageNumber
|
||||||
|
end
|
||||||
|
|
||||||
|
return isComplete, ""
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if Spearhead == nil then Spearhead = {} end
|
||||||
|
Spearhead.API = SpearheadAPI
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---@class AirGroup
|
||||||
|
---@field groupName string
|
||||||
|
---@field groupState GroupState
|
||||||
|
local AirGroup = {}
|
||||||
|
|
||||||
|
---@alias GroupState
|
||||||
|
---| "UnSpawned"
|
||||||
|
---| "ReadOnTheRamp
|
||||||
|
---| "InTransit"
|
||||||
|
---| "OnStation"
|
||||||
|
---| "RtbInTen"
|
||||||
|
---| "Rtb"
|
||||||
|
---| "Dead"
|
||||||
|
---| "Rearming"
|
||||||
|
|
||||||
|
|
||||||
|
---@alias AirGroupType
|
||||||
|
---| "CAP"
|
||||||
|
|
||||||
|
---| "CAS"
|
||||||
|
---| "SEAD"
|
||||||
|
---| "INTERCEPT"
|
||||||
|
---| ""
|
||||||
|
|
||||||
|
|
||||||
|
---@class GroupNameData
|
||||||
|
---@field type AirGroupType
|
||||||
|
---@field isBackup boolean
|
||||||
|
---@field zonesConfig table<string, string>
|
||||||
|
|
||||||
|
local function parseGroupName(groupName)
|
||||||
|
|
||||||
|
local split_string = Spearhead.Util.split_string(groupName, "_")
|
||||||
|
local partCount = Spearhead.Util.tableLength(split_string)
|
||||||
|
|
||||||
|
if partCount >= 3 then
|
||||||
|
|
||||||
|
---@type boolean
|
||||||
|
local isBackup = false
|
||||||
|
|
||||||
|
do -- config
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@generic T: AirGroup
|
||||||
|
---@param o T
|
||||||
|
---@param groupName string
|
||||||
|
---@return T
|
||||||
|
function AirGroup.New(o, groupName)
|
||||||
|
AirGroup.__index = AirGroup
|
||||||
|
local o = o or {}
|
||||||
|
local self = setmetatable(o, AirGroup)
|
||||||
|
|
||||||
|
self.groupName = groupName
|
||||||
|
self.groupState = "UnSpawned"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ function CapConfig:new()
|
|||||||
local deathDelay = tonumber(SpearheadConfig.CapConfig.deathDelay) or 1800
|
local deathDelay = tonumber(SpearheadConfig.CapConfig.deathDelay) or 1800
|
||||||
---@return number
|
---@return number
|
||||||
o.getDeathDelay = function(self) return deathDelay end
|
o.getDeathDelay = function(self) return deathDelay end
|
||||||
o.logLevel = Spearhead.LoggerTemplate.LogLevelOptions.INFO
|
o.logLevel = "INFO"
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,44 +1,38 @@
|
|||||||
|
|
||||||
|
--- @class StageConfig
|
||||||
|
--- @field isEnabled boolean
|
||||||
|
--- @field isDrawStagesEnabled boolean
|
||||||
|
--- @field isAutoStages boolean
|
||||||
|
--- @field startingStage integer
|
||||||
|
--- @field maxMissionsPerStage integer
|
||||||
|
--- @field logLevel LogLevel
|
||||||
|
|
||||||
|
|
||||||
local StageConfig = {};
|
local StageConfig = {};
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@return StageConfig
|
||||||
function StageConfig:new()
|
function StageConfig:new()
|
||||||
local o = {}
|
|
||||||
setmetatable(o, { __index = self })
|
|
||||||
|
|
||||||
if SpearheadConfig == nil then SpearheadConfig = {} end
|
if SpearheadConfig == nil then SpearheadConfig = {} end
|
||||||
if SpearheadConfig.StageConfig == nil then SpearheadConfig.StageConfig = {} end
|
if SpearheadConfig.StageConfig == nil then SpearheadConfig.StageConfig = {} end
|
||||||
|
|
||||||
local enabled = SpearheadConfig.StageConfig.enabled
|
---@type StageConfig
|
||||||
if enabled == nil then enabled = true end
|
local o = {
|
||||||
---@return boolean
|
isEnabled = SpearheadConfig.StageConfig.enabled or true,
|
||||||
o.isEnabled = function(self) return enabled == true end
|
isDrawStagesEnabled = SpearheadConfig.StageConfig.drawStages or true,
|
||||||
|
isAutoStages = SpearheadConfig.StageConfig.autoStages or true,
|
||||||
|
startingStage = SpearheadConfig.StageConfig.startingStage or 1,
|
||||||
|
maxMissionsPerStage = SpearheadConfig.StageConfig.maxMissionStage or 10,
|
||||||
|
logLevel = "INFO"
|
||||||
|
}
|
||||||
|
|
||||||
local drawStages = SpearheadConfig.StageConfig.drawStages
|
if SpearheadConfig.StageConfig.debugEnabled == true then
|
||||||
if drawStages == nil then drawStages = true end
|
o.logLevel = "DEBUG"
|
||||||
---@return boolean
|
|
||||||
o.isDrawStagesEnabled = function(self) return drawStages == true end
|
|
||||||
|
|
||||||
local autoStages = SpearheadConfig.StageConfig.autoStages or true
|
|
||||||
if autoStages == nil then autoStages = true end
|
|
||||||
---@return boolean
|
|
||||||
o.isAutoStages = function(self) return autoStages end
|
|
||||||
|
|
||||||
local maxMissionsPerStage = SpearheadConfig.StageConfig.maxMissionStage
|
|
||||||
o.getMaxMissionsPerStage = function(self) return maxMissionsPerStage end
|
|
||||||
|
|
||||||
o.logLevel = Spearhead.LoggerTemplate.LogLevelOptions.INFO
|
|
||||||
|
|
||||||
local startingStage = SpearheadConfig.StageConfig.startingStage or 1
|
|
||||||
o.getStartingStage = function(self) return startingStage end
|
|
||||||
|
|
||||||
o.toString = function()
|
|
||||||
return Spearhead.Util.toString({
|
|
||||||
maxMissionsPerStage = maxMissionsPerStage,
|
|
||||||
enabled = enabled,
|
|
||||||
drawStages = drawStages,
|
|
||||||
autoStages = autoStages
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
setmetatable(o, { __index = self })
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
do -- mission aliases
|
||||||
|
|
||||||
|
--- @alias MissionPriority
|
||||||
|
--- | "primary"
|
||||||
|
--- | "secondary"
|
||||||
|
|
||||||
|
--- @alias missionType
|
||||||
|
--- | "nil"
|
||||||
|
--- | "STRIKE"
|
||||||
|
--- | "BAI"
|
||||||
|
--- | "DEAD"
|
||||||
|
--- | "SAM"
|
||||||
|
|
||||||
|
--- @alias MissionState
|
||||||
|
--- | "NEW"
|
||||||
|
--- | "ACTIVE"
|
||||||
|
--- | "COMPLETED"
|
||||||
|
|
||||||
|
--- @alias LogLevel
|
||||||
|
--- | "DEBUG"
|
||||||
|
--- | "INFO"
|
||||||
|
--- | "WARN"
|
||||||
|
--- | "ERROR"
|
||||||
|
--- | "NONE"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---@class Array<T>: { [integer]: T }
|
||||||
|
|
||||||
|
--- @class Position
|
||||||
|
--- @field x number x position (Top-Down on Map)
|
||||||
|
--- @field y number y altitude
|
||||||
|
--- @field z number z position (Left-Right on Map)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ local fleetGroups = {}
|
|||||||
|
|
||||||
GlobalFleetManager.start = function(database)
|
GlobalFleetManager.start = function(database)
|
||||||
|
|
||||||
local logger = Spearhead.LoggerTemplate:new("CARRIERFLEET", Spearhead.LoggerTemplate.LogLevelOptions.INFO)
|
local logger = Spearhead.LoggerTemplate:new("CARRIERFLEET", "INFO")
|
||||||
|
|
||||||
local all_groups = Spearhead.DcsUtil.getAllGroupNames()
|
local all_groups = Spearhead.DcsUtil.getAllGroupNames()
|
||||||
for _, groupName in pairs(all_groups) do
|
for _, groupName in pairs(all_groups) do
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
|
||||||
|
local Persistence = {}
|
||||||
|
do
|
||||||
|
---@class PersistentData
|
||||||
|
---@field dead_units table<string, DeathState>
|
||||||
|
---@field activeStage integer|nil
|
||||||
|
|
||||||
|
---@class DeathState
|
||||||
|
---@field isDead boolean
|
||||||
|
---@field pos Position
|
||||||
|
---@field heading number
|
||||||
|
---@field type string
|
||||||
|
---@field country_id integer
|
||||||
|
|
||||||
|
|
||||||
|
local persistanceWriteIntervalSeconds = 15
|
||||||
|
local enabled = false
|
||||||
|
|
||||||
|
---@type PersistentData
|
||||||
|
local tables = {
|
||||||
|
dead_units = {},
|
||||||
|
activeStage = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local logger = {}
|
||||||
|
|
||||||
|
if SpearheadConfig == nil then SpearheadConfig = {} end
|
||||||
|
if SpearheadConfig.Persistence == nil then SpearheadConfig.Persistence = {} end
|
||||||
|
|
||||||
|
local path = nil
|
||||||
|
local updateRequired = false
|
||||||
|
|
||||||
|
local createFileIfNotExists = function()
|
||||||
|
if not path then return end
|
||||||
|
|
||||||
|
local f = io.open(path, "r")
|
||||||
|
if f == nil then
|
||||||
|
f = io.open(path, "w+")
|
||||||
|
if f == nil then
|
||||||
|
logger:error("Could not create a file")
|
||||||
|
else
|
||||||
|
f:write("{}")
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local loadTablesFromFile = function()
|
||||||
|
if not path then return end
|
||||||
|
|
||||||
|
logger:info("Loading data from persistance file...")
|
||||||
|
local f = io.open(path, "r")
|
||||||
|
if f == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local json = f:read("*a")
|
||||||
|
local lua = net.json2lua(json)
|
||||||
|
|
||||||
|
if lua.activeStage then
|
||||||
|
logger:info("Found active stage from save: " .. lua.activeStage)
|
||||||
|
tables.activeStage = lua.activeStage
|
||||||
|
end
|
||||||
|
|
||||||
|
if lua.dead_units then
|
||||||
|
logger:debug("Found saved dead units")
|
||||||
|
for name, deadState in pairs(lua.dead_units) do
|
||||||
|
logger:debug("Found saved dead unit: " .. name)
|
||||||
|
|
||||||
|
if type(deadState) == "table" then
|
||||||
|
tables.dead_units[name] = {
|
||||||
|
isDead = deadState.isDead == true,
|
||||||
|
pos = deadState.pos,
|
||||||
|
heading = deadState.heading,
|
||||||
|
type = deadState.type,
|
||||||
|
country_id = deadState.country_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local writeToFile = function()
|
||||||
|
if not path then return end
|
||||||
|
|
||||||
|
local f = io.open(path, "w+")
|
||||||
|
if f == nil then
|
||||||
|
error("Could not open file for writing")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local jsonString = net.lua2json(tables)
|
||||||
|
f:write(jsonString)
|
||||||
|
|
||||||
|
if f ~= nil then
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local UpdateContinuous = function(null, time)
|
||||||
|
|
||||||
|
if updateRequired then
|
||||||
|
local status, result = pcall(writeToFile)
|
||||||
|
if status == false then
|
||||||
|
env.error("[Spearhead][Persistence] Could not write state to file: " .. result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return time + persistanceWriteIntervalSeconds
|
||||||
|
end
|
||||||
|
|
||||||
|
Persistence.UpdateNow = function()
|
||||||
|
if enabled == true then
|
||||||
|
writeToFile()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Persistence.isEnabled = function()
|
||||||
|
return enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param persistenceLogger Logger
|
||||||
|
Persistence.Init = function(persistenceLogger)
|
||||||
|
logger = persistenceLogger
|
||||||
|
|
||||||
|
logger:info("Initiating Persistence Manager")
|
||||||
|
|
||||||
|
if lfs == nil or io == nil then
|
||||||
|
env.error("[Spearhead][Persistence] lfs and io seem to be sanitized. Persistence is skipped and disabled")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
path = (SpearheadConfig.Persistence.directory or (lfs.writedir() .. "\\Data" )) .. "\\" .. (SpearheadConfig.Persistence.fileName or "Spearhead_Persistence.json")
|
||||||
|
|
||||||
|
createFileIfNotExists()
|
||||||
|
loadTablesFromFile()
|
||||||
|
timer.scheduleFunction(UpdateContinuous, nil, timer.getTime() + 120)
|
||||||
|
enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
---Sets the stage in the persistence table
|
||||||
|
---@param stageNumber number
|
||||||
|
Persistence.SetActiveStage = function(stageNumber)
|
||||||
|
tables.activeStage = stageNumber
|
||||||
|
updateRequired = true
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the active stage as in the persistance file
|
||||||
|
---@return integer|nil
|
||||||
|
Persistence.GetActiveStage = function()
|
||||||
|
if tables.activeStage then
|
||||||
|
return tables.activeStage
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---Check if the unit was dead during the last save. Nil if persitance not enabled
|
||||||
|
---@param unitName string name
|
||||||
|
---@return DeathState|nil { isDead, pos = {x,y,z}, heading, type, country_id }
|
||||||
|
Persistence.UnitDeadState = function(unitName)
|
||||||
|
if Persistence.isEnabled() == false then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local entry = tables.dead_units[unitName]
|
||||||
|
if entry then
|
||||||
|
return entry
|
||||||
|
else
|
||||||
|
return { isDead = false }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Pass the unit to be saved as "dead"
|
||||||
|
---@param name string
|
||||||
|
---@param position Position { x, y ,z }
|
||||||
|
---@param heading number
|
||||||
|
---@param type string
|
||||||
|
---@param country_id number
|
||||||
|
Persistence.UnitKilled = function (name, position, heading, type, country_id)
|
||||||
|
if enabled == false then return end
|
||||||
|
|
||||||
|
tables.dead_units[name] = {
|
||||||
|
isDead = true,
|
||||||
|
pos = position,
|
||||||
|
heading = heading,
|
||||||
|
type = type,
|
||||||
|
country_id = country_id,
|
||||||
|
isCleaned = false
|
||||||
|
}
|
||||||
|
updateRequired = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Spearhead == nil then Spearhead = {} end
|
||||||
|
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||||
|
if Spearhead.classes.persistence == nil then Spearhead.classes.persistence = {} end
|
||||||
|
Spearhead.classes.persistence.Persistence = Persistence
|
||||||
+93
-43
@@ -78,8 +78,14 @@ do -- INIT UTIL
|
|||||||
---comment
|
---comment
|
||||||
---@param str string
|
---@param str string
|
||||||
---@param findable string
|
---@param findable string
|
||||||
|
---@param ignoreCase boolean?
|
||||||
---@return boolean
|
---@return boolean
|
||||||
UTIL.startswith = function(str, findable)
|
UTIL.startswith = function(str, findable, ignoreCase)
|
||||||
|
|
||||||
|
if ignoreCase == true then
|
||||||
|
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
return str:find('^' .. findable) ~= nil
|
return str:find('^' .. findable) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -114,6 +120,14 @@ do -- INIT UTIL
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param a table DCS Point vector {x, z , y}
|
||||||
|
---@param b table DCS Point vector {x, z , y}
|
||||||
|
---@return number
|
||||||
|
function UTIL.VectorDistance(a, b)
|
||||||
|
return math.sqrt((b.x - a.x) ^ 2 + (b.z - a.z) ^ 2)
|
||||||
|
end
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param polygon table of pairs { x, z }
|
---@param polygon table of pairs { x, z }
|
||||||
---@param x number X location
|
---@param x number X location
|
||||||
@@ -452,8 +466,8 @@ do -- INIT DCS_UTIL
|
|||||||
return StaticObject.getByName(groupName) ~= nil
|
return StaticObject.getByName(groupName) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---comment
|
---destroy the given group
|
||||||
---@param groupName string destroy the given group
|
---@param groupName string
|
||||||
function DCS_UTIL.DestroyGroup(groupName)
|
function DCS_UTIL.DestroyGroup(groupName)
|
||||||
if DCS_UTIL.IsGroupStatic(groupName) then
|
if DCS_UTIL.IsGroupStatic(groupName) then
|
||||||
local object = StaticObject.getByName(groupName)
|
local object = StaticObject.getByName(groupName)
|
||||||
@@ -468,6 +482,22 @@ do -- INIT DCS_UTIL
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---destroy the given unit
|
||||||
|
function DCS_UTIL.DestroyUnit(unitName)
|
||||||
|
if DCS_UTIL.IsGroupStatic(unitName) == true then
|
||||||
|
local object = StaticObject.getByName(unitName)
|
||||||
|
if object ~= nil then
|
||||||
|
object:destroy()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local unit = Unit.getByName(unitName)
|
||||||
|
if unit and unit:isExist() then
|
||||||
|
unit:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- takes a list of units and returns all the units that are in any of the zones
|
--- takes a list of units and returns all the units that are in any of the zones
|
||||||
---@param unit_names table unit names
|
---@param unit_names table unit names
|
||||||
---@param zone_names table zone names
|
---@param zone_names table zone names
|
||||||
@@ -604,7 +634,7 @@ do -- INIT DCS_UTIL
|
|||||||
--- takes a x, y poistion and checks if it is inside any of the zones
|
--- takes a x, y poistion and checks if it is inside any of the zones
|
||||||
---@param x number North South position
|
---@param x number North South position
|
||||||
---@param z number West East position
|
---@param z number West East position
|
||||||
---@param zone_name table zone names
|
---@param zone_name string zone name
|
||||||
---@return boolean result
|
---@return boolean result
|
||||||
function DCS_UTIL.isPositionInZone(x, z, zone_name)
|
function DCS_UTIL.isPositionInZone(x, z, zone_name)
|
||||||
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
||||||
@@ -786,6 +816,37 @@ do -- INIT DCS_UTIL
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Spawn an corpse
|
||||||
|
---@param countryId number countryId
|
||||||
|
---@param unitType string
|
||||||
|
---@param location table { z, y, z}
|
||||||
|
---@param heading number
|
||||||
|
function DCS_UTIL.SpawnCorpse(countryId, unitName, unitType, location, heading)
|
||||||
|
local name = "dead_" .. unitName
|
||||||
|
|
||||||
|
local staticObj = {
|
||||||
|
["heading"] = heading,
|
||||||
|
--["shape_name"] = "stolovaya",
|
||||||
|
["type"] = unitType,
|
||||||
|
["name"] = name,
|
||||||
|
["y"] = location.z,
|
||||||
|
["x"] = location.x,
|
||||||
|
["dead"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
coalition.addStaticObject(countryId, staticObj)
|
||||||
|
end
|
||||||
|
|
||||||
|
function DCS_UTIL.CleanCorpse(unitName)
|
||||||
|
local unitName = "dead_" .. unitName
|
||||||
|
|
||||||
|
local object = StaticObject.getByName(unitName)
|
||||||
|
|
||||||
|
if object then
|
||||||
|
object:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- spawns the units as specified in the mission file itself
|
--- spawns the units as specified in the mission file itself
|
||||||
--- location and route can be nil and will then use default route
|
--- location and route can be nil and will then use default route
|
||||||
---@param groupName string
|
---@param groupName string
|
||||||
@@ -793,19 +854,20 @@ do -- INIT DCS_UTIL
|
|||||||
---@param route table? route of the group. If nil wil be the default route.
|
---@param route table? route of the group. If nil wil be the default route.
|
||||||
---@param uncontrolled boolean? Sets the group to be uncontrolled on spawn
|
---@param uncontrolled boolean? Sets the group to be uncontrolled on spawn
|
||||||
---@return table? new_group the Group class that was spawned
|
---@return table? new_group the Group class that was spawned
|
||||||
|
---@return boolean? isStatic whether the group is a static or not
|
||||||
function DCS_UTIL.SpawnGroupTemplate(groupName, location, route, uncontrolled)
|
function DCS_UTIL.SpawnGroupTemplate(groupName, location, route, uncontrolled)
|
||||||
if groupName == nil then
|
if groupName == nil then
|
||||||
return
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local template = DCS_UTIL.GetMizGroupOrDefault(groupName, nil)
|
local template = DCS_UTIL.GetMizGroupOrDefault(groupName, nil)
|
||||||
if template == nil then
|
if template == nil then
|
||||||
return nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
if template.category == DCS_UTIL.GroupCategory.STATIC then
|
if template.category == DCS_UTIL.GroupCategory.STATIC then
|
||||||
--TODO: Implement location and route stuff
|
--TODO: Implement location and route stuff
|
||||||
local spawn_template = template.group_template
|
local spawn_template = template.group_template
|
||||||
coalition.addStaticObject(template.country_id, spawn_template)
|
return coalition.addStaticObject(template.country_id, spawn_template), true
|
||||||
else
|
else
|
||||||
local spawn_template = template.group_template
|
local spawn_template = template.group_template
|
||||||
if location ~= nil then
|
if location ~= nil then
|
||||||
@@ -833,7 +895,7 @@ do -- INIT DCS_UTIL
|
|||||||
spawn_template.uncontrolled = uncontrolled
|
spawn_template.uncontrolled = uncontrolled
|
||||||
end
|
end
|
||||||
local new_group = coalition.addGroup(template.country_id, template.category, spawn_template)
|
local new_group = coalition.addGroup(template.country_id, template.category, spawn_template)
|
||||||
return new_group
|
return new_group, false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -843,11 +905,14 @@ do -- INIT DCS_UTIL
|
|||||||
bingoSetting = bingoSetting + offset
|
bingoSetting = bingoSetting + offset
|
||||||
|
|
||||||
local group = Group.getByName(groupName)
|
local group = Group.getByName(groupName)
|
||||||
|
if group then
|
||||||
for _, unit in pairs(group:getUnits()) do
|
for _, unit in pairs(group:getUnits()) do
|
||||||
if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then
|
if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -857,22 +922,25 @@ Spearhead.DcsUtil = DCS_UTIL
|
|||||||
|
|
||||||
local LOGGER = {}
|
local LOGGER = {}
|
||||||
do
|
do
|
||||||
LOGGER.LogLevelOptions = {
|
|
||||||
DEBUG = 0,
|
|
||||||
INFO = 1,
|
|
||||||
WARN = 2,
|
|
||||||
ERROR = 3,
|
|
||||||
NONE = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
local PreFix = "Spearhead"
|
local PreFix = "Spearhead"
|
||||||
|
|
||||||
function LOGGER:new(logger_name, logLevel, liveLoggingLevel)
|
--- @class Logger
|
||||||
|
--- @field debug fun(self:Logger, text:string)
|
||||||
|
--- @field info fun(self:Logger, text:string)
|
||||||
|
--- @field warn fun(self:Logger, text:string)
|
||||||
|
--- @field error fun(self:Logger, text:string)
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param logger_name any
|
||||||
|
---@param logLevel LogLevel
|
||||||
|
---@return Logger
|
||||||
|
function LOGGER:new(logger_name, logLevel)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, { __index = self })
|
setmetatable(o, { __index = self })
|
||||||
o.LoggerName = logger_name or "(loggername not set)"
|
o.LoggerName = logger_name or "(loggername not set)"
|
||||||
o.LogLevel = logLevel or LOGGER.LogLevelOptions.INFO
|
o.LogLevel = logLevel or "INFO"
|
||||||
o.LiveLoggingLevel = liveLoggingLevel or LOGGER.LogLevelOptions.NONE
|
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param self table self logger
|
---@param self table self logger
|
||||||
@@ -883,13 +951,9 @@ do
|
|||||||
end
|
end
|
||||||
message = UTIL.toString(message)
|
message = UTIL.toString(message)
|
||||||
|
|
||||||
if self.LogLevel <= LOGGER.LogLevelOptions.INFO then
|
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" then
|
||||||
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.INFO then
|
|
||||||
trigger.action.outText(message, 20)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
@@ -900,12 +964,8 @@ do
|
|||||||
end
|
end
|
||||||
message = UTIL.toString(message)
|
message = UTIL.toString(message)
|
||||||
|
|
||||||
if self.LogLevel <= LOGGER.LogLevelOptions.WARN then
|
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then
|
||||||
env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||||
end
|
|
||||||
|
|
||||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.WARN then
|
|
||||||
trigger.action.outText(message, 20)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -919,12 +979,8 @@ do
|
|||||||
|
|
||||||
message = UTIL.toString(message)
|
message = UTIL.toString(message)
|
||||||
|
|
||||||
if self.LogLevel <= LOGGER.LogLevelOptions.ERROR then
|
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.logLevel == "ERROR" then
|
||||||
env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||||
end
|
|
||||||
|
|
||||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.ERROR then
|
|
||||||
trigger.action.outText(message, 20)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -937,13 +993,9 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
message = UTIL.toString(message)
|
message = UTIL.toString(message)
|
||||||
if self.LogLevel <= LOGGER.LogLevelOptions.DEBUG then
|
if self.LogLevel == "DEBUG" then
|
||||||
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message)
|
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.DEBUG then
|
|
||||||
trigger.action.outText(message, 20)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -957,15 +1009,13 @@ function Spearhead.AddMissionEditorWarning(warningMessage)
|
|||||||
table.insert(Spearhead.MissionEditingWarnings, warningMessage or "skip")
|
table.insert(Spearhead.MissionEditingWarnings, warningMessage or "skip")
|
||||||
end
|
end
|
||||||
|
|
||||||
missionCommands.addSubMenu("Missions")
|
|
||||||
|
|
||||||
local loadDone = false
|
local loadDone = false
|
||||||
Spearhead.LoadingDone = function()
|
Spearhead.LoadingDone = function()
|
||||||
if loadDone == true then
|
if loadDone == true then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local warningLogger = Spearhead.LoggerTemplate:new("MISSIONPARSER", Spearhead.LoggerTemplate.LogLevelOptions.INFO, 4)
|
local warningLogger = Spearhead.LoggerTemplate:new("MISSIONPARSER", "INFO")
|
||||||
if Spearhead.Util.tableLength(Spearhead.MissionEditingWarnings) > 0 then
|
if Spearhead.Util.tableLength(Spearhead.MissionEditingWarnings) > 0 then
|
||||||
for key, message in pairs(Spearhead.MissionEditingWarnings) do
|
for key, message in pairs(Spearhead.MissionEditingWarnings) do
|
||||||
warningLogger:warn(message)
|
warningLogger:warn(message)
|
||||||
|
|||||||
+381
-377
@@ -1,235 +1,265 @@
|
|||||||
-- 3
|
|
||||||
|
|
||||||
local SpearheadDB = {}
|
---@class DatabaseTables
|
||||||
do -- DB
|
---@field AllZoneNames Array<string> All Zone Names
|
||||||
local singleton = nil
|
---@field StageZoneNames Array<string> All Stage Zone Names
|
||||||
|
---@field StageZones table<string, StageZoneData> table<StageZoneName, StageZoneData>
|
||||||
|
---@field MissionZones Array<string> All Mission Zone Names
|
||||||
|
---@field RandomMissionZones Array<string> All Random mission names
|
||||||
|
---@field StageZonesByNumber table<string, Array<string>> Stage zones grouped by index number
|
||||||
|
---@field AllFarpZones Array<string>
|
||||||
|
---@field FarpIdsInFarpZones table<string, Array<integer>> farp pad Id's in farp zones.
|
||||||
|
---@field CapRoutes Array<string> All Cap route zone names
|
||||||
|
---@field CapDataPerStageNumber table<integer, CapData> table<StageNumber, table>
|
||||||
|
---@field CarrierRouteZones Array<string> All Carrier routes zones
|
||||||
|
---@field BlueSams Array<string> All blue sam zones
|
||||||
|
---@field DescriptionsByMission table<string,string> table<ZoneName, Description>
|
||||||
|
---@field AirbaseDataPerAirfield table<string, AirbaseData>
|
||||||
|
---@field BlueSamDataPerZone table<string, BlueSamData>
|
||||||
|
---@field MissionZoneData table<string, MissionZoneData>
|
||||||
|
---@field FarpZoneData table<string,FarpZoneData>
|
||||||
|
|
||||||
|
---@class CapData
|
||||||
|
---@field routes Array<CapRoute>
|
||||||
|
---@field current integer
|
||||||
|
|
||||||
|
---@class CapRoute
|
||||||
|
---@field point1 Vec3
|
||||||
|
---@field point2 Vec3?
|
||||||
|
|
||||||
|
---@class StageZoneData
|
||||||
|
---@field StageZoneName string
|
||||||
|
---@field AirbaseNames Array<string>
|
||||||
|
---@field FarpZones Array<string>
|
||||||
|
---@field MissionZones Array<string>
|
||||||
|
---@field RandomMissionZones Array<string>
|
||||||
|
---@field StageIndex string
|
||||||
|
---@field BlueSamZones Array<string>
|
||||||
|
---@field MiscGroups Array<string>
|
||||||
|
|
||||||
|
---@class AirbaseData
|
||||||
|
---@field CapGroups Array<string>
|
||||||
|
---@field RedGroups Array<string>
|
||||||
|
---@field BlueGroups Array<string>
|
||||||
|
|
||||||
|
---@class BlueSamData
|
||||||
|
---@field Groups Array<string>
|
||||||
|
|
||||||
|
---@class MissionZoneData
|
||||||
|
---@field Groups Array<string>
|
||||||
|
|
||||||
|
---@class MissionData
|
||||||
|
---@field Groups Array<string>
|
||||||
|
|
||||||
|
---@class FarpZoneData
|
||||||
|
---@field Groups Array<string>
|
||||||
|
|
||||||
|
---@class Database
|
||||||
|
---@field private _tables DatabaseTables
|
||||||
|
---@field private _logger Logger
|
||||||
|
local Database = {}
|
||||||
|
|
||||||
---comment
|
---comment
|
||||||
---@param Logger table
|
---@param Logger table
|
||||||
---@return table
|
---@return Database
|
||||||
function SpearheadDB:new(Logger, debug)
|
function Database.New(Logger, debug)
|
||||||
if not debug then debug = false end
|
|
||||||
if singleton ~= nil then
|
|
||||||
Logger:info("Returning an already initiated instance of SpearheadDB")
|
|
||||||
return singleton
|
|
||||||
end
|
|
||||||
|
|
||||||
local o = {}
|
---@type DatabaseTables
|
||||||
setmetatable(o, { __index = self })
|
local tables = {
|
||||||
|
AllZoneNames = {},
|
||||||
|
BlueSams = {},
|
||||||
|
CapRoutes = {},
|
||||||
|
CapDataPerStageNumber = {},
|
||||||
|
CarrierRouteZones = {},
|
||||||
|
DescriptionsByMission = {},
|
||||||
|
FarpIdsInFarpZones = {},
|
||||||
|
MissionZones = {},
|
||||||
|
StageZoneNames = {},
|
||||||
|
RandomMissionZones = {},
|
||||||
|
StageZones = {},
|
||||||
|
StageZonesByNumber = {},
|
||||||
|
AllFarpZones = {},
|
||||||
|
AirbaseDataPerAirfield = {},
|
||||||
|
BlueSamDataPerZone = {},
|
||||||
|
MissionZoneData = {},
|
||||||
|
FarpZoneData = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Database.__index = Database
|
||||||
|
local self = setmetatable({}, Database)
|
||||||
|
|
||||||
|
self._logger = Logger
|
||||||
|
self._tables = tables
|
||||||
|
|
||||||
o.Logger = Logger
|
|
||||||
o.tables = {}
|
|
||||||
do --INIT ALL TABLES
|
|
||||||
Logger:debug("Initiating tables")
|
Logger:debug("Initiating tables")
|
||||||
|
|
||||||
o.tables.all_zones = {}
|
|
||||||
o.tables.stage_zones = {}
|
|
||||||
o.tables.mission_zones = {}
|
|
||||||
o.tables.random_mission_zones = {}
|
|
||||||
o.tables.farp_zones = {}
|
|
||||||
o.tables.cap_route_zones = {}
|
|
||||||
o.tables.carrier_route_zones = {}
|
|
||||||
o.tables.blue_sams = {}
|
|
||||||
|
|
||||||
o.tables.stage_zonesByNumer = {}
|
|
||||||
o.tables.stage_numberPerzone = {}
|
|
||||||
|
|
||||||
do -- INIT ZONE TABLES
|
do -- INIT ZONE TABLES
|
||||||
for zone_ind, zone_data in pairs(Spearhead.DcsUtil.__trigger_zones) do
|
for zone_ind, zone_data in pairs(Spearhead.DcsUtil.__trigger_zones) do
|
||||||
local zone_name = zone_data.name
|
local zone_name = zone_data.name
|
||||||
local split_string = Spearhead.Util.split_string(zone_name, "_")
|
local split_string = Spearhead.Util.split_string(zone_name, "_")
|
||||||
table.insert(o.tables.all_zones, zone_name)
|
table.insert(self._tables.AllZoneNames, zone_name)
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "missionstage" then
|
if string.lower(split_string[1]) == "missionstage" then
|
||||||
table.insert(o.tables.stage_zones, zone_name)
|
table.insert(self._tables.StageZoneNames, zone_name)
|
||||||
if split_string[2] then
|
if split_string[2] then
|
||||||
local stringified = tostring(split_string[2]) or "unknown"
|
local stringified = tostring(split_string[2]) or "unknown"
|
||||||
if o.tables.stage_zonesByNumer[stringified] == nil then
|
if self._tables.StageZonesByNumber[stringified] == nil then
|
||||||
o.tables.stage_zonesByNumer[stringified] = {}
|
self._tables.StageZonesByNumber[stringified] = {}
|
||||||
end
|
end
|
||||||
table.insert(o.tables.stage_zonesByNumer[stringified], zone_name)
|
table.insert(self._tables.StageZonesByNumber[stringified], zone_name)
|
||||||
o.tables.stage_numberPerzone[zone_name] = stringified
|
|
||||||
|
---@type StageZoneData
|
||||||
|
local stageData = {
|
||||||
|
StageZoneName = zone_name,
|
||||||
|
StageIndex = stringified,
|
||||||
|
AirbaseNames = {},
|
||||||
|
BlueSamZones = {},
|
||||||
|
FarpZones = {},
|
||||||
|
MissionZones = {},
|
||||||
|
RandomMissionZones = {},
|
||||||
|
MiscGroups = {}
|
||||||
|
}
|
||||||
|
self._tables.StageZones[zone_name] = zone_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if string.lower(split_string[1]) == "waitingstage" then
|
||||||
|
table.insert(self._tables.StageZoneNames, zone_name)
|
||||||
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "mission" then
|
if string.lower(split_string[1]) == "mission" then
|
||||||
table.insert(o.tables.mission_zones, zone_name)
|
table.insert(self._tables.MissionZones, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "randommission" then
|
if string.lower(split_string[1]) == "randommission" then
|
||||||
table.insert(o.tables.random_mission_zones, zone_name)
|
table.insert(self._tables.RandomMissionZones, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "farp" then
|
if string.lower(split_string[1]) == "farp" then
|
||||||
table.insert(o.tables.farp_zones, zone_name)
|
table.insert(self._tables.AllFarpZones, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "caproute" then
|
if string.lower(split_string[1]) == "caproute" then
|
||||||
table.insert(o.tables.cap_route_zones, zone_name)
|
table.insert(self._tables.CapRoutes, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "carrierroute" then
|
if string.lower(split_string[1]) == "carrierroute" then
|
||||||
table.insert(o.tables.carrier_route_zones, zone_name)
|
table.insert(self._tables.CarrierRouteZones, zone_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.lower(split_string[1]) == "bluesam" then
|
if string.lower(split_string[1]) == "bluesam" then
|
||||||
table.insert(o.tables.blue_sams, zone_name)
|
table.insert(self._tables.BlueSams, zone_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Logger:debug("initiated zone tables, continuing with descriptions")
|
Logger:debug("initiated zone tables, continuing with descriptions")
|
||||||
o.tables.descriptions = {}
|
|
||||||
do --load markers
|
do --load markers
|
||||||
if env.mission.drawings and env.mission.drawings.layers then
|
if env.mission.drawings and env.mission.drawings.layers then
|
||||||
for i, layer in pairs(env.mission.drawings.layers) do
|
for i, layer in pairs(env.mission.drawings.layers) do
|
||||||
if string.lower(layer.name) == "author" then
|
if string.lower(layer.name) == "author" then
|
||||||
for key, layer_object in pairs(layer.objects) do
|
for key, layer_object in pairs(layer.objects) do
|
||||||
local inZone = Spearhead.DcsUtil.isPositionInZones(layer_object.mapX, layer_object.mapY,
|
|
||||||
o.tables.mission_zones)
|
|
||||||
if Spearhead.Util.tableLength(inZone) >= 1 then
|
|
||||||
local name = inZone[1]
|
|
||||||
if name ~= nil then
|
|
||||||
o.tables.descriptions[name] = layer_object.text
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local inZone = Spearhead.DcsUtil.isPositionInZones(layer_object.mapX, layer_object.mapY,
|
if Spearhead.Util.startswith(layer_object.name, "stagebriefing", true) then
|
||||||
o.tables.random_mission_zones)
|
--[[
|
||||||
if Spearhead.Util.tableLength(inZone) >= 1 then
|
TODO: Stage Briefings
|
||||||
local name = inZone[1]
|
]]
|
||||||
if name ~= nil then
|
|
||||||
o.tables.descriptions[name] = layer_object.text
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.tables.blueSamZonesPerStage = {}
|
|
||||||
for _, stageZoneName in pairs(o.tables.stage_zones) do
|
|
||||||
|
|
||||||
if o.tables.blueSamZonesPerStage[stageZoneName] == nil then
|
|
||||||
o.tables.blueSamZonesPerStage[stageZoneName] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, blueSamStageName in pairs(o.tables.blue_sams) do
|
|
||||||
|
|
||||||
if Spearhead.DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then
|
|
||||||
table.insert(o.tables.blueSamZonesPerStage[stageZoneName], blueSamStageName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.tables.missionZonesPerStage = {}
|
|
||||||
for key, missionZone in pairs(o.tables.mission_zones) do
|
|
||||||
local found = false
|
|
||||||
local i = 1
|
|
||||||
while found == false and i <= Spearhead.Util.tableLength(o.tables.stage_zones) do
|
|
||||||
local stageZone = o.tables.stage_zones[i]
|
|
||||||
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZone) == true then
|
|
||||||
if o.tables.missionZonesPerStage[stageZone] == nil then
|
|
||||||
o.tables.missionZonesPerStage[stageZone] = {}
|
|
||||||
end
|
|
||||||
table.insert(o.tables.missionZonesPerStage[stageZone], missionZone)
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.tables.randomMissionZonesPerStage = {}
|
|
||||||
for key, missionZone in pairs(o.tables.random_mission_zones) do
|
|
||||||
local found = false
|
|
||||||
local i = 1
|
|
||||||
while found == false and i <= Spearhead.Util.tableLength(o.tables.stage_zones) do
|
|
||||||
local stageZone = o.tables.stage_zones[i]
|
|
||||||
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZone) == true then
|
|
||||||
if o.tables.randomMissionZonesPerStage[stageZone] == nil then
|
|
||||||
o.tables.randomMissionZonesPerStage[stageZone] = {}
|
|
||||||
end
|
|
||||||
table.insert(o.tables.randomMissionZonesPerStage[stageZone], missionZone)
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local isAirbaseInZone = {}
|
|
||||||
o.tables.airbasesPerStage = {}
|
|
||||||
o.tables.farpIdsInFarpZones = {}
|
|
||||||
local airbases = world.getAirbases()
|
|
||||||
for _, airbase in pairs(airbases) do
|
|
||||||
local baseId = airbase:getID()
|
|
||||||
local point = airbase:getPoint()
|
|
||||||
local found = false
|
|
||||||
for _, zoneName in pairs(o.tables.stage_zones) do
|
|
||||||
if found == false then
|
|
||||||
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, zoneName) == true then
|
|
||||||
found = true
|
|
||||||
local baseIdString = tostring(baseId) or "nil"
|
|
||||||
isAirbaseInZone[baseIdString] = true
|
|
||||||
|
|
||||||
if airbase:getDesc().category == 0 then
|
|
||||||
--Airbase
|
|
||||||
if Spearhead.DcsUtil.getStartingCoalition(baseId) == 2 then
|
|
||||||
airbase:setCoalition(1)
|
|
||||||
airbase:autoCapture(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
if o.tables.airbasesPerStage[zoneName] == nil then
|
|
||||||
o.tables.airbasesPerStage[zoneName] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(o.tables.airbasesPerStage[zoneName], baseId)
|
|
||||||
else
|
else
|
||||||
|
local inZone = Spearhead.DcsUtil.isPositionInZones(layer_object.mapX, layer_object.mapY, self._tables.MissionZones)
|
||||||
|
if Spearhead.Util.tableLength(inZone) >= 1 then
|
||||||
|
local name = inZone[1]
|
||||||
|
if name ~= nil then
|
||||||
|
self._tables.DescriptionsByMission[name] = layer_object.text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local inZone = Spearhead.DcsUtil.isPositionInZones(layer_object.mapX, layer_object.mapY,
|
||||||
|
self._tables.RandomMissionZones)
|
||||||
|
if Spearhead.Util.tableLength(inZone) >= 1 then
|
||||||
|
local name = inZone[1]
|
||||||
|
if name ~= nil then
|
||||||
|
self._tables.DescriptionsByMission[name] = layer_object.text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, stageZoneName in pairs(self._tables.StageZoneNames) do
|
||||||
|
local stageData = self._tables.StageZones[stageZoneName]
|
||||||
|
if stageData then
|
||||||
|
|
||||||
|
-- fill blue sams
|
||||||
|
for _, blueSamStageName in pairs(self._tables.BlueSams) do
|
||||||
|
if Spearhead.DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then
|
||||||
|
table.insert(stageData.BlueSamZones, blueSamStageName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fill missions
|
||||||
|
for key, missionZone in pairs(self._tables.MissionZones) do
|
||||||
|
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
|
||||||
|
table.insert(stageData.MissionZones, missionZone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fill random missions
|
||||||
|
for key, missionZone in pairs(self._tables.RandomMissionZones) do
|
||||||
|
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
|
||||||
|
table.insert(stageData.RandomMissionZones, missionZone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fill airbases
|
||||||
|
for _, airbase in pairs(world.getAirbases()) do
|
||||||
|
local point = airbase:getPoint()
|
||||||
|
|
||||||
|
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then
|
||||||
|
if airbase:getDesc().category == 0 then
|
||||||
|
table.insert(stageData.AirbaseNames, airbase:getName())
|
||||||
|
elseif airbase:getDesc().category == 1 then
|
||||||
-- farp
|
-- farp
|
||||||
local i = 1
|
--[[
|
||||||
local farpFound = false
|
TODO: FARP ZONES FILL
|
||||||
while farpFound == false and i <= Spearhead.Util.tableLength(o.tables.farp_zones) do
|
]]
|
||||||
local farpZoneName = o.tables.farp_zones[i]
|
|
||||||
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, farpZoneName) == true then
|
|
||||||
farpFound = true
|
|
||||||
|
|
||||||
if o.tables.farpIdsInFarpZones[farpZoneName] == nil then
|
|
||||||
o.tables.farpIdsInFarpZones[farpZoneName] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
airbase:setCoalition(1)
|
|
||||||
airbase:autoCapture(false)
|
|
||||||
table.insert(o.tables.farpIdsInFarpZones[farpZoneName], baseIdString)
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- fill farp zones
|
||||||
|
for _, farpZoneName in pairs(self._tables.AllFarpZones) do
|
||||||
o.tables.farpZonesPerStage = {}
|
if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then
|
||||||
for _, farpZoneName in pairs(o.tables.farp_zones) do
|
table.insert(stageData.FarpZones, farpZoneName)
|
||||||
local findFirst = function(farpZoneName)
|
|
||||||
for _, stage_zone in pairs(o.tables.stage_zones) do
|
|
||||||
if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stage_zone) then
|
|
||||||
return stage_zone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local found = findFirst(farpZoneName)
|
|
||||||
if found then
|
|
||||||
if o.tables.farpZonesPerStage[found] == nil then
|
|
||||||
o.tables.farpZonesPerStage[found] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(o.tables.farpZonesPerStage[found], farpZoneName)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Logger:info("initiated the database with amount of zones: ")
|
||||||
|
Logger:info("Stages: " .. Spearhead.Util.tableLength(self._tables.StageZones))
|
||||||
|
Logger:info("Total Missions: " .. Spearhead.Util.tableLength(self._tables.MissionZoneData))
|
||||||
|
Logger:info("Random Missions: " .. Spearhead.Util.tableLength(self._tables.RandomMissionZones))
|
||||||
|
Logger:info("Farps: " .. Spearhead.Util.tableLength(self._tables.AllFarpZones))
|
||||||
|
Logger:info("Airbases: " .. Spearhead.Util.tableLength(self._tables.AirbaseDataPerAirfield))
|
||||||
|
|
||||||
|
for _, missionZone in pairs(self._tables.MissionZones) do
|
||||||
|
if self._tables.DescriptionsByMission[missionZone] == nil then
|
||||||
|
Spearhead.AddMissionEditorWarning("Mission with zonename: " ..
|
||||||
|
missionZone .. " does not have a briefing")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, missionZone in pairs(self._tables.RandomMissionZones) do
|
||||||
|
if self._tables.DescriptionsByMission[missionZone] == nil then
|
||||||
|
Spearhead.AddMissionEditorWarning("Mission with zonename: " ..
|
||||||
|
missionZone .. " does not have a briefing")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local is_group_taken = {}
|
local is_group_taken = {}
|
||||||
do
|
do
|
||||||
@@ -259,131 +289,129 @@ do -- DB
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
--- airbaseId <> groupname[]
|
---@private
|
||||||
o.tables.capGroupsOnAirbase = {}
|
---@param baseName string
|
||||||
local loadCapUnits = function()
|
---@return AirbaseData
|
||||||
|
function Database:getOrCreateAirbaseData(baseName)
|
||||||
|
local baseData = self._tables.AirbaseDataPerAirfield[baseName]
|
||||||
|
if baseData == nil then
|
||||||
|
baseData = {
|
||||||
|
CapGroups = {},
|
||||||
|
RedGroups = {},
|
||||||
|
BlueGroups = {}
|
||||||
|
}
|
||||||
|
self._tables.AirbaseDataPerAirfield[baseName] = baseData
|
||||||
|
end
|
||||||
|
return baseData
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function Database:loadCapUnits()
|
||||||
local all_groups = getAvailableCAPGroups()
|
local all_groups = getAvailableCAPGroups()
|
||||||
local airbases = world.getAirbases()
|
local airbases = world.getAirbases()
|
||||||
for _, airbase in pairs(airbases) do
|
for _, airbase in pairs(airbases) do
|
||||||
local baseId = airbase:getID()
|
local baseId = airbase:getID()
|
||||||
local point = airbase:getPoint()
|
local point = airbase:getPoint()
|
||||||
local zone = Spearhead.DcsUtil.getAirbaseZoneById(baseId) or
|
local zone = Spearhead.DcsUtil.getAirbaseZoneById(baseId) or { x = point.x, z = point.z, radius = 4000 }
|
||||||
{ x = point.x, z = point.z, radius = 4000 }
|
|
||||||
o.tables.capGroupsOnAirbase[baseId] = {}
|
local baseData = self:getOrCreateAirbaseData(airbase:getName())
|
||||||
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(o.tables.capGroupsOnAirbase[baseId], groupName)
|
table.insert(baseData.CapGroups, groupName)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self._tables.AirbaseDataPerAirfield[airbase:getName()] = baseData
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
o.tables.samUnitsPerSamZone = {}
|
function Database:loadBlueSamUnits()
|
||||||
local loadBlueSamUnits = function()
|
local all_groups = Spearhead.DcsUtil.getAllGroupNames()
|
||||||
local all_groups = getAvailableGroups()
|
for _, blueSamZone in pairs(self._tables.BlueSams) do
|
||||||
for _, blueSamZone in pairs(o.tables.blue_sams) do
|
self._tables.BlueSamDataPerZone[blueSamZone] = {
|
||||||
o.tables.samUnitsPerSamZone[blueSamZone] = {}
|
Groups = {}
|
||||||
|
}
|
||||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, blueSamZone)
|
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, blueSamZone)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(o.tables.samUnitsPerSamZone[blueSamZone], groupName)
|
table.insert(self._tables.BlueSamDataPerZone[blueSamZone].Groups, groupName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
--- missionZoneName <> groupname[]
|
function Database:loadMissionzoneUnits()
|
||||||
o.tables.groupsInMissionZone = {}
|
|
||||||
local loadMissionzoneUnits = function()
|
|
||||||
local all_groups = getAvailableGroups()
|
local all_groups = getAvailableGroups()
|
||||||
for _, missionZoneName in pairs(o.tables.mission_zones) do
|
for _, missionZoneName in pairs(self._tables.MissionZones) do
|
||||||
o.tables.groupsInMissionZone[missionZoneName] = {}
|
self._tables.MissionZoneData[missionZoneName] = {
|
||||||
|
Groups = {}
|
||||||
|
}
|
||||||
|
|
||||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName)
|
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(o.tables.groupsInMissionZone[missionZoneName], groupName)
|
table.insert(self._tables.MissionZoneData[missionZoneName].Groups, groupName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- missionZoneName <> groupname[]
|
---@private
|
||||||
o.tables.groupsInRandomMissions = {}
|
function Database:loadRandomMissionzoneUnits()
|
||||||
local loadRandomMissionzoneUnits = function()
|
|
||||||
local all_groups = getAvailableGroups()
|
local all_groups = getAvailableGroups()
|
||||||
for _, missionZoneName in pairs(o.tables.random_mission_zones) do
|
for _, missionZoneName in pairs(self._tables.RandomMissionZones) do
|
||||||
o.tables.groupsInRandomMissions[missionZoneName] = {}
|
self._tables.MissionZoneData[missionZoneName] = {
|
||||||
|
Groups = {}
|
||||||
|
}
|
||||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName)
|
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(o.tables.groupsInRandomMissions[missionZoneName], groupName)
|
table.insert(self._tables.MissionZoneData[missionZoneName].Groups, groupName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- farpZoneName <> groupname[]
|
---@private
|
||||||
o.tables.groupsInFarpZone = {}
|
function Database:loadFarpGroups()
|
||||||
local loadFarpGroups = function()
|
|
||||||
local all_groups = getAvailableGroups()
|
local all_groups = getAvailableGroups()
|
||||||
for _, farpZone in pairs(o.tables.farp_zones) do
|
for _, farpZone in pairs(self._tables.AllFarpZones) do
|
||||||
o.tables.groupsInFarpZone[farpZone] = {}
|
self._tables.FarpZoneData[farpZone] = {
|
||||||
|
Groups = {}
|
||||||
|
}
|
||||||
|
|
||||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone)
|
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(o.tables.groupsInFarpZone[farpZone], groupName)
|
table.insert(self._tables.FarpZoneData[farpZone].Groups, groupName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- farpZoneName <> groupname[]
|
function Database:loadAirbaseGroups()
|
||||||
o.tables.redAirbaseGroupsPerAirbase = {}
|
|
||||||
o.tables.blueAirbaseGroupsPerAirbase = {}
|
|
||||||
local loadAirbaseGroups = function()
|
|
||||||
local all_groups = getAvailableGroups()
|
local all_groups = getAvailableGroups()
|
||||||
local airbases = world.getAirbases()
|
for _, stageZone in pairs(self._tables.StageZones) do
|
||||||
for _, airbase in pairs(airbases) do
|
|
||||||
local baseId = tostring(airbase:getID())
|
|
||||||
local point = airbase:getPoint()
|
|
||||||
local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneById(baseId) or
|
|
||||||
{ x = point.x, z = point.z, radius = 4000 }
|
|
||||||
|
|
||||||
if isAirbaseInZone[tostring(baseId) or "something"] == true and airbaseZone and airbase:getDesc().category == Airbase.Category.AIRDROME then
|
for _, baseName in pairs(stageZone.AirbaseNames) do
|
||||||
if debug then
|
local base = Airbase.getByName(baseName)
|
||||||
if airbaseZone.zone_type == Spearhead.DcsUtil.ZoneType.Polygon then
|
|
||||||
local functionString = "trigger.action.markupToAll(7, -1, " .. baseId + 300 .. ","
|
|
||||||
for _, vecpoint in pairs(airbaseZone.verts) do
|
|
||||||
functionString = functionString .. " { x=" .. vecpoint.x .. ", y=0,z=" .. vecpoint.z ..
|
|
||||||
"},"
|
|
||||||
end
|
|
||||||
functionString = functionString .. "{0,1,0,1}, {0,0,0,0}, 1)"
|
|
||||||
|
|
||||||
env.info(functionString)
|
if base then
|
||||||
---@diagnostic disable-next-line: deprecated
|
local basedata = self:getOrCreateAirbaseData(baseName)
|
||||||
local f, err = loadstring(functionString)
|
local baseId = base:getID()
|
||||||
if f then
|
local point = base:getPoint()
|
||||||
f()
|
local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneById(baseId) or { x = point.x, z = point.z, radius = 4000 }
|
||||||
else
|
|
||||||
env.info(err)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
trigger.action.circleToAll(-1, baseId, { x = point.x, y = 0, z = point.z }, 2048,
|
|
||||||
{ 1, 0, 0, 1 }, { 0, 0, 0, 0 }, 1, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
if airbaseZone and base:getDesc().category == Airbase.Category.AIRDROME then
|
||||||
o.tables.redAirbaseGroupsPerAirbase[baseId] = {}
|
|
||||||
o.tables.blueAirbaseGroupsPerAirbase[baseId] = {}
|
|
||||||
local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone)
|
local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
if Spearhead.DcsUtil.IsGroupStatic(groupName) == true then
|
if Spearhead.DcsUtil.IsGroupStatic(groupName) == true then
|
||||||
local object = StaticObject.getByName(groupName)
|
local object = StaticObject.getByName(groupName)
|
||||||
if object then
|
if object then
|
||||||
if object:getCoalition() == coalition.side.RED then
|
if object:getCoalition() == coalition.side.RED then
|
||||||
table.insert(o.tables.redAirbaseGroupsPerAirbase[baseId], groupName)
|
table.insert(basedata.RedGroups, groupName)
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
elseif object:getCoalition() == coalition.side.BLUE then
|
elseif object:getCoalition() == coalition.side.BLUE then
|
||||||
table.insert(o.tables.blueAirbaseGroupsPerAirbase[baseId], groupName)
|
table.insert(basedata.BlueGroups, groupName)
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -391,10 +419,10 @@ do -- DB
|
|||||||
local group = Group.getByName(groupName)
|
local group = Group.getByName(groupName)
|
||||||
if group then
|
if group then
|
||||||
if group:getCoalition() == coalition.side.RED then
|
if group:getCoalition() == coalition.side.RED then
|
||||||
table.insert(o.tables.redAirbaseGroupsPerAirbase[baseId], groupName)
|
table.insert(basedata.RedGroups, groupName)
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
elseif group:getCoalition() == coalition.side.BLUE then
|
elseif group:getCoalition() == coalition.side.BLUE then
|
||||||
table.insert(o.tables.blueAirbaseGroupsPerAirbase[baseId], groupName)
|
table.insert(basedata.BlueGroups, groupName)
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -403,71 +431,65 @@ do -- DB
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
o.tables.miscGroupsInStages = {}
|
---@private
|
||||||
local loadMiscGroupsInStages = function()
|
function Database:loadMiscGroupsInStages()
|
||||||
local all_groups = getAvailableGroups()
|
local all_groups = getAvailableGroups()
|
||||||
for _, stage_zone in pairs(o.tables.stage_zones) do
|
for _, stageZone in pairs(self._tables.StageZones) do
|
||||||
o.tables.miscGroupsInStages[stage_zone] = {}
|
stageZone.MiscGroups = {}
|
||||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, stage_zone)
|
|
||||||
|
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, stageZone.StageZoneName)
|
||||||
for _, groupName in pairs(groups) do
|
for _, groupName in pairs(groups) do
|
||||||
if Spearhead.DcsUtil.IsGroupStatic(groupName) == true then
|
if Spearhead.DcsUtil.IsGroupStatic(groupName) == true then
|
||||||
local object = StaticObject.getByName(groupName)
|
local object = StaticObject.getByName(groupName)
|
||||||
if object and object:getCoalition() ~= coalition.side.NEUTRAL then
|
if object and object:getCoalition() ~= coalition.side.NEUTRAL then
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(o.tables.miscGroupsInStages[stage_zone], groupName)
|
table.insert(stageZone.MiscGroups, groupName)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local group = Group.getByName(groupName)
|
local group = Group.getByName(groupName)
|
||||||
if group and group:getCoalition() ~= coalition.side.NEUTRAL then
|
if group and group:getCoalition() ~= coalition.side.NEUTRAL then
|
||||||
is_group_taken[groupName] = true
|
is_group_taken[groupName] = true
|
||||||
table.insert(o.tables.miscGroupsInStages[stage_zone], groupName)
|
table.insert(stageZone.MiscGroups, groupName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
loadCapUnits()
|
self:loadCapUnits()
|
||||||
loadBlueSamUnits()
|
self:loadBlueSamUnits()
|
||||||
loadMissionzoneUnits()
|
self:loadMissionzoneUnits()
|
||||||
loadRandomMissionzoneUnits()
|
self:loadRandomMissionzoneUnits()
|
||||||
loadFarpGroups()
|
self:loadFarpGroups()
|
||||||
loadAirbaseGroups()
|
self:loadAirbaseGroups()
|
||||||
loadMiscGroupsInStages()
|
self:loadMiscGroupsInStages()
|
||||||
|
|
||||||
-- local cleanup = function () --CLean up all groups that are now managed inside zones by spearhead
|
|
||||||
|
|
||||||
-- local count = 0
|
for _, zoneData in pairs(self._tables.StageZones) do
|
||||||
-- for name, taken in pairs(is_group_taken) do
|
|
||||||
-- if taken == true then
|
|
||||||
-- Spearhead.DcsUtil.DestroyGroup(name)
|
|
||||||
-- count = count + 1
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- Logger:info("Destroyed " .. count .. " units that are now managed in zones by Spearhead")
|
|
||||||
-- end
|
|
||||||
-- cleanup()
|
|
||||||
|
|
||||||
--- key: zoneName value: { current, routes = [ { point1, point2 } ] }
|
local number = zoneData.StageIndex
|
||||||
o.tables.capRoutesPerStageNumber = {}
|
|
||||||
for _, zoneName in pairs(o.tables.stage_zones) do
|
|
||||||
local number = tostring(o.tables.stage_numberPerzone[zoneName] or "unknown")
|
|
||||||
|
|
||||||
if o.tables.capRoutesPerStageNumber[number] == nil then
|
for _, cap_route_zone in pairs(self._tables.CapRoutes) do
|
||||||
o.tables.capRoutesPerStageNumber[number] = {
|
if Spearhead.DcsUtil.isZoneInZone(cap_route_zone, zoneData.StageZoneName) == true then
|
||||||
current = 0,
|
|
||||||
routes = {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, cap_route_zone in pairs(o.tables.cap_route_zones) do
|
|
||||||
if Spearhead.DcsUtil.isZoneInZone(cap_route_zone, zoneName) == true then
|
|
||||||
local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone)
|
local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone)
|
||||||
if zone then
|
if zone then
|
||||||
|
|
||||||
|
---@type CapData
|
||||||
|
local capData = self._tables.CapDataPerStageNumber[number]
|
||||||
|
if capData == nil then
|
||||||
|
capData = {
|
||||||
|
routes = {},
|
||||||
|
current = 0
|
||||||
|
}
|
||||||
|
self._tables.CapDataPerStageNumber[number] = capData
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if zone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then
|
if zone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then
|
||||||
table.insert(o.tables.capRoutesPerStageNumber[number].routes,
|
table.insert(capData.routes, { point1 = { x = zone.x, z = zone.z }, point2 = nil })
|
||||||
{ point1 = { x = zone.x, z = zone.z }, point2 = nil })
|
|
||||||
else
|
else
|
||||||
local function getDist(a, b)
|
local function getDist(a, b)
|
||||||
return math.sqrt((b.x - a.x) ^ 2 + (b.z - a.z) ^ 2)
|
return math.sqrt((b.x - a.x) ^ 2 + (b.z - a.z) ^ 2)
|
||||||
@@ -492,7 +514,7 @@ do -- DB
|
|||||||
end
|
end
|
||||||
|
|
||||||
if biggestA and biggestB then
|
if biggestA and biggestB then
|
||||||
table.insert(o.tables.capRoutesPerStageNumber[number].routes,
|
table.insert(capData.routes,
|
||||||
{
|
{
|
||||||
point1 = { x = biggestA.x, z = biggestA.z },
|
point1 = { x = biggestA.x, z = biggestA.z },
|
||||||
point2 = { x = biggestB.x, z = biggestB.z }
|
point2 = { x = biggestB.x, z = biggestB.z }
|
||||||
@@ -504,18 +526,18 @@ do -- DB
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
o.Logger:debug(o.tables.capRoutesPerStageNumber)
|
return self
|
||||||
|
|
||||||
o.tables.missionCodes = {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
o.GetDescriptionForMission = function(self, missionZoneName)
|
|
||||||
return self.tables.descriptions[missionZoneName]
|
function Database:GetDescriptionForMission(missionZoneName)
|
||||||
|
return self._tables.DescriptionsByMission[missionZoneName]
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getCapRouteInZone = function(self, stageNumber, baseId)
|
function Database:getCapRouteInZone(stageNumber, baseId)
|
||||||
local stageNumber = tostring(stageNumber) or "nothing"
|
local stageNumber = tostring(stageNumber) or "nothing"
|
||||||
local routeData = self.tables.capRoutesPerStageNumber[stageNumber]
|
local routeData = self._tables.CapDataPerStageNumber[stageNumber]
|
||||||
if routeData then
|
if routeData then
|
||||||
local count = Spearhead.Util.tableLength(routeData.routes)
|
local count = Spearhead.Util.tableLength(routeData.routes)
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
@@ -535,7 +557,7 @@ do -- DB
|
|||||||
local aY = pC.z + vY / magV * radius;
|
local aY = pC.z + vY / magV * radius;
|
||||||
return { x = aX, z = aY }
|
return { x = aX, z = aY }
|
||||||
end
|
end
|
||||||
local stageZoneName = Spearhead.Util.randomFromList(self.tables.stage_zonesByNumer[stageNumber]) or
|
local stageZoneName = Spearhead.Util.randomFromList(self._tables.StageZonesByNumber[stageNumber]) or
|
||||||
"none"
|
"none"
|
||||||
local stagezone = Spearhead.DcsUtil.getZoneByName(stageZoneName)
|
local stagezone = Spearhead.DcsUtil.getZoneByName(stageZoneName)
|
||||||
if stagezone then
|
if stagezone then
|
||||||
@@ -569,92 +591,101 @@ do -- DB
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
---comment
|
|
||||||
---@param self table
|
|
||||||
---@param number number
|
|
||||||
---@return string zoneName
|
|
||||||
o.getStageZonesByStageNumber = function(self, number)
|
|
||||||
local numberString = tostring(number)
|
|
||||||
return self.tables.stage_zonesByNumer[numberString]
|
|
||||||
end
|
|
||||||
|
|
||||||
---comment
|
|
||||||
---@param self table
|
|
||||||
---@return table result a list of stage zone names
|
---@return table result a list of stage zone names
|
||||||
o.getStagezoneNames = function(self)
|
function Database:getStagezoneNames()
|
||||||
return self.tables.stage_zones
|
return self._tables.AllZoneNames
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getCarrierRouteZones = function(self)
|
function Database:getCarrierRouteZones()
|
||||||
return self.tables.carrier_route_zones
|
return self._tables.CarrierRouteZones
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getMissionsForStage = function(self, stagename)
|
---@return Array<string>
|
||||||
return self.tables.missionZonesPerStage[stagename] or {}
|
function Database:getMissionsForStage(stagename)
|
||||||
|
local stageZone = self._tables.StageZones[stagename]
|
||||||
|
if not stageZone then return {} end
|
||||||
|
return stageZone.MissionZones
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getRandomMissionsForStage = function(self, stagename)
|
---@return Array<string>
|
||||||
return self.tables.randomMissionZonesPerStage[stagename] or {}
|
function Database:getRandomMissionsForStage(stagename)
|
||||||
|
local stageZone = self._tables.StageZones[stagename]
|
||||||
|
if not stageZone then return {} end
|
||||||
|
return stageZone.RandomMissionZones
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getGroupsForMissionZone = function(self, missionZoneName)
|
---@return Array<string>?
|
||||||
if Spearhead.Util.startswith(missionZoneName, "RANDOMMISSION") == true then
|
function Database:getGroupsForMissionZone(missionZoneName)
|
||||||
return self.tables.groupsInRandomMissions[missionZoneName] or {}
|
local missionZoneData = self._tables.MissionZoneData[missionZoneName]
|
||||||
end
|
if not missionZoneData then return nil end
|
||||||
return self.tables.groupsInMissionZone[missionZoneName] or {}
|
return missionZoneData.Groups
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getMissionBriefingForMissionZone = function(self, missionZoneName)
|
---@return string?
|
||||||
return self.tables.descriptions[missionZoneName] or ""
|
function Database:getMissionBriefingForMissionZone(missionZoneName)
|
||||||
|
return self._tables.DescriptionsByMission[missionZoneName]
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self table
|
---@param self table
|
||||||
---@param stageName string
|
---@param stageName string
|
||||||
---@return table result airbase IDs. Use Spearhead.DcsUtil.getAirbaseById
|
---@return table result airbase Names
|
||||||
o.getAirbaseIdsInStage = function(self, stageName)
|
function Database:getAirbaseIdsInStage(stageName)
|
||||||
return self.tables.airbasesPerStage[stageName] or {}
|
return self.tables.airbasesPerStage[stageName] or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getFarpZonesInStage = function(self, stageName)
|
|
||||||
return self.tables.farpZonesPerStage[stageName]
|
---@param airbaseName string
|
||||||
|
---@return Array<string>?
|
||||||
|
function Database:getCapGroupsAtAirbase(airbaseName)
|
||||||
|
local airbaseData = self._tables.AirbaseDataPerAirfield[airbaseName]
|
||||||
|
if not airbaseData then return nil end
|
||||||
|
return airbaseData.CapGroups
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self table
|
|
||||||
---@param airbaseId number
|
|
||||||
---@return table
|
|
||||||
o.getCapGroupsAtAirbase = function(self, airbaseId)
|
|
||||||
return self.tables.capGroupsOnAirbase[airbaseId] or {}
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param self table
|
|
||||||
---@param stageName string
|
---@param stageName string
|
||||||
---@return table
|
---@return Array<string>
|
||||||
o.getBlueSamsInStage = function(self, stageName)
|
function Database:getBlueSamsInStage(stageName)
|
||||||
return self.tables.blueSamZonesPerStage[stageName] or {}
|
local stageData = self._tables.StageZones[stageName]
|
||||||
|
if not stageData then return {} end
|
||||||
|
return stageData.BlueSamZones
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param self table
|
|
||||||
---@param samZone string
|
---@param samZone string
|
||||||
---@return table
|
---@return Array<string>
|
||||||
o.getBlueSamGroupsInZone = function(self, samZone)
|
function Database:getBlueSamGroupsInZone(samZone)
|
||||||
return self.tables.samUnitsPerSamZone[samZone] or {}
|
local blueSamData = self._tables.BlueSamDataPerZone[samZone]
|
||||||
|
if not blueSamData then return {} end
|
||||||
|
return blueSamData.Groups
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getRedGroupsAtAirbase = function(self, airbaseId)
|
---@param airbaseName string
|
||||||
local baseId = tostring(airbaseId)
|
---@return Array<string>
|
||||||
return self.tables.redAirbaseGroupsPerAirbase[baseId] or {}
|
function Database:getRedGroupsAtAirbase(airbaseName)
|
||||||
|
local airbaseData = self._tables.AirbaseDataPerAirfield[airbaseName]
|
||||||
|
if not airbaseData then return {} end
|
||||||
|
return airbaseData.RedGroups
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getBlueGroupsAtAirbase = function(self, airbaseId)
|
---@param airbaseName string
|
||||||
local baseId = tostring(airbaseId)
|
---@return Array<string>
|
||||||
return self.tables.blueAirbaseGroupsPerAirbase[baseId] or {}
|
function Database:getBlueGroupsAtAirbase(airbaseName)
|
||||||
|
local airbaseData = self._tables.AirbaseDataPerAirfield[airbaseName]
|
||||||
|
if not airbaseData then return {} end
|
||||||
|
return airbaseData.BlueGroups
|
||||||
end
|
end
|
||||||
|
|
||||||
o.getMiscGroupsAtStage = function(self, stageName)
|
function Database:getMiscGroupsAtStage(stageName)
|
||||||
return self.tables.miscGroupsInStages[stageName] or {}
|
local stageZone = self._tables.StageZones[stageName]
|
||||||
|
if not stageZone then return {} end
|
||||||
|
return stageZone.MiscGroups
|
||||||
end
|
end
|
||||||
|
|
||||||
o.GetNewMissionCode = function(self)
|
---comment
|
||||||
|
---@param self table
|
||||||
|
---@return integer|nil
|
||||||
|
function Database:GetNewMissionCode(self)
|
||||||
local code = nil
|
local code = nil
|
||||||
local tries = 0
|
local tries = 0
|
||||||
while code == nil and tries < 10 do
|
while code == nil and tries < 10 do
|
||||||
@@ -671,33 +702,6 @@ do -- DB
|
|||||||
]]
|
]]
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- LOG STATE
|
|
||||||
Logger:info("initiated the database with amount of zones: ")
|
|
||||||
Logger:info("Stages: " .. Spearhead.Util.tableLength(o.tables.stage_zones))
|
|
||||||
Logger:info("Missions: " .. Spearhead.Util.tableLength(o.tables.mission_zones))
|
|
||||||
Logger:info("Random Missions: " .. Spearhead.Util.tableLength(o.tables.random_mission_zones))
|
|
||||||
Logger:info("Farps: " .. Spearhead.Util.tableLength(o.tables.farp_zones))
|
|
||||||
Logger:info("Airbases: " .. Spearhead.Util.tableLength(o.tables.airbasesPerStage))
|
|
||||||
Logger:info("RedAirbase Groups: " .. Spearhead.Util.tableLength(o.tables.redAirbaseGroupsPerAirbase["21"]))
|
|
||||||
|
|
||||||
|
|
||||||
for _, missionZone in pairs(o.tables.mission_zones) do
|
Spearhead.DB = Database
|
||||||
if o.tables.descriptions[missionZone] == nil then
|
|
||||||
Spearhead.AddMissionEditorWarning("Mission with zonename: " ..
|
|
||||||
missionZone .. " does not have a briefing")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, randomMission in pairs(o.tables.random_mission_zones) do
|
|
||||||
if o.tables.descriptions[randomMission] == nil then
|
|
||||||
Spearhead.AddMissionEditorWarning("Mission with zonename: " ..
|
|
||||||
randomMission .. " does not have a briefing")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
singleton = o
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Spearhead.DB = SpearheadDB
|
|
||||||
|
|||||||
@@ -1,26 +1,39 @@
|
|||||||
|
|
||||||
local SpearheadEvents = {}
|
local SpearheadEvents = {}
|
||||||
do
|
do
|
||||||
do -- STAGE NUMBER CHANGED
|
|
||||||
local OnStageNumberChangedListeners = {}
|
---@type Logger
|
||||||
local OnStageNumberChangedHandlers = {}
|
local logger = nil
|
||||||
|
|
||||||
|
---@param logLevel LogLevel
|
||||||
|
SpearheadEvents.Init = function(logLevel)
|
||||||
|
logger = Spearhead.LoggerTemplate:new("Events", logLevel)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local warn = function(text)
|
local warn = function(text)
|
||||||
env.warn("[Spearhead][Events] " .. (text or "nil"))
|
if logger then
|
||||||
|
logger:warn(text)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local error = function(text)
|
local logError = function(text)
|
||||||
env.error("[Spearhead][Events] " .. (text or "nil"))
|
if logger then logger:error(text) end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local logDebug = function(text)
|
||||||
|
if logger then logger:debug(text) end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class OnStageChangedListener
|
||||||
|
---@field OnStageNumberChanged fun(self:OnStageChangedListener, number:integer)
|
||||||
|
|
||||||
|
do -- STAGE NUMBER CHANGED
|
||||||
|
local OnStageNumberChangedListeners = {}
|
||||||
|
local OnStageNumberChangedHandlers = {}
|
||||||
---Add a stage zone number changed listener
|
---Add a stage zone number changed listener
|
||||||
---@param listener table object with function OnStageNumberChanged(self, number)
|
---@param listener OnStageChangedListener object with function OnStageNumberChanged(self, number)
|
||||||
SpearheadEvents.AddStageNumberChangedListener = function(listener)
|
SpearheadEvents.AddStageNumberChangedListener = function(listener)
|
||||||
if type(listener) ~= "table" then
|
|
||||||
warn("Event listener not of type table, did you mean to use handler?")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
table.insert(OnStageNumberChangedListeners, listener)
|
table.insert(OnStageNumberChangedListeners, listener)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -36,21 +49,27 @@ do
|
|||||||
|
|
||||||
---@param newStageNumber number
|
---@param newStageNumber number
|
||||||
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber)
|
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber)
|
||||||
|
pcall(function ()
|
||||||
|
Spearhead.classes.persistence.Persistence.SetActiveStage(newStageNumber)
|
||||||
|
end)
|
||||||
|
|
||||||
for _, callable in pairs(OnStageNumberChangedListeners) do
|
for _, callable in pairs(OnStageNumberChangedListeners) do
|
||||||
local succ, err = pcall(function()
|
local succ, err = pcall(function()
|
||||||
callable:OnStageNumberChanged(newStageNumber)
|
callable:OnStageNumberChanged(newStageNumber)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, callable in pairs(OnStageNumberChangedHandlers) do
|
for _, callable in pairs(OnStageNumberChangedHandlers) do
|
||||||
local succ, err = pcall(callable, newStageNumber)
|
local succ, err = pcall(callable, newStageNumber)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Spearhead.StageNumber = newStageNumber
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -72,11 +91,15 @@ do
|
|||||||
table.insert(onLandEventListeners[unitName], landListener)
|
table.insert(onLandEventListeners[unitName], landListener)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@class OnUnitLostListener
|
||||||
|
---@field OnUnitLost fun(self:OnUnitLostListener, unit:table)
|
||||||
|
|
||||||
|
---@type table<string,Array<OnUnitLostListener>>
|
||||||
local OnUnitLostListeners = {}
|
local OnUnitLostListeners = {}
|
||||||
---This listener gets fired for any event that can indicate a loss of a unit.
|
---This listener gets fired for any event that can indicate a loss of a unit.
|
||||||
---Such as: Eject, Crash, Dead, Unit_Lost,
|
---Such as: Eject, Crash, Dead, Unit_Lost,
|
||||||
---@param unitName any
|
---@param unitName any
|
||||||
---@param unitLostListener table Object with function: OnUnitLost(initiatorUnit)
|
---@param unitLostListener OnUnitLostListener
|
||||||
SpearheadEvents.addOnUnitLostEventListener = function(unitName, unitLostListener)
|
SpearheadEvents.addOnUnitLostEventListener = function(unitName, unitLostListener)
|
||||||
if type(unitLostListener) ~= "table" then
|
if type(unitLostListener) ~= "table" then
|
||||||
warn("Unit lost Event listener not of type table/object")
|
warn("Unit lost Event listener not of type table/object")
|
||||||
@@ -119,7 +142,7 @@ do
|
|||||||
callable:OnGroupRTB(groupName)
|
callable:OnGroupRTB(groupName)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -154,7 +177,7 @@ do
|
|||||||
callable:OnGroupRTBInTen(groupName)
|
callable:OnGroupRTBInTen(groupName)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -190,7 +213,7 @@ do
|
|||||||
callable:OnGroupOnStation(groupName)
|
callable:OnGroupOnStation(groupName)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -198,39 +221,6 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do --COMMANDS
|
|
||||||
do -- status updates
|
|
||||||
local onStatusRequestReceivedListeners = {}
|
|
||||||
---comment
|
|
||||||
---@param listener table object with OnStatusRequestReceived(self, groupId)
|
|
||||||
SpearheadEvents.AddOnStatusRequestReceivedListener = function(listener)
|
|
||||||
if type(listener) ~= "table" then
|
|
||||||
warn("Unit lost Event listener not of type table/object")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(onStatusRequestReceivedListeners, listener)
|
|
||||||
end
|
|
||||||
|
|
||||||
local triggerStatusRequestReceived = function(groupId)
|
|
||||||
for _, callable in pairs(onStatusRequestReceivedListeners) do
|
|
||||||
local succ, err = pcall(function()
|
|
||||||
callable:OnStatusRequestReceived(groupId)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
SpearheadEvents.AddCommandsToGroup = function(groupId)
|
|
||||||
local base = "MISSIONS"
|
|
||||||
if groupId then
|
|
||||||
missionCommands.addCommandForGroup(groupId, "Stage Status", nil, triggerStatusRequestReceived,
|
|
||||||
groupId)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
do -- PLAYER ENTER UNIT
|
do -- PLAYER ENTER UNIT
|
||||||
local playerEnterUnitListeners = {}
|
local playerEnterUnitListeners = {}
|
||||||
---comment
|
---comment
|
||||||
@@ -252,7 +242,7 @@ do
|
|||||||
callable:OnPlayerEntersUnit(unit)
|
callable:OnPlayerEntersUnit(unit)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -260,6 +250,20 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do -- Ejection events
|
||||||
|
|
||||||
|
local unitEjectListeners = {}
|
||||||
|
SpearheadEvents.AddOnUnitEjectedListener = function(listener)
|
||||||
|
if type(listener) ~= "table" then
|
||||||
|
warn("Unit lost Event listener not of type table/object")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(unitEjectListeners, listener)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
local e = {}
|
local e = {}
|
||||||
function e:onEvent(event)
|
function e:onEvent(event)
|
||||||
if event.id == world.event.S_EVENT_LAND or event.id == world.event.S_EVENT_RUNWAY_TOUCH then
|
if event.id == world.event.S_EVENT_LAND or event.id == world.event.S_EVENT_RUNWAY_TOUCH then
|
||||||
@@ -273,7 +277,7 @@ do
|
|||||||
callable:OnUnitLanded(unit, airbase)
|
callable:OnUnitLanded(unit, airbase)
|
||||||
end)
|
end)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -285,6 +289,11 @@ do
|
|||||||
event.id == world.event.S_EVENT_EJECTION or
|
event.id == world.event.S_EVENT_EJECTION or
|
||||||
event.id == world.event.S_EVENT_UNIT_LOST then
|
event.id == world.event.S_EVENT_UNIT_LOST then
|
||||||
local object = event.initiator
|
local object = event.initiator
|
||||||
|
|
||||||
|
if object and object.getName then
|
||||||
|
logDebug("Receiving death event from: " .. object:getName())
|
||||||
|
end
|
||||||
|
|
||||||
if object and object.getName and OnUnitLostListeners[object:getName()] then
|
if object and object.getName and OnUnitLostListeners[object:getName()] then
|
||||||
for _, callable in pairs(OnUnitLostListeners[object:getName()]) do
|
for _, callable in pairs(OnUnitLostListeners[object:getName()]) do
|
||||||
local succ, err = pcall(function()
|
local succ, err = pcall(function()
|
||||||
@@ -292,12 +301,20 @@ do
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
logError(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if event.id == world.event.S_EVENT_EJECTION then
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.id == world.event.S_EVENT_MISSION_END then
|
||||||
|
Spearhead.classes.persistence.Persistence.UpdateNow()
|
||||||
|
end
|
||||||
|
|
||||||
local AI_GROUPS = {}
|
local AI_GROUPS = {}
|
||||||
|
|
||||||
local function CheckAndTriggerSpawnAsync(unit, time)
|
local function CheckAndTriggerSpawnAsync(unit, time)
|
||||||
|
|||||||
@@ -1,47 +1,221 @@
|
|||||||
|
|
||||||
|
|
||||||
local StagesByName = {}
|
local StagesByName = {}
|
||||||
|
|
||||||
|
---@type table<string, Array<Stage>>
|
||||||
local StagesByIndex = {}
|
local StagesByIndex = {}
|
||||||
|
|
||||||
|
---@type table<string, Array<Stage>>
|
||||||
|
local SideStageByIndex = {}
|
||||||
|
|
||||||
|
---@type table<string, Array<WaitingStage>>
|
||||||
|
local WaitingStagesByIndex = {}
|
||||||
|
|
||||||
|
local currentStage = -99
|
||||||
|
|
||||||
|
|
||||||
GlobalStageManager = {}
|
GlobalStageManager = {}
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param database Database
|
||||||
|
---@param stageConfig StageConfig
|
||||||
|
---@return nil
|
||||||
function GlobalStageManager:NewAndStart(database, stageConfig)
|
function GlobalStageManager:NewAndStart(database, stageConfig)
|
||||||
local logger = Spearhead.LoggerTemplate:new("StageManager", stageConfig.logLevel)
|
local logger = Spearhead.LoggerTemplate:new("StageManager", stageConfig.logLevel)
|
||||||
|
logger:info("Using Stage Log Level: " .. stageConfig.logLevel)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, { __index = self })
|
setmetatable(o, { __index = self })
|
||||||
|
|
||||||
o.logger = logger
|
o.logger = logger
|
||||||
|
if stageConfig.isAutoStages ~= true then
|
||||||
o.onStageCompleted = function(self, stage)
|
logger:warn("Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message")
|
||||||
local stageNumber = tostring(stage.stageNumber)
|
|
||||||
local anyActive = false
|
|
||||||
for _, stage in pairs(StagesByIndex[stageNumber] or {}) do
|
|
||||||
if stage.isActive then anyActive = true end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if anyActive == false and stageConfig:isAutoStages() == true then
|
---@type OnStageChangedListener
|
||||||
Spearhead.Events.PublishStageNumberChanged(tonumber(stageNumber) + 1)
|
local OnStageNumberChangedListener = {
|
||||||
|
OnStageNumberChanged = function (self, number)
|
||||||
|
currentStage = number
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener)
|
||||||
|
|
||||||
|
---@type StageCompleteListener
|
||||||
|
local OnStageCompleteListener = {
|
||||||
|
OnStageComplete = function(self, stage)
|
||||||
|
logger:debug("Receiving stage complete event from: " .. stage.zoneName)
|
||||||
|
|
||||||
|
local anyIncomplete = false
|
||||||
|
logger:debug("Checking stages for index: " .. tostring(currentStage))
|
||||||
|
for index, stage in pairs(StagesByIndex[tostring(currentStage)]) do
|
||||||
|
if stage:IsComplete() == false then
|
||||||
|
anyIncomplete = true
|
||||||
|
logger:debug("Need to wait for Stage " .. stage.zoneName .. " to be completed")
|
||||||
|
else
|
||||||
|
logger:debug("Stage verified to be completed: " .. stage.zoneName)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if anyIncomplete == false and stageConfig.isAutoStages == true then
|
||||||
|
|
||||||
|
-- CHECK WAITING STAGES
|
||||||
|
local nextStage = currentStage + 1
|
||||||
|
|
||||||
|
if WaitingStagesByIndex[tostring(nextStage)] then
|
||||||
|
for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do
|
||||||
|
if waitingStage:IsActive() == false then
|
||||||
|
waitingStage:ActivateStage()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local anyWaiting = false
|
||||||
|
if WaitingStagesByIndex[tostring(nextStage)] then
|
||||||
|
for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do
|
||||||
|
if waitingStage:IsComplete() == false then
|
||||||
|
anyWaiting = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if anyWaiting == false then
|
||||||
|
logger:debug("Setting next stage to: " .. tostring(currentStage + 1))
|
||||||
|
Spearhead.Events.PublishStageNumberChanged(currentStage + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
for _, stageName in pairs(database:getStagezoneNames()) do
|
for _, stageName in pairs(database:getStagezoneNames()) do
|
||||||
|
|
||||||
local stagelogger = Spearhead.LoggerTemplate:new(stageName, stageConfig.logLevel)
|
if Spearhead.Util.startswith(stageName, "missionstage", true) then
|
||||||
local stage = Spearhead.internal.Stage:new(stageName, database, stagelogger, stageConfig)
|
local valid = true
|
||||||
|
local split = Spearhead.Util.split_string(stageName, "_")
|
||||||
|
if Spearhead.Util.tableLength(split) < 2 then
|
||||||
|
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format")
|
||||||
|
valid = false
|
||||||
|
end
|
||||||
|
|
||||||
if stage then
|
if Spearhead.Util.tableLength(split) < 3 then
|
||||||
stage:AddStageCompleteListener(o);
|
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a stage name")
|
||||||
StagesByName[stageName] = stage
|
valid = false
|
||||||
local indexString = tostring(stage.stageNumber)
|
end
|
||||||
if StagesByIndex[indexString] == nil then StagesByIndex[indexString] = {} end
|
|
||||||
table.insert(StagesByIndex[indexString], stage)
|
local orderNumber = nil
|
||||||
logger:info("Initiated " .. Spearhead.Util.tableLength(StagesByName) .. " airbases for cap")
|
local isSideStage = false
|
||||||
|
if valid == true then
|
||||||
|
local orderNumberString = string.lower(split[2])
|
||||||
|
if Spearhead.Util.startswith(orderNumberString, "x") == true then
|
||||||
|
isSideStage = true
|
||||||
|
|
||||||
|
local orderNumberString = string.gsub(orderNumberString, "x", "")
|
||||||
|
orderNumber = tonumber(orderNumberString)
|
||||||
|
else
|
||||||
|
orderNumber = tonumber(split[2])
|
||||||
|
end
|
||||||
|
|
||||||
|
if orderNumber == nil then
|
||||||
|
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a valid order number : " .. split[2])
|
||||||
|
valid = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local stageDisplayName = split[3]
|
||||||
|
local stagelogger = Spearhead.LoggerTemplate:new(stageName, stageConfig.logLevel)
|
||||||
|
if valid == true and orderNumber then
|
||||||
|
|
||||||
|
---@type StageInitData
|
||||||
|
local initData = {
|
||||||
|
stageDisplayName = stageDisplayName,
|
||||||
|
stageNumber = orderNumber,
|
||||||
|
stageZoneName = stageName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSideStage == true then
|
||||||
|
local stage = Spearhead.classes.stageClasses.Stages.ExtraStage.New(database, stageConfig, stagelogger, initData)
|
||||||
|
stage:AddStageCompleteListener(OnStageCompleteListener)
|
||||||
|
|
||||||
|
if SideStageByIndex[tostring(orderNumber)] == nil then SideStageByIndex[tostring(orderNumber)] = {} end
|
||||||
|
table.insert(SideStageByIndex[tostring(orderNumber)], stage)
|
||||||
|
else
|
||||||
|
local stage = Spearhead.classes.stageClasses.Stages.PrimaryStage.New(database, stageConfig, stagelogger, initData)
|
||||||
|
stage:AddStageCompleteListener(OnStageCompleteListener)
|
||||||
|
|
||||||
|
if StagesByIndex[tostring(orderNumber)] == nil then StagesByIndex[tostring(orderNumber)] = {} end
|
||||||
|
table.insert(StagesByIndex[tostring(orderNumber)], stage)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Spearhead.Util.startswith(stageName, "waitingstage", true) then
|
||||||
|
local valid = true
|
||||||
|
|
||||||
|
local split = Spearhead.Util.split_string(stageName, "_")
|
||||||
|
|
||||||
|
if Spearhead.Util.tableLength(split) < 3 then
|
||||||
|
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format")
|
||||||
|
valid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if valid == true then
|
||||||
|
local stageIndexString = split[2]
|
||||||
|
local stageIndex = tonumber(stageIndexString)
|
||||||
|
|
||||||
|
if not stageIndex then
|
||||||
|
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a valid order number")
|
||||||
|
valid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local waitingSecondsString = split[3]
|
||||||
|
local waitingSeconds = tonumber(waitingSecondsString)
|
||||||
|
if not waitingSeconds then
|
||||||
|
Spearhead.AddMissionEditorWarning("Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter")
|
||||||
|
valid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if valid == true then
|
||||||
|
local stagelogger = Spearhead.LoggerTemplate:new(stageName, stageConfig.logLevel)
|
||||||
|
|
||||||
|
---@type WaitingStageInitData
|
||||||
|
local initData = {
|
||||||
|
stageDisplayName = "Waiting Stage " .. stageIndex,
|
||||||
|
stageNumber = stageIndex or -99,
|
||||||
|
stageZoneName = stageName,
|
||||||
|
waitingSeconds = waitingSeconds --[[@as integer]]
|
||||||
|
}
|
||||||
|
local waitingStage = Spearhead.classes.stageClasses.Stages.WaitingStage.New(database, stageConfig, stagelogger, initData)
|
||||||
|
|
||||||
|
if WaitingStagesByIndex[tostring(stageIndex)] == nil then
|
||||||
|
WaitingStagesByIndex[tostring(stageIndex)] = {}
|
||||||
|
end
|
||||||
|
table.insert(WaitingStagesByIndex[tostring(stageIndex)], waitingStage)
|
||||||
|
|
||||||
|
waitingStage:AddStageCompleteListener(OnStageCompleteListener)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param stageNumber number
|
||||||
|
---@return boolean | nil
|
||||||
|
GlobalStageManager.isStageComplete = function (stageNumber)
|
||||||
|
|
||||||
|
local stageIndex = tostring(stageNumber)
|
||||||
|
|
||||||
|
if StagesByIndex[stageIndex] == nil then return nil end
|
||||||
|
|
||||||
|
for _, stage in ipairs(StagesByIndex[stageIndex]) do
|
||||||
|
if stage:IsComplete() == false then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
if not Spearhead.internal then Spearhead.internal = {} end
|
if not Spearhead.internal then Spearhead.internal = {} end
|
||||||
Spearhead.internal.GlobalStageManager = GlobalStageManager
|
Spearhead.internal.GlobalStageManager = GlobalStageManager
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---@class SpearheadGroup : OnUnitLostListener
|
||||||
|
---@field groupName string
|
||||||
|
---@field private isStatic boolean
|
||||||
|
---@field private isSpawned boolean
|
||||||
|
local SpearheadGroup = {}
|
||||||
|
|
||||||
|
function SpearheadGroup.New(groupName)
|
||||||
|
|
||||||
|
SpearheadGroup.__index = SpearheadGroup
|
||||||
|
|
||||||
|
local o = {}
|
||||||
|
local self = setmetatable(o, SpearheadGroup)
|
||||||
|
|
||||||
|
self.isStatic = Spearhead.DcsUtil.IsGroupStatic(groupName) == true
|
||||||
|
self.groupName = groupName
|
||||||
|
self.isSpawned = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function SpearheadGroup:SpawnCorpsesOnly()
|
||||||
|
|
||||||
|
if self.isSpawned == true then return end
|
||||||
|
|
||||||
|
local group = Spearhead.DcsUtil.SpawnGroupTemplate(self.groupName)
|
||||||
|
if group then
|
||||||
|
for _, unit in pairs(group:getUnits()) do
|
||||||
|
local deathState = Spearhead.classes.persistence.Persistence.UnitDeadState(unit:getName())
|
||||||
|
|
||||||
|
if deathState and deathState.isDead == true then
|
||||||
|
Spearhead.DcsUtil.DestroyUnit(self.groupName, unit:getName())
|
||||||
|
Spearhead.DcsUtil.SpawnCorpse(deathState.country_id, unit:getName(), deathState.type, deathState.pos, deathState.heading)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.isSpawned = true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function SpearheadGroup:Spawn()
|
||||||
|
|
||||||
|
if self.isSpawned == true then return end
|
||||||
|
|
||||||
|
local group = Spearhead.DcsUtil.SpawnGroupTemplate(self.groupName)
|
||||||
|
if group then
|
||||||
|
for _, unit in pairs(group:getUnits()) do
|
||||||
|
local deathState = Spearhead.classes.persistence.Persistence.UnitDeadState(unit:getName())
|
||||||
|
|
||||||
|
if deathState and deathState.isDead == true then
|
||||||
|
Spearhead.DcsUtil.DestroyUnit(self.groupName, unit:getName())
|
||||||
|
Spearhead.DcsUtil.SpawnCorpse(deathState.country_id, unit:getName(), deathState.type, deathState.pos, deathState.heading)
|
||||||
|
else
|
||||||
|
Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self)
|
||||||
|
end
|
||||||
|
|
||||||
|
Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.isSpawned = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function SpearheadGroup:Destroy()
|
||||||
|
self.isSpawned = false
|
||||||
|
Spearhead.DcsUtil.DestroyGroup(self.groupName)
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@return integer
|
||||||
|
function SpearheadGroup:GetCoalition()
|
||||||
|
if self.isStatic == true then
|
||||||
|
local object = StaticObject.getByName(self.groupName)
|
||||||
|
return object:getCoalition()
|
||||||
|
else
|
||||||
|
local group = Group.getByName(self.groupName)
|
||||||
|
return group:getCoalition()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function SpearheadGroup:OnUnitLost(object)
|
||||||
|
local name = object:getName()
|
||||||
|
local pos = object:getPoint()
|
||||||
|
local type = object:getDesc().typeName
|
||||||
|
local position = object:getPosition()
|
||||||
|
local heading = math.atan2(position.x.z, position.x.x)
|
||||||
|
local country_id = object:getCountry()
|
||||||
|
Spearhead.classes.persistence.Persistence.UnitKilled(name, pos, heading, type, country_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@return table result list of objects
|
||||||
|
function SpearheadGroup:GetUnits()
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
if self.isStatic == true then
|
||||||
|
local staticObject = StaticObject.getByName(self.groupName)
|
||||||
|
if staticObject then
|
||||||
|
table.insert(result, staticObject)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local group = Group.getByName(self.groupName)
|
||||||
|
for _, unit in pairs(group:getUnits()) do
|
||||||
|
table.insert(result, unit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end
|
||||||
|
Spearhead.classes.stageClasses.Groups.SpearheadGroup = SpearheadGroup
|
||||||
@@ -1,401 +0,0 @@
|
|||||||
|
|
||||||
--- A mission Object.
|
|
||||||
local Mission = {}
|
|
||||||
do -- INIT Mission Class
|
|
||||||
|
|
||||||
local MINIMAL_UNITS_ALIVE_RATIO = 0.20
|
|
||||||
|
|
||||||
local Defaults = {}
|
|
||||||
Defaults.MainMenu = "Missions"
|
|
||||||
Defaults.SelectMenuSubMenus = { Defaults.MainMenu, "Select Mission" }
|
|
||||||
Defaults.ShowMissionSubs = { Defaults.MainMenu }
|
|
||||||
|
|
||||||
local PlayersInMission = {}
|
|
||||||
local MissionType = {
|
|
||||||
UNKNOWN = 0,
|
|
||||||
STRIKE = 1,
|
|
||||||
BAI = 2,
|
|
||||||
DEAD = 3,
|
|
||||||
SAM = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
do --INIT MISSION TYPE FUNCTIONS
|
|
||||||
---Parse string to mission type
|
|
||||||
---@param input string
|
|
||||||
MissionType.Parse = function(input)
|
|
||||||
if input == nil then
|
|
||||||
return Mission.MissionType.UNKNOWN
|
|
||||||
end
|
|
||||||
|
|
||||||
input = string.lower(input)
|
|
||||||
if input == "dead" then return MissionType.DEAD end
|
|
||||||
if input == "strike" then return MissionType.STRIKE end
|
|
||||||
if input == "bai" then return MissionType.BAI end
|
|
||||||
if input == "sam" then return MissionType.SAM end
|
|
||||||
return Mission.MissionType.UNKNOWN
|
|
||||||
end
|
|
||||||
|
|
||||||
---comment
|
|
||||||
---@param input number missionType
|
|
||||||
---@return string text
|
|
||||||
MissionType.toString = function(input)
|
|
||||||
if input == MissionType.DEAD then return "DEAD" end
|
|
||||||
if input == MissionType.STRIKE then return "STRIKE" end
|
|
||||||
if input == MissionType.BAI then return "BAI" end
|
|
||||||
if input == MissionType.SAM then return "SAM" end
|
|
||||||
return "?"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Mission.MissionType = MissionType
|
|
||||||
|
|
||||||
Mission.MissionState = {
|
|
||||||
NEW = 0,
|
|
||||||
ACTIVE = 1,
|
|
||||||
COMPLETED = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
---comment
|
|
||||||
---@param missionZoneName string missionZoneName
|
|
||||||
---@param database table db dependency injection
|
|
||||||
---@return table?
|
|
||||||
function Mission:new(missionZoneName, database, logger)
|
|
||||||
local o = {}
|
|
||||||
setmetatable(o, { __index = self })
|
|
||||||
|
|
||||||
local function ParseGroupName(input)
|
|
||||||
local split_name = Spearhead.Util.split_string(input, "_")
|
|
||||||
local split_length = Spearhead.Util.tableLength(split_name)
|
|
||||||
if Spearhead.Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
|
|
||||||
Spearhead.AddMissionEditorWarning("Random Mission with zonename " .. input .. " not in right format")
|
|
||||||
return nil
|
|
||||||
elseif split_length < 3 then
|
|
||||||
Spearhead.AddMissionEditorWarning("Mission with zonename" .. input .. " not in right format")
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
local type = split_name[2]
|
|
||||||
local parsedType = Mission.MissionType.Parse(type)
|
|
||||||
|
|
||||||
if parsedType == nil then
|
|
||||||
Spearhead.AddMissionEditorWarning("Mission with zonename '" .. input .. "' has an unsupported type '" .. (type or "nil" ))
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
local name = split_name[3]
|
|
||||||
return {
|
|
||||||
missionName = name,
|
|
||||||
type = parsedType
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local parsed = ParseGroupName(missionZoneName)
|
|
||||||
if parsed == nil then return nil end
|
|
||||||
|
|
||||||
o.missionZoneName = missionZoneName
|
|
||||||
o.database = database
|
|
||||||
o.groupNames = database:getGroupsForMissionZone(missionZoneName)
|
|
||||||
o.name = parsed.missionName
|
|
||||||
o.missionType = parsed.type
|
|
||||||
o.missionTypeDisplayName = Mission.MissionType.toString(o.missionType)
|
|
||||||
o.startingGroups = Spearhead.Util.tableLength(o.groupNames)
|
|
||||||
o.missionState = Mission.MissionState.NEW
|
|
||||||
o.missionbriefing = database:GetDescriptionForMission(missionZoneName)
|
|
||||||
o.startingUnits = 0
|
|
||||||
o.logger = logger
|
|
||||||
o.code = database:GetNewMissionCode()
|
|
||||||
|
|
||||||
o.groupNamesPerUnit = {}
|
|
||||||
|
|
||||||
o.groupUnitAliveDict = {}
|
|
||||||
o.targetAliveStates = {}
|
|
||||||
o.hasSpecificTargets = false
|
|
||||||
|
|
||||||
local CheckStateAsync = function (self, time)
|
|
||||||
self:CheckAndUpdateSelf()
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
o.GetState = function(self)
|
|
||||||
return self.missionState
|
|
||||||
end
|
|
||||||
|
|
||||||
o.OnUnitLost = function(self, object)
|
|
||||||
--[[
|
|
||||||
OnUnit lost event
|
|
||||||
]]--
|
|
||||||
self.logger:debug("Getting on unit lost event")
|
|
||||||
|
|
||||||
local category = Object.getCategory(object)
|
|
||||||
if category == Object.Category.UNIT then
|
|
||||||
local unitName = object:getName()
|
|
||||||
self.logger:debug("UnitName:" .. unitName)
|
|
||||||
local groupName = self.groupNamesPerUnit[unitName]
|
|
||||||
self.groupUnitAliveDict[groupName][unitName] = false
|
|
||||||
|
|
||||||
if self.targetAliveStates[groupName][unitName] then
|
|
||||||
self.targetAliveStates[groupName][unitName] = false
|
|
||||||
end
|
|
||||||
elseif category == Object.Category.STATIC then
|
|
||||||
local name = object:getName()
|
|
||||||
self.groupUnitAliveDict[name][name] = false
|
|
||||||
|
|
||||||
self.logger:debug("Name " .. name)
|
|
||||||
|
|
||||||
if self.targetAliveStates[name][name] then
|
|
||||||
self.targetAliveStates[name][name] = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
timer.scheduleFunction(CheckStateAsync, self, timer.getTime() + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
o.MissionCompleteListeners = {}
|
|
||||||
---comment
|
|
||||||
---@param self table
|
|
||||||
---@param listener table Object that implements "OnMissionComplete(self, mission)"
|
|
||||||
o.AddMissionCompleteListener = function(self, listener)
|
|
||||||
if type(listener) ~= "table" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(self.MissionCompleteListeners, listener)
|
|
||||||
end
|
|
||||||
|
|
||||||
local TriggerMissionComplete = function(self)
|
|
||||||
for _, callable in pairs(self.MissionCompleteListeners) do
|
|
||||||
local succ, err = pcall( function()
|
|
||||||
callable:OnMissionComplete(self)
|
|
||||||
end)
|
|
||||||
if err then
|
|
||||||
self.logger:warn("Error in misstion complete listener:" .. err)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local StartCheckingAndUpdateSelfContinuous = function (self)
|
|
||||||
local CheckAndUpdate = function(self, time)
|
|
||||||
self:CheckAndUpdateSelf(true)
|
|
||||||
if self.missionState == Mission.MissionState.COMPLETED or self.missionState == Mission.MissionState.NEW then
|
|
||||||
return nil
|
|
||||||
else
|
|
||||||
return time + 60
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
timer.scheduleFunction(CheckAndUpdate, self, timer.getTime() + 300)
|
|
||||||
end
|
|
||||||
|
|
||||||
---comment
|
|
||||||
---@param self table
|
|
||||||
---@param checkUnitHealth boolean?
|
|
||||||
o.CheckAndUpdateSelf = function(self, checkUnitHealth)
|
|
||||||
if not checkUnitHealth then checkUnitHealth = false end
|
|
||||||
|
|
||||||
if checkUnitHealth == true then
|
|
||||||
local function unitAliveState(unitName)
|
|
||||||
local unit = Unit.getByName(unitName)
|
|
||||||
return unit ~= nil and unit:isExist() == true and unit:getLife() > 0.1
|
|
||||||
end
|
|
||||||
|
|
||||||
for groupName, unitNameDict in pairs(self.groupUnitAliveDict) do
|
|
||||||
for unitName, isAlive in pairs(unitNameDict) do
|
|
||||||
if isAlive == true then
|
|
||||||
self.groupUnitAliveDict[groupName][unitName] = unitAliveState(unitName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for groupName, unitNameDict in pairs(self.targetAliveStates) do
|
|
||||||
for unitName, isAlive in pairs(unitNameDict) do
|
|
||||||
if isAlive == true then
|
|
||||||
self.targetAliveStates[groupName][unitName] = unitAliveState(unitName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.missionState == Mission.MissionState.COMPLETED then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.hasSpecificTargets == true then
|
|
||||||
local specificTargetsAlive = false
|
|
||||||
for groupName, unitNameDict in pairs(self.targetAliveStates) do
|
|
||||||
for unitName, isAlive in pairs(unitNameDict) do
|
|
||||||
if isAlive == true then
|
|
||||||
specificTargetsAlive = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if specificTargetsAlive == false then
|
|
||||||
self.missionState = Mission.MissionState.COMPLETED
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local function CountAliveGroups()
|
|
||||||
|
|
||||||
self.logger:debug(self.groupUnitAliveDict)
|
|
||||||
|
|
||||||
local aliveGroups = 0
|
|
||||||
|
|
||||||
for _, group in pairs(self.groupUnitAliveDict) do
|
|
||||||
local groupTotal = 0
|
|
||||||
local groupDeath = 0
|
|
||||||
for _, isAlive in pairs(group) do
|
|
||||||
if isAlive ~= true then
|
|
||||||
groupDeath = groupDeath + 1
|
|
||||||
end
|
|
||||||
groupTotal = groupTotal + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local aliveRatio = (groupTotal - groupDeath) / groupTotal
|
|
||||||
if aliveRatio >= MINIMAL_UNITS_ALIVE_RATIO then
|
|
||||||
aliveGroups = aliveGroups + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return aliveGroups
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.missionType == Mission.MissionType.STRIKE then --strike targets should normally have TGT targets
|
|
||||||
if CountAliveGroups() == 0 then
|
|
||||||
self.missionState = Mission.MissionState.COMPLETED
|
|
||||||
end
|
|
||||||
elseif self.missionType == Mission.MissionType.BAI then
|
|
||||||
if CountAliveGroups() == 0 then
|
|
||||||
self.missionState = Mission.MissionState.COMPLETED
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--[[
|
|
||||||
TODO: Other checks for mission complete
|
|
||||||
]]
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.missionState == Mission.MissionState.COMPLETED then
|
|
||||||
self.logger:debug("Mission complete " .. self.name)
|
|
||||||
trigger.action.outText("Mission " .. self.name .. " (" .. self.code .. ") was completed succesfully!", 20)
|
|
||||||
|
|
||||||
TriggerMissionComplete(self)
|
|
||||||
--Schedule cleanup after 5 minutes of mission complete
|
|
||||||
--timer.scheduleFunction(CleanupDelayedAsync, self, timer.getTime() + 300)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---Activates groups for this mission
|
|
||||||
---@param self table
|
|
||||||
o.Activate = function(self)
|
|
||||||
if self.missionState == Mission.MissionState.ACTIVE then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.missionState = Mission.MissionState.ACTIVE
|
|
||||||
do --spawn groups
|
|
||||||
for key, groupname in pairs(self.groupNames) do
|
|
||||||
Spearhead.DcsUtil.SpawnGroupTemplate(groupname)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
StartCheckingAndUpdateSelfContinuous(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ToStateString = function(self)
|
|
||||||
if self.hasSpecificTargets then
|
|
||||||
local dead = 0
|
|
||||||
local total = 0
|
|
||||||
for _, group in pairs(self.targetAliveStates) do
|
|
||||||
for _, isAlive in pairs(group) do
|
|
||||||
total = total + 1
|
|
||||||
if isAlive == false then
|
|
||||||
dead = dead + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local completionPercentage = math.floor((dead / total) * 100)
|
|
||||||
return "Targets Destroyed: " .. completionPercentage .. "%"
|
|
||||||
else
|
|
||||||
local dead = 0
|
|
||||||
local total = 0
|
|
||||||
for _, group in pairs(self.groupUnitAliveDict) do
|
|
||||||
for _, isAlive in pairs(group) do
|
|
||||||
total = total + 1
|
|
||||||
if isAlive == false then
|
|
||||||
dead = dead + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local completionPercentage = math.floor((dead / total) * 100)
|
|
||||||
return "Targets Destroyed: " .. completionPercentage .. "%"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.ShowBriefing = function(self, groupId)
|
|
||||||
local stateString = ToStateString(self)
|
|
||||||
|
|
||||||
if self.missionbriefing == nil then self.missionbriefing = "No briefing available" end
|
|
||||||
local text = "Mission [" .. self.code .. "] ".. self.name .. "\n \n" .. self.missionbriefing .. " \n \n" .. stateString
|
|
||||||
trigger.action.outTextForGroup(groupId, text, 30);
|
|
||||||
end
|
|
||||||
|
|
||||||
o.Cleanup = function(self)
|
|
||||||
for key, groupName in pairs(self.groupNames) do
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(groupName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local Init = function(self)
|
|
||||||
for key, group_name in pairs(self.groupNames) do
|
|
||||||
|
|
||||||
|
|
||||||
self.groupUnitAliveDict[group_name] = {}
|
|
||||||
self.targetAliveStates[group_name] = {}
|
|
||||||
|
|
||||||
if Spearhead.DcsUtil.IsGroupStatic(group_name) then
|
|
||||||
Spearhead.Events.addOnUnitLostEventListener(group_name, self)
|
|
||||||
|
|
||||||
if Spearhead.Util.startswith(group_name, "TGT_") == true then
|
|
||||||
self.targetAliveStates[group_name][group_name] = true
|
|
||||||
self.hasSpecificTargets = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local group = Group.getByName(group_name)
|
|
||||||
local isGroupTarget = Spearhead.Util.startswith(group_name, "TGT_")
|
|
||||||
|
|
||||||
self.startingUnits = self.startingUnits + group:getInitialSize()
|
|
||||||
for _, unit in pairs(group:getUnits()) do
|
|
||||||
local unitName = unit:getName()
|
|
||||||
|
|
||||||
self.groupNamesPerUnit[unitName] = group_name
|
|
||||||
|
|
||||||
Spearhead.Events.addOnUnitLostEventListener(unitName, self)
|
|
||||||
|
|
||||||
|
|
||||||
if isGroupTarget == true or Spearhead.Util.startswith(unitName, "TGT_") == true then
|
|
||||||
self.targetAliveStates[group_name][unitName] = true
|
|
||||||
self.hasSpecificTargets = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.missionType == MissionType.BAI then
|
|
||||||
if Spearhead.DcsUtil.IsGroupStatic(group_name) ~= true then
|
|
||||||
self.groupUnitAliveDict[group_name][unitName] = true
|
|
||||||
end
|
|
||||||
elseif self.missionType == MissionType.DEAD or self.missionType == MissionType.SAM then
|
|
||||||
local desc = unit:getDesc()
|
|
||||||
local attributes = desc.attributes
|
|
||||||
if attributes["SAM"] == true or attributes["SAM TR"] or attributes["AAA"] then
|
|
||||||
self.targetAliveStates[group_name][unitName] = true
|
|
||||||
self.hasSpecificTargets = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.groupUnitAliveDict[group_name][unitName] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(group_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Init(o)
|
|
||||||
return o;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not Spearhead.internal then Spearhead.internal = {} end
|
|
||||||
Spearhead.internal.Mission = Mission
|
|
||||||
@@ -0,0 +1,404 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---@class Mission : OnUnitLostListener
|
||||||
|
---@field name string
|
||||||
|
---@field missionType missionType
|
||||||
|
---@field code string
|
||||||
|
---@field priority MissionPriority
|
||||||
|
---@field private _state MissionState
|
||||||
|
---@field private _zoneName string
|
||||||
|
---@field private _database Database
|
||||||
|
---@field private _logger Logger
|
||||||
|
---@field private _missionBriefing string
|
||||||
|
---@field private _missionGroups MissionGroups
|
||||||
|
---@field private _completeListeners Array<MissionCompleteListener>
|
||||||
|
local Mission = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- @class MissionCompleteListener
|
||||||
|
--- @field OnMissionComplete fun(self: any, mission:Mission)
|
||||||
|
|
||||||
|
--- @class MissionGroups
|
||||||
|
--- @field hasTargets boolean
|
||||||
|
--- @field groups Array<SpearheadGroup>
|
||||||
|
--- @field unitsAlive table<string, table<string, boolean>>
|
||||||
|
--- @field targetsAlive table<string, table<string, boolean>>
|
||||||
|
--- @field groupNamesPerunit table<string,string>
|
||||||
|
|
||||||
|
MINIMAL_UNITS_ALIVE_RATIO = 0.21
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param zoneName string
|
||||||
|
---@param priority MissionPriority
|
||||||
|
---@param database Database
|
||||||
|
---@param logger Logger
|
||||||
|
---@return Mission?
|
||||||
|
function Mission.New(zoneName, priority, database, logger)
|
||||||
|
|
||||||
|
local function ParseZoneName(input)
|
||||||
|
local split_name = Spearhead.Util.split_string(input, "_")
|
||||||
|
local split_length = Spearhead.Util.tableLength(split_name)
|
||||||
|
if Spearhead.Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
|
||||||
|
Spearhead.AddMissionEditorWarning("Random Mission with zonename " .. input .. " not in right format")
|
||||||
|
return nil
|
||||||
|
elseif split_length < 3 then
|
||||||
|
Spearhead.AddMissionEditorWarning("Mission with zonename" .. input .. " not in right format")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type missionType
|
||||||
|
local parsedType = "nil"
|
||||||
|
|
||||||
|
local inputType = string.lower(split_name[2])
|
||||||
|
if inputType == "dead" then parsedType = "DEAD" end
|
||||||
|
if inputType == "strike" then parsedType = "STRIKE" end
|
||||||
|
if inputType == "bai" then parsedType = "BAI" end
|
||||||
|
if inputType == "sam" then parsedType = "SAM" end
|
||||||
|
|
||||||
|
if parsedType == "nil" then
|
||||||
|
Spearhead.AddMissionEditorWarning("Mission with zonename '" .. input .. "' has an unsupported type '" .. (type or "nil" ))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local name = split_name[3]
|
||||||
|
return {
|
||||||
|
missionName = name,
|
||||||
|
type = parsedType
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local parsed = ParseZoneName(zoneName)
|
||||||
|
|
||||||
|
if parsed == nil then return end
|
||||||
|
|
||||||
|
Mission.__index = Mission
|
||||||
|
local o = {}
|
||||||
|
local self = setmetatable(o, Mission)
|
||||||
|
|
||||||
|
self._zoneName = zoneName
|
||||||
|
self.name = parsed.missionName
|
||||||
|
self.missionType = parsed.type
|
||||||
|
self.code = tostring(database:GetNewMissionCode())
|
||||||
|
self.priority = priority
|
||||||
|
self._state = "NEW"
|
||||||
|
|
||||||
|
self._logger = logger
|
||||||
|
self._database = database
|
||||||
|
self._completeListeners = {}
|
||||||
|
|
||||||
|
self._missionBriefing = database:getMissionBriefingForMissionZone(zoneName)
|
||||||
|
self._missionGroups = {
|
||||||
|
groups = {},
|
||||||
|
unitsAlive = {},
|
||||||
|
targetsAlive = {},
|
||||||
|
hasTargets = false,
|
||||||
|
groupNamesPerunit = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup
|
||||||
|
local groupNames = database:getGroupsForMissionZone(zoneName)
|
||||||
|
for _, groupName in pairs(groupNames) do
|
||||||
|
|
||||||
|
local spearheadGroup = SpearheadGroup.New(groupName)
|
||||||
|
table.insert(self._missionGroups.groups, spearheadGroup)
|
||||||
|
|
||||||
|
local isGroupTarget =Spearhead.Util.startswith(string.lower(groupName), "tgt_")
|
||||||
|
for _, unit in pairs(spearheadGroup:GetUnits())do
|
||||||
|
local unitName = unit:getName()
|
||||||
|
local isUnitTarget = Spearhead.Util.startswith(string.lower(unitName), "tgt_")
|
||||||
|
|
||||||
|
if self._missionGroups.unitsAlive[groupName] == nil then
|
||||||
|
self._missionGroups.unitsAlive[groupName] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
self._missionGroups.unitsAlive[groupName][unitName] = true
|
||||||
|
self._missionGroups.groupNamesPerunit[unitName] = groupName
|
||||||
|
|
||||||
|
if isGroupTarget == true or isUnitTarget == true then
|
||||||
|
self._missionGroups.hasTargets = true
|
||||||
|
|
||||||
|
if self._missionGroups.targetsAlive[groupName] == nil then
|
||||||
|
self._missionGroups.targetsAlive[groupName] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
self._missionGroups.targetsAlive[groupName][unitName] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
Spearhead.Events.addOnUnitLostEventListener(unitName, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
Spearhead.DcsUtil.DestroyGroup(groupName)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@return MissionState
|
||||||
|
function Mission:GetState()
|
||||||
|
return self._state
|
||||||
|
end
|
||||||
|
|
||||||
|
function Mission:SpawnPersistedState()
|
||||||
|
for _, group in pairs(self._missionGroups.groups) do
|
||||||
|
group:SpawnCorpsesOnly()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Mission:SpawnActive()
|
||||||
|
|
||||||
|
self._logger:info("Activating " .. self.name)
|
||||||
|
|
||||||
|
self._state = "ACTIVE"
|
||||||
|
for _, group in pairs(self._missionGroups.groups) do
|
||||||
|
group:Spawn()
|
||||||
|
end
|
||||||
|
|
||||||
|
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.AddMissionToCommands(self)
|
||||||
|
|
||||||
|
self:StartCheckingContinuous()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function Mission:StartCheckingContinuous()
|
||||||
|
---comment
|
||||||
|
---@param mission Mission
|
||||||
|
---@param time any
|
||||||
|
---@return unknown
|
||||||
|
local Check = function (mission, time)
|
||||||
|
mission:UpdateState(true, true)
|
||||||
|
|
||||||
|
if mission:GetState() == "COMPLETED" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return time + 30
|
||||||
|
end
|
||||||
|
timer.scheduleFunction(Check, self, timer.getTime() + 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@return string?
|
||||||
|
function Mission:ToStateString()
|
||||||
|
if self._missionGroups.hasTargets == true then
|
||||||
|
local dead = 0
|
||||||
|
local total = 0
|
||||||
|
if self._missionGroups.targetsAlive then
|
||||||
|
for _, group in pairs(self._missionGroups.targetsAlive) do
|
||||||
|
for _, isAlive in pairs(group) do
|
||||||
|
total = total + 1
|
||||||
|
if isAlive == false then
|
||||||
|
dead = dead + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if total > 0 then
|
||||||
|
local completionPercentage = math.floor((dead / total) * 100)
|
||||||
|
return "Targets Destroyed: " .. completionPercentage .. "%"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local dead = 0
|
||||||
|
local total = 0
|
||||||
|
if self._missionGroups.unitsAlive then
|
||||||
|
for _, group in pairs(self._missionGroups.unitsAlive) do
|
||||||
|
for _, isAlive in pairs(group) do
|
||||||
|
total = total + 1
|
||||||
|
if isAlive == false then
|
||||||
|
dead = dead + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if total > 0 then
|
||||||
|
local completionPercentage = math.floor((dead / total) * 100)
|
||||||
|
return "Units Destroyed: " .. completionPercentage .. "%"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param groupId integer
|
||||||
|
function Mission:ShowBriefing(groupId)
|
||||||
|
|
||||||
|
local stateString = self:ToStateString()
|
||||||
|
|
||||||
|
if self._missionBriefing == nil or self._missionBriefing == "" then self._missionBriefing = "No briefing available" end
|
||||||
|
local text = "Mission [" .. self.code .. "] ".. self.name .. "\n \n" .. self._missionBriefing .. " \n \n" .. stateString
|
||||||
|
trigger.action.outTextForGroup(groupId, text, 30);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param checkHealth boolean
|
||||||
|
---@param messageIfDone boolean
|
||||||
|
function Mission:UpdateState(checkHealth, messageIfDone)
|
||||||
|
if checkHealth == nil then checkHealth = false end
|
||||||
|
if messageIfDone == false then messageIfDone = true end
|
||||||
|
|
||||||
|
if self._state == "COMPLETED" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if checkHealth == true then
|
||||||
|
local function unitAliveState(unitName)
|
||||||
|
local staticObject = StaticObject.getByName(unitName)
|
||||||
|
if staticObject then
|
||||||
|
if staticObject:isExist() == true then
|
||||||
|
local life0 = staticObject:getDesc().life
|
||||||
|
if staticObject:getLife() / life0 < 0.3 then
|
||||||
|
self._logger:debug("exploding unit")
|
||||||
|
trigger.action.explosion(staticObject:getPoint(), 100)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local unit = Unit.getByName(unitName)
|
||||||
|
|
||||||
|
local alive = unit ~= nil and unit:isExist() == true
|
||||||
|
if alive == true then
|
||||||
|
if unit:getLife() / unit:getLife0() < 0.2 then
|
||||||
|
self._logger:debug("exploding unit")
|
||||||
|
trigger.action.explosion(unit:getPoint(), 100)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for groupName, unitNameDict in pairs(self._missionGroups.unitsAlive) do
|
||||||
|
for unitName, isAlive in pairs(unitNameDict) do
|
||||||
|
if isAlive == true then
|
||||||
|
self._missionGroups.unitsAlive[groupName][unitName] = unitAliveState(unitName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for groupName, unitNameDict in pairs(self._missionGroups.targetsAlive) do
|
||||||
|
for unitName, isAlive in pairs(unitNameDict) do
|
||||||
|
if isAlive == true then
|
||||||
|
self._missionGroups.targetsAlive[groupName][unitName] = unitAliveState(unitName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._missionGroups.hasTargets == true then
|
||||||
|
|
||||||
|
local anyTargetAlive = function()
|
||||||
|
for _, units in pairs(self._missionGroups.targetsAlive) do
|
||||||
|
for _, isAlive in pairs(units) do
|
||||||
|
if isAlive == true then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if anyTargetAlive() ~= true then
|
||||||
|
self._state = "COMPLETED"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local function CountAliveGroups()
|
||||||
|
local aliveGroups = 0
|
||||||
|
|
||||||
|
for _, group in pairs(self._missionGroups.unitsAlive) do
|
||||||
|
local groupTotal = 0
|
||||||
|
local groupDeath = 0
|
||||||
|
for _, isAlive in pairs(group) do
|
||||||
|
if isAlive ~= true then
|
||||||
|
groupDeath = groupDeath + 1
|
||||||
|
end
|
||||||
|
groupTotal = groupTotal + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local aliveRatio = (groupTotal - groupDeath) / groupTotal
|
||||||
|
if aliveRatio >= MINIMAL_UNITS_ALIVE_RATIO then
|
||||||
|
aliveGroups = aliveGroups + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return aliveGroups
|
||||||
|
end
|
||||||
|
|
||||||
|
if CountAliveGroups() == 0 then
|
||||||
|
self._state = "COMPLETED"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param mission Mission
|
||||||
|
local NotifyMissionComplete = function(mission)
|
||||||
|
mission:NotifyMissionComplete()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if self._state == "COMPLETED" then
|
||||||
|
timer.scheduleFunction(NotifyMissionComplete, self, timer.getTime() + 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---private usage advised
|
||||||
|
function Mission:NotifyMissionComplete()
|
||||||
|
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.RemoveMissionToCommands(self)
|
||||||
|
self._logger:info("Mission Completed: " .. self._zoneName)
|
||||||
|
trigger.action.outText("Mission " .. self.name .. " [" .. self.code .. "] was completed succesfully" , 20)
|
||||||
|
|
||||||
|
for _, listener in pairs(self._completeListeners) do
|
||||||
|
pcall(function()
|
||||||
|
listener:OnMissionComplete(self)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param listener MissionCompleteListener Object that implements "OnMissionComplete(self, mission)"
|
||||||
|
function Mission:AddMissionCompleteListener(listener)
|
||||||
|
if type(listener) ~= "table" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
table.insert(self._completeListeners, listener)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Mission:OnUnitLost(object)
|
||||||
|
--[[
|
||||||
|
OnUnit lost event
|
||||||
|
]]--
|
||||||
|
self._logger:debug("Getting on unit lost event")
|
||||||
|
|
||||||
|
local category = Object.getCategory(object)
|
||||||
|
if category == Object.Category.UNIT then
|
||||||
|
local unitName = object:getName()
|
||||||
|
self._logger:debug("UnitName:" .. unitName)
|
||||||
|
|
||||||
|
local groupName = self._missionGroups.groupNamesPerunit[unitName]
|
||||||
|
self._missionGroups.unitsAlive[groupName][unitName] = false
|
||||||
|
|
||||||
|
if self._missionGroups.targetsAlive[groupName] and self._missionGroups.targetsAlive[groupName][unitName] then
|
||||||
|
self._missionGroups.targetsAlive[groupName][unitName] = false
|
||||||
|
end
|
||||||
|
elseif category == Object.Category.STATIC then
|
||||||
|
local name = object:getName()
|
||||||
|
self._missionGroups.unitsAlive[name][name] = false
|
||||||
|
|
||||||
|
self._logger:debug("Name " .. name)
|
||||||
|
|
||||||
|
if self._missionGroups.targetsAlive[name] and self._missionGroups.targetsAlive[name][name] then
|
||||||
|
self._missionGroups.targetsAlive[name][name] = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:UpdateState(false, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Missions then Spearhead.classes.stageClasses.Missions = {} end
|
||||||
|
Spearhead.classes.stageClasses.Missions.Mission = Mission
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
---@class BlueSam
|
||||||
|
---@field Activate fun(self: BlueSam)
|
||||||
|
---@field private _database Database
|
||||||
|
---@field private _logger Logger
|
||||||
|
---@field private _zoneName string
|
||||||
|
---@field private _blueGroups Array<SpearheadGroup>
|
||||||
|
---@field private _cleanupUnits table<string, boolean>
|
||||||
|
local BlueSam = {}
|
||||||
|
|
||||||
|
function BlueSam.New(database, logger, zoneName)
|
||||||
|
BlueSam.__index = BlueSam
|
||||||
|
local self = setmetatable({}, BlueSam)
|
||||||
|
|
||||||
|
self._database = database
|
||||||
|
self._logger = logger
|
||||||
|
self._zoneName = zoneName
|
||||||
|
|
||||||
|
self._blueGroups = {}
|
||||||
|
self._cleanupUnits = {}
|
||||||
|
|
||||||
|
do
|
||||||
|
local groups = database:getBlueSamGroupsInZone(zoneName)
|
||||||
|
|
||||||
|
local blueUnitsPos = {}
|
||||||
|
local redUnitsPos = {}
|
||||||
|
|
||||||
|
for _, groupName in pairs(groups) do
|
||||||
|
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||||
|
if SpearheadGroup then
|
||||||
|
|
||||||
|
if SpearheadGroup:GetCoalition() == 2 then
|
||||||
|
table.insert(self._blueGroups, SpearheadGroup)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
for _, unit in pairs(SpearheadGroup:GetUnits()) do
|
||||||
|
if SpearheadGroup:GetCoalition() == 1 then
|
||||||
|
table.insert(blueUnitsPos, unit:getPoint())
|
||||||
|
elseif SpearheadGroup:GetCoalition() == 2 then
|
||||||
|
table.insert(redUnitsPos, unit:getPoint())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
SpearheadGroup:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- check cleanup requirements
|
||||||
|
-- Checks is any of the units are withing range (5m) of another unit.
|
||||||
|
-- If so, make sure to add them to the cleanup list.
|
||||||
|
|
||||||
|
local cleanup_distance = 5
|
||||||
|
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
|
||||||
|
for redUnitName, redUnitPos in pairs(redUnitsPos) do
|
||||||
|
local distance = Spearhead.Util.VectorDistance(blueUnitPos, redUnitPos)
|
||||||
|
env.info("distance: " .. tostring(distance))
|
||||||
|
if distance <= cleanup_distance then
|
||||||
|
self._cleanupUnits[redUnitName] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function BlueSam:Activate()
|
||||||
|
for unitName, needsCleanup in pairs(self._cleanupUnits) do
|
||||||
|
if needsCleanup == true then
|
||||||
|
Spearhead.DcsUtil.DestroyUnit(unitName)
|
||||||
|
else
|
||||||
|
local deathState = Spearhead.classes.persistence.Persistence.UnitDeadState(unitName)
|
||||||
|
if deathState and deathState.isDead == true then
|
||||||
|
Spearhead.DcsUtil.SpawnCorpse(deathState.country_id, unitName, deathState.type, deathState.pos, deathState.heading)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, group in pairs(self._blueGroups) do
|
||||||
|
group:Spawn()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if Spearhead == nil then Spearhead = {} end
|
||||||
|
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||||
|
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||||
|
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
|
||||||
|
Spearhead.classes.stageClasses.Missions.SpecialZones = BlueSam
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
|
||||||
|
---@class StageBase
|
||||||
|
---@field private _database Database
|
||||||
|
---@field private _logger Logger
|
||||||
|
---@field private _red_groups Array<SpearheadGroup>
|
||||||
|
---@field private _blue_groups Array<SpearheadGroup>
|
||||||
|
---@field private _cleanup_units table<string, boolean>
|
||||||
|
---@field private _airbase table?
|
||||||
|
---@field private _initialSide number?
|
||||||
|
local StageBase = {}
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param databaseManager table
|
||||||
|
---@param logger table
|
||||||
|
---@param airbaseId integer
|
||||||
|
---@return StageBase
|
||||||
|
function StageBase.New(databaseManager, logger, airbaseId)
|
||||||
|
|
||||||
|
StageBase.__index = StageBase
|
||||||
|
local self = setmetatable({}, StageBase)
|
||||||
|
|
||||||
|
self._database = databaseManager
|
||||||
|
self._logger = logger
|
||||||
|
|
||||||
|
self._red_groups = {}
|
||||||
|
self._blue_groups = {}
|
||||||
|
self._cleanup_units = {}
|
||||||
|
|
||||||
|
self._airbase = Spearhead.DcsUtil.getAirbaseById(airbaseId)
|
||||||
|
self._initialSide = Spearhead.DcsUtil.getStartingCoalition(airbaseId)
|
||||||
|
|
||||||
|
do --init
|
||||||
|
local redUnitsPos = {}
|
||||||
|
local blueUnitsPos = {}
|
||||||
|
|
||||||
|
do -- fill tables
|
||||||
|
local redGroups = databaseManager:getRedGroupsAtAirbase(airbaseId)
|
||||||
|
if redGroups then
|
||||||
|
for _, groupName in pairs(redGroups) do
|
||||||
|
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||||
|
table.insert(self._red_groups, shGroup)
|
||||||
|
|
||||||
|
for _, unit in shGroup:GetUnits() do
|
||||||
|
redUnitsPos[unit:getName()] = unit:getPoint()
|
||||||
|
end
|
||||||
|
|
||||||
|
shGroup:Destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local blueGroups = databaseManager:getBlueGroupsAtAirbase(airbaseId)
|
||||||
|
if blueGroups then
|
||||||
|
for _, groupName in pairs(blueGroups) do
|
||||||
|
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||||
|
table.insert(self._blue_groups, shGroup)
|
||||||
|
|
||||||
|
for _, unit in shGroup:GetUnits() do
|
||||||
|
blueUnitsPos[unit:getName()] = unit:getPoint()
|
||||||
|
end
|
||||||
|
|
||||||
|
shGroup:Destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- check cleanup requirements
|
||||||
|
-- Checks is any of the units are withing range (5m) of another unit.
|
||||||
|
-- If so, make sure to add them to the cleanup list.
|
||||||
|
|
||||||
|
local cleanup_distance = 5
|
||||||
|
|
||||||
|
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
|
||||||
|
for redUnitName, redUnitPos in pairs(redUnitsPos) do
|
||||||
|
local distance = Spearhead.Util.VectorDistance(blueUnitPos, redUnitPos)
|
||||||
|
env.info("distance: " .. tostring(distance))
|
||||||
|
if distance <= cleanup_distance then
|
||||||
|
self._cleanup_units[redUnitName] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function StageBase:SpawnRedUnits()
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param groups Array<SpearheadGroup>
|
||||||
|
local spawnAsync = function(groups)
|
||||||
|
for _, group in pairs(groups) do
|
||||||
|
group:Spawn()
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(spawnAsync, self._red_groups, timer.getTime() + 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function StageBase:CleanRedUnits()
|
||||||
|
for _, value in pairs(self._red_groups) do
|
||||||
|
value:SpawnCorpsesOnly()
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, unitName in pairs(self._cleanup_units) do
|
||||||
|
Spearhead.DcsUtil.DestroyUnit(unitName)
|
||||||
|
Spearhead.DcsUtil.CleanCorpse(unitName)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function StageBase:SpawnBlueUnits()
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param groups Array<SpearheadGroup>
|
||||||
|
local spawnAsync = function(groups)
|
||||||
|
for _, group in pairs(groups) do
|
||||||
|
group:Spawn()
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(spawnAsync, self._blue_groups, timer.getTime() + 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
function StageBase:ActivateRedStage()
|
||||||
|
if self._initialSide == 2 and self._airbase then
|
||||||
|
self._airbase:setCoalition(1)
|
||||||
|
self._airbase:autoCapture(false)
|
||||||
|
end
|
||||||
|
self:SpawnRedUnits()
|
||||||
|
end
|
||||||
|
|
||||||
|
function StageBase:ActivateBlueStage()
|
||||||
|
if self._initialSide == 2 and self._airbase then
|
||||||
|
self._airbase:setCoalition(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:CleanRedUnits()
|
||||||
|
self:SpawnBlueUnits()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if Spearhead == nil then Spearhead = {} end
|
||||||
|
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||||
|
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||||
|
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
|
||||||
|
Spearhead.classes.stageClasses.SpecialZones.StageBase = StageBase
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,497 +0,0 @@
|
|||||||
|
|
||||||
local Stage = {}
|
|
||||||
do --init STAGE DIRECTOR
|
|
||||||
|
|
||||||
|
|
||||||
local stageDrawingId = 0
|
|
||||||
|
|
||||||
---comment
|
|
||||||
---@param stagezone_name string
|
|
||||||
---@param database table
|
|
||||||
---@param logger table
|
|
||||||
---@return table?
|
|
||||||
function Stage:new(stagezone_name, database, logger, stageConfig)
|
|
||||||
local o = {}
|
|
||||||
setmetatable(o, { __index = self })
|
|
||||||
|
|
||||||
o.zoneName = stagezone_name
|
|
||||||
|
|
||||||
local split = Spearhead.Util.split_string(stagezone_name, "_")
|
|
||||||
if Spearhead.Util.tableLength(split) < 2 then
|
|
||||||
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stagezone_name .. " does not have a order number or valid format")
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local orderNumber = tonumber(split[2])
|
|
||||||
if orderNumber == nil then
|
|
||||||
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stagezone_name .. " does not have a valid order number : " .. split[2])
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
o.stageNumber = orderNumber
|
|
||||||
o.isActive = false
|
|
||||||
o.database = database
|
|
||||||
o.logger = logger
|
|
||||||
o.db = {}
|
|
||||||
o.db.missionsByCode = {}
|
|
||||||
o.db.missions = {}
|
|
||||||
o.db.sams = {}
|
|
||||||
o.db.redAirbasegroups = {}
|
|
||||||
o.db.blueAirbasegroups = {}
|
|
||||||
o.db.blueSamGroups = {}
|
|
||||||
o.db.airbaseIds = {}
|
|
||||||
o.db.farps = {}
|
|
||||||
o.activeStage = -99
|
|
||||||
o.preActivated = false
|
|
||||||
o.stageConfig = stageConfig or {}
|
|
||||||
o.stageDrawingId = stageDrawingId + 1
|
|
||||||
|
|
||||||
stageDrawingId = stageDrawingId + 1
|
|
||||||
|
|
||||||
do --Init Stage
|
|
||||||
logger:info("Initiating new Stage with name: " .. stagezone_name)
|
|
||||||
|
|
||||||
local missionZones = database:getMissionsForStage(stagezone_name)
|
|
||||||
for _, missionZone in pairs(missionZones) do
|
|
||||||
local mission = Spearhead.internal.Mission:new(missionZone, database, logger)
|
|
||||||
if mission then
|
|
||||||
o.db.missionsByCode[mission.code] = mission
|
|
||||||
if mission.missionType == Spearhead.internal.Mission.MissionType.SAM then
|
|
||||||
table.insert(o.db.sams, mission)
|
|
||||||
else
|
|
||||||
table.insert(o.db.missions, mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local randomMissionNames = database:getRandomMissionsForStage(stagezone_name)
|
|
||||||
|
|
||||||
local randomMissionByName = {}
|
|
||||||
for _, missionZoneName in pairs(randomMissionNames) do
|
|
||||||
local mission = Spearhead.internal.Mission:new(missionZoneName, database, logger)
|
|
||||||
if mission then
|
|
||||||
if randomMissionByName[mission.name] == nil then
|
|
||||||
randomMissionByName[mission.name] = {}
|
|
||||||
end
|
|
||||||
table.insert(randomMissionByName[mission.name], mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, missions in pairs(randomMissionByName) do
|
|
||||||
local mission = Spearhead.Util.randomFromList(missions)
|
|
||||||
if mission then
|
|
||||||
o.db.missionsByCode[mission.code] = mission
|
|
||||||
if mission.missionType == Spearhead.internal.Mission.MissionType.SAM then
|
|
||||||
table.insert(o.db.sams, mission)
|
|
||||||
else
|
|
||||||
table.insert(o.db.missions, mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local airbaseIds = database:getAirbaseIdsInStage(o.zoneName)
|
|
||||||
if airbaseIds ~= nil and type(airbaseIds) == "table" then
|
|
||||||
o.db.airbaseIds = airbaseIds
|
|
||||||
for _, airbaseId in pairs(airbaseIds) do
|
|
||||||
|
|
||||||
for _, groupName in pairs(database:getRedGroupsAtAirbase(airbaseId)) do
|
|
||||||
table.insert(o.db.redAirbasegroups, groupName)
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(groupName)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, groupName in pairs(database:getBlueGroupsAtAirbase(airbaseId)) do
|
|
||||||
table.insert(o.db.blueAirbasegroups, groupName)
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(groupName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, samZoneName in pairs(database:getBlueSamsInStage(o.zoneName)) do
|
|
||||||
for _, samGroup in pairs(database:getBlueSamGroupsInZone(samZoneName)) do
|
|
||||||
table.insert(o.db.blueSamGroups, samGroup)
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(samGroup)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local miscGroups = database:getMiscGroupsAtStage(o.zoneName)
|
|
||||||
for _, groupName in pairs(miscGroups) do
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(groupName)
|
|
||||||
end
|
|
||||||
|
|
||||||
local farps = database:getFarpZonesInStage(o.zoneName)
|
|
||||||
if farps ~= nil and type(farps) == "table" then o.db.farps = farps end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.StageCompleteListeners = {}
|
|
||||||
---comment
|
|
||||||
---@param self table
|
|
||||||
---@param StageCompleteListener table an Object with function onStageCompleted(stage)
|
|
||||||
o.AddStageCompleteListener = function(self, StageCompleteListener)
|
|
||||||
|
|
||||||
if type(StageCompleteListener) ~= "table" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
table.insert(self.StageCompleteListeners, StageCompleteListener)
|
|
||||||
end
|
|
||||||
|
|
||||||
local triggerStageCompleteListeners = function(self)
|
|
||||||
self.isActive = false
|
|
||||||
for _, callable in pairs(self.StageCompleteListeners) do
|
|
||||||
local succ, err = pcall( function()
|
|
||||||
callable:onStageCompleted(self)
|
|
||||||
end)
|
|
||||||
if err then
|
|
||||||
self.logger:warn("Error in misstion complete listener:" .. err)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.IsComplete = function(self)
|
|
||||||
for i, mission in pairs(self.db.missions) do
|
|
||||||
local state = mission:GetState()
|
|
||||||
if state == Spearhead.internal.Mission.MissionState.ACTIVE or state == Spearhead.internal.Mission.MissionState.NEW then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local CheckContinuousAsync = function(self, time)
|
|
||||||
self.logger:info("Checking stage completion for stage: " .. self.zoneName)
|
|
||||||
if self.activeStage == self.stageNumber then
|
|
||||||
return nil -- stop looping if this stage is not even active
|
|
||||||
end
|
|
||||||
|
|
||||||
if self:IsComplete() == true then
|
|
||||||
triggerStageCompleteListeners(self)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return time + 60
|
|
||||||
end
|
|
||||||
|
|
||||||
---Activates all SAMS, Airbase units etc all at once.
|
|
||||||
---@param self table
|
|
||||||
o.PreActivate = function(self)
|
|
||||||
if self.preActivated == false then
|
|
||||||
self.preActivated = true
|
|
||||||
for key, mission in pairs(self.db.sams) do
|
|
||||||
if mission and mission.Activate then
|
|
||||||
mission:Activate()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.logger:debug("Pre-activating stage with airbase groups amount: " .. Spearhead.Util.tableLength(self.db.redAirbasegroups))
|
|
||||||
|
|
||||||
for _ , groupName in pairs(self.db.redAirbasegroups) do
|
|
||||||
Spearhead.DcsUtil.SpawnGroupTemplate(groupName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.activeStage == self.stageNumber then
|
|
||||||
for _, mission in pairs(self.db.sams) do
|
|
||||||
self:AddCommmandsForMissionToAllPlayers(mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local activateMissionsIfApplicableAsync = function(self)
|
|
||||||
self:ActivateMissionsIfApplicable(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
o.MarkStage = function(self, blue)
|
|
||||||
local fillColor = {1, 0, 0, 0.1}
|
|
||||||
local line ={ 1, 0,0, 1 }
|
|
||||||
if blue == true then
|
|
||||||
fillColor = {0, 0, 1, 0.1}
|
|
||||||
line ={ 0, 0,1, 1 }
|
|
||||||
end
|
|
||||||
|
|
||||||
local zone = Spearhead.DcsUtil.getZoneByName(self.zoneName)
|
|
||||||
if zone and self.stageConfig:isDrawStagesEnabled() == true then
|
|
||||||
self.logger:debug("drawing stage")
|
|
||||||
if zone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then
|
|
||||||
trigger.action.circleToAll(-1, self.stageDrawingId, {x = zone.x, y = 0 , z = zone.z}, zone.radius, {0,0,0,0}, {0,0,0,0},4, true)
|
|
||||||
else
|
|
||||||
--trigger.action.circleToAll(-1, self.stageDrawingId, {x = zone.x, y = 0 , z = zone.z}, zone.radius, { 1, 0,0, 1 }, {1,0,0,1},4, true)
|
|
||||||
trigger.action.quadToAll( -1, self.stageDrawingId, zone.verts[1], zone.verts[2], zone.verts[3], zone.verts[4], {0,0,0,0}, {0,0,0,0}, 4, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
trigger.action.setMarkupColorFill(self.stageDrawingId, fillColor)
|
|
||||||
trigger.action.setMarkupColor(self.stageDrawingId, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.ActivateStage = function(self)
|
|
||||||
self.isActive = true;
|
|
||||||
|
|
||||||
pcall(function()
|
|
||||||
self:MarkStage()
|
|
||||||
end)
|
|
||||||
|
|
||||||
self:PreActivate()
|
|
||||||
|
|
||||||
local miscGroups = self.database:getMiscGroupsAtStage(self.zoneName)
|
|
||||||
self.logger:debug("Activating Misc groups for zone: " .. Spearhead.Util.tableLength(miscGroups))
|
|
||||||
for _, groupName in pairs(miscGroups) do
|
|
||||||
Spearhead.DcsUtil.SpawnGroupTemplate(groupName)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, mission in pairs(self.db.missions) do
|
|
||||||
if mission.missionType == Spearhead.internal.Mission.MissionType.DEAD then
|
|
||||||
mission:Activate()
|
|
||||||
self:AddCommmandsForMissionToAllPlayers(mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
timer.scheduleFunction(activateMissionsIfApplicableAsync, self, timer.getTime() + 5)
|
|
||||||
|
|
||||||
timer.scheduleFunction(CheckContinuousAsync, self, timer.getTime() + 60)
|
|
||||||
end
|
|
||||||
|
|
||||||
o.ActivateMissionsIfApplicable = function (self)
|
|
||||||
local activeCount = 0
|
|
||||||
|
|
||||||
local availableMissions = {}
|
|
||||||
for _, mission in pairs(self.db.missionsByCode) do
|
|
||||||
local state = mission:GetState()
|
|
||||||
|
|
||||||
if state == Spearhead.internal.Mission.MissionState.ACTIVE then
|
|
||||||
activeCount = activeCount + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if state == Spearhead.internal.Mission.MissionState.NEW then
|
|
||||||
table.insert(availableMissions, mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local max = self.stageConfig:getMaxMissionsPerStage() or 10
|
|
||||||
|
|
||||||
local availableMissionsCount = Spearhead.Util.tableLength(availableMissions)
|
|
||||||
if activeCount < max and availableMissionsCount > 0 then
|
|
||||||
for i = activeCount+1, max do
|
|
||||||
if availableMissionsCount == 0 then
|
|
||||||
i = max+1 --exits this loop
|
|
||||||
else
|
|
||||||
local index = math.random(1, availableMissionsCount)
|
|
||||||
local mission = table.remove(availableMissions, index)
|
|
||||||
if mission then
|
|
||||||
mission:Activate()
|
|
||||||
self:AddCommmandsForMissionToAllPlayers(mission)
|
|
||||||
activeCount = activeCount + 1;
|
|
||||||
end
|
|
||||||
availableMissionsCount = availableMissionsCount - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
---Cleans up all missions
|
|
||||||
---@param self table
|
|
||||||
o.Clean = function(self)
|
|
||||||
for key, mission in pairs(self.db.missions) do
|
|
||||||
mission:Cleanup()
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, samMission in pairs(self.db.sams) do
|
|
||||||
samMission:Cleanup()
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, airbase in pairs(self.db.airbases) do
|
|
||||||
for _, redGroupName in pairs(airbase.redAirbaseGroupNames) do
|
|
||||||
Spearhead.DcsUtil.DcsUtil.DestroyGroup(redGroupName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
logger:debug("'" .. Spearhead.Util.toString(self.zoneName) .. "' cleaned")
|
|
||||||
end
|
|
||||||
|
|
||||||
local ActivateBlueAsync = function(self)
|
|
||||||
pcall(function()
|
|
||||||
self:MarkStage(true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
for _, blueSamGroupName in pairs(self.db.blueSamGroups) do
|
|
||||||
Spearhead.DcsUtil.SpawnGroupTemplate(blueSamGroupName)
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, airbaseId in pairs(self.db.airbaseIds) do
|
|
||||||
local airbase = Spearhead.DcsUtil.getAirbaseById(airbaseId)
|
|
||||||
|
|
||||||
if airbase then
|
|
||||||
local startingCoalition = Spearhead.DcsUtil.getStartingCoalition(airbaseId)
|
|
||||||
if startingCoalition == coalition.side.BLUE then
|
|
||||||
airbase:setCoalition(2)
|
|
||||||
for _, blueGroupName in pairs(self.db.blueAirbasegroups) do
|
|
||||||
Spearhead.DcsUtil.SpawnGroupTemplate(blueGroupName)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
airbase:setCoalition(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---Sets airfields to blue and spawns friendly farps
|
|
||||||
o.ActivateBlueStage = function(self)
|
|
||||||
logger:debug("Setting stage '" .. Spearhead.Util.toString(self.zoneName) .. "' to blue")
|
|
||||||
|
|
||||||
for _, groupName in pairs(self.db.redAirbasegroups) do
|
|
||||||
Spearhead.DcsUtil.DestroyGroup(groupName)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, mission in pairs(self.db.missions) do
|
|
||||||
mission:Cleanup()
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, mission in pairs(self.db.sams) do
|
|
||||||
mission:Cleanup()
|
|
||||||
end
|
|
||||||
|
|
||||||
timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
o.OnStatusRequestReceived = function(self, groupId)
|
|
||||||
if self.activeStage ~= self.stageNumber then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
trigger.action.outTextForGroup(groupId, "Status Update incoming... ", 3)
|
|
||||||
|
|
||||||
local text = "Mission Status: \n"
|
|
||||||
|
|
||||||
local totalmissions = 0
|
|
||||||
local completedMissions = 0
|
|
||||||
for _, mission in pairs(self.db.missionsByCode) do
|
|
||||||
totalmissions = totalmissions + 1
|
|
||||||
if mission.missionState == Spearhead.internal.Mission.MissionState.ACTIVE then
|
|
||||||
|
|
||||||
text = text .. "\n [" .. mission.code .. "] " .. mission.name ..
|
|
||||||
" (" .. mission.missionTypeDisplayName .. ") \n"
|
|
||||||
end
|
|
||||||
|
|
||||||
if mission.missionState == Spearhead.internal.Mission.MissionState.COMPLETED then
|
|
||||||
completedMissions = completedMissions + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local completionPercentage = math.floor((completedMissions / totalmissions) * 100)
|
|
||||||
text = text .. " \n Missions Complete: " .. completionPercentage .. "%"
|
|
||||||
|
|
||||||
self.logger:debug(text)
|
|
||||||
trigger.action.outTextForGroup(groupId, text, 20)
|
|
||||||
end
|
|
||||||
|
|
||||||
o.OnStageNumberChanged = function (self, number)
|
|
||||||
|
|
||||||
if self.activeStage == number then --only activate once for a stage
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local previousActive = self.activeStage
|
|
||||||
self.activeStage = number
|
|
||||||
if Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
|
|
||||||
self:PreActivate()
|
|
||||||
end
|
|
||||||
|
|
||||||
if number == self.stageNumber then
|
|
||||||
self:ActivateStage()
|
|
||||||
end
|
|
||||||
|
|
||||||
if previousActive <= self.stageNumber then
|
|
||||||
if number > self.stageNumber then
|
|
||||||
self:ActivateBlueStage()
|
|
||||||
self:RemoveAllMissionCommands()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- input = { self, groupId, missionCode }
|
|
||||||
local ShowBriefingClicked = function (input)
|
|
||||||
|
|
||||||
local self = input.self
|
|
||||||
local groupId = input.groupId
|
|
||||||
local missionCode = input.missionCode
|
|
||||||
|
|
||||||
local mission = self.db.missionsByCode[missionCode]
|
|
||||||
if mission then
|
|
||||||
mission:ShowBriefing(groupId)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.RemoveMissionCommands = function (self, mission)
|
|
||||||
|
|
||||||
self.logger:debug("Removing commands for: " .. mission.name)
|
|
||||||
|
|
||||||
local folderName = mission.name .. "(" .. mission.missionTypeDisplayName .. ")"
|
|
||||||
for i = 0, 2 do
|
|
||||||
local players = coalition.getPlayers(i)
|
|
||||||
for _, playerUnit in pairs(players) do
|
|
||||||
local groupId = playerUnit:getGroup():getID()
|
|
||||||
missionCommands.removeItemForGroup(groupId, { "Missions", folderName })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.RemoveAllMissionCommands = function (self)
|
|
||||||
for _, mission in pairs(self.db.missionsByCode) do
|
|
||||||
self:RemoveMissionCommands(mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.AddCommandsForMissionToGroup = function (self, groupId, mission)
|
|
||||||
local folderName = mission.name .. "(" .. mission.missionTypeDisplayName .. ")"
|
|
||||||
missionCommands.addSubMenuForGroup(groupId, folderName, { "Missions"} )
|
|
||||||
missionCommands.addCommandForGroup(groupId, "Show Briefing", { "Missions", folderName }, ShowBriefingClicked, { self = self, groupId = groupId, missionCode = mission.code })
|
|
||||||
end
|
|
||||||
|
|
||||||
o.AddCommmandsForMissionToAllPlayers = function(self, mission)
|
|
||||||
for i = 0, 2 do
|
|
||||||
local players = coalition.getPlayers(i)
|
|
||||||
for _, playerUnit in pairs(players) do
|
|
||||||
local groupId = playerUnit:getGroup():getID()
|
|
||||||
self:AddCommandsForMissionToGroup(groupId, mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o.OnPlayerEntersUnit = function (self, unit)
|
|
||||||
if self.activeStage == self.stageNumber then
|
|
||||||
local groupId = unit:getGroup():getID()
|
|
||||||
for _, mission in pairs(self.db.missionsByCode) do
|
|
||||||
if mission.missionState == Spearhead.internal.Mission.MissionState.ACTIVE then
|
|
||||||
self:AddCommandsForMissionToGroup(groupId, mission)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local removeMissionCommandsDelayed = function(input)
|
|
||||||
local self = input.self
|
|
||||||
local mission = input.mission
|
|
||||||
self:RemoveMissionCommands(mission)
|
|
||||||
end
|
|
||||||
|
|
||||||
o.OnMissionComplete = function(self, mission)
|
|
||||||
timer.scheduleFunction(removeMissionCommandsDelayed, { self = self, mission = mission}, timer.getTime() + 20)
|
|
||||||
|
|
||||||
if(self:IsComplete()) then
|
|
||||||
timer.scheduleFunction(triggerStageCompleteListeners, self, timer.getTime() + 15)
|
|
||||||
else
|
|
||||||
timer.scheduleFunction(activateMissionsIfApplicableAsync, self, timer.getTime() + 10)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, mission in pairs(o.db.missionsByCode) do
|
|
||||||
mission:AddMissionCompleteListener(o)
|
|
||||||
end
|
|
||||||
|
|
||||||
Spearhead.Events.AddOnPlayerEnterUnitListener(o)
|
|
||||||
Spearhead.Events.AddOnStatusRequestReceivedListener(o)
|
|
||||||
Spearhead.Events.AddStageNumberChangedListener(o)
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not Spearhead.internal then Spearhead.internal = {} end
|
|
||||||
Spearhead.internal.Stage = Stage
|
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
|
||||||
|
---@alias StageColor
|
||||||
|
---| "RED"
|
||||||
|
---| "BLUE"
|
||||||
|
---| "GRAY"
|
||||||
|
|
||||||
|
--- @class StageData
|
||||||
|
--- @field missionsByCode table<string, Mission>
|
||||||
|
--- @field missions Array<Mission>
|
||||||
|
--- @field sams Array<Mission>
|
||||||
|
--- @field blueSams Array<BlueSam>
|
||||||
|
--- @field airbases Array<StageBase>
|
||||||
|
--- @field miscGroups Array<SpearheadGroup>
|
||||||
|
--- @field maxMissions integer
|
||||||
|
|
||||||
|
--- @class StageInitData
|
||||||
|
--- @field stageZoneName string
|
||||||
|
--- @field stageNumber integer
|
||||||
|
--- @field stageDisplayName string
|
||||||
|
|
||||||
|
|
||||||
|
--- @class StageCompleteListener
|
||||||
|
--- @field OnStageComplete fun(self:StageCompleteListener, stage:Stage)
|
||||||
|
|
||||||
|
--- @class Stage : MissionCompleteListener, OnStageChangedListener
|
||||||
|
--- @field zoneName string
|
||||||
|
--- @field stageName string
|
||||||
|
--- @field stageNumber number
|
||||||
|
--- @field protected _isActive boolean
|
||||||
|
--- @field protected _isComplete boolean
|
||||||
|
--- @field protected _missionPriority MissionPriority
|
||||||
|
--- @field protected _database Database
|
||||||
|
--- @field protected _db StageData
|
||||||
|
--- @field protected _logger Logger
|
||||||
|
--- @field protected _preActivated boolean
|
||||||
|
--- @field protected _activeStage integer
|
||||||
|
--- @field protected _stageConfig StageConfig
|
||||||
|
--- @field protected _stageDrawingId integer
|
||||||
|
--- @field protected _spawnedGroups Array<string>
|
||||||
|
--- @field protected _stageCompleteListeners Array<StageCompleteListener>
|
||||||
|
--- @field protected CheckContinuousAsync fun(self:Stage, time:number) : number?
|
||||||
|
--- @field protected OnPostStageComplete fun(self:Stage)?
|
||||||
|
--- @field protected OnPostBlueActivated fun(self:Stage)?
|
||||||
|
local Stage = {}
|
||||||
|
|
||||||
|
local stageDrawingId = 100
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param database Database
|
||||||
|
---@param stageConfig StageConfig
|
||||||
|
---@param logger any
|
||||||
|
---@param initData StageInitData
|
||||||
|
---@param missionPriority MissionPriority
|
||||||
|
---@return Stage
|
||||||
|
function Stage.New(database, stageConfig, logger, initData, missionPriority)
|
||||||
|
|
||||||
|
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup
|
||||||
|
|
||||||
|
Stage.__index = Stage
|
||||||
|
local o = {}
|
||||||
|
local self = setmetatable(o, Stage)
|
||||||
|
|
||||||
|
self.zoneName = initData.stageZoneName
|
||||||
|
self.stageNumber = initData.stageNumber
|
||||||
|
self._isActive = false
|
||||||
|
self._isComplete = false
|
||||||
|
self.stageName = initData.stageDisplayName
|
||||||
|
|
||||||
|
self.OnPostStageComplete = nil
|
||||||
|
self.OnPostBlueActivated = nil
|
||||||
|
|
||||||
|
self._database = database
|
||||||
|
self._logger = logger
|
||||||
|
self._db = {
|
||||||
|
missionsByCode = {},
|
||||||
|
missions = {},
|
||||||
|
sams ={},
|
||||||
|
blueSams = {},
|
||||||
|
airbases ={},
|
||||||
|
miscGroups = {},
|
||||||
|
maxMissions = stageConfig.maxMissionsPerStage
|
||||||
|
}
|
||||||
|
self._activeStage = -99
|
||||||
|
self._preActivated = false
|
||||||
|
self._stageConfig = stageConfig or {}
|
||||||
|
self._stageDrawingId = stageDrawingId + 1
|
||||||
|
self._spawnedGroups = {}
|
||||||
|
self._missionPriority = missionPriority
|
||||||
|
self._stageCompleteListeners = {}
|
||||||
|
|
||||||
|
stageDrawingId = stageDrawingId + 1
|
||||||
|
|
||||||
|
self._logger:info("Initiating new Stage with name: " .. self.zoneName)
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param self Stage
|
||||||
|
---@param time number?
|
||||||
|
self.CheckContinuousAsync = function (self, time)
|
||||||
|
|
||||||
|
self:CheckAndUpdateSelf()
|
||||||
|
if self:IsComplete() == true then
|
||||||
|
self:NotifyComplete()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return time + 20
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- load tables
|
||||||
|
local missionZones = database:getMissionsForStage(self.zoneName)
|
||||||
|
for _, missionZone in pairs(missionZones) do
|
||||||
|
local mission = Spearhead.classes.stageClasses.Missions.Mission.New(missionZone, self._missionPriority, database, logger)
|
||||||
|
if mission then
|
||||||
|
self._db.missionsByCode[mission.code] = mission
|
||||||
|
if mission.missionType == "SAM" then
|
||||||
|
table.insert(self._db.sams, mission)
|
||||||
|
else
|
||||||
|
table.insert(self._db.missions, mission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local randomMissionNames = database:getRandomMissionsForStage(self.zoneName)
|
||||||
|
local randomMissionByName = {}
|
||||||
|
for _, missionZoneName in pairs(randomMissionNames) do
|
||||||
|
local mission = Spearhead.classes.stageClasses.Missions.Mission.New(missionZoneName, "primary", database, logger)
|
||||||
|
if mission then
|
||||||
|
if randomMissionByName[mission.name] == nil then
|
||||||
|
randomMissionByName[mission.name] = {}
|
||||||
|
end
|
||||||
|
table.insert(randomMissionByName[mission.name], mission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, missions in pairs(randomMissionByName) do
|
||||||
|
local mission = Spearhead.Util.randomFromList(missions)
|
||||||
|
if mission then
|
||||||
|
self._db.missionsByCode[mission.code] = mission
|
||||||
|
if mission.missionType == "SAM" then
|
||||||
|
table.insert(self._db.sams, mission)
|
||||||
|
else
|
||||||
|
table.insert(self._db.missions, mission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, mission in pairs(self._db.missionsByCode) do
|
||||||
|
mission:AddMissionCompleteListener(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
local airbaseIds = database:getAirbaseIdsInStage(self.zoneName)
|
||||||
|
if airbaseIds ~= nil and type(airbaseIds) == "table" then
|
||||||
|
for _, airbaseId in pairs(airbaseIds) do
|
||||||
|
local airbase = Spearhead.classes.stageClasses.SpecialZones.StageBase.New(database, logger, airbaseId)
|
||||||
|
table.insert(self._db.airbases, airbase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, samZoneName in pairs(database:getBlueSamsInStage(self.zoneName)) do
|
||||||
|
local blueSam = Spearhead.classes.stageClasses.SpecialZones.BlueSam:new(database, logger, samZoneName)
|
||||||
|
table.insert(self._db.blueSams, blueSam)
|
||||||
|
end
|
||||||
|
|
||||||
|
local miscGroups = database:getMiscGroupsAtStage(self.zoneName)
|
||||||
|
for _, groupName in pairs(miscGroups) do
|
||||||
|
local miscGroup = SpearheadGroup.New(groupName)
|
||||||
|
|
||||||
|
table.insert(self._db.miscGroups, miscGroup)
|
||||||
|
Spearhead.DcsUtil.DestroyGroup(groupName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Spearhead.Events.AddStageNumberChangedListener(self)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function Stage:IsComplete()
|
||||||
|
if self._isComplete == true then return true end
|
||||||
|
|
||||||
|
for i, mission in pairs(self._db.sams) do
|
||||||
|
local state = mission:GetState()
|
||||||
|
if state == "ACTIVE" or state == "NEW" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, mission in pairs(self._db.missions) do
|
||||||
|
local state = mission:GetState()
|
||||||
|
if state == "ACTIVE" or state == "NEW" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self._isComplete = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function Stage:IsActive()
|
||||||
|
return self._isActive == true
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
function Stage:CheckAndUpdateSelf()
|
||||||
|
self._logger:debug("Checking on Stage: " .. self.zoneName)
|
||||||
|
|
||||||
|
local activeCount = 0
|
||||||
|
local dbTables = self:GetStageTables()
|
||||||
|
|
||||||
|
local availableMissions = {}
|
||||||
|
for _, mission in pairs(dbTables.missionsByCode) do
|
||||||
|
local state = mission:GetState()
|
||||||
|
|
||||||
|
if state == "ACTIVE" then
|
||||||
|
activeCount = activeCount + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == "NEW" then
|
||||||
|
table.insert(availableMissions, mission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local max = dbTables.maxMissions
|
||||||
|
local availableMissionsCount = Spearhead.Util.tableLength(availableMissions)
|
||||||
|
if activeCount < max and availableMissionsCount > 0 then
|
||||||
|
for i = activeCount+1, max do
|
||||||
|
if availableMissionsCount == 0 then
|
||||||
|
i = max+1 --exits this loop
|
||||||
|
else
|
||||||
|
local index = math.random(1, availableMissionsCount)
|
||||||
|
|
||||||
|
---@type Mission
|
||||||
|
local mission = table.remove(availableMissions, index)
|
||||||
|
if mission then
|
||||||
|
mission:SpawnActive()
|
||||||
|
activeCount = activeCount + 1;
|
||||||
|
end
|
||||||
|
availableMissionsCount = availableMissionsCount - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---private use only
|
||||||
|
function Stage:NotifyComplete()
|
||||||
|
|
||||||
|
self._logger:info("Stage complete: " .. self.stageName)
|
||||||
|
|
||||||
|
for _, listener in pairs(self._stageCompleteListeners) do
|
||||||
|
pcall(function()
|
||||||
|
listener:OnStageComplete(self)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.OnPostStageComplete then
|
||||||
|
timer.scheduleFunction(self.OnPostStageComplete, self, timer.getTime() + 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param listener StageCompleteListener
|
||||||
|
function Stage:AddStageCompleteListener(listener)
|
||||||
|
table.insert(self._stageCompleteListeners, listener)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Activates all SAMS, Airbase units etc all at once.
|
||||||
|
function Stage:PreActivate()
|
||||||
|
if self._preActivated == false then
|
||||||
|
self._preActivated = true
|
||||||
|
for key, mission in pairs(self._db.sams) do
|
||||||
|
if mission then
|
||||||
|
mission:SpawnActive()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, airbase in pairs(self._db.airbases) do
|
||||||
|
airbase:ActivateRedStage()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param stageColor StageColor
|
||||||
|
function Stage:MarkStage(stageColor)
|
||||||
|
local fillColor = {1, 0, 0, 0.1}
|
||||||
|
local line ={ 1, 0,0, 1 }
|
||||||
|
|
||||||
|
if stageColor == "RED" then
|
||||||
|
fillColor = {1, 0, 0, 0.1}
|
||||||
|
line ={ 1, 0,0, 1 }
|
||||||
|
elseif stageColor =="BLUE" then
|
||||||
|
fillColor = {0, 0, 1, 0.1}
|
||||||
|
line ={ 0, 0,1, 1 }
|
||||||
|
elseif stageColor == "GRAY" then
|
||||||
|
fillColor = {80/255, 80/255, 80/255, 0.15}
|
||||||
|
line ={ 80/255, 80/255,80/255, 1 }
|
||||||
|
end
|
||||||
|
|
||||||
|
local zone = Spearhead.DcsUtil.getZoneByName(self.zoneName)
|
||||||
|
if zone and self._stageConfig.isDrawStagesEnabled == true then
|
||||||
|
self._logger:debug("drawing stage: " .. self.zoneName)
|
||||||
|
if zone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then
|
||||||
|
trigger.action.circleToAll(-1, self._stageDrawingId, {x = zone.x, y = 0 , z = zone.z}, zone.radius, {0,0,0,0}, {0,0,0,0},4, true)
|
||||||
|
else
|
||||||
|
--trigger.action.circleToAll(-1, self.stageDrawingId, {x = zone.x, y = 0 , z = zone.z}, zone.radius, { 1, 0,0, 1 }, {1,0,0,1},4, true)
|
||||||
|
trigger.action.quadToAll( -1, self._stageDrawingId, zone.verts[1], zone.verts[2], zone.verts[3], zone.verts[4], {0,0,0,0}, {0,0,0,0}, 4, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
trigger.action.setMarkupColorFill(self._stageDrawingId, fillColor)
|
||||||
|
trigger.action.setMarkupColor(self._stageDrawingId, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Stage:ActivateStage()
|
||||||
|
self._isActive = true;
|
||||||
|
|
||||||
|
pcall(function()
|
||||||
|
self:MarkStage("RED")
|
||||||
|
end)
|
||||||
|
|
||||||
|
self:PreActivate()
|
||||||
|
|
||||||
|
self._logger:debug("Activating Misc groups for zone. Count: " .. Spearhead.Util.tableLength(self._db.miscGroups))
|
||||||
|
for _, miscGroup in pairs(self._db.miscGroups) do
|
||||||
|
miscGroup:Spawn()
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, mission in pairs(self._db.missions) do
|
||||||
|
if mission.missionType == "DEAD" then
|
||||||
|
mission:SpawnActive()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(self.CheckContinuousAsync, self, timer.getTime() + 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Private usage only
|
||||||
|
---@return StageData
|
||||||
|
function Stage:GetStageTables()
|
||||||
|
return self._db
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param self Stage
|
||||||
|
---@param number integer
|
||||||
|
function Stage:OnStageNumberChanged(number)
|
||||||
|
|
||||||
|
if self._activeStage == number then --only activate once for a stage
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local previousActive = self._activeStage
|
||||||
|
self._activeStage = number
|
||||||
|
if Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
|
||||||
|
self:PreActivate()
|
||||||
|
end
|
||||||
|
|
||||||
|
if number == self.stageNumber then
|
||||||
|
self:ActivateStage()
|
||||||
|
end
|
||||||
|
|
||||||
|
if previousActive <= self.stageNumber then
|
||||||
|
if number > self.stageNumber then
|
||||||
|
self:ActivateBlueStage()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Stage:GetBriefing()
|
||||||
|
return "Briefing For "
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param self Stage
|
||||||
|
---@param mission Mission
|
||||||
|
Stage.OnMissionComplete = function(self, mission)
|
||||||
|
self:CheckAndUpdateSelf()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---private use only
|
||||||
|
function Stage:ActivateBlueGroups()
|
||||||
|
|
||||||
|
for _, blueSam in pairs(self._db.blueSams) do
|
||||||
|
blueSam:Activate()
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, airbase in pairs(self._db.airbases) do
|
||||||
|
airbase:ActivateBlueStage()
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.OnPostBlueActivated then
|
||||||
|
pcall(function()
|
||||||
|
self:OnPostBlueActivated()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Stage:ActivateBlueStage()
|
||||||
|
|
||||||
|
self._logger:debug("Setting stage '" .. Spearhead.Util.toString(self.zoneName) .. "' to blue")
|
||||||
|
|
||||||
|
for _, mission in pairs(self._db.missions) do
|
||||||
|
mission:SpawnPersistedState()
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, mission in pairs(self._db.sams) do
|
||||||
|
mission:SpawnPersistedState()
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, miscGroup in pairs(self._db.miscGroups) do
|
||||||
|
miscGroup:Spawn()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param self Stage
|
||||||
|
local ActivateBlueAsync = function(self)
|
||||||
|
pcall(function()
|
||||||
|
self:MarkStage("BLUE")
|
||||||
|
end)
|
||||||
|
|
||||||
|
self:ActivateBlueGroups()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Stages.BaseStage then Spearhead.classes.stageClasses.Stages.BaseStage = {} end
|
||||||
|
Spearhead.classes.stageClasses.Stages.BaseStage.Stage = Stage
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
---@class ExtraStage : Stage
|
||||||
|
local ExtraStage = {}
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param database Database
|
||||||
|
---@param stageConfig StageConfig
|
||||||
|
---@param logger any
|
||||||
|
---@param initData StageInitData
|
||||||
|
---@return ExtraStage
|
||||||
|
function ExtraStage.New(database, stageConfig, logger, initData)
|
||||||
|
|
||||||
|
-- "Import"
|
||||||
|
local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage
|
||||||
|
setmetatable(ExtraStage, Stage)
|
||||||
|
|
||||||
|
ExtraStage.__index = ExtraStage
|
||||||
|
local self = Stage.New(database, stageConfig, logger, initData, "secondary") --[[@as ExtraStage]]
|
||||||
|
setmetatable(self, ExtraStage)
|
||||||
|
|
||||||
|
self.OnPostBlueActivated = function (selfStage)
|
||||||
|
selfStage:MarkStage("GRAY")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.OnPostStageComplete = function (selfStage)
|
||||||
|
self:ActivateBlueStage()
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param self Stage
|
||||||
|
---@param number integer
|
||||||
|
function ExtraStage:OnStageNumberChanged(number)
|
||||||
|
|
||||||
|
self._activeStage = number
|
||||||
|
if Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then
|
||||||
|
self:PreActivate()
|
||||||
|
end
|
||||||
|
|
||||||
|
if number == self.stageNumber then
|
||||||
|
self:ActivateStage()
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._isComplete == true then
|
||||||
|
self:ActivateBlueStage()
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
|
||||||
|
Spearhead.classes.stageClasses.Stages.ExtraStage = ExtraStage
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
---@class PrimaryStage : Stage
|
||||||
|
local PrimaryStage = {}
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param database Database
|
||||||
|
---@param stageConfig StageConfig
|
||||||
|
---@param logger any
|
||||||
|
---@param initData StageInitData
|
||||||
|
---@return PrimaryStage
|
||||||
|
function PrimaryStage.New(database, stageConfig, logger, initData)
|
||||||
|
|
||||||
|
-- "Import"
|
||||||
|
local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage
|
||||||
|
setmetatable(PrimaryStage, Stage)
|
||||||
|
PrimaryStage.__index = PrimaryStage
|
||||||
|
setmetatable(PrimaryStage, {__index = Stage})
|
||||||
|
|
||||||
|
local o = Stage.New(database, stageConfig, logger, initData, "primary") --[[@as PrimaryStage]]
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
|
||||||
|
Spearhead.classes.stageClasses.Stages.PrimaryStage = PrimaryStage
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
---@class WaitingStage : Stage
|
||||||
|
---@field private _waitTimeSeconds integer
|
||||||
|
---@field private _startTime number
|
||||||
|
local WaitingStage = {}
|
||||||
|
|
||||||
|
|
||||||
|
---@class WaitingStageInitData : StageInitData
|
||||||
|
---@field waitingSeconds integer
|
||||||
|
local WaitingStageInitData = {}
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param database Database
|
||||||
|
---@param stageConfig StageConfig
|
||||||
|
---@param logger any
|
||||||
|
---@param initData WaitingStageInitData
|
||||||
|
---@return WaitingStage
|
||||||
|
function WaitingStage.New(database, stageConfig, logger, initData)
|
||||||
|
|
||||||
|
-- "Import"
|
||||||
|
local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage
|
||||||
|
setmetatable(WaitingStage, Stage)
|
||||||
|
WaitingStage.__index = WaitingStage
|
||||||
|
setmetatable(WaitingStage, {__index = Stage})
|
||||||
|
|
||||||
|
local self = Stage.New(database, stageConfig, logger, initData, "primary") --[[@as WaitingStage]]
|
||||||
|
setmetatable(self, WaitingStage)
|
||||||
|
|
||||||
|
self._waitTimeSeconds = 5
|
||||||
|
if initData.waitingSeconds and initData.waitingSeconds > 5 then self._waitTimeSeconds = initData.waitingSeconds end
|
||||||
|
self._startTime = nil
|
||||||
|
|
||||||
|
self.CheckContinuousAsync = function (self, time)
|
||||||
|
if self:IsComplete() then
|
||||||
|
self:NotifyComplete()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return time + 2
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function WaitingStage:ActivateStage()
|
||||||
|
|
||||||
|
self._logger:info("Starting Waiting Stage '" .. self.zoneName .. "' which will complete in about " .. self._waitTimeSeconds .. " seconds")
|
||||||
|
|
||||||
|
self._isActive = true
|
||||||
|
self._startTime = timer.getTime()
|
||||||
|
timer.scheduleFunction(self.CheckContinuousAsync, self, self._startTime + self._waitTimeSeconds)
|
||||||
|
end
|
||||||
|
|
||||||
|
function WaitingStage:IsComplete()
|
||||||
|
if timer.getTime() > (self._startTime + self._waitTimeSeconds) then return true end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function WaitingStage:OnStageNumberChanged()
|
||||||
|
self._logger:debug("Waiting Stage OnStageNumberChanged override")
|
||||||
|
end
|
||||||
|
|
||||||
|
function WaitingStage:MarkStage(stageColor)
|
||||||
|
self._logger:debug("Waiting Stage MarkStage override")
|
||||||
|
end
|
||||||
|
|
||||||
|
function WaitingStage:GetExpectedTime()
|
||||||
|
return self._startTime + self._waitTimeSeconds
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Spearhead.classes then Spearhead.classes = {} end
|
||||||
|
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||||
|
if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end
|
||||||
|
Spearhead.classes.stageClasses.Stages.WaitingStage = WaitingStage
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
local MissionCommandsHelper = {}
|
||||||
|
do
|
||||||
|
---@type table<string, Mission>
|
||||||
|
local missionsByCode = {}
|
||||||
|
|
||||||
|
---@type table<string, boolean>
|
||||||
|
local enabledByCode = {}
|
||||||
|
|
||||||
|
local updateNeeded = false
|
||||||
|
|
||||||
|
---Add a mission to the F10 commands menu
|
||||||
|
---@param mission Mission
|
||||||
|
MissionCommandsHelper.AddMissionToCommands = function (mission)
|
||||||
|
missionsByCode[tostring(mission.code)] = mission
|
||||||
|
enabledByCode[tostring(mission.code)] = true
|
||||||
|
updateNeeded = true
|
||||||
|
end
|
||||||
|
|
||||||
|
---Removes a mission from the F10 commands menu
|
||||||
|
---@param mission Mission
|
||||||
|
MissionCommandsHelper.RemoveMissionToCommands = function (mission)
|
||||||
|
enabledByCode[tostring(mission.code)] = false
|
||||||
|
updateNeeded = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local folderNames = {
|
||||||
|
primary = "Primary Missions",
|
||||||
|
secondary = "Secondary Missions"
|
||||||
|
}
|
||||||
|
|
||||||
|
---Add Base Folder
|
||||||
|
---@param groupId integer
|
||||||
|
local addMissionFolders = function(groupId)
|
||||||
|
missionCommands.addSubMenuForGroup(groupId, folderNames.primary)
|
||||||
|
missionCommands.addSubMenuForGroup(groupId, folderNames.secondary)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Add Mission Folder
|
||||||
|
---@param groupId integer
|
||||||
|
local removeMissionFolders = function(groupId)
|
||||||
|
missionCommands.removeItemForGroup(groupId , { folderNames.primary } )
|
||||||
|
missionCommands.removeItemForGroup(groupId , { folderNames.secondary } )
|
||||||
|
end
|
||||||
|
|
||||||
|
local missionBriefingRequested = function(args)
|
||||||
|
---@type Mission
|
||||||
|
local mission = args.mission
|
||||||
|
local groupID = args.groupId
|
||||||
|
|
||||||
|
mission:ShowBriefing(groupID)
|
||||||
|
end
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param groupId integer
|
||||||
|
---@param mission Mission
|
||||||
|
local addMissionCommands = function(groupId, mission)
|
||||||
|
|
||||||
|
local path = nil
|
||||||
|
|
||||||
|
if mission.priority == "primary" then
|
||||||
|
path = { [1] = folderNames.primary }
|
||||||
|
elseif mission.priority == "secondary" then
|
||||||
|
path = { [1] = folderNames.secondary }
|
||||||
|
end
|
||||||
|
|
||||||
|
if path then
|
||||||
|
local missionFolderName = "[" .. mission.code .. "]" .. mission.name
|
||||||
|
missionCommands.addSubMenuForGroup(groupId, missionFolderName, path)
|
||||||
|
table.insert(path, missionFolderName)
|
||||||
|
missionCommands.addCommandForGroup(groupId, "Briefing" , path , missionBriefingRequested, { groupId = groupId, mission = mission })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local updateCommandsForGroup = function(group)
|
||||||
|
local groupID = group:getID()
|
||||||
|
|
||||||
|
-- Cleanup mission folder
|
||||||
|
removeMissionFolders(groupID)
|
||||||
|
|
||||||
|
-- Add mission folders
|
||||||
|
addMissionFolders(groupID)
|
||||||
|
|
||||||
|
for code, enabled in pairs(enabledByCode) do
|
||||||
|
if enabled == true then
|
||||||
|
local mission = missionsByCode[code]
|
||||||
|
if mission then
|
||||||
|
addMissionCommands(groupID, mission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local UpdateContinuous = function(none, time)
|
||||||
|
if updateNeeded == false then
|
||||||
|
return time + 15
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, unit in pairs(Spearhead.DcsUtil.getAllPlayerUnits()) do
|
||||||
|
if unit and unit:isExist() then
|
||||||
|
local group = unit:getGroup()
|
||||||
|
if group then
|
||||||
|
updateCommandsForGroup(group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
updateNeeded = false
|
||||||
|
return time + 15
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(UpdateContinuous, {}, timer.getTime() + 10)
|
||||||
|
|
||||||
|
do -- Player enter unit listener
|
||||||
|
local onPlayerEnterUnit = function(unit)
|
||||||
|
if unit then
|
||||||
|
local group = unit:getGroup()
|
||||||
|
if group then updateCommandsForGroup(group) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local OnPlayerEnterUnitListener = {
|
||||||
|
OnPlayerEntersUnit = function (self, unit)
|
||||||
|
onPlayerEnterUnit(unit)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
Spearhead.Events.AddOnPlayerEnterUnitListener(OnPlayerEnterUnitListener)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Spearhead == nil then Spearhead = {} end
|
||||||
|
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||||
|
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||||
|
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
|
||||||
|
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper = MissionCommandsHelper
|
||||||
+20
-3
@@ -32,7 +32,6 @@ SpearheadConfig = {
|
|||||||
-- unit: seconds
|
-- unit: seconds
|
||||||
deathDelay = 1800, -- default 1800
|
deathDelay = 1800, -- default 1800
|
||||||
},
|
},
|
||||||
|
|
||||||
StageConfig = {
|
StageConfig = {
|
||||||
|
|
||||||
-- management of stages and its missions.
|
-- management of stages and its missions.
|
||||||
@@ -40,7 +39,7 @@ SpearheadConfig = {
|
|||||||
enabled = true, -- default true
|
enabled = true, -- default true
|
||||||
|
|
||||||
--Will draw the active and the next stage
|
--Will draw the active and the next stage
|
||||||
drawStages = false, -- default true
|
drawStages = true, -- default true
|
||||||
|
|
||||||
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
|
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
|
||||||
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
|
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
|
||||||
@@ -53,7 +52,25 @@ SpearheadConfig = {
|
|||||||
maxMissionStage = 10,
|
maxMissionStage = 10,
|
||||||
|
|
||||||
--Stage starting number
|
--Stage starting number
|
||||||
startingStage = 1
|
startingStage = 1,
|
||||||
|
|
||||||
|
---DEBUG logging. Consider keeping this disabled
|
||||||
|
debugEnabled = false
|
||||||
|
},
|
||||||
|
Persistence = {
|
||||||
|
--- io and lfs cannot be sanitized in the MissionScripting.lua
|
||||||
|
|
||||||
|
--- enables or disables the persistence logic in spearhead
|
||||||
|
enabled = false,
|
||||||
|
|
||||||
|
--- sets the directory where the persistence file is stored
|
||||||
|
--- if nil then lfs.writedir() will be used.
|
||||||
|
--- which will
|
||||||
|
directory = nil ,
|
||||||
|
|
||||||
|
--- the filename of the persistence file. Should end with .json for convention, but any text extension should do.
|
||||||
|
fileName = "Spearhead_Persistence.json"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-16
@@ -1,28 +1,47 @@
|
|||||||
|
|
||||||
|
|
||||||
local basePath = "C:\\Repos\\DCS\\Spearhead\\classes\\"
|
|
||||||
|
|
||||||
assert(loadfile(basePath .. "config.lua"))()
|
--assert(loadfile("C:\\Repos\\DCS\\Spearhead\\dev\\dev_classes.lua"))()
|
||||||
|
|
||||||
assert(loadfile(basePath .. "spearhead_base.lua"))()
|
local basePath = "C:\\Repos\\DCS\\Spearhead\\"
|
||||||
assert(loadfile(basePath .. "spearhead_routeutil.lua"))()
|
local classPath = basePath .. "classes\\"
|
||||||
assert(loadfile(basePath .. "spearhead_events.lua"))()
|
|
||||||
assert(loadfile(basePath .. "spearhead_db.lua"))()
|
|
||||||
|
|
||||||
assert(loadfile(basePath .. "fleetClasses\\FleetGroup.lua"))()
|
assert(loadfile(classPath .. "spearhead_base.lua"))()
|
||||||
assert(loadfile(basePath .. "fleetClasses\\GlobalFleetManager.lua"))()
|
assert(loadfile(classPath .. "spearhead_routeutil.lua"))()
|
||||||
|
assert(loadfile(classPath .. "spearhead_events.lua"))()
|
||||||
|
assert(loadfile(classPath .. "spearhead_db.lua"))()
|
||||||
|
|
||||||
assert(loadfile(basePath .. "configuration\\CapConfig.lua"))()
|
assert(loadfile(classPath .. "fleetClasses\\FleetGroup.lua"))()
|
||||||
assert(loadfile(basePath .. "configuration\\StageConfig.lua"))()
|
assert(loadfile(classPath .. "fleetClasses\\GlobalFleetManager.lua"))()
|
||||||
|
|
||||||
assert(loadfile(basePath .. "stageClasses\\GlobalStageManager.lua"))()
|
assert(loadfile(classPath .. "configuration\\CapConfig.lua"))()
|
||||||
assert(loadfile(basePath .. "stageClasses\\Mission.lua"))()
|
assert(loadfile(classPath .. "configuration\\StageConfig.lua"))()
|
||||||
assert(loadfile(basePath .. "stageClasses\\Stage.lua"))()
|
|
||||||
|
|
||||||
assert(loadfile(basePath .. "capClasses\\CapGroup.lua"))()
|
assert(loadfile(classPath .. "stageClasses\\GlobalStageManager.lua"))()
|
||||||
assert(loadfile(basePath .. "capClasses\\GlobalCapManager.lua"))()
|
assert(loadfile(classPath .. "stageClasses\\Missions\\Mission.lua"))()
|
||||||
assert(loadfile(basePath .. "capClasses\\CapAirbase.lua"))()
|
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\Stages\\BaseStage\\Stage.lua"))()
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\Stages\\PrimaryStage.lua"))()
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\Stages\\ExtraStage.lua"))()
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\Stages\\WaitingStage.lua"))()
|
||||||
|
|
||||||
|
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadGroup.lua"))()
|
||||||
|
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\helpers\\MissionCommandsHelper.lua"))()
|
||||||
|
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\StageBase.lua"))()
|
||||||
|
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\BlueSam.lua"))()
|
||||||
|
|
||||||
|
assert(loadfile(classPath .. "capClasses\\CapGroup.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\GlobalCapManager.lua"))()
|
||||||
|
assert(loadfile(classPath .. "capClasses\\CapAirbase.lua"))()
|
||||||
|
|
||||||
|
assert(loadfile(classPath .. "persistence\\Persistence.lua"))()
|
||||||
|
|
||||||
-- Startup:
|
-- Startup:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
assert(loadfile(basePath .. "main.lua"))()
|
assert(loadfile(basePath .. "main.lua"))()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
SpearheadConfig = {
|
||||||
|
|
||||||
|
CapConfig = {
|
||||||
|
--quickly enable of disable the entire CAP Logic
|
||||||
|
--(you can also just rename all units to not be named "CAP_")
|
||||||
|
enabled = true, -- default true
|
||||||
|
|
||||||
|
--min ground speed for CAP aircraft during patrol
|
||||||
|
-- unit: knots
|
||||||
|
minSpeed = 400, -- default 400
|
||||||
|
|
||||||
|
--max speed for CAP aircraft during patrol
|
||||||
|
-- unit: knots
|
||||||
|
maxSpeed = 500, -- default 500
|
||||||
|
|
||||||
|
--minAlt for aircraft on patrol
|
||||||
|
-- unit: feet
|
||||||
|
minAlt = 18000, -- default 18000
|
||||||
|
|
||||||
|
--maxAlt for aircraft on patrol
|
||||||
|
-- unit: feet
|
||||||
|
maxAlt = 28000, -- default 28000
|
||||||
|
|
||||||
|
--Delay for aircraft from touchdown to off the chocks.
|
||||||
|
-- unit: seconds
|
||||||
|
rearmDelay = 600, -- default 600
|
||||||
|
|
||||||
|
--Delay for aircraft from death to takeoff.
|
||||||
|
--When the seconds remaining is the same at the rearmDelay it will be spawned on the ramp and follow the rearm logic.
|
||||||
|
-- !! Can not be lower than rearmDelay
|
||||||
|
-- unit: seconds
|
||||||
|
deathDelay = 1800, -- default 1800
|
||||||
|
},
|
||||||
|
StageConfig = {
|
||||||
|
|
||||||
|
-- management of stages and its missions.
|
||||||
|
-- This is not related to CAP managers which will continue to work even if stage management is disabled
|
||||||
|
enabled = true, -- default true
|
||||||
|
|
||||||
|
--Will draw the active and the next stage
|
||||||
|
drawStages = true, -- default true
|
||||||
|
|
||||||
|
--AutoStages will continue to the next stage automatically on completion of the missions within the stage.
|
||||||
|
-- If you want to make it so the next stage triggers only when you want to disable it here and manually implement the actions needed.
|
||||||
|
--[[
|
||||||
|
TODO: Add manual stage transition documentation
|
||||||
|
]]
|
||||||
|
autoStages = true, --default true
|
||||||
|
|
||||||
|
--Maximum missions per stage (includes all types of missions)
|
||||||
|
maxMissionStage = 10,
|
||||||
|
|
||||||
|
--Stage starting number
|
||||||
|
startingStage = 1,
|
||||||
|
|
||||||
|
---DEBUG logging. Consider keeping this disabled
|
||||||
|
debugEnabled = true
|
||||||
|
},
|
||||||
|
Persistence = {
|
||||||
|
--- io and lfs cannot be sanitized in the MissionScripting.lua
|
||||||
|
|
||||||
|
--- enables or disables the persistence logic in spearhead
|
||||||
|
enabled = false,
|
||||||
|
|
||||||
|
--- sets the directory where the persistence file is stored
|
||||||
|
--- if nil then lfs.writedir() will be used.
|
||||||
|
--- which will
|
||||||
|
directory = nil ,
|
||||||
|
|
||||||
|
--- the filename of the persistence file. Should end with .json for convention, but any text extension should do.
|
||||||
|
fileName = "Spearhead_Persistence.json"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,21 +1,38 @@
|
|||||||
|
|
||||||
--Single player purpose
|
--Single player purpose
|
||||||
|
|
||||||
|
|
||||||
local debug = false
|
local debug = false
|
||||||
local id = net.get_my_player_id()
|
local id = net.get_my_player_id()
|
||||||
if id == 0 then
|
if id == 0 then
|
||||||
debug = true
|
debug = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local dbLogger = Spearhead.LoggerTemplate:new("database", Spearhead.LoggerTemplate.LogLevelOptions.INFO)
|
local startTime = timer.getTime() * 1000
|
||||||
local standardLogger = Spearhead.LoggerTemplate:new("", Spearhead.LoggerTemplate.LogLevelOptions.INFO)
|
|
||||||
|
Spearhead.Events.Init("DEBUG")
|
||||||
|
|
||||||
|
local dbLogger = Spearhead.LoggerTemplate:new("database", "INFO")
|
||||||
|
local standardLogger = Spearhead.LoggerTemplate:new("", "INFO")
|
||||||
local databaseManager = Spearhead.DB:new(dbLogger, debug)
|
local databaseManager = Spearhead.DB:new(dbLogger, debug)
|
||||||
|
|
||||||
local capConfig = Spearhead.internal.configuration.CapConfig:new();
|
local capConfig = Spearhead.internal.configuration.CapConfig:new();
|
||||||
local stageConfig = Spearhead.internal.configuration.StageConfig:new();
|
local stageConfig = Spearhead.internal.configuration.StageConfig:new();
|
||||||
|
|
||||||
standardLogger:info("Using StageConfig: ".. stageConfig:toString())
|
local startingStage = stageConfig.startingStage or 1
|
||||||
|
if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then
|
||||||
|
standardLogger:info("Persistence enabled")
|
||||||
|
local persistenceLogger = Spearhead.LoggerTemplate:new("Persistence", "INFO")
|
||||||
|
Spearhead.classes.persistence.Persistence.Init(persistenceLogger)
|
||||||
|
|
||||||
|
local persistanceStage = Spearhead.classes.persistence.Persistence.GetActiveStage()
|
||||||
|
if persistanceStage then
|
||||||
|
standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage)
|
||||||
|
startingStage = persistanceStage
|
||||||
|
end
|
||||||
|
else
|
||||||
|
standardLogger:info("Persistence disabled")
|
||||||
|
end
|
||||||
|
|
||||||
Spearhead.internal.GlobalCapManager.start(databaseManager, capConfig, stageConfig)
|
Spearhead.internal.GlobalCapManager.start(databaseManager, capConfig, stageConfig)
|
||||||
Spearhead.internal.GlobalStageManager:NewAndStart(databaseManager, stageConfig)
|
Spearhead.internal.GlobalStageManager:NewAndStart(databaseManager, stageConfig)
|
||||||
@@ -26,11 +43,14 @@ local SetStageDelayed = function(number, time)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local startingStage = stageConfig:getStartingStage() or 1
|
|
||||||
|
|
||||||
timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3)
|
timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3)
|
||||||
|
|
||||||
|
env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms")
|
||||||
|
local duration = (timer.getTime() * 1000) - startTime
|
||||||
|
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
|
||||||
|
|
||||||
Spearhead.LoadingDone()
|
Spearhead.LoadingDone()
|
||||||
|
|
||||||
--Check lines of code in directory per file:
|
--Check lines of code in directory per file:
|
||||||
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; && GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
|
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; && GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
|
||||||
-- find . -name '*.lua' | xargs wc -l
|
-- find . -name '*.lua' | xargs wc -l
|
||||||
@@ -53,4 +73,3 @@ Spearhead.LoadingDone()
|
|||||||
-- 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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user