Develop (#16)
* Initial Methods for SpearheadAPI * Added Spearhead API * Added Spearhead API * fixed compile for api * Added notes for API in beta * Added notes for API in beta * Added Menu to all doc pages * Added Menu to all doc pages * added it for real * added it for real * fixed develop * fixed develop * fixed develop * wip * wip * updated * work in progress * work in progress * added empty docs page * wip * removed SAR files from persistence branch * Added Blue Sams as seperate class * added initial persistance * added initial persistance * added initial persistance * added initial persistance * added initial persistance * added menu to persistence markdown * added menu to persistence markdown * added menu to persistence markdown * docs update * wip * wip * Refactor of Stage classes. * wip * wip * wip * reworked bluesam and stagebase * Added waiting stage functionality * updated docs * wip * wip * wip * updated * wip * wip * wip * refactored without issues, needs testing --------- Co-authored-by: Dutchie <54616262+dutchie032@users.noreply.github.com> Co-authored-by: dutchie032 <dutchie032> Co-authored-by: ex61wi <tim.rorije@ing.com>
This commit is contained in:
committed by
GitHub
co-authored by
Dutchie
dutchie032 <dutchie032>
ex61wi
parent
9c6a5ed217
commit
ff392e0e7e
@@ -0,0 +1,404 @@
|
||||
|
||||
|
||||
---@class Mission : OnUnitLostListener
|
||||
---@field name string
|
||||
---@field missionType missionType
|
||||
---@field code string
|
||||
---@field priority MissionPriority
|
||||
---@field private _state MissionState
|
||||
---@field private _zoneName string
|
||||
---@field private _database Database
|
||||
---@field private _logger Logger
|
||||
---@field private _missionBriefing string
|
||||
---@field private _missionGroups MissionGroups
|
||||
---@field private _completeListeners Array<MissionCompleteListener>
|
||||
local Mission = {}
|
||||
|
||||
|
||||
|
||||
--- @class MissionCompleteListener
|
||||
--- @field OnMissionComplete fun(self: any, mission:Mission)
|
||||
|
||||
--- @class MissionGroups
|
||||
--- @field hasTargets boolean
|
||||
--- @field groups Array<SpearheadGroup>
|
||||
--- @field unitsAlive table<string, table<string, boolean>>
|
||||
--- @field targetsAlive table<string, table<string, boolean>>
|
||||
--- @field groupNamesPerunit table<string,string>
|
||||
|
||||
MINIMAL_UNITS_ALIVE_RATIO = 0.21
|
||||
|
||||
---comment
|
||||
---@param zoneName string
|
||||
---@param priority MissionPriority
|
||||
---@param database Database
|
||||
---@param logger Logger
|
||||
---@return Mission?
|
||||
function Mission.New(zoneName, priority, database, logger)
|
||||
|
||||
local function ParseZoneName(input)
|
||||
local split_name = Spearhead.Util.split_string(input, "_")
|
||||
local split_length = Spearhead.Util.tableLength(split_name)
|
||||
if Spearhead.Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then
|
||||
Spearhead.AddMissionEditorWarning("Random Mission with zonename " .. input .. " not in right format")
|
||||
return nil
|
||||
elseif split_length < 3 then
|
||||
Spearhead.AddMissionEditorWarning("Mission with zonename" .. input .. " not in right format")
|
||||
return nil
|
||||
end
|
||||
|
||||
---@type missionType
|
||||
local parsedType = "nil"
|
||||
|
||||
local inputType = string.lower(split_name[2])
|
||||
if inputType == "dead" then parsedType = "DEAD" end
|
||||
if inputType == "strike" then parsedType = "STRIKE" end
|
||||
if inputType == "bai" then parsedType = "BAI" end
|
||||
if inputType == "sam" then parsedType = "SAM" end
|
||||
|
||||
if parsedType == "nil" then
|
||||
Spearhead.AddMissionEditorWarning("Mission with zonename '" .. input .. "' has an unsupported type '" .. (type or "nil" ))
|
||||
return nil
|
||||
end
|
||||
local name = split_name[3]
|
||||
return {
|
||||
missionName = name,
|
||||
type = parsedType
|
||||
}
|
||||
end
|
||||
|
||||
local parsed = ParseZoneName(zoneName)
|
||||
|
||||
if parsed == nil then return end
|
||||
|
||||
Mission.__index = Mission
|
||||
local o = {}
|
||||
local self = setmetatable(o, Mission)
|
||||
|
||||
self._zoneName = zoneName
|
||||
self.name = parsed.missionName
|
||||
self.missionType = parsed.type
|
||||
self.code = tostring(database:GetNewMissionCode())
|
||||
self.priority = priority
|
||||
self._state = "NEW"
|
||||
|
||||
self._logger = logger
|
||||
self._database = database
|
||||
self._completeListeners = {}
|
||||
|
||||
self._missionBriefing = database:getMissionBriefingForMissionZone(zoneName)
|
||||
self._missionGroups = {
|
||||
groups = {},
|
||||
unitsAlive = {},
|
||||
targetsAlive = {},
|
||||
hasTargets = false,
|
||||
groupNamesPerunit = {}
|
||||
}
|
||||
|
||||
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup
|
||||
local groupNames = database:getGroupsForMissionZone(zoneName)
|
||||
for _, groupName in pairs(groupNames) do
|
||||
|
||||
local spearheadGroup = SpearheadGroup.New(groupName)
|
||||
table.insert(self._missionGroups.groups, spearheadGroup)
|
||||
|
||||
local isGroupTarget =Spearhead.Util.startswith(string.lower(groupName), "tgt_")
|
||||
for _, unit in pairs(spearheadGroup:GetUnits())do
|
||||
local unitName = unit:getName()
|
||||
local isUnitTarget = Spearhead.Util.startswith(string.lower(unitName), "tgt_")
|
||||
|
||||
if self._missionGroups.unitsAlive[groupName] == nil then
|
||||
self._missionGroups.unitsAlive[groupName] = {}
|
||||
end
|
||||
|
||||
self._missionGroups.unitsAlive[groupName][unitName] = true
|
||||
self._missionGroups.groupNamesPerunit[unitName] = groupName
|
||||
|
||||
if isGroupTarget == true or isUnitTarget == true then
|
||||
self._missionGroups.hasTargets = true
|
||||
|
||||
if self._missionGroups.targetsAlive[groupName] == nil then
|
||||
self._missionGroups.targetsAlive[groupName] = {}
|
||||
end
|
||||
|
||||
self._missionGroups.targetsAlive[groupName][unitName] = true
|
||||
end
|
||||
|
||||
Spearhead.Events.addOnUnitLostEventListener(unitName, self)
|
||||
end
|
||||
|
||||
Spearhead.DcsUtil.DestroyGroup(groupName)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
---comment
|
||||
---@return MissionState
|
||||
function Mission:GetState()
|
||||
return self._state
|
||||
end
|
||||
|
||||
function Mission:SpawnPersistedState()
|
||||
for _, group in pairs(self._missionGroups.groups) do
|
||||
group:SpawnCorpsesOnly()
|
||||
end
|
||||
end
|
||||
|
||||
function Mission:SpawnActive()
|
||||
|
||||
self._logger:info("Activating " .. self.name)
|
||||
|
||||
self._state = "ACTIVE"
|
||||
for _, group in pairs(self._missionGroups.groups) do
|
||||
group:Spawn()
|
||||
end
|
||||
|
||||
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.AddMissionToCommands(self)
|
||||
|
||||
self:StartCheckingContinuous()
|
||||
end
|
||||
|
||||
---@private
|
||||
function Mission:StartCheckingContinuous()
|
||||
---comment
|
||||
---@param mission Mission
|
||||
---@param time any
|
||||
---@return unknown
|
||||
local Check = function (mission, time)
|
||||
mission:UpdateState(true, true)
|
||||
|
||||
if mission:GetState() == "COMPLETED" then
|
||||
return nil
|
||||
end
|
||||
return time + 30
|
||||
end
|
||||
timer.scheduleFunction(Check, self, timer.getTime() + 30)
|
||||
end
|
||||
|
||||
---@private
|
||||
---@return string?
|
||||
function Mission:ToStateString()
|
||||
if self._missionGroups.hasTargets == true then
|
||||
local dead = 0
|
||||
local total = 0
|
||||
if self._missionGroups.targetsAlive then
|
||||
for _, group in pairs(self._missionGroups.targetsAlive) do
|
||||
for _, isAlive in pairs(group) do
|
||||
total = total + 1
|
||||
if isAlive == false then
|
||||
dead = dead + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if total > 0 then
|
||||
local completionPercentage = math.floor((dead / total) * 100)
|
||||
return "Targets Destroyed: " .. completionPercentage .. "%"
|
||||
end
|
||||
else
|
||||
local dead = 0
|
||||
local total = 0
|
||||
if self._missionGroups.unitsAlive then
|
||||
for _, group in pairs(self._missionGroups.unitsAlive) do
|
||||
for _, isAlive in pairs(group) do
|
||||
total = total + 1
|
||||
if isAlive == false then
|
||||
dead = dead + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if total > 0 then
|
||||
local completionPercentage = math.floor((dead / total) * 100)
|
||||
return "Units Destroyed: " .. completionPercentage .. "%"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param groupId integer
|
||||
function Mission:ShowBriefing(groupId)
|
||||
|
||||
local stateString = self:ToStateString()
|
||||
|
||||
if self._missionBriefing == nil or self._missionBriefing == "" then self._missionBriefing = "No briefing available" end
|
||||
local text = "Mission [" .. self.code .. "] ".. self.name .. "\n \n" .. self._missionBriefing .. " \n \n" .. stateString
|
||||
trigger.action.outTextForGroup(groupId, text, 30);
|
||||
end
|
||||
|
||||
|
||||
---@param checkHealth boolean
|
||||
---@param messageIfDone boolean
|
||||
function Mission:UpdateState(checkHealth, messageIfDone)
|
||||
if checkHealth == nil then checkHealth = false end
|
||||
if messageIfDone == false then messageIfDone = true end
|
||||
|
||||
if self._state == "COMPLETED" then
|
||||
return
|
||||
end
|
||||
|
||||
if checkHealth == true then
|
||||
local function unitAliveState(unitName)
|
||||
local staticObject = StaticObject.getByName(unitName)
|
||||
if staticObject then
|
||||
if staticObject:isExist() == true then
|
||||
local life0 = staticObject:getDesc().life
|
||||
if staticObject:getLife() / life0 < 0.3 then
|
||||
self._logger:debug("exploding unit")
|
||||
trigger.action.explosion(staticObject:getPoint(), 100)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
local unit = Unit.getByName(unitName)
|
||||
|
||||
local alive = unit ~= nil and unit:isExist() == true
|
||||
if alive == true then
|
||||
if unit:getLife() / unit:getLife0() < 0.2 then
|
||||
self._logger:debug("exploding unit")
|
||||
trigger.action.explosion(unit:getPoint(), 100)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for groupName, unitNameDict in pairs(self._missionGroups.unitsAlive) do
|
||||
for unitName, isAlive in pairs(unitNameDict) do
|
||||
if isAlive == true then
|
||||
self._missionGroups.unitsAlive[groupName][unitName] = unitAliveState(unitName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for groupName, unitNameDict in pairs(self._missionGroups.targetsAlive) do
|
||||
for unitName, isAlive in pairs(unitNameDict) do
|
||||
if isAlive == true then
|
||||
self._missionGroups.targetsAlive[groupName][unitName] = unitAliveState(unitName)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self._missionGroups.hasTargets == true then
|
||||
|
||||
local anyTargetAlive = function()
|
||||
for _, units in pairs(self._missionGroups.targetsAlive) do
|
||||
for _, isAlive in pairs(units) do
|
||||
if isAlive == true then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if anyTargetAlive() ~= true then
|
||||
self._state = "COMPLETED"
|
||||
end
|
||||
else
|
||||
local function CountAliveGroups()
|
||||
local aliveGroups = 0
|
||||
|
||||
for _, group in pairs(self._missionGroups.unitsAlive) do
|
||||
local groupTotal = 0
|
||||
local groupDeath = 0
|
||||
for _, isAlive in pairs(group) do
|
||||
if isAlive ~= true then
|
||||
groupDeath = groupDeath + 1
|
||||
end
|
||||
groupTotal = groupTotal + 1
|
||||
end
|
||||
|
||||
local aliveRatio = (groupTotal - groupDeath) / groupTotal
|
||||
if aliveRatio >= MINIMAL_UNITS_ALIVE_RATIO then
|
||||
aliveGroups = aliveGroups + 1
|
||||
end
|
||||
end
|
||||
|
||||
return aliveGroups
|
||||
end
|
||||
|
||||
if CountAliveGroups() == 0 then
|
||||
self._state = "COMPLETED"
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param mission Mission
|
||||
local NotifyMissionComplete = function(mission)
|
||||
mission:NotifyMissionComplete()
|
||||
return nil
|
||||
end
|
||||
if self._state == "COMPLETED" then
|
||||
timer.scheduleFunction(NotifyMissionComplete, self, timer.getTime() + 3)
|
||||
end
|
||||
end
|
||||
|
||||
---private usage advised
|
||||
function Mission:NotifyMissionComplete()
|
||||
Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.RemoveMissionToCommands(self)
|
||||
self._logger:info("Mission Completed: " .. self._zoneName)
|
||||
trigger.action.outText("Mission " .. self.name .. " [" .. self.code .. "] was completed succesfully" , 20)
|
||||
|
||||
for _, listener in pairs(self._completeListeners) do
|
||||
pcall(function()
|
||||
listener:OnMissionComplete(self)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---@param listener MissionCompleteListener Object that implements "OnMissionComplete(self, mission)"
|
||||
function Mission:AddMissionCompleteListener(listener)
|
||||
if type(listener) ~= "table" then
|
||||
return
|
||||
end
|
||||
table.insert(self._completeListeners, listener)
|
||||
end
|
||||
|
||||
function Mission:OnUnitLost(object)
|
||||
--[[
|
||||
OnUnit lost event
|
||||
]]--
|
||||
self._logger:debug("Getting on unit lost event")
|
||||
|
||||
local category = Object.getCategory(object)
|
||||
if category == Object.Category.UNIT then
|
||||
local unitName = object:getName()
|
||||
self._logger:debug("UnitName:" .. unitName)
|
||||
|
||||
local groupName = self._missionGroups.groupNamesPerunit[unitName]
|
||||
self._missionGroups.unitsAlive[groupName][unitName] = false
|
||||
|
||||
if self._missionGroups.targetsAlive[groupName] and self._missionGroups.targetsAlive[groupName][unitName] then
|
||||
self._missionGroups.targetsAlive[groupName][unitName] = false
|
||||
end
|
||||
elseif category == Object.Category.STATIC then
|
||||
local name = object:getName()
|
||||
self._missionGroups.unitsAlive[name][name] = false
|
||||
|
||||
self._logger:debug("Name " .. name)
|
||||
|
||||
if self._missionGroups.targetsAlive[name] and self._missionGroups.targetsAlive[name][name] then
|
||||
self._missionGroups.targetsAlive[name][name] = false
|
||||
end
|
||||
end
|
||||
self:UpdateState(false, true)
|
||||
end
|
||||
|
||||
if not Spearhead.classes then Spearhead.classes = {} end
|
||||
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||
if not Spearhead.classes.stageClasses.Missions then Spearhead.classes.stageClasses.Missions = {} end
|
||||
Spearhead.classes.stageClasses.Missions.Mission = Mission
|
||||
|
||||
|
||||
Reference in New Issue
Block a user