mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-22 11:34:04 +00:00
912 lines
32 KiB
Lua
912 lines
32 KiB
Lua
--- **Ops** - (R2.5) - Manage behavior of the Carrier Strike Group.
|
|
--
|
|
-- **Main Features:**
|
|
--
|
|
-- * Nice stuff.
|
|
--
|
|
-- ===
|
|
--
|
|
-- ### Author: **funkyfranky**
|
|
-- @module Ops.Skipper
|
|
-- @image OPS_Skipper.png
|
|
|
|
|
|
--- SKIPPER class.
|
|
-- @type SKIPPER
|
|
-- @field #string ClassName Name of the class.
|
|
-- @field #boolean Debug Debug mode. Messages to all about status.
|
|
-- @field #string sid Class id string for output to DCS log file.
|
|
-- @field #string carriername The name of the carrier unit.
|
|
-- @field Wrapper.Group#GROUP group The carrier strike group.
|
|
-- @field #table menu Table of menu items.
|
|
-- @field Functional.Warehouse#WAREHOUSE warehouse The warehouse of the carrier.
|
|
-- @field Functional.Artillery#ARTY arty The artillery object of the carrier.
|
|
-- @field #table intruders Table of intruders, i.e. groups inside the CCA. Each element is of type #SKIPPPER.Intruder.
|
|
-- @field Ops.CarrierAirWing#CVW cvw Carrier Air Wing.
|
|
-- @extends Ops.Airboss#AIRBOSS
|
|
|
|
--- Be surprised!
|
|
--
|
|
-- ===
|
|
--
|
|
-- 
|
|
--
|
|
-- # The SKIPPER Concept
|
|
--
|
|
--
|
|
--
|
|
-- @field #SKIPPER
|
|
SKIPPER = {
|
|
ClassName = "SKIPPER",
|
|
sid = nil,
|
|
carriername = nil,
|
|
group = nil,
|
|
menu = nil,
|
|
warehouse = nil,
|
|
arty = nil,
|
|
intruders = {},
|
|
cvw = nil,
|
|
}
|
|
|
|
--- Intruder.
|
|
-- @type SKIPPER.Intruder
|
|
-- @field Wrapper.Group#GROUP group Intruder group object.
|
|
-- @field #string groupname Name of the intruder group.
|
|
-- @field #number time0 Abs. mission time first detected inside CCA.
|
|
-- @field #number dist0 Distance first detected inside CCA.
|
|
-- @field DCS#Coalition.Side coalition Coalition side.
|
|
-- @field #number threadlevel Thread level of intruder.
|
|
-- @field #string threadtext Thread text.
|
|
-- @field #number category Group category.
|
|
-- @field #string categoryname Group category name.
|
|
-- @field #string typename Type name of group.
|
|
-- @field #table menu The defence menu entries.
|
|
|
|
--- FlightControl class version.
|
|
-- @field #string version
|
|
SKIPPER.version="0.0.5"
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- TODO list
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
-- TODO: A lot!
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Constructor
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Create a new SKIPPER class object for a specific aircraft carrier unit.
|
|
-- @param #SKIPPER self
|
|
-- @param #string carriername Name of the carrier.
|
|
-- @param #string alias Skipper alias.
|
|
-- @return #SKIPPER self
|
|
function SKIPPER:New(carriername, alias)
|
|
|
|
-- Inherit everything from FSM class.
|
|
--local self=BASE:Inherit(self, FSM:New()) -- #SKIPPER
|
|
local self=BASE:Inherit(self, AIRBOSS:New(carriername, alias)) -- #SKIPPER
|
|
|
|
if not self.carrier then
|
|
BASE:E(string.format("ERROR: Could not find carrier %s!", carriername))
|
|
return nil
|
|
end
|
|
|
|
--self.alias=alias or carriername
|
|
|
|
--self:
|
|
|
|
self.carriername=carriername
|
|
|
|
self.group=self.carrier:GetGroup()
|
|
|
|
self.arty=ARTY:New(self.group, carriername, self.alias)
|
|
|
|
self.warehouse=WAREHOUSE:New(carriername, self.alias)
|
|
|
|
local skipper=self
|
|
|
|
--- Function for WAREHOUSE self request events.
|
|
function self.warehouse:OnAfterSelfRequest(From,Event,To,groupset,request)
|
|
env.info("FF Warehous self request!")
|
|
skipper:_SelfRequest(self, groupset, request)
|
|
end
|
|
|
|
|
|
-- Set some string id for output to DCS.log file.
|
|
self.sid=string.format("SKIPPER %s |", self.carriername)
|
|
|
|
-- Add FSM transitions.
|
|
-- From State --> Event --> To State
|
|
self:AddTransition("*", "SkipperStatus", "*") -- Skipper status update.
|
|
self:AddTransition("*", "IntruderAlert", "*") -- New intruder detected.
|
|
|
|
------------------------
|
|
--- Pseudo Functions ---
|
|
------------------------
|
|
|
|
--- Triggers the FSM event "Start". Starts the SKIPPER. Initializes parameters and starts event handlers.
|
|
-- @function [parent=#SKIPPER] Start
|
|
-- @param #SKIPPER self
|
|
|
|
--- Triggers the FSM event "Start" after a delay. Starts the SKIPPER. Initializes parameters and starts event handlers.
|
|
-- @function [parent=#SKIPPER] __Start
|
|
-- @param #SKIPPER self
|
|
-- @param #number delay Delay in seconds.
|
|
|
|
--- Triggers the FSM event "Stop". Stops the SKIPPER and all its event handlers.
|
|
-- @param #SKIPPER self
|
|
|
|
--- Triggers the FSM event "Stop" after a delay. Stops the SKIPPER and all its event handlers.
|
|
-- @function [parent=#SKIPPER] __Stop
|
|
-- @param #SKIPPER self
|
|
-- @param #number delay Delay in seconds.
|
|
|
|
--- Triggers the FSM event "SkipperStatus".
|
|
-- @function [parent=#SKIPPER] SkipperStatus
|
|
-- @param #SKIPPER self
|
|
|
|
--- Triggers the FSM event "SkipperStatus" after a delay.
|
|
-- @function [parent=#SKIPPER] __SkipperStatus
|
|
-- @param #SKIPPER self
|
|
-- @param #number delay Delay in seconds.
|
|
|
|
--- Triggers the FSM event "IntruderAlert".
|
|
-- @function [parent=#SKIPPER] IntruderAlert
|
|
-- @param #SKIPPER self
|
|
-- @param #SKIPPER.Intruder Intruder data table.
|
|
|
|
--- Triggers the FSM delayed event "IntruderAlert".
|
|
-- @function [parent=#SKIPPER] __IntruderAlert
|
|
-- @param #SKIPPER self
|
|
-- @param #number delay Delay in seconds before the function is called.
|
|
-- @param #SKIPPER.Intruder Intruder data table.
|
|
|
|
--- On after "IntruderAlert" event user function. Called when a missile was launched.
|
|
-- @function [parent=#SKIPPER] OnAfterIntruderAlert
|
|
-- @param #SKIPPER self
|
|
-- @param #string From From state.
|
|
-- @param #string Event Event.
|
|
-- @param #string To To state.
|
|
-- @param #SKIPPER.Intruder Intruder data table.
|
|
|
|
|
|
-- Debug trace.
|
|
if true then
|
|
self.Debug=true
|
|
BASE:TraceOnOff(true)
|
|
BASE:TraceClass(self.ClassName)
|
|
BASE:TraceLevel(1)
|
|
self.arty:GetAmmo(true)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- User Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Optimized F10 radio menu for a single carrier. The menu entries will be stored directly under F10 Other/Skipper/ and not F10 Other/Skipper/"Carrier Alias"/.
|
|
-- **WARNING**: If you use this with two SKIPPER objects/carriers, the radio menu will be screwed up!
|
|
-- @param #SKIPPER self
|
|
-- @param #boolean switch If true or nil single menu is enabled. If false, menu is for multiple carriers in the mission.
|
|
-- @return #SKIPPER self
|
|
function SKIPPER:SetMenuSingleCarrier(switch)
|
|
if switch==true or switch==nil then
|
|
self.menusingle=true
|
|
else
|
|
self.menusingle=false
|
|
end
|
|
return self
|
|
end
|
|
|
|
|
|
--- Get carrier coalition.
|
|
-- @param #SKIPPER self
|
|
-- @return #number Coalition side of carrier.
|
|
function SKIPPER:GetCoalition()
|
|
return self.carrier:GetCoalition()
|
|
end
|
|
|
|
--- Get carrier coordinate.
|
|
-- @param #SKIPPER self
|
|
-- @return Core.Point#COORDINATE Carrier coordinate.
|
|
function SKIPPER:GetCoordinate()
|
|
return self.carrier:GetCoordinate()
|
|
end
|
|
|
|
--- Get WAREHOUSE object associated with the carrier.
|
|
-- @param #SKIPPER self
|
|
-- @return Functional.Warehouse#WAREHOUSE Warehouse object.
|
|
function SKIPPER:GetWarehouseCarrier()
|
|
return self.warehouse
|
|
end
|
|
|
|
--- Get ARTY object associated with the carrier strike group.
|
|
-- @param #SKIPPER self
|
|
-- @return Functional.Artillery#ARTY Arty object.
|
|
function SKIPPER:GetArty()
|
|
return self.arty
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Status
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Start SKIPPER FSM. Handle events.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:onafterStart(From, Event, To)
|
|
|
|
-- Start parent airboss.
|
|
self:GetParent(self).onafterStart(self, From, Event, To)
|
|
|
|
-- Info.
|
|
self:I(self.sid..string.format("Starting SKIPPER v%s for carrier %s on map %s.", SKIPPER.version, self.carriername, self.theatre))
|
|
|
|
-- Start ARTY.
|
|
self.arty:Start()
|
|
|
|
-- Start Warehouse.
|
|
self.warehouse:Start()
|
|
|
|
-- Add F10 radio menu.
|
|
self:_SetMenuCoalition()
|
|
|
|
-- Handle events.
|
|
self:HandleEvent(EVENTS.Birth)
|
|
self:HandleEvent(EVENTS.EngineStartup)
|
|
self:HandleEvent(EVENTS.Takeoff)
|
|
self:HandleEvent(EVENTS.Land)
|
|
self:HandleEvent(EVENTS.EngineShutdown)
|
|
self:HandleEvent(EVENTS.Crash)
|
|
|
|
-- Init status updates.
|
|
self:__SkipperStatus(-1)
|
|
end
|
|
|
|
--- Update status.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:onafterSkipperStatus(From, Event, To)
|
|
|
|
local fsmstate=self:GetState()
|
|
|
|
-- Check zone for flights inbound.
|
|
self:_CheckIntruder()
|
|
|
|
-- Info text.
|
|
local text=string.format("State %s", fsmstate)
|
|
self:I(self.sid..text)
|
|
|
|
self:__SkipperStatus(-30)
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- FSM Events
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- CCA Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Scan carrier zone for (new) units.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:_CheckIntruder()
|
|
|
|
-- Carrier position.
|
|
local coord=self:GetCoordinate()
|
|
|
|
-- Scan radius = radius of the CCA.
|
|
local RCCZ=self.zoneCCA:GetRadius()
|
|
|
|
-- Debug info.
|
|
self:T(self.sid..string.format("Scanning Carrier Controlled Area. Radius=%.1f NM.", UTILS.MetersToNM(RCCZ)))
|
|
|
|
-- Scan units in carrier zone.
|
|
local _,_,_,unitscan=coord:ScanObjects(RCCZ, true, false, false)
|
|
|
|
|
|
-- Make a table with all groups currently in the CCA zone.
|
|
local insideCCA={}
|
|
for _,_unit in pairs(unitscan) do
|
|
local unit=_unit --Wrapper.Unit#UNIT
|
|
|
|
-- Necessary conditions to be met:
|
|
local airborne=unit:IsAir()
|
|
local inzone=unit:IsInZone(self.zoneCCA)
|
|
local friendly=self:GetCoalition()==unit:GetCoalition()
|
|
|
|
-- Check if this an aircraft and that it is airborne and closing in.
|
|
if inzone then
|
|
|
|
local group=unit:GetGroup()
|
|
local groupname=group:GetName()
|
|
|
|
if insideCCA[groupname]==nil and groupname~=self.group:GetName() then
|
|
insideCCA[groupname]=group
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
-- Intruder menu needs update.
|
|
local updateintrudermenu=false
|
|
|
|
-- Find out if any known intruder is not in the CCA any more.
|
|
for i=#self.intruders,1,-1 do
|
|
local intruder=self.intruders[i] --#SKIPPER.Intruder
|
|
|
|
-- Loop over current groups in CCA.
|
|
local gotit=false
|
|
for groupname,_group in pairs(insideCCA) do
|
|
local group=_group --Wrapper.Group#GROUP
|
|
|
|
if groupname==intruder.groupname then
|
|
gotit=true
|
|
end
|
|
end
|
|
|
|
-- Intruder is gone!
|
|
if not gotit then
|
|
|
|
-- Remove from table.
|
|
table.remove(self.intruders, i)
|
|
|
|
-- Menu update required.
|
|
updateintrudermenu=true
|
|
end
|
|
|
|
end
|
|
|
|
for groupname,_group in pairs(insideCCA) do
|
|
local group=_group --Wrapper.Group#GROUP
|
|
|
|
-- Find out if any known intruder is not in the CCA any more.
|
|
local gotit=false
|
|
for i=1,#self.intruders do
|
|
local intruder=self.intruders[i] --#SKIPPER.Intruder
|
|
if groupname==intruder.groupname then
|
|
gotit=true
|
|
end
|
|
end
|
|
|
|
if not gotit then
|
|
|
|
-- Create a new intruder table.
|
|
local intruder={} --#SKIPPER.Intruder
|
|
intruder.coalition=group:GetCoalition()
|
|
intruder.group=group
|
|
intruder.threadlevel, intruder.threadtext=group:GetUnit(1):GetThreatLevel()
|
|
intruder.time0=timer.getAbsTime()
|
|
intruder.dist0=self:GetCoordinate():Get2DDistance(group:GetCoordinate())
|
|
intruder.groupname=groupname
|
|
intruder.typename=group:GetTypeName()
|
|
intruder.category=group:GetCategory()
|
|
intruder.categoryname=group:GetCategoryName()
|
|
|
|
-- Add intruder to list.
|
|
table.insert(self.intruders, intruder)
|
|
|
|
-- Trigger alert!
|
|
self:IntruderAlert(intruder)
|
|
|
|
-- Menu update required.
|
|
updateintrudermenu=true
|
|
end
|
|
end
|
|
|
|
-- Update intruder menu if necessary.
|
|
if updateintrudermenu then
|
|
self:_SortIntruders()
|
|
self:_UpdateIntruderMenu()
|
|
end
|
|
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Misc Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Menu Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Patrol carrier.
|
|
-- @param #SKIPPER self
|
|
-- @return #SKIPPER self
|
|
function SKIPPER:_SetMenuCoalition()
|
|
|
|
-- Get coalition.
|
|
local Coalition=self:GetCoalition()
|
|
|
|
-- Init menu table.
|
|
self.menu=self.menu or {}
|
|
|
|
-- Init menu coalition table.
|
|
self.menu[Coalition]=self.menu[Coalition] or {}
|
|
|
|
-- Shortcut.
|
|
local menu=self.menu[Coalition]
|
|
|
|
if self.menusingle then
|
|
-- F10/Skipper/...
|
|
if not menu.Skipper then
|
|
menu.Skipper=MENU_COALITION:New(Coalition, "Skipper")
|
|
end
|
|
else
|
|
-- F10/Skipper/<Carrier Alias>/...
|
|
if not menu.Root then
|
|
menu.Root=MENU_COALITION:New(Coalition, "Skipper")
|
|
end
|
|
menu.Skipper=MENU_COALITION:New(Coalition, self.alias, menu.Root)
|
|
end
|
|
|
|
------------------
|
|
-- Defence Menu --
|
|
------------------
|
|
|
|
menu.SetSpeed = MENU_COALITION:New(Coalition, "Set Speed", menu.Skipper)
|
|
menu.SetSpeed_00 = MENU_COALITION_COMMAND:New(Coalition, "Hold Position", menu.SetSpeed, self._SetSpeed, self, 0)
|
|
menu.SetSpeed_05 = MENU_COALITION_COMMAND:New(Coalition, "5 knots", menu.SetSpeed, self._SetSpeed, self, 5)
|
|
menu.SetSpeed_10 = MENU_COALITION_COMMAND:New(Coalition, "10 knots", menu.SetSpeed, self._SetSpeed, self, 10)
|
|
menu.SetSpeed_15 = MENU_COALITION_COMMAND:New(Coalition, "15 knots", menu.SetSpeed, self._SetSpeed, self, 15)
|
|
menu.SetSpeed_20 = MENU_COALITION_COMMAND:New(Coalition, "20 knots", menu.SetSpeed, self._SetSpeed, self, 20)
|
|
menu.SetSpeed_25 = MENU_COALITION_COMMAND:New(Coalition, "25 knots", menu.SetSpeed, self._SetSpeed, self, 25)
|
|
menu.SetSpeed_30 = MENU_COALITION_COMMAND:New(Coalition, "30 knots", menu.SetSpeed, self._SetSpeed, self, 30)
|
|
menu.SetSpeed_99 = MENU_COALITION_COMMAND:New(Coalition, "Restore Route", menu.SetSpeed, self.CarrierResume, self)
|
|
|
|
menu.Recovery=MENU_COALITION:New(Coalition, "Recovery", menu.Skipper)
|
|
|
|
-- Set wind on deck.
|
|
menu.SetWoD = MENU_COALITION:New(Coalition, "Wind on Deck", menu.Recovery)
|
|
menu.SetWoD_10 = MENU_COALITION_COMMAND:New(Coalition, "10 knots", menu.SetWoD, self._SetWoD, self, 10)
|
|
menu.SetWoD_15 = MENU_COALITION_COMMAND:New(Coalition, "15 knots", menu.SetWoD, self._SetWoD, self, 15)
|
|
menu.SetWoD_20 = MENU_COALITION_COMMAND:New(Coalition, "20 knots", menu.SetWoD, self._SetWoD, self, 20)
|
|
menu.SetWoD_25 = MENU_COALITION_COMMAND:New(Coalition, "25 knots", menu.SetWoD, self._SetWoD, self, 25)
|
|
menu.SetWoD_30 = MENU_COALITION_COMMAND:New(Coalition, "30 knots", menu.SetWoD, self._SetWoD, self, 30)
|
|
|
|
-- Set Duration.
|
|
menu.SetRtime = MENU_COALITION:New(Coalition, "Duration", menu.Recovery)
|
|
menu.SetRtime_15 = MENU_COALITION_COMMAND:New(Coalition, "15 min", menu.SetRtime, self._SetRtime, self, 15)
|
|
menu.SetRtime_30 = MENU_COALITION_COMMAND:New(Coalition, "30 min", menu.SetRtime, self._SetRtime, self, 30)
|
|
menu.SetRtime_45 = MENU_COALITION_COMMAND:New(Coalition, "45 min", menu.SetRtime, self._SetRtime, self, 45)
|
|
menu.SetRtime_60 = MENU_COALITION_COMMAND:New(Coalition, "60 min", menu.SetRtime, self._SetRtime, self, 60)
|
|
menu.SetRtime_90 = MENU_COALITION_COMMAND:New(Coalition, "90 min", menu.SetRtime, self._SetRtime, self, 90)
|
|
|
|
-- Start/Stop.
|
|
menu.SetUturn = MENU_COALITION_COMMAND:New(Coalition, "U-turn On/Off", menu.Recovery, self._SetUturn, self)
|
|
menu.CaseI = MENU_COALITION_COMMAND:New(Coalition, "Start CASE I", menu.Recovery, self._StartCaseX, self, 1)
|
|
menu.CaseII = MENU_COALITION_COMMAND:New(Coalition, "Start CASE II", menu.Recovery, self._StartCaseX, self, 2)
|
|
menu.CaseIII = MENU_COALITION_COMMAND:New(Coalition, "Start CASE III", menu.Recovery, self._StartCaseX, self, 3)
|
|
menu.Rstop = MENU_COALITION_COMMAND:New(Coalition, "Pause/Unpause", menu.Recovery, self._Rpause, self)
|
|
menu.Rstop = MENU_COALITION_COMMAND:New(Coalition, "Stop Recovery", menu.Recovery, self._Rstop, self)
|
|
|
|
|
|
------------------
|
|
-- Defence Menu --
|
|
------------------
|
|
|
|
menu.Defence = MENU_COALITION:New(Coalition, "Defence", menu.Skipper)
|
|
menu.Defence_SetROE = MENU_COALITION:New(Coalition, "Set ROE", menu.Defence)
|
|
menu.Defence_SetROE_Hold = MENU_COALITION_COMMAND:New(Coalition, "Weapon Hold", menu.Defence_SetROE, self._SetROE, self, "Hold")
|
|
menu.Defence_SetROE_Free = MENU_COALITION_COMMAND:New(Coalition, "Weapon Free", menu.Defence_SetROE, self._SetROE, self, "Free")
|
|
menu.Defence_SetROE_Return = MENU_COALITION_COMMAND:New(Coalition, "Return Fire", menu.Defence_SetROE, self._SetROE, self, "Return")
|
|
|
|
menu.Defence_Intruders = MENU_COALITION:New(Coalition, "Intruders", menu.Defence)
|
|
menu.Defence_Ammo = MENU_COALITION_COMMAND:New(Coalition, "Report Ammo", menu.Defence, self.arty.GetAmmo, self.arty, true)
|
|
menu.Defence_IntruderR = MENU_COALITION_COMMAND:New(Coalition, "Report Intruders", menu.Defence, self._ListIntruders, self)
|
|
menu.Defence_LaunchCAP = MENU_COALITION_COMMAND:New(Coalition, "Lauch CAP", menu.Defence, self.LaunchCAP, self)
|
|
|
|
--------------------
|
|
-- Warehouse Menu --
|
|
--------------------
|
|
|
|
if self.warehouse then
|
|
menu.Warehouse=MENU_COALITION:New(Coalition, "Warehouse", menu.Skipper)
|
|
menu.Warehouse_Assets = MENU_COALITION_COMMAND:New(Coalition, "Reports On/Off", menu.Warehouse, self.WarehouseReportsToggle, self)
|
|
menu.Warehouse_Assets = MENU_COALITION_COMMAND:New(Coalition, "Report Assets", menu.Warehouse, self.ReportWarehouseStock, self)
|
|
end
|
|
|
|
end
|
|
|
|
--- Warehouse reports on/off.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:WarehouseReportsToggle()
|
|
self.warehouse.Report=not self.warehouse.Report
|
|
MESSAGE:New(string.format("Warehouse reports are now %s", tostring(self.warehouse.Report)), 10, "SKIPPER", true):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
|
|
--- Report warehouse stock.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:ReportWarehouseStock()
|
|
local text=self.warehouse:_GetStockAssetsText(false)
|
|
MESSAGE:New(text, 10, "SKIPPER", true):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Handle self requests from carrier warehouse.
|
|
-- @param #SKIPPER self
|
|
-- @param Functional.Warehouse#WAREHOUSE warehouse The (carrier) warehouse object.
|
|
-- @param Core.Set#SET_GROUP groupset Group set.
|
|
-- @param Functional.Warehouse#WAREHOUSE.Pendingitem request Request.
|
|
function SKIPPER:_SelfRequest(warehouse, groupset, request)
|
|
|
|
local assignment=request.assignment
|
|
|
|
env.info(string.format("FF warehouse self request assignment %s", tostring(assignment)))
|
|
|
|
end
|
|
|
|
--- Launch a Intercept flight.
|
|
-- @param #SKIPPER self
|
|
-- @param #SKIPPER.Intruder intruder Intruder group.
|
|
function SKIPPER:LaunchIntercept(intruder)
|
|
|
|
--local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, WAREHOUSE.Attribute.AIR_FIGHTER, false)
|
|
local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, "Intercept", false)
|
|
|
|
if n>0 then
|
|
|
|
self.warehouse:AddRequest(self.warehouse, WAREHOUSE.Descriptor.ASSIGNMENT, "Intercept", 1, nil, nil, nil, "Intercept")
|
|
|
|
local capcoord=intruder.group:GetCoordinate()
|
|
|
|
capcoord:MarkToAll("Intruder coord")
|
|
|
|
function self.warehouse.OnAfterSelfRequest(warehouse, From, Event, To, Groupset, Request)
|
|
local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem
|
|
local groupset=Groupset --Core.Set#SET_GROUP
|
|
|
|
for _,_group in pairs(groupset:GetSet()) do
|
|
local group=_group --Wrapper.Group#GROUP
|
|
|
|
if request.assignment=="Intercept" then
|
|
|
|
-- Task orbit.
|
|
local tasks={}
|
|
|
|
for _,unit in pairs(intruder.group:GetUnits()) do
|
|
tasks[#tasks+1]=group:TaskAttackUnit(unit)
|
|
end
|
|
|
|
local speed=group:GetSpeedMax()
|
|
local altitude=intruder.group:GetAltitude()
|
|
|
|
-- Create waypoints.
|
|
local wp={}
|
|
wp[1]=self:GetCoordinate():WaypointAirTakeOffParking()
|
|
wp[2]=self:GetCoordinate():SetAltitude(altitude):WaypointAirTurningPoint(COORDINATE.WaypointAltType.BARO, speed, {tasks}, "Attack Intruder")
|
|
|
|
-- Start uncontrolled group.
|
|
group:StartUncontrolled()
|
|
|
|
--airboss:SetExcludeAI()
|
|
|
|
-- Route group
|
|
group:Route(wp)
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
MESSAGE:New("No INTERCEPT fighters currently available", 5, "SKIPPER"):ToCoalition(self:GetCoalition())
|
|
end
|
|
end
|
|
|
|
|
|
--- Launch a CAP flight.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:LaunchCAP()
|
|
|
|
--local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, WAREHOUSE.Attribute.AIR_FIGHTER, false)
|
|
local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, "CAP", false)
|
|
|
|
if n>0 then
|
|
|
|
self.warehouse:AddRequest(self.warehouse, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_FIGHTER, 1, nil, nil, nil, "CAP")
|
|
|
|
local capcoord=self.zoneCCA:GetRandomCoordinate(self.zoneCCA:GetRadius()/2)
|
|
|
|
capcoord:MarkToAll("CAP coord")
|
|
|
|
function self.warehouse.OnAfterSelfRequest(warehouse, From, Event, To, Groupset, Request)
|
|
local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem
|
|
local groupset=Groupset --Core.Set#SET_GROUP
|
|
|
|
for _,_group in pairs(groupset:GetSet()) do
|
|
local group=_group --Wrapper.Group#GROUP
|
|
|
|
if request.assignment=="CAP" then
|
|
|
|
local alt=UTILS.FeetToMeters(20000)
|
|
|
|
-- Task orbit.
|
|
local taskOrbit=group:TaskOrbit(capcoord, alt, UTILS.KnotsToMps(400), capcoord:Translate(UTILS.NMToMeters(15), 0))
|
|
|
|
local wp={}
|
|
wp[1]=self:GetCoordinate():WaypointAirTakeOffParking()
|
|
wp[2]=self:GetCoordinate():SetAltitude(alt):WaypointAirTurningPoint(COORDINATE.WaypointAltType.BARO, UTILS.KnotsToKmph(350), {taskOrbit}, "CAP")
|
|
|
|
-- Start uncontrolled group.
|
|
group:StartUncontrolled()
|
|
|
|
--airboss:SetExcludeAI()
|
|
|
|
-- Route group.
|
|
group:Route(wp)
|
|
end
|
|
end
|
|
end
|
|
|
|
else
|
|
MESSAGE:New("No CAP fighters currently available", 5, "SKIPPER"):ToCoalition(self:GetCoalition())
|
|
end
|
|
end
|
|
|
|
|
|
--- Enage an intruder by ARTY.
|
|
-- @param #SKIPPER self
|
|
-- @param #SKIPPER.Intruder intruder The intruder data.
|
|
-- @param #number weapontype Type of weapon.
|
|
function SKIPPER:EngageArty(intruder, weapontype)
|
|
|
|
weapontype=weapontype or ARTY.WeaponType.Auto
|
|
|
|
if intruder.group and intruder.group:IsAlive() then
|
|
|
|
-- Current position.
|
|
local coord=intruder.group:GetCoordinate()
|
|
|
|
if intruder.category==Group.Category.GROUND or intruder.category==Group.Category.TRAIN then
|
|
self.arty:AssignTargetCoord(coord, 50, 100, 5, 1, nil, weapontype, intruder.groupname, false)
|
|
elseif intruder.category==Group.Category.SHIP then
|
|
self.arty:AssignAttackGroup(intruder.group, 50, 100, 5, 1, nil, weapontype, intruder.groupname, false)
|
|
elseif intruder.category==Group.Category.AIRPLANE or intruder.category==Group.Category.HELICOPTER then
|
|
self.arty:AssignAttackGroup(intruder.group, 50, 100, 5, 1, nil, weapontype, intruder.groupname, false)
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
--- Sort intruders table.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:_SortIntruders()
|
|
|
|
-- Sort potential section members wrt to distance to lead.
|
|
local function _sort(_a,_b)
|
|
local a=_a --#SKIPPER.Intruder
|
|
local b=_b --#SKIPPER.Intruder
|
|
return a.threadlevel>b.threadlevel
|
|
end
|
|
|
|
table.sort(self.intruders, _sort)
|
|
end
|
|
|
|
--- Add sub menu for this intruder.
|
|
-- @param #SKIPPER self
|
|
-- @param #SKIPPER.Intruder intruder The intruder data.
|
|
-- @param #boolean removed If true, an intruder was removed. If false or nil, a new intruder was added.
|
|
function SKIPPER:_UpdateIntruderMenu()
|
|
|
|
for _,_intruder in ipairs(self.intruders) do
|
|
local intruder=_intruder --#SKIPPER.Intruder
|
|
self:_AddIntruderMenu(intruder)
|
|
end
|
|
|
|
end
|
|
|
|
--- Add sub menu for this intruder.
|
|
-- @param #SKIPPER self
|
|
-- @param #SKIPPER.Intruder intruder The intruder data.
|
|
function SKIPPER:_AddIntruderMenu(intruder)
|
|
|
|
local Coalition=self:GetCoalition()
|
|
|
|
local root=self.menu[Coalition].Defence_Intruders
|
|
|
|
local menu=MENU_COALITION:New(Coalition, intruder.typename, root)
|
|
|
|
if intruder.category==Group.Category.GROUND then
|
|
MENU_COALITION_COMMAND:New(Coalition, "Engage Shells", menu, self.EngageArty, self, intruder, ARTY.WeaponType.Cannon)
|
|
MENU_COALITION_COMMAND:New(Coalition, "Engage Cruise M", menu, self.EngageArty, self, intruder, ARTY.WeaponType.CruiseMissile)
|
|
elseif intruder.category==Group.Category.SHIP then
|
|
MENU_COALITION_COMMAND:New(Coalition, "Engage", menu, self.EngageArty, self, intruder)
|
|
elseif intruder.category==Group.Category.AIRPLANE or intruder.category==Group.Category.HELICOPTER then
|
|
MENU_COALITION_COMMAND:New(Coalition, "Engage A2A", menu, self.LaunchIntercept, self, intruder)
|
|
end
|
|
|
|
-- Set menu.
|
|
intruder.menu=menu
|
|
|
|
end
|
|
|
|
--- Remove sub menu for this intruder.
|
|
-- @param #SKIPPER self
|
|
-- @param #SKIPPER.Intruder intruder The intruder data.
|
|
function SKIPPER:_RemoveIntruderMenu(intruder)
|
|
|
|
if intruder.menu then
|
|
local menu=intruder.menu --Core.Menu#MENU_COALITION
|
|
|
|
menu:Remove()
|
|
|
|
intruder.menu=nil
|
|
end
|
|
|
|
end
|
|
|
|
|
|
--- Intruders.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:_ListIntruders()
|
|
|
|
local text="Current Intruders:"
|
|
|
|
for i,_intruder in pairs(self.intruders) do
|
|
local intruder=_intruder --#SKIPPER.Intruder
|
|
text=text..string.format("\n[%d] %s*%d, %s [%d/10]", i, intruder.typename, #intruder.group:GetUnits(), intruder.categoryname, intruder.threadlevel)
|
|
end
|
|
|
|
if #self.intruders==0 then
|
|
text=text.." none."
|
|
end
|
|
|
|
MESSAGE:New(text, 10, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Start Case X recovery.
|
|
-- @param #SKIPPER self
|
|
-- @param #number case Recovery case (1,2,3).
|
|
function SKIPPER:_StartCaseX(case)
|
|
|
|
--[[
|
|
self.skipperTime=self.skipperTime or 30
|
|
self.skipperSpeed=self.skipperSpeed or 25
|
|
if self.skipperUturn==nil then
|
|
self.skipperUturn=false
|
|
end
|
|
]]
|
|
|
|
-- Inform player.
|
|
local text=string.format("Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.", case, self.skipperTime, self.skipperSpeed, tostring(self.skipperUturn))
|
|
|
|
if self:IsRecovering() then
|
|
text="negative, carrier is already recovering."
|
|
MESSAGE:New(string.format(text), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
return
|
|
end
|
|
|
|
-- Recovery staring in 5 min for 30 min.
|
|
local t0=timer.getAbsTime()+5*60
|
|
local t9=t0+self.skipperTime*60
|
|
local C0=UTILS.SecondsToClock(t0)
|
|
local C9=UTILS.SecondsToClock(t9)
|
|
|
|
-- Carrier will turn into the wind. Wind on deck 25 knots. U-turn on.
|
|
self:AddRecoveryWindow(C0, C9, case, 30, true, self.skipperSpeed, self.skipperUturn)
|
|
|
|
MESSAGE:New(string.format(text), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Toggle recovery U-turn option.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:_SetUturn()
|
|
|
|
self.skipperUturn=not self.skipperUturn
|
|
|
|
MESSAGE:New(string.format("Recovery U-turn is now %s.", tostring(self.skipperUturn)), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Set manual recovery duration.
|
|
-- @param #SKIPPER self
|
|
-- @param #number time Duration in minutes.
|
|
function SKIPPER:_SetRtime(time)
|
|
|
|
self.skipperTime=time
|
|
|
|
MESSAGE:New(string.format("Recovery duration set to %d min.", time), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
|
|
--- Set wind on deck for manual recovery start.
|
|
-- @param #SKIPPER self
|
|
-- @param #number speed Speed in knots.
|
|
function SKIPPER:_SetWoD(speed)
|
|
|
|
self.skipperSpeed=speed
|
|
|
|
MESSAGE:New(string.format("Wind on Deck set to %d knots.", speed), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Set new speed for all waypoints.
|
|
-- @param #SKIPPER self
|
|
-- @param #number speed Speed in knots.
|
|
function SKIPPER:_SetSpeed(speed)
|
|
|
|
-- Loop over waypoints.
|
|
for n=1,#self.waypoints do
|
|
local coord=self.waypoints[n] --Core.Point#COORDINATE
|
|
|
|
coord.Velocity=UTILS.KnotsToMps(speed)
|
|
end
|
|
|
|
self:_PatrolRoute()
|
|
|
|
end
|
|
|
|
--- Set rules of engagement.
|
|
-- @param #SKIPPER self
|
|
-- @param #string roe "Hold", "Free", "Return".
|
|
function SKIPPER:_SetROE(roe)
|
|
|
|
if roe=="Hold" then
|
|
self.group:OptionROEHoldFire()
|
|
elseif roe=="Free" then
|
|
self.group:OptionROEOpenFire()
|
|
elseif roe=="Return" then
|
|
self.group:OptionROEReturnFire()
|
|
end
|
|
|
|
MESSAGE:New(string.format("ROE set to %s", roe), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Set alarm state. (Not useful/working for ships.)
|
|
-- @param #SKIPPER self
|
|
-- @param #string state "Green", "Red", "Auto".
|
|
function SKIPPER:_SetALS(state)
|
|
|
|
if state=="Green" then
|
|
self.group:OptionAlarmStateGreen()
|
|
elseif state=="Red" then
|
|
self.group:OptionAlarmStateRed()
|
|
elseif state=="Auto" then
|
|
self.group:OptionAlarmStateAuto()
|
|
end
|
|
|
|
MESSAGE:New(string.format("Alarm state set to %s", state), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Function to stop the carrier.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:CarrierHold()
|
|
env.info("Carrier Hold!")
|
|
|
|
-- Get current position.
|
|
local pos=self.group:GetCoordinate()
|
|
|
|
-- Create a new waypoint.
|
|
local wp=pos:WaypointGround(0)
|
|
|
|
-- Create new route consisting of only this position ==> Stop!
|
|
self.group:Route({wp})
|
|
|
|
MESSAGE:New(string.format("Carrier is holding current position."), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
--- Function to stop the carrier.
|
|
-- @param #SKIPPER self
|
|
function SKIPPER:CarrierResume()
|
|
env.info("Carrier Resume Route!")
|
|
|
|
self:_InitWaypoints()
|
|
|
|
local nextWP,n=self:_GetNextWaypoint()
|
|
|
|
self:_PatrolRoute(n)
|
|
|
|
MESSAGE:New(string.format("Carrier is resuming route to waypoint #%d.", n), 5, self.ClassName):ToCoalition(self:GetCoalition())
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|