Dcs lua tool (#66)
* custom drawings and animations for video * wip * Work in progress refactor for toolkit * wip * Refactored everything for Lua Compile Tool * Trying out the github action * updated the github action ref * initial working version --------- Co-authored-by: ex61wi <tim.rorije@ing.com> Co-authored-by: dutchie031 <dutchie031>
This commit is contained in:
committed by
GitHub
co-authored by
ex61wi
dutchie031 <dutchie031>
parent
c027921527
commit
128aed1d2b
@@ -0,0 +1,459 @@
|
||||
local SpearheadEvents = require("classes.spearhead_events")
|
||||
local RTBMission = require("classes.capClasses.taskings.RTB")
|
||||
local Util = require("classes.util.Util")
|
||||
local DcsUtil = require("classes.util.DcsUtil")
|
||||
|
||||
---@class AirGroup : OnUnitLostListener
|
||||
---@field protected _logger Logger
|
||||
---@field protected _groupName string
|
||||
---@field protected _groupType AirGroupType
|
||||
---@field protected _state AirGroupState
|
||||
---@field protected _isSpawned boolean
|
||||
---@field protected _config CapConfig
|
||||
---@field protected _checkLivenessNumber number
|
||||
---@field protected _spawnManager SpawnManager
|
||||
local AirGroup = {}
|
||||
AirGroup.__index = AirGroup
|
||||
|
||||
---@param logger Logger
|
||||
---@param groupName string
|
||||
---@param groupType AirGroupType
|
||||
---@param config CapConfig
|
||||
---@param spawnManager SpawnManager
|
||||
function AirGroup:New(groupName, groupType, config, logger, spawnManager)
|
||||
self._groupName = groupName
|
||||
self._groupType = groupType
|
||||
self._isSpawned = false
|
||||
self._state = "UnSpawned" -- Default state
|
||||
self._config = config
|
||||
self._logger = logger
|
||||
self._spawnManager = spawnManager
|
||||
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group then
|
||||
SpearheadEvents.addOnGroupRTBListener(self._groupName, self)
|
||||
SpearheadEvents.addOnGroupRTBInTenListener(self._groupName, self)
|
||||
SpearheadEvents.addOnGroupOnStationListener(self._groupName, self)
|
||||
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self)
|
||||
SpearheadEvents.addOnUnitLandEventListener(unit:getName(), self)
|
||||
end
|
||||
|
||||
spawnManager:DestroyGroup(groupName)
|
||||
end
|
||||
end
|
||||
|
||||
function AirGroup:GetState()
|
||||
return self._state
|
||||
end
|
||||
|
||||
function AirGroup:GetName()
|
||||
return self._groupName
|
||||
end
|
||||
|
||||
function AirGroup:MarkRearmComplete()
|
||||
self:Respawn(false)
|
||||
if self._state == "Rearming" then
|
||||
self:SetState("ReadyOnTheRamp")
|
||||
end
|
||||
end
|
||||
|
||||
---@protected
|
||||
function AirGroup:SetMission(mission)
|
||||
self:SetState("InTransit")
|
||||
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group then
|
||||
local controller = group:getController()
|
||||
if controller then
|
||||
controller:setCommand({
|
||||
id = 'Start',
|
||||
params = {}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local setMissionDelayed = function(data, time)
|
||||
data.self:SetMissionPrivate(data.mission)
|
||||
end
|
||||
|
||||
local data = {
|
||||
self = self,
|
||||
mission = mission
|
||||
}
|
||||
|
||||
timer.scheduleFunction(setMissionDelayed, data, timer.getTime() + 5)
|
||||
end
|
||||
|
||||
function AirGroup:SetMissionPrivate(mission)
|
||||
self:SetState("InTransit")
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group and mission then
|
||||
group:getController():setTask(mission)
|
||||
self._logger:debug("mission - Task set for group: " .. self._groupName)
|
||||
end
|
||||
end
|
||||
|
||||
---@param airbase Airbase
|
||||
function AirGroup:SendRTB(airbase)
|
||||
self._logger:debug("AirGroup:SendRTB called for group: " .. self._groupName)
|
||||
self:SetState("Rtb")
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group then
|
||||
---@type Vec3
|
||||
local location = nil
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
if unit and unit:isExist() == true and unit:inAir() == true then
|
||||
location = unit:getPoint()
|
||||
break
|
||||
end
|
||||
end
|
||||
if location then
|
||||
local mission = RTBMission.getAsMission(airbase, { x= location.x, y= location.z }, self._config)
|
||||
group:getController():setTask(mission)
|
||||
self._logger:debug("AirGroup:SendRTB - Task set for group: " .. self._groupName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AirGroup:Spawn()
|
||||
if self._isSpawned then return end
|
||||
self:SpawnInternal(false)
|
||||
end
|
||||
|
||||
function AirGroup:IsInAir()
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group then
|
||||
local units = group:getUnits()
|
||||
for _, unit in pairs(units) do
|
||||
if unit:inAir() == true then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param force boolean
|
||||
---@param withoutLoadout boolean?
|
||||
---@protected
|
||||
function AirGroup:SpawnInternal(force, withoutLoadout)
|
||||
|
||||
if withoutLoadout == nil then withoutLoadout = false end
|
||||
|
||||
if self._isSpawned and force ~= true then return end
|
||||
|
||||
---@type SpawnOverrides
|
||||
local overrides = {
|
||||
emptyLoadouts = withoutLoadout,
|
||||
uncontrolled = true
|
||||
}
|
||||
|
||||
local group, isStatic = self._spawnManager:SpawnGroup(self._groupName, overrides, false)
|
||||
if isStatic == true then
|
||||
--- If For Some reaons someone tries to schedule static units as CAP classes
|
||||
self._state = "UnSpawned"
|
||||
return
|
||||
end
|
||||
group = group --[[@as Group]]
|
||||
if group then
|
||||
self._group = group
|
||||
self._isSpawned = true
|
||||
self._initialSize = #group:getUnits()
|
||||
self._liveState = {}
|
||||
else
|
||||
self._logger:error("Failed to spawn group: " .. self._groupName)
|
||||
end
|
||||
|
||||
if self._state == "UnSpawned" then
|
||||
self:SetState("ReadyOnTheRamp")
|
||||
end
|
||||
|
||||
---@param selfA AirGroup
|
||||
local function CheckLivenessTask(selfA, time)
|
||||
local interval = selfA:CheckLiveness()
|
||||
if not interval then return end
|
||||
return time + interval
|
||||
end
|
||||
|
||||
if self._checkLivenessNumber then
|
||||
--try remove. Throws Error when number does not exist anymore, hence the pcall
|
||||
pcall(function()
|
||||
timer.removeFunction(self._checkLivenessNumber)
|
||||
end)
|
||||
end
|
||||
|
||||
self._checkLivenessNumber = timer.scheduleFunction(CheckLivenessTask, self, timer.getTime() + 5)
|
||||
end
|
||||
|
||||
|
||||
---@param withoutLoadout boolean?
|
||||
function AirGroup:Respawn(withoutLoadout)
|
||||
self:SpawnInternal(true, withoutLoadout)
|
||||
end
|
||||
|
||||
---@protected
|
||||
---@param state AirGroupState
|
||||
function AirGroup:SetState(state)
|
||||
if self._state == state then return end
|
||||
self._state = state
|
||||
self._logger:debug("AirGroup:State changed for group: " .. self._groupName .. " to state: " .. state)
|
||||
end
|
||||
|
||||
---@return number? timeInterval
|
||||
function AirGroup:CheckLiveness()
|
||||
local isAlive = false
|
||||
local group = Group.getByName(self._groupName)
|
||||
|
||||
if group and group:isExist() == true then
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
if unit and unit:isExist() == true and unit:getLife() > (unit:getLife0() * 0.3) then
|
||||
isAlive = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isAlive == false then
|
||||
self:SetState("Dead")
|
||||
self:CheckStateAndStartRepairRearm()
|
||||
return nil
|
||||
end
|
||||
|
||||
return 10
|
||||
end
|
||||
|
||||
---@protected
|
||||
function AirGroup:OnLastUnitLanded()
|
||||
--- Once landed monitor the units until it's at it's designated location (or died)
|
||||
|
||||
-- Units
|
||||
|
||||
---@class CheckGroupForRestartData
|
||||
---@field self AirGroup
|
||||
---@field lastLocations table<string,Vec3>
|
||||
---@field lastChangeTime number
|
||||
|
||||
---@param data CheckGroupForRestartData
|
||||
---@param time number
|
||||
local checkGroupForRestart = function(data, time)
|
||||
local group = Group.getByName(data.self:GetName())
|
||||
if not group then
|
||||
self:CheckStateAndStartRepairRearm()
|
||||
return
|
||||
end
|
||||
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
if unit and unit:isExist() then
|
||||
local pos = unit:getPoint()
|
||||
|
||||
|
||||
if data.lastLocations[unit:getName()] == nil then
|
||||
data.lastLocations[unit:getName()] = pos
|
||||
return time + 5
|
||||
end
|
||||
|
||||
if pos and Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then
|
||||
data.lastChangeTime = time
|
||||
end
|
||||
data.lastLocations[unit:getName()] = pos
|
||||
end
|
||||
end
|
||||
|
||||
if data.lastChangeTime + 30 < time then
|
||||
-- If no change in 30 seconds, assume all units are parked
|
||||
local withoutLoadout = true
|
||||
data.self:Respawn(withoutLoadout)
|
||||
data.self:CheckStateAndStartRepairRearm()
|
||||
return
|
||||
end
|
||||
|
||||
return time + 5
|
||||
end
|
||||
|
||||
---@type CheckGroupForRestartData
|
||||
local data = {
|
||||
self = self,
|
||||
lastLocations = {},
|
||||
lastChangeTime = timer.getTime()
|
||||
}
|
||||
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group then
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
data.lastLocations[unit:getName()] = unit:getPoint()
|
||||
end
|
||||
end
|
||||
timer.scheduleFunction(checkGroupForRestart, data, timer.getTime() + 5)
|
||||
self._logger:debug("AirGroup:OnLastUnitLanded - Monitoring group: " .. self._groupName)
|
||||
end
|
||||
|
||||
function AirGroup:CheckStateAndStartRepairRearm()
|
||||
self._logger:debug("AirGroup:CheckStateAndStartRepairRearm called for group: " .. self._groupName)
|
||||
local group = Group.getByName(self._groupName)
|
||||
local anyAlive = false
|
||||
local allAlive = true
|
||||
|
||||
if group then
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
if unit and unit:isExist() == true and unit:getLife() > (unit:getLife0() * 0.3) then
|
||||
anyAlive = true
|
||||
else
|
||||
allAlive = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if anyAlive == false then
|
||||
-- Schedule Spawn + Repair + Rearm
|
||||
self:StartRespawn()
|
||||
return
|
||||
end
|
||||
|
||||
if allAlive == false then
|
||||
--- Schedule Spawn + Repair + Rearm
|
||||
self:StartRepair()
|
||||
return
|
||||
end
|
||||
|
||||
--- Reschedule Spawn + Rearm
|
||||
self:StartRearm()
|
||||
end
|
||||
|
||||
do --- RESPAWN FUNCTIONS
|
||||
--[[
|
||||
TODO: Checks to be added to the functions in case a group is destroyed while waiting for repair/rearm.
|
||||
]]
|
||||
|
||||
function AirGroup:StartRespawn()
|
||||
self:SetState("Dead")
|
||||
|
||||
---@param selfA AirGroup
|
||||
local respawnTask = function(selfA, time)
|
||||
selfA:StartRepair()
|
||||
end
|
||||
|
||||
local delay = self._config:getDeathDelay()
|
||||
if delay < 2 then
|
||||
delay = 2
|
||||
end
|
||||
return timer.scheduleFunction(respawnTask, self, timer.getTime() + delay)
|
||||
end
|
||||
|
||||
function AirGroup:StartRepair()
|
||||
self:SetState("Repairing")
|
||||
|
||||
if self._isSpawned == false then
|
||||
self:Spawn()
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param selfA AirGroup
|
||||
local rearmTask = function(selfA, time)
|
||||
self:StartRearm()
|
||||
end
|
||||
|
||||
local delay = self._config:getRepairDelay()
|
||||
if delay < 2 then
|
||||
delay = 2
|
||||
end
|
||||
|
||||
return timer.scheduleFunction(rearmTask, self, timer.getTime() + delay)
|
||||
end
|
||||
|
||||
function AirGroup:StartRearm()
|
||||
self:SetState("Rearming")
|
||||
|
||||
if self._isSpawned == false then
|
||||
self:Spawn()
|
||||
end
|
||||
|
||||
local rearmDelay = self._config:getRearmDelay()
|
||||
if rearmDelay < 2 then
|
||||
rearmDelay = 2
|
||||
end
|
||||
|
||||
---@param selfA AirGroup
|
||||
local rearmTask = function(selfA, time)
|
||||
selfA:MarkRearmComplete()
|
||||
end
|
||||
|
||||
-- Schedule Rearm Complete
|
||||
return timer.scheduleFunction(rearmTask, self, timer.getTime() + rearmDelay)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do --EVENT LISTENERS
|
||||
---@param unit Unit
|
||||
function AirGroup:OnUnitLost(unit)
|
||||
self:CheckLiveness()
|
||||
end
|
||||
|
||||
---@param groupName string
|
||||
function AirGroup:OnGroupRTBInTen(groupName)
|
||||
if self._groupName == groupName then
|
||||
self._logger:debug("AirGroup:OnGroupRTBInTen called for group: " .. self._groupName)
|
||||
self:SetState("RtbInTen")
|
||||
end
|
||||
end
|
||||
|
||||
---@param groupName string
|
||||
function AirGroup:OnGroupRTB(groupName)
|
||||
if self._groupName == groupName then
|
||||
self._logger:debug("AirGroup:OnGroupRTB called for group: " .. self._groupName)
|
||||
self:SetState("Rtb")
|
||||
end
|
||||
end
|
||||
|
||||
function AirGroup:OnUnitLanded(unit, airbase)
|
||||
local anyInAir = false
|
||||
local group = Group.getByName(self._groupName)
|
||||
if group then
|
||||
for _, u in pairs(group:getUnits()) do
|
||||
if u and u:isExist() == true and u:inAir() == true then
|
||||
anyInAir = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not anyInAir then
|
||||
self:OnLastUnitLanded()
|
||||
end
|
||||
end
|
||||
|
||||
function AirGroup:OnGroupOnStation(groupName)
|
||||
if self._groupName == groupName then
|
||||
self:SetState("OnStation")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AirGroup:Destroy()
|
||||
self._spawnManager:DestroyGroup(self._groupName)
|
||||
end
|
||||
|
||||
return AirGroup
|
||||
|
||||
---@alias AirGroupState
|
||||
---| "UnSpawned"
|
||||
---| "ReadyOnTheRamp
|
||||
---| "InTransit"
|
||||
---| "OnStation"
|
||||
---| "RtbInTen"
|
||||
---| "Rtb"
|
||||
---| "Dead"
|
||||
---| "Repairing"
|
||||
---| "Rearming"
|
||||
|
||||
|
||||
---@alias AirGroupType
|
||||
---| "CAP"
|
||||
---| "SWEEP"
|
||||
---| "INTERCEPT"
|
||||
|
||||
---| "CAS"
|
||||
---| "SEAD"
|
||||
---| "INTERCEPT"
|
||||
---| ""
|
||||
Reference in New Issue
Block a user