Files
MOOSE/Moose Development/Moose/Ops/NavyGroup.lua
T
2020-05-14 22:50:27 +02:00

533 lines
17 KiB
Lua

--- **Ops** - Enhanced Naval Group.
--
-- **Main Features:**
--
-- * Dynamically add and remove waypoints
-- * Let the group steam into the wind.
-- * Command a full stop.
-- * Let a submarine dive and surface.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.NavyGroup
-- @image OPS_NavyGroup.png
--- NAVYGROUP class.
-- @type NAVYGROUP
-- @extends Ops.OpsGroup#OPSGROUP
--- *Something must be left to chance; nothing is sure in a sea fight above all.* -- Horatio Nelson
--
-- ===
--
-- ![Banner Image](..\Presentations\NAVYGROUP\NavyGroup_Main.jpg)
--
-- # The NAVYGROUP Concept
--
-- This class enhances naval groups.
--
-- @field #NAVYGROUP
NAVYGROUP = {
ClassName = "NAVYGROUP",
}
--- Navy group element.
-- @type NAVYGROUP.Element
-- @field #string name Name of the element, i.e. the unit.
-- @field #string typename Type name.
--- NavyGroup version.
-- @field #string version
NAVYGROUP.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Stop and resume route.
-- TODO: Add waypoints.
-- TODO: Add tasks.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new NAVYGROUP class object.
-- @param #NAVYGROUP self
-- @param #string GroupName Name of the group.
-- @return #NAVYGROUP self
function NAVYGROUP:New(GroupName)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, OPSGROUP:New(GroupName)) -- #NAVYGROUP
-- Set some string id for output to DCS.log file.
self.lid=string.format("NAVYGROUP %s | ", self.groupname)
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "FullStop", "Holding") -- Hold position.
self:AddTransition("*", "TurnIntoWind", "*") -- Hold position.
self:AddTransition("*", "Cruise", "Cruising") -- Hold position.
self:AddTransition("*", "Dive", "Diving") -- Hold position.
self:AddTransition("Diving", "Surface", "Cruising") -- Hold position.
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the NAVYGROUP. Initializes parameters and starts event handlers.
-- @function [parent=#NAVYGROUP] Start
-- @param #NAVYGROUP self
--- Triggers the FSM event "Start" after a delay. Starts the NAVYGROUP. Initializes parameters and starts event handlers.
-- @function [parent=#NAVYGROUP] __Start
-- @param #NAVYGROUP self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the NAVYGROUP and all its event handlers.
-- @param #NAVYGROUP self
--- Triggers the FSM event "Stop" after a delay. Stops the NAVYGROUP and all its event handlers.
-- @function [parent=#NAVYGROUP] __Stop
-- @param #NAVYGROUP self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Status".
-- @function [parent=#NAVYGROUP] Status
-- @param #NAVYGROUP self
--- Triggers the FSM event "Status" after a delay.
-- @function [parent=#NAVYGROUP] __Status
-- @param #NAVYGROUP self
-- @param #number delay Delay in seconds.
-- Debug trace.
if false then
self.Debug=true
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
end
self:_InitGroup()
self:Start()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add a *scheduled* task.
-- @param #NAVYGROUP self
-- @param Core.Point#COORDINATE Coordinate Coordinate of the target.
-- @param #number Nshots Number of shots to fire. Default 3.
-- @param #number WeaponType Type of weapon. Default auto.
-- @param #string Clock Time when to start the attack.
-- @param #number Prio Priority of the task.
function NAVYGROUP:AddTaskFireAtPoint(Coordinate, Radius, Nshots, WeaponType, Clock, Prio)
local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, Coordinate:GetVec2(), Radius, Nshots, WeaponType)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start NAVYGROUP FSM. Handle events.
-- @param #NAVYGROUP self
function NAVYGROUP:onafterStart(From, Event, To)
-- Info.
self:I(self.lid..string.format("Starting NAVYGROUP v%s for %s", NAVYGROUP.version, self.groupname))
-- Update route.
--self:UpdateRoute()
-- Init status updates.
self:__Status(-1)
end
--- Update status.
-- @param #NAVYGROUP self
function NAVYGROUP:onafterStatus(From, Event, To)
local fsmstate=self:GetState()
local speed=self.group:GetVelocityKNOTS()
-- Info text.
local text=string.format("State %s: Speed=%.1f knots", fsmstate, speed)
self:I(self.lid..text)
self:__Status(-30)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "UpdateRoute" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number n Waypoint number. Default is next waypoint.
function NAVYGROUP:onafterUpdateRoute(From, Event, To, n)
-- Update route from this waypoint number onwards.
n=n or self.currentwp+1
-- Update waypoint tasks, i.e. inject WP tasks into waypoint table.
self:_UpdateWaypointTasks()
-- Waypoints.
local waypoints={}
-- Current velocity.
local speed=self.group and self.group:GetVelocityKMH() or 100
local current=self:GetCoordinate():WaypointNaval(speed)
table.insert(waypoints, current)
-- Add remaining waypoints to route.
for i=n, #self.waypoints do
local wp=self.waypoints[i]
self:E({wp=wp})
-- Set speed.
wp.speed=UTILS.KmphToMps(self.speedCruise)
-- Add waypoint.
table.insert(waypoints, wp)
end
if #waypoints>1 then
-- Route group to all defined waypoints remaining.
self.group:Route(waypoints, 1)
else
---
-- No waypoints left
---
self:UpdateRoute(1)
end
end
--- On after "TurnIntoWind" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number Duration Duration in seconds.
-- @param #number Speed Speed in knots.
-- @param #boolean Uturn Return to the place we came from.
function NAVYGROUP:onafterTurnIntoWind(From, Event, To, Duration, Speed, Uturn)
self.turnintowind=timer.getAbsTime()
local headingTo=self:GetCoordinate():GetWind(50)
local distance=UTILS.NMToMeters(1000)
local wp={}
local coord=self:GetCoordinate()
local Coord=coord:Translate(distance, headingTo)
wp[1]=coord:WaypointNaval(Speed)
wp[2]=Coord:WaypointNaval(Speed)
self.group:Route(wp, 1)
end
--- On after "FullStop" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function NAVYGROUP:onafterFullStop(From, Event, To)
-- Get current position.
local pos=self:GetCoordinate()
-- Create a new waypoint.
local wp=pos:WaypointNaval(0)
-- Create new route consisting of only this position ==> Stop!
self.group:Route({wp})
end
--- On after "Cruise" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function NAVYGROUP:onafterCruise(From, Event, To)
self:UpdateRoute()
end
--- On after "PassingWaypoint" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number n Waypoint passed.
-- @param #number N Total number of waypoints.
function NAVYGROUP:onafterPassingWaypoint(From, Event, To, n, N)
self:I(self.lid..string.format("Passed waypoint %d of %d", n, N))
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Routing
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Route group along waypoints. Enroute tasks are also applied.
-- @param #NAVYGROUP self
-- @param #table waypoints Table of waypoints.
-- @return #NAVYGROUP self
function NAVYGROUP:Route(waypoints)
if self:IsAlive() then
-- DCS task combo.
local Tasks={}
-- Route (Mission) task.
local TaskRoute=self.group:TaskRoute(waypoints)
table.insert(Tasks, TaskRoute)
-- TaskCombo of enroute and mission tasks.
local TaskCombo=self.group:TaskCombo(Tasks)
-- Set tasks.
if #Tasks>1 then
self:SetTask(TaskCombo)
else
self:SetTask(TaskRoute)
end
else
self:E(self.lid.."ERROR: Group is not alive!")
end
return self
end
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
-- @param #NAVYGROUP self
-- @return #NAVYGROUP self
function NAVYGROUP:_InitGroup()
-- First check if group was already initialized.
if self.groupinitialized then
self:E(self.lid.."WARNING: Group was already initialized!")
return
end
-- Get template of group.
self.template=self.group:GetTemplate()
-- Helo group.
--self.isSubmarine=self.group:IsSubmarine()
-- Is (template) group late activated.
self.isLateActivated=self.template.lateActivation
-- Max speed in km/h.
self.speedmax=self.group:GetSpeedMax()
-- Cruise speed: 70% of max speed but within limit.
self.speedCruise=self.speedmax*0.7
-- Group ammo.
--self.ammo=self:GetAmmoTot()
self.traveldist=0
self.traveltime=timer.getAbsTime()
self.position=self:GetCoordinate()
-- Radio parameters from template.
--self.radioOn=self.template.communication
self.radioFreq=self.template.units[1].frequency
self.radioModu=self.template.units[1].modulation
-- If not set by the use explicitly yet, we take the template values as defaults.
if not self.radioFreqDefault then
self.radioFreqDefault=self.radioFreq
self.radioModuDefault=self.radioModu
end
-- Set default formation.
if not self.formationDefault then
if self.ishelo then
self.formationDefault=ENUMS.Formation.RotaryWing.EchelonLeft.D300
else
self.formationDefault=ENUMS.Formation.FixedWing.EchelonLeft.Group
end
end
-- Get first unit. This is used to extract other parameters.
local unit=self.group:GetUnit(1)
local units=self.group:GetUnits()
for _,_unit in pairs(units) do
local element={} --#NAVYGROUP.Element
local unit=_unit --Wrapper.Unit#UNIT
element.name=unit:GetName()
element.typename=unit:GetTypeName()
table.insert(self.elements, element)
end
if unit then
self.descriptors=unit:GetDesc()
self.actype=unit:GetTypeName()
-- Debug info.
local text=string.format("Initialized Navy Group %s:\n", self.groupname)
text=text..string.format("AC type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedmax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radioFreq, UTILS.GetModulationName(self.radioModu), tostring(self.radioOn))
--text=text..string.format("Ammo = %d (G=%d/R=%d/B=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Bombs, self.ammo.Missiles)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self.group:IsAlive()))
--text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
-- Init done.
self.groupinitialized=true
end
return self
end
--- Initialize Mission Editor waypoints.
-- @param #NAVYGROUP self
-- @param #table waypoints Table of waypoints. Default is from group template.
-- @return #NAVYGROUP self
function NAVYGROUP:InitWaypoints(waypoints)
-- Template waypoints.
self.waypoints0=self.group:GetTemplateRoutePoints()
-- Waypoints of group as defined in the ME.
self.waypoints=waypoints or UTILS.DeepCopy(self.waypoints0)
-- Debug info.
self:I(self.lid..string.format("Initializing %d waypoints", #self.waypoints))
-- Update route.
if #self.waypoints>0 then
-- Check if only 1 wp?
if #self.waypoints==1 then
self.passedfinalwp=true
end
-- Update route (when airborne).
self:__UpdateRoute(-1)
end
return self
end
--- Initialize Mission Editor waypoints.
-- @param #NAVYGROUP self
function NAVYGROUP:_UpdateWaypointTasks()
local waypoints=self.waypoints
local nwaypoints=#waypoints
for i,wp in pairs(waypoints) do
if i>self.currentwp or nwaypoints==1 then
-- Debug info.
self:T2(self.lid..string.format("Updating waypoint task for waypoint %d/%d. Last waypoint passed %d.", i, nwaypoints, self.currentwp))
-- Tasks of this waypoint
local taskswp={}
-- At each waypoint report passing.
local TaskPassingWaypoint=self.group:TaskFunction("NAVYGROUP._PassingWaypoint", self, i)
table.insert(taskswp, TaskPassingWaypoint)
-- Waypoint task combo.
wp.task=self.group:TaskCombo(taskswp)
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set rules of engagement.
-- @param #NAVYGROUP self
-- @param #string roe "Hold", "Free", "Return".
function NAVYGROUP:_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 #NAVYGROUP self
-- @param #string state "Green", "Red", "Auto".
function NAVYGROUP:_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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------