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
|
||||
username: dutchie032
|
||||
repo: dutchie032.github.io
|
||||
sourcefileA: Reference
|
||||
sourcefileB: GetStarted
|
||||
defaults:
|
||||
run:
|
||||
@@ -34,9 +33,12 @@ jobs:
|
||||
run: git config --global user.name "automated action"
|
||||
- name: clone
|
||||
run: git clone --single-branch --branch $branch https://github.com/$username/$repo
|
||||
|
||||
|
||||
- name: commit
|
||||
run: git pull --rebase
|
||||
working-directory: ./dutchie032.github.io
|
||||
|
||||
- name: ls
|
||||
run: ls
|
||||
- name: Replace strings in file
|
||||
@@ -51,21 +53,32 @@ jobs:
|
||||
with open(fileName, "w") as f:
|
||||
f.write(newText)
|
||||
|
||||
with open("./spearhead/config.lua", "r") as luaFile:
|
||||
with open("./spearhead/config.lua", "r") as luaFile:
|
||||
luaConfig = luaFile.read()
|
||||
replace("./spearhead/_docs/Reference.md", "##!config!##", luaConfig)
|
||||
|
||||
lastUpdateTime = datetime.datetime.now().isoformat()
|
||||
replace("./spearhead/_docs/Reference.md", "##!lastupdatetime!##", lastUpdateTime)
|
||||
replace("./spearhead/_docs/GetStarted.md", "##!lastupdatetime!##", lastUpdateTime)
|
||||
menu = ""
|
||||
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
|
||||
|
||||
- name: mkdir
|
||||
run: mkdir -p ./$repo/Spearhead
|
||||
- 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/$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/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/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"
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
@@ -87,4 +100,5 @@ jobs:
|
||||
working-directory: ./dutchie032.github.io
|
||||
- name: push
|
||||
run: git push
|
||||
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
|
||||
working-directory: ./dutchie032.github.io
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"sumneko.lua"
|
||||
]
|
||||
}
|
||||
Vendored
+7
-1
@@ -12,6 +12,12 @@
|
||||
"coalition",
|
||||
"timer",
|
||||
"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")
|
||||
with open(mainFileName, 'r') as mainFile:
|
||||
compiled += "do --Main\n"
|
||||
string = mainFile.read()
|
||||
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:
|
||||
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/>
|
||||
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
|
||||
|
||||
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!##
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
<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.
|
||||
</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>
|
||||
<summary>MISSION_</summary>
|
||||
</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/>
|
||||
Everything is tied to at a stage. <br/>
|
||||
|
||||
|
||||
|
||||
## Mission
|
||||
|
||||
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
|
||||
---@return number
|
||||
o.getDeathDelay = function(self) return deathDelay end
|
||||
o.logLevel = Spearhead.LoggerTemplate.LogLevelOptions.INFO
|
||||
o.logLevel = "INFO"
|
||||
|
||||
return o;
|
||||
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 = {};
|
||||
|
||||
---comment
|
||||
---@return StageConfig
|
||||
function StageConfig:new()
|
||||
local o = {}
|
||||
setmetatable(o, { __index = self })
|
||||
|
||||
if SpearheadConfig == nil then SpearheadConfig = {} end
|
||||
if SpearheadConfig.StageConfig == nil then SpearheadConfig.StageConfig = {} end
|
||||
|
||||
local enabled = SpearheadConfig.StageConfig.enabled
|
||||
if enabled == nil then enabled = true end
|
||||
---@return boolean
|
||||
o.isEnabled = function(self) return enabled == true end
|
||||
---@type StageConfig
|
||||
local o = {
|
||||
isEnabled = SpearheadConfig.StageConfig.enabled or true,
|
||||
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 drawStages == nil then drawStages = true end
|
||||
---@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
|
||||
})
|
||||
if SpearheadConfig.StageConfig.debugEnabled == true then
|
||||
o.logLevel = "DEBUG"
|
||||
end
|
||||
|
||||
setmetatable(o, { __index = self })
|
||||
|
||||
return o;
|
||||
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)
|
||||
|
||||
local logger = Spearhead.LoggerTemplate:new("CARRIERFLEET", Spearhead.LoggerTemplate.LogLevelOptions.INFO)
|
||||
local logger = Spearhead.LoggerTemplate:new("CARRIERFLEET", "INFO")
|
||||
|
||||
local all_groups = Spearhead.DcsUtil.getAllGroupNames()
|
||||
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
|
||||
+96
-46
@@ -78,8 +78,14 @@ do -- INIT UTIL
|
||||
---comment
|
||||
---@param str string
|
||||
---@param findable string
|
||||
---@param ignoreCase 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
|
||||
end
|
||||
|
||||
@@ -114,6 +120,14 @@ do -- INIT UTIL
|
||||
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
|
||||
---@param polygon table of pairs { x, z }
|
||||
---@param x number X location
|
||||
@@ -452,8 +466,8 @@ do -- INIT DCS_UTIL
|
||||
return StaticObject.getByName(groupName) ~= nil
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param groupName string destroy the given group
|
||||
---destroy the given group
|
||||
---@param groupName string
|
||||
function DCS_UTIL.DestroyGroup(groupName)
|
||||
if DCS_UTIL.IsGroupStatic(groupName) then
|
||||
local object = StaticObject.getByName(groupName)
|
||||
@@ -468,6 +482,22 @@ do -- INIT DCS_UTIL
|
||||
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
|
||||
---@param unit_names table unit 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
|
||||
---@param x number North South position
|
||||
---@param z number West East position
|
||||
---@param zone_name table zone names
|
||||
---@param zone_name string zone name
|
||||
---@return boolean result
|
||||
function DCS_UTIL.isPositionInZone(x, z, zone_name)
|
||||
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
||||
@@ -786,6 +816,37 @@ do -- INIT DCS_UTIL
|
||||
return result
|
||||
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
|
||||
--- location and route can be nil and will then use default route
|
||||
---@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 uncontrolled boolean? Sets the group to be uncontrolled on spawn
|
||||
---@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)
|
||||
if groupName == nil then
|
||||
return
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local template = DCS_UTIL.GetMizGroupOrDefault(groupName, nil)
|
||||
if template == nil then
|
||||
return nil
|
||||
return nil, nil
|
||||
end
|
||||
if template.category == DCS_UTIL.GroupCategory.STATIC then
|
||||
--TODO: Implement location and route stuff
|
||||
local spawn_template = template.group_template
|
||||
coalition.addStaticObject(template.country_id, spawn_template)
|
||||
return coalition.addStaticObject(template.country_id, spawn_template), true
|
||||
else
|
||||
local spawn_template = template.group_template
|
||||
if location ~= nil then
|
||||
@@ -833,7 +895,7 @@ do -- INIT DCS_UTIL
|
||||
spawn_template.uncontrolled = uncontrolled
|
||||
end
|
||||
local new_group = coalition.addGroup(template.country_id, template.category, spawn_template)
|
||||
return new_group
|
||||
return new_group, false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -843,11 +905,14 @@ do -- INIT DCS_UTIL
|
||||
bingoSetting = bingoSetting + offset
|
||||
|
||||
local group = Group.getByName(groupName)
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then
|
||||
return true
|
||||
if group then
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -857,22 +922,25 @@ Spearhead.DcsUtil = DCS_UTIL
|
||||
|
||||
local LOGGER = {}
|
||||
do
|
||||
LOGGER.LogLevelOptions = {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERROR = 3,
|
||||
NONE = 4
|
||||
}
|
||||
|
||||
|
||||
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 = {}
|
||||
setmetatable(o, { __index = self })
|
||||
o.LoggerName = logger_name or "(loggername not set)"
|
||||
o.LogLevel = logLevel or LOGGER.LogLevelOptions.INFO
|
||||
o.LiveLoggingLevel = liveLoggingLevel or LOGGER.LogLevelOptions.NONE
|
||||
o.LogLevel = logLevel or "INFO"
|
||||
|
||||
---comment
|
||||
---@param self table self logger
|
||||
@@ -883,13 +951,9 @@ do
|
||||
end
|
||||
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)
|
||||
end
|
||||
|
||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.INFO then
|
||||
trigger.action.outText(message, 20)
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
@@ -900,12 +964,8 @@ do
|
||||
end
|
||||
message = UTIL.toString(message)
|
||||
|
||||
if self.LogLevel <= LOGGER.LogLevelOptions.WARN then
|
||||
env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||
end
|
||||
|
||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.WARN then
|
||||
trigger.action.outText(message, 20)
|
||||
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then
|
||||
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -919,12 +979,8 @@ do
|
||||
|
||||
message = UTIL.toString(message)
|
||||
|
||||
if self.LogLevel <= LOGGER.LogLevelOptions.ERROR then
|
||||
env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||
end
|
||||
|
||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.ERROR then
|
||||
trigger.action.outText(message, 20)
|
||||
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.logLevel == "ERROR" then
|
||||
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -937,13 +993,9 @@ do
|
||||
end
|
||||
|
||||
message = UTIL.toString(message)
|
||||
if self.LogLevel <= LOGGER.LogLevelOptions.DEBUG then
|
||||
if self.LogLevel == "DEBUG" then
|
||||
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message)
|
||||
end
|
||||
|
||||
if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.DEBUG then
|
||||
trigger.action.outText(message, 20)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -957,15 +1009,13 @@ function Spearhead.AddMissionEditorWarning(warningMessage)
|
||||
table.insert(Spearhead.MissionEditingWarnings, warningMessage or "skip")
|
||||
end
|
||||
|
||||
missionCommands.addSubMenu("Missions")
|
||||
|
||||
local loadDone = false
|
||||
Spearhead.LoadingDone = function()
|
||||
if loadDone == true then
|
||||
return
|
||||
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
|
||||
for key, message in pairs(Spearhead.MissionEditingWarnings) do
|
||||
warningLogger:warn(message)
|
||||
|
||||
+600
-596
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,39 @@
|
||||
|
||||
local SpearheadEvents = {}
|
||||
do
|
||||
|
||||
---@type Logger
|
||||
local logger = nil
|
||||
|
||||
---@param logLevel LogLevel
|
||||
SpearheadEvents.Init = function(logLevel)
|
||||
logger = Spearhead.LoggerTemplate:new("Events", logLevel)
|
||||
end
|
||||
|
||||
|
||||
local warn = function(text)
|
||||
if logger then
|
||||
logger:warn(text)
|
||||
end
|
||||
end
|
||||
|
||||
local logError = function(text)
|
||||
if logger then logger:error(text) 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 = {}
|
||||
|
||||
|
||||
local warn = function(text)
|
||||
env.warn("[Spearhead][Events] " .. (text or "nil"))
|
||||
end
|
||||
|
||||
local error = function(text)
|
||||
env.error("[Spearhead][Events] " .. (text or "nil"))
|
||||
end
|
||||
|
||||
---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)
|
||||
if type(listener) ~= "table" then
|
||||
warn("Event listener not of type table, did you mean to use handler?")
|
||||
return
|
||||
end
|
||||
table.insert(OnStageNumberChangedListeners, listener)
|
||||
end
|
||||
|
||||
@@ -36,21 +49,27 @@ do
|
||||
|
||||
---@param newStageNumber number
|
||||
SpearheadEvents.PublishStageNumberChanged = function(newStageNumber)
|
||||
pcall(function ()
|
||||
Spearhead.classes.persistence.Persistence.SetActiveStage(newStageNumber)
|
||||
end)
|
||||
|
||||
for _, callable in pairs(OnStageNumberChangedListeners) do
|
||||
local succ, err = pcall(function()
|
||||
callable:OnStageNumberChanged(newStageNumber)
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
|
||||
for _, callable in pairs(OnStageNumberChangedHandlers) do
|
||||
local succ, err = pcall(callable, newStageNumber)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
|
||||
Spearhead.StageNumber = newStageNumber
|
||||
end
|
||||
end
|
||||
|
||||
@@ -72,11 +91,15 @@ do
|
||||
table.insert(onLandEventListeners[unitName], landListener)
|
||||
end
|
||||
|
||||
---@class OnUnitLostListener
|
||||
---@field OnUnitLost fun(self:OnUnitLostListener, unit:table)
|
||||
|
||||
---@type table<string,Array<OnUnitLostListener>>
|
||||
local OnUnitLostListeners = {}
|
||||
---This listener gets fired for any event that can indicate a loss of a unit.
|
||||
---Such as: Eject, Crash, Dead, Unit_Lost,
|
||||
---@param unitName any
|
||||
---@param unitLostListener table Object with function: OnUnitLost(initiatorUnit)
|
||||
---@param unitLostListener OnUnitLostListener
|
||||
SpearheadEvents.addOnUnitLostEventListener = function(unitName, unitLostListener)
|
||||
if type(unitLostListener) ~= "table" then
|
||||
warn("Unit lost Event listener not of type table/object")
|
||||
@@ -119,7 +142,7 @@ do
|
||||
callable:OnGroupRTB(groupName)
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -154,7 +177,7 @@ do
|
||||
callable:OnGroupRTBInTen(groupName)
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -190,7 +213,7 @@ do
|
||||
callable:OnGroupOnStation(groupName)
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -198,39 +221,6 @@ do
|
||||
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
|
||||
local playerEnterUnitListeners = {}
|
||||
---comment
|
||||
@@ -252,7 +242,7 @@ do
|
||||
callable:OnPlayerEntersUnit(unit)
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -260,6 +250,20 @@ do
|
||||
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 = {}
|
||||
function e:onEvent(event)
|
||||
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)
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -285,6 +289,11 @@ do
|
||||
event.id == world.event.S_EVENT_EJECTION or
|
||||
event.id == world.event.S_EVENT_UNIT_LOST then
|
||||
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
|
||||
for _, callable in pairs(OnUnitLostListeners[object:getName()]) do
|
||||
local succ, err = pcall(function()
|
||||
@@ -292,12 +301,20 @@ do
|
||||
end)
|
||||
|
||||
if err then
|
||||
error(err)
|
||||
logError(err)
|
||||
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 function CheckAndTriggerSpawnAsync(unit, time)
|
||||
|
||||
@@ -1,47 +1,221 @@
|
||||
|
||||
|
||||
local StagesByName = {}
|
||||
|
||||
---@type table<string, Array<Stage>>
|
||||
local StagesByIndex = {}
|
||||
|
||||
---@type table<string, Array<Stage>>
|
||||
local SideStageByIndex = {}
|
||||
|
||||
---@type table<string, Array<WaitingStage>>
|
||||
local WaitingStagesByIndex = {}
|
||||
|
||||
local currentStage = -99
|
||||
|
||||
|
||||
GlobalStageManager = {}
|
||||
|
||||
---comment
|
||||
---@param database Database
|
||||
---@param stageConfig StageConfig
|
||||
---@return nil
|
||||
function GlobalStageManager:NewAndStart(database, stageConfig)
|
||||
local logger = Spearhead.LoggerTemplate:new("StageManager", stageConfig.logLevel)
|
||||
logger:info("Using Stage Log Level: " .. stageConfig.logLevel)
|
||||
local o = {}
|
||||
setmetatable(o, { __index = self })
|
||||
|
||||
o.logger = logger
|
||||
|
||||
o.onStageCompleted = function(self, stage)
|
||||
local stageNumber = tostring(stage.stageNumber)
|
||||
local anyActive = false
|
||||
for _, stage in pairs(StagesByIndex[stageNumber] or {}) do
|
||||
if stage.isActive then anyActive = true end
|
||||
end
|
||||
|
||||
if anyActive == false and stageConfig:isAutoStages() == true then
|
||||
Spearhead.Events.PublishStageNumberChanged(tonumber(stageNumber) + 1)
|
||||
end
|
||||
|
||||
if stageConfig.isAutoStages ~= true then
|
||||
logger:warn("Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message")
|
||||
end
|
||||
|
||||
---@type OnStageChangedListener
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
local stagelogger = Spearhead.LoggerTemplate:new(stageName, stageConfig.logLevel)
|
||||
local stage = Spearhead.internal.Stage:new(stageName, database, stagelogger, stageConfig)
|
||||
if Spearhead.Util.startswith(stageName, "missionstage", true) then
|
||||
local valid = true
|
||||
local split = Spearhead.Util.split_string(stageName, "_")
|
||||
if Spearhead.Util.tableLength(split) < 2 then
|
||||
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format")
|
||||
valid = false
|
||||
end
|
||||
|
||||
if stage then
|
||||
stage:AddStageCompleteListener(o);
|
||||
StagesByName[stageName] = stage
|
||||
local indexString = tostring(stage.stageNumber)
|
||||
if StagesByIndex[indexString] == nil then StagesByIndex[indexString] = {} end
|
||||
table.insert(StagesByIndex[indexString], stage)
|
||||
logger:info("Initiated " .. Spearhead.Util.tableLength(StagesByName) .. " airbases for cap")
|
||||
if Spearhead.Util.tableLength(split) < 3 then
|
||||
Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a stage name")
|
||||
valid = false
|
||||
end
|
||||
|
||||
local orderNumber = nil
|
||||
local isSideStage = false
|
||||
if valid == true then
|
||||
local orderNumberString = string.lower(split[2])
|
||||
if Spearhead.Util.startswith(orderNumberString, "x") == true then
|
||||
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
|
||||
|
||||
return o
|
||||
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
|
||||
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
|
||||
deathDelay = 1800, -- default 1800
|
||||
},
|
||||
|
||||
StageConfig = {
|
||||
|
||||
-- management of stages and its missions.
|
||||
@@ -40,7 +39,7 @@ SpearheadConfig = {
|
||||
enabled = true, -- default true
|
||||
|
||||
--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.
|
||||
-- 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,
|
||||
|
||||
--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"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-17
@@ -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"))()
|
||||
assert(loadfile(basePath .. "spearhead_routeutil.lua"))()
|
||||
assert(loadfile(basePath .. "spearhead_events.lua"))()
|
||||
assert(loadfile(basePath .. "spearhead_db.lua"))()
|
||||
local basePath = "C:\\Repos\\DCS\\Spearhead\\"
|
||||
local classPath = basePath .. "classes\\"
|
||||
|
||||
assert(loadfile(basePath .. "fleetClasses\\FleetGroup.lua"))()
|
||||
assert(loadfile(basePath .. "fleetClasses\\GlobalFleetManager.lua"))()
|
||||
assert(loadfile(classPath .. "spearhead_base.lua"))()
|
||||
assert(loadfile(classPath .. "spearhead_routeutil.lua"))()
|
||||
assert(loadfile(classPath .. "spearhead_events.lua"))()
|
||||
assert(loadfile(classPath .. "spearhead_db.lua"))()
|
||||
|
||||
assert(loadfile(basePath .. "configuration\\CapConfig.lua"))()
|
||||
assert(loadfile(basePath .. "configuration\\StageConfig.lua"))()
|
||||
assert(loadfile(classPath .. "fleetClasses\\FleetGroup.lua"))()
|
||||
assert(loadfile(classPath .. "fleetClasses\\GlobalFleetManager.lua"))()
|
||||
|
||||
assert(loadfile(basePath .. "stageClasses\\GlobalStageManager.lua"))()
|
||||
assert(loadfile(basePath .. "stageClasses\\Mission.lua"))()
|
||||
assert(loadfile(basePath .. "stageClasses\\Stage.lua"))()
|
||||
assert(loadfile(classPath .. "configuration\\CapConfig.lua"))()
|
||||
assert(loadfile(classPath .. "configuration\\StageConfig.lua"))()
|
||||
|
||||
assert(loadfile(basePath .. "capClasses\\CapGroup.lua"))()
|
||||
assert(loadfile(basePath .. "capClasses\\GlobalCapManager.lua"))()
|
||||
assert(loadfile(basePath .. "capClasses\\CapAirbase.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\GlobalStageManager.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\Missions\\Mission.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:
|
||||
|
||||
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
|
||||
|
||||
|
||||
local debug = false
|
||||
local id = net.get_my_player_id()
|
||||
if id == 0 then
|
||||
debug = true
|
||||
end
|
||||
|
||||
local dbLogger = Spearhead.LoggerTemplate:new("database", Spearhead.LoggerTemplate.LogLevelOptions.INFO)
|
||||
local standardLogger = Spearhead.LoggerTemplate:new("", Spearhead.LoggerTemplate.LogLevelOptions.INFO)
|
||||
local startTime = timer.getTime() * 1000
|
||||
|
||||
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 capConfig = Spearhead.internal.configuration.CapConfig: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.GlobalStageManager:NewAndStart(databaseManager, stageConfig)
|
||||
@@ -26,11 +43,14 @@ local SetStageDelayed = function(number, time)
|
||||
return nil
|
||||
end
|
||||
|
||||
local startingStage = stageConfig:getStartingStage() or 1
|
||||
|
||||
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()
|
||||
|
||||
--Check lines of code in directory per file:
|
||||
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; && GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
|
||||
-- find . -name '*.lua' | xargs wc -l
|
||||
@@ -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)
|
||||
|
||||
-- end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user