---@class DatabaseTables ---@field AllZoneNames Array All Zone Names ---@field StageZoneNames Array All Stage Zone Names ---@field StageZones table table ---@field MissionZones Array All Mission Zone Names ---@field RandomMissionZones Array All Random mission names ---@field MissionZonesLocations table All Mission Zone Locations ---@field StageZonesByNumber table> Stage zones grouped by index number ---@field AllFarpZones Array ---@field AllSceneryObjects Array All scenery objects ---@field AllCapRoutes Array All Cap route zone names ---@field AllInterceptZones Array All intercept zones ---@field capZonesByCapZoneID table ---@field interceptZonesByZoneID table> ---@field CarrierRouteZones Array All Carrier routes zones ---@field BlueSams Array All blue sam zones ---@field SupplyHubZones Array All supply hub zones ---@field AirbaseDataPerAirfield table ---@field BlueSamDataPerZone table ---@field MissionZoneData table ---@field FarpZoneData table ---@field missionCodes table ---@field CustomDrawings Array ---@class CapRoute ---@field zones Array ---@field current number ---@class StageZoneData ---@field StageZoneName string ---@field StageBriefing string? ---@field AirbaseNames Array ---@field FarpZones Array ---@field MissionZones Array ---@field RandomMissionZones Array ---@field StageIndex string ---@field BlueSamZones Array ---@field SupplyHubZones Array ---@field SupplyHubZonesInFarp table ---@field MiscGroups Array ---@class AirbaseData ---@field CapGroups Array ---@field SweepGroups Array ---@field InterceptGroups Array ---@field RedGroups Array ---@field BlueGroups Array ---@field supplyHubNames Array ---@field buildingKilos number? ---@class BlueSamData ---@field groups Array ---@field buildingKilos number? ---@class MissionZoneData ---@field ZoneName string ---@field RedGroups Array ---@field SceneryTargets Array ---@field BlueGroups Array ---@field description string? ---@field descriptionLocation Vec2? ---@field dependsOn Array ---@field completeAt number? ---@class FarpZoneData ---@field groups Array ---@field padNames Array ---@field buildingKilos number? ---@field supplyHubNames Array ---@class Database ---@field private _tables DatabaseTables ---@field private _logger Logger local Database = {} ---comment ---@param Logger Logger ---@return Database function Database.New(Logger) ---@type DatabaseTables local tables = { AllZoneNames = {}, BlueSams = {}, AllCapRoutes = {}, capZonesByCapZoneID = {}, AllInterceptZones = {}, AllSceneryObjects = {}, interceptZonesByZoneID = {}, CarrierRouteZones = {}, MissionZones = {}, MissionZonesLocations = {}, StageZoneNames = {}, RandomMissionZones = {}, StageZones = {}, StageZonesByNumber = {}, AllFarpZones = {}, AirbaseDataPerAirfield = {}, BlueSamDataPerZone = {}, MissionZoneData = {}, FarpZoneData = {}, missionCodes = {}, SupplyHubZones = {}, CustomDrawings = {} } Database.__index = Database local self = setmetatable({}, Database) self._logger = Logger self._tables = tables self._logger:debug("Initiating tables") do -- INIT ZONE TABLES for zone_ind, zone_data in pairs(Spearhead.DcsUtil.__trigger_zones) do local zone_name = zone_data.name ---@type Vec2 local zoneLocation = { x = zone_data.location.x, y = zone_data.location.y } local split_string = Spearhead.Util.split_string(zone_name, "_") table.insert(self._tables.AllZoneNames, zone_name) if string.lower(split_string[1]) == "missionstage" then table.insert(self._tables.StageZoneNames, zone_name) if split_string[2] then local stringified = tostring(split_string[2]) or "unknown" if self._tables.StageZonesByNumber[stringified] == nil then self._tables.StageZonesByNumber[stringified] = {} end table.insert(self._tables.StageZonesByNumber[stringified], zone_name) ---@type StageZoneData local stageData = { StageZoneName = zone_name, StageIndex = stringified, AirbaseNames = {}, BlueSamZones = {}, FarpZones = {}, MissionZones = {}, RandomMissionZones = {}, MiscGroups = {}, SupplyHubZones = {}, SupplyHubZonesInFarp = {} } self._tables.StageZones[zone_name] = stageData end end local lowered = string.lower(split_string[1]) if lowered == "waitingstage" then table.insert(self._tables.StageZoneNames, zone_name) end if lowered == "mission" then table.insert(self._tables.MissionZones, zone_name) self._tables.MissionZonesLocations[zone_name] = zoneLocation end if lowered == "randommission" then table.insert(self._tables.RandomMissionZones, zone_name) self._tables.MissionZonesLocations[zone_name] = zoneLocation end if lowered == "farp" then table.insert(self._tables.AllFarpZones, zone_name) end if lowered == "caproute" then table.insert(self._tables.AllCapRoutes, zone_name) end if lowered == "interceptzone" then table.insert(self._tables.AllInterceptZones, zone_name) end if lowered == "carrierroute" then table.insert(self._tables.CarrierRouteZones, zone_name) end if lowered == "bluesam" then table.insert(self._tables.BlueSams, zone_name) end if lowered == "supplyhub" then table.insert(self._tables.SupplyHubZones, zone_name) end if lowered == "scenerytarget" or lowered == "scenerytargets" then local sceneryObjects = Spearhead.DcsUtil.getSceneryObjectsInZone(zone_data) for _, sceneryObject in pairs(sceneryObjects) do table.insert(self._tables.AllSceneryObjects, sceneryObject) end end end end self._logger:debug("initiated zone tables, continuing with 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 if Spearhead.Util.startswith(string.lower(layer_object.name), "buildable", true) == true then local airbaseData = self:getAirbaseDataForDrawLayer(layer_object) if airbaseData then self._logger:debug("found airbase data for " .. layer_object.name) local number = tonumber(layer_object.text) airbaseData.buildingKilos = number end end end end end end end ---@type table do -- custom drawings 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 if Spearhead.Util.startswith(layer_object.name, "drawing_", true) then local object = layer_object --[[@as DrawingObject]] local customDrawing = Spearhead.classes.stageClasses.drawings.CustomDrawing.New(object) table.insert(self._tables.CustomDrawings, customDrawing) end end end end end end ---@type table local availableSupplyHubs = {} for _, supplyHubZoneName in pairs(self._tables.SupplyHubZones) do availableSupplyHubs[supplyHubZoneName] = true end for _, stageZoneName in pairs(self._tables.StageZoneNames) do local stageData = self._tables.StageZones[stageZoneName] if stageData then -- Find and add Briefing 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 if Spearhead.Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then local zone = Spearhead.DcsUtil.getZoneByName(stageZoneName) local vec2 = { x = layer_object.mapX, y = layer_object.mapY } if zone and Spearhead.Util.is2dPointInZone(vec2, zone) == true then local description = layer_object.text if description and description ~= "" then stageData.StageBriefing = description end end end end end end end -- fill blue sams for _, blueSamStageName in pairs(self._tables.BlueSams) do if Spearhead.DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then table.insert(stageData.BlueSamZones, blueSamStageName) end end --- fill farp zones for _, farpZoneName in pairs(self._tables.AllFarpZones) do if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then table.insert(stageData.FarpZones, farpZoneName) for hubZoneName, available in pairs(availableSupplyHubs) do if available == true and Spearhead.DcsUtil.isZoneInZone(hubZoneName, farpZoneName) == true then local farpZoneData = self:getOrCreateFarpDataForZone(farpZoneName) if farpZoneData then table.insert(farpZoneData.supplyHubNames, hubZoneName) availableSupplyHubs[hubZoneName] = false end end end end end -- fill airbases for _, airbase in pairs(world.getAirbases()) do local point = airbase:getPoint() if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then if airbase:getDesc().category == 0 then table.insert(stageData.AirbaseNames, airbase:getName()) local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName()) for hubZoneName, available in pairs(availableSupplyHubs) do local zone = Spearhead.DcsUtil.getZoneByName(hubZoneName) if zone and airbaseZone then if available == true and Spearhead.Util.is2dPointInZone(zone.location, airbaseZone) == true then local airbaseData = self:getOrCreateAirbaseData(airbase:getName()) if airbaseData then table.insert(airbaseData.supplyHubNames, hubZoneName) availableSupplyHubs[hubZoneName] = false end end end end end end end -- fill supply hubs for supplyHubZone, available in pairs(availableSupplyHubs) do if available == true and Spearhead.DcsUtil.isZoneInZone(supplyHubZone, stageZoneName) == true then table.insert(stageData.SupplyHubZones, supplyHubZone) end end for _, farpZoneName in pairs(stageData.FarpZones) do for _, supplyHubZone in pairs(self._tables.SupplyHubZones) do if Spearhead.DcsUtil.isZoneInZone(supplyHubZone, farpZoneName) == true then stageData.SupplyHubZonesInFarp[supplyHubZone] = farpZoneName end end end -- fill missions for key, missionZone in pairs(self._tables.MissionZones) do if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then table.insert(stageData.MissionZones, missionZone) end end -- fill random missions for key, missionZone in pairs(self._tables.RandomMissionZones) do if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then table.insert(stageData.RandomMissionZones, missionZone) end end end end for _, missionZone in pairs(self._tables.MissionZones) do if self._tables.MissionZoneData[missionZone] == nil or self._tables.MissionZoneData[missionZone].description == nil then Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZone .. " does not have a briefing") end end for _, missionZone in pairs(self._tables.RandomMissionZones) do if self._tables.MissionZoneData[missionZone] == nil or self._tables.MissionZoneData[missionZone].description == nil then Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZone .. " does not have a briefing") end end for _, farpZoneName in pairs(self._tables.AllFarpZones) do for _, airbase in pairs(world.getAirbases()) do if airbase:getDesc().category == Airbase.Category.HELIPAD then local name = airbase:getName() if self._tables.FarpZoneData[farpZoneName] == nil then self._tables.FarpZoneData[farpZoneName] = { groups = {}, padNames = {}, supplyHubNames = {} } end local position = airbase:getPoint() if Spearhead.DcsUtil.isPositionInZone(position.x, position.z, farpZoneName) == true then table.insert(self._tables.FarpZoneData[farpZoneName].padNames, name) end end end end self:initAvailableUnits() self:loadCapUnits() self:loadBlueSamUnits() self:loadMissionzoneUnits() self:loadRandomMissionzoneUnits() self:loadFarpData() self:loadAirbaseGroups() self:loadMiscGroupsInStages() for _, cap_route_zone in pairs(self._tables.AllCapRoutes) do local split = Spearhead.Util.split_string(cap_route_zone, "_") local zoneID = split[2] if zoneID then if tables.capZonesByCapZoneID[zoneID] == nil then tables.capZonesByCapZoneID[zoneID] = { zones = {}, current = 1 } end local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone) if zone then table.insert(tables.capZonesByCapZoneID[zoneID].zones, zone) end end end for _, interceptZone in pairs(self._tables.AllInterceptZones) do local split = Spearhead.Util.split_string(interceptZone, "_") local zoneID = split[2] if zoneID then if tables.interceptZonesByZoneID[zoneID] == nil then tables.interceptZonesByZoneID[zoneID] = {} end local zone = Spearhead.DcsUtil.getZoneByName(interceptZone) if zone then table.insert(tables.interceptZonesByZoneID[zoneID], zone) end end end local totalUnits = 0 local missions = 0 for _, data in pairs(self._tables.MissionZoneData) do missions = missions + 1 for _, groupName in pairs(data.RedGroups) do local group = Group.getByName(groupName) if group then totalUnits = totalUnits + group:getInitialSize() end end for _, groupName in pairs(data.BlueGroups) do local group = Group.getByName(groupName) if group then totalUnits = totalUnits + group:getInitialSize() end end end if missions == 0 then missions = 1 end self._logger:info("initiated the database with amount of zones: ") self._logger:info("Stages: " .. Spearhead.Util.tableLength(self._tables.StageZones)) self._logger:info("Total Missions: " .. Spearhead.Util.tableLength(self._tables.MissionZoneData)) self._logger:info("Average units per mission: " .. totalUnits / missions) self._logger:info("Random Missions: " .. Spearhead.Util.tableLength(self._tables.RandomMissionZones)) self._logger:info("Farps: " .. Spearhead.Util.tableLength(self._tables.AllFarpZones)) self._logger:info("Airbases: " .. Spearhead.Util.tableLength(self._tables.AirbaseDataPerAirfield)) return self end local is_group_taken = {} 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 ---@private function Database:initAvailableUnits() do local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames() for _, value in pairs(all_groups) do is_group_taken[value] = false end end end ---comment ---@param layer_object table ---@return AirbaseData? function Database:getAirbaseDataForDrawLayer(layer_object) for _, airbase in pairs(world.getAirbases()) do local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName()) if zone and Spearhead.Util.is2dPointInZone({ x = layer_object.mapX, y = layer_object.mapY }, zone) == true then return self:getOrCreateAirbaseData(airbase:getName()) end end return nil end ---@private function Database:getOrCreateBlueSamDataForZone(zoneName) local blueSamData = self._tables.BlueSamDataPerZone[zoneName] if blueSamData == nil then blueSamData = { groups = {}, } self._tables.BlueSamDataPerZone[zoneName] = blueSamData end return blueSamData end ---@private function Database:getOrCreateFarpDataForZone(zoneName) local farpData = self._tables.FarpZoneData[zoneName] if farpData == nil then farpData = { padNames = {}, groups = {}, supplyHubNames = {} } self._tables.FarpZoneData[zoneName] = farpData end return farpData end ---@private ---@param baseName string ---@return AirbaseData function Database:getOrCreateAirbaseData(baseName) local baseData = self._tables.AirbaseDataPerAirfield[baseName] if baseData == nil then baseData = { CapGroups = {}, InterceptGroups = {}, SweepGroups = {}, RedGroups = {}, BlueGroups = {}, supplyHubNames = {} } self._tables.AirbaseDataPerAirfield[baseName] = baseData end return baseData end ---@private function Database:loadCapUnits() local all_groups = getAvailableCAPGroups() local airbases = world.getAirbases() for _, airbase in pairs(airbases) do local point = airbase:getPoint() ---@type SpearheadTriggerZone? local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName()) if zone == nil then zone = { location = { x = point.x, y = point.z }, radius = 4000, name = "temp_zone", verts = {}, zone_type = "Cilinder" } end local baseData = self:getOrCreateAirbaseData(airbase:getName()) local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, zone) for _, groupName in pairs(groups) do is_group_taken[groupName] = true if Spearhead.Util.startswith(groupName, "CAP_A", true) or Spearhead.Util.startswith(groupName, "CAP_B", true) then table.insert(baseData.CapGroups, groupName) elseif Spearhead.Util.startswith(groupName, "CAP_I", true) then table.insert(baseData.InterceptGroups, groupName) elseif Spearhead.Util.startswith(groupName, "CAP_S", true) then table.insert(baseData.SweepGroups, groupName) end end self._tables.AirbaseDataPerAirfield[airbase:getName()] = baseData end end ---@private function Database:loadBlueSamUnits() local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames() for _, blueSamZone in pairs(self._tables.BlueSams) do local samData = self:getOrCreateBlueSamDataForZone(blueSamZone) local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, blueSamZone) for _, groupName in pairs(groups) do is_group_taken[groupName] = true table.insert(samData.groups, groupName) end local triggerZone = Spearhead.DcsUtil.getZoneByName(blueSamZone) if triggerZone then for _, kvPair in pairs(triggerZone.properties) do if kvPair.key and Spearhead.Util.startswith(kvPair.key, "buildable") then local number = tonumber(kvPair.value) if number and number > 0 then samData.buildingKilos = number end end end end end end ---@return Array function Database:getCustomDrawings() if self._tables.CustomDrawings == nil then return {} end return self._tables.CustomDrawings end ---Loads all units, data and briefings ---@private function Database:LoadZoneData(missionZoneName) local all_groups = getAvailableGroups() self._tables.MissionZoneData[missionZoneName] = { RedGroups = {}, BlueGroups = {}, SceneryTargets = {}, ZoneName = missionZoneName, dependsOn = {} } local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName) for _, groupName in pairs(groups) do if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then local object = StaticObject.getByName(groupName) if object and object:getCoalition() == coalition.side.RED then table.insert(self._tables.MissionZoneData[missionZoneName].RedGroups, groupName) elseif object then table.insert(self._tables.MissionZoneData[missionZoneName].BlueGroups, groupName) end else local group = Group.getByName(groupName) if group and group:getCoalition() == coalition.side.RED then table.insert(self._tables.MissionZoneData[missionZoneName].RedGroups, groupName) elseif group then table.insert(self._tables.MissionZoneData[missionZoneName].BlueGroups, groupName) end end is_group_taken[groupName] = true end for _, sceneryObject in pairs(self._tables.AllSceneryObjects) do local point = sceneryObject:GetPoint() if point then if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, missionZoneName) == true then table.insert(self._tables.MissionZoneData[missionZoneName].SceneryTargets, sceneryObject) end end end -- Check for properties and adds the settings to the mission data local triggerZone = Spearhead.DcsUtil.getZoneByName(missionZoneName) if triggerZone and triggerZone.properties then for _, kvPair in pairs(triggerZone.properties) do local key = kvPair.key if Spearhead.Util.startswith(key, "dependson", true) == true then table.insert(self._tables.MissionZoneData[missionZoneName].dependsOn, kvPair.value) elseif Spearhead.Util.startswith(key, "completeat") == true then local value = tonumber(kvPair.value) if value then if value > 1 and value <= 100 then value = value / 100 elseif value > 100 then Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZoneName .. " has a complete at value of " .. value .. " which is higher than 100, this will not work as intended") end self._tables.MissionZoneData[missionZoneName].completeAt = value end end end end ---Adds Briefings 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 vec2 = { x = layer_object.mapX, y = layer_object.mapY } if triggerZone and Spearhead.Util.is2dPointInZone(vec2, triggerZone) then if layer_object.name and Spearhead.Util.startswith(layer_object.name, "briefing_", true) then local description = layer_object.text if description and description ~= "" then self._tables.MissionZoneData[missionZoneName].description = description self._tables.MissionZoneData[missionZoneName].descriptionLocation = vec2 end end end end end end end end ---@private function Database:loadMissionzoneUnits() for _, missionZoneName in pairs(self._tables.MissionZones) do self:LoadZoneData(missionZoneName) end end ---@private function Database:loadRandomMissionzoneUnits() for _, missionZoneName in pairs(self._tables.RandomMissionZones) do self:LoadZoneData(missionZoneName) end end ---@private function Database:loadFarpData() local all_groups = getAvailableGroups() for _, farpZone in pairs(self._tables.AllFarpZones) do local farpzoneData = self:getOrCreateFarpDataForZone(farpZone) local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone) for _, groupName in pairs(groups) do is_group_taken[groupName] = true table.insert(farpzoneData.groups, groupName) end local triggerZone = Spearhead.DcsUtil.getZoneByName(farpZone) if triggerZone then for _, kvPair in pairs(triggerZone.properties) do if kvPair.key and Spearhead.Util.startswith(kvPair.key, "buildable", true) == true then local number = tonumber(kvPair.value) if number and number > 0 then farpzoneData.buildingKilos = number end end end end end end function Database:loadAirbaseGroups() local all_groups = getAvailableGroups() for _, stageZone in pairs(self._tables.StageZones) do for _, baseName in pairs(stageZone.AirbaseNames) do local base = Airbase.getByName(baseName) if base then local basedata = self:getOrCreateAirbaseData(baseName) local point = base:getPoint() local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(baseName) if airbaseZone == nil then airbaseZone = { location = { x = point.x, y = point.z }, radius = 4000, name = "temp_zone", verts = {}, zone_type = "Cilinder" } end if airbaseZone and base:getDesc().category == Airbase.Category.AIRDROME then local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone) for _, groupName in pairs(groups) do if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then local object = StaticObject.getByName(groupName) if object then if object:getCoalition() == coalition.side.RED then table.insert(basedata.RedGroups, groupName) is_group_taken[groupName] = true elseif object:getCoalition() == coalition.side.BLUE then table.insert(basedata.BlueGroups, groupName) is_group_taken[groupName] = true end end else local group = Group.getByName(groupName) if group then if group:getCoalition() == coalition.side.RED then table.insert(basedata.RedGroups, groupName) is_group_taken[groupName] = true elseif group:getCoalition() == coalition.side.BLUE then table.insert(basedata.BlueGroups, groupName) is_group_taken[groupName] = true end end end end end end end end end ---@private function Database:loadMiscGroupsInStages() local all_groups = getAvailableGroups() for _, stageZone in pairs(self._tables.StageZones) do stageZone.MiscGroups = {} local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, stageZone.StageZoneName) for _, groupName in pairs(groups) do if Spearhead.classes.helpers.MizGroupsManager.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(stageZone.MiscGroups, groupName) end else local group = Group.getByName(groupName) if group and group:getCoalition() ~= coalition.side.NEUTRAL then is_group_taken[groupName] = true table.insert(stageZone.MiscGroups, groupName) end end end end end ---comment ---@param missionZoneName any ---@return Vec2? function Database:GetLocationForMissionZone(missionZoneName) if self._tables.MissionZoneData[missionZoneName] and self._tables.MissionZoneData[missionZoneName].descriptionLocation then return self._tables.MissionZoneData[missionZoneName].descriptionLocation end return self._tables.MissionZonesLocations[missionZoneName] end ---@param zoneID string ---@return SpearheadTriggerZone? function Database:GetCapZoneForZoneID(zoneID) zoneID = tostring(zoneID) or "nothing" local capZonesForID = self._tables.capZonesByCapZoneID[zoneID] if capZonesForID and capZonesForID.zones then local count = Spearhead.Util.tableLength(capZonesForID.zones) if count == 0 then self._logger:warn("Tried to get cap zone for zoneID: " .. zoneID .. " but cap zones were empty for this ID") return nil end capZonesForID.current = capZonesForID.current + 1 if Spearhead.Util.tableLength(capZonesForID.zones) < capZonesForID.current then capZonesForID.current = 1 end return capZonesForID.zones[capZonesForID.current] else self._logger:warn("Tried to get cap zone for zoneID: " .. zoneID .. " but no cap zones were found for this ID") end return nil end function Database:GetInterceptZonesForZoneID(zoneID) zoneID = tostring(zoneID) or "nothing" local interceptZonesForID = self._tables.interceptZonesByZoneID[zoneID] if not interceptZonesForID then return {} end return interceptZonesForID end ---@return Array result a list of stage zone names function Database:getStagezoneNames() return self._tables.StageZoneNames end function Database:getCarrierRouteZones() return self._tables.CarrierRouteZones end ---@param stagename string ---@return string function Database:getBriefingForStage(stagename) local stageZone = self._tables.StageZones[stagename] if not stageZone then return "" end return stageZone.StageBriefing or "" end ---@return Array function Database:getMissionsForStage(stagename) local stageZone = self._tables.StageZones[stagename] if not stageZone then return {} end return stageZone.MissionZones end ---@return Array function Database:getRandomMissionsForStage(stagename) local stageZone = self._tables.StageZones[stagename] if not stageZone then return {} end return stageZone.RandomMissionZones end ---@return MissionZoneData? function Database:getMissionDataForZone(missionZoneName) return self._tables.MissionZoneData[missionZoneName] end ---@param stageName string ---@return table result airbase Names function Database:getAirbaseNamesInStage(stageName) local stageData = self._tables.StageZones[stageName] if not stageData then return {} end return stageData.AirbaseNames or {} end function Database:getFarpNamesInStage(stageName) local stageData = self._tables.StageZones[stageName] if not stageData then return {} end return stageData.FarpZones or {} end ---@return FarpZoneData? function Database:getFarpDataForZone(farpZoneName) local farpData = self._tables.FarpZoneData[farpZoneName] if not farpData then return nil end return farpData end ---@return AirbaseData? ---@param baseName string function Database:getAirbaseDataForZone(baseName) local baseData = self._tables.AirbaseDataPerAirfield[baseName] if not baseData then return nil end return baseData end ---@return string? function Database:getStageBriefingForStage(stageName) local stageData = self._tables.StageZones[stageName] if not stageData then return nil end return stageData.StageBriefing or nil end ---@param stageName string ---@return Array function Database:getBlueSamsInStage(stageName) local stageData = self._tables.StageZones[stageName] if not stageData then return {} end return stageData.BlueSamZones end ---@param stageName string ---@return Array function Database:getSupplyHubsInStage(stageName) local stageData = self._tables.StageZones[stageName] if not stageData then return {} end return stageData.SupplyHubZones end ---comment ---@param stageName any ---@param supplyZoneName any ---@return nil function Database:getFarpDependencyForSupplyHub(stageName, supplyZoneName) local stageData = self._tables.StageZones[stageName] if not stageData then return nil end return stageData.SupplyHubZonesInFarp[supplyZoneName] end ---@param samZone string ---@return BlueSamData? function Database:getBlueSamDataForZone(samZone) return self._tables.BlueSamDataPerZone[samZone] end function Database:getMiscGroupsAtStage(stageName) local stageZone = self._tables.StageZones[stageName] if not stageZone then return {} end return stageZone.MiscGroups end ---@return integer|nil function Database:GetNewMissionCode() 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 if not Spearhead then Spearhead = {} end Spearhead.DB = Database