Work in progress refactor for toolkit

This commit is contained in:
2026-03-06 19:07:54 +01:00
parent 97f68389d4
commit d77279328e
61 changed files with 1987 additions and 3375 deletions
Vendored
BIN
View File
Binary file not shown.
+3 -1
View File
@@ -1,2 +1,4 @@
**/.DS_Store **/.DS_Store
.DS_Store .DS_Store
/dist
+4 -1
View File
@@ -16,5 +16,8 @@
"disableFolding": false "disableFolding": false
} }
}, },
"livePreview.serverRoot": "/_docs" "livePreview.serverRoot": "/_docs",
"Lua.workspace.library": [
"/Users/ex61wi/Developer/personal/DcsMissionScriptingTools/dutchies-dcs-scripting-tools/lua-addons"
]
} }
-1349
View File
File diff suppressed because it is too large Load Diff
@@ -1,22 +0,0 @@
local GlobalFleetManager = {}
local fleetGroups = {}
GlobalFleetManager.start = function(database)
local logger = Spearhead.LoggerTemplate.new("CARRIERFLEET", "INFO")
local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames()
for _, groupName in pairs(all_groups) do
if Spearhead.Util.startswith(string.lower(groupName), "carriergroup" ) == true then
logger:info("Registering " .. groupName .. " as a managed fleet")
local carrierGroup = Spearhead.internal.FleetGroup:new(groupName, database, logger)
table.insert(fleetGroups, carrierGroup)
end
end
end
if not Spearhead.internal then Spearhead.internal = {} end
Spearhead.internal.GlobalFleetManager = GlobalFleetManager
-82
View File
@@ -1,82 +0,0 @@
--Single player purpose
local defaultLogLevel = "INFO"
if SpearheadConfig and SpearheadConfig.debugEnabled == true then
defaultLogLevel = "DEBUG"
end
local startTime = timer.getTime() * 1000
Spearhead.Events.Init(defaultLogLevel)
local dbLogger = Spearhead.LoggerTemplate.new("database", defaultLogLevel)
local standardLogger = Spearhead.LoggerTemplate.new("", defaultLogLevel)
local databaseManager = Spearhead.DB.New(dbLogger)
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(defaultLogLevel) -- initiate
local capConfig = Spearhead.internal.configuration.CapConfig:new();
local stageConfig = Spearhead.internal.configuration.StageConfig:new();
local startingStage = stageConfig.startingStage or 1
if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then
standardLogger:info("Persistence enabled")
local persistenceLogger = Spearhead.LoggerTemplate.new("Persistence", defaultLogLevel)
Spearhead.classes.persistence.Persistence.Init(persistenceLogger)
local persistanceStage = Spearhead.classes.persistence.Persistence.GetActiveStage()
if persistanceStage then
standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage)
startingStage = persistanceStage
end
else
standardLogger:info("Persistence disabled")
end
local spawnLogger = Spearhead.LoggerTemplate.new("SpawnManager", defaultLogLevel)
local spawnManager = Spearhead.classes.helpers.SpawnManager.new(spawnLogger)
local detectionLogger = Spearhead.LoggerTemplate.new("DetectionManager", defaultLogLevel)
local detectionManager = Spearhead.classes.capClasses.detection.DetectionManager.New(detectionLogger)
Spearhead.classes.capClasses.GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
Spearhead.internal.GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
Spearhead.internal.GlobalFleetManager.start(databaseManager)
local SetStageDelayed = function(number, time)
Spearhead.Events.PublishStageNumberChanged(number)
return nil
end
timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3)
env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms")
local duration = (timer.getTime() * 1000) - startTime
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
Spearhead.LoadingDone()
Spearhead.internal.GlobalStageManager:printFullOverview()
--Check lines of code in directory per file:
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
-- find . -name '*.lua' | xargs wc -l
--- ==================== DEBUG ORDER OR ZONE VEC ===========================
-- local zone = Spearhead.DcsUtil.getZoneByName("MISSIONSTAGE_99")
-- local count = Spearhead.Util.tableLength(zone.verts)
-- for i = 1, count - 1 do
-- local a = zone.verts[i]
-- local b = zone.verts[i+1]
-- local color = {0,0,0,1}
-- color[i] = 1
-- trigger.action.textToAll(-1, 46+i , { x= a.x, y = 0, z = a.z } , color, {0,0,0}, 24 , true , "" .. i )
-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true)
-- end
@@ -44,7 +44,4 @@ function Queue:toList()
return items return items
end end
return Queue
if Spearhead == nil then Spearhead = {} end
if Spearhead._baseClasses == nil then Spearhead._baseClasses = {} end
Spearhead._baseClasses.Queue = Queue
@@ -1,3 +1,6 @@
local SpearheadEvents = require("classes.spearhead_events")
local GlobalStageManager = require("classes.stageClasses.GlobalStageManager")
---@type Array<OnMissionCompleteListener> ---@type Array<OnMissionCompleteListener>
local MissionCompleteListeners = {} local MissionCompleteListeners = {}
@@ -16,18 +19,18 @@ SpearheadAPI = {
return false, "stageNumber " .. stageNumber .. " is not a valid number" return false, "stageNumber " .. stageNumber .. " is not a valid number"
end end
Spearhead.Events.PublishStageNumberChanged(stageNumber) SpearheadEvents.PublishStageNumberChanged(stageNumber)
return true, "" return true, ""
end, end,
getCurrentStage = function() getCurrentStage = function()
return Spearhead.StageNumber or nil return GlobalStageManager.getCurrentStage() or nil
end, end,
isStageComplete = function(stageNumber) isStageComplete = function(stageNumber)
if type(stageNumber) ~= "number" then if type(stageNumber) ~= "number" then
return false, "stageNumber " .. stageNumber .. " is not a valid number" return false, "stageNumber " .. stageNumber .. " is not a valid number"
end end
local isComplete = Spearhead.internal.GlobalStageManager.isStageComplete(stageNumber) local isComplete = GlobalStageManager.isStageComplete(stageNumber)
if isComplete == nil then if isComplete == nil then
return nil, "no stage found with number " .. stageNumber return nil, "no stage found with number " .. stageNumber
end end
@@ -1,3 +1,11 @@
local Util = require("classes.util.Util")
local CapGroup = require("classes.capClasses.airGroups.CapGroup")
local SweepGroup = require("classes.capClasses.airGroups.SweepGroup")
local InterceptGroup = require("classes.capClasses.airGroups.InterceptGroup")
local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker")
local SpearheadEvents = require("classes.util.SpearheadEvents")
local RunwayStrikeMission = require("classes.stageClasses.missions.RunwayStrikeMission")
---@class CapBase : OnStageChangedListener ---@class CapBase : OnStageChangedListener
---@field private airbaseName string ---@field private airbaseName string
---@field private logger table ---@field private logger table
@@ -54,7 +62,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
local baseData = database:getAirbaseDataForZone(airbaseName) local baseData = database:getAirbaseDataForZone(airbaseName)
if baseData and baseData.CapGroups then if baseData and baseData.CapGroups then
for key, name in pairs(baseData.CapGroups) do for key, name in pairs(baseData.CapGroups) do
local capGroup = Spearhead.classes.capClasses.airGroups.CapGroup.New(name, capConfig, logger, spawnManager) local capGroup = CapGroup.New(name, capConfig, logger, spawnManager)
if capGroup then if capGroup then
self.capGroupsByName[name] = capGroup self.capGroupsByName[name] = capGroup
end end
@@ -63,7 +71,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
if baseData and baseData.SweepGroups then if baseData and baseData.SweepGroups then
for key, name in pairs(baseData.SweepGroups) do for key, name in pairs(baseData.SweepGroups) do
local sweepGroup = Spearhead.classes.capClasses.airGroups.SweepGroup.New(name, capConfig, logger, spawnManager) local sweepGroup = SweepGroup.New(name, capConfig, logger, spawnManager)
if sweepGroup then if sweepGroup then
self.sweepGroupsByName[name] = sweepGroup self.sweepGroupsByName[name] = sweepGroup
end end
@@ -72,22 +80,21 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
if baseData and baseData.InterceptGroups then if baseData and baseData.InterceptGroups then
for key, name in pairs(baseData.InterceptGroups) do for key, name in pairs(baseData.InterceptGroups) do
local interceptGroup = Spearhead.classes.capClasses.airGroups.InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager) local interceptGroup = InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager)
if interceptGroup then if interceptGroup then
self.interceptGroupsByName[name] = interceptGroup self.interceptGroupsByName[name] = interceptGroup
end end
end end
end end
local capFlights = Spearhead.Util.tableLength(self.capGroupsByName) local capFlights = Util.tableLength(self.capGroupsByName)
local sweepFlights = Spearhead.Util.tableLength(self.sweepGroupsByName) local sweepFlights = Util.tableLength(self.sweepGroupsByName)
local interceptFlights = Spearhead.Util.tableLength(self.interceptGroupsByName) local interceptFlights = Util.tableLength(self.interceptGroupsByName)
logger:info(airbaseName .. " : " .. capFlights .." CAP | " .. sweepFlights .. " SWEEP | " .. interceptFlights .. " INTERCEPT") logger:info(airbaseName .. " : " .. capFlights .." CAP | " .. sweepFlights .. " SWEEP | " .. interceptFlights .. " INTERCEPT")
self:CreateRunwayStrikeMission(database) self:CreateRunwayStrikeMission(database)
SpearheadEvents.AddStageNumberChangedListener(self)
Spearhead.Events.AddStageNumberChangedListener(self)
timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15) timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15)
@@ -111,7 +118,7 @@ function CapBase:CreateRunwayStrikeMission(database)
" at airbase " .. " at airbase " ..
self.airbaseName .. self.airbaseName ..
" with heading " .. runway.course .. " and length " .. runway.length .. " and width " .. runway.width) " with heading " .. runway.course .. " and length " .. runway.length .. " and width " .. runway.width)
local mission = Spearhead.classes.stageClasses.missions.RunwayStrikeMission.new(runway, self.airbaseName, local mission = RunwayStrikeMission.new(runway, self.airbaseName,
database, self.logger, self.runwayBombingTracker) database, self.logger, self.runwayBombingTracker)
self.runwayStrikeMissions[runway.Name] = mission self.runwayStrikeMissions[runway.Name] = mission
end end
@@ -338,7 +345,7 @@ function CapBase:CheckAndScheduleIntercept()
local unit = Unit.getByName(unitName) local unit = Unit.getByName(unitName)
if unit and unit:isExist() then if unit and unit:isExist() then
local unitPos = unit:getPoint() local unitPos = unit:getPoint()
if Spearhead.Util.is3dPointInZone(unitPos, zone) == true then if Util.is3dPointInZone(unitPos, zone) == true then
if not unitsToInterceptPerZone[zone.name] then if not unitsToInterceptPerZone[zone.name] then
unitsToInterceptPerZone[zone.name] = {} unitsToInterceptPerZone[zone.name] = {}
end end
@@ -356,7 +363,7 @@ function CapBase:CheckAndScheduleIntercept()
for name, targets in pairs(unitsToInterceptPerZone) do for name, targets in pairs(unitsToInterceptPerZone) do
local required = 0 local required = 0
local nrTargets = Spearhead.Util.tableLength(targets) local nrTargets = Util.tableLength(targets)
if targets and nrTargets > 0 then if targets and nrTargets > 0 then
required = math.ceil(nrTargets / ratio) required = math.ceil(nrTargets / ratio)
end end
@@ -424,7 +431,4 @@ function CapBase:IsBaseActiveWhenStageIsActive(stageNumber)
return false return false
end end
if not Spearhead then Spearhead = {} end return CapBase
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
Spearhead.classes.capClasses.CapAirbase = CapBase
@@ -1,3 +1,9 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker")
local CapAirbase = require("classes.capClasses.CapAirbase")
---@class GlobalCapManager
local GlobalCapManager = {} local GlobalCapManager = {}
do do
local airbasesPerStage = {} local airbasesPerStage = {}
@@ -17,9 +23,9 @@ do
function GlobalCapManager.start(database, capConfig, detectionManager, stageConfig, logLevel, spawnManager) function GlobalCapManager.start(database, capConfig, detectionManager, stageConfig, logLevel, spawnManager)
if initiated == true then return end if initiated == true then return end
local logger = Spearhead.LoggerTemplate.new("AirbaseManager", logLevel) local logger = Logger.new("AirbaseManager", logLevel)
local bombTrackLogger = Spearhead.LoggerTemplate.new("RunwayBombingTracker", logLevel) local bombTrackLogger = Logger.new("RunwayBombingTracker", logLevel)
local runwayBombingTracker = Spearhead.classes.capClasses.runwayBombing.RunwayBombingTracker.new(bombTrackLogger) local runwayBombingTracker = RunwayBombingTracker.new(bombTrackLogger)
local zones = database:getStagezoneNames() local zones = database:getStagezoneNames()
if zones then if zones then
@@ -32,9 +38,9 @@ do
if airbaseNames then if airbaseNames then
for _, airbaseName in pairs(airbaseNames) do for _, airbaseName in pairs(airbaseNames) do
if airbaseName then if airbaseName then
local airbaseSpecificLogger = Spearhead.LoggerTemplate.new("CAP_" .. airbaseName, logLevel) local airbaseSpecificLogger = Logger.new("CAP_" .. airbaseName, logLevel)
local airbase = Spearhead.classes.capClasses.CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager) local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager)
if airbase then if airbase then
table.insert(airbasesPerStage[stageName], airbase) table.insert(airbasesPerStage[stageName], airbase)
@@ -46,16 +52,14 @@ do
end end
end end
logger:info("Initiated " .. Spearhead.Util.tableLength(allAirbasesByName) .. " airbases for cap") logger:info("Initiated " .. Util.tableLength(allAirbasesByName) .. " airbases for cap")
initiated = true initiated = true
local InfoFunctions = {}
---returns if there is CAP active ---returns if there is CAP active
---@param zoneName any ---@param zoneName any
---@param activeZoneNumber number ---@param activeZoneNumber number
---@return boolean ---@return boolean
InfoFunctions.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber) GlobalCapManager.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber)
for _, airbase in pairs(airbasesPerStage[zoneName]) do for _, airbase in pairs(airbasesPerStage[zoneName]) do
if airbase:IsBaseActiveWhenStageIsActive(activeZoneNumber) == true then if airbase:IsBaseActiveWhenStageIsActive(activeZoneNumber) == true then
return true return true
@@ -63,14 +67,7 @@ do
end end
return false return false
end end
Spearhead.capInfo = InfoFunctions
end end
end end
return GlobalCapManager
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
Spearhead.classes.capClasses.GlobalCapManager = GlobalCapManager
@@ -1,3 +1,8 @@
local SpearheadEvents = require("classes.spearhead_events")
local RTBMission = require("classes.capClasses.taskings.RTB")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
---@class AirGroup : OnUnitLostListener ---@class AirGroup : OnUnitLostListener
---@field protected _logger Logger ---@field protected _logger Logger
---@field protected _groupName string ---@field protected _groupName string
@@ -26,13 +31,13 @@ function AirGroup:New(groupName, groupType, config, logger, spawnManager)
local group = Group.getByName(self._groupName) local group = Group.getByName(self._groupName)
if group then if group then
Spearhead.Events.addOnGroupRTBListener(self._groupName, self) SpearheadEvents.addOnGroupRTBListener(self._groupName, self)
Spearhead.Events.addOnGroupRTBInTenListener(self._groupName, self) SpearheadEvents.addOnGroupRTBInTenListener(self._groupName, self)
Spearhead.Events.addOnGroupOnStationListener(self._groupName, self) SpearheadEvents.addOnGroupOnStationListener(self._groupName, self)
for _, unit in pairs(group:getUnits()) do for _, unit in pairs(group:getUnits()) do
Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self) SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self)
Spearhead.Events.addOnUnitLandEventListener(unit:getName(), self) SpearheadEvents.addOnUnitLandEventListener(unit:getName(), self)
end end
spawnManager:DestroyGroup(groupName) spawnManager:DestroyGroup(groupName)
@@ -105,7 +110,7 @@ function AirGroup:SendRTB(airbase)
end end
end end
if location then if location then
local mission = Spearhead.classes.capClasses.taskings.RTB.getAsMission(airbase, { x= location.x, y= location.z }, self._config) local mission = RTBMission.getAsMission(airbase, { x= location.x, y= location.z }, self._config)
group:getController():setTask(mission) group:getController():setTask(mission)
self._logger:debug("AirGroup:SendRTB - Task set for group: " .. self._groupName) self._logger:debug("AirGroup:SendRTB - Task set for group: " .. self._groupName)
end end
@@ -248,7 +253,7 @@ function AirGroup:OnLastUnitLanded()
return time + 5 return time + 5
end end
if pos and Spearhead.Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then if pos and Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then
data.lastChangeTime = time data.lastChangeTime = time
end end
data.lastLocations[unit:getName()] = pos data.lastLocations[unit:getName()] = pos
@@ -426,14 +431,10 @@ do --EVENT LISTENERS
end end
function AirGroup:Destroy() function AirGroup:Destroy()
Spearhead.DcsUtil.DestroyGroup(self._groupName) self._spawnManager:DestroyGroup(self._groupName)
end end
if not Spearhead then Spearhead = {} end return AirGroup
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.AirGroup = AirGroup
---@alias AirGroupState ---@alias AirGroupState
---| "UnSpawned" ---| "UnSpawned"
@@ -1,3 +1,7 @@
local AirGroup = require("classes.capClasses.airGroups.AirGroup")
local CAP = require("classes.capClasses.taskings.CAP")
local Util = require("classes.util.Util")
local MissionEditorWarner = require("classes.util.MissionEditorWarnings")
---@class CapGroup : AirGroup ---@class CapGroup : AirGroup
---@field private _targetZoneIdPerStage table<string, string> ---@field private _targetZoneIdPerStage table<string, string>
@@ -14,9 +18,9 @@ CapGroup.__index = CapGroup
---@return CapGroup ---@return CapGroup
function CapGroup.New(groupName, config, logger, spawnManager) function CapGroup.New(groupName, config, logger, spawnManager)
setmetatable(CapGroup, Spearhead.classes.capClasses.airGroups.AirGroup) setmetatable(CapGroup, AirGroup)
local self = setmetatable({}, CapGroup) --[[@as CapGroup]] local self = setmetatable({}, CapGroup) --[[@as CapGroup]]
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "CAP", config, logger, spawnManager) AirGroup.New(self, groupName, "CAP", config, logger, spawnManager)
self._targetZoneIdPerStage = {} self._targetZoneIdPerStage = {}
@@ -65,10 +69,10 @@ function CapGroup:SendToZone(zone, targetZoneID, airbase)
end end
if isInAir == true then if isInAir == true then
local mission = Spearhead.classes.capClasses.taskings.CAP.getAsMission(self._groupName, airbase, zone, self._config) local mission = CAP.getAsMission(self._groupName, airbase, zone, self._config)
self:SetMission(mission) self:SetMission(mission)
else else
local mission = Spearhead.classes.capClasses.taskings.CAP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config) local mission = CAP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
if mission then if mission then
self:SetMission(mission) self:SetMission(mission)
else else
@@ -80,8 +84,8 @@ end
---@private ---@private
function CapGroup:InitWithName(groupName) function CapGroup:InitWithName(groupName)
local split_string = Spearhead.Util.split_string(groupName, "_") local split_string = Util.split_string(groupName, "_")
local partCount = Spearhead.Util.tableLength(split_string) local partCount = Util.tableLength(split_string)
if partCount >= 3 then if partCount >= 3 then
local configPart = split_string[2] local configPart = split_string[2]
@@ -95,34 +99,34 @@ function CapGroup:InitWithName(groupName)
elseif first == "[" then elseif first == "[" then
self._isBackup = false self._isBackup = false
else else
Spearhead.AddMissionEditorWarning("Could not parse the CAP config for group: " .. groupName) MissionEditorWarner.Add("Could not parse the CAP config for group: " .. groupName)
return return
end end
local subsplit = Spearhead.Util.split_string(configPart, "|") local subsplit = Util.split_string(configPart, "|")
if subsplit then if subsplit then
for key, value in pairs(subsplit) do for key, value in pairs(subsplit) do
local keySplit = Spearhead.Util.split_string(value, "]") local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2] local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1]) local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Spearhead.Util.split_string(allActives, ",") local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do for _, value in pairs(commaSeperated) do
local dashSeperated = Spearhead.Util.split_string(value, "-") local dashSeperated = Util.split_string(value, "-")
if Spearhead.Util.tableLength(dashSeperated) > 1 then if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1]) local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2]) local till = tonumber(dashSeperated[2])
for i = from, till do for i = from, till do
if Spearhead.Util.strContains(targetZone, "A") == true then if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i)) self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
else else
self._targetZoneIdPerStage[tostring(i)] = targetZone self._targetZoneIdPerStage[tostring(i)] = targetZone
end end
end end
else else
if Spearhead.Util.strContains(targetZone, "A") == true then if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1])) self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
else else
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
@@ -132,15 +136,11 @@ function CapGroup:InitWithName(groupName)
end end
end end
env.info("Capgroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage)) env.info("Capgroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage))
else else
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) MissionEditorWarner.Add("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
end end
end end
if not Spearhead then Spearhead = {} end return CapGroup
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.CapGroup = CapGroup
@@ -1,3 +1,9 @@
local AirGroup = require("classes.capClasses.airGroups.AirGroup")
local INTERCEPT = require("classes.capClasses.taskings.INTERCEPT")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local MissionEditorWarner = require("classes.util.MissionEditorWarnings")
---@class InterceptGroup : AirGroup ---@class InterceptGroup : AirGroup
---@field private _targetNames Array<string> ---@field private _targetNames Array<string>
---@field private _targetZoneName string ---@field private _targetZoneName string
@@ -22,9 +28,9 @@ InterceptGroup.__index = InterceptGroup
---@return InterceptGroup? ---@return InterceptGroup?
function InterceptGroup.New(groupName, config, logger, detectionManager, spawnManager) function InterceptGroup.New(groupName, config, logger, detectionManager, spawnManager)
setmetatable(InterceptGroup, Spearhead.classes.capClasses.airGroups.AirGroup) setmetatable(InterceptGroup, AirGroup)
local self = setmetatable({}, InterceptGroup) local self = setmetatable({}, InterceptGroup)
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "INTERCEPT", config, logger, spawnManager) AirGroup.New(self, groupName, "INTERCEPT", config, logger, spawnManager)
local group = Group.getByName(groupName) local group = Group.getByName(groupName)
if not group then if not group then
@@ -135,7 +141,7 @@ function InterceptGroup:GetClosestTarget()
local targetUnit = Unit.getByName(targetName) local targetUnit = Unit.getByName(targetName)
if targetUnit and targetUnit:isExist() then if targetUnit and targetUnit:isExist() then
local pos = targetUnit:getPoint() local pos = targetUnit:getPoint()
local distance = Spearhead.Util.VectorDistance3d(groupPoint, pos) local distance = Util.VectorDistance3d(groupPoint, pos)
if distance < closestDistance then if distance < closestDistance then
closestDistance = distance closestDistance = distance
closestUnit = targetUnit closestUnit = targetUnit
@@ -179,7 +185,7 @@ function InterceptGroup:UpdateTask()
end end
end end
if Spearhead.DcsUtil.IsBingoFuel(self._groupName) then if DcsUtil.IsBingoFuel(self._groupName) then
self._logger:debug("InterceptGroup: " .. self._groupName .. " is at bingo fuel, returning to base") self._logger:debug("InterceptGroup: " .. self._groupName .. " is at bingo fuel, returning to base")
self:SendRTB(self._airbase) self:SendRTB(self._airbase)
return nil -- Return to base if bingo fuel return nil -- Return to base if bingo fuel
@@ -187,7 +193,7 @@ function InterceptGroup:UpdateTask()
if selfDetected and self:IsInAir() == true then if selfDetected and self:IsInAir() == true then
if self._currentTargetName and self._currentTargetName == closestUnit:getName() then if self._currentTargetName and self._currentTargetName == closestUnit:getName() then
local distance = Spearhead.Util.VectorDistance3d(closestUnit:getPoint(), groupPoint) local distance = Util.VectorDistance3d(closestUnit:getPoint(), groupPoint)
if distance < 30 * 1852 then if distance < 30 * 1852 then
-- If the target is within 10 nautical miles, continue attacking -- If the target is within 10 nautical miles, continue attacking
self._logger:debug("InterceptGroup: " .. self._groupName .. " continues attacking target " .. closestUnit:getName()) self._logger:debug("InterceptGroup: " .. self._groupName .. " continues attacking target " .. closestUnit:getName())
@@ -199,7 +205,7 @@ function InterceptGroup:UpdateTask()
local vec3 = closestUnit:getPoint() local vec3 = closestUnit:getPoint()
local vec2 = { x = vec3.x, y = vec3.z } local vec2 = { x = vec3.x, y = vec3.z }
local groupPointVec2 = { x = groupPoint.x, y = groupPoint.z } local groupPointVec2 = { x = groupPoint.x, y = groupPoint.z }
local mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt) local mission = INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt)
self:SetMission(mission) self:SetMission(mission)
self._currentTargetName = closestUnit:getName() self._currentTargetName = closestUnit:getName()
return 15 -- Just continue attacking the detected target return 15 -- Just continue attacking the detected target
@@ -219,7 +225,7 @@ function InterceptGroup:UpdateTask()
local mission = nil local mission = nil
if self:IsInAir() == true then if self:IsInAir() == true then
-- If the group is in the air, create an intercept mission -- If the group is in the air, create an intercept mission
mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromInAir( mission = INTERCEPT.getMissionFromInAir(
self._groupName, self._groupName,
{ x = groupPoint.x, y = groupPoint.z }, { x = groupPoint.x, y = groupPoint.z },
interceptPoint, interceptPoint,
@@ -229,7 +235,7 @@ function InterceptGroup:UpdateTask()
alt alt
) )
else else
mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromAirbase( mission = INTERCEPT.getMissionFromAirbase(
self._groupName, self._groupName,
interceptPoint, interceptPoint,
self._airbase, self._airbase,
@@ -310,33 +316,33 @@ end
---@private ---@private
function InterceptGroup:InitWithName(groupName) function InterceptGroup:InitWithName(groupName)
local split_string = Spearhead.Util.split_string(groupName, "_") local split_string = Util.split_string(groupName, "_")
local partCount = Spearhead.Util.tableLength(split_string) local partCount = Util.tableLength(split_string)
if partCount >= 3 then if partCount >= 3 then
local configPart = split_string[2] local configPart = split_string[2]
configPart = string.sub(configPart, 2, #configPart) configPart = string.sub(configPart, 2, #configPart)
local subsplit = Spearhead.Util.split_string(configPart, "|") local subsplit = Util.split_string(configPart, "|")
if subsplit then if subsplit then
for key, value in pairs(subsplit) do for key, value in pairs(subsplit) do
local keySplit = Spearhead.Util.split_string(value, "]") local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2] local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1]) local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Spearhead.Util.split_string(allActives, ",") local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do for _, value in pairs(commaSeperated) do
local dashSeperated = Spearhead.Util.split_string(value, "-") local dashSeperated = Util.split_string(value, "-")
if Spearhead.Util.tableLength(dashSeperated) > 1 then if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1]) local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2]) local till = tonumber(dashSeperated[2])
for i = from, till do for i = from, till do
if Spearhead.Util.strContains(targetZone, "A") == true then if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i)) self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
else else
self._targetZoneIdPerStage[tostring(i)] = targetZone self._targetZoneIdPerStage[tostring(i)] = targetZone
end end
end end
else else
if Spearhead.Util.strContains(targetZone, "A") == true then if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1])) self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1]))
else else
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone
@@ -346,16 +352,12 @@ function InterceptGroup:InitWithName(groupName)
end end
end end
env.info("interceptGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage)) env.info("interceptGroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage))
else else
Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) MissionEditorWarner.Add("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount)
end end
end end
if not Spearhead then Spearhead = {} end return InterceptGroup
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.InterceptGroup = InterceptGroup
@@ -1,3 +1,8 @@
local AirGroup = require("classes.capClasses.airGroups.AirGroup")
local SWEEP = require("classes.capClasses.taskings.SWEEP")
local Util = require("classes.util.Util")
local MissionEditorWarner = require("classes.util.MissionEditorWarnings")
---@class SweepGroup : AirGroup ---@class SweepGroup : AirGroup
---@field _targetZoneIdPerStage table<number, string> ---@field _targetZoneIdPerStage table<number, string>
---@field _currentTargetZoneID string? ---@field _currentTargetZoneID string?
@@ -11,9 +16,9 @@ SweepGroup.__index = SweepGroup
---@param spawnManager SpawnManager ---@param spawnManager SpawnManager
---@return SweepGroup ---@return SweepGroup
function SweepGroup.New(groupName, config, logger, spawnManager) function SweepGroup.New(groupName, config, logger, spawnManager)
setmetatable(SweepGroup, Spearhead.classes.capClasses.airGroups.AirGroup) setmetatable(SweepGroup, AirGroup)
local self = setmetatable({}, SweepGroup) --[[@as SweepGroup]] local self = setmetatable({}, SweepGroup) --[[@as SweepGroup]]
Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "SWEEP", config, logger, spawnManager) AirGroup.New(self, groupName, "SWEEP", config, logger, spawnManager)
self._targetZoneIdPerStage = {} self._targetZoneIdPerStage = {}
self:InitWithName(groupName) self:InitWithName(groupName)
@@ -47,7 +52,7 @@ function SweepGroup:SendToZone(zone, targetZoneID, airbase)
local group = Group.getByName(self._groupName) local group = Group.getByName(self._groupName)
local mission = Spearhead.classes.capClasses.taskings.SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config) local mission = SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config)
if mission then if mission then
---@type SetTaskParams ---@type SetTaskParams
local params = { local params = {
@@ -68,35 +73,35 @@ end
---@private ---@private
function SweepGroup:InitWithName(groupName) function SweepGroup:InitWithName(groupName)
local split_string = Spearhead.Util.split_string(groupName, "_") local split_string = Util.split_string(groupName, "_")
local partCount = Spearhead.Util.tableLength(split_string) local partCount = Util.tableLength(split_string)
if partCount >= 3 then if partCount >= 3 then
local configPart = split_string[2] local configPart = split_string[2]
configPart = string.sub(configPart, 2, #configPart) configPart = string.sub(configPart, 2, #configPart)
local subsplit = Spearhead.Util.split_string(configPart, "|") local subsplit = Util.split_string(configPart, "|")
if subsplit then if subsplit then
for key, value in pairs(subsplit) do for key, value in pairs(subsplit) do
local keySplit = Spearhead.Util.split_string(value, "]") local keySplit = Util.split_string(value, "]")
local targetZone = keySplit[2] local targetZone = keySplit[2]
local allActives = string.sub(keySplit[1], 2, #keySplit[1]) local allActives = string.sub(keySplit[1], 2, #keySplit[1])
local commaSeperated = Spearhead.Util.split_string(allActives, ",") local commaSeperated = Util.split_string(allActives, ",")
for _, value in pairs(commaSeperated) do for _, value in pairs(commaSeperated) do
local dashSeperated = Spearhead.Util.split_string(value, "-") local dashSeperated = Util.split_string(value, "-")
if Spearhead.Util.tableLength(dashSeperated) > 1 then if Util.tableLength(dashSeperated) > 1 then
local from = tonumber(dashSeperated[1]) local from = tonumber(dashSeperated[1])
local till = tonumber(dashSeperated[2]) local till = tonumber(dashSeperated[2])
for i = from, till do for i = from, till do
if Spearhead.Util.strContains(targetZone, "A") == true then if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i)) self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i))
else else
self._targetZoneIdPerStage[tostring(i)] = targetZone self._targetZoneIdPerStage[tostring(i)] = targetZone
end end
end end
else else
if Spearhead.Util.strContains(targetZone, "A") == true then if Util.strContains(targetZone, "A") == true then
self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A",
tostring(dashSeperated[1])) tostring(dashSeperated[1]))
else else
@@ -107,15 +112,11 @@ function SweepGroup:InitWithName(groupName)
end end
end end
env.info("SweepGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage)) env.info("SweepGroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage))
else else
Spearhead.AddMissionEditorWarning("SWEEP Group with name: " .. MissionEditorWarner.Add("SWEEP Group with name: " ..
groupName .. "should have at least 3 parts, but has " .. partCount) groupName .. "should have at least 3 parts, but has " .. partCount)
end end
end end
if not Spearhead then Spearhead = {} end return SweepGroup
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end
Spearhead.classes.capClasses.airGroups.SweepGroup = SweepGroup
@@ -161,8 +161,4 @@ function DetectionManager:UpdateDetectedUnitsByCoalition(coalitionSide)
end end
end end
if not Spearhead then Spearhead = {} end return DetectionManager
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.detection then Spearhead.classes.capClasses.detection = {} end
Spearhead.classes.capClasses.detection.DetectionManager = DetectionManager
@@ -1,3 +1,6 @@
local SpearheadEvents = require("classes.spearhead_events")
local Util = require("classes.util.Util")
---@class RunwayBombingTracker : OnWeaponFiredListener ---@class RunwayBombingTracker : OnWeaponFiredListener
---@field trackedRunways table<Runway, RunwayStrikeMission> ---@field trackedRunways table<Runway, RunwayStrikeMission>
---@field private _logger Logger ---@field private _logger Logger
@@ -11,8 +14,7 @@ function RunwayBombingTracker.new(logger)
local self = setmetatable({}, RunwayBombingTracker) local self = setmetatable({}, RunwayBombingTracker)
self._logger = logger self._logger = logger
SpearheadEvents.AddWeaponFiredListener(self)
Spearhead.Events.AddWeaponFiredListener(self)
return self return self
end end
@@ -111,15 +113,10 @@ function RunwayBombingTracker:OnWeaponImpact(weaponDesc, impactPoint)
local zone= strikeMission:GetRunwayZone() local zone= strikeMission:GetRunwayZone()
if Spearhead.Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then
strikeMission:RunwayHit(impactPoint, explosiveMass) strikeMission:RunwayHit(impactPoint, explosiveMass)
end end
end end
end end
return RunwayBombingTracker
if not Spearhead then Spearhead = {} end
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.runwayBombing then Spearhead.classes.capClasses.runwayBombing = {} end
Spearhead.classes.capClasses.runwayBombing.RunwayBombingTracker = RunwayBombingTracker
@@ -1,3 +1,6 @@
local Util = require("classes.util.Util")
local RTB = require("classes.capClasses.taskings.RTB")
---@class CAPTasking ---@class CAPTasking
local CAP = {} local CAP = {}
@@ -38,7 +41,7 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
for indexA, pointA in ipairs(capZone.verts) do for indexA, pointA in ipairs(capZone.verts) do
for indexB, pointB in ipairs(capZone.verts) do for indexB, pointB in ipairs(capZone.verts) do
if pointA ~= pointB then if pointA ~= pointB then
local distance = Spearhead.Util.VectorDistance2d(pointA, pointB) local distance = Util.VectorDistance2d(pointA, pointB)
if distance > furthestDistance then if distance > furthestDistance then
furthestDistance = distance furthestDistance = distance
furthestA = indexA furthestA = indexA
@@ -58,12 +61,12 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
local furthest = pointA local furthest = pointA
local closest = pointB local closest = pointB
local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB) local heading = Util.vectorHeadingFromTo(pointA, pointB)
if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then if Util.VectorDistance2d(baseVec2, pointB) > Util.VectorDistance2d(baseVec2, pointA) then
furthest = pointB furthest = pointB
closest = pointA closest = pointA
heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA) heading = Util.vectorHeadingFromTo(pointB, pointA)
end end
local distance = furthestDistance local distance = furthestDistance
@@ -87,8 +90,8 @@ end
local GetOutboundTask = function(airbase, capZone, capConfig) local GetOutboundTask = function(airbase, capZone, capConfig)
local airbaseVec3 = airbase:getPoint() local airbaseVec3 = airbase:getPoint()
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z } local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location) local heading = Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520) local point = Util.vectorMove(airbaseVec2, heading, 18520)
return { return {
alt = 2000, alt = 2000,
@@ -130,9 +133,9 @@ function CAP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig)
[1] = GetOutboundTask(airbase, capZone, capConfig), [1] = GetOutboundTask(airbase, capZone, capConfig),
[2] = GetOutboundTask(airbase, capZone, capConfig), [2] = GetOutboundTask(airbase, capZone, capConfig),
[3] = CAP.getAsTasking(groupName, airbase, capZone, capConfig), [3] = CAP.getAsTasking(groupName, airbase, capZone, capConfig),
[4] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig), [4] = RTB.getApproachPoint(airbase, capZone.location, capConfig),
[5] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), [5] = RTB.getInitialPoint(airbase),
[6] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) [6] = RTB.getLandingPoint(airbase)
} }
local mission = { local mission = {
@@ -155,9 +158,9 @@ end
function CAP.getAsMission(groupName, airbase, capZone, capConfig) function CAP.getAsMission(groupName, airbase, capZone, capConfig)
local points = { local points = {
[1] = CAP.getAsTasking(groupName, airbase, capZone, capConfig), [1] = CAP.getAsTasking(groupName, airbase, capZone, capConfig),
[2] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig), [2] = RTB.getApproachPoint(airbase, capZone.location, capConfig),
[3] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), [3] = RTB.getInitialPoint(airbase),
[4] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) [4] = RTB.getLandingPoint(airbase)
} }
local mission = { local mission = {
@@ -320,8 +323,4 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig)
} }
end end
if not Spearhead then Spearhead = {} end return CAP
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.CAP = CAP
@@ -1,4 +1,5 @@
local Util = require("classes.util.Util")
local RTB = require("classes.capClasses.taskings.RTB")
---@class InterceptTasking ---@class InterceptTasking
local INTERCEPT = {} local INTERCEPT = {}
@@ -14,11 +15,11 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con
local airbaseVec3 = airbase:getPoint() local airbaseVec3 = airbase:getPoint()
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z } local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, interceptPoint) local heading = Util.vectorHeadingFromTo(airbaseVec2, interceptPoint)
env.info("BLAAT: heading from runway to first point: " .. heading) env.info("BLAAT: heading from runway to first point: " .. heading)
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 3*1852) local point = Util.vectorMove(airbaseVec2, heading, 3*1852)
local pointA, pointB, pointC, pointD = INTERCEPT.getInterceptTaskPoint(groupName, point, interceptPoint, airbaseVec2, config, speed, alt) local pointA, pointB, pointC, pointD = INTERCEPT.getInterceptTaskPoint(groupName, point, interceptPoint, airbaseVec2, config, speed, alt)
@@ -28,9 +29,9 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con
[3] = pointB, [3] = pointB,
[4] = pointC, [4] = pointC,
[5] = pointD, [5] = pointD,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config), [6] = RTB.getApproachPoint(airbase, interceptPoint, config),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), [7] = RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) [8] = RTB.getLandingPoint(airbase)
} }
local mission = { local mission = {
@@ -67,9 +68,9 @@ function INTERCEPT.getMissionFromInAir(groupName, currentPosition, interceptPoin
[3] = pointB, [3] = pointB,
[4] = pointC, [4] = pointC,
[5] = pointD, [5] = pointD,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config), [6] = RTB.getApproachPoint(airbase, interceptPoint, config),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), [7] = RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) [8] = RTB.getLandingPoint(airbase)
} }
local mission = { local mission = {
@@ -109,9 +110,9 @@ function INTERCEPT.getUnitInterceptMissionFromAir(groupName, currentPoint, targe
[3] = pointB, [3] = pointB,
[4] = pointC, [4] = pointC,
[5] = pointD, [5] = pointD,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, currentPoint, config), [6] = RTB.getApproachPoint(airbase, currentPoint, config),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), [7] = RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) [8] = RTB.getLandingPoint(airbase)
} }
local mission = { local mission = {
@@ -392,8 +393,4 @@ function INTERCEPT.getUnitInterceptTaskPoint(groupName, currentPoint, targetPosi
end end
if not Spearhead then Spearhead = {} end return INTERCEPT
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.INTERCEPT = INTERCEPT
@@ -1,3 +1,4 @@
local Util = require("classes.util.Util")
---@class RTBTasking ---@class RTBTasking
local RTB = {} local RTB = {}
@@ -33,7 +34,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
local runways = airbase:getRunways() local runways = airbase:getRunways()
local windVec = atmosphere.getWind(airbase:getPoint()) local windVec = atmosphere.getWind(airbase:getPoint())
local mPerS = Spearhead.Util.vectorMagnitude(windVec) local mPerS = Util.vectorMagnitude(windVec)
if mPerS > 0 then if mPerS > 0 then
for i = 1, #runways do for i = 1, #runways do
@@ -48,7 +49,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
end end
local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0} local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0}
local alignment = Spearhead.Util.vectorAlignment(windVec, runwayVec) local alignment = Util.vectorAlignment(windVec, runwayVec)
if minAlignment == nil or alignment < minAlignment then if minAlignment == nil or alignment < minAlignment then
activeCourse = i activeCourse = i
@@ -68,7 +69,7 @@ local getRunwayIntoWindCourseRad = function(airbase)
end end
local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0} local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0}
local alignment = Spearhead.Util.vectorAlignment(windVec, runwayVec) local alignment = Util.vectorAlignment(windVec, runwayVec)
if minAlignment == nil or alignment < minAlignment then if minAlignment == nil or alignment < minAlignment then
activeCourse = i activeCourse = i
@@ -102,7 +103,7 @@ local function calcInitialPoint(airbase)
local flipped = (heading + 180) % 360 local flipped = (heading + 180) % 360
local basePoint = airbase:getPoint() local basePoint = airbase:getPoint()
local basePointVec2 = {x = basePoint.x, y = basePoint.z} local basePointVec2 = {x = basePoint.x, y = basePoint.z}
local point = Spearhead.Util.vectorMove(basePointVec2, flipped, 22000) local point = Util.vectorMove(basePointVec2, flipped, 22000)
return point, flipped return point, flipped
@@ -116,10 +117,10 @@ end
function RTB.getApproachPoint(airbase, missionPoint, capConfig) function RTB.getApproachPoint(airbase, missionPoint, capConfig)
local initialPoint, headingFromRunway = calcInitialPoint(airbase) local initialPoint, headingFromRunway = calcInitialPoint(airbase)
local pointA = Spearhead.Util.vectorMove(initialPoint, headingFromRunway - 45, 9000) local pointA = Util.vectorMove(initialPoint, headingFromRunway - 45, 9000)
local pointB = Spearhead.Util.vectorMove(initialPoint, headingFromRunway + 45, 9000) local pointB = Util.vectorMove(initialPoint, headingFromRunway + 45, 9000)
if Spearhead.Util.VectorDistance2d(missionPoint, pointA) > Spearhead.Util.VectorDistance2d(missionPoint, pointB) then if Util.VectorDistance2d(missionPoint, pointA) > Util.VectorDistance2d(missionPoint, pointB) then
pointA = pointB pointA = pointB
end end
@@ -194,8 +195,4 @@ function RTB.getLandingPoint(airbase)
end end
if not Spearhead then Spearhead = {} end return RTB
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.RTB = RTB
@@ -1,3 +1,6 @@
local Util = require("classes.util.Util")
local RTB = require("classes.capClasses.taskings.RTB")
---@class SWEEPTasking ---@class SWEEPTasking
local SWEEP = {} local SWEEP = {}
@@ -31,7 +34,7 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
for indexA, pointA in ipairs(capZone.verts) do for indexA, pointA in ipairs(capZone.verts) do
for indexB, pointB in ipairs(capZone.verts) do for indexB, pointB in ipairs(capZone.verts) do
if pointA ~= pointB then if pointA ~= pointB then
local distance = Spearhead.Util.VectorDistance2d(pointA, pointB) local distance = Util.VectorDistance2d(pointA, pointB)
if distance > furthestDistance then if distance > furthestDistance then
furthestDistance = distance furthestDistance = distance
furthestA = indexA furthestA = indexA
@@ -51,12 +54,12 @@ local function GetCAPPointFromTriggerZone(airBase, capZone)
local furthest = pointA local furthest = pointA
local closest = pointB local closest = pointB
local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB) local heading = Util.vectorHeadingFromTo(pointA, pointB)
if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then if Util.VectorDistance2d(baseVec2, pointB) > Util.VectorDistance2d(baseVec2, pointA) then
furthest = pointB furthest = pointB
closest = pointA closest = pointA
heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA) heading = Util.vectorHeadingFromTo(pointB, pointA)
end end
local distance = furthestDistance local distance = furthestDistance
@@ -80,8 +83,8 @@ end
local GetOutboundTask = function(airbase, capZone, capConfig) local GetOutboundTask = function(airbase, capZone, capConfig)
local airbaseVec3 = airbase:getPoint() local airbaseVec3 = airbase:getPoint()
local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z } local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z }
local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location) local heading = Util.vectorHeadingFromTo(airbaseVec2, capZone.location)
local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520) local point = Util.vectorMove(airbaseVec2, heading, 18520)
return { return {
alt = 2000, alt = 2000,
@@ -128,9 +131,9 @@ function SWEEP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig)
[3] = pointA, [3] = pointA,
[4] = pointB, [4] = pointB,
[5] = pointC, [5] = pointC,
[6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig), [6] = RTB.getApproachPoint(airbase, capZone.location, capConfig),
[7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), [7] = RTB.getInitialPoint(airbase),
[8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) [8] = RTB.getLandingPoint(airbase)
} }
local mission = { local mission = {
@@ -280,8 +283,4 @@ function SWEEP.getAsTasking(groupName, airbase, capZone, capConfig)
return pointA, pointB, pointC return pointA, pointB, pointC
end end
if not Spearhead then Spearhead = {} end return SWEEP
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end
if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end
Spearhead.classes.capClasses.taskings.SWEEP = SWEEP
@@ -95,6 +95,4 @@ function CapConfig:getDeathDelay()
return self._deathDelay return self._deathDelay
end end
if not Spearhead.internal then Spearhead.internal = {} end return CapConfig
if not Spearhead.internal.configuration then Spearhead.internal.configuration = {} end
Spearhead.internal.configuration.CapConfig = CapConfig;
@@ -29,6 +29,5 @@ function GlobalConfig:getBriefingTime()
end end
if Spearhead == nil then Spearhead = {} end return GlobalConfig
Spearhead.GlobalConfig = GlobalConfig.New()
@@ -7,8 +7,6 @@
--- @field startingStage integer --- @field startingStage integer
--- @field maxMissionsPerStage integer --- @field maxMissionsPerStage integer
--- @field AmountPreactivateStage integer --- @field AmountPreactivateStage integer
local StageConfig = {}; local StageConfig = {};
---comment ---comment
@@ -34,6 +32,4 @@ function StageConfig:new()
return o; return o;
end end
if not Spearhead.internal then Spearhead.internal = {} end return StageConfig
if not Spearhead.internal.configuration then Spearhead.internal.configuration = {} end
Spearhead.internal.configuration.StageConfig = StageConfig;
@@ -1,3 +1,10 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local MissionEditorWarning = require("classes.util.MissionEditorWarnings")
local RouteUtil = require("classes.spearhead_routeutil")
local SpearheadEvents = require("classes.spearhead_events")
---@class FleetGroup
local FleetGroup = {} local FleetGroup = {}
---comment ---comment
@@ -13,9 +20,9 @@ function FleetGroup:new(fleetGroupName, database, logger)
o.fleetGroupName = fleetGroupName o.fleetGroupName = fleetGroupName
o.logger = logger o.logger = logger
local split_name = Spearhead.Util.split_string(fleetGroupName, "_") local split_name = Util.split_string(fleetGroupName, "_")
if Spearhead.Util.tableLength(split_name) < 2 then if Util.tableLength(split_name) < 2 then
Spearhead.AddMissionEditorWarning("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_<fleetname>") MissionEditorWarning.Add("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_<fleetname>")
return nil return nil
end end
o.fleetNameIdentifier = split_name[2] o.fleetNameIdentifier = split_name[2]
@@ -27,12 +34,12 @@ function FleetGroup:new(fleetGroupName, database, logger)
do --INIT do --INIT
local carrierRouteZones = database:getCarrierRouteZones() local carrierRouteZones = database:getCarrierRouteZones()
for _, zoneName in pairs(carrierRouteZones) do for _, zoneName in pairs(carrierRouteZones) do
if Spearhead.Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then if Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then
local zone = Spearhead.DcsUtil.getZoneByName(zoneName) local zone = DcsUtil.getZoneByName(zoneName)
if zone and zone.zone_type == Spearhead.DcsUtil.ZoneType.Polygon then if zone and zone.zone_type == DcsUtil.ZoneType.Polygon then
local split_string = Spearhead.Util.split_string(zoneName, "_") local split_string = Util.split_string(zoneName, "_")
if Spearhead.Util.tableLength(split_string) < 3 then if Util.tableLength(split_string) < 3 then
Spearhead.AddMissionEditorWarning( MissionEditorWarning.Add(
"CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName) "CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName)
else else
@@ -48,7 +55,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
for ii = i + 1, 4 do for ii = i + 1, 4 do
local a = zone.verts[i] local a = zone.verts[i]
local b = zone.verts[ii] local b = zone.verts[ii]
local dist = Spearhead.Util.VectorDistance2d(a, b) local dist = Util.VectorDistance2d(a, b)
if biggest == nil or dist > biggest then if biggest == nil or dist > biggest then
biggestA = a biggestA = a
@@ -65,17 +72,17 @@ function FleetGroup:new(fleetGroupName, database, logger)
return nil, nil return nil, nil
end end
if Spearhead.Util.startswith(namePart, "%[") == true then if Util.startswith(namePart, "%[") == true then
namePart = Spearhead.Util.split_string(namePart, "[")[1] namePart = Util.split_string(namePart, "[")[1]
end end
if Spearhead.Util.strContains(namePart, "%]") == true then if Util.strContains(namePart, "%]") == true then
namePart = Spearhead.Util.split_string(namePart, "]")[1] namePart = Util.split_string(namePart, "]")[1]
end end
local split_numbers = Spearhead.Util.split_string(namePart, "-") local split_numbers = Util.split_string(namePart, "-")
if Spearhead.Util.tableLength(split_numbers) < 2 then if Util.tableLength(split_numbers) < 2 then
Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName) MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
return nil, nil return nil, nil
end end
@@ -83,7 +90,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
local second = tonumber(split_numbers[2]) local second = tonumber(split_numbers[2])
if first == nil or second == nil then if first == nil or second == nil then
Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName) MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
return nil, nil return nil, nil
end end
return first, second return first, second
@@ -97,11 +104,11 @@ function FleetGroup:new(fleetGroupName, database, logger)
end end
o.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} } o.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} }
else else
Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName) MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[<number>-<number>]: " .. zoneName)
end end
end end
else else
Spearhead.AddMissionEditorWarning("CARRIERROUTE cannot be a cilinder: " .. zoneName) MissionEditorWarning.Add("CARRIERROUTE cannot be a cilinder: " .. zoneName)
end end
end end
end end
@@ -115,7 +122,7 @@ function FleetGroup:new(fleetGroupName, database, logger)
local group = Group.getByName(groupName) local group = Group.getByName(groupName)
if group then if group then
logger:info("Sending " .. fleetGroupName .. " to " .. targetZone) logger:info("Sending " .. groupName .. " to " .. targetZone)
group:getController():setTask(task) group:getController():setTask(task)
end end
end end
@@ -124,14 +131,13 @@ function FleetGroup:new(fleetGroupName, database, logger)
local targetZone = self.targetZonePerStage[tostring(number)] local targetZone = self.targetZonePerStage[tostring(number)]
if targetZone and targetZone ~= self.currentTargetZone then if targetZone and targetZone ~= self.currentTargetZone then
local points = self.pointsPerZone[targetZone] local points = self.pointsPerZone[targetZone]
local task = Spearhead.RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB) local task = RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB)
timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5) timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5)
end end
end end
Spearhead.Events.AddStageNumberChangedListener(o) SpearheadEvents.AddStageNumberChangedListener(o)
return o return o
end end
if not Spearhead.internal then Spearhead.internal = {} end return FleetGroup
Spearhead.internal.FleetGroup = FleetGroup
@@ -0,0 +1,26 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local FleetGroup = require("classes.fleetClasses.FleetGroup")
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
---@class GlobalFleetManager
local GlobalFleetManager = {}
local fleetGroups = {}
GlobalFleetManager.start = function(database)
local logger = Logger.new("CARRIERFLEET", "INFO")
local all_groups = MizGroupsManager.getAllGroupNames()
for _, groupName in pairs(all_groups) do
if Util.startswith(string.lower(groupName), "carriergroup" ) == true then
logger:info("Registering " .. groupName .. " as a managed fleet")
local carrierGroup = FleetGroup:new(groupName, database, logger)
table.insert(fleetGroups, carrierGroup)
end
end
end
return GlobalFleetManager
@@ -1,3 +1,5 @@
local DcsUtil = require("classes.util.DcsUtil")
---@class SpawnData ---@class SpawnData
---@field groupTemplate table ---@field groupTemplate table
---@field isStatic boolean ---@field isStatic boolean
@@ -14,17 +16,17 @@ MizGroupsManager._spawnTemplateData = {}
do --init do --init
for coalition_name, coalition_data in pairs(env.mission.coalition) do for coalition_name, coalition_data in pairs(env.mission.coalition) do
local coalition_nr = Spearhead.DcsUtil.stringToCoalition(coalition_name) local coalition_nr = DcsUtil.stringToCoalition(coalition_name)
if coalition_data.country then if coalition_data.country then
for country_index, country_data in pairs(coalition_data.country) do for country_index, country_data in pairs(coalition_data.country) do
for category_name, categorydata in pairs(country_data) do for category_name, categorydata in pairs(country_data) do
local category_id = Spearhead.DcsUtil.stringToGroupCategory(category_name) local category_id = DcsUtil.stringToGroupCategory(category_name)
if category_id ~= nil and type(categorydata) == "table" and categorydata.group ~= nil and type(categorydata.group) == "table" then if category_id ~= nil and type(categorydata) == "table" and categorydata.group ~= nil and type(categorydata.group) == "table" then
for group_index, group in pairs(categorydata.group) do for group_index, group in pairs(categorydata.group) do
local name = group.name local name = group.name
local skippable = false local skippable = false
local isStatic = false local isStatic = false
if category_id == Spearhead.DcsUtil.GroupCategory.STATIC then if category_id == DcsUtil.GroupCategory.STATIC then
isStatic = true isStatic = true
local unit = group.units[1] local unit = group.units[1]
if unit and unit.category == "Heliports" then if unit and unit.category == "Heliports" then
@@ -75,7 +77,4 @@ function MizGroupsManager.getSpawnTemplateData(groupName)
return MizGroupsManager._spawnTemplateData[groupName] return MizGroupsManager._spawnTemplateData[groupName]
end end
if not Spearhead then Spearhead = {} end return MizGroupsManager
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.helpers then Spearhead.classes.helpers = {} end
Spearhead.classes.helpers.MizGroupsManager = MizGroupsManager
@@ -1,3 +1,8 @@
local MizGroupsManager = require("classes.helpers.MizGroupsManager")
local Persistence = require("classes.persistence.Persistence")
local SpearheadEvents = require("classes.spearhead_events")
local Util = require("classes.util.Util")
---@class SpawnManager : OnUnitLostListener ---@class SpawnManager : OnUnitLostListener
---@field private _persistedUnits table<string,boolean> ---@field private _persistedUnits table<string,boolean>
---@field private _logger Logger ---@field private _logger Logger
@@ -29,7 +34,7 @@ end
---@return boolean isStatic ---@return boolean isStatic
function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant) function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant)
local spawnData = Spearhead.classes.helpers.MizGroupsManager.getSpawnTemplateData(groupName) local spawnData = MizGroupsManager.getSpawnTemplateData(groupName)
if spawnData == nil then if spawnData == nil then
env.error("SpawnManager:SpawnGroup - No spawn template found for group: " .. groupName) env.error("SpawnManager:SpawnGroup - No spawn template found for group: " .. groupName)
@@ -67,7 +72,7 @@ end
---@param groupName string ---@param groupName string
---@return boolean ---@return boolean
function SpawnManager:IsGroupStatic(groupName) function SpawnManager:IsGroupStatic(groupName)
local isStatic = Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) local isStatic = MizGroupsManager.IsGroupStatic(groupName)
if isStatic ~= nil then return isStatic end if isStatic ~= nil then return isStatic end
return StaticObject.getByName(groupName) ~= nil return StaticObject.getByName(groupName) ~= nil
@@ -89,7 +94,7 @@ function SpawnManager:OnUnitLost(object)
heading = heading heading = heading
end end
Spearhead.classes.persistence.Persistence.UnitKilled( Persistence.UnitKilled(
name, name,
object:getPoint(), object:getPoint(),
heading, heading,
@@ -119,7 +124,7 @@ do --- privates
country = override.countryID or spawnData.country country = override.countryID or spawnData.country
end end
local spawnTemplate = Spearhead.Util.deepCopyTable(spawnData.groupTemplate) local spawnTemplate = Util.deepCopyTable(spawnData.groupTemplate)
---@type Array<string> ---@type Array<string>
local removeableUnitNames = {} local removeableUnitNames = {}
--[[ --[[
@@ -131,8 +136,8 @@ do --- privates
for _, unit in pairs(spawnTemplate["units"]) do for _, unit in pairs(spawnTemplate["units"]) do
local name = unit["name"] local name = unit["name"]
Spearhead.Events.addOnUnitLostEventListener(name, self) SpearheadEvents.addOnUnitLostEventListener(name, self)
local state = Spearhead.classes.persistence.Persistence.UnitState(name) local state = Persistence.UnitState(name)
if state then if state then
if state.isDead == true then if state.isDead == true then
removeableUnitNames[#removeableUnitNames+1] = name removeableUnitNames[#removeableUnitNames+1] = name
@@ -170,7 +175,7 @@ do --- privates
if isPersistent == true then if isPersistent == true then
self._persistedUnits[unit:getName()] = true self._persistedUnits[unit:getName()] = true
end end
Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self) SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self)
end end
return group return group
@@ -195,10 +200,10 @@ do --- privates
country = overrides.countryID or spawnData.country country = overrides.countryID or spawnData.country
end end
local spawnTemplate = Spearhead.Util.deepCopyTable(spawnData.groupTemplate) local spawnTemplate = Util.deepCopyTable(spawnData.groupTemplate)
--for static Objecst groupNames and unit names are the same and is always 1:1 --for static Objecst groupNames and unit names are the same and is always 1:1
local persistentState = Spearhead.classes.persistence.Persistence.UnitState(groupName) local persistentState = Persistence.UnitState(groupName)
if persistentState then if persistentState then
if persistentState.isDead == true then if persistentState.isDead == true then
spawnTemplate["dead"] = true spawnTemplate["dead"] = true
@@ -226,7 +231,7 @@ do --- privates
if isPersistent == true then if isPersistent == true then
self._persistedUnits[groupName] = true self._persistedUnits[groupName] = true
Spearhead.Events.addOnUnitLostEventListener(groupName, self) SpearheadEvents.addOnUnitLostEventListener(groupName, self)
end end
return object return object
@@ -238,7 +243,7 @@ do --- privates
if not unit then return end if not unit then return end
local deadState = Spearhead.classes.persistence.Persistence.UnitState(unit:getName()) local deadState = Persistence.UnitState(unit:getName())
if deadState and deadState.isDead == true then if deadState and deadState.isDead == true then
unit:destroy() -- Destroy the unit if it is dead unit:destroy() -- Destroy the unit if it is dead
local staticObject = { local staticObject = {
@@ -255,10 +260,7 @@ do --- privates
end end
if not Spearhead then Spearhead = {} end return SpawnManager
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.helpers then Spearhead.classes.helpers = {} end
Spearhead.classes.helpers.SpawnManager = SpawnManager
--Old Spawn Method --Old Spawn Method
@@ -1,4 +1,6 @@
local Util = require("classes.util.Util")
---@class Perstistence ---@class Perstistence
---@field private _path string ---@field private _path string
---@field private _updateRequired boolean ---@field private _updateRequired boolean
@@ -81,7 +83,7 @@ do
if lua then if lua then
tables = lua tables = lua
logger:debug("Loaded persistence data: " .. Spearhead.Util.toString(lua)) logger:debug("Loaded persistence data: " .. Util.toString(lua))
else else
logger:error("Could not load persistence file, using default tables") logger:error("Could not load persistence file, using default tables")
end end
@@ -143,8 +145,8 @@ do
local latestFile, lastNumber = default, 0 local latestFile, lastNumber = default, 0
for file in lfs.dir(dir) do for file in lfs.dir(dir) do
local split = Spearhead.Util.split_string(file, ".") local split = Util.split_string(file, ".")
local doesStartWith = Spearhead.Util.startswith(file, startsWith, true) local doesStartWith = Util.startswith(file, startsWith, true)
if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then
local numberString = split[#split-1] local numberString = split[#split-1]
local number = tonumber(numberString) local number = tonumber(numberString)
@@ -179,7 +181,7 @@ do
if SpearheadConfig.Persistence.fileName then if SpearheadConfig.Persistence.fileName then
local userFileName = SpearheadConfig.Persistence.fileName local userFileName = SpearheadConfig.Persistence.fileName
local split = Spearhead.Util.split_string(userFileName, ".") local split = Util.split_string(userFileName, ".")
if not split and #split < 3 then if not split and #split < 3 then
split[#split+1] = "0" split[#split+1] = "0"
@@ -199,12 +201,12 @@ do
end end
end end
local split = Spearhead.Util.split_string(fileName, ".") local split = Util.split_string(fileName, ".")
local matchingPart = table.concat(Spearhead.Util.sublist(split, 1, #split-2), ".") local matchingPart = table.concat(Util.sublist(split, 1, #split-2), ".")
local lastFile = getLastFileOrDefault(dir--[[@as string]], matchingPart, fileName) local lastFile = getLastFileOrDefault(dir--[[@as string]], matchingPart, fileName)
local fileSplit = Spearhead.Util.split_string(lastFile, ".") local fileSplit = Util.split_string(lastFile, ".")
fileSplit[#fileSplit-1] = tostring(tonumber(fileSplit[#fileSplit-1]) + 1) fileSplit[#fileSplit-1] = tostring(tonumber(fileSplit[#fileSplit-1]) + 1)
fileName = table.concat(fileSplit, ".") fileName = table.concat(fileSplit, ".")
@@ -329,7 +331,4 @@ do
end end
end end
if Spearhead == nil then Spearhead = {} end return Persistence
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.persistence == nil then Spearhead.classes.persistence = {} end
Spearhead.classes.persistence.Persistence = Persistence
@@ -1009,5 +1009,4 @@ function Database:GetNewMissionCode()
]] ]]
end end
if not Spearhead then Spearhead = {} end return Database
Spearhead.DB = Database
@@ -1,4 +1,4 @@
---@class SpearheadEvents
local SpearheadEvents = {} local SpearheadEvents = {}
do do
@@ -398,5 +398,4 @@ do
world.addEventHandler(e) world.addEventHandler(e)
end end
if Spearhead == nil then Spearhead = {} end return SpearheadEvents
Spearhead.Events = SpearheadEvents
@@ -1,3 +1,4 @@
---@class SpearheadRouteUtil
local ROUTE_UTIL = {} local ROUTE_UTIL = {}
do --setup route util do --setup route util
---comment ---comment
@@ -461,5 +462,4 @@ do --setup route util
end end
end end
if Spearhead == nil then Spearhead = {} end return ROUTE_UTIL
Spearhead.RouteUtil = ROUTE_UTIL
@@ -20,6 +20,8 @@ local currentStage = -99
local GlobalStageManager = {} local GlobalStageManager = {}
GlobalStageManager.__index = GlobalStageManager GlobalStageManager.__index = GlobalStageManager
GlobalStageManager.getCurrentStage = function() return currentStage end
---comment ---comment
---@param database Database ---@param database Database
---@param stageConfig StageConfig ---@param stageConfig StageConfig
@@ -45,6 +47,7 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa
end end
} }
Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener) Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener)
for _, stageName in pairs(database:getStagezoneNames()) do for _, stageName in pairs(database:getStagezoneNames()) do
@@ -282,5 +285,4 @@ GlobalStageManager.isStageComplete = function (stageNumber)
return true return true
end end
if not Spearhead.internal then Spearhead.internal = {} end return GlobalStageManager
Spearhead.internal.GlobalStageManager = GlobalStageManager
@@ -1,5 +1,5 @@
local DcsUtil = require("classes.util.DcsUtil")
---@class SpearheadGroup : OnUnitLostListener ---@class SpearheadGroup : OnUnitLostListener
---@field private _groupName string ---@field private _groupName string
@@ -149,7 +149,7 @@ end
function SpearheadGroup:SetInvisible() function SpearheadGroup:SetInvisible()
if self._isStatic == true then if self._isStatic == true then
local country = Spearhead.DcsUtil.GetNeutralCountry() local country = DcsUtil.GetNeutralCountry()
---@type SpawnOverrides ---@type SpawnOverrides
local overrides = { local overrides = {
@@ -187,7 +187,4 @@ function SpearheadGroup:SetVisible()
end end
end end
if not Spearhead.classes then Spearhead.classes = {} end return SpearheadGroup
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,3 +1,5 @@
local Persistence = require("classes.persistence.Persistence")
---@class SpearheadSceneryObject ---@class SpearheadSceneryObject
---@field private persistentName string The persistent name of the scenery object ---@field private persistentName string The persistent name of the scenery object
---@field private objectID number The ID of the scenery object ---@field private objectID number The ID of the scenery object
@@ -47,7 +49,7 @@ end
function SpearheadSceneryObject:MarkDead() function SpearheadSceneryObject:MarkDead()
if self.isDead == true then return end if self.isDead == true then return end
self.isDead = true self.isDead = true
Spearhead.classes.persistence.Persistence.UnitKilled(self.persistentName, self:GetPoint(), 0, "Scenery") Persistence.UnitKilled(self.persistentName, self:GetPoint(), 0, "Scenery")
end end
function SpearheadSceneryObject:UpdateStatePersistently() function SpearheadSceneryObject:UpdateStatePersistently()
@@ -55,7 +57,7 @@ function SpearheadSceneryObject:UpdateStatePersistently()
return return
end end
local state = Spearhead.classes.persistence.Persistence.UnitState(self.persistentName) local state = Persistence.UnitState(self.persistentName)
if state and state.isDead == true then if state and state.isDead == true then
trigger.action.explosion(self:GetPoint(), 1000) trigger.action.explosion(self:GetPoint(), 1000)
self.isDead = true self.isDead = true
@@ -71,7 +73,4 @@ function SpearheadSceneryObject:GetPoint()
return Object.getPoint(self.internalObj) return Object.getPoint(self.internalObj)
end end
if not Spearhead.classes then Spearhead.classes = {} end return SpearheadSceneryObject
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end
Spearhead.classes.stageClasses.Groups.SpearheadSceneryObject = SpearheadSceneryObject
@@ -148,10 +148,4 @@ function BlueSam:OnBuildingComplete()
self:SpawnGroups() self:SpawnGroups()
end end
return BlueSam
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.BlueSam = BlueSam
@@ -132,8 +132,4 @@ function FarpZone:SetPadsBlue()
end end
end end
if Spearhead == nil then Spearhead = {} end return FarpZone
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.FarpZone = FarpZone
@@ -187,10 +187,4 @@ function StageBase:OnBuildingComplete()
self:FinaliseBlueStage() self:FinaliseBlueStage()
end end
return StageBase
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
@@ -80,9 +80,4 @@ function SupplyHub:Activate()
end end
return SupplyHub
if not Spearhead then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
Spearhead.classes.stageClasses.SpecialZones.SupplyHub = SupplyHub
@@ -197,9 +197,4 @@ function BuildableZone:SpawnAmount(amount)
return true return true
end end
if not Spearhead then Spearhead = {} end return BuildableZone
Spearhead.classes = Spearhead.classes or {}
Spearhead.classes.stageClasses = Spearhead.classes.stageClasses or {}
Spearhead.classes.stageClasses.SpecialZones = Spearhead.classes.stageClasses.SpecialZones or {}
Spearhead.classes.stageClasses.SpecialZones.abstract = Spearhead.classes.stageClasses.SpecialZones.abstract or {}
Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone = BuildableZone
@@ -575,11 +575,7 @@ function Stage:ActivateBlueStage()
timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3) timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3)
end end
if not Spearhead.classes then Spearhead.classes = {} end return Stage
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
@@ -61,9 +61,6 @@ function ExtraStage:OnStageNumberChanged(number)
end end
if not Spearhead.classes then Spearhead.classes = {} end return ExtraStage
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
@@ -22,9 +22,6 @@ function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager)
end end
if not Spearhead.classes then Spearhead.classes = {} end return PrimaryStage
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
@@ -69,9 +69,6 @@ function WaitingStage:GetExpectedTime()
return self._startTime + self._waitTimeSeconds return self._startTime + self._waitTimeSeconds
end end
if not Spearhead.classes then Spearhead.classes = {} end return WaitingStage
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
@@ -1,9 +1,10 @@
local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper")
local Util = require("classes.util.Util")
---@class CustomDrawing ---@class CustomDrawing
---@field private _id integer? ---@field private _id integer?
---@field private _drawingObject DrawingObject ---@field private _drawingObject DrawingObject
---@field private _helper DrawingHelper
---@field private _startingStage number ---@field private _startingStage number
---@field private _removeAtStage number ---@field private _removeAtStage number
local CustomDrawing = {} local CustomDrawing = {}
@@ -17,12 +18,11 @@ function CustomDrawing.New(drawingObject, id)
local self = setmetatable({}, CustomDrawing) local self = setmetatable({}, CustomDrawing)
self._drawingObject = drawingObject self._drawingObject = drawingObject
self._id = id self._id = id
self._helper = Spearhead.classes.stageClasses.drawings.helper.DrawingHelper
local name = drawingObject.name local name = drawingObject.name
local split = Spearhead.Util.split_string(name or "", "_") local split = Util.split_string(name or "", "_")
local secondPart = split[2] or "1" local secondPart = split[2] or "1"
local splitPart = Spearhead.Util.split_string(secondPart, ":") local splitPart = Util.split_string(secondPart, ":")
self._startingStage = tonumber(splitPart[1]) or 1 self._startingStage = tonumber(splitPart[1]) or 1
self._removeAtStage = tonumber(splitPart[2]) or math.huge self._removeAtStage = tonumber(splitPart[2]) or math.huge
return self return self
@@ -35,18 +35,14 @@ function CustomDrawing:GetStartAndStop()
end end
function CustomDrawing:Draw() function CustomDrawing:Draw()
self._id = self._helper.Draw(self._drawingObject) self._id = DrawingHelper.Draw(self._drawingObject)
end end
function CustomDrawing:Remove() function CustomDrawing:Remove()
if self._id ~= nil then if self._id ~= nil then
self._helper.Remove(self._id) DrawingHelper.Remove(self._id)
self._id = nil self._id = nil
end end
end end
if not Spearhead then Spearhead = {} end return CustomDrawing
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end
Spearhead.classes.stageClasses.drawings.CustomDrawing = CustomDrawing
@@ -220,9 +220,4 @@ end
---@field public b number ---@field public b number
if not Spearhead then Spearhead = {} end return DrawingHelper
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end
if not Spearhead.classes.stageClasses.drawings.helper then Spearhead.classes.stageClasses.drawings.helper = {} end
Spearhead.classes.stageClasses.drawings.helper.DrawingHelper = DrawingHelper
@@ -1,3 +1,7 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
---@class BattleManager ---@class BattleManager
---@field private _name string ---@field private _name string
---@field private _logger Logger ---@field private _logger Logger
@@ -21,7 +25,7 @@ function BattleManager.New(redGroups, blueGroups, name, logLevel)
self._isActive = false self._isActive = false
self._name = name self._name = name
self._logger = Spearhead.LoggerTemplate.new("BattleManager_" .. name, logLevel) self._logger = Logger.new("BattleManager_" .. name, logLevel)
self._redGroups = redGroups self._redGroups = redGroups
self._blueGroups = blueGroups self._blueGroups = blueGroups
@@ -167,7 +171,7 @@ function BattleManager:getBestAmmo(unit)
end end
end end
local entry = Spearhead.Util.randomFromList(shells) local entry = Util.randomFromList(shells)
if entry and entry.desc and entry.desc.warhead then if entry and entry.desc and entry.desc.warhead then
local caliber = entry.desc.warhead.caliber local caliber = entry.desc.warhead.caliber
if caliber > 50 then if caliber > 50 then
@@ -213,10 +217,10 @@ function BattleManager:ToShootingHulls(groups)
end end
end end
local hulls = Spearhead.Util.getSeparatedConvexHulls(points, 50) local hulls = Util.getSeparatedConvexHulls(points, 50)
local enlargedHulls = {} local enlargedHulls = {}
for _, hull in pairs(hulls) do for _, hull in pairs(hulls) do
local enlarged = Spearhead.Util.enlargeConvexHull(hull, 25) local enlarged = Util.enlargeConvexHull(hull, 25)
if enlarged then if enlarged then
table.insert(enlargedHulls, enlarged) table.insert(enlargedHulls, enlarged)
end end
@@ -237,15 +241,15 @@ end
---@return Vec2? ---@return Vec2?
function BattleManager:GetRandomPoint(origin, groupHulls) function BattleManager:GetRandomPoint(origin, groupHulls)
local hull = Spearhead.Util.randomFromList(groupHulls) --[[@as Array<Vec2>]] local hull = Util.randomFromList(groupHulls) --[[@as Array<Vec2>]]
if not hull then return nil end if not hull then return nil end
local shootPoints = Spearhead.Util.GetTangentHullPointsFromOrigin(hull, origin) local shootPoints = Util.GetTangentHullPointsFromOrigin(hull, origin)
if debugDrawing == true then if debugDrawing == true then
self:DrawDebugZone({ hull }) self:DrawDebugZone({ hull })
end end
return Spearhead.Util.randomFromList(shootPoints) --[[@as Vec2]] return Util.randomFromList(shootPoints) --[[@as Vec2]]
end end
do --DEBUG do --DEBUG
@@ -258,7 +262,7 @@ do --DEBUG
color = {r = 0, g = 0, b = 1, a = 1} color = {r = 0, g = 0, b = 1, a = 1}
end end
Spearhead.DcsUtil.DrawLine(unit:getPoint(), {x = target.x, y = 0, z = target.y}, color, 1) DcsUtil.DrawLine(unit:getPoint(), {x = target.x, y = 0, z = target.y}, color, 1)
end end
---@param hulls Array<Array<Vec2>> ---@param hulls Array<Array<Vec2>>
@@ -274,13 +278,9 @@ do --DEBUG
location = { x=drawHull[1].x, y=drawHull[1].y }, location = { x=drawHull[1].x, y=drawHull[1].y },
} }
Spearhead.DcsUtil.DrawZone(zone, {r =0, g=0, b =1, a = 0.5} ,{r =0, g= 0, b =1, a = 0}, 1) DcsUtil.DrawZone(zone, {r =0, g=0, b =1, a = 0.5} ,{r =0, g= 0, b =1, a = 0}, 1)
end end
end end
end --DEBUG end --DEBUG
if Spearhead == nil then Spearhead = {} end return BattleManager
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
Spearhead.classes.stageClasses.helpers.BattleManager = BattleManager
@@ -0,0 +1,20 @@
---@class MaxLoadConfig
---@field maxInternalLoad number
---@type table<string, MaxLoadConfig>
local MaxLoadConfig = {
["Mi-8MT"] = {
maxInternalLoad = 4000,
},
["CH-47Fbl1"] = {
maxInternalLoad = 10000
},
["Mi-24P"] = {
maxInternalLoad = 2000
},
["UH-1H"] = {
maxInternalLoad = 2000
}
}
return MaxLoadConfig
@@ -1,3 +1,10 @@
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local Logger = require("classes.util.Logger")
local SpearheadEvents = require("classes.spearhead_events")
local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker")
local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper")
---@class MissionCommandsHelper ---@class MissionCommandsHelper
---@field missionsByCode table<string, Mission> @table of missions by their code ---@field missionsByCode table<string, Mission> @table of missions by their code
@@ -17,8 +24,8 @@ MissionCommandsHelper.__index = MissionCommandsHelper
---@param groupPos Vec2 ---@param groupPos Vec2
local function sortMissions(list, groupPos) local function sortMissions(list, groupPos)
table.sort(list, function(a, b) table.sort(list, function(a, b)
local distA = Spearhead.Util.VectorDistance2d(groupPos, a.location or {x=0, y=0}) local distA = Util.VectorDistance2d(groupPos, a.location or {x=0, y=0})
local distB = Spearhead.Util.VectorDistance2d(groupPos, b.location or {x=0, y=0}) local distB = Util.VectorDistance2d(groupPos, b.location or {x=0, y=0})
return distA < distB; return distA < distB;
end) end)
end end
@@ -33,7 +40,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
if instance == nil then if instance == nil then
instance = setmetatable({}, MissionCommandsHelper) instance = setmetatable({}, MissionCommandsHelper)
instance._logger = Spearhead.LoggerTemplate.new("MissionCommandsHelper", logLevel) instance._logger = Logger.new("MissionCommandsHelper", logLevel)
instance._logger:info("Creating MissionCommandsHelper instance") instance._logger:info("Creating MissionCommandsHelper instance")
@@ -45,7 +52,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
instance._supplyHubGroups = {} instance._supplyHubGroups = {}
instance._stageBriefings = {} instance._stageBriefings = {}
instance._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logLevel) instance._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logLevel)
---comment ---comment
---@param selfA MissionCommandsHelper ---@param selfA MissionCommandsHelper
@@ -56,7 +63,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
return time + 10 return time + 10
end end
for _, unit in pairs(Spearhead.DcsUtil.getAllPlayerUnits()) do for _, unit in pairs(DcsUtil.getAllPlayerUnits()) do
if unit and unit:isExist() then if unit and unit:isExist() then
local group = unit:getGroup() local group = unit:getGroup()
if group then if group then
@@ -71,7 +78,7 @@ function MissionCommandsHelper.getOrCreate(logLevel)
end end
timer.scheduleFunction(instance.updateContinuous, instance, timer.getTime() + 5) timer.scheduleFunction(instance.updateContinuous, instance, timer.getTime() + 5)
Spearhead.Events.AddOnPlayerEnterUnitListener(instance) SpearheadEvents.AddOnPlayerEnterUnitListener(instance)
end end
@@ -174,7 +181,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID)
local text = "Missions Overview\n\n" local text = "Missions Overview\n\n"
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(id) local group = DcsUtil.GetPlayerGroupByGroupID(id)
---@type Vec2 ---@type Vec2
local groupPos = { x=0, y=0 } local groupPos = { x=0, y=0 }
if group then if group then
@@ -193,7 +200,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID)
if lead and lead:isExist() == true then if lead and lead:isExist() == true then
local pos = lead:getPoint() local pos = lead:getPoint()
local Vec2Pos = { x= pos.x, y=pos.z } local Vec2Pos = { x= pos.x, y=pos.z }
local distance = Spearhead.Util.VectorDistance2d(Vec2Pos, mission.location) / 1852 local distance = Util.VectorDistance2d(Vec2Pos, mission.location) / 1852
distanceText = string.format("~%d", math.floor(distance)) distanceText = string.format("~%d", math.floor(distance))
end end
end end
@@ -323,7 +330,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
local perFolder = 9 local perFolder = 9
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
---@type Vec2 ---@type Vec2
local groupPos = { x=0, y=0 } local groupPos = { x=0, y=0 }
if group then if group then
@@ -351,7 +358,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for _, mission in pairs(primaryMissions) do for _, mission in pairs(primaryMissions) do
count = count + 1 count = count + 1
if count <= perFolder then if count <= perFolder then
local copied = Spearhead.Util.deepCopyTable(path) local copied = Util.deepCopyTable(path)
self:addMissionCommands(groupID, copied, mission) self:addMissionCommands(groupID, copied, mission)
else else
local name = "Next Menu ..." local name = "Next Menu ..."
@@ -380,7 +387,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID)
for _, mission in pairs(secondaryMissions) do for _, mission in pairs(secondaryMissions) do
count = count + 1 count = count + 1
if count <= perFolder then if count <= perFolder then
local copied = Spearhead.Util.deepCopyTable(path) local copied = Util.deepCopyTable(path)
self:addMissionCommands(groupID, copied, mission) self:addMissionCommands(groupID, copied, mission)
else else
local name = "Next Menu ..." local name = "Next Menu ..."
@@ -401,14 +408,14 @@ function MissionCommandsHelper:addMissionCommands(groupId, path, mission)
if path then if path then
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId) local group = DcsUtil.GetPlayerGroupByGroupID(groupId)
local distance = "[?]" local distance = "[?]"
if group then if group then
local lead = group:getUnit(1) local lead = group:getUnit(1)
if lead and lead:isExist() == true then if lead and lead:isExist() == true then
local pos = lead:getPoint() local pos = lead:getPoint()
local Vec2Pos = { x= pos.x, y=pos.z } local Vec2Pos = { x= pos.x, y=pos.z }
local dist = Spearhead.Util.VectorDistance2d(Vec2Pos, mission.location) / 1852 local dist = Util.VectorDistance2d(Vec2Pos, mission.location) / 1852
distance = "[" .. string.format("~%dnM", math.floor(dist)) .. "]" distance = "[" .. string.format("~%dnM", math.floor(dist)) .. "]"
end end
end end
@@ -435,7 +442,7 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID)
self._logger:debug("Adding supply hub commands for group: " .. tostring(groupID)) self._logger:debug("Adding supply hub commands for group: " .. tostring(groupID))
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then return end if group == nil then return end
local unit = group:getUnit(1) local unit = group:getUnit(1)
@@ -482,7 +489,7 @@ end
function MissionCommandsHelper:AddCargoCommands(groupID) function MissionCommandsHelper:AddCargoCommands(groupID)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then return end if group == nil then return end
local unit = group:getUnit(1) local unit = group:getUnit(1)
@@ -504,7 +511,7 @@ function MissionCommandsHelper:AddCargoCommands(groupID)
local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID()) local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID())
if cargo then if cargo then
for cargoType, amount in pairs(cargo) do for cargoType, amount in pairs(cargo) do
local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(cargoType) local cargoConfig = SupplyConfigHelper.getSupplyConfig(cargoType)
if cargoConfig then if cargoConfig then
for i = 1, amount do for i = 1, amount do
local path = { [1] = folderNames.cargo } local path = { [1] = folderNames.cargo }
@@ -531,7 +538,7 @@ function MissionCommandsHelper:addMissionFolders(groupId)
missionCommands.addSubMenuForGroup(groupId, folderNames.supplyHub) missionCommands.addSubMenuForGroup(groupId, folderNames.supplyHub)
end end
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId) local group = DcsUtil.GetPlayerGroupByGroupID(groupId)
if group == nil then return end if group == nil then return end
local unit = group:getUnit(1) local unit = group:getUnit(1)
@@ -561,8 +568,4 @@ function MissionCommandsHelper:ResetFolders(groupID)
self:addMissionFolders(groupID) self:addMissionFolders(groupID)
end end
if Spearhead == nil then Spearhead = {} end return MissionCommandsHelper
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
@@ -1,4 +1,6 @@
local Util = require("classes.util.Util")
---@alias SupplyType ---@alias SupplyType
---| "FARP_CRATE" ---| "FARP_CRATE"
---| "SAM_CRATE" ---| "SAM_CRATE"
@@ -65,24 +67,6 @@ local SupplyConfig = {
}, },
} }
---@class MaxLoadConfig
---@field maxInternalLoad number
---@type table<string, MaxLoadConfig>
MaxLoadConfig = {
["Mi-8MT"] = {
maxInternalLoad = 4000,
},
["CH-47Fbl1"] = {
maxInternalLoad = 10000
},
["Mi-24P"] = {
maxInternalLoad = 2000
},
["UH-1H"] = {
maxInternalLoad = 2000
}
}
---@class SupplyConfigHelper ---@class SupplyConfigHelper
local SupplyConfigHelper = {} local SupplyConfigHelper = {}
@@ -92,7 +76,7 @@ local SupplyConfigHelper = {}
---@return SupplyConfig? ---@return SupplyConfig?
function SupplyConfigHelper.fromObjectName(name) function SupplyConfigHelper.fromObjectName(name)
for configName, config in pairs(SupplyConfig) do for configName, config in pairs(SupplyConfig) do
if Spearhead.Util.startswith(name, configName, true) == true then if Util.startswith(name, configName, true) == true then
return config return config
end end
end end
@@ -104,11 +88,6 @@ function SupplyConfigHelper.getSupplyConfig(type)
return SupplyConfig[type] return SupplyConfig[type]
end end
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end return SupplyConfigHelper
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
if Spearhead.classes.stageClasses.helpers.supplies == nil then Spearhead.classes.stageClasses.helpers.supplies = {} end
Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig = MaxLoadConfig
Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper = SupplyConfigHelper
@@ -1,4 +1,12 @@
local Logger = require("classes.util.Logger")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local SpearheadEvents = require("classes.spearhead_events")
local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper")
local MaxLoadConfig = require("classes.stageClasses.helpers.MaxLoadConfig")
env.info("Spearhead SupplyUnitsTracker loaded") env.info("Spearhead SupplyUnitsTracker loaded")
---@class SupplyUnitsTracker ---@class SupplyUnitsTracker
@@ -23,7 +31,7 @@ function SupplyUnitsTracker.getOrCreate(logLevel)
if singleton == nil then if singleton == nil then
singleton = setmetatable({}, SupplyUnitsTracker) singleton = setmetatable({}, SupplyUnitsTracker)
singleton._logger = Spearhead.LoggerTemplate.new("SupplyUnitsTracker", logLevel) singleton._logger = Logger.new("SupplyUnitsTracker", logLevel)
singleton._unitPositions = {} singleton._unitPositions = {}
singleton._cargoInUnits = {} singleton._cargoInUnits = {}
singleton._supplyUnitsByName = {} singleton._supplyUnitsByName = {}
@@ -31,9 +39,9 @@ function SupplyUnitsTracker.getOrCreate(logLevel)
singleton._registeredHubs = {} singleton._registeredHubs = {}
singleton._supplyUnitSpawnedListener = {} singleton._supplyUnitSpawnedListener = {}
singleton._commandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel) singleton._commandsHelper = MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel)
Spearhead.Events.AddOnPlayerEnterUnitListener(singleton) SpearheadEvents.AddOnPlayerEnterUnitListener(singleton)
---@param selfA SupplyUnitsTracker ---@param selfA SupplyUnitsTracker
local function updateTask(selfA, time) local function updateTask(selfA, time)
@@ -96,7 +104,7 @@ function SupplyUnitsTracker:AddOnSupplyUnitSpawnedListener(listener)
end end
function SupplyUnitsTracker:Update() function SupplyUnitsTracker:Update()
local players = Spearhead.DcsUtil.getAllPlayerUnits() local players = DcsUtil.getAllPlayerUnits()
for _, player in pairs(players) do for _, player in pairs(players) do
if player ~= nil and player:isExist() and self:IsSupplyUnit(player) == true then if player ~= nil and player:isExist() and self:IsSupplyUnit(player) == true then
self._supplyUnitsByName[player:getName()] = player self._supplyUnitsByName[player:getName()] = player
@@ -127,7 +135,7 @@ function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType)
if unitID == nil or crateType == nil then return end if unitID == nil or crateType == nil then return end
local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID) local unit = DcsUtil.GetPlayerUnitByID(unitID)
if unit == nil then return end if unit == nil then return end
if self._cargoInUnits[unitID] == nil then if self._cargoInUnits[unitID] == nil then
@@ -174,7 +182,7 @@ function SupplyUnitsTracker:UpdateWeightForUnit(unit)
local weight = 0 local weight = 0
if self._cargoInUnits[tostring(unit:getID())] then if self._cargoInUnits[tostring(unit:getID())] then
for crateType, count in pairs(self._cargoInUnits[tostring(unit:getID())]) do for crateType, count in pairs(self._cargoInUnits[tostring(unit:getID())]) do
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfig and count then if crateConfig and count then
weight = weight + (crateConfig.weight * count) weight = weight + (crateConfig.weight * count)
end end
@@ -197,7 +205,7 @@ function SupplyUnitsTracker:CheckUnitsInZones()
if enabled == true then if enabled == true then
local zone = hub:GetZone() local zone = hub:GetZone()
if zone ~= nil then if zone ~= nil then
if Spearhead.Util.is3dPointInZone(pos, zone) then if Util.is3dPointInZone(pos, zone) then
self._commandsHelper:MarkUnitInSupplyHub(group:getID()) self._commandsHelper:MarkUnitInSupplyHub(group:getID())
else else
self._commandsHelper:MarkUnitOutsideSupplyHub(group:getID()) self._commandsHelper:MarkUnitOutsideSupplyHub(group:getID())
@@ -241,7 +249,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType)
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType) self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID) local unit = DcsUtil.GetPlayerUnitByID(unitID)
if unit == nil or unit:isExist() == false then return end if unit == nil or unit:isExist() == false then return end
local group = unit:getGroup() local group = unit:getGroup()
if group == nil then if group == nil then
@@ -251,7 +259,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType)
self:RemoveCargoFromUnit(unitID, crateType) self:RemoveCargoFromUnit(unitID, crateType)
self:UpdateWeightForUnit(unit) self:UpdateWeightForUnit(unit)
local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) local cargoConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if cargoConfig == nil then if cargoConfig == nil then
self._logger:error("Invalid crate type: " .. crateType) self._logger:error("Invalid crate type: " .. crateType)
@@ -285,10 +293,10 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType)
self._logger:debug("UnitRequestCrateLoading called with groupID: " .. groupID .. " and crateType: " .. crateType) self._logger:debug("UnitRequestCrateLoading called with groupID: " .. groupID .. " and crateType: " .. crateType)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group ~= nil then if group ~= nil then
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfig == nil then if crateConfig == nil then
self._logger:error("Invalid crate type: " .. crateType) self._logger:error("Invalid crate type: " .. crateType)
return return
@@ -341,7 +349,7 @@ end
---@return boolean ---@return boolean
function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType) function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType)
local crateConfigA = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfigA == nil then if crateConfigA == nil then
trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5) trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5)
return false return false
@@ -354,7 +362,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType)
end end
end end
local unitConfig = Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig[unit:getTypeName()] local unitConfig = MaxLoadConfig[unit:getTypeName()]
if unitConfig == nil then if unitConfig == nil then
trigger.action.outTextForUnit(unit:getID(), "Your unit type is not configured for logistics: " .. crateType, 5) trigger.action.outTextForUnit(unit:getID(), "Your unit type is not configured for logistics: " .. crateType, 5)
self._logger:error("Invalid unit type: " .. unit:getTypeName()) self._logger:error("Invalid unit type: " .. unit:getTypeName())
@@ -383,10 +391,10 @@ end
---@param crateType CrateType ---@param crateType CrateType
function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType) function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then if group == nil then
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType)
if crateConfig == nil then if crateConfig == nil then
self._logger:error("Invalid crate type: " .. crateType) self._logger:error("Invalid crate type: " .. crateType)
return return
@@ -446,9 +454,4 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit)
end end
return SupplyUnitsTracker
if Spearhead == nil then Spearhead = {} end
if Spearhead.classes == nil then Spearhead.classes = {} end
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker = SupplyUnitsTracker
@@ -1,4 +1,10 @@
local Mission = require("classes.stageClasses.missions.baseMissions.Mission")
local Util = require("classes.util.Util")
local DcsUtil = require("classes.util.DcsUtil")
local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local GlobalConfig = require("classes.configuration.GlobalConfig")
local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper")
---@class BuildableMission : Mission, SupplyUnitSpawnedListener ---@class BuildableMission : Mission, SupplyUnitSpawnedListener
---@field private _requiredKilos number ---@field private _requiredKilos number
@@ -27,7 +33,6 @@ BuildableMission.__index = BuildableMission
---@param logger Logger ---@param logger Logger
function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType) function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType)
local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission
setmetatable(BuildableMission, Mission) setmetatable(BuildableMission, Mission)
local self = setmetatable({}, { __index = BuildableMission }) local self = setmetatable({}, { __index = BuildableMission })
@@ -42,7 +47,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
if noLandingZone then if noLandingZone then
local verts = noLandingZone.verts local verts = noLandingZone.verts
local enlarged = Spearhead.Util.enlargeConvexHull(verts, 300) local enlarged = Util.enlargeConvexHull(verts, 300)
---@type SpearheadTriggerZone ---@type SpearheadTriggerZone
local dropOfZone = { local dropOfZone = {
@@ -71,7 +76,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self._onCrateDroppedOfListeners = {} self._onCrateDroppedOfListeners = {}
self._completeListeners = {} self._completeListeners = {}
self._markIDsPerGroup = {} self._markIDsPerGroup = {}
self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel) self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logger.LogLevel)
self._state = "NEW" self._state = "NEW"
@@ -82,7 +87,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi
self.priority = "secondary" self.priority = "secondary"
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(self._logger.LogLevel) self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(self._logger.LogLevel)
self._crateType = requiredCrateType self._crateType = requiredCrateType
@@ -96,11 +101,11 @@ end
function BuildableMission:ShowBriefing(groupID) function BuildableMission:ShowBriefing(groupID)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) local group = DcsUtil.GetPlayerGroupByGroupID(groupID)
if group == nil then return end if group == nil then return end
local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group) local unitType = DcsUtil.getUnitTypeFromGroup(group)
local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType) local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
local siteType = "FARP" local siteType = "FARP"
if self._crateType == "SAM_CRATE" then if self._crateType == "SAM_CRATE" then
@@ -119,18 +124,18 @@ function BuildableMission:ShowBriefing(groupID)
"\n\n" .. "\n\n" ..
"NOTE: Do not land in the orange construction zone!" "NOTE: Do not land in the orange construction zone!"
trigger.action.outTextForGroup(groupID, briefing, Spearhead.GlobalConfig:getBriefingTime()) trigger.action.outTextForGroup(groupID, briefing, GlobalConfig:getBriefingTime())
end end
function BuildableMission:MarkMissionAreaToGroup(groupID) function BuildableMission:MarkMissionAreaToGroup(groupID)
if self._markIDsPerGroup[groupID] then if self._markIDsPerGroup[groupID] then
Spearhead.DcsUtil.RemoveMark(self._markIDsPerGroup[groupID]) DcsUtil.RemoveMark(self._markIDsPerGroup[groupID])
end end
local text = "[" .. self.code .. "] " .. self.name .. " | " .. self._crateType local text = "[" .. self.code .. "] " .. self.name .. " | " .. self._crateType
local location = { x= self.location.x, y=land.getHeight(self.location), z=self.location.y } local location = { x= self.location.x, y=land.getHeight(self.location), z=self.location.y }
local markID = Spearhead.DcsUtil.AddMarkToGroup(groupID, text, location) local markID = DcsUtil.AddMarkToGroup(groupID, text, location)
self._markIDsPerGroup[groupID] = markID self._markIDsPerGroup[groupID] = markID
end end
@@ -163,7 +168,7 @@ function BuildableMission:SpawnActive()
local lineColor = { r=230/255, g=93/255, b=49/255, a=1} local lineColor = { r=230/255, g=93/255, b=49/255, a=1}
---@type DrawColor ---@type DrawColor
local fillColor = { r=230/255, g=93/255, b=49/255, a=0.2} local fillColor = { r=230/255, g=93/255, b=49/255, a=0.2}
self._noLandingZoneId = Spearhead.DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6) self._noLandingZoneId = DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6)
if self._dropOffZone == nil then if self._dropOffZone == nil then
self._logger:error("No drop off zone found for mission: " .. self.code) self._logger:error("No drop off zone found for mission: " .. self.code)
@@ -172,7 +177,7 @@ function BuildableMission:SpawnActive()
local lineColor2 = { r=0, g=0, b=1, a=1} local lineColor2 = { r=0, g=0, b=1, a=1}
local fillColor2 = { r=0, g=0, b=1, a=0} local fillColor2 = { r=0, g=0, b=1, a=0}
self._dropOffZoneId = Spearhead.DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6) self._dropOffZoneId = DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6)
---@param selfA BuildableMission ---@param selfA BuildableMission
---@param time number ---@param time number
@@ -230,17 +235,17 @@ function BuildableMission:CheckCratesInZone()
local crates = self._supplyUnitsTracker:GetCargoCratesDropped() local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
for _, staticObject in pairs(crates) do for _, staticObject in pairs(crates) do
if staticObject and staticObject:isExist() and Spearhead.Util.startswith(staticObject:getName(), self._crateType, true) then if staticObject and staticObject:isExist() and Util.startswith(staticObject:getName(), self._crateType, true) then
local pos = staticObject:getPoint() local pos = staticObject:getPoint()
if Spearhead.Util.is3dPointInZone(pos, self._dropOffZone) then if Util.is3dPointInZone(pos, self._dropOffZone) then
table.insert(foundCrates, staticObject) table.insert(foundCrates, staticObject)
end end
end end
end end
for _, foundCrate in pairs(foundCrates) do for _, foundCrate in pairs(foundCrates) do
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.fromObjectName(foundCrate:getName()) local crateConfig = SupplyConfigHelper.fromObjectName(foundCrate:getName())
if crateConfig then if crateConfig then
self._droppedKilos = self._droppedKilos + crateConfig.weight self._droppedKilos = self._droppedKilos + crateConfig.weight
foundCrate:destroy() foundCrate:destroy()
@@ -249,8 +254,8 @@ function BuildableMission:CheckCratesInZone()
end end
if self._droppedKilos >= self._requiredKilos then if self._droppedKilos >= self._requiredKilos then
Spearhead.DcsUtil.RemoveMark(self._noLandingZoneId) DcsUtil.RemoveMark(self._noLandingZoneId)
Spearhead.DcsUtil.RemoveMark(self._dropOffZoneId) DcsUtil.RemoveMark(self._dropOffZoneId)
self:NotifyMissionComplete() self:NotifyMissionComplete()
self._state = "COMPLETED" self._state = "COMPLETED"
end end
@@ -258,7 +263,7 @@ function BuildableMission:CheckCratesInZone()
if self._state == "COMPLETED" then if self._state == "COMPLETED" then
for groupID, markID in pairs(self._markIDsPerGroup) do for groupID, markID in pairs(self._markIDsPerGroup) do
if markID then if markID then
Spearhead.DcsUtil.RemoveMark(markID) DcsUtil.RemoveMark(markID)
self._markIDsPerGroup[groupID] = nil self._markIDsPerGroup[groupID] = nil
end end
end end
@@ -267,7 +272,4 @@ function BuildableMission:CheckCratesInZone()
end end
if not Spearhead.classes then Spearhead.classes = {} end return BuildableMission
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
Spearhead.classes.stageClasses.missions.BuildableMission = BuildableMission
@@ -537,9 +537,4 @@ function RunwayStrikeMission:RunwayToSpearheadZone(runway)
} }
end end
return RunwayStrikeMission
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
Spearhead.classes.stageClasses.missions.RunwayStrikeMission = RunwayStrikeMission
@@ -517,7 +517,4 @@ function ZoneMission:MarkLastContact(unit)
self._lastContactMarkerID = Spearhead.DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point) self._lastContactMarkerID = Spearhead.DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point)
end end
if not Spearhead.classes then Spearhead.classes = {} end return ZoneMission
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
Spearhead.classes.stageClasses.missions.ZoneMission = ZoneMission
@@ -1,5 +1,8 @@
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local DcsUtil = require("classes.util.DcsUtil")
local Util = require("classes.util.Util")
local GlobalConfig = require("classes.configuration.GlobalConfig")
---@class Mission ---@class Mission
---@field name string ---@field name string
@@ -49,7 +52,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief
self.location = database:GetLocationForMissionZone(zoneName) self.location = database:GetLocationForMissionZone(zoneName)
self.missionTypeDisplay = self.missionType self.missionTypeDisplay = self.missionType
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel) self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel)
return true, "success" return true, "success"
end end
@@ -77,11 +80,11 @@ end
---@param groupId number ---@param groupId number
function Mission:ShowBriefing(groupId) function Mission:ShowBriefing(groupId)
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId) local group = DcsUtil.GetPlayerGroupByGroupID(groupId)
if group == nil then return end if group == nil then return end
local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group) local unitType = DcsUtil.getUnitTypeFromGroup(group)
local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType) local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
self._logger:debug("Coords converted: " .. coords) self._logger:debug("Coords converted: " .. coords)
local stateString = self:ToStateString() local stateString = self:ToStateString()
@@ -89,12 +92,12 @@ function Mission:ShowBriefing(groupId)
local briefing = self._missionBriefing local briefing = self._missionBriefing
briefing = Spearhead.Util.replaceString(briefing, "{{coords}}", coords) briefing = Util.replaceString(briefing, "{{coords}}", coords)
briefing = Spearhead.Util.replaceString(briefing, "{{ coords }}", coords) briefing = Util.replaceString(briefing, "{{ coords }}", coords)
local text = "Mission [" .. local text = "Mission [" ..
self.code .. "] " .. self.name .. "\n \n" .. briefing .. " \n \n" .. stateString self.code .. "] " .. self.name .. "\n \n" .. briefing .. " \n \n" .. stateString
trigger.action.outTextForGroup(groupId, text, Spearhead.GlobalConfig:getBriefingTime()); trigger.action.outTextForGroup(groupId, text, GlobalConfig:getBriefingTime());
end end
@@ -135,14 +138,6 @@ function Mission:ToStateString() return "status: in progress" end
--endregion --endregion
if not Spearhead.classes then Spearhead.classes = {} end
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
if not Spearhead.classes.stageClasses.missions.baseMissions then Spearhead.classes.stageClasses.missions.baseMissions = {} end
Spearhead.classes.stageClasses.missions.baseMissions.Mission = Mission
do --aliases do --aliases
--- @alias MissionPriority --- @alias MissionPriority
@@ -168,4 +163,4 @@ do --aliases
end end
return Mission
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
local Util = require("classes.util.Util")
--- @class Logger
--- @field LoggerName string the name of the logger
--- @field LogLevel string the log level of the logger
local LOGGER = {}
do
local PreFix = "Spearhead"
---comment
---@param logger_name any
---@param logLevel LogLevel
---@return Logger
function LOGGER.new(logger_name, logLevel)
LOGGER.__index = LOGGER
local self = setmetatable({}, LOGGER)
self.LoggerName = logger_name or "(loggername not set)"
self.LogLevel = logLevel or "INFO"
return self
end
---@param message any the message
function LOGGER:info(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" then
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
end
end
---comment
---@param message string
function LOGGER:warn(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then
env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
end
end
---@param message any -- the message
function LOGGER:error(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.LogLevel == "ERROR" then
env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
end
end
---@param message any the message
function LOGGER:debug(message)
if message == nil then
return
end
message = Util.toString(message)
if self.LogLevel == "DEBUG" then
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message)
end
end
end
return LOGGER
@@ -0,0 +1,26 @@
---@class MissionEditingWarnings
local MissionEditingWarnings = {}
function MissionEditingWarnings.Add(warningMessage)
table.insert(MissionEditingWarnings, warningMessage or "skip")
end
---@param logger Logger
function MissionEditingWarnings.WriteAll(logger)
if not logger then
return
end
if not MissionEditingWarnings or #MissionEditingWarnings == 0 then
return
end
logger:warn("Mission Editor Warnings:")
for _, warning in ipairs(MissionEditingWarnings) do
logger:warn("- " .. warning)
end
end
return MissionEditingWarnings
+503
View File
@@ -0,0 +1,503 @@
---@class Util
local UTIL = {}
do -- INIT UTIL
---splits a string in sub parts by seperator
---@param input string
---@param seperator string
---@return table result list of strings
function UTIL.split_string(input, seperator)
if seperator == nil then
seperator = " "
end
local result = {}
if input == nil then
return result
end
for str in string.gmatch(input, "[^" .. seperator .. "]+") do
table.insert(result, str)
end
return result
end
---comment
---@param table any
---@return number
function UTIL.tableLength(table)
if table == nil then return 0 end
local count = 0
for _ in pairs(table) do count = count + 1 end
return count
end
---@param orig table
---@return table copy
function UTIL.deepCopyTable(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[UTIL.deepCopyTable(orig_key)] = UTIL.deepCopyTable(orig_value)
end
setmetatable(copy, UTIL.deepCopyTable(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
---Gets a random from the list
---@param list Array
---@return any @random element from the list
function UTIL.randomFromList(list)
local max = #list
if max == 0 or max == nil then
return nil
end
local random = math.random(0, max)
if random == 0 then random = 1 end
return list[random]
end
---@param list Array
---@param start number start
---@param n number length
---@return Array
function UTIL.sublist(list, start, n)
local result = {}
for i = start, n do
result[#result + 1] = list[i]
end
return result
end
---@param str string
---@param find string
---@param replace string
---@return string
function UTIL.replaceString(str, find, replace)
if str == nil then return "" end
if find == nil or replace == nil then return str end
local result = str:gsub(find, replace)
return result
end
local function table_print(tt, indent, done)
done = done or {}
indent = indent or 0
if type(tt) == "table" then
local sb = {}
for key, value in pairs(tt) do
table.insert(sb, string.rep(" ", indent)) -- indent it
if type(value) == "table" and not done[value] then
done[value] = true
table.insert(sb, "[\"" ..key .. "\"]" .. " = {\n");
table.insert(sb, table_print(value, indent + 2, done))
table.insert(sb, string.rep(" ", indent)) -- indent it
table.insert(sb, "},\n");
elseif "number" == type(key) then
table.insert(sb, string.format("\"%s\",\n", tostring(value)))
else
table.insert(sb, string.format(
"[\"%s\"] = \"%s\",\n", tostring(key), tostring(value)))
end
end
return table.concat(sb)
else
return tt .. "\n"
end
end
---comment
---@param str string
---@param findable string
---@param ignoreCase boolean?
---@return boolean
UTIL.startswith = function(str, findable, ignoreCase)
if ignoreCase == true then
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
end
return str:find('^' .. findable) ~= nil
end
---comment
---@param str string
---@param findable string
---@return boolean
UTIL.strContains = function(str, findable)
return str:find(findable) ~= nil
end
---comment
---@param str string
---@param findableTable table
---@return boolean
UTIL.startswithAny = function(str, findableTable)
for key, value in pairs(findableTable) do
if type(value) == "string" and UTIL.startswith(str, value) then return true end
end
return false
end
function UTIL.toString(something)
if something == nil then
return "nil"
elseif "table" == type(something) then
return table_print(something)
elseif "string" == type(something) then
return something
else
return tostring(something)
end
end
---comment
---@param a Vec2
---@param b Vec2
---@return number
function UTIL.VectorDistance2d(a, b)
return math.sqrt((b.x - a.x) ^ 2 + (b.y - a.y) ^ 2)
end
---comment
---@param a Vec3
---@param b Vec3
---@return number
function UTIL.VectorDistance3d(a, b)
return UTIL.vectorMagnitude({ x = a.x - b.x, y = a.y - b.y, z = a.z - b.z })
end
---comment
---@param vec Vec3
---@return number
function UTIL.vectorMagnitude(vec)
return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5
end
---@param vec Vec3
---@return Vec3
function UTIL.vectorNormalize(vec)
local magnitude = UTIL.vectorMagnitude(vec)
if magnitude == 0 then
return { x = 0, y = 0, z = 0 }
end
return { x = vec.x / magnitude, y = vec.y / magnitude, z = vec.z / magnitude }
end
---@param vec Vec2
---@param direction number @in degrees
---@param distance number
---@return Vec2
function UTIL.vectorMove(vec, direction, distance)
local rad = math.rad(direction)
local x = vec.x + (math.cos(rad) * distance)
local y = vec.y + (math.sin(rad) * distance)
return { x = x, y = y}
end
---comment
---@param vec1 Vec2
---@param vec2 Vec2
---@return number in degrees
function UTIL.vectorHeadingFromTo(vec1, vec2)
local dx = vec2.x - vec1.x
local dy = vec2.y - vec1.y
local heading = math.deg(math.atan2(dy, dx))
if heading < 0 then
heading = heading + 360
end
return heading
end
---@param vec1 Vec3
---@param vec2 Vec3
---@return number alignment a number in range [-1,1]. > 0 same direction. < 0 opposite direction
function UTIL.vectorAlignment(vec1, vec2)
local vec1Norm = UTIL.vectorNormalize(vec1)
local vec2Norm = UTIL.vectorNormalize(vec2)
return ((vec1Norm.x * vec2Norm.x) + (vec1Norm.y * vec2Norm.y) + (vec1Norm.z * vec2Norm.z))
end
local function isInComplexPolygon(polygon, x, y)
local function getEdges(poly)
local result = {}
for i = 1, #poly do
local point1 = poly[i]
local point2Index = i + 1
if point2Index > #poly then point2Index = 1 end
local point2 = poly[point2Index]
local edge = { x1 = point1.x, z1 = point1.y, x2 = point2.x, z2 = point2.y }
table.insert(result, edge)
end
return result
end
local edges = getEdges(polygon)
local count = 0;
for _, edge in pairs(edges) do
if (x < edge.x1) ~= (x < edge.x2) and y < edge.z1 + ((x - edge.x1) / (edge.x2 - edge.x1)) * (edge.z2 - edge.z1) then
count = count + 1
-- if (yp < y1) != (yp < y2) and xp < x1 + ((yp-y1)/(y2-y1))*(x2-x1) then
-- count = count + 1
end
end
return count % 2 == 1
end
---comment
---@param polygon Array<Vec2> of pairs { x, y }
---@param x number X location
---@param y number Y location
---@return boolean
function UTIL.IsPointInPolygon(polygon, x, y)
return isInComplexPolygon(polygon, x, y)
end
---@param point Vec3
---@param zone SpearheadTriggerZone
function UTIL.is3dPointInZone(point, zone)
if zone.zone_type == "Polygon" and zone.verts then
if UTIL.IsPointInPolygon(zone.verts, point.x, point.z) == true then
return true
end
else
if (((point.x - zone.location.x) ^ 2 + (point.z - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then
return true
end
end
return false
end
---@param point Vec2
---@param zone SpearheadTriggerZone
function UTIL.is2dPointInZone(point, zone)
if zone.zone_type == "Polygon" and zone.verts then
if UTIL.IsPointInPolygon(zone.verts, point.x, point.y) == true then
return true
end
else
if (((point.x - zone.location.x) ^ 2 + (point.y - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then
return true
end
end
return false
end
---comment
---@param points Array<Vec2> points
---@return Array<Vec2> hullPoints
function UTIL.getConvexHull(points)
if #points == 0 then
return {}
end
---comment
---@param a Vec2
---@param b Vec2
---@param c Vec2
---@return boolean
local function ccw(a, b, c)
return (b.y - a.y) * (c.x - a.x) > (b.x - a.x) * (c.y - a.y)
end
table.sort(points, function(left, right)
return left.y < right.y
end)
local hull = {}
-- lower hull
for _, point in pairs(points) do
while #hull >= 2 and not ccw(hull[#hull - 1], hull[#hull], point) do
table.remove(hull, #hull)
end
table.insert(hull, point)
end
-- upper hull
local t = #hull + 1
for i = #points, 1, -1 do
local point = points[i]
while #hull >= t and not ccw(hull[#hull - 1], hull[#hull], point) do
table.remove(hull, #hull)
end
table.insert(hull, point)
end
table.remove(hull, #hull)
return hull
end
---Splits points into clusters with at least minSeparation between clusters, returns convex hull for each cluster
---@param points Array<Vec2>
---@param minSeparation number
---@return Array<Array<Vec2>> hulls
function UTIL.getSeparatedConvexHulls(points, minSeparation)
if #points == 0 then return {} end
-- Simple clustering: group points that are within minSeparation of each other
local clusters = {}
local assigned = {}
for i, p in ipairs(points) do
if not assigned[i] then
local cluster = { p }
assigned[i] = true
-- Find all points close to p (BFS)
local queue = { i }
while #queue > 0 do
local idx = table.remove(queue)
local base = points[idx]
for j, q in ipairs(points) do
if not assigned[j] then
local dx = base.x - q.x
local dy = base.y - q.y
if (dx * dx + dy * dy) <= (minSeparation * minSeparation) then
table.insert(cluster, q)
assigned[j] = true
table.insert(queue, j)
end
end
end
end
table.insert(clusters, cluster)
end
end
-- Compute convex hull for each cluster
local hulls = {}
for _, cluster in ipairs(clusters) do
local hull = UTIL.getConvexHull(cluster)
if #hull > 0 then
table.insert(hulls, hull)
end
end
return hulls
end
---@param points Array<Vec2>
function UTIL.enlargeConvexHull(points, meters)
if points == nil or #points == 0 then
return {}
end
---@type Array<Vec2>
local allpoints = {}
for _, point in pairs(points) do
table.insert(allpoints, point)
allpoints[#allpoints + 1] = point
allpoints[#allpoints+1] = { x = point.x + meters, y = point.y, }
allpoints[#allpoints+1] = { x = point.x - meters, y = point.y, }
allpoints[#allpoints+1] = { x = point.x, y = point.y + meters, }
allpoints[#allpoints+1] = { x = point.x, y = point.y - meters, }
allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, }
allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, }
allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, }
allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, }
end
return UTIL.getConvexHull(allpoints)
end
---Returns hull points visible from origin (not blocked by hull edges)
---@param hull Array<Vec2>
---@param origin Vec2
---@return Array<Vec2>
function UTIL.GetVisibleHullPointsFromOrigin(hull, origin)
local function segmentsIntersect(a, b, c, d)
-- Helper: returns true if segment ab intersects cd (excluding endpoints)
local function ccw(p1, p2, p3)
return (p3.y - p1.y) * (p2.x - p1.x) > (p2.y - p1.y) * (p3.x - p1.x)
end
return (ccw(a, c, d) ~= ccw(b, c, d)) and (ccw(a, b, c) ~= ccw(a, b, d))
end
local n = #hull
local visible = {}
for i = 1, n do
local p = hull[i]
local isVisible = true
-- Check against all hull edges except those incident to p
for j = 1, n do
local a = hull[j]
local b = hull[(j % n) + 1]
-- skip edges incident to p
if (a ~= p and b ~= p) then
if segmentsIntersect(origin, p, a, b) then
isVisible = false
break
end
end
end
if isVisible then
table.insert(visible, p)
end
end
return visible
end
---Returns the two tangent points ("scratch points") from origin to the convex hull
---@param hull Array<Vec2>
---@param origin Vec2
---@return Array<Vec2>
function UTIL.GetTangentHullPointsFromOrigin(hull, origin)
if hull == nil or #hull <= 0 then
return {}
end
local function orientation(a, b, c)
-- Returns >0 if c is to the left of ab, <0 if to the right, 0 if colinear
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
end
local n = #hull
if n == 0 then return {} end
if n == 1 then return { hull[1] } end
-- Find left tangent: the hull point where all other points are to the right of the line from origin to that point
local function findTangent(isLeft)
local best = 1
for i = 2, n do
local o = orientation(origin, hull[best], hull[i])
if (isLeft and o < 0) or (not isLeft and o > 0) then
best = i
end
end
return hull[best]
end
local leftTangent = findTangent(true)
local rightTangent = findTangent(false)
-- If tangents are the same (can happen if origin is colinear), only return one
if leftTangent.x == rightTangent.x and leftTangent.y == rightTangent.y then
return { leftTangent }
else
return { leftTangent, rightTangent }
end
end
end
return UTIL
+96
View File
@@ -0,0 +1,96 @@
--Single player purpose
local Logger = require("classes.util.Logger")
local DcsUtil = require("classes.util.DcsUtil")
local Database = require("classes.spearhead_db")
local SpearheadEvents = require("classes.spearhead_events")
local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper")
local CapConfig = require("classes.configuration.CapConfig")
local StageConfig = require("classes.configuration.StageConfig")
local Persistence = require("classes.persistence.Persistence")
local SpawnManager = require("classes.helpers.SpawnManager")
local DetectionManager = require("classes.capClasses.detection.DetectionManager")
local GlobalCapManager = require("classes.capClasses.GlobalCapManager")
local GlobalStageManager = require("classes.stageClasses.GlobalStageManager")
local GlobalFleetManager = require("classes.fleetClasses.GlobalFleetManager")
local MissionEditorWarnings = require("classes.util.MissionEditorWarnings")
local defaultLogLevel = "INFO"
if SpearheadConfig and SpearheadConfig.debugEnabled == true then
defaultLogLevel = "DEBUG"
end
local startTime = timer.getTime() * 1000
SpearheadEvents.Init(defaultLogLevel)
local dbLogger = Logger.new("database", defaultLogLevel)
local standardLogger = Logger.new("", defaultLogLevel)
local databaseManager = Database.New(dbLogger)
MissionCommandsHelper.getOrCreate(defaultLogLevel) -- initiate
local capConfig = CapConfig:new();
local stageConfig = StageConfig:new();
local startingStage = stageConfig.startingStage or 1
if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then
standardLogger:info("Persistence enabled")
local persistenceLogger = Logger.new("Persistence", defaultLogLevel)
Persistence.Init(persistenceLogger)
local persistanceStage = Persistence.GetActiveStage()
if persistanceStage then
standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage)
startingStage = persistanceStage
end
else
standardLogger:info("Persistence disabled")
end
local spawnLogger = Logger.new("SpawnManager", defaultLogLevel)
local spawnManager = SpawnManager.new(spawnLogger)
local detectionLogger = Logger.new("DetectionManager", defaultLogLevel)
local detectionManager = DetectionManager.New(detectionLogger)
GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager)
GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager)
GlobalFleetManager.start(databaseManager)
local SetStageDelayed = function(number, time)
SpearheadEvents.PublishStageNumberChanged(number)
return nil
end
timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3)
env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms")
local duration = (timer.getTime() * 1000) - startTime
standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms")
local missionEditorWarningsLogger = Logger.new("MissionEditorWarnings", defaultLogLevel)
MissionEditorWarnings.WriteAll(missionEditorWarningsLogger)
GlobalStageManager:printFullOverview()
--Check lines of code in directory per file:
-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum
-- find . -name '*.lua' | xargs wc -l
--- ==================== DEBUG ORDER OR ZONE VEC ===========================
-- local zone = Spearhead.DcsUtil.getZoneByName("MISSIONSTAGE_99")
-- local count = Spearhead.Util.tableLength(zone.verts)
-- for i = 1, count - 1 do
-- local a = zone.verts[i]
-- local b = zone.verts[i+1]
-- local color = {0,0,0,1}
-- color[i] = 1
-- trigger.action.textToAll(-1, 46+i , { x= a.x, y = 0, z = a.z } , color, {0,0,0}, 24 , true , "" .. i )
-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true)
-- end