mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
OPS
- Added new **CONDITION** class - Added new **OPERATION** class
This commit is contained in:
parent
ae54cd8fde
commit
edbfa9117d
279
Moose Development/Moose/Core/Condition.lua
Normal file
279
Moose Development/Moose/Core/Condition.lua
Normal file
@ -0,0 +1,279 @@
|
||||
--- **Core** - Define any or all conditions to be evaluated.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Add arbitrary numbers of conditon functions
|
||||
-- * Evaluate *any* or *all* conditions
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
-- @module Core.Condition
|
||||
-- @image Core_Condition.png
|
||||
|
||||
|
||||
--- CONDITON class.
|
||||
-- @type CONDITION
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field #boolean isAny General functions are evaluated as any condition.
|
||||
-- @field #boolean negateResult Negeate result of evaluation.
|
||||
-- @field #table functionsGen General condition functions.
|
||||
-- @field #table functionsAny Any condition functions.
|
||||
-- @field #table functionsAll All condition functions.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *Better three hours too soon than a minute too late.* - William Shakespeare
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The CONDITION Concept
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #CONDITION
|
||||
CONDITION = {
|
||||
ClassName = "CONDITION",
|
||||
lid = nil,
|
||||
functionsGen = {},
|
||||
functionsAny = {},
|
||||
functionsAll = {},
|
||||
}
|
||||
|
||||
--- Condition function.
|
||||
-- @type CONDITION.Function
|
||||
-- @field #function func Callback function to check for a condition. Should return a `#boolean`.
|
||||
-- @field #table arg (Optional) Arguments passed to the condition callback function if any.
|
||||
|
||||
--- CONDITION class version.
|
||||
-- @field #string version
|
||||
CONDITION.version="0.0.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Make FSM.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new CONDITION object.
|
||||
-- @param #CONDITION self
|
||||
-- @param #string Name (Optional) Name used in the logs.
|
||||
-- @return #CONDITION self
|
||||
function CONDITION:New(Name)
|
||||
|
||||
-- Inherit BASE.
|
||||
local self=BASE:Inherit(self, BASE:New()) --#CONDITION
|
||||
|
||||
self.name=Name or "Condition X"
|
||||
|
||||
self.lid=string.format("%s | ", self.name)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set that general condition functions return `true` if `any` function returns `true`. Default is that *all* functions must return `true`.
|
||||
-- @param #CONDITION self
|
||||
-- @param #boolean Any If `true`, *any* condition can be true. Else *all* conditions must result `true`.
|
||||
-- @return #CONDITION self
|
||||
function CONDITION:SetAny(Any)
|
||||
self.isAny=Any
|
||||
return self
|
||||
end
|
||||
|
||||
--- Negate result.
|
||||
-- @param #CONDITION self
|
||||
-- @param #boolean Negate If `true`, result is negated else not.
|
||||
-- @return #CONDITION self
|
||||
function CONDITION:SetNegateResult(Negate)
|
||||
self.negateResult=Negate
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
|
||||
-- @param #CONDITION self
|
||||
-- @param #function Function The function to call.
|
||||
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||
--
|
||||
-- @usage
|
||||
-- local function isAequalB(a, b)
|
||||
-- return a==b
|
||||
-- end
|
||||
--
|
||||
-- myCondition:AddFunction(isAequalB, a, b)
|
||||
--
|
||||
-- @return #CONDITION self
|
||||
function CONDITION:AddFunction(Function, ...)
|
||||
|
||||
-- Condition function.
|
||||
local condition=self:_CreateCondition(Function, ...)
|
||||
|
||||
-- Add to table.
|
||||
table.insert(self.functionsGen, condition)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
|
||||
-- @param #CONDITION self
|
||||
-- @param #function Function The function to call.
|
||||
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||
-- @return #CONDITION self
|
||||
function CONDITION:AddFunctionAny(Function, ...)
|
||||
|
||||
-- Condition function.
|
||||
local condition=self:_CreateCondition(Function, ...)
|
||||
|
||||
-- Add to table.
|
||||
table.insert(self.functionsAny, condition)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
|
||||
-- @param #CONDITION self
|
||||
-- @param #function Function The function to call.
|
||||
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||
-- @return #CONDITION self
|
||||
function CONDITION:AddFunctionAll(Function, ...)
|
||||
|
||||
-- Condition function.
|
||||
local condition=self:_CreateCondition(Function, ...)
|
||||
|
||||
-- Add to table.
|
||||
table.insert(self.functionsAll, condition)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Evaluate conditon functions.
|
||||
-- @param #CONDITION self
|
||||
-- @param #boolean AnyTrue If `true`, evaluation return `true` if *any* condition function returns `true`. By default, *all* condition functions must return true.
|
||||
-- @return #boolean Result of condition functions.
|
||||
function CONDITION:Evaluate(AnyTrue)
|
||||
|
||||
-- Any condition for gen.
|
||||
local evalAny=self.isAny
|
||||
if AnyTrue~=nil then
|
||||
evalAny=AnyTrue
|
||||
end
|
||||
|
||||
local isGen=nil
|
||||
if evalAny then
|
||||
isGen=self:_EvalConditionsAny(self.functionsGen)
|
||||
else
|
||||
isGen=self:_EvalConditionsAll(self.functionsGen)
|
||||
end
|
||||
|
||||
-- Is any?
|
||||
local isAny=self:_EvalConditionsAny(self.functionsAny)
|
||||
|
||||
-- Is all?
|
||||
local isAll=self:_EvalConditionsAll(self.functionsAll)
|
||||
|
||||
-- Result.
|
||||
local result=isGen and isAny and isAll
|
||||
|
||||
-- Negate result.
|
||||
if self.negateResult then
|
||||
result=not result
|
||||
end
|
||||
|
||||
-- Debug message.
|
||||
self:I(self.lid..string.format("Evaluate: isGen=%s, isAny=%s, isAll=%s (negate=%s) ==> result=%s", tostring(isGen), tostring(isAny), tostring(isAll), tostring(self.negateResult), tostring(result)))
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
--- Check if all given condition are true.
|
||||
-- @param #CONDITION self
|
||||
-- @param #table functions Functions to evaluate.
|
||||
-- @return #boolean If true, all conditions were true (or functions was empty/nil). Returns false if at least one condition returned false.
|
||||
function CONDITION:_EvalConditionsAll(functions)
|
||||
|
||||
-- At least one condition?
|
||||
local gotone=false
|
||||
|
||||
|
||||
-- Any stop condition must be true.
|
||||
for _,_condition in pairs(functions or {}) do
|
||||
local condition=_condition --#CONDITION.Function
|
||||
|
||||
-- At least one condition was defined.
|
||||
gotone=true
|
||||
|
||||
-- Call function.
|
||||
local istrue=condition.func(unpack(condition.arg))
|
||||
|
||||
-- Any false will return false.
|
||||
if not istrue then
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- All conditions were true.
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--- Check if any of the given conditions is true.
|
||||
-- @param #CONDITION self
|
||||
-- @param #table functions Functions to evaluate.
|
||||
-- @return #boolean If true, at least one condition is true (or functions was emtpy/nil).
|
||||
function CONDITION:_EvalConditionsAny(functions)
|
||||
|
||||
-- At least one condition?
|
||||
local gotone=false
|
||||
|
||||
-- Any stop condition must be true.
|
||||
for _,_condition in pairs(functions or {}) do
|
||||
local condition=_condition --#CONDITION.Function
|
||||
|
||||
-- At least one condition was defined.
|
||||
gotone=true
|
||||
|
||||
-- Call function.
|
||||
local istrue=condition.func(unpack(condition.arg))
|
||||
|
||||
-- Any true will return true.
|
||||
if istrue then
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- No condition was true.
|
||||
if gotone then
|
||||
return false
|
||||
else
|
||||
-- No functions passed.
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Create conditon fucntion object.
|
||||
-- @param #CONDITION self
|
||||
-- @param #function Function The function to call.
|
||||
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||
-- @return #CONDITION.Function Condition function.
|
||||
function CONDITION:_CreateCondition(Function, ...)
|
||||
|
||||
local condition={} --#CONDITION.Function
|
||||
|
||||
condition.func=Function
|
||||
condition.arg={}
|
||||
if arg then
|
||||
condition.arg=arg
|
||||
end
|
||||
|
||||
return condition
|
||||
end
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -31,6 +31,7 @@ __Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Astar.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Condition.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
||||
@ -101,6 +102,7 @@ __Moose.Include( 'Scripts/Moose/Ops/Chief.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Flotilla.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Fleet.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Awacs.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Operation.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/FlightControl.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
|
||||
|
||||
@ -4143,7 +4143,7 @@ function AUFTRAG:CheckGroupsDone()
|
||||
if groupdata then
|
||||
if not (groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED) then
|
||||
-- At least this flight is not DONE or CANCELLED.
|
||||
self:T(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!", groupdata.opsgroup.groupname, groupdata.status))
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!", groupdata.opsgroup.groupname, groupdata.status:upper()))
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -4155,7 +4155,7 @@ function AUFTRAG:CheckGroupsDone()
|
||||
local status=self:GetLegionStatus(legion)
|
||||
if not status==AUFTRAG.Status.CANCELLED then
|
||||
-- At least one LEGION has not CANCELLED.
|
||||
self:T(self.lid..string.format("CheckGroupsDone: LEGION %s is not CANCELLED but in state %s. Mission NOT DONE!", legion.alias, status))
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: LEGION %s is not CANCELLED but in state %s. Mission NOT DONE!", legion.alias, status))
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -4163,7 +4163,7 @@ function AUFTRAG:CheckGroupsDone()
|
||||
-- Check commander status.
|
||||
if self.commander then
|
||||
if not self.statusCommander==AUFTRAG.Status.CANCELLED then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: COMMANDER is not CANCELLED but in state %s. Mission NOT DONE!", self.statusCommander))
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: COMMANDER is not CANCELLED but in state %s. Mission NOT DONE!", self.statusCommander))
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -4171,14 +4171,14 @@ function AUFTRAG:CheckGroupsDone()
|
||||
-- Check chief status.
|
||||
if self.chief then
|
||||
if not self.statusChief==AUFTRAG.Status.CANCELLED then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!", self.statusChief))
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!", self.statusChief))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- These are early stages, where we might not even have a opsgroup defined to be checked. If there were any groups, we checked above.
|
||||
if self:IsPlanned() or self:IsQueued() or self:IsRequested() then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState()))
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState()))
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
-- @field #table tacanChannel List of TACAN channels available to the cohort.
|
||||
-- @field #number weightAsset Weight of one assets group in kg.
|
||||
-- @field #number cargobayLimit Cargo bay capacity in kg.
|
||||
-- @field #table operations Operations this cohort is part of.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *I came, I saw, I conquered.* -- Julius Caesar
|
||||
@ -82,6 +83,7 @@ COHORT = {
|
||||
cargobayLimit = 0,
|
||||
descriptors = {},
|
||||
properties = {},
|
||||
operations = {},
|
||||
}
|
||||
|
||||
--- COHORT class version.
|
||||
@ -1546,6 +1548,16 @@ function COHORT:_MissileCategoryName(categorynumber)
|
||||
return cat
|
||||
end
|
||||
|
||||
--- Add an OPERATION.
|
||||
-- @param #COHORT self
|
||||
-- @param Ops.Operation#OPERATION Operation The operation this cohort is part of.
|
||||
-- @return #COHORT self
|
||||
function COHORT:_AddOperation(Operation)
|
||||
|
||||
self.operations[Operation.name]=Operation
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
-- @field #table missionqueue Mission queue.
|
||||
-- @field #table transportqueue Transport queue.
|
||||
-- @field #table targetqueue Target queue.
|
||||
-- @field #table opsqueue Operations queue.
|
||||
-- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.SupplyZone`.
|
||||
-- @field #table refuellingZones Refuelling zones. Each element is of type `#BRIGADE.SupplyZone`.
|
||||
-- @field #table capZones CAP zones. Each element is of type `#AIRWING.PatrolZone`.
|
||||
@ -127,6 +128,7 @@ COMMANDER = {
|
||||
missionqueue = {},
|
||||
transportqueue = {},
|
||||
targetqueue = {},
|
||||
opsqueue = {},
|
||||
rearmingZones = {},
|
||||
refuellingZones = {},
|
||||
capZones = {},
|
||||
@ -841,6 +843,9 @@ function COMMANDER:onafterStatus(From, Event, To)
|
||||
self:T(self.lid..text)
|
||||
end
|
||||
|
||||
-- Check Operations queue.
|
||||
self:CheckOpsQueue()
|
||||
|
||||
-- Check target queue and add missions.
|
||||
self:CheckTargetQueue()
|
||||
|
||||
@ -1217,6 +1222,28 @@ end
|
||||
-- Mission Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Check OPERATIONs queue.
|
||||
-- @param #COMMANDER self
|
||||
function COMMANDER:CheckOpsQueue()
|
||||
|
||||
-- Number of missions.
|
||||
local Nops=#self.opsqueue
|
||||
|
||||
-- Treat special cases.
|
||||
if Nops==0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Loop over operations.
|
||||
for _,_ops in pairs(self.opsqueue) do
|
||||
local operation=_ops --Ops.Operation#OPRATION
|
||||
|
||||
--TODO: What?
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Check target queue and assign ONE valid target by adding it to the mission queue of the COMMANDER.
|
||||
-- @param #COMMANDER self
|
||||
function COMMANDER:CheckTargetQueue()
|
||||
@ -1281,7 +1308,7 @@ function COMMANDER:CheckTargetQueue()
|
||||
local isReadyStart=target:EvalConditionsAll(target.conditionStart)
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Target %s: Alive=%s, Threat=%s, Important=%s", target:GetName(), tostring(isAlive), tostring(isThreat), tostring(isImportant))
|
||||
local text=string.format("Target %s: Alive=%s, Important=%s", target:GetName(), tostring(isAlive), tostring(isImportant))
|
||||
self:T2(self.lid..text)
|
||||
|
||||
-- Check that target is alive and not already a mission has been assigned.
|
||||
|
||||
@ -149,13 +149,13 @@ FLIGHTCONTROL = {
|
||||
FLIGHTCONTROL.FlightStatus={
|
||||
PARKING="Parking",
|
||||
READYTX="Ready To Taxi",
|
||||
TAXIOUT="Taxi to runway",
|
||||
TAXIOUT="Taxi To Runway",
|
||||
READYTO="Ready For Takeoff",
|
||||
TAKEOFF="Takeoff",
|
||||
INBOUND="Inbound",
|
||||
HOLDING="Holding",
|
||||
LANDING="Landing",
|
||||
TAXIINB="Taxi Inbound",
|
||||
TAXIINB="Taxi To Parking",
|
||||
ARRIVED="Arrived",
|
||||
}
|
||||
|
||||
@ -172,21 +172,20 @@ FLIGHTCONTROL.version="0.5.2"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
--
|
||||
|
||||
-- TODO: Runway destroyed.
|
||||
-- TODO: Support airwings. Dont give clearance for Alert5 or if mission has not started.
|
||||
-- TODO: Switch to enable/disable AI messages.
|
||||
-- TODO: Improve ATC TTS messages.
|
||||
-- TODO: Add helos.
|
||||
-- TODO: Talk me down option.
|
||||
-- TODO: ATIS option.
|
||||
-- TODO: Check runways and clean up.
|
||||
-- TODO: Accept and forbit parking spots.
|
||||
-- TODO: Define holding zone.
|
||||
-- TODO: Add FARPS?
|
||||
-- DONE: Define holding zone.
|
||||
-- DONE: Basic ATC voice overs.
|
||||
-- DONE: Add SRS TTS.
|
||||
-- DONE: Add parking guard.
|
||||
-- NOGO: Add FARPS?
|
||||
-- DONE: Interface with FLIGHTGROUP.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -793,7 +792,7 @@ function FLIGHTCONTROL:_CheckQueues()
|
||||
else
|
||||
-- TODO: Humans have to confirm via F10 menu.
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.LANDING)
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
end
|
||||
|
||||
-- Set time last flight got landing clearance.
|
||||
@ -1299,7 +1298,7 @@ function FLIGHTCONTROL:_RemoveFlightFromQueue(queue, flight, queuename)
|
||||
table.remove(queue, i)
|
||||
|
||||
if not flight.isAI then
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
end
|
||||
|
||||
return true, i
|
||||
@ -1797,6 +1796,9 @@ function FLIGHTCONTROL:_CreatePlayerMenu(flight, mainmenu)
|
||||
MENU_GROUP_COMMAND:New(group, "Radio Check", helpmenu, self._PlayerRadioCheck, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Confirm Status", helpmenu, self._PlayerConfirmStatus, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Mark Holding", helpmenu, self._PlayerNotImplemented, self, groupname)
|
||||
if gotcontrol and flight:IsInbound() and flight.stack then
|
||||
MENU_GROUP_COMMAND:New(group, "Vector Holding", helpmenu, self._PlayerVectorInbound, self, groupname)
|
||||
end
|
||||
|
||||
---
|
||||
-- Info Menu
|
||||
@ -1833,21 +1835,23 @@ function FLIGHTCONTROL:_CreatePlayerMenu(flight, mainmenu)
|
||||
-- Taxiing
|
||||
---
|
||||
|
||||
if status==FLIGHTCONTROL.FlightStatus.READYTO then
|
||||
if status==FLIGHTCONTROL.FlightStatus.READYTX or status==FLIGHTCONTROL.FlightStatus.TAXIOUT then
|
||||
-- Flight is "ready to taxi" (awaiting clearance) or "taxiing to runway".
|
||||
MENU_GROUP_COMMAND:New(group, "Request Takeoff", rootmenu, self._PlayerRequestTakeoff, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Taxi", rootmenu, self._PlayerAbortTaxi, self, groupname)
|
||||
elseif status==FLIGHTCONTROL.FlightStatus.READYTO then
|
||||
-- Flight is ready for take off.
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Takeoff", rootmenu, self._PlayerAbortTakeoff, self, groupname)
|
||||
elseif status==FLIGHTCONTROL.FlightStatus.TAKEOFF then
|
||||
-- Flight is taking off.
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Takeoff", rootmenu, self._PlayerAbortTakeoff, self, groupname)
|
||||
elseif status==FLIGHTCONTROL.FlightStatus.READYTX or status==FLIGHTCONTROL.FlightStatus.TAXIOUT then
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Taxi", rootmenu, self._PlayerAbortTaxi, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Takeoff", rootmenu, self._PlayerRequestTakeoff, self, groupname)
|
||||
elseif status==FLIGHTCONTROL.FlightStatus.TAXIINB then
|
||||
-- Could be after "abort taxi" call and we changed our mind (again)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Parking", rootmenu, self._PlayerRequestParking, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Taxi", rootmenu, self._PlayerRequestTaxi, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Parking", rootmenu, self._PlayerRequestParking, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Arrived at Parking", rootmenu, self._PlayerArrived, self, groupname)
|
||||
end
|
||||
|
||||
MENU_GROUP_COMMAND:New(group, "Arrived and Parking", rootmenu, self._PlayerArrived, self, groupname)
|
||||
|
||||
elseif flight:IsAirborne() then
|
||||
---
|
||||
-- Airborne
|
||||
@ -1858,16 +1862,19 @@ function FLIGHTCONTROL:_CreatePlayerMenu(flight, mainmenu)
|
||||
-- Inbound
|
||||
---
|
||||
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Inbound", rootmenu, self._PlayerAbortInbound, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Holding", rootmenu, self._PlayerHolding, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Inbound", rootmenu, self._PlayerAbortInbound, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Parking", rootmenu, self._PlayerRequestParking, self, groupname)
|
||||
|
||||
|
||||
elseif flight:IsHolding() then
|
||||
---
|
||||
-- Holding
|
||||
---
|
||||
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Holding", rootmenu, self._PlayerAbortHolding, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Landing", rootmenu, self._PlayerConfirmLanding, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Holding", rootmenu, self._PlayerAbortHolding, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Parking", rootmenu, self._PlayerRequestParking, self, groupname)
|
||||
|
||||
elseif flight:IsLanding() then
|
||||
---
|
||||
@ -1875,11 +1882,16 @@ function FLIGHTCONTROL:_CreatePlayerMenu(flight, mainmenu)
|
||||
---
|
||||
|
||||
MENU_GROUP_COMMAND:New(group, "Abort Landing", rootmenu, self._PlayerAbortLanding, self, groupname)
|
||||
|
||||
end
|
||||
|
||||
if flight:IsInbound() or flight:IsHolding() or flight:IsLanding() or flight:IsLanded() then
|
||||
MENU_GROUP_COMMAND:New(group, "Request Parking", rootmenu, self._PlayerRequestParking, self, groupname)
|
||||
|
||||
elseif flight:IsLanded() then
|
||||
---
|
||||
-- Landed
|
||||
---
|
||||
|
||||
MENU_GROUP_COMMAND:New(group, "Arrived at Parking", rootmenu, self._PlayerArrived, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(group, "Request Parking", rootmenu, self._PlayerRequestParking, self, groupname)
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
@ -2159,26 +2171,26 @@ function FLIGHTCONTROL:_PlayerRequestInbound(groupname)
|
||||
-- Call sign.
|
||||
local callsign=flight:GetCallsignName()
|
||||
|
||||
-- Get player element.
|
||||
local player=flight:GetPlayerElement()
|
||||
|
||||
-- Pilot calls inbound for landing.
|
||||
local text=string.format("%s, %s, inbound for landing", self.alias, callsign)
|
||||
|
||||
-- Radio message.
|
||||
self:TransmissionPilot(text, flight)
|
||||
|
||||
-- Current player coord.
|
||||
local flightcoord=flight:GetCoordinate(nil, player.name)
|
||||
|
||||
-- Distance from player to airbase.
|
||||
local dist=flight:GetCoordinate():Get2DDistance(self:GetCoordinate())
|
||||
local dist=flightcoord:Get2DDistance(self:GetCoordinate())
|
||||
|
||||
if dist<UTILS.NMToMeters(50) then
|
||||
|
||||
-- Call RTB event.
|
||||
-- Call RTB event. This also sets the flight control and flight status to INBOUND and updates the menu.
|
||||
flight:RTB(self.airbase)
|
||||
|
||||
-- Set flightcontrol for this flight.
|
||||
flight:SetFlightControl(self)
|
||||
|
||||
-- Add flight to inbound queue.
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.INBOUND)
|
||||
|
||||
-- Get holding point.
|
||||
local stack=self:_GetHoldingStack(flight)
|
||||
|
||||
@ -2190,14 +2202,11 @@ function FLIGHTCONTROL:_PlayerRequestInbound(groupname)
|
||||
-- Stack.
|
||||
flight.stack=stack
|
||||
|
||||
-- Current postion.
|
||||
local coord=flight:GetCoordinate()
|
||||
|
||||
-- Heading to holding point.
|
||||
local heading=coord:HeadingTo(stack.pos0)
|
||||
local heading=flightcoord:HeadingTo(stack.pos0)
|
||||
|
||||
-- Distance to holding point.
|
||||
local distance=coord:Get2DDistance(stack.pos0)
|
||||
local distance=flightcoord:Get2DDistance(stack.pos0)
|
||||
|
||||
local dist=UTILS.MetersToNM(distance)
|
||||
|
||||
@ -2209,7 +2218,7 @@ function FLIGHTCONTROL:_PlayerRequestInbound(groupname)
|
||||
self:TransmissionTower(text, flight, 15)
|
||||
|
||||
-- Create player menu.
|
||||
flight:_UpdateMenu()
|
||||
--flight:_UpdateMenu()
|
||||
|
||||
else
|
||||
self:E(self.lid..string.format("WARNING: Could not get holding stack for flight %s", flight:GetName()))
|
||||
@ -2239,6 +2248,56 @@ function FLIGHTCONTROL:_PlayerRequestInbound(groupname)
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Player vector to inbound
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param #string groupname Name of the flight group.
|
||||
function FLIGHTCONTROL:_PlayerVectorInbound(groupname)
|
||||
|
||||
-- Get flight group.
|
||||
local flight=_DATABASE:GetOpsGroup(groupname) --Ops.FlightGroup#FLIGHTGROUP
|
||||
|
||||
if flight then
|
||||
|
||||
if flight:IsInbound() and self:IsControlling(flight) and flight.stack then
|
||||
|
||||
-- Call sign.
|
||||
local callsign=flight:GetCallsignName()
|
||||
|
||||
-- Get player element.
|
||||
local player=flight:GetPlayerElement()
|
||||
|
||||
-- Current player coord.
|
||||
local flightcoord=flight:GetCoordinate(nil, player.name)
|
||||
|
||||
-- Distance from player to airbase.
|
||||
local dist=flightcoord:Get2DDistance(self:GetCoordinate())
|
||||
|
||||
-- Call sign.
|
||||
local callsign=flight:GetCallsignName()
|
||||
|
||||
-- Heading to holding point.
|
||||
local heading=flightcoord:HeadingTo(flight.stack.pos0)
|
||||
|
||||
-- Distance to holding point.
|
||||
local distance=flightcoord:Get2DDistance(flight.stack.pos0)
|
||||
|
||||
local dist=UTILS.MetersToNM(distance)
|
||||
|
||||
-- Message text.
|
||||
local text=string.format("%s, fly heading %03d for %d nautical miles, hold at angels %d.",
|
||||
callsign, self.alias, heading, dist, flight.stack.angels)
|
||||
|
||||
-- Send message.
|
||||
self:TextMessageToFlight(text, flight)
|
||||
|
||||
end
|
||||
else
|
||||
self:E(self.lid..string.format("Cannot find flight group %s.", tostring(groupname)))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Player aborts inbound.
|
||||
-- @param #FLIGHTCONTROL self
|
||||
-- @param #string groupname Name of the flight group.
|
||||
@ -2274,11 +2333,14 @@ function FLIGHTCONTROL:_PlayerAbortInbound(groupname)
|
||||
self:E(self.lid.."ERROR: No stack!")
|
||||
end
|
||||
|
||||
-- Remove flight. This also updates the menu.
|
||||
self:_RemoveFlight(flight)
|
||||
|
||||
-- Set flight to cruise.
|
||||
flight:Cruise()
|
||||
|
||||
-- Remove flight. This also updates the menu.
|
||||
self:_RemoveFlight(flight)
|
||||
-- Create player menu.
|
||||
--flight:_UpdateMenu()
|
||||
|
||||
else
|
||||
|
||||
@ -2313,19 +2375,27 @@ function FLIGHTCONTROL:_PlayerHolding(groupname)
|
||||
|
||||
if self:IsControlling(flight) then
|
||||
|
||||
-- Callsign.
|
||||
local callsign=flight:GetCallsignName()
|
||||
|
||||
-- Player element.
|
||||
local player=flight:GetPlayerElement()
|
||||
|
||||
-- Holding stack.
|
||||
local stack=flight.stack
|
||||
|
||||
if stack then
|
||||
|
||||
local Coordinate=flight:GetCoordinate()
|
||||
-- Current coordinate.
|
||||
local Coordinate=flight:GetCoordinate(nil, player.name)
|
||||
|
||||
-- Distance.
|
||||
local dist=stack.pos0:Get2DDistance(Coordinate)
|
||||
|
||||
if dist<5000 then
|
||||
|
||||
-- Message to flight
|
||||
local text=string.format("Roger, you are added to the holding queue!")
|
||||
local text=string.format("%s, roger, you are added to the holding queue!", callsign)
|
||||
self:TextMessageToFlight(text, flight, 10, true)
|
||||
|
||||
-- Call holding event.
|
||||
@ -2396,7 +2466,7 @@ function FLIGHTCONTROL:_PlayerAbortHolding(groupname)
|
||||
-- Not holding any more.
|
||||
flight.Tholding=nil
|
||||
|
||||
-- Set flight to cruise.
|
||||
-- Set flight to cruise. This also updates the menu.
|
||||
flight:Cruise()
|
||||
|
||||
-- Set flight.
|
||||
@ -2471,8 +2541,7 @@ function FLIGHTCONTROL:_PlayerConfirmLanding(groupname)
|
||||
self:TransmissionTower(text, flight, 10)
|
||||
|
||||
-- Create player menu.
|
||||
flight:_UpdateMenu()
|
||||
--self:_CreatePlayerMenu(flight, flight.menu.atc)
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
else
|
||||
|
||||
@ -2577,7 +2646,7 @@ function FLIGHTCONTROL:_PlayerRequestTaxi(groupname)
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.READYTX)
|
||||
|
||||
-- Update menu.
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
else
|
||||
self:TextMessageToFlight(string.format("Negative, you must be PARKING to request TAXI!"), flight)
|
||||
@ -2616,7 +2685,7 @@ function FLIGHTCONTROL:_PlayerAbortTaxi(groupname)
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.PARKING)
|
||||
|
||||
-- Update menu.
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
elseif flight:IsTaxiing() then
|
||||
|
||||
@ -2628,7 +2697,7 @@ function FLIGHTCONTROL:_PlayerAbortTaxi(groupname)
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.TAXIINB)
|
||||
|
||||
-- Update menu.
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
else
|
||||
self:TextMessageToFlight(string.format("Negative, you must be PARKING or TAXIING to abort TAXI!"), flight)
|
||||
@ -2690,15 +2759,15 @@ function FLIGHTCONTROL:_PlayerRequestTakeoff(groupname)
|
||||
]]
|
||||
|
||||
-- We only check for landing flights.
|
||||
local text=""
|
||||
local text=string.format("%s, %s, ", callsign, self.alias)
|
||||
if Nlanding==0 then
|
||||
text="No current traffic. You are cleared for takeoff."
|
||||
text=text.."no current traffic. You are cleared for takeoff."
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.TAKEOFF)
|
||||
elseif Nlanding>0 then
|
||||
if Nlanding==1 then
|
||||
text=string.format("Negative, we got %d flight inbound before it's your turn. Hold position until futher notice.", Nlanding)
|
||||
text=text..string.format("negative, we got %d flight inbound before it's your turn. Hold position until futher notice.", Nlanding)
|
||||
else
|
||||
text=string.format("Negative, we got %d flights inbound. Hold positon until futher notice.", Nlanding)
|
||||
text=text..string.format("negative, we got %d flights inbound. Hold positon until futher notice.", Nlanding)
|
||||
end
|
||||
end
|
||||
|
||||
@ -2706,7 +2775,7 @@ function FLIGHTCONTROL:_PlayerRequestTakeoff(groupname)
|
||||
self:TransmissionTower(text, flight, 10)
|
||||
|
||||
-- Update menu.
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
else
|
||||
self:TextMessageToFlight(string.format("Negative, you must request TAXI before you can request TAKEOFF!"), flight)
|
||||
@ -2755,7 +2824,7 @@ function FLIGHTCONTROL:_PlayerAbortTakeoff(groupname)
|
||||
self:TransmissionTower(text, flight, 10)
|
||||
|
||||
-- Update menu.
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
else
|
||||
self:TextMessageToFlight("Negative, You are NOT in the takeoff queue", flight)
|
||||
@ -2857,7 +2926,7 @@ function FLIGHTCONTROL:_PlayerArrived(groupname)
|
||||
local player=flight:GetPlayerElement()
|
||||
|
||||
-- Get current coordinate.
|
||||
local coord=flight:GetCoordinate(player.name)
|
||||
local coord=flight:GetCoordinate(nil, player.name)
|
||||
|
||||
--Closest parking spot.
|
||||
local spot=self:GetClosestParkingSpot(coord)
|
||||
@ -2882,7 +2951,7 @@ function FLIGHTCONTROL:_PlayerArrived(groupname)
|
||||
self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.PARKING)
|
||||
|
||||
-- Create player menu.
|
||||
flight:_UpdateMenu()
|
||||
flight:_UpdateMenu(0.5)
|
||||
|
||||
-- Create mark on F10 map.
|
||||
--[[
|
||||
|
||||
@ -385,7 +385,7 @@ function FLIGHTGROUP:SetReadyForTakeoff(ReadyTO, Delay)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the FLIGHTCONTROL controlling this flight group.
|
||||
--- Set the FLIGHTCONTROL controlling this flight group. Also updates the player menu after 0.5 sec.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol The FLIGHTCONTROL object.
|
||||
-- @return #FLIGHTGROUP self
|
||||
@ -569,7 +569,7 @@ function FLIGHTGROUP:IsParking()
|
||||
return self:Is("Parking")
|
||||
end
|
||||
|
||||
--- Check if flight is parking.
|
||||
--- Check if is taxiing to the runway.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @return #boolean If true, flight is taxiing after engine start up.
|
||||
function FLIGHTGROUP:IsTaxiing()
|
||||
@ -1638,10 +1638,10 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To)
|
||||
self:SetFlightControl(flightcontrol)
|
||||
else
|
||||
-- F10 other menu.
|
||||
self:_UpdateMenu()
|
||||
self:_UpdateMenu(0.5)
|
||||
end
|
||||
else
|
||||
self:_UpdateMenu()
|
||||
self:_UpdateMenu(0.5)
|
||||
end
|
||||
|
||||
end
|
||||
@ -1679,7 +1679,7 @@ function FLIGHTGROUP:onafterParking(From, Event, To)
|
||||
|
||||
if flightcontrol then
|
||||
|
||||
-- Set FC for this flight
|
||||
-- Set FC for this flight. This also updates the menu.
|
||||
self:SetFlightControl(flightcontrol)
|
||||
|
||||
if self.flightcontrol then
|
||||
@ -1779,20 +1779,7 @@ function FLIGHTGROUP:onafterCruise(From, Event, To)
|
||||
-- AI
|
||||
---
|
||||
|
||||
--[[
|
||||
if self:IsTransporting() then
|
||||
if self.cargoTransport and self.cargoTZC and self.cargoTZC.DeployAirbase then
|
||||
self:LandAtAirbase(self.cargoTZC.DeployAirbase)
|
||||
end
|
||||
elseif self:IsPickingup() then
|
||||
if self.cargoTransport and self.cargoTZC and self.cargoTZC.PickupAirbase then
|
||||
self:LandAtAirbase(self.cargoTZC.PickupAirbase)
|
||||
end
|
||||
else
|
||||
self:_CheckGroupDone(nil, 120)
|
||||
end
|
||||
]]
|
||||
|
||||
-- Check group Done.
|
||||
self:_CheckGroupDone(nil, 120)
|
||||
|
||||
else
|
||||
@ -1801,7 +1788,7 @@ function FLIGHTGROUP:onafterCruise(From, Event, To)
|
||||
-- CLIENT
|
||||
---
|
||||
|
||||
self:_UpdateMenu(0.1)
|
||||
--self:_UpdateMenu(0.1)
|
||||
|
||||
end
|
||||
|
||||
@ -3202,7 +3189,7 @@ function FLIGHTGROUP:_InitGroup(Template)
|
||||
|
||||
-- Set callsign. Default is set on spawn if not modified by user.
|
||||
local callsign=template.units[1].callsign
|
||||
self:I({callsign=callsign})
|
||||
--self:I({callsign=callsign})
|
||||
if type(callsign)=="number" then -- Sometimes callsign is just "101".
|
||||
local cs=tostring(callsign)
|
||||
callsign={}
|
||||
@ -4184,8 +4171,15 @@ function FLIGHTGROUP:_UpdateMenu(delay)
|
||||
self:ScheduleOnce(delay, FLIGHTGROUP._UpdateMenu, self)
|
||||
else
|
||||
|
||||
-- Get current position of group.
|
||||
local position=self:GetCoordinate()
|
||||
-- Message to group.
|
||||
MESSAGE:New("Updating MENU state="..self:GetState(), 5):ToGroup(self.group)
|
||||
env.info(self.lid.."updating menu state=")
|
||||
|
||||
-- Player element.
|
||||
local player=self:GetPlayerElement()
|
||||
|
||||
-- Get current position of player.
|
||||
local position=self:GetCoordinate(nil, player.name)
|
||||
|
||||
-- Get all FLIGHTCONTROLS
|
||||
local fc={}
|
||||
@ -4211,6 +4205,7 @@ function FLIGHTGROUP:_UpdateMenu(delay)
|
||||
-- Remove all submenus.
|
||||
self.menu.atc.root:RemoveSubMenus()
|
||||
|
||||
-- Create help menu.
|
||||
self:_CreateMenuAtcHelp(self.menu.atc.root)
|
||||
|
||||
-- Max menu entries.
|
||||
@ -4260,6 +4255,7 @@ function FLIGHTGROUP:_CreateMenuAtcHelp(rootmenu)
|
||||
---
|
||||
MENU_GROUP_COMMAND:New(self.group, "Subtitles On/Off", helpmenu, self._MenuNotImplemented, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(self.group, "My Voice On/Off", helpmenu, self._MenuNotImplemented, self, groupname)
|
||||
MENU_GROUP_COMMAND:New(self.group, "Update Menu", helpmenu, self._UpdateMenu, self, 0)
|
||||
MENU_GROUP_COMMAND:New(self.group, "My Status", helpmenu, self._PlayerMyStatus, self, groupname)
|
||||
|
||||
end
|
||||
|
||||
434
Moose Development/Moose/Ops/Operation.lua
Normal file
434
Moose Development/Moose/Ops/Operation.lua
Normal file
@ -0,0 +1,434 @@
|
||||
--- **Ops** - Operation with multiple phases.
|
||||
--
|
||||
-- ## Main Features:
|
||||
--
|
||||
-- * Define operation phases
|
||||
-- * Dedicate resources to operations
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Operation).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Ops.Operation
|
||||
-- @image OPS_Operation.png
|
||||
|
||||
|
||||
--- OPERATION class.
|
||||
-- @type OPERATION
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity level.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field #string name Name of the operation.
|
||||
-- @field #table cohorts Dedicated cohorts.
|
||||
-- @field #table legions Dedicated legions.
|
||||
-- @field #table phases Phases.
|
||||
-- @field #number counterPhase Running number counting the phases.
|
||||
-- @field #OPERATION.Phase phase Currently active phase (if any).
|
||||
--
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *A warrior's mission is to foster the success of others.* -- Morihei Ueshiba
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The OPERATION Concept
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #OPERATION
|
||||
OPERATION = {
|
||||
ClassName = "OPERATION",
|
||||
verbose = 0,
|
||||
lid = nil,
|
||||
cohorts = {},
|
||||
legions = {},
|
||||
phases = {},
|
||||
counterPhase = 0,
|
||||
}
|
||||
|
||||
--- Global mission counter.
|
||||
_OPERATIONID=0
|
||||
|
||||
--- Operation phase.
|
||||
-- @type OPERATION.Phase
|
||||
-- @field #number uid Unique ID of the phase.
|
||||
-- @field #string name Name of the phase.
|
||||
-- @field Core.Condition#CONDITION conditionOver Conditions when the phase is over.
|
||||
-- @field #boolean isOver If `true`, phase is over.
|
||||
|
||||
--- OPERATION class version.
|
||||
-- @field #string version
|
||||
OPERATION.version="0.0.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new generic OPERATION object.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string Name Name of the operation. Be creative! Default "Operation-01" where the last number is a running number.
|
||||
-- @return #OPERATION self
|
||||
function OPERATION:New(Name)
|
||||
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #OPERATION
|
||||
|
||||
-- Increase global counter.
|
||||
_OPERATIONID=_OPERATIONID+1
|
||||
|
||||
-- Set Name.
|
||||
self.name=Name or string.format("Operation-%02d", _OPERATIONID)
|
||||
|
||||
-- Set log ID.
|
||||
self.lid=string.format("%s | ",self.name)
|
||||
|
||||
|
||||
-- FMS start state is PLANNED.
|
||||
self:SetStartState("Planned")
|
||||
|
||||
-- Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("*", "Start", "Running")
|
||||
|
||||
self:AddTransition("*", "StatusUpdate", "*")
|
||||
|
||||
self:AddTransition("Running", "Pause", "Paused")
|
||||
self:AddTransition("Paused", "Unpause", "Running")
|
||||
|
||||
self:AddTransition("*", "ChangePhase", "*")
|
||||
self:AddTransition("*", "PhaseChange", "*")
|
||||
|
||||
self:AddTransition("*", "Over", "Over")
|
||||
|
||||
self:AddTransition("*", "Stop", "Stopped")
|
||||
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
------------------------
|
||||
|
||||
--- Triggers the FSM event "StatusUpdate".
|
||||
-- @function [parent=#OPERATION] StatusUpdate
|
||||
-- @param #OPERATION self
|
||||
|
||||
--- Triggers the FSM event "Status" after a delay.
|
||||
-- @function [parent=#OPERATION] __StatusUpdate
|
||||
-- @param #OPERATION self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Stop".
|
||||
-- @function [parent=#OPERATION] Stop
|
||||
-- @param #OPERATION self
|
||||
|
||||
--- Triggers the FSM event "Stop" after a delay.
|
||||
-- @function [parent=#OPERATION] __Stop
|
||||
-- @param #OPERATION self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "PhaseChange".
|
||||
-- @function [parent=#OPERATION] PhaseChange
|
||||
-- @param #OPERATION self
|
||||
-- @param #OPERATION.Phase Phase The new phase.
|
||||
|
||||
--- Triggers the FSM event "PhaseChange" after a delay.
|
||||
-- @function [parent=#OPERATION] __PhaseChange
|
||||
-- @param #OPERATION self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param #OPERATION.Phase Phase The new phase.
|
||||
|
||||
--- On after "PhaseChange" event.
|
||||
-- @function [parent=#OPERATION] OnAfterPhaseChange
|
||||
-- @param #OPERATION self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #OPERATION.Phase Phase The new phase.
|
||||
|
||||
-- Init status update.
|
||||
self:__StatusUpdate(-1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User API Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new generic OPERATION object.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number.
|
||||
-- @param Core.Condition#CONDITION ConditionOver Condition when the phase is over.
|
||||
-- @return #OPERATION.Phase Phase table object.
|
||||
function OPERATION:AddPhase(Name, ConditionOver)
|
||||
|
||||
-- Increase phase counter.
|
||||
self.counterPhase=self.counterPhase+1
|
||||
|
||||
local phase={} --#OPERATION.Phase
|
||||
phase.uid=self.counterPhase
|
||||
phase.name=Name or string.format("Phase-%02d", self.counterPhase)
|
||||
phase.conditionOver=ConditionOver or CONDITION:New(Name)
|
||||
phase.isOver=false
|
||||
|
||||
-- Add phase.
|
||||
table.insert(self.phases, phase)
|
||||
|
||||
return phase
|
||||
end
|
||||
|
||||
--- Get a phase by its name.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number.
|
||||
-- @return #OPERATION.Phase Phase table object or nil if phase could not be found.
|
||||
function OPERATION:GetPhaseByName(Name)
|
||||
|
||||
for _,_phase in pairs(self.phases or {}) do
|
||||
local phase=_phase --#OPERATION.Phase
|
||||
if phase.name==Name then
|
||||
return phase
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Assign cohort to operation.
|
||||
-- @param #OPERATION self
|
||||
-- @param Ops.Cohort#COHORT Cohort The cohort
|
||||
-- @return #OPERATION self
|
||||
function OPERATION:AssignCohort(Cohort)
|
||||
|
||||
self.cohorts[Cohort.name]=Cohort
|
||||
|
||||
end
|
||||
|
||||
--- Assign legion to operation. All cohorts of this legion will be assigned and are only available
|
||||
-- @param #OPERATION self
|
||||
-- @param Ops.Legion#LEGION Legion The legion to be assigned.
|
||||
-- @return #OPERATION self
|
||||
function OPERATION:AssignLegion(Legion)
|
||||
|
||||
self.legions[Legion.alias]=Legion
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Set start and stop time of the operation.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added.
|
||||
-- @param #string ClockStop (Optional) Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time.
|
||||
-- @return #OPERATION self
|
||||
function OPERATION:SetTime(ClockStart, ClockStop)
|
||||
|
||||
-- Current mission time.
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
-- Set start time. Default in 5 sec.
|
||||
local Tstart=Tnow+5
|
||||
if ClockStart and type(ClockStart)=="number" then
|
||||
Tstart=Tnow+ClockStart
|
||||
elseif ClockStart and type(ClockStart)=="string" then
|
||||
Tstart=UTILS.ClockToSeconds(ClockStart)
|
||||
end
|
||||
|
||||
-- Set stop time. Default nil.
|
||||
local Tstop=nil
|
||||
if ClockStop and type(ClockStop)=="number" then
|
||||
Tstop=Tnow+ClockStop
|
||||
elseif ClockStop and type(ClockStop)=="string" then
|
||||
Tstop=UTILS.ClockToSeconds(ClockStop)
|
||||
end
|
||||
|
||||
self.Tstart=Tstart
|
||||
self.Tstop=Tstop
|
||||
|
||||
if Tstop then
|
||||
self.duration=self.Tstop-self.Tstart
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get currrently active phase.
|
||||
-- @param #OPERATION self
|
||||
-- @return #OPERATION.Phase Current phase or `nil` if no current phase is active.
|
||||
function OPERATION:GetPhaseActive()
|
||||
return self.phase
|
||||
end
|
||||
|
||||
--- Get next phase.
|
||||
-- @param #OPERATION self
|
||||
-- @return #OPERATION.Phase Next phase or `nil` if no next phase exists.
|
||||
function OPERATION:GetPhaseNext()
|
||||
|
||||
for _,_phase in pairs(self.phases or {}) do
|
||||
local phase=_phase --#OPERATION.Phase
|
||||
|
||||
if not phase.isOver then
|
||||
-- Return first phase that is not over.
|
||||
return phase
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Count phases.
|
||||
-- @param #OPERATION self
|
||||
-- @return #number Number of phases
|
||||
function OPERATION:CountPhases()
|
||||
|
||||
local N=0
|
||||
for phasename, phase in pairs(self.phases) do
|
||||
N=N+1
|
||||
end
|
||||
|
||||
return N
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Status Update
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- On after "Start" event.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function OPERATION:onafterStart(From, Event, To)
|
||||
|
||||
-- Get
|
||||
local Phase=self:GetPhaseNext()
|
||||
|
||||
if Phase then
|
||||
self:PhaseChange(Phase)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On after "StatusUpdate" event.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function OPERATION:onafterStatusUpdate(From, Event, To)
|
||||
|
||||
-- Current abs. mission time.
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
-- Current FSM state.
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
-- Current phase.
|
||||
local currphase=self:GetPhaseActive()
|
||||
local phasename=currphase and currphase.name or "None"
|
||||
local Nphase=self:CountPhases()
|
||||
|
||||
-- General info.
|
||||
local text=string.format("State=%s: Phase=%s, Phases=%d", fsmstate, phasename, Nphase)
|
||||
self:I(self.lid..text)
|
||||
|
||||
-- Info on phases.
|
||||
local text="Phases:"
|
||||
for i,_phase in pairs(self.phases) do
|
||||
local phase=_phase --#OPERATION.Phase
|
||||
text=text..string.format("\n[%d] %s", i, phase.name)
|
||||
end
|
||||
if text=="Phases:" then text=text.." None" end
|
||||
|
||||
-- Next status update.
|
||||
self:__StatusUpdate(-30)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- FSM Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- On after "ChangePhase" event.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #OPERATION.Phase Phase The new phase.
|
||||
function OPERATION:onafterChangePhase(From, Event, To, Phase)
|
||||
|
||||
-- Debug message.
|
||||
self:T(self.lid..string.format("Changed to phase: %s", Phase.name))
|
||||
|
||||
-- Set currently active phase.
|
||||
self.phase=Phase
|
||||
|
||||
end
|
||||
|
||||
--- On after "Over" event.
|
||||
-- @param #OPERATION self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #OPERATION.Phase Phase The new phase.
|
||||
function OPERATION:onafterOver(From, Event, To)
|
||||
|
||||
-- Debug message.
|
||||
self:T(self.lid..string.format("Operation is over!"))
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Misc Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Check phases.
|
||||
-- @param #OPERATION self
|
||||
function OPERATION:_CheckPhases()
|
||||
|
||||
-- Currently active phase.
|
||||
local phase=self:GetPhaseActive()
|
||||
|
||||
-- Check if active phase is over.
|
||||
if phase then
|
||||
phase.isOver=phase.conditionOver:Evaluate()
|
||||
end
|
||||
|
||||
-- If no current phase or current phase is over, get next phase.
|
||||
if phase==nil or (phase and phase.isOver) then
|
||||
|
||||
-- Get next phase.
|
||||
local Phase=self:GetPhaseNext()
|
||||
|
||||
if Phase then
|
||||
|
||||
-- Change phase to next one.
|
||||
self:PhaseChange(Phase)
|
||||
|
||||
else
|
||||
|
||||
-- No further phases defined ==> Operation is over.
|
||||
self:Over()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -11607,7 +11607,7 @@ function OPSGROUP:SetDefaultCallsign(CallsignName, CallsignNumber)
|
||||
self.callsignDefault.NumberGroup=CallsignNumber or 1
|
||||
self.callsignDefault.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad)
|
||||
|
||||
self:I(self.lid..string.format("Default callsign=%s", self.callsignDefault.NameSquad))
|
||||
--self:I(self.lid..string.format("Default callsign=%s", self.callsignDefault.NameSquad))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -30,6 +30,7 @@ Core/Timer.lua
|
||||
Core/Goal.lua
|
||||
Core/Spot.lua
|
||||
Core/TextAndSound.lua
|
||||
Core/Condition.lua
|
||||
|
||||
Wrapper/Object.lua
|
||||
Wrapper/Identifiable.lua
|
||||
@ -96,6 +97,7 @@ Ops/Chief.lua
|
||||
Ops/CSAR.lua
|
||||
Ops/CTLD.lua
|
||||
Ops/Awacs.lua
|
||||
Ops/Operation.lua
|
||||
Ops/FlightControl.lua
|
||||
|
||||
AI/AI_Balancer.lua
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user