From f7c22d348c3d413fc8783a80c0c00dfdc421546f Mon Sep 17 00:00:00 2001 From: dutchie032 Date: Tue, 24 Sep 2024 07:21:27 +0200 Subject: [PATCH] migrated repo --- .vscode/settings.json | 16 + SPEARHEADDEV.miz | Bin 0 -> 121233 bytes SPEARHEADDEV.zip | Bin 0 -> 39915 bytes SpearheadCompile.py | 39 + SpearheadConfig.lua | 2 + capClasses/CapAirbase.lua | 200 ++++ capClasses/CapGroup.lua | 465 ++++++++ capClasses/GlobalCapManager.lua | 61 ++ spearhead.lua | 162 +++ spearhead_base.lua | 1560 +++++++++++++++++++++++++++ spearhead_cap.lua | 31 + spearhead_db.lua | 589 ++++++++++ stageClasses/GlobalStageManager.lua | 22 + stageClasses/Mission.lua | 396 +++++++ stageClasses/Stage.lua | 249 +++++ 15 files changed, 3792 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 SPEARHEADDEV.miz create mode 100644 SPEARHEADDEV.zip create mode 100644 SpearheadCompile.py create mode 100644 SpearheadConfig.lua create mode 100644 capClasses/CapAirbase.lua create mode 100644 capClasses/CapGroup.lua create mode 100644 capClasses/GlobalCapManager.lua create mode 100644 spearhead.lua create mode 100644 spearhead_base.lua create mode 100644 spearhead_cap.lua create mode 100644 spearhead_db.lua create mode 100644 stageClasses/GlobalStageManager.lua create mode 100644 stageClasses/Mission.lua create mode 100644 stageClasses/Stage.lua diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ca275db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "Lua.diagnostics.globals": [ + "missionCommands", + "env", + "Airbase", + "world", + "trigger", + "StaticObject", + "Group", + "Unit", + "Object", + "coalition", + "timer", + "AI" + ] +} \ No newline at end of file diff --git a/SPEARHEADDEV.miz b/SPEARHEADDEV.miz new file mode 100644 index 0000000000000000000000000000000000000000..0d09e53bb3ac2677504ec711901cb6febfa1483e GIT binary patch literal 121233 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 K;r{k3n*RW2B5nKt literal 0 HcmV?d00001 diff --git a/SPEARHEADDEV.zip b/SPEARHEADDEV.zip new file mode 100644 index 0000000000000000000000000000000000000000..a0f304ffb88209860d889870cb1aec49ac563370 GIT binary patch literal 39915 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 literal 0 HcmV?d00001 diff --git a/SpearheadCompile.py b/SpearheadCompile.py new file mode 100644 index 0000000..86f2317 --- /dev/null +++ b/SpearheadCompile.py @@ -0,0 +1,39 @@ +import os +import sys +import glob + +Order = [ + "spearhead_defaults.lua", + "spearhead_base.lua", + "spearhead_events.lua", + "spearhead_dcsbase.lua", + "spearhead_routes.lua", + "spearhead_mission.lua", + "spearhead_db.lua", + "spearhead_stage.lua", + "spearhead_cap.lua", + "spearhead.lua" +] + +def compile(root, target): + compiled = "" + for filename in Order: + path = os.path.join(root, filename) + with open(path,'r') as file: + fileContents = file.read() + compiled += fileContents + print(path) + + with open(target, "w") as targetFile: + targetFile.write(compiled) + + +if __name__ == "__main__" : + args = sys.argv[1:] + root = args[0] + target = args[1] + + print(f"Source: {root}") + print(f"target: {target}") + + compile(root, target) \ No newline at end of file diff --git a/SpearheadConfig.lua b/SpearheadConfig.lua new file mode 100644 index 0000000..b2771f1 --- /dev/null +++ b/SpearheadConfig.lua @@ -0,0 +1,2 @@ +---KEEP THIS LINE +if not SpearheadConfig then SpearheadConfig = {} end diff --git a/capClasses/CapAirbase.lua b/capClasses/CapAirbase.lua new file mode 100644 index 0000000..7bb616f --- /dev/null +++ b/capClasses/CapAirbase.lua @@ -0,0 +1,200 @@ + +local CapBase = {} + +---comment +---@param airbaseId number +---@param database table +---@param logger table +---@param capConfig table +---@param stageConfig table +---@return table +function CapBase:new(airbaseId, database, logger, capConfig, stageConfig) + local o = {} + setmetatable(o, { __index = self }) + + o.groupNames = database:getCapGroupsAtAirbase(airbaseId) + o.database = database + o.airbaseId = airbaseId + o.logger = logger + o.activeStage = 0 + o.capConfig = capConfig + o.spawned = false + + if capConfig == nil then + capConfig = {} + table.insert(Spearhead.MissionEditingWarnings,"CapConfig is nil") + else + if capConfig.minSpeed == nil then Spearhead.MissionEditingWarnings("CapConfig.minSpeed is nil") end + if capConfig.maxSpeed == nil then Spearhead.MissionEditingWarnings("CapConfig.maxSpeed is nil") end + if capConfig.minAlt == nil then Spearhead.MissionEditingWarnings("CapConfig.minAlt is nil") end + if capConfig.maxAlt == nil then Spearhead.MissionEditingWarnings("CapConfig.maxAlt is nil") end + if capConfig.minDurationOnStation == nil then Spearhead.MissionEditingWarnings("CapConfig.minDurationOnStation is nil") end + if capConfig.maxDurationOnStation == nil then Spearhead.MissionEditingWarnings("CapConfig.maxDurationOnStation is nil") end + if capConfig.rearmDelay == nil then Spearhead.MissionEditingWarnings("CapConfig.rearmDelay is nil") end + if capConfig.deathDelay == nil then Spearhead.MissionEditingWarnings("CapConfig.deathDelay is nil") end + end + + o.activeCapStages = (stageConfig or {}).capActiveStages or 10 + + o.lastStatesByName = {} + o.groupsByName = {} + o.PrimaryGroups = {} + o.BackupGroups = {} + + local CheckReschedulingAsync = function(self, time) + self:CheckAndScheduleCAP() + end + + o.OnGroupStateUpdated = function (self, capGroup) + --[[ + There is no update needed for INTRANSIT, ONSTATION or REARMING as the PREVIOUS state already was checked and nothing changes in the actual overal state. + ]]-- + if capGroup.state == Spearhead.internal.CapGroup.GroupState.INTRANSIT + or capGroup.state == Spearhead.internal.CapGroup.GroupState.ONSTATION + or capGroup.state == Spearhead.internal.CapGroup.GroupState.REARMING + then + return + end + timer.scheduleFunction(CheckReschedulingAsync, self, timer.getTime() + 1) + end + + for key, name in pairs(o.groupNames) do + local capGroup = Spearhead.internal.CapGroup:new(name, airbaseId, logger, database, capConfig) + if capGroup then + o.groupsByName[name] = capGroup + + if capGroup.isBackup ==true then + table.insert(o.BackupGroups, capGroup) + else + table.insert(o.PrimaryGroups, capGroup) + end + + capGroup:AddOnStateUpdatedListener(o) + end + end + + o.SpawnIfApplicable = function(self) + self.logger:debug("Check spawns for airbase " .. self.airbaseId ) + for groupName, capGroup in pairs(self.groupsByName) do + + local activeStage = tostring(self.activeStage) + local targetStage = capGroup:GetTargetZone(activeStage) + + if targetStage ~= nil and capGroup.state == Spearhead.internal.CapGroup.GroupState.UNSPAWNED then + capGroup:SpawnOnTheRamp() + self.spawned = true + end + end + end + + o.CheckAndScheduleCAP = function (self) + + self.logger:debug("Check taskings for airbase " .. self.airbaseId ) + + local countPerStage = {} + local requiredPerStage = {} + + --Count back up groups that are active or reassign to the new zone if that's needed + for _, backupGroup in pairs(self.BackupGroups) do + if backupGroup.state == Spearhead.internal.CapGroup.GroupState.INTRANSIT or backupGroup.state == Spearhead.internal.CapGroup.GroupState.ONSTATION then + local supposedTargetStage = backupGroup:GetTargetZone(self.activeStage) + if supposedTargetStage then + if supposedTargetStage ~= backupGroup.assignedStageNumber then + backupGroup:SendToStage(supposedTargetStage) + end + + if countPerStage[supposedTargetStage] == nil then + countPerStage[supposedTargetStage] = 0 + end + countPerStage[supposedTargetStage] = countPerStage[supposedTargetStage] + 1 + else + backupGroup:SendRTB() + end + elseif backupGroup.state == Spearhead.internal.CapGroup.GroupState.RTBINTEN and backupGroup:GetTargetZone(self.activeStage) ~= backupGroup.assignedStageNumber then + backupGroup:SendRTB() + end + end + + --Schedule or reassign primary units if applicable + for _, primaryGroup in pairs(self.PrimaryGroups) do + local supposedTargetStage = primaryGroup:GetTargetZone(self.activeStage) + if supposedTargetStage then + if requiredPerStage[supposedTargetStage] == nil then + requiredPerStage[supposedTargetStage] = 0 + end + + if countPerStage[supposedTargetStage] == nil + then + countPerStage[supposedTargetStage] = 0 + end + + requiredPerStage[supposedTargetStage] = requiredPerStage[supposedTargetStage] + 1 + + if primaryGroup.state == Spearhead.internal.CapGroup.GroupState.READYONRAMP then + if countPerStage[supposedTargetStage] < requiredPerStage[supposedTargetStage] then + primaryGroup:SendToStage(supposedTargetStage) + countPerStage[supposedTargetStage] = countPerStage[supposedTargetStage] + 1 + end + elseif primaryGroup.state == Spearhead.internal.CapGroup.GroupState.INTRANSIT or primaryGroup.state == Spearhead.internal.CapGroup.GroupState.ONSTATION then + if supposedTargetStage ~= primaryGroup.assignedStageNumber then + if countPerStage[supposedTargetStage] < requiredPerStage[supposedTargetStage] then + primaryGroup:SendToStage(supposedTargetStage) + else + countPerStage[supposedTargetStage] = countPerStage[supposedTargetStage] + 1 + primaryGroup:SendRTB() + end + end + countPerStage[supposedTargetStage] = countPerStage[supposedTargetStage] + 1 + elseif primaryGroup.state == Spearhead.internal.CapGroup.GroupState.RTBINTEN and primaryGroup:GetTargetZone(self.activeStage) ~= primaryGroup.assignedStageNumber then + primaryGroup:SendRTB() + end + else + if primaryGroup.state == Spearhead.internal.CapGroup.GroupState.INTRANSIT or primaryGroup.state == Spearhead.internal.CapGroup.GroupState.ONSTATION then + primaryGroup:SendRTB() + end + end + end + + for _, backupGroup in pairs(self.BackupGroups) do + if backupGroup.state == Spearhead.internal.CapGroup.GroupState.READYONRAMP then + local supposedTargetStage = backupGroup:GetTargetZone(self.activeStage) + if supposedTargetStage then + if countPerStage[supposedTargetStage] == nil then + countPerStage[supposedTargetStage] = 0 + end + + if countPerStage[supposedTargetStage] < requiredPerStage[supposedTargetStage] then + backupGroup:SendToStage(supposedTargetStage) + countPerStage[supposedTargetStage] = countPerStage[supposedTargetStage] + 1 + end + end + end + end + end + + o.OnStageNumberChanged = function (self, number) + self.activeStage = number + self:SpawnIfApplicable() + timer.scheduleFunction(CheckReschedulingAsync, self, timer.getTime() + 5) + end + + ---Check if any CAP is active when a certain stage is active + ---@param self table + ---@param stageNumber number + ---@return boolean + o.IsBaseActiveWhenStageIsActive = function (self, stageNumber) + for _, group in pairs(self.PrimaryGroups) do + local target = group:GetTargetZone(stageNumber) + if target ~= nil then + return true + end + end + return false + end + + Spearhead.Events.AddStageNumberChangedListener(o) + return o +end + +if not Spearhead.internal then Spearhead.internal = {} end +Spearhead.internal.CapAirbase = CapBase \ No newline at end of file diff --git a/capClasses/CapGroup.lua b/capClasses/CapGroup.lua new file mode 100644 index 0000000..751db8a --- /dev/null +++ b/capClasses/CapGroup.lua @@ -0,0 +1,465 @@ +--[[ + +#### Why? +For Spearhead there's a lot of stages and states the mission can be in.
+To make sure CAP units will be at the place where the mission maker expects them to be there's a naming convention that should help.
+You as a mission maker will have full controll over where they are supposed to be, the script will take care of getting them there. +The CAP manager's goal is to provide dedicated aircraft scheduling that doesn't reset every stage reset. + +Naming: CAP\_\<"A" | B"\>\_\ +#### Config: +``` +1 at x: [] +n and n at x: [,] +n till n at x: [-] +n till n and n at x: [-,] +n till n at Active: [-]A + +divider: | + +examples: + +CAP_A[1-4,6]7|[5,7]8_SomeName => Will fly CAP at stage 7 when stages 1 through 4 and 6 are active and will fly CAP at 8 when 5 and 7 are active +CAP_A[2-5]5|[6]6_SomeName => Will fly CAP at stage 5 when stages 2 through 5 active and will fly CAP at 6 when 6 is active +CAP_A[1-5]A|[6]7_SomeName => Will fly CAP at the ACTIVE stage if Stages 1-5 are active. Basically following the active stages. Then when 6 is active it will fly in 7 + +CAP_B[1-5]A|[6]7_SomeName => Will fly BACKUP CAP for the active zones 1 through 5 and back up for 7 when 6 is active. + +``` + +### How many? And how to add backups? + +To fascilitate a nice flow of the mission and also make sure it doesn't oversaturate the zones with aircraft the script works with a Active/Backup system in the naming.
+This really doesn't mean much per se once the mission runs, but most importantly is that the A units define how many groups there should be max in a zone at a time.
+The B units will simply be used to fill that amount if the A units can't due to RTB, Death, Rearming etc.
+ +#### Example + +Take the units: +``` +CAP_A[1-5]A_SomeName1 +CAP_A[1-5]A_SomeName2 +CAP_B[1-3,5]A_SomeName +``` +`CAP_A...` units are primary units where the `CAP_B...` units are the backups.
+In this case the CAP manager sees that for stages 1 through 5 this configuration requires 2 groups in the active zone.
+If one of those 2 groups dies or is going back to base the B group will be used to top up the CAP units at that zone.
+After scheduling the B units the A units that are back at base ready on the ramp will also not be scheduled until the CAP units that are active in the zone (inlcuding B units) drop below the required CAP unit (of 2 in this example) + +In this example there is no Backup unit for zone 4. This might quiet down the CAP a little as the Active groups will have to rearm and refuel without there being any backup. + +### What the cap manager does: +- Spawn aircraft on the ramp (or despawn when they are not needed anymore for culling) +- Send out aircraft based on where they are supposed to be +- Send Aircraft RTB after X time.
+ RTB in this sense means back to its base of origin. Not the closest friendly base like DCS does. +- Simulates Rearming and then sending them out when needed. +- Delays aircraft for X amount of time before spawning and rearming after a groups demise. +- Aircraft are spawned on the ramp so OCA does have effect. (Be sure to also take a look at the Airbase and SAM spawning for defences) + +### Future Ideas + +- Aircraft rearm hubs with finite spawns on other airbases that get replenished by aircraft flying in. + + +]] -- + +local CapHelper = {} +do + ---comment + ---@param groupName string + ---@return table? + CapHelper.ParseGroupName = function(groupName) + local split_string = Spearhead.Util.split_string(groupName, "_") + local partCount = Spearhead.Util.tableLength(split_string) + if partCount >= 3 then + local result = {} + result.zonesConfig = {} + + -- CAP_[1-5]5|[6]6|[7]7_Sukhoi + -- CAP_[1-5,7]A|[6]7_Sukhoi + + local configPart = split_string[2] + local first = configPart:sub(1, 1) + if first == "A" then + result.isBackup = false + configPart = string.sub(configPart, 2, #configPart) + elseif first == "B" then + configPart = string.sub(configPart, 2, #configPart) + result.isBackup = true + elseif first == "[" then + result.isBackup = false + else + table.insert(Spearhead.MissionEditingWarnings, "Could not parse the CAP config for group: " .. groupName) + return nil + end + + local subsplit = Spearhead.Util.split_string(configPart, "|") + if subsplit then + for key, value in pairs(subsplit) do + local keySplit = Spearhead.Util.split_string(value, "]") + local targetZone = keySplit[2] + local allActives = string.sub(keySplit[1], 2, #keySplit[1]) + local commaSeperated = Spearhead.Util.split_string(allActives, ",") + for _, value in pairs(commaSeperated) do + local dashSeperated = Spearhead.Util.split_string(value, "-") + if Spearhead.Util.tableLength(dashSeperated) > 1 then + local from = tonumber(dashSeperated[1]) + local till = tonumber(dashSeperated[2]) + + for i = from, till do + if targetZone == "A" then + result.zonesConfig[tostring(i)] = tostring(i) + else + result.zonesConfig[tostring(i)] = tostring(targetZone) + end + end + else + if targetZone == "A" then + result.zonesConfig[tostring(dashSeperated[1])] = tostring(dashSeperated[1]) + else + result.zonesConfig[tostring(dashSeperated[1])] = tostring(targetZone) + end + end + end + end + end + return result + else + table.insert(Spearhead.MissionEditingWarnings, + "CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) + return nil + end + end +end + +---comment +---@param input table { groupName, task, logger } +---@param time number +---@return nil +local function setTaskAsync(input, time) + local task = input.task + local groupName = input.groupName + local group = Group.getByName(groupName) + + if task then + group:getController():setTask(task) + if input.logger ~= nil then + input.logger:debug("task set succesfully to group " .. groupName) + end + end + return nil +end + +local CapGroup = {} + +CapGroup.GroupState = { + UNSPAWNED = 0, + READYONRAMP = 1, + INTRANSIT = 2, + ONSTATION = 3, + RTBINTEN = 4, + RTB = 5, + DEAD = 6, + REARMING = 7, + DESPAWNED = 8 +} + +local function SetReadyOnRampAsync(self, time) + self:SetState(CapGroup.GroupState.READYONRAMP) +end + +---comment +---@param groupName string +---@param airbaseId number +---@param logger table logger dependency injection +---@param database table database dependency injection +---@param capConfig table config dependency injection +---@return table? o +function CapGroup:new(groupName, airbaseId, logger, database, capConfig) + local o = {} + setmetatable(o, { __index = self }) + + local RESPAWN_AFTER_TOUCHDOWN_SECONDS = 180 + + -- initials + o.groupName = groupName + o.airbaseId = airbaseId + o.logger = logger + o.database = database + + local parsed = CapHelper.ParseGroupName(groupName) + if parsed == nil then return nil end + o.capZonesConfig = parsed.zonesConfig + o.isBackup = parsed.isBackup + + --vars + o.assignedStageName = nil + o.assignedStageNumber = nil + + o.state = CapGroup.GroupState.UNSPAWNED + o.aliveUnits = {} + o.landedUnits = {} + o.unitCount = 0 + o.onStationSince = 0 + o.currentCapTaskingDuration = 0 + + --config + o.capConfig = {} + + if not capConfig then capConfig = {} end + o.capConfig.maxDeviationRange = capConfig.maxDeviationRange + o.capConfig.minSpeed = (capConfig.minSpeed or 400) * 0.514444 + o.capConfig.maxSpeed = (capConfig.maxSpeed or 500) * 0.514444 + o.capConfig.minAlt = (capConfig.minAlt or 8000) * 0.3048 + o.capConfig.maxAlt = (capConfig.maxAlt or 27000) * 0.3048 + o.capConfig.minDurationOnStation = capConfig.minDurationOnStation or 600 + o.capConfig.maxDurationOnstation = capConfig.maxDurationOnStation or 1800 + o.capConfig.rearmDelay = capConfig.rearmDelay or 300 + + ---comment + ---@param self table + ---@param currentActive number + ---@return table + o.GetTargetZone = function (self, currentActive) + return self.capZonesConfig[tostring(currentActive)] + end + + o.SetState = function(self, state) + self.state = state + self:PublishUnitUpdatedEvent() + end + + o.StartRearm = function(self) + self:SpawnOnTheRamp() + self:SetState(CapGroup.GroupState.REARMING) + timer.scheduleFunction(SetReadyOnRampAsync, self, timer.getTime() + self.capConfig.rearmDelay - RESPAWN_AFTER_TOUCHDOWN_SECONDS) + end + + o.SpawnOnTheRamp = function(self) + self.aliveUnits = {} + self.landedUnits = {} + self.onStationSince = 0 + + local group = Spearhead.DcsUtil.SpawnGroupTemplate(self.groupName, nil, nil, true) + if group then + self.unitCount = group:getInitialSize() + + if self.state == CapGroup.GroupState.UNSPAWNED then + self:SetState(CapGroup.GroupState.READYONRAMP) + end + + for _, unit in pairs(group:getUnits()) do + local name = unit:getName() + self.aliveUnits[name] = true + self.landedUnits[name] = false + end + end + end + + o.Despawn = function(self) + Spearhead.DcsUtil.DestroyGroup(self.groupName) + end + + o.SendRTB = function(self) + local group = Group.getByName(self.groupName) + if group and group:isExist() then + local speed = math.random(self.capConfig.minSpeed, self.capConfig.maxSpeed) + local rtbTask, errormessage = Spearhead.RouteUtil.CreateRTBMission(self.groupName, self.airbaseId, speed) + if rtbTask then + timer.scheduleFunction(setTaskAsync, { task = rtbTask, groupName = self.groupName, logger = self.logger }, timer.getTime() + 3) + else + self.logger:error("No RTB task could be created for group: " .. self.groupName .. " due to " .. errormessage) + end + end + end + + ---Starts and send this group to perform CAP at a stage + ---@param self any + ---@param stageZoneNumber string + o.SendToStage = function(self, stageZoneNumber) + if self.state == CapGroup.GroupState.DEAD or self.state == CapGroup.GroupState.RTB then + return --Can't task a unit that's dead or RTB + end + + local stageZoneName = self.database:getStageZoneByStageNumber(stageZoneNumber) + self.assignedStageNumber = stageZoneNumber + self.assignedStageName = stageZoneName + local group = Group.getByName(self.groupName) + if group and group:isExist() then + local zone = Spearhead.DcsUtil.getZoneByName(stageZoneName) + if zone then + self.logger:debug("Sending group out " .. self.groupName) + local controller = group:getController() + local capPoints = database:getCapRouteInZone(stageZoneName, self.airbaseId) or { point1 = { x = zone.x, z = zone.z }, point2 = nil } + + local altitude = math.random(self.capConfig.minAlt, self.capConfig.maxAlt) + local speed = math.random(self.capConfig.minSpeed, self.capConfig.maxSpeed) + local attackHelos = false + local deviationDistance = self.capConfig.maxDeviationRange + local capTask + if self.state == CapGroup.GroupState.ONRAMP or self.onStationSince == 0 then + controller:setCommand({ + id = 'Start', + params = {} + }) + local duration = math.random(self.capConfig.minDurationOnStation, self.capConfig + .maxDurationOnstation) + self.logger:debug("random schedule min: " .. + tostring(self.capConfig.minDurationOnStation or "nil") .. + " max: " .. tostring(self.capConfig.maxDurationOnstation or "nil") .. " actual " .. duration) + self.currentCapTaskingDuration = duration + + + capTask = Spearhead.RouteUtil.createCapMission(self.groupName, self.airbaseId, capPoints.point1, capPoints.point2, altitude, speed, duration, attackHelos, deviationDistance) + else + local duration = self.currentCapTaskingDuration - (timer.getTime() - o.onStationSince) + capTask = Spearhead.RouteUtil.createCapMission(self.groupName, self.airbaseId, capPoints.point1, capPoints.point2, altitude, speed, duration, attackHelos, deviationDistance) + end + + if capTask then + timer.scheduleFunction(setTaskAsync, + { task = capTask, groupName = self.groupName, logger = self.logger }, timer.getTime() + 3) + end + self:SetState(CapGroup.GroupState.INTRANSIT) + end + end + end + + ---Starts and send a unit to another airbase + ---@param self table + ---@param airdomeId any + o.SendToAirbase = function(self, airdomeId) + self.airbaseId = airdomeId + local speed = math.random(self.capConfig.minSpeed, self.capConfig.maxSpeed) + local rtbTask = Spearhead.RouteUtil.CreateRTBMission(self.groupName, airdomeId, speed) + local group = Group.getByName(self.groupName) + local controller = group:getController() + controller:setCommand({ + id = 'Start', + params = {} + }) + timer.scheduleFunction(setTaskAsync, { task = rtbTask, groupName = self.groupName, logger = self.logger }, + timer.getTime() + 5) + end + + o.OnGroupRTB = function(self, groupName) + if groupName == self.groupName then + self.logger:debug("Setting group " .. + groupName .. + " to state RTB after a total of " .. + timer.getTime() - self.onStationSince .. "s of the " .. self.currentCapTaskingDuration .. "s") + self:SetState(CapGroup.GroupState.RTB) + end + end + + o.OnGroupRTBInTen = function(self, groupName) + if groupName == self.groupName then + self:SetState(CapGroup.GroupState.RTBINTEN) + end + end + + o.OnGroupOnStation = function(self, groupName) + if groupName == self.groupName then + self.onStationSince = timer.getTime() + self.logger:debug("Setting group " .. groupName .. " to state Onstation") + self:SetState(CapGroup.GroupState.ONSTATION) + end + end + + ---comment + ---@param self table + ---@param proActive boolean Will check all units in group for aliveness + o.UpdateState = function(self, proActive) + local landed = false + local landedCount = 0 + for name, landedBool in pairs(self.landedUnits) do + if landedBool == true then + landedCount = landedCount + 1 + landed = true + end + end + + local deadCount = 0 + for name, isAlive in pairs(self.aliveUnits) do + if isAlive == false then + deadCount = deadCount + 1 + end + end + + local function DelayedStartRearm(input, time) + local capGroup = input.self + capGroup:StartRearm() + end + + if landedCount + deadCount == self.unitCount then + if landed then + timer.scheduleFunction(DelayedStartRearm, { self = self }, timer.getTime() + RESPAWN_AFTER_TOUCHDOWN_SECONDS) + else + local delay = self.capConfig.deathDelay - self.capConfig.rearmDelay + RESPAWN_AFTER_TOUCHDOWN_SECONDS + timer.scheduleFunction(DelayedStartRearm, { self = self }, timer.getTime() + delay) + end + end + end + + o.eventListeners = {} + ---comment + ---@param self table + ---@param listener table object with function OnGroupStateUpdated(capGroupTable) + o.AddOnStateUpdatedListener = function(self, listener) + if type(listener) ~= "table" then + self.logger:error("Listener not of type table for AddOnStateUpdatedListener") + return + end + + if listener.OnGroupStateUpdated == nil then + self.logger:error("Listener does not implement OnGroupStateUpdated") + return + end + table.insert(self.eventListeners, listener) + end + + o.PublishUnitUpdatedEvent = function(self) + for _, callable in pairs(self.eventListeners) do + local _, error = pcall(function() + callable:OnGroupStateUpdated(self) + end) + if error then + self.logger:error(error) + end + end + end + + o.OnUnitLanded = function(self, initiatorUnit, airbase) + if airbase then + local airdomeId = airbase:getID() + self.airbaseId = airdomeId + end + local name = initiatorUnit:getName() + self.logger:debug("Received unit land event for unit " .. name .. " of group " .. self.groupName) + + self.landedUnits[name] = true + self:UpdateState(false) + end + + o.OnUnitLost = function(self, initiatorUnit) + self.logger:debug("Received unit lost event for group " .. self.groupName) + if initiatorUnit then + self.aliveUnits[initiatorUnit:getName()] = false + end + self:UpdateState(false) + end + + Spearhead.Events.addOnGroupRTBListener(o.groupName, o) + Spearhead.Events.addOnGroupRTBInTenListener(o.groupName, o) + Spearhead.Events.addOnGroupOnStationListener(o.groupName, o) + local units = Group.getByName(groupName):getUnits() + for key, unit in pairs(units) do + Spearhead.Events.addOnUnitLandEventListener(unit:getName(), o) + Spearhead.Events.addOnUnitLostEventListener(unit:getName(), o) + end + return o +end + +if not Spearhead.internal then Spearhead.internal = {} end +Spearhead.internal.CapGroup = CapGroup diff --git a/capClasses/GlobalCapManager.lua b/capClasses/GlobalCapManager.lua new file mode 100644 index 0000000..500671a --- /dev/null +++ b/capClasses/GlobalCapManager.lua @@ -0,0 +1,61 @@ +local GlobalCapManager = {} +do + local airbasesPerStage = {} + local allAirbasesByName = {} + local activeAirbasesPerActiveStage = {} + local unitsPerzonePerStage = {} + + local initiated = false + + function GlobalCapManager.start(database, capConfig, stageConfig) + if initiated == true then return end + + local logger = Spearhead.LoggerTemplate:new("AirbaseManager", Spearhead.config.logLevel) + + local zones = database:getStagezoneNames() + if zones then + for key, stageName in pairs(zones) do + if airbasesPerStage[stageName] == nil then + airbasesPerStage[stageName] = {} + end + + local airbaseIds = database:getAirbaseIdsInStage(stageName) + if airbaseIds then + for _, id in pairs(airbaseIds) do + local airbaseName = Spearhead.DcsUtil.getAirbaseName(id) + if airbaseName then + local airbaseSpecificLogger = Spearhead.LoggerTemplate:new("CAP_" .. airbaseName, Spearhead.config.logLevel) + local airbase = Spearhead.internal.CapAirbase:new(id, database, airbaseSpecificLogger, capConfig, stageConfig) + if airbase then + table.insert(airbasesPerStage[stageName], airbase) + allAirbasesByName[airbaseName] = airbase + end + end + end + end + end + end + + logger:info("Initiated " .. Spearhead.Util.tableLength(allAirbasesByName) .. " airbases for cap") + initiated = true + + local InfoFunctions = {} + + ---returns if there is CAP active + ---@param zoneName any + ---@param activeZoneNumber number + ---@return boolean + InfoFunctions.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber) + for _, airbase in pairs(airbasesPerStage[zoneName]) do + if airbase:IsBaseActiveWhenStageIsActive(activeZoneNumber) == true then + return true + end + end + return false + end + + Spearhead.capInfo = InfoFunctions + end +end + +Spearhead.internal.GlobalCapManager = GlobalCapManager diff --git a/spearhead.lua b/spearhead.lua new file mode 100644 index 0000000..16d2ff6 --- /dev/null +++ b/spearhead.lua @@ -0,0 +1,162 @@ +--[[ + The Mission Manager creates a way to create missions in a stage like manner with missions without having to worry about monitoring and triggering of said missions. + + The Mission manager assumes players are BLUE and are fighting RED. (Which countries and spawns, that's up to you) + + Mission Naming TriggerZone: + Stage: MISSIONSTAGE__Name + Mission: MISSION__Name + Random Mission: RANDOMMISSION___ + + IMPORTANT NOTES + - DO NOT put mission zones inside of mission zones. + - DO NOT let stage zones overlap (or without there being anything indide of said overlap, this includes airbases) + + + Stage Naming: MISSIONSTAGE__Name + NOTE: Multiple stages can have an order number. + This gives you the opportunity to add multiple zones in a stage. + All stages need to be completed for the next stage order to start. + + Player SPAWNS + Airbases + It is assumed all player spawns are Dynamic slots. + These work relatively nicely nowadays and with a dynamic mission it can provide the best experience. + FARPS + Need to be in TriggerZone with name convention: "FARP_" + will be removed at start and activated inside of the active stage only, so be wary of where you place them. + + Mission Types and their logic: + Random Missions: + To maximise replayability randomisation is directly supported. + There is 2 ways. First you can randomise the units inside of the mission zone the second way is to randomise the mission zone altogether. + + 1. Randomising the units in the mission. + You can use the "Chance" function for groups to spawn or not spawn groups inside of a mission zone. + The framework will only take control over the units after the initial spawn and will therefore not spawn units that did not get spawned on initial creation. + NOTE: This however gives the least predictable outcome and can easily lead to empty missions. + + 2. Randomised mission zones + The best way to randomise it to create X amount of trigger zones with the same mission and let the framework pick 1 on initialisation. + Naming convention: RANDOMMISSION___ (eg. RANDOMMISSION_BAI_BYRON_1 and RANDOMMISSION_BAI_BYRON_2) + RANDOMMISSION: Recogniser + tasking: the tasking just like any other mission + NAME: Codename of the mission. (Use single word only for commands later on) + number: can be any number. Only intention it to make it unique for the editor to not freak out. + + The framework will recognise that RANDOMMISSION_BAI_BYRON_1 and RANDOMMISSION_BAI_BYRON_2 compete against each other and will select a random one and add that to the stage. + After that a random mission will act just like any other mission. + + TIP: If you want a mission that doesn't always spawn: You can do something like the following example: + - RANDOMMISSION_BAI_BYRON_1 => The mission you want to spawn about 1 every 4 times with the units and description + - RANDOMMISSION_BAI_BYRON_2 => Empty trigger zone + - RANDOMMISSION_BAI_BYRON_3 => Empty trigger zone + - RANDOMMISSION_BAI_BYRON_4 => Empty trigger zone + + Special Types: + SAM + All SAM missions will be spawned during the stage, so there's no random pop-ups + SAM missions will be have slightly different ways of briefing and will be shown in the overview of a stage as "known air defenses". + SAMS however do not count towards the completion of the zone and will be despawned once all other missions are done. + SAM missions of the NEXT 2 stages (by order) will also be spawned. + This makes it so you can create defenses of airfields where CAP units are spawned and add long range defenses without having to make the stage huge. + Eg. If MISSIONSTAGE_1_Name is active then all stages with numbers 2 and 3 will also have active SAMS. + + SAM vs DEAD + Generally best practive: Use SAM missions for long range sams that need to be active for longer. + Use DEAD for shorter range popup sams like moving SHORADS. + + + AIRBASES + Airbases have a special logic to them. This is to make sure that it's manageable which bases are used by friendly forces after pushing along. + Capturable bases can be selected and units on airbases are managed. + + Logic is based on the starting coalition of the base. + RED + The base will be used for CAP of the enemy. + On Capture the base will turn NEUTRAL. + Units inside of the airport circle will despawn on the Stage completion. + NEUTRAL + Nothing will be done. It will not be used and units around the airport will not be specifically managed. + BLUE + Airbase will be set to "RED" on intialisation. + All blue units will be despawned and red units spawned on activation of the zone. + When the zone is captured by blue (by finishing the missions), all red units will be removed and all blue units inside it will spawn. + + Airbase Units + All units inside of the circle of the airbase (shown in the me) and not in a mission zone will be regarded as a airbase unit and spawned when the airbase becomes active. + + Missions on airbases. + Missions at airbases are perfectly possible. Any unit that is part of that mission will not be regarded as an "Airbase unit" + + AWACS + ENEMY + An enemy awacs will be spawned in the active stage + 2 unless it's disabled with Config.DisableAwacs. + TODO: AWACS logic + FRIENDLY + Friendly AWACS will be spawned at the ACTIVE stage - 2. Which means that at the start there will be no awacs. + There is one awacs spawned per stage with a delay of 15 minutes delay for respawn per default. + If there is enough blue fighters a red fighter group will be spawned randomly to try and intercept the AWACS. + A message will pop up and players are expected to defend it. + This can be disabled wth Config.DisableAwacsInterceptTask + + + SCRIPTERS + This area of the documentation is for mission makers that want to hook into the framework from their own scripts. + This script will expose flags of it's state, but there are no public methods to alter the framework (at this time). + + FLAGS: + TODO: Expose flags for stage and other metrics +]] -- + +--[[ + TODOLIST: + - FARPS and Airbases V + - RANDOM missions + + - CAP Manager + - Mission Activation + - OPTIONAL Drawings + +]] -- + + +local dbLogger = Spearhead.LoggerTemplate:new("database", Spearhead.config.logLevel) +local databaseManager = Spearhead.DB:new(dbLogger) + +local capConfig = { + maxDeviationRange = 32186, --20NM -- sets max deviation before flight starts pulling back, + minSpeed = 400, + maxSpeed = 500, + minAlt = 18000, + maxAlt = 28000, + minDurationOnStation = 1800, + maxDurationOnStation = 2700, + rearmDelay = 600, + deathDelay = 1800 +} + +local stageConfig = { + preActivatedStages = 3, + capActiveStages = 4 +} + +Spearhead.internal.GlobalCapManager.start(databaseManager, capConfig, stageConfig) +Spearhead.internal.GlobalStageManager.start(databaseManager) + +local activateStage = function (number) + local succ, err = pcall( function () + Spearhead.Events.PublishStageNumberChanged(number) + end) + env.error(err) +end + +missionCommands.addCommand("stage1", {}, activateStage, 1) +missionCommands.addCommand("stage2", {}, activateStage, 2) +missionCommands.addCommand("stage3", {}, activateStage, 3) +missionCommands.addCommand("stage4", {}, activateStage, 4) + +Spearhead.LoadingDone() +--Check lines of code in directory per file: +-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name } +-- find . -name '*.lua' | xargs wc -l \ No newline at end of file diff --git a/spearhead_base.lua b/spearhead_base.lua new file mode 100644 index 0000000..c2872f5 --- /dev/null +++ b/spearhead_base.lua @@ -0,0 +1,1560 @@ +--1 + +--- DEFAULT Values +if Spearhead == nil then Spearhead = {} end + +Spearhead.config = {} +Spearhead.config.logLevel = 0 + +--Set SpearheadConfig if nil without values +if not SpearheadConfig then SpearheadConfig = {} end + +if not SpearheadConfig.CAP then SpearheadConfig.CAP = {} end +Spearhead.config.CAP = { + + --- Enables or disables the cap altogether. + enable = SpearheadConfig.CAP.enable or true, + + --- Sets the AI cap units + engageHelos = SpearheadConfig.CAP.engageHelos or false, + + --- sets the tech type. { 0 = custom, 1 = Modern , 2 = WW2 } + technologyType = SpearheadConfig.CAP.technologyType or 1, + + bingoRatio = SpearheadConfig.CAP.bingoRatio or 0.20, + +} + +if not SpearheadConfig.Stages then SpearheadConfig.Stages = {} end + +Spearhead.config.Stages = { + preActivatedStages = SpearheadConfig.Stages.maxPreActivated or 3, + capActiveStages = SpearheadConfig.Stages.capActiveStages or 4, +} + +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 + + ---Gets a random from the list + ---@param list table + 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 + + 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 + ---@return boolean + UTIL.startswith = 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 +end +Spearhead.Util = UTIL + +---DCS UTIL Takes inspiration from MIST but only takes the things it needs, changes for DCS updates and different vision for advanced mission scripting stuff. +---It also adds functions that make the other TDCS scripts easier without taking too much "control" away like MOOSE can sometimes. +local DCS_UTIL = {} +do -- INIT DCS_UTIL + do -- local databases + --[[ + groupdata = { + category, + country_id, + group_template + } + ]] -- + DCS_UTIL.__miz_groups = {} + DCS_UTIL.__groupNames = {} + DCS_UTIL.__blueGroupNames = {} + DCS_UTIL.__redGroupNames = {} + --[[ + zone = { + name, + + zone_type, + x, + z, + radius + verts, + + } + ]] -- + DCS_UTIL.__trigger_zones = {} + end + + DCS_UTIL.Coalition = + { + NEUTRAL = 0, + RED = 1, + BLUE = 2 + } + + DCS_UTIL.ZoneType = { + Cilinder = 0, + Polygon = 2 + } + + DCS_UTIL.GroupCategory = { + AIRPLANE = 0, + HELICOPTER = 1, + GROUND = 2, + SHIP = 3, + TRAIN = 4, + STATIC = 5 --CUSTOM CATEGORY + } + + DCS_UTIL.__airbaseNamesById = {} + + DCS_UTIL.__airportsStartingCoalition = {} + DCS_UTIL.__warehouseStartingCoalition = {} + function DCS_UTIL.__INIT() + do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment + do -- init group tables + for coalition_name, coalition_data in pairs(env.mission.coalition) do + local coalition_nr = DCS_UTIL.stringToCoalition(coalition_name) + if coalition_data.country then + for country_index, country_data in pairs(coalition_data.country) do + for category_name, categorydata in pairs(country_data) do + local category_id = DCS_UTIL.stringToGroupCategory(category_name) + 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 + local name = group.name + if category_id == DCS_UTIL.GroupCategory.STATIC then + local unit = group.units[1] + name = unit.name + local staticObj = { + heading = unit.heading, + name = unit.name, + x = unit.x, + y = unit.y, + type = unit.type, + dead = group.dead + } + + if string.lower(unit.category) == "planes" then + staticObj.livery_id = unit.livery_id + end + + group = staticObj + end + + table.insert(DCS_UTIL.__groupNames, name) + DCS_UTIL.__miz_groups[name] = + { + category = category_id, + country_id = country_data.id, + group_template = group + } + + if coalition_nr == 1 then + table.insert(DCS_UTIL.__redGroupNames, name) + elseif coalition_nr == 2 then + table.insert(DCS_UTIL.__blueGroupNames, name) + end + end + end + end + end + end + end + end + + do --init trigger zones + for i, trigger_zone in pairs(env.mission.triggers.zones) do + local verticies = {} + if trigger_zone.verticies and type(trigger_zone.verticies) == "table" then + for ii, vert in pairs(trigger_zone.verticies) do + table.insert(verticies, { x = vert.x, z = vert.y }) + end + end + + --see if this works. Swap 3 and 4 to make sure points are ordered and edges can be created + local p4 = verticies[4] + local p3 = verticies[3] + verticies[3] = p4 + verticies[4] = p3 + + local zone = { + name = trigger_zone.name, + zone_type = trigger_zone.type, + x = trigger_zone.x, + z = trigger_zone.y, + radius = trigger_zone.radius, + verts = verticies + } + + DCS_UTIL.__trigger_zones[zone.name] = zone + end + end + + do -- init airports and warehouses + if env.warehouses.airports then + for warehouse_id, value in pairs(env.warehouses.airports) do + if warehouse_id ~= nil then + warehouse_id = tostring(warehouse_id) or "nil" + local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition) + DCS_UTIL.__airportsStartingCoalition[warehouse_id] = coalitionNumber + end + end + end + + if env.warehouses.warehouses then + DCS_UTIL.__warehouseStartingCoalition[-1] = "placeholder" + for warehouse_id, value in pairs(env.warehouses.warehouses) do + if warehouse_id ~= nil then + warehouse_id = tostring(warehouse_id) or "nil" + local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition) + DCS_UTIL.__warehouseStartingCoalition[warehouse_id] = coalitionNumber + end + end + end + end + + do -- fill airbaseNames + local airbases = world.getAirbases() + if airbases then + for _, airbase in pairs(airbases) do + local name = airbase:getName() + local id = tostring(airbase:getID()) + + if name and id then + DCS_UTIL.__airbaseNamesById[id] = name + end + end + end + end + end + end + + ---maps the coalition name to the DCS coalition integer + ---@param input string the name + ---@return integer + function DCS_UTIL.stringToCoalition(input) + --[[ + coalition.side = { + NEUTRAL = 0 + RED = 1 + BLUE = 2 + } + ]] -- + local input = string.lower(input) + if input == 'neutrals' or input == "neutral" or input == "0" then + return DCS_UTIL.Coalition.NEUTRAL + end + + if input == 'red' or input == "1" then + return DCS_UTIL.Coalition.RED + end + + if input == 'blue' or input == "2" then + return DCS_UTIL.Coalition.BLUE + end + + return -1 + end + + ---checks if the groupname is a static group + ---@param groupName any + function DCS_UTIL.IsGroupStatic(groupName) + return DCS_UTIL.__miz_groups[groupName].category == 5; + end + + ---comment + ---@param groupName string destroy the given group + function DCS_UTIL.DestroyGroup(groupName) + if DCS_UTIL.IsGroupStatic(groupName) then + local object = StaticObject.getByName(groupName) + if object ~= nil then + object:destroy() + end + else + local group = Group.getByName(groupName) + if group and group:isExist() then + group:destroy() + end + end + end + + ---comment + ---@param polygon table of pairs { x, z } + ---@param x number X location + ---@param z number Y location + ---@return boolean + function DCS_UTIL.IsPointInPolygon(polygon, x, z) + local function isInComplexPolygon(polygon, x, z) + local function getEdges(poly) + local moved = {} + moved[#poly] = poly[1] + for i = 2, #poly do + moved[i - 1] = poly[i] + end + + local result = {} + for i = 1, #poly do + local point1 = moved[i] + local point2 = poly[i] + local edge = { x1 = point1.x, z1 = point1.z, x2 = point2.x, z2 = point2.z } + 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 z < 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 + return isInComplexPolygon(polygon, x, z) + end + + --- takes a list of units and returns all the units that are in any of the zones + ---@param unit_names table unit names + ---@param zone_names table zone names + ---@return table unit list of objects { unit = UNIT, zone_name = zoneName} + function DCS_UTIL.getUnitsInZones(unit_names, zone_names) + local units = {} + local zones = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) or StaticObject.getByName(unit_names[k]) + if unit and unit:isExist() == true then + units[#units + 1] = unit + end + end + + for index, zone_name in pairs(zone_names) do + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone then + zones[#zones + 1] = zone + end + end + + local in_zone_units = {} + for units_ind = 1, #units do + local lUnit = units[units_ind] + local unit_pos = lUnit:getPosition().p + local lCat = Object.getCategory(lUnit) + for zone_name, zone in pairs(zones) do + if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit + if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then + if DCS_UTIL.IsPointInPolygon(zone.verts, unit_pos.x, unit_pos.z) == true then + in_zone_units[#in_zone_units + 1] = { unit = lUnit, zone_name = zone.name } + end + else + if (((unit_pos.x - zone.x) ^ 2 + (unit_pos.z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then + in_zone_units[#in_zone_units + 1] = { unit = lUnit, zone_name = zone.name } + end + end + end + end + end + return in_zone_units + end + + --- takes a list of groups and returns all the group leaders that are in any of the zones + ---@param group_names table unit names + ---@param zone_name string zone names + ---@return table groupnames list of group names + function DCS_UTIL.getGroupsInZone(group_names, zone_name) + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone == nil then + return {} + end + + -- MAP Just for mapping sake + local custom_zone = { + x = zone.x, + z = zone.z, + zone_type = zone.zone_type, + radius = zone.radius, + verts = zone.verts + } + + return DCS_UTIL.areGroupsInCustomZone(group_names, custom_zone) + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param group_names table North South position + ---@param zone table { x, z, zonetype, radius, verts } + ---@return table groupnames list of groups that are in the zone + function DCS_UTIL.areGroupsInCustomZone(group_names, zone) + local units = {} + if Spearhead.Util.tableLength(group_names) < 1 then return {} end + + for k = 1, #group_names do + local entry = nil + local group = Group.getByName(group_names[k]) + if group ~= nil then + entry = { unit = group:getUnit(1), groupname = group_names[k] } + else + entry = { unit = StaticObject.getByName(group_names[k]), groupname = group_names[k] } + end + + if entry and entry.unit and entry.unit:isExist() == true then + units[#units + 1] = entry + end + end + + local result_groups = {} + for _, entry in pairs(units) do + local pos = entry.unit:getPoint() + if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then + if DCS_UTIL.IsPointInPolygon(zone.verts, pos.x, pos.z) == true then + table.insert(result_groups, entry.groupname) + end + else + if (((pos.x - zone.x) ^ 2 + (pos.z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then + table.insert(result_groups, entry.groupname) + end + end + end + return result_groups + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param x number North South position + ---@param z number West East position + ---@param zone_names table zone names + ---@return table zones list of objects { zone_name = zoneName} + function DCS_UTIL.isPositionInZones(x, z, zone_names) + local zones = {} + for index, zone_name in pairs(zone_names) do + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone then + zones[#zones + 1] = zone + end + end + + local result_zones = {} + for zone_name, zone in pairs(zones) do + if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then + if DCS_UTIL.IsPointInPolygon(zone.verts, x, z) == true then + result_zones[#result_zones + 1] = zone.name + end + else + if (((x - zone.x) ^ 2 + (z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then + result_zones[#result_zones + 1] = zone.name + end + end + end + return result_zones + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param x number North South position + ---@param z number West East position + ---@param zone_name table zone names + ---@return boolean result + function DCS_UTIL.isPositionInZone(x, z, zone_name) + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then + if DCS_UTIL.IsPointInPolygon(zone.verts, x, z) == true then + return true + end + else + if (((x - zone.x) ^ 2 + (z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then + return true + end + end + return false + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param zone_name string + ---@param parent_zone_name string + ---@return boolean result + function DCS_UTIL.isZoneInZone(zone_name, parent_zone_name) + local zoneA = DCS_UTIL.__trigger_zones[zone_name] + local zoneB = DCS_UTIL.__trigger_zones[parent_zone_name] + + if zoneB.zone_type == DCS_UTIL.ZoneType.Polygon and zoneB.verts then + if DCS_UTIL.IsPointInPolygon(zoneB.verts, zoneA.x, zoneA.z) == true then + return true + end + else + if (((zoneA.x - zoneB.x) ^ 2 + (zoneA.z - zoneB.z) ^ 2) ^ 0.5 <= zoneB.radius) then + return true + end + end + return false + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param x number North South position + ---@param z number West East position + ---@param zone table { x, z, zonetype, radius } + ---@return boolean result + function DCS_UTIL.isPositionInCustomZone(x, z, zone) + if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then + if DCS_UTIL.IsPointInPolygon(zone.verts, x, z) == true then + return true + end + else + if (((x - zone.x) ^ 2 + (z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then + return true + end + end + return false + end + + ---comment + ---@param zone_name any + ---@return table? zone { name,b zone_type, x, z, radius, verts } + function DCS_UTIL.getZoneByName(zone_name) + if zone_name == nil then return nil end + return DCS_UTIL.__trigger_zones[zone_name] + end + + ---maps the category name to the DCS group category + ---@param input string the name + ---@return integer? + function DCS_UTIL.stringToGroupCategory(input) + input = string.lower(input) + if input == 'airplane' or input == 'plane' then + return DCS_UTIL.GroupCategory.AIRPLANE + end + if input == 'helicopter' then + return DCS_UTIL.GroupCategory.HELICOPTER + end + if input == 'ground' or input == 'vehicle' then + return DCS_UTIL.GroupCategory.GROUND + end + if input == 'ship' then + return DCS_UTIL.GroupCategory.SHIP + end + if input == 'train' then + return DCS_UTIL.GroupCategory.TRAIN + end + if input == "static" then + return DCS_UTIL.GroupCategory.STATIC + end + return nil; + end + + --- get the group config as per start of the mission + --- group = { + --- category, + --- country_id, + --- group_template + --- } + ---@param groupname string groupName you're looking for + function DCS_UTIL.GetMizGroupOrDefault(groupname, default) + local group = DCS_UTIL.__miz_groups[groupname] + if group == nil then + return default + end + return group + end + + ---comment Get all group names. Can be a LOT + ---Includes statics + ---@return table groups + function DCS_UTIL.getAllGroupNames() + return DCS_UTIL.__groupNames + end + + ---comment Get all BLUE group names. Can be a LOT + ---Includes statics + ---@return table groups + function DCS_UTIL.getAllBlueGroupNames() + return DCS_UTIL.__blueGroupNames + end + + ---comment Get all RED group names. Can be a LOT + ---Includes statics + ---@return table groups + function DCS_UTIL.getAllRedGroupNames() + return DCS_UTIL.__redGroupNames + end + + ---comment Get all units that are players + ---@return table units + function DCS_UTIL.getAllPlayerUnits() + local units = {} + for key, value in pairs({ 1, 2, 3 }) do + local players = coalition.getPlayers(value) + for key, unit in pairs(players) do + units[#units + 1] = unit + end + end + return units + end + + ---get base name from ID + ---@param baseId number + ---@return string? name + function DCS_UTIL.getAirbaseName(baseId) + local stringified = tostring(baseId) + return DCS_UTIL.__airbaseNamesById[stringified] + end + + ---get base from id + ---@param baseId number + ---@return table? table + function DCS_UTIL.getAirbaseById(baseId) + local name = DCS_UTIL.getAirbaseName(baseId) + if name == nil then return nil end + return Airbase.getByName(name) + end + + ---Get the starting coalition of a farp or airbase + ---@return number? coalition + function DCS_UTIL.getStartingCoalition(baseId) + if baseId == nil then + return nil + end + + --STRING based dictionary otherwise it'll be a string/collapsed array + baseId = tostring(baseId) or "nil" + + local result = DCS_UTIL.__airportsStartingCoalition[baseId] + if result == nil then + result = DCS_UTIL.__warehouseStartingCoalition[baseId] + end + return result + end + + ---Gets all groups that have players + ---@return table groups + function DCS_UTIL.getAllPlayerGroups() + local groupNames = {} + local result = {} + for key, value in pairs({ 1, 2, 3 }) do + local players = coalition.getPlayers(value) + for key, unit in pairs(players) do + local group = unit:getGroup() + if group ~= nil then + local name = group:getName() + if name ~= nil then + if groupNames[name] ~= nil then + groupNames[name] = 1 + table.insert(result, group) + end + end + end + end + end + return result + end + + --- spawns the units as specified in the mission file itself + --- location and route can be nil and will then use default route + ---@param groupName string + ---@param location table? vector 3 data. { x , z, alt } + ---@param route table? route of the group. If nil wil be the default route. + ---@param uncontrolled boolean? Sets the group to be uncontrolled on spawn + ---@return table? new_group the Group class that was spawned + function DCS_UTIL.SpawnGroupTemplate(groupName, location, route, uncontrolled) + if groupName == nil then + return + end + + local template = DCS_UTIL.GetMizGroupOrDefault(groupName, nil) + if template == nil then + return nil + end + if template.category == DCS_UTIL.GroupCategory.STATIC then + --TODO: Implement location and route stuff + local spawn_template = template.group_template + coalition.addStaticObject(template.country_id, spawn_template) + else + local spawn_template = template.group_template + if location ~= nil then + local x_offset + if location.x ~= nil then x_offset = spawn_template.x - location.x end + + local y_offset + if location.z ~= nil then y_offset = spawn_template.y - location.z end + + spawn_template.x = location.x + spawn_template.y = location.z + + for i, unit in pairs(spawn_template.units) do + unit.x = unit.x - x_offset + unit.y = unit.y - y_offset + unit.alt = location.alt + end + end + + if route ~= nil then + spawn_template.route = route + end + + if uncontrolled ~= nil then + spawn_template.uncontrolled = uncontrolled + end + local new_group = coalition.addGroup(template.country_id, template.category, spawn_template) + return new_group + end + end + + function DCS_UTIL.IsBingoFuel(groupName, offset) + if offset == nil then offset = 0 end + local bingoSetting = 0.20 + if Spearhead.config and Spearhead.config.CAP then + bingoSetting = Spearhead.config.CAP.bingoRatio or 0.20 + end + + bingoSetting = bingoSetting + offset + + local group = Group.getByName(groupName) + for _, unit in pairs(group:getUnits()) do + if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then + if Spearhead.config.logLevel == Spearhead.LoggerTemplate.LogLevelOptions.DEBUG then + env.info("[Spearhead][DcsUtil][DEBUG] " .. groupName .. " is bingo fuel: " .. unit:getFuel()) + end + return true + end + end + return false + end + + DCS_UTIL.__INIT(); +end +Spearhead.DcsUtil = DCS_UTIL + +local LOGGER = {} +do + LOGGER.LogLevelOptions = { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + NONE = 4 + } + + local PreFix = "Spearhead" + + function LOGGER:new(logger_name, logLevel, liveLoggingLevel) + local o = {} + setmetatable(o, { __index = self }) + o.LoggerName = logger_name or "(loggername not set)" + o.LogLevel = logLevel or LOGGER.LogLevelOptions.INFO + o.LiveLoggingLevel = liveLoggingLevel or LOGGER.LogLevelOptions.NONE + + ---comment + ---@param self table self logger + ---@param message any the message + o.info = function(self, message) + if message == nil then + return + end + message = UTIL.toString(message) + + if self.LogLevel <= LOGGER.LogLevelOptions.INFO then + env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) + end + + if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.INFO then + trigger.action.outText(message, 20) + end + end + + ---comment + ---@param message string + o.warn = function(self, message) + if message == nil then + return + end + message = UTIL.toString(message) + + if self.LogLevel <= LOGGER.LogLevelOptions.WARN then + env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) + end + + if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.WARN then + trigger.action.outText(message, 20) + end + end + + ---comment + ---@param self table -- logger + ---@param message any -- the message + o.error = function(self, message) + if message == nil then + return + end + + message = UTIL.toString(message) + + if self.LogLevel <= LOGGER.LogLevelOptions.ERROR then + env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) + end + + if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.ERROR then + trigger.action.outText(message, 20) + end + end + + ---write debug + ---@param self table + ---@param message any the message + o.debug = function(self, message) + if message == nil then + return + end + + message = UTIL.toString(message) + if self.LogLevel <= LOGGER.LogLevelOptions.DEBUG then + env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message) + end + + if self.LiveLoggingLevel <= LOGGER.LogLevelOptions.DEBUG then + trigger.action.outText(message, 20) + end + end + + + return o + end +end +Spearhead.LoggerTemplate = LOGGER + +local ROUTE_UTIL = {} +do --setup route util + ---comment + ---@param attackHelos boolean + ---@return table + local function GetCAPTargetTypes(attackHelos) + local targetTypes = { + [1] = "Fighters", + [2] = "Multirole fighters", + [3] = "Bombers", + } + + if attackHelos then + targetTypes[4] = "Helicopters" + end + + return targetTypes + end + + ---comment + ---@param airdromeId number + ---@param basePoint table { x, z, y } (y == alt) + ---@param speed number the speed + ---@return table task + local RtbTask = function(airdromeId, basePoint, speed) + if basePoint == nil then + basePoint = Spearhead.Util.getAirbaseById(airdromeId):getPoint() + end + + return { + alt = basePoint.y, + action = "Landing", + alt_type = "BARO", + speed = speed, + ETA = 0, + ETA_locked = false, + x = basePoint.x, + y = basePoint.z, + speed_locked = true, + formation_template = "", + airdromeId = airdromeId, + type = "Land", + task = { + id = "ComboTask", + params = { + tasks = {} + } + } + } + end + + ---comment + ---@param groupName string + ---@param position table { x, y} + ---@param altitude number + ---@param speed number + ---@param duration number + ---@param engageHelos boolean + ---@param pattern string ["Race-Track"|"Circle"] + ---@return table + local CapTask = function(groupName, position, altitude, speed, duration, engageHelos, deviationdistance, pattern) + local durationBefore10 = duration - 600 + if durationBefore10 < 0 then durationBefore10 = 0 end + local durationAfter10 = 600 + if duration < 600 then + durationAfter10 = duration + end + + return { + alt = altitude, + action = "Turning Point", + alt_type = "BARO", + speed = speed, + ETA = 0, + ETA_locked = false, + x = position.x, + y = position.z, + speed_locked = true, + formation_template = "", + task = { + id = "ComboTask", + params = { + tasks = { + [1] = { + number = 1, + auto = false, + id = "WrappedAction", + enabled = "true", + params = { + action = { + id = "Script", + params = { + command = "pcall(Spearhead.Events.PublishOnStation, \"" .. groupName .. "\")" + } + } + } + }, + [2] = { + id = 'EngageTargets', + params = { + maxDist = deviationdistance, + maxDistEnabled = deviationdistance >= 0, -- required to check maxDist + targetTypes = GetCAPTargetTypes(engageHelos), + priority = 0 + } + }, + [3] = { + number = 3, + auto = false, + id = "ControlledTask", + enabled = true, + params = { + task = { + id = "Orbit", + params = { + altitude = altitude, + pattern = pattern, + speed = speed, + } + }, + stopCondition = { + duration = durationBefore10, + condition = "return Spearhead.DcsUtil.IsBingoFuel(\"" .. groupName .. "\", 0.10)", + } + } + }, + [4] = { + number = 4, + auto = false, + id = "WrappedAction", + enabled = "true", + params = { + action = { + id = "Script", + params = { + command = "pcall(Spearhead.Events.PublishRTBInTen, \"" .. groupName .. "\")" + } + } + } + }, + [5] = { + number = 5, + auto = false, + id = "ControlledTask", + enabled = true, + params = { + task = { + id = "Orbit", + params = { + altitude = altitude, + pattern = pattern, + speed = speed, + } + }, + stopCondition = { + duration = durationAfter10, + condition = "return Spearhead.DcsUtil.IsBingoFuel(\"" .. groupName .. "\")", + } + } + }, + [6] = { + number = 6, + auto = false, + id = "WrappedAction", + enabled = "true", + params = { + action = { + id = "Script", + params = { + command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")" + } + } + } + } + } + } + } + } + end + + ---comment + ---@param position table { x, y} + ---@param altitude number + ---@param speed number + ---@param childTasks table + ---@return table + local FlyToPointTask = function(position, altitude, speed, childTasks) + return { + alt = altitude, + action = "Turning Point", + alt_type = "BARO", + speed = speed, + ETA = 0, + ETA_locked = false, + x = position.x, + y = position.z, + speed_locked = true, + formation_template = "", + task = { + id = "ComboTask", + params = { + tasks = childTasks or {} + } + } + } + end + + ---comment + ---@param groupName string groupName you're creating this route for + ---@param airdromeId number airdromeId + ---@param capPoint table { x, z } + ---@param altitude number + ---@param speed number + ---@param durationOnStation number + ---@param attackHelos boolean + ---@param deviationDistance number + ---@return table route + ROUTE_UTIL.createCapMission = function(groupName, airdromeId, capPoint, racetrackSecondPoint, altitude, speed, + durationOnStation, attackHelos, deviationDistance) + local baseName = Spearhead.DcsUtil.getAirbaseName(airdromeId) + if baseName == nil then + return {} + end + + durationOnStation = durationOnStation or 1800 + altitude = altitude or 3000 + speed = speed or 130 + attackHelos = attackHelos or false + deviationDistance = deviationDistance or 32186 + + local base = Airbase.getByName(baseName) + if base == nil then + return {} + end + + local additionalFlyOverTasks = { + { + enabled = true, + auto = false, + id = "WrappedAction", + number = 1, + params = { + action = { + id = "Option", + params = { + variantIndex = 2, + name = AI.Option.Air.id.FORMATION, + formationIndex = 2, + value = 131074 + } + } + } + } + } + + local orbitType = "Circle" + if racetrackSecondPoint then orbitType = "Race-Track" end + + local basePoint = base:getPoint() + local points = {} + if racetrackSecondPoint == nil then + points = { + [1] = FlyToPointTask(capPoint, altitude, speed, additionalFlyOverTasks), + [2] = CapTask(groupName, capPoint, altitude, speed, durationOnStation, attackHelos, deviationDistance, + orbitType), + [3] = RtbTask(airdromeId, basePoint, speed) + } + else + points = { + [1] = FlyToPointTask(capPoint, altitude, speed, additionalFlyOverTasks), + [2] = CapTask(groupName, capPoint, altitude, speed, durationOnStation, attackHelos, deviationDistance, + orbitType), + [3] = FlyToPointTask(racetrackSecondPoint, altitude, speed, {}), + [4] = RtbTask(airdromeId, basePoint, speed) + } + end + + return { + id = 'Mission', + params = { + airborne = true, + route = { + points = points + } + } + } + end + + ---Creates an RTB task. The first point is to trigger the TDCS OnRTB Event, the second task will be the actual RTB point + ---If any of the values are not met it will return nil + ---@param groupName string + ---@param airdromeId number + ---@param speed number + ---@return table?, string ComboTask + ROUTE_UTIL.CreateRTBMission = function(groupName, airdromeId, speed) + --[[ + TODO: Test the creation and pubishing of event and the timing of said event + ]] -- + + local base = Spearhead.DcsUtil.getAirbaseById(airdromeId) + if base == nil then + return nil, "No airbase found for ID " .. tostring(airdromeId) + end + + local group = Group.getByName(groupName) + local pos; + local i = 1 + local units = group:getUnits() + while pos == nil and i <= Spearhead.Util.tableLength(units) do + local unit = units[i] + if unit and unit:isExist() == true and unit:inAir() == true then + pos = unit:getPoint() + end + i = i + 1 + end + + speed = speed or 130 + if pos == nil then + return nil, "Could not find any unit in the air to set the RTB task" + end + + local additionalFlyOverTasks = { + { + enabled = true, + auto = false, + id = "WrappedAction", + number = 1, + params = { + action = { + id = "Option", + params = { + variantIndex = 2, + name = AI.Option.Air.id.FORMATION, + formationIndex = 2, + value = 131074 + } + } + } + } + } + + return { + id = "Mission", + params = { + airborne = true, -- RTB mission generally are given to airborne units + route = { + points = { + + [1] = { + alt = pos.y, + action = "Turning Point", + alt_type = "BARO", + speed = speed, + ETA = 0, + ETA_locked = false, + x = pos.x, + y = pos.z, + speed_locked = true, + formation_template = "", + task = { + id = "ComboTask", + params = { + tasks = { + [1] = { + number = 1, + auto = false, + id = "WrappedAction", + enabled = "true", + params = { + action = { + id = "Script", + params = { + command = "pcall(Spearhead.Events.PublishRTB, \"" .. + groupName .. "\")" + } + } + } + } + } + } + } + }, + [2] = FlyToPointTask(base:getPoint(), 600, speed, additionalFlyOverTasks), + [3] = RtbTask(airdromeId, base:getPoint(), speed) + } + } + } + }, "" + end +end + +Spearhead.RouteUtil = ROUTE_UTIL + +local SpearheadEvents = {} +do + local SpearheadLogger = Spearhead.LoggerTemplate:new("Spearhead Events", Spearhead.config.logLevel) + + do -- STAGE NUMBER CHANGED + local OnStageNumberChangedListeners = {} + local OnStageNumberChangedHandlers = {} + + ---Add a stage zone number changed listener + ---@param listener table object with function OnStageNumberChanged(self, number) + SpearheadEvents.AddStageNumberChangedListener = function(listener) + if type(listener) ~= "table" then + SpearheadLogger:warn("Event listener not of type table, did you mean to use handler?") + return + end + table.insert(OnStageNumberChangedListeners, listener) + end + + ---Add a stage zone number changed listener + ---@param handler function function(number) + SpearheadEvents.AddStageNumberChangedHandler = function(handler) + if type(handler) ~= "function" then + SpearheadLogger:warn("Event handler not of type function, did you mean to use listener?") + return + end + table.insert(OnStageNumberChangedHandlers, handler) + end + + ---@param newStageNumber number + SpearheadEvents.PublishStageNumberChanged = function(newStageNumber) + for _, callable in pairs(OnStageNumberChangedListeners) do + local succ, err = pcall(function() + callable:OnStageNumberChanged(newStageNumber) + end) + if err then + SpearheadLogger:error(err) + end + end + + for _, callable in pairs(OnStageNumberChangedHandlers) do + local succ, err = pcall(callable, newStageNumber) + if err then + SpearheadLogger:error(err) + end + end + end + end + + local onLandEventListeners = {} + ---Add an event listener to a specific unit + ---@param unitName string to call when the unit lands + ---@param landListener table table with function OnUnitLanded(self, initiatorUnit, airbase) + SpearheadEvents.addOnUnitLandEventListener = function(unitName, landListener) + if type(landListener) ~= "table" then + SpearheadLogger:warn("Event handler not of type table/object") + return + end + + SpearheadLogger:debug("Added Land event handler for unit: " .. unitName) + + if onLandEventListeners[unitName] == nil then + onLandEventListeners[unitName] = {} + end + table.insert(onLandEventListeners[unitName], landListener) + end + + local OnUnitLostListeners = {} + ---This listener gets fired for any event that can indicate a loss of a unit. + ---Such as: Eject, Crash, Dead, Unit_Lost, + ---@param unitName any + ---@param unitLostListener table Object with function: OnUnitLost(initiatorUnit) + SpearheadEvents.addOnUnitLostEventListener = function(unitName, unitLostListener) + if type(unitLostListener) ~= "table" then + SpearheadLogger:warn("Unit lost Event listener not of type table/object") + return + end + + if OnUnitLostListeners[unitName] == nil then + OnUnitLostListeners[unitName] = {} + end + + table.insert(OnUnitLostListeners[unitName], unitLostListener) + end + + do -- ON RTB + local OnGroupRTBListeners = {} + ---Adds a function to the events listener that triggers when a group publishes themselves RTB. + ---This is only available when a ROUTE is created via the Spearhead.RouteUtil + ---@param groupName string the groupname to expect + ---@param handlingObject table object with OnGroupRTB(self, groupName) + SpearheadEvents.addOnGroupRTBListener = function(groupName, handlingObject) + if type(handlingObject) ~= "table" then + SpearheadLogger:warn("Event handler not of type table/object") + return + end + + if OnGroupRTBListeners[groupName] == nil then + OnGroupRTBListeners[groupName] = {} + end + + table.insert(OnGroupRTBListeners[groupName], handlingObject) + end + + ---Publish the Group to RTB + ---@param groupName string + SpearheadEvents.PublishRTB = function(groupName) + SpearheadLogger:debug("Publishing RTB event for group " .. groupName) + if groupName ~= nil then + if OnGroupRTBListeners[groupName] then + for _, callable in pairs(OnGroupRTBListeners[groupName]) do + local succ, err = pcall(function() + callable:OnGroupRTB(groupName) + end) + if err then + SpearheadLogger:error(err) + end + end + end + end + end + + local OnGroupRTBInTenListeners = {} + ---Adds a function to the events listener that triggers when a group publishes themselves RTB. + ---This is only available when a ROUTE is created via the Spearhead.RouteUtil + ---@param groupName string the groupname to expect + ---@param handlingObject table object with OnGroupRTBInTen(self, groupName) + SpearheadEvents.addOnGroupRTBInTenListener = function(groupName, handlingObject) + if type(handlingObject) ~= "table" then + SpearheadLogger:warn("Event handler not of type table/object") + return + end + + if OnGroupRTBInTenListeners[groupName] == nil then + OnGroupRTBInTenListeners[groupName] = {} + end + + table.insert(OnGroupRTBInTenListeners[groupName], handlingObject) + end + + ---Publish the Group is RTB + ---@param groupName string + SpearheadEvents.PublishRTBInTen = function(groupName) + SpearheadLogger:debug("Publishing RTB in TEN event for group " .. groupName) + if groupName ~= nil then + if OnGroupRTBInTenListeners[groupName] then + for _, callable in pairs(OnGroupRTBInTenListeners[groupName]) do + local succ, err = pcall(function() + callable:OnGroupRTBInTen(groupName) + end) + if err then + SpearheadLogger:error(err) + end + end + end + end + end + end + + do -- ON Station + local OnGroupOnStationListeners = {} + ---Adds a function to the events listener that triggers when a group publishes themselves RTB. + ---This is only available when a ROUTE is created via the Spearhead.RouteUtil + ---@param groupName string the groupname to expect + SpearheadEvents.addOnGroupOnStationListener = function(groupName, handlingObject) + if type(handlingObject) ~= "table" then + SpearheadLogger:warn("Event handler not of type table/object") + return + end + + if OnGroupOnStationListeners[groupName] == nil then + OnGroupOnStationListeners[groupName] = {} + end + + table.insert(OnGroupOnStationListeners[groupName], handlingObject) + end + + ---Publish the Group to RTB + ---@param groupName string + SpearheadEvents.PublishOnStation = function(groupName) + SpearheadLogger:debug("Publishing onStation event for group " .. groupName) + if groupName ~= nil then + if OnGroupOnStationListeners[groupName] then + for _, callable in pairs(OnGroupOnStationListeners[groupName]) do + local succ, err = pcall(function() + callable:OnGroupOnStation(groupName) + end) + if err then + SpearheadLogger:error(err) + end + end + end + end + end + end + + + local e = {} + function e:onEvent(event) + if event.id == world.event.S_EVENT_LAND or event.id == world.event.S_EVENT_RUNWAY_TOUCH then + local unit = event.initiator + local airbase = event.place + if unit ~= nil then + local name = unit:getName() + if onLandEventListeners[name] then + for _, callable in pairs(onLandEventListeners[name]) do + local succ, err = pcall(function() + callable:OnUnitLanded(unit, airbase) + end) + if err then + SpearheadLogger:error(err) + end + end + end + end + end + + if event.id == world.event.S_EVENT_DEAD or + event.id == world.event.S_EVENT_CRASH or + event.id == world.event.S_EVENT_EJECTION or + event.id == world.event.S_EVENT_UNIT_LOST then + local object = event.initiator + if object and OnUnitLostListeners[object:getName()] then + for _, callable in pairs(OnUnitLostListeners[object:getName()]) do + local succ, err = pcall(function() + callable:OnUnitLost(object) + end) + + if err then + SpearheadLogger:error(err) + end + end + end + end + end + + world.addEventHandler(e) +end +Spearhead.Events = SpearheadEvents + +Spearhead.MissionEditingWarnings = {} +function Spearhead.AddMissionEditorWarning(warningMessage) + table.insert(Spearhead.MissionEditingWarnings, warningMessage or "skip") +end + +local loadDone = false +Spearhead.LoadingDone = function() + if loadDone == true then + return + end + + local warningLogger = Spearhead.LoggerTemplate:new("MISSIONPARSER", Spearhead.LoggerTemplate.LogLevelOptions.INFO, 4) + if Spearhead.Util.tableLength(Spearhead.MissionEditingWarnings) > 0 then + for key, message in pairs(Spearhead.MissionEditingWarnings) do + warningLogger:warn(message) + end + else + warningLogger:info("No issues detected") + end + + loadDone = true +end diff --git a/spearhead_cap.lua b/spearhead_cap.lua new file mode 100644 index 0000000..eac0cf9 --- /dev/null +++ b/spearhead_cap.lua @@ -0,0 +1,31 @@ +--[[ + + CAP Standby Timing + This works with 3 timing + + + + MONITORING AND PREDICTIONS + TODO: Make sure that before the mission is started the mission maker has a way to see how many groups(units) are scheduled for each zone. + + + CAP Naming + CAP_1_1_ means the required cap state is 1 and it's the first unit. This means these units will be the first units to be dispatched. + + CAP_1-2_1_ means the unit cap state is 1 AND 2 and it will spawn a unique + + +]]-- + +--[[ + TODO: Spawn groups that are in state 1 or 2 at position and then give them waypoints when tasked. +]]-- + +local StageCapManager = {} +do + + + +end + +Spearhead.StageCapManager = StageCapManager \ No newline at end of file diff --git a/spearhead_db.lua b/spearhead_db.lua new file mode 100644 index 0000000..91923d3 --- /dev/null +++ b/spearhead_db.lua @@ -0,0 +1,589 @@ +-- 3 + +local SpearheadDB = {} +do -- DB + + local singleton = nil + + ---comment + ---@param Logger table + ---@return table + function SpearheadDB:new(Logger) + if singleton ~= nil then + Logger:info("Returning an already initiated instance of SpearheadDB") + return singleton + end + + local o = {} + setmetatable(o, { __index = self }) + + o.Logger = Logger + o.tables = {} + do --INIT ALL TABLES + Logger:debug("Initiating tables") + + o.tables.all_zones = {} + o.tables.stage_zones = {} + o.tables.mission_zones = {} + o.tables.random_mission_zones = {} + o.tables.farp_zones = {} + o.tables.cap_route_zones = {} + + o.tables.stage_zonesByNumer = {} + + do -- INIT ZONE TABLES + if env.mission.triggers and env.mission.triggers.zones then + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + local zone_name = zone_data.name + local split_string = Spearhead.Util.split_string(zone_name, "_") + table.insert(o.tables.all_zones, zone_name) + + if string.lower(split_string[1]) == "missionstage" then + table.insert(o.tables.stage_zones, zone_name) + if split_string[2] then + local stringified = tostring(split_string[2]) or "unknown" + if o.tables.stage_zonesByNumer[stringified] == nil then + o.tables.stage_zonesByNumer[stringified] = zone_name + else + table.insert(Spearhead.MissionEditingWarnings, "Duplicate Stage Order number found. This zone will work, but will not be part of the CAP script") + end + + end + end + + if string.lower(split_string[1]) == "mission" then + table.insert(o.tables.mission_zones, zone_name) + end + + if string.lower(split_string[1]) == "randommission" then + table.insert(o.tables.random_mission_zones, zone_name) + end + + if string.lower(split_string[1]) == "farp" then + table.insert(o.tables.farp_zones, zone_name) + end + + if string.lower(split_string[1]) == "caproute" then + table.insert(o.tables.cap_route_zones, zone_name) + end + end + end + end + + Logger:debug("initiated zone tables, continuing with descriptions") + o.tables.descriptions = {} + do --load markers + if env.mission.drawings and env.mission.drawings.layers then + for i, layer in pairs(env.mission.drawings.layers) do + if string.lower(layer.name) == "author" then + for key, layer_object in pairs(layer.objects) do + local inZone = Spearhead.DcsUtil.isPositionInZones(layer_object.mapX, layer_object.mapY, o.tables.mission_zones) + if Spearhead.Util.tableLength(inZone) >= 1 then + local name = inZone[1] + if name ~= nil then + o.tables.descriptions[name] = layer_object.text + end + end + end + end + end + end + end + + o.tables.missionZonesPerStage = {} + for key, missionZone in pairs(o.tables.mission_zones) do + local found = false + local i = 1 + while found == false and i <= Spearhead.Util.tableLength(o.tables.stage_zones) do + local stageZone = o.tables.stage_zones[i] + if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZone) == true then + if o.tables.missionZonesPerStage[stageZone] == nil then + o.tables.missionZonesPerStage[stageZone] = {} + end + table.insert(o.tables.missionZonesPerStage[stageZone], missionZone) + end + i = i + 1 + end + end + + o.tables.randomMissionZonesPerStage = {} + for key, missionZone in pairs(o.tables.random_mission_zones) do + local found = false + local i = 1 + while found == false and i <= Spearhead.Util.tableLength(o.tables.stage_zones) do + local stageZone = o.tables.stage_zones[i] + if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZone) == true then + if o.tables.randomMissionZonesPerStage[stageZone] == nil then + o.tables.randomMissionZonesPerStage[stageZone] = {} + end + table.insert(o.tables.randomMissionZonesPerStage[stageZone], missionZone) + end + i = i + 1 + end + end + + local isAirbaseInZone = {} + o.tables.airbasesPerStage = {} + o.tables.farpIdsInFarpZones = {} + local airbases = world.getAirbases() + for _, airbase in pairs(airbases) do + local baseId = airbase:getID() + local point = airbase:getPoint() + local found = false + for _, zoneName in pairs(o.tables.stage_zones) do + if found == false then + if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, zoneName) == true then + found = true + local baseIdString = tostring(baseId) or "nil" + isAirbaseInZone[baseIdString] = true + + if airbase:getDesc().category == 0 then + --Airbase + if Spearhead.DcsUtil.getStartingCoalition(baseId) == 2 then + airbase:setCoalition(1) + airbase:autoCapture(false) + end + + if o.tables.airbasesPerStage[zoneName] == nil then + o.tables.airbasesPerStage[zoneName] = {} + end + + table.insert(o.tables.airbasesPerStage[zoneName], baseId) + else + -- farp + local i = 1 + local farpFound = false + while farpFound == false and i <= Spearhead.Util.tableLength(o.tables.farp_zones) do + local farpZoneName = o.tables.farp_zones[i] + if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, farpZoneName) == true then + farpFound = true + + if o.tables.farpIdsInFarpZones[farpZoneName] == nil then + o.tables.farpIdsInFarpZones[farpZoneName] = {} + end + + airbase:setCoalition(1) + airbase:autoCapture(false) + table.insert(o.tables.farpIdsInFarpZones[farpZoneName], baseIdString) + end + i = i +1 + end + end + end + end + end + end + + + o.tables.farpZonesPerStage = {} + for _, farpZoneName in pairs(o.tables.farp_zones) do + local findFirst = function (farpZoneName) + for _, stage_zone in pairs(o.tables.stage_zones) do + if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stage_zone) then + return stage_zone + end + end + return nil + end + + local found = findFirst(farpZoneName) + if found then + + if o.tables.farpZonesPerStage[found] == nil then + o.tables.farpZonesPerStage[found] = {} + end + + table.insert(o.tables.farpZonesPerStage[found], farpZoneName) + end + end + + + local is_group_taken = {} + do + local all_groups = Spearhead.DcsUtil.getAllGroupNames() + for _, value in pairs(all_groups) do + is_group_taken[value] = false + end + end + + local getAvailableGroups = function() + local result = {} + for name, value in pairs(is_group_taken) do + if value == false then + table.insert(result, name) + end + end + return result + end + + local getAvailableCAPGroups = function() + local result = {} + for name, value in pairs(is_group_taken) do + if value == false and Spearhead.Util.startswith(name, "CAP") then + table.insert(result, name) + end + end + return result + end + + --- airbaseId <> groupname[] + o.tables.capGroupsOnAirbase = {} + local loadCapUnits = function() + local all_groups = getAvailableCAPGroups() + Logger:debug(all_groups) + local airbases = world.getAirbases() + for _, airbase in pairs(airbases) do + local baseId = airbase:getID() + local point = airbase:getPoint() + + o.tables.capGroupsOnAirbase[baseId] = {} + local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, + { x = point.x, z = point.z, radius = 6600 }) + for _, groupName in pairs(groups) do + is_group_taken[groupName] = true + table.insert(o.tables.capGroupsOnAirbase[baseId], groupName) + end + end + end + + --- missionZoneName <> groupname[] + o.tables.groupsInMissionZone = {} + local loadMissionzoneUnits = function() + local all_groups = getAvailableGroups() + for _, missionZoneName in pairs(o.tables.mission_zones) do + o.tables.groupsInMissionZone[missionZoneName] = {} + local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName) + for _, groupName in pairs(groups) do + is_group_taken[groupName] = true + table.insert(o.tables.groupsInMissionZone[missionZoneName], groupName) + end + end + end + + --- missionZoneName <> groupname[] + o.tables.groupsInRandomMissions = {} + local loadRandomMissionzoneUnits = function() + local all_groups = getAvailableGroups() + for _, missionZoneName in pairs(o.tables.random_mission_zones) do + o.tables.groupsInRandomMissions[missionZoneName] = {} + local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName) + for _, groupName in pairs(groups) do + is_group_taken[groupName] = true + table.insert(o.tables.groupsInRandomMissions[missionZoneName], groupName) + end + end + end + + --- farpZoneName <> groupname[] + o.tables.groupsInFarpZone = {} + local loadFarpGroups = function() + local all_groups = getAvailableGroups() + for _, farpZone in pairs(o.tables.farp_zones) do + o.tables.groupsInFarpZone[farpZone] = {} + local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone) + for _, groupName in pairs(groups) do + is_group_taken[groupName] = true + table.insert(o.tables.groupsInFarpZone[farpZone], groupName) + end + end + end + + --- farpZoneName <> groupname[] + o.tables.redAirbaseGroupsPerAirbase = {} + o.tables.blueAirbaseGroupsPerAirbase = {} + local loadAirbaseGroups = function() + local all_groups = getAvailableGroups() + local airbases = world.getAirbases() + for _, airbase in pairs(airbases) do + local baseId = tostring(airbase:getID()) + local point = airbase:getPoint() + + if isAirbaseInZone[tostring(baseId) or "something" ] == true then + o.tables.redAirbaseGroupsPerAirbase[baseId] = {} + o.tables.blueAirbaseGroupsPerAirbase[baseId] = {} + local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, { x = point.x, z = point.z, radius = 6600 }) + for _, groupName in pairs(groups) do + + if Spearhead.DcsUtil.IsGroupStatic(groupName) == true then + local object = StaticObject.getByName(groupName) + if object then + if object:getCoalition() == coalition.side.RED then + table.insert(o.tables.redAirbaseGroupsPerAirbase[baseId], groupName) + elseif object:getCoalition() == coalition.side.BLUE then + table.insert(o.tables.blueAirbaseGroupsPerAirbase[baseId], groupName) + end + end + else + local group = Group.getByName(groupName) + if group then + if group:getCoalition() == coalition.side.RED then + table.insert(o.tables.redAirbaseGroupsPerAirbase[baseId], groupName) + is_group_taken[groupName] = true + elseif group:getCoalition() == coalition.side.BLUE then + table.insert(o.tables.blueAirbaseGroupsPerAirbase[baseId], groupName) + is_group_taken[groupName] = true + end + end + end + end + end + end + end + + o.tables.miscGroupsInStage = {} + local loadMiscGroupsInStage = function () + local all_groups = getAvailableGroups() + for _, stage_zone in pairs(o.tables.stage_zones) do + o.tables.miscGroupsInStage[stage_zone] = {} + local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, stage_zone) + for _, groupName in pairs(groups) do + + if Spearhead.DcsUtil.IsGroupStatic(groupName) == true then + local object = StaticObject.getByName(groupName) + if object and object:getCoalition() ~= coalition.side.NEUTRAL then + is_group_taken[groupName] = true + table.insert(o.tables.miscGroupsInStage[stage_zone], groupName) + end + else + local group = Group.getByName(groupName) + if group and group:getCoalition() ~= coalition.side.NEUTRAL then + is_group_taken[groupName] = true + table.insert(o.tables.miscGroupsInStage[stage_zone], groupName) + end + end + end + end + end + + loadCapUnits() + loadMissionzoneUnits() + loadRandomMissionzoneUnits() + loadFarpGroups() + loadAirbaseGroups() + loadMiscGroupsInStage() + + local cleanup = function () --CLean up all groups that are now managed inside zones by spearhead + + local count = 0 + for name, taken in pairs(is_group_taken) do + if taken == true then + Spearhead.DcsUtil.DestroyGroup(name) + count = count + 1 + end + end + Logger:info("Destroyed " .. count .. " units that are now managed in zones by Spearhead") + end + cleanup() + + --- key: zoneName value: { current, routes = [ { point1, point2 } ] } + o.tables.capRoutes = {} + for _, zoneName in pairs(o.tables.stage_zones) do + + local configValue = { + current = 0, + routes = {} + } + for _, cap_route_zone in pairs(o.tables.cap_route_zones) do + if Spearhead.DcsUtil.isZoneInZone(cap_route_zone, zoneName) == true then + local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone) + if zone then + if zone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then + table.insert(configValue.routes, { point1 = { x = zone.x , z = zone.z }, point2 = nil } ) + else + local function getDist(a, b) + return math.sqrt((b.x - a.x) ^ 2 + (b.z - a.z) ^ 2) + end + + local biggest = nil + local biggestA = nil + local biggestB = nil + + for i=1, 3 do + for ii = i + 1, 4 do + + local a = zone.verts[i] + local b = zone.verts[ii] + local dist = getDist(a,b) + + if biggest == nil or dist > biggest then + biggestA = a + biggestB = b + biggest = dist + end + end + end + + if biggestA and biggestB then + table.insert(configValue.routes, + { + point1 = { x = biggestA.x , z = biggestA.z }, + point2 = { x = biggestB.x , z = biggestB.z } + }) + end + end + end + end + end + o.tables.capRoutes[zoneName] = configValue + end + + o.tables.missionCodes = {} + end + + o.GetDescriptionForMission = function(self, missionZoneName) + return self.tables.descriptions[missionZoneName] + end + + o.getCapRouteInZone = function(self, targetZone, baseId) + + local routeData = self.tables.capRoutes[targetZone] + if routeData then + local count = Spearhead.Util.tableLength(routeData.routes) + if count > 0 then + routeData.current = routeData.current + 1 + if count < routeData.current then + routeData.current = 1 + end + + return routeData.routes[routeData.current] + end + end + do + local function GetClosestPointOnCircle(pC, radius, p) + local vX = p.x - pC.x; + local vY = p.z - pC.z; + local magV = math.sqrt(vX*vX + vY*vY); + local aX = pC.x + vX / magV * radius; + local aY = pC.z + vY / magV * radius; + return { x = aX, z = aY } + end + local stagezone = Spearhead.DcsUtil.getZoneByName(targetZone) + if stagezone then + local base = Spearhead.DcsUtil.getAirbaseById(baseId) + if base then + local closest = nil + if stagezone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then + closest = GetClosestPointOnCircle({x = stagezone.x, z = stagezone.z}, stagezone.radius, base:getPoint()) + else + local function getDist(a, b) + return math.sqrt((b.x - a.x) ^ 2 + (b.z - a.z) ^ 2) + end + + local closestDistance = -1 + for _, vert in pairs(stagezone.verts) do + local distance = getDist(vert, base:getPoint()) + if closestDistance == -1 or distance < closestDistance then + closestDistance = distance + closest = vert + end + end + end + + if math.random(1,2)%2 == 0 then + return { point1 = closest, point2 = {x = stagezone.x, z = stagezone.z} } + else + return { point1 = {x = stagezone.x, z = stagezone.z}, point2 = closest } + end + end + end + end + + end + ---comment + ---@param self table + ---@param number number + ---@return string zoneName + o.getStageZoneByStageNumber = function (self, number) + local numberString = tostring(number) + return self.tables.stage_zonesByNumer[numberString] + end + + ---comment + ---@param self table + ---@return table result a list of stage zone names + o.getStagezoneNames = function (self) + return self.tables.stage_zones + end + + o.getMissionsForStage = function (self, stagename) + return self.tables.missionZonesPerStage[stagename] or {} + end + + o.getRandomMissionsForStage = function(self, stagename) + return self.tables.randomMissionZonesPerStage[stagename] or {} + end + + o.getGroupsForMissionZone = function (self, missionZoneName) + if Spearhead.Util.startswith(missionZoneName, "RANDOMMISSION") == true then + return self.tables.groupsInRandomMissions[missionZoneName] or {} + end + return self.tables.groupsInMissionZone[missionZoneName] or {} + end + + o.getMissionBriefingForMissionZone = function (self, missionZoneName) + return self.tables.descriptions[missionZoneName] or "" + end + + ---comment + ---@param self table + ---@param stageName string + ---@return table result airbase IDs. Use Spearhead.DcsUtil.getAirbaseById + o.getAirbaseIdsInStage = function (self, stageName) + return self.tables.airbasesPerStage[stageName] or {} + end + + o.getFarpZonesInStage = function (self, stageName) + return self.tables.farpZonesPerStage[stageName] + end + + ---comment + ---@param self table + ---@param airbaseId number + ---@return table + o.getCapGroupsAtAirbase = function(self, airbaseId) + return self.tables.capGroupsOnAirbase[airbaseId] or {} + end + + o.getRedGroupsAtAirbase = function (self, airbaseId) + local baseId = tostring(airbaseId) + return self.tables.redAirbaseGroupsPerAirbase[baseId] + end + + o.getBlueGroupsAtAirbase = function (self, airbaseId) + local baseId = tostring(airbaseId) + return self.tables.blueAirbaseGroupsPerAirbase[baseId] + end + + o.GetNewMissionCode = function (self) + local code = nil + local tries = 0 + while code == nil and tries < 10 do + local random = math.random(1000, 9999) + if self.tables.missionCodes[random] == nil then + code = random + end + tries = tries + 1 + end + return code + + --[[ + TODO: What to do when there's no random possible + ]] + end + + do -- LOG STATE + Logger:info("initiated the database with amount of zones: ") + Logger:info("Stages: " .. Spearhead.Util.tableLength(o.tables.stage_zones)) + Logger:info("Missions: " .. Spearhead.Util.tableLength(o.tables.mission_zones)) + Logger:info("Random Missions: " .. Spearhead.Util.tableLength(o.tables.random_mission_zones)) + Logger:info("Farps: " .. Spearhead.Util.tableLength(o.tables.farp_zones)) + Logger:info("Airbases: " .. Spearhead.Util.tableLength(o.tables.airbasesPerStage)) + Logger:info("RedAirbase Groups: " .. Spearhead.Util.tableLength(o.tables.redAirbaseGroupsPerAirbase["21"])) + Spearhead.Util.tableLength(o.tables.airbasesPerStage) + end + singleton = o + return o + end +end + +Spearhead.DB = SpearheadDB diff --git a/stageClasses/GlobalStageManager.lua b/stageClasses/GlobalStageManager.lua new file mode 100644 index 0000000..c513da2 --- /dev/null +++ b/stageClasses/GlobalStageManager.lua @@ -0,0 +1,22 @@ + + +local StagesByName = {} + + +GlobalStageManager = {} +GlobalStageManager.start = function (database) + + for _, stageName in pairs(database:getStagezoneNames()) do + + local logger = Spearhead.LoggerTemplate:new(stageName, Spearhead.config.logLevel) + local stage = Spearhead.internal.Stage:new(stageName, database, logger) + + StagesByName[stageName] = stage + + local logger = Spearhead.LoggerTemplate:new("StageManager", Spearhead.config.logLevel) + logger:info("Initiated " .. Spearhead.Util.tableLength(StagesByName) .. " airbases for cap") + + end +end + +Spearhead.internal.GlobalStageManager = GlobalStageManager diff --git a/stageClasses/Mission.lua b/stageClasses/Mission.lua new file mode 100644 index 0000000..db0217d --- /dev/null +++ b/stageClasses/Mission.lua @@ -0,0 +1,396 @@ +--[[ +# 3. Missions + +A mission is a completable objective with a state and a continuous check to see if itself is completed.
+The delay between checks is quite big, but it also is checked on unit deaths and other events. + +### Placement +The placement of MISSION trigger zones can be anywhere.
+The order of unit detection is `CAP` > `MISSION` > `AIRBASE` > `STAGE`
+This means that if a unit has a name that starts with `"CAP_"` it will not be included in a mission.
+But all other units in a `MISSION` trigger zone will be managed as part of that mission. + +Units inside a `MISSION` do not have to stay within the triggerzone.
+They just need to be inside the zone at the start of the mission.
+You can for example let a `BAI` mission drive back and forth between airbases. The `MISSIONZONE` only needs to be around the units that are part of the objective, not the waypoints. + +### Naming +`MISSION__`
+`RANDOMMISSION___` (Read RANDOMISATION below) + +With:
+`name` = A name that is easy to remember and type. Like a codename. Exmaples: BYRON, PLUKE, etc.
+`type` = any of the below described types. Special types are marked with an * + +TIP: You can click on the type to get more details + +

+SAM* +  SAM Sites are managed a little different. SAM Sites can be used to guide players and to protect airfields.
+  In the future when deepstrike missions might come into scope these SAM sites will also be more important.
+  SAM sites will be activated when a zone is "Pre-Active".
+  A stage is "Pre-Active" when there is a CAP base active, or there is other things to do that would need the SAM site to be live (OCA, DEEPSTRIKE, EXTRACTION \<= all feature development)
+  If you want a SAM site to become active ONLY when the stage is fully active, then `DEAD` is the type for you! + +  Completion logic
+  TODO: documentation: Completion logic + +
+ +
+DEAD + +  DEAD missions will be spawned on activation of the stage.
+  ALL DEAD missions will be activated right at the start of a stage.
+  This might be against the "randomisation" feel, but it is to make sure mission don't get activated randomly and players get ambushed by a random spawn.
+ +  Completion logic
+  TODO: documentation: Completion logic + + +
+ +
+BAI +
+ +
+STRIKE +  STRIKE missions will be activated randomly until all of them are completed.
+  A strike mission can be placed anywhere, even on airbases + +  Completion logic
+  TODO: documentation: Completion logic + +
+ +### Randomisation + +You can randomise missions.
+Spearhead will pick up all mission zones that start with `"RANDOMMISSION_"`
+Then it will combine each `RANDOMMISSION` in a zone with the same `` and pick a random one.
+It will always pick 1 and only 1. + +This means you have some options for randomisation.
+For example, if you have 1 missionzone with name `RANDOMMISSION_SAM_PLUKE_1` that is filled with an SA-2 and another zone with `RANDOMMISSION_SAM_PLUKE_2` filled with an SA-3 then some runs of the mission it will spawn an SA-3 and sometimes spawn an SA-3. (works for any mission type) + +What you can also do is add empty `RANDOMMISSION_` zones next to the filled `RANDOMMISSION_` zone. +For example. You have a `RANDOMMISSION_DEAD_BYRON_1` filled with an SA-19 driving around and 2 more `RANDOMMISSION_DEAD_BYRON_<2 & 3>` zones then it will have a 33% chance of being spawned. +If a zone is empty it will not be briefed, activated or count towards completion of the `STAGE` + +]] + +--- A mission Object. +local Mission = {} +do -- INIT Mission Class + + local MINIMAL_UNITS_ALIVE_RATIO = 0.20 + + local Defaults = {} + Defaults.MainMenu = "Missions" + Defaults.SelectMenuSubMenus = { Defaults.MainMenu, "Select Mission" } + Defaults.ShowMissionSubs = { Defaults.MainMenu } + + local PlayersInMission = {} + local MissionType = { + UNKNOWN = 0, + STRIKE = 1, + BAI = 2, + DEAD = 3, + SAM = 4, + } + + do --INIT MISSION TYPE FUNCTIONS + ---Parse string to mission type + ---@param input string + MissionType.Parse = function(input) + if input == nil then + return Mission.MissionType.UNKNOWN + end + + input = string.lower(input) + if input == "dead" then return MissionType.DEAD end + if input == "strike" then return MissionType.STRIKE end + if input == "bai" then return MissionType.BAI end + if input == "sam" then return MissionType.SAM end + return Mission.MissionType.UNKNOWN + end + + ---comment + ---@param input number missionType + ---@return string text + MissionType.toString = function(input) + if input == MissionType.DEAD then return "DEAD" end + if input == MissionType.STRIKE then return "STRIKE" end + if input == MissionType.BAI then return "BAI" end + if input == MissionType.SAM then return "SAM" end + return "?" + end + end + Mission.MissionType = MissionType + + Mission.MissionState = { + NEW = 0, + ACTIVE = 1, + COMPLETED = 2, + } + + ---comment + ---@param missionZoneName string missionZoneName + ---@param database table db dependency injection + ---@return table? + function Mission:new(missionZoneName, database, logger) + local o = {} + setmetatable(o, { __index = self }) + + local function ParseGroupName(input) + local split_name = Spearhead.Util.split_string(input, "_") + local split_length = Spearhead.Util.tableLength(split_name) + if Spearhead.Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then + Spearhead.AddMissionEditorWarning("Random Mission with zonename " .. input .. " not in right format") + return nil + elseif split_length < 3 then + Spearhead.AddMissionEditorWarning("Mission with zonename" .. input .. " not in right format") + return nil + end + local type = split_name[2] + local parsedType = Mission.MissionType.Parse(type) + + if parsedType == nil then + Spearhead.AddMissionEditorWarning("Mission with zonename '" .. input .. "' has an unsupported type '" .. (type or "nil" )) + return nil + end + local name = split_name[3] + return { + missionName = name, + type = parsedType + } + end + + local parsed = ParseGroupName(missionZoneName) + if parsed == nil then return nil end + + o.missionZoneName = missionZoneName + o.database = database + o.groupNames = database:getGroupsForMissionZone(missionZoneName) + o.name = parsed.missionName + o.missionType = parsed.type + o.startingGroups = Spearhead.Util.tableLength(o.groupNames) + o.missionState = Mission.MissionState.NEW + o.missionbriefing = database:GetDescriptionForMission(missionZoneName) + o.startingUnits = 0 + o.logger = logger + o.code = database:GetNewMissionCode() + + o.groupUnitAliveDict = {} + o.targetAliveStates = {} + o.hasSpecificTargets = false + + local CheckStateAsync = function (self, time) + self:CheckAndUpdateSelf() + return nil + end + + o.OnUnitLost = function(self, object) + --[[ + OnUnit lost event + ]]-- + local category = object:getCategory() + if category == Object.Category.UNIT then + local unitName = object:getName() + local groupName = object:getGroup():getName() + self.groupUnitAliveDict[groupName][unitName] = false + + if self.targetAliveStates[groupName][unitName] then + self.targetAliveStates[groupName][unitName] = false + end + elseif category == Object.Category.STATIC then + local name = object:getName() + self.groupUnitAliveDict[name][name] = false + + if self.targetAliveStates[name][name] then + self.targetAliveStates[name][name] = false + end + end + CheckStateAsync(false) + end + + o.MissionCompleteListeners = {} + ---comment + ---@param self table + ---@param listener table Object that implements "OnMissionComplete(self, mission)" + o.AddMissionCompleteListener = function(self, listener) + if type(listener) ~= "table" then + return + end + + table.insert(self.MissionCompleteListeners, listener) + end + + local TriggerMissionComplete = function(self) + for _, callable in pairs(self.MissionCompleteListeners) do + local succ, err = pcall( function() + callable:OnMissionComplete(self) + end) + if err then + self.logger:warn("Error in misstion complete listener:" .. err) + end + end + end + + + local StartCheckingAndUpdateSelfContinuous = function (self) + local CheckAndUpdate = function(self, time) + self:CheckAndUpdateSelf(true) + if self.missionState == Mission.MissionState.COMPLETED or self.missionState == Mission.MissionState.NEW then + return nil + else + return time + 60 + end + end + + timer.scheduleFunction(CheckAndUpdate, self, timer.getTime() + 300) + end + + local CleanupDelayedAsync = function (self, time) + self:Cleanup() + return nil + end + + ---comment + ---@param self table + ---@param checkUnitHealth boolean? + o.CheckAndUpdateSelf = function(self, checkUnitHealth) + if not checkUnitHealth then checkUnitHealth = false end + + if self.missionState == Mission.MissionState.COMPLETED then + return + end + --[[ + TODO: Check own state based on mission type + ]]-- + + local specificTargetsAlive = false + if self.hasSpecificTargets == true then + for groupName, unitNameDict in pairs(self.targetAliveStates) do + for unitName, isAlive in pairs(unitNameDict) do + if isAlive == true then + specificTargetsAlive = true + end + end + end + else + + local function CountAliveGroups() + local aliveGroups = 0 + + for _, group in pairs(self.groupUnitAliveDict) do + local groupTotal = 0 + local groupDeath = 0 + for _, isAlive in pairs(group) do + if isAlive ~= true then + groupDeath = groupDeath + 1 + end + groupTotal = groupTotal + 1 + end + + local aliveRatio = (groupTotal - groupDeath) / groupTotal + if aliveRatio >= MINIMAL_UNITS_ALIVE_RATIO then + aliveGroups = 1 + end + end + end + + if self.missionType == Mission.MissionType.STRIKE then --strike targets should normally have TGT targets + if CountAliveGroups() == 0 then + self.missionState = Mission.MissionState.COMPLETED + end + elseif self.missionType == Mission.MissionType.BAI then + if CountAliveGroups() == 0 then + self.missionState = Mission.MissionState.COMPLETED + end + end + --[[ + TODO: Other checks for mission complete + ]] + end + + + if self.missionState == Mission.MissionState.COMPLETED then + TriggerMissionComplete(self) + --Schedule cleanup after 5 minutes of mission complete + timer.scheduleFunction(CleanupDelayedAsync, self, timer.getTime() + 300) + end + end + + ---Activates groups for this mission + ---@param self table + o.Activate = function(self) + if self.missionState == Mission.MissionState.ACTIVE then + return + end + + self.missionState = Mission.MissionState.ACTIVE + do --spawn groups + for key, groupname in pairs(self.groupNames) do + Spearhead.DcsUtil.SpawnGroupTemplate(groupname) + end + end + + StartCheckingAndUpdateSelfContinuous(self) + end + + o.ShowBriefing = function(self, unitId) + local text = "Mission #" .. self.code .. "\n" .. self.missionbriefing .. " \n \nState TODO" + trigger.action.outTextForUnit(unitId, text, 30); + end + + o.Cleanup = function(self) + for key, groupName in pairs(self.groupNames) do + Spearhead.DcsUtil.DestroyGroup(groupName) + end + end + + local Init = function(self) + for key, group_name in pairs(self.groupNames) do + + self.groupUnitAliveDict[group_name] = {} + self.targetAliveStates[group_name] = {} + + if Spearhead.DcsUtil.IsGroupStatic(group_name) then + self.startingUnits = self.startingUnits + 1 + Spearhead.Events.addOnUnitLostEventListener(group_name, self) + + self.groupUnitAliveDict[group_name][group_name] = true + if Spearhead.Util.startswith(group_name, "TGT_") == true then + self.targetAliveStates[group_name][group_name] = true + end + + else + local group = Group.getByName(group_name) + local isGroupTarget = Spearhead.Util.startswith(group_name, "TGT_") + + self.startingUnits = self.startingUnits + group:getInitialSize() + for _, unit in pairs(group:getUnits()) do + local unitName = unit:getName() + Spearhead.Events.addOnUnitLostEventListener(unitName, self) + self.groupUnitAliveDict[group_name][unitName] = true + + if isGroupTarget == true or Spearhead.Util.startswith(unitName, "TGT_") == true then + self.targetAliveStates[group_name][unitName] = true + end + end + end + end + + if Spearhead.Util.tableLength(self.targetAliveStates) > 0 then + self.hasSpecificTargets = true + end + end + + Init(o) + return o; + end +end + +Spearhead.internal.Mission = Mission \ No newline at end of file diff --git a/stageClasses/Stage.lua b/stageClasses/Stage.lua new file mode 100644 index 0000000..2806503 --- /dev/null +++ b/stageClasses/Stage.lua @@ -0,0 +1,249 @@ +--[[ + +# 2. Stages + +]] + + +local Stage = {} +do --init STAGE DIRECTOR + + ---comment + ---@param stagezone_name string + ---@param database table + ---@param logger table + ---@return table? + function Stage:new(stagezone_name, database, logger) + local o = {} + setmetatable(o, { __index = self }) + + o.zoneName = stagezone_name + + local split = Spearhead.Util.split_string(stagezone_name, "_") + if Spearhead.Util.tableLength(split) < 2 then + Spearhead.AddMissionEditorWarning("Stage zone with name " .. stagezone_name .. " does not have a order number or valid format") + return nil + end + + local orderNumber = tonumber(split[2]) + if orderNumber == nil then + Spearhead.AddMissionEditorWarning("Stage zone with name " .. stagezone_name .. " does not have a valid order number : " .. split[2]) + return nil + end + + o.stageNumber = orderNumber + o.database = database + + o.db = {} + o.db.missions = {} + o.db.sams = {} + o.db.redAirbasegroups = {} + o.db.blueAirbasegroups = {} + o.db.airbaseIds = {} + o.db.farps = {} + o.preActivated = false + + do --Init Stage + logger:info("Initiating new Stage with name: " .. stagezone_name) + + local missionZones = database:getMissionsForStage(stagezone_name) + for _, missionZone in pairs(missionZones) do + local mission = Spearhead.internal.Mission:new(missionZone, database) + if mission then + if mission.missionType == Spearhead.internal.Mission.MissionType.SAM then + table.insert(o.db.sams, mission) + else + table.insert(o.db.missions, mission) + end + end + end + + local randomMissionNames = database:getRandomMissionsForStage(stagezone_name) + + local randomMissionByName = {} + for _, missionZoneName in pairs(randomMissionNames) do + local mission = Spearhead.internal.Mission:new(missionZoneName, database) + if mission then + if randomMissionByName[mission.name] == nil then + randomMissionByName[mission.name] = {} + end + table.insert(randomMissionByName[mission.name], mission) + end + end + + for _, missions in pairs(randomMissionByName) do + local mission = Spearhead.Util.randomFromList(missions) + if mission then + if mission.missionType == Spearhead.internal.Mission.MissionType.SAM then + table.insert(o.db.sams, mission) + else + table.insert(o.db.missions, mission) + end + end + end + + local airbaseIds = database:getAirbaseIdsInStage(o.zoneName) + if airbaseIds ~= nil and type(airbaseIds) == "table" then + o.db.airbaseIds = airbaseIds + for _, airbaseId in pairs(airbaseIds) do + + for _, groupName in pairs(database:getRedGroupsAtAirbase(airbaseId)) do + logger:debug("blaat)") + table.insert(o.db.redAirbasegroups, groupName) + end + + for _, groupName in pairs(database:getBlueGroupsAtAirbase(airbaseId)) do + table.insert(o.db.blueAirbasegroups, groupName) + end + end + end + + local farps = database:getFarpZonesInStage(o.zoneName) + if farps ~= nil and type(farps) == "table" then o.db.farps = farps end + end + + o.IsComplete = function(self) + for i, mission in pairs(self.db.missions) do + local state = mission:GetState() + if state == Spearhead.Mission.MissionState.ACTIVE or state == Spearhead.Mission.MissionState.NEW then + return false + end + end + return true + end + + ---Activates all SAMS, Airbase units etc all at once. + ---@param self table + o.PreActivate = function(self) + if self.preActivated == true then return end + self.preActivated = true + for key, mission in pairs(self.db.sams) do + if mission and mission.Activate then + mission:Activate() + end + end + + logger:debug("blaat Pre-activating stage with airbase groups amount: " .. Spearhead.Util.tableLength(self.db.redAirbasegroups)) + + for _ , groupName in pairs(self.db.redAirbasegroups) do + Spearhead.DcsUtil.SpawnGroupTemplate(groupName) + end + end + + o.ActiveStage = function(self) + if self.preActivated == false then + self:PreActivate() + end + + --[[ + TODO: Activate Stage + ]]-- + + end + + o.ActivateAllMissions = function(self) + for _, mission in pairs(self.db.missions) do + mission:Activate() + end + end + + ---Cleans up all missions + ---@param self table + o.Clean = function(self) + for key, mission in pairs(self.db.missions) do + mission:Cleanup() + end + + for key, samMission in pairs(self.db.sams) do + samMission:Cleanup() + end + + for _, airbase in pairs(self.db.airbases) do + for _, redGroupName in pairs(airbase.redAirbaseGroupNames) do + Spearhead.DcsUtil.DcsUtil.DestroyGroup(redGroupName) + end + end + + logger:debug("'" .. Spearhead.Util.toString(self.zoneName) .. "' cleaned") + end + + local ActivateBlueAsync = function(self) + for key, airbaseId in pairs(self.db.airbaseIds) do + local airbase = Spearhead.DcsUtil.getAirbaseById(airbaseId) + + if airbase then + local startingCoalition = Spearhead.DcsUtil.getStartingCoalition(airbaseId) + if startingCoalition == coalition.side.BLUE then + airbase:setCoalition(2) + for _, blueGroupName in pairs(self.db.blueAirbasegroups) do + Spearhead.DcsUtil.SpawnGroupTemplate(blueGroupName) + end + else + airbase:setCoalition(0) + end + end + end + end + + ---Sets airfields to blue and spawns friendly farps + o.ActivateBlueStage = function(self) + logger:debug("Setting stage '" .. Spearhead.Util.toString(self.zoneName) .. "' to blue") + + for _, groupName in pairs(self.db.redAirbasegroups) do + Spearhead.DcsUtil.DestroyGroup(groupName) + end + + for _, mission in pairs(self.db.missions) do + mission:Cleanup() + end + + for _, mission in pairs(self.db.sams) do + mission:Cleanup() + end + + timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3) + + -- for key, farp in pairs(self.db.farps) do + -- if farp.helipadnames then + -- for _, helipadName in pairs(farp.helipadnames) do + -- local helipad = Airbase.getByName(helipadName) + -- if helipad then + -- logger:debug("Enabling: '" .. helipad:getName() .. "'") + -- helipad:setCoalition(2) + -- else + -- logger:warn(helipadName .. " not found when spawning farps") + -- end + -- end + -- end + + -- if farp.group_names then + -- for _, groupName in pairs(farp.group_names) do + -- Spearhead.DcsUtil.SpawnGroupTemplate(groupName) + -- end + -- end + -- end + end + + o.ManageStage = function(self) + end + + o.OnStageNumberChanged = function (self, number) + if Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then + self:PreActivate() + end + + if number == self.stageNumber then + self:ActiveStage() + end + + if number > self.stageNumber then + self:ActivateBlueStage() + end + end + + Spearhead.Events.AddStageNumberChangedListener(o) + return o + end +end + +Spearhead.internal.Stage = Stage \ No newline at end of file