Merge pull request #172 from FlightControl-Master/Moose-Release

-- Fixed DETECTION DISPATCHER
-- Need to test this in a group.
-- Revised messages adding @ signs, which represent to who the message
is targetted.
This commit is contained in:
Sven Van de Velde 2016-12-18 11:42:38 +01:00 committed by GitHub
commit 8eaa6a39d9
73 changed files with 407 additions and 515 deletions

View File

@ -144,13 +144,15 @@ do -- ACT_ROUTE
-- @param #string From
-- @param #string To
function ACT_ROUTE:onbeforeRoute( ProcessUnit, Event, From, To )
self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } )
if ProcessUnit:IsAlive() then
self:F( "BeforeRoute 2" )
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then
self:T( { HasArrived = HasArrived } )
if not HasArrived then
self:__Report( 1 )
self:Report()
end
self.DisplayCount = 1
else
@ -208,6 +210,7 @@ do -- ACT_ROUTE_ZONE
function ACT_ROUTE_ZONE:Init( FsmRoute )
self.TargetZone = FsmRoute.TargetZone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true

View File

@ -550,6 +550,8 @@ do -- FSM_PROCESS
return self:GetTask():GetMission():GetCommandCenter()
end
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
--- Send a message of the @{Task} to the Group of the Unit.
-- @param #FSM_PROCESS self
function FSM_PROCESS:Message( Message )
@ -558,6 +560,12 @@ function FSM_PROCESS:Message( Message )
local CC = self:GetCommandCenter()
local TaskGroup = self.Controllable:GetGroup()
local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets.
local Callsign = self.Controllable:GetCallsign()
local Prefix = Callsign and " @ " .. Callsign .. PlayerName or ""
Message = Prefix .. ": " .. Message
CC:MessageToGroup( Message, TaskGroup )
end

View File

@ -1475,6 +1475,25 @@ function SET_UNIT:GetUnitThreatLevels()
return UnitThreatLevels
end
--- Calculate the maxium A2G threat level of the SET_UNIT.
-- @param #SET_UNIT self
function SET_UNIT:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetSet() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end
--- Returns if the @{Set} has targets having a radar (of a given type).
-- @param #SET_UNIT self
-- @param Dcs.DCSWrapper.Unit#Unit.RadarType RadarType

View File

@ -64,7 +64,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName()
self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition()
self.Missions = setmetatable( {}, { __mode = "v" } )
self.Missions = {}
self:EventOnBirth(
--- @param #COMMANDCENTER self
@ -142,7 +142,7 @@ end
-- @return #string
function COMMANDCENTER:GetName()
return self.HQName
return self.CommandCenterName
end
--- Gets the POSITIONABLE of the HQ command center.
@ -188,7 +188,12 @@ end
function COMMANDCENTER:SetMenu()
self:F()
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "HQ" )
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" )
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu()
end
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
@ -218,9 +223,14 @@ end
--- Send a CC message to a GROUP.
-- @param #COMMANDCENTER self
function COMMANDCENTER:MessageToGroup( Message, TaskGroup )
-- @param #string Message
-- @param Wrapper.Group#GROUP TaskGroup
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name )
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup )
local Prefix = Name and "@ Group (" .. Name .. "): " or ''
Message = Prefix .. Message
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() )
end

View File

@ -382,7 +382,6 @@ do -- DETECTION_DISPATCHER
if Task then
if Task:IsStatePlanned() and DetectedArea.Changed == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTaskMenu( Task )
Task = Mission:RemoveTask( Task )
end
end
@ -486,7 +485,7 @@ do -- DETECTION_DISPATCHER
end
-- TODO set menus using the HQ coordinator
Mission:SetMenu()
Mission:GetCommandCenter():SetMenu()
if #AreaMsg > 0 then
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do

View File

@ -200,7 +200,6 @@ end
--- Sets the Planned Task menu.
-- @param #MISSION self
-- @param Core.Menu#MENU_COALITION CommandCenterMenu
function MISSION:SetMenu()
self:F()
@ -210,6 +209,17 @@ function MISSION:SetMenu()
end
end
--- Removes the Planned Task menu.
-- @param #MISSION self
function MISSION:RemoveMenu()
self:F()
for _, Task in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK
Task:RemoveMenu()
end
end
--- Gets the COMMANDCENTER.
-- @param #MISSION self

View File

@ -439,7 +439,8 @@ function TASK:MessageToGroups( Message )
local CC = Mission:GetCommandCenter()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
CC:MessageToGroup( Message, TaskGroup )
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() )
end
end
@ -527,7 +528,6 @@ function TASK:SetMenu()
self.SetGroup:Flush()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
self:RemoveMenuForGroup( TaskGroup )
if self:IsStatePlanned() or self:IsStateReplanned() then
self:SetMenuForGroup( TaskGroup )
end
@ -873,7 +873,7 @@ function TASK:onenterAssigned( Event, From, To )
self:E("Task Assigned")
self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned!" )
self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." )
self:GetMission():__Start()
end
@ -992,10 +992,12 @@ function TASK:ReportDetails()
PlayerNames[#PlayerNames+1] = PlayerName
end
end
PlayerNameText = table.concat( PlayerNames, ", " )
local PlayerNameText = table.concat( PlayerNames, ", " )
Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText )
end
-- Loop each Process in the Task, and find Reporting Details.
return Report:Text()
end

View File

@ -1,84 +0,0 @@
--- @module Task_Client_Menu
--- TASK2_MENU_CLIENT class
-- @type TASK2_MENU_CLIENT
-- @field Wrapper.Unit#UNIT TaskUnit
-- @field Core.Set#SET_UNIT TargetSet
-- @field Core.Menu#MENU_CLIENT_COMMAND MenuTask
-- @extends Task2#TASK2
TASK2_MENU_CLIENT = {
ClassName = "TASK2_MENU_CLIENT",
TargetSet = nil,
}
--- Creates a new MENU handling machine.
-- @param #TASK2_MENU_CLIENT self
-- @param Tasking.Mission#MISSION Mission
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string MenuText The text of the menu item.
-- @return #TASK2_MENU_CLIENT self
function TASK2_MENU_CLIENT:New( Mission, TaskUnit, MenuText )
-- Inherits from BASE
local self = BASE:Inherit( self, TASK2:New( Mission, TaskUnit ) ) -- #TASK2_MENU_CLIENT
self.MenuText = MenuText
self.Fsm = FSM_TASK:New( self, {
initial = 'Unassigned',
events = {
{ name = 'Menu', from = 'Unassigned', to = 'AwaitingMenu' },
{ name = 'Assign', from = 'AwaitingMenu', to = 'Assigned' },
},
callbacks = {
onMenu = self.OnMenu,
onAssign = self.OnAssign,
},
endstates = {
'Assigned'
},
} )
return self
end
--- Task Events
--- StateMachine callback function for a TASK2
-- @param #TASK2_MENU_CLIENT self
-- @param Core.Fsm#FSM_TASK Fsm
-- @param #string Event
-- @param #string From
-- @param #string To
function TASK2_MENU_CLIENT:OnMenu( Fsm, Event, From, To )
self:E( { Event, From, To, self.TaskUnit.UnitName} )
self.TaskUnit:Message( "Press F10 for task menu", 15 )
self.Menu = MENU_CLIENT:New( self.TaskUnit, self.Mission:GetName(), nil )
self.MenuTask = MENU_CLIENT_COMMAND:New( self.TaskUnit, self.MenuText, self.Menu, self.MenuAssign, self )
end
--- Menu function.
-- @param #TASK2_MENU_CLIENT self
function TASK2_MENU_CLIENT:MenuAssign()
self:E( )
self.TaskUnit:Message( "Menu Assign", 15 )
self:NextEvent( self.Fsm.Assign )
end
--- StateMachine callback function for a TASK2
-- @param #TASK2_MENU_CLIENT self
-- @param Core.Fsm#FSM_TASK Fsm
-- @param #string Event
-- @param #string From
-- @param #string To
function TASK2_MENU_CLIENT:OnAssign( Fsm, Event, From, To )
self:E( { Event, From, To, self.TaskUnit.UnitName} )
self.TaskUnit:Message( "Assign Task", 15 )
self.MenuTask:Remove()
end

View File

@ -45,19 +45,19 @@ do -- TASK_A2G
self.TargetZone = TargetZone
self.FACUnit = FACUnit
local Fsm = self:GetUnitProcess()
local A2GUnitProcess = self:GetUnitProcess()
Fsm:AddProcess( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddAction ( "Rejected", "Eject", "Planned" )
Fsm:AddAction ( "Arrived", "Update", "Updated" )
Fsm:AddProcess( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
Fsm:AddProcess( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" )
A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" )
A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
--Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) )
Fsm:AddAction ( "Accounted", "Success", "Success" )
Fsm:AddAction ( "Failed", "Fail", "Failed" )
A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" )
A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
function A2GUnitProcess:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()

View File

@ -1,283 +0,0 @@
--- This module contains the AI_BALANCER class.
--
-- ===
--
-- 1) @{AI.AI_Balancer#AI_BALANCER} class, extends @{Core.Base#BASE}
-- =======================================================
-- The @{AI.AI_Balancer#AI_BALANCER} class controls the dynamic spawning of AI GROUPS depending on a SET_CLIENT.
-- There will be as many AI GROUPS spawned as there at CLIENTS in SET_CLIENT not spawned.
-- The AI_Balancer uses the @{PatrolCore.Zone#AI_PATROLZONE} class to make AI patrol an zone until the fuel treshold is reached.
--
-- 1.1) AI_BALANCER construction method:
-- ------------------------------------
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New} method:
--
-- * @{#AI_BALANCER.New}: Creates a new AI_BALANCER object.
--
-- 1.2) AI_BALANCER returns AI to Airbases:
-- ---------------------------------------
-- You can configure to have the AI to return to:
--
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the home @{Wrapper.Airbase#AIRBASE}.
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}.
--
-- 1.3) AI_BALANCER allows AI to patrol specific zones:
-- ---------------------------------------------------
-- Use @{AI.AI_Balancer#AI_BALANCER.SetPatrolZone}() to specify a zone where the AI needs to patrol.
--
-- ===
--
-- **API CHANGE HISTORY**
-- ======================
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2016-08-17: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval )
--
-- * Want to ensure that the methods starting with **Init** are the first called methods before any _Spawn_ method is called!
-- * This notation makes it now more clear which methods are initialization methods and which methods are Spawn enablement methods.
--
-- ===
--
-- AUTHORS and CONTRIBUTIONS
-- =========================
--
-- ### Contributions:
--
-- * **Dutch_Baron (James)**: Who you can search on the Eagle Dynamics Forums.
-- Working together with James has resulted in the creation of the AI_BALANCER class.
-- James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
--
-- * **SNAFU**:
-- Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE.
-- None of the script code has been used however within the new AI_BALANCER moose class.
--
-- ### Authors:
--
-- * FlightControl: Framework Design & Programming
--
-- @module AI_Balancer
--- AI_BALANCER class
-- @type AI_BALANCER
-- @field Core.Set#SET_CLIENT SetClient
-- @field Functional.Spawn#SPAWN SpawnAI
-- @field #boolean ToNearestAirbase
-- @field Core.Set#SET_AIRBASE ReturnAirbaseSet
-- @field Dcs.DCSTypes#Distance ReturnTresholdRange
-- @field #boolean ToHomeAirbase
-- @field PatrolCore.Zone#AI_PATROLZONE PatrolZone
-- @extends Core.Base#BASE
AI_BALANCER = {
ClassName = "AI_BALANCER",
PatrolZones = {},
AIGroups = {},
}
--- Creates a new AI_BALANCER object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
-- @param #AI_BALANCER self
-- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
-- @param SpawnAI A SPAWN object that will spawn the AI units required, balancing the SetClient.
-- @return #AI_BALANCER self
function AI_BALANCER:New( SetClient, SpawnAI )
-- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() )
self.SetClient = SetClient
if type( SpawnAI ) == "table" then
if SpawnAI.ClassName and SpawnAI.ClassName == "SPAWN" then
self.SpawnAI = { SpawnAI }
else
local SpawnObjects = true
for SpawnObjectID, SpawnObject in pairs( SpawnAI ) do
if SpawnObject.ClassName and SpawnObject.ClassName == "SPAWN" then
self:E( SpawnObject.ClassName )
else
self:E( "other object" )
SpawnObjects = false
end
end
if SpawnObjects == true then
self.SpawnAI = SpawnAI
else
error( "No SPAWN object given in parameter SpawnAI, either as a single object or as a table of objects!" )
end
end
end
self.ToNearestAirbase = false
self.ReturnHomeAirbase = false
self.AIMonitorSchedule = SCHEDULER:New( self, self._ClientAliveMonitorScheduler, {}, 1, 10, 0 )
return self
end
--- Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}.
-- @param #AI_BALANCER self
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}.
-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Core.Set#SET_AIRBASE}s to evaluate where to return to.
function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet )
self.ToNearestAirbase = true
self.ReturnTresholdRange = ReturnTresholdRange
self.ReturnAirbaseSet = ReturnAirbaseSet
end
--- Returns the AI to the home @{Wrapper.Airbase#AIRBASE}.
-- @param #AI_BALANCER self
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}.
function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange )
self.ToHomeAirbase = true
self.ReturnTresholdRange = ReturnTresholdRange
end
--- Let the AI patrol a @{Zone} with a given Speed range and Altitude range.
-- @param #AI_BALANCER self
-- @param PatrolCore.Zone#AI_PATROLZONE PatrolZone The @{PatrolZone} where the AI needs to patrol.
-- @return PatrolCore.Zone#AI_PATROLZONE self
function AI_BALANCER:SetPatrolZone( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
self.PatrolZone = AI_PATROLZONE:New(
self.SpawnAI,
PatrolZone,
PatrolFloorAltitude,
PatrolCeilingAltitude,
PatrolMinSpeed,
PatrolMaxSpeed
)
end
--- Get the @{PatrolZone} object assigned by the @{AI_Balancer} object.
-- @param #AI_BALANCER self
-- @return PatrolCore.Zone#AI_PATROLZONE PatrolZone The @{PatrolZone} where the AI needs to patrol.
function AI_BALANCER:GetPatrolZone()
return self.PatrolZone
end
--- @param #AI_BALANCER self
function AI_BALANCER:_ClientAliveMonitorScheduler()
self.SetClient:ForEachClient(
--- @param Wrapper.Client#CLIENT Client
function( Client )
local ClientAIAliveState = Client:GetState( self, 'AIAlive' )
self:T( ClientAIAliveState )
if Client:IsAlive() then
if ClientAIAliveState == true then
Client:SetState( self, 'AIAlive', false )
local AIGroup = self.AIGroups[Client.UnitName] -- Wrapper.Group#GROUP
-- local PatrolZone = Client:GetState( self, "PatrolZone" )
-- if PatrolZone then
-- PatrolZone = nil
-- Client:ClearState( self, "PatrolZone" )
-- end
if self.ToNearestAirbase == false and self.ToHomeAirbase == false then
AIGroup:Destroy()
else
-- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group.
-- If there is a CLIENT, the AI stays engaged and will not return.
-- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected.
local PlayerInRange = { Value = false }
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange )
self:E( RangeZone )
_DATABASE:ForEachPlayer(
--- @param Wrapper.Unit#UNIT RangeTestUnit
function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange )
self:E( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } )
if RangeTestUnit:IsInZone( RangeZone ) == true then
self:E( "in zone" )
if RangeTestUnit:GetCoalition() ~= AIGroup:GetCoalition() then
self:E( "in range" )
PlayerInRange.Value = true
end
end
end,
--- @param Core.Zone#ZONE_RADIUS RangeZone
-- @param Wrapper.Group#GROUP AIGroup
function( RangeZone, AIGroup, PlayerInRange )
local AIGroupTemplate = AIGroup:GetTemplate()
if PlayerInRange.Value == false then
if self.ToHomeAirbase == true then
local WayPointCount = #AIGroupTemplate.route.points
local SwitchWayPointCommand = AIGroup:CommandSwitchWayPoint( 1, WayPointCount, 1 )
AIGroup:SetCommand( SwitchWayPointCommand )
AIGroup:MessageToRed( "Returning to home base ...", 30 )
else
-- Okay, we need to send this Group back to the nearest base of the Coalition of the AI.
--TODO: i need to rework the POINT_VEC2 thing.
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
self:T( ClosestAirbase.AirbaseName )
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
AIGroupTemplate.route = RTBRoute
AIGroup:Respawn( AIGroupTemplate )
end
end
end
, RangeZone, AIGroup, PlayerInRange
)
end
end
else
if not ClientAIAliveState or ClientAIAliveState == false then
Client:SetState( self, 'AIAlive', true )
-- OK, spawn a new group from the SpawnAI objects provided.
local SpawnAICount = #self.SpawnAI
local SpawnAIIndex = math.random( 1, SpawnAICount )
local AIGroup = self.SpawnAI[SpawnAIIndex]:Spawn()
AIGroup:E( "spawning new AIGroup" )
--TODO: need to rework UnitName thing ...
self.AIGroups[Client.UnitName] = AIGroup
--- Now test if the AIGroup needs to patrol a zone, otherwise let it follow its route...
if self.PatrolZone then
self.PatrolZones[#self.PatrolZones+1] = AI_PATROLZONE:New(
self.PatrolZone.PatrolZone,
self.PatrolZone.PatrolFloorAltitude,
self.PatrolZone.PatrolCeilingAltitude,
self.PatrolZone.PatrolMinSpeed,
self.PatrolZone.PatrolMaxSpeed
)
if self.PatrolZone.PatrolManageFuel == true then
self.PatrolZones[#self.PatrolZones]:ManageFuel( self.PatrolZone.PatrolFuelTresholdPercentage, self.PatrolZone.PatrolOutOfFuelOrbitTime )
end
self.PatrolZones[#self.PatrolZones]:SetGroup( AIGroup )
--self.PatrolZones[#self.PatrolZones+1] = PatrolZone
--Client:SetState( self, "PatrolZone", PatrolZone )
end
end
end
end
)
return true
end

View File

@ -49,12 +49,12 @@ do -- TASK_SEAD
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddAction ( "Rejected", "Eject", "Planned" )
Fsm:AddAction ( "Arrived", "Update", "Updated" )
Fsm:AddTransition( "Rejected", "Eject", "Planned" )
Fsm:AddTransition( "Arrived", "Update", "Updated" )
Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } )
Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
Fsm:AddAction ( "Accounted", "Success", "Success" )
Fsm:AddAction ( "Failed", "Fail", "Failed" )
Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
self:E( { self } )

View File

@ -865,4 +865,22 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius )
return nil
end
--- Calculate the maxium A2G threat level of the Group.
-- @param #GROUP self
function GROUP:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetUnits() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end

View File

@ -210,6 +210,13 @@ function IDENTIFIABLE:GetDesc()
return nil
end
--- Gets the CallSign of the IDENTIFIABLE, which is a blank by default.
-- @param #IDENTIFIABLE self
-- @return #string The CallSign of the IDENTIFIABLE.
function IDENTIFIABLE:GetCallsign()
return ''
end

View File

@ -280,12 +280,14 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessage( Message, Duration )
function POSITIONABLE:GetMessage( Message, Duration, Name )
local DCSObject = self:GetDCSObject()
if DCSObject then
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")" )
Name = Name or self:GetTypeName()
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" )
end
return nil
@ -296,12 +298,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToAll( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToAll( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToAll()
self:GetMessage( Message, Duration, Name ):ToAll()
end
return nil
@ -312,12 +315,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToCoalition( MessageCoalition )
self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition )
end
return nil
@ -329,12 +333,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToRed( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToRed( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToRed()
self:GetMessage( Message, Duration, Name ):ToRed()
end
return nil
@ -345,12 +350,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToBlue( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToBlue( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToBlue()
self:GetMessage( Message, Duration, Name ):ToBlue()
end
return nil
@ -362,12 +368,13 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Wrapper.Client#CLIENT Client The client object receiving the message.
function POSITIONABLE:MessageToClient( Message, Duration, Client )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToClient( Client )
self:GetMessage( Message, Duration, Name ):ToClient( Client )
end
return nil
@ -379,13 +386,14 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
self:GetMessage( Message, Duration ):ToGroup( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup )
end
end
@ -397,12 +405,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:Message( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:Message( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToGroup( self )
self:GetMessage( Message, Duration, Name ):ToGroup( self )
end
return nil

View File

@ -1,5 +1,5 @@
env.info( '*** MOOSE STATIC INCLUDE START *** ' )
env.info( 'Moose Generation Timestamp: 20161217_2210' )
env.info( 'Moose Generation Timestamp: 20161218_1138' )
local base = _G
Include = {}
@ -8818,6 +8818,25 @@ function SET_UNIT:GetUnitThreatLevels()
return UnitThreatLevels
end
--- Calculate the maxium A2G threat level of the SET_UNIT.
-- @param #SET_UNIT self
function SET_UNIT:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetSet() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end
--- Returns if the @{Set} has targets having a radar (of a given type).
-- @param #SET_UNIT self
-- @param Dcs.DCSWrapper.Unit#Unit.RadarType RadarType
@ -11141,6 +11160,8 @@ do -- FSM_PROCESS
return self:GetTask():GetMission():GetCommandCenter()
end
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
--- Send a message of the @{Task} to the Group of the Unit.
-- @param #FSM_PROCESS self
function FSM_PROCESS:Message( Message )
@ -11149,6 +11170,12 @@ function FSM_PROCESS:Message( Message )
local CC = self:GetCommandCenter()
local TaskGroup = self.Controllable:GetGroup()
local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets.
local Callsign = self.Controllable:GetCallsign()
local Prefix = Callsign and " @ " .. Callsign .. PlayerName or ""
Message = Prefix .. ": " .. Message
CC:MessageToGroup( Message, TaskGroup )
end
@ -11619,6 +11646,13 @@ function IDENTIFIABLE:GetDesc()
return nil
end
--- Gets the CallSign of the IDENTIFIABLE, which is a blank by default.
-- @param #IDENTIFIABLE self
-- @return #string The CallSign of the IDENTIFIABLE.
function IDENTIFIABLE:GetCallsign()
return ''
end
@ -11909,12 +11943,14 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessage( Message, Duration )
function POSITIONABLE:GetMessage( Message, Duration, Name )
local DCSObject = self:GetDCSObject()
if DCSObject then
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")" )
Name = Name or self:GetTypeName()
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" )
end
return nil
@ -11925,12 +11961,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToAll( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToAll( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToAll()
self:GetMessage( Message, Duration, Name ):ToAll()
end
return nil
@ -11941,12 +11978,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToCoalition( MessageCoalition )
self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition )
end
return nil
@ -11958,12 +11996,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToRed( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToRed( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToRed()
self:GetMessage( Message, Duration, Name ):ToRed()
end
return nil
@ -11974,12 +12013,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToBlue( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToBlue( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToBlue()
self:GetMessage( Message, Duration, Name ):ToBlue()
end
return nil
@ -11991,12 +12031,13 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Wrapper.Client#CLIENT Client The client object receiving the message.
function POSITIONABLE:MessageToClient( Message, Duration, Client )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToClient( Client )
self:GetMessage( Message, Duration, Name ):ToClient( Client )
end
return nil
@ -12008,13 +12049,14 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
self:GetMessage( Message, Duration ):ToGroup( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup )
end
end
@ -12026,12 +12068,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:Message( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:Message( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToGroup( self )
self:GetMessage( Message, Duration, Name ):ToGroup( self )
end
return nil
@ -15114,6 +15157,24 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius )
return nil
end
--- Calculate the maxium A2G threat level of the Group.
-- @param #GROUP self
function GROUP:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetUnits() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end
--- This module contains the UNIT class.
--
@ -25821,13 +25882,15 @@ do -- ACT_ROUTE
-- @param #string From
-- @param #string To
function ACT_ROUTE:onbeforeRoute( ProcessUnit, Event, From, To )
self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } )
if ProcessUnit:IsAlive() then
self:F( "BeforeRoute 2" )
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then
self:T( { HasArrived = HasArrived } )
if not HasArrived then
self:__Report( 1 )
self:Report()
end
self.DisplayCount = 1
else
@ -25885,6 +25948,7 @@ do -- ACT_ROUTE_ZONE
function ACT_ROUTE_ZONE:Init( FsmRoute )
self.TargetZone = FsmRoute.TargetZone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
@ -26461,7 +26525,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName()
self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition()
self.Missions = setmetatable( {}, { __mode = "v" } )
self.Missions = {}
self:EventOnBirth(
--- @param #COMMANDCENTER self
@ -26539,7 +26603,7 @@ end
-- @return #string
function COMMANDCENTER:GetName()
return self.HQName
return self.CommandCenterName
end
--- Gets the POSITIONABLE of the HQ command center.
@ -26585,7 +26649,12 @@ end
function COMMANDCENTER:SetMenu()
self:F()
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "HQ" )
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" )
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu()
end
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
@ -26615,9 +26684,14 @@ end
--- Send a CC message to a GROUP.
-- @param #COMMANDCENTER self
function COMMANDCENTER:MessageToGroup( Message, TaskGroup )
-- @param #string Message
-- @param Wrapper.Group#GROUP TaskGroup
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name )
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup )
local Prefix = Name and "@ Group (" .. Name .. "): " or ''
Message = Prefix .. Message
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() )
end
@ -26865,7 +26939,6 @@ end
--- Sets the Planned Task menu.
-- @param #MISSION self
-- @param Core.Menu#MENU_COALITION CommandCenterMenu
function MISSION:SetMenu()
self:F()
@ -26875,6 +26948,17 @@ function MISSION:SetMenu()
end
end
--- Removes the Planned Task menu.
-- @param #MISSION self
function MISSION:RemoveMenu()
self:F()
for _, Task in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK
Task:RemoveMenu()
end
end
--- Gets the COMMANDCENTER.
-- @param #MISSION self
@ -28048,7 +28132,8 @@ function TASK:MessageToGroups( Message )
local CC = Mission:GetCommandCenter()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
CC:MessageToGroup( Message, TaskGroup )
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() )
end
end
@ -28136,7 +28221,6 @@ function TASK:SetMenu()
self.SetGroup:Flush()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
self:RemoveMenuForGroup( TaskGroup )
if self:IsStatePlanned() or self:IsStateReplanned() then
self:SetMenuForGroup( TaskGroup )
end
@ -28482,7 +28566,7 @@ function TASK:onenterAssigned( Event, From, To )
self:E("Task Assigned")
self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned!" )
self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." )
self:GetMission():__Start()
end
@ -28601,10 +28685,12 @@ function TASK:ReportDetails()
PlayerNames[#PlayerNames+1] = PlayerName
end
end
PlayerNameText = table.concat( PlayerNames, ", " )
local PlayerNameText = table.concat( PlayerNames, ", " )
Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText )
end
-- Loop each Process in the Task, and find Reporting Details.
return Report:Text()
end
@ -28994,7 +29080,6 @@ do -- DETECTION_DISPATCHER
if Task then
if Task:IsStatePlanned() and DetectedArea.Changed == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTaskMenu( Task )
Task = Mission:RemoveTask( Task )
end
end
@ -29098,7 +29183,7 @@ do -- DETECTION_DISPATCHER
end
-- TODO set menus using the HQ coordinator
Mission:SetMenu()
Mission:GetCommandCenter():SetMenu()
if #AreaMsg > 0 then
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
@ -29169,12 +29254,12 @@ do -- TASK_SEAD
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddAction ( "Rejected", "Eject", "Planned" )
Fsm:AddAction ( "Arrived", "Update", "Updated" )
Fsm:AddTransition( "Rejected", "Eject", "Planned" )
Fsm:AddTransition( "Arrived", "Update", "Updated" )
Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } )
Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
Fsm:AddAction ( "Accounted", "Success", "Success" )
Fsm:AddAction ( "Failed", "Fail", "Failed" )
Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
self:E( { self } )
@ -29243,19 +29328,19 @@ do -- TASK_A2G
self.TargetZone = TargetZone
self.FACUnit = FACUnit
local Fsm = self:GetUnitProcess()
local A2GUnitProcess = self:GetUnitProcess()
Fsm:AddProcess( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddAction ( "Rejected", "Eject", "Planned" )
Fsm:AddAction ( "Arrived", "Update", "Updated" )
Fsm:AddProcess( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
Fsm:AddProcess( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" )
A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" )
A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
--Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) )
Fsm:AddAction ( "Accounted", "Success", "Success" )
Fsm:AddAction ( "Failed", "Fail", "Failed" )
A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" )
A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
function A2GUnitProcess:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()

View File

@ -1,5 +1,5 @@
env.info( '*** MOOSE STATIC INCLUDE START *** ' )
env.info( 'Moose Generation Timestamp: 20161217_2210' )
env.info( 'Moose Generation Timestamp: 20161218_1138' )
local base = _G
Include = {}
@ -8818,6 +8818,25 @@ function SET_UNIT:GetUnitThreatLevels()
return UnitThreatLevels
end
--- Calculate the maxium A2G threat level of the SET_UNIT.
-- @param #SET_UNIT self
function SET_UNIT:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetSet() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end
--- Returns if the @{Set} has targets having a radar (of a given type).
-- @param #SET_UNIT self
-- @param Dcs.DCSWrapper.Unit#Unit.RadarType RadarType
@ -11141,6 +11160,8 @@ do -- FSM_PROCESS
return self:GetTask():GetMission():GetCommandCenter()
end
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
--- Send a message of the @{Task} to the Group of the Unit.
-- @param #FSM_PROCESS self
function FSM_PROCESS:Message( Message )
@ -11149,6 +11170,12 @@ function FSM_PROCESS:Message( Message )
local CC = self:GetCommandCenter()
local TaskGroup = self.Controllable:GetGroup()
local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets.
local Callsign = self.Controllable:GetCallsign()
local Prefix = Callsign and " @ " .. Callsign .. PlayerName or ""
Message = Prefix .. ": " .. Message
CC:MessageToGroup( Message, TaskGroup )
end
@ -11619,6 +11646,13 @@ function IDENTIFIABLE:GetDesc()
return nil
end
--- Gets the CallSign of the IDENTIFIABLE, which is a blank by default.
-- @param #IDENTIFIABLE self
-- @return #string The CallSign of the IDENTIFIABLE.
function IDENTIFIABLE:GetCallsign()
return ''
end
@ -11909,12 +11943,14 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessage( Message, Duration )
function POSITIONABLE:GetMessage( Message, Duration, Name )
local DCSObject = self:GetDCSObject()
if DCSObject then
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")" )
Name = Name or self:GetTypeName()
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" )
end
return nil
@ -11925,12 +11961,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToAll( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToAll( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToAll()
self:GetMessage( Message, Duration, Name ):ToAll()
end
return nil
@ -11941,12 +11978,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToCoalition( MessageCoalition )
self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition )
end
return nil
@ -11958,12 +11996,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToRed( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToRed( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToRed()
self:GetMessage( Message, Duration, Name ):ToRed()
end
return nil
@ -11974,12 +12013,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:MessageToBlue( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToBlue( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToBlue()
self:GetMessage( Message, Duration, Name ):ToBlue()
end
return nil
@ -11991,12 +12031,13 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Wrapper.Client#CLIENT Client The client object receiving the message.
function POSITIONABLE:MessageToClient( Message, Duration, Client )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToClient( Client )
self:GetMessage( Message, Duration, Name ):ToClient( Client )
end
return nil
@ -12008,13 +12049,14 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
self:GetMessage( Message, Duration ):ToGroup( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup )
end
end
@ -12026,12 +12068,13 @@ end
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
function POSITIONABLE:Message( Message, Duration )
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:Message( Message, Duration, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessage( Message, Duration ):ToGroup( self )
self:GetMessage( Message, Duration, Name ):ToGroup( self )
end
return nil
@ -15114,6 +15157,24 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius )
return nil
end
--- Calculate the maxium A2G threat level of the Group.
-- @param #GROUP self
function GROUP:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetUnits() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end
--- This module contains the UNIT class.
--
@ -25821,13 +25882,15 @@ do -- ACT_ROUTE
-- @param #string From
-- @param #string To
function ACT_ROUTE:onbeforeRoute( ProcessUnit, Event, From, To )
self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } )
if ProcessUnit:IsAlive() then
self:F( "BeforeRoute 2" )
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then
self:T( { HasArrived = HasArrived } )
if not HasArrived then
self:__Report( 1 )
self:Report()
end
self.DisplayCount = 1
else
@ -25885,6 +25948,7 @@ do -- ACT_ROUTE_ZONE
function ACT_ROUTE_ZONE:Init( FsmRoute )
self.TargetZone = FsmRoute.TargetZone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
@ -26461,7 +26525,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName()
self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition()
self.Missions = setmetatable( {}, { __mode = "v" } )
self.Missions = {}
self:EventOnBirth(
--- @param #COMMANDCENTER self
@ -26539,7 +26603,7 @@ end
-- @return #string
function COMMANDCENTER:GetName()
return self.HQName
return self.CommandCenterName
end
--- Gets the POSITIONABLE of the HQ command center.
@ -26585,7 +26649,12 @@ end
function COMMANDCENTER:SetMenu()
self:F()
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "HQ" )
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" )
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu()
end
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
@ -26615,9 +26684,14 @@ end
--- Send a CC message to a GROUP.
-- @param #COMMANDCENTER self
function COMMANDCENTER:MessageToGroup( Message, TaskGroup )
-- @param #string Message
-- @param Wrapper.Group#GROUP TaskGroup
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name )
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup )
local Prefix = Name and "@ Group (" .. Name .. "): " or ''
Message = Prefix .. Message
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() )
end
@ -26865,7 +26939,6 @@ end
--- Sets the Planned Task menu.
-- @param #MISSION self
-- @param Core.Menu#MENU_COALITION CommandCenterMenu
function MISSION:SetMenu()
self:F()
@ -26875,6 +26948,17 @@ function MISSION:SetMenu()
end
end
--- Removes the Planned Task menu.
-- @param #MISSION self
function MISSION:RemoveMenu()
self:F()
for _, Task in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK
Task:RemoveMenu()
end
end
--- Gets the COMMANDCENTER.
-- @param #MISSION self
@ -28048,7 +28132,8 @@ function TASK:MessageToGroups( Message )
local CC = Mission:GetCommandCenter()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
CC:MessageToGroup( Message, TaskGroup )
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() )
end
end
@ -28136,7 +28221,6 @@ function TASK:SetMenu()
self.SetGroup:Flush()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
self:RemoveMenuForGroup( TaskGroup )
if self:IsStatePlanned() or self:IsStateReplanned() then
self:SetMenuForGroup( TaskGroup )
end
@ -28482,7 +28566,7 @@ function TASK:onenterAssigned( Event, From, To )
self:E("Task Assigned")
self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned!" )
self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." )
self:GetMission():__Start()
end
@ -28601,10 +28685,12 @@ function TASK:ReportDetails()
PlayerNames[#PlayerNames+1] = PlayerName
end
end
PlayerNameText = table.concat( PlayerNames, ", " )
local PlayerNameText = table.concat( PlayerNames, ", " )
Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText )
end
-- Loop each Process in the Task, and find Reporting Details.
return Report:Text()
end
@ -28994,7 +29080,6 @@ do -- DETECTION_DISPATCHER
if Task then
if Task:IsStatePlanned() and DetectedArea.Changed == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTaskMenu( Task )
Task = Mission:RemoveTask( Task )
end
end
@ -29098,7 +29183,7 @@ do -- DETECTION_DISPATCHER
end
-- TODO set menus using the HQ coordinator
Mission:SetMenu()
Mission:GetCommandCenter():SetMenu()
if #AreaMsg > 0 then
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
@ -29169,12 +29254,12 @@ do -- TASK_SEAD
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddAction ( "Rejected", "Eject", "Planned" )
Fsm:AddAction ( "Arrived", "Update", "Updated" )
Fsm:AddTransition( "Rejected", "Eject", "Planned" )
Fsm:AddTransition( "Arrived", "Update", "Updated" )
Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } )
Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
Fsm:AddAction ( "Accounted", "Success", "Success" )
Fsm:AddAction ( "Failed", "Fail", "Failed" )
Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
self:E( { self } )
@ -29243,19 +29328,19 @@ do -- TASK_A2G
self.TargetZone = TargetZone
self.FACUnit = FACUnit
local Fsm = self:GetUnitProcess()
local A2GUnitProcess = self:GetUnitProcess()
Fsm:AddProcess( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddAction ( "Rejected", "Eject", "Planned" )
Fsm:AddAction ( "Arrived", "Update", "Updated" )
Fsm:AddProcess( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
Fsm:AddProcess( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" )
A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" )
A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
--Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) )
Fsm:AddAction ( "Accounted", "Success", "Success" )
Fsm:AddAction ( "Failed", "Fail", "Failed" )
A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" )
A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
function A2GUnitProcess:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()

View File

@ -1,7 +1,7 @@
local HQ = GROUP:FindByName( "HQ", "Bravo HQ" )
local CommandCenter = COMMANDCENTER:New( HQ )
local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
local Scoring = SCORING:New( "Detect Demo" )

View File

@ -110,7 +110,11 @@ local TargetZone = ZONE:New( "Target Zone" )
-- 2. The set of groups of planes that pilots can join.
-- 3. The name of the Task... This can be any name, and will be provided when the Pilot joins the task.
-- 4. A type of the Task. When Tasks are in state Planned, then a menu can be provided that group the task based on this given type.
local SEADTask = TASK:New( Mission, SEADSet, "SEAD Radars Vector 1", "SEAD" ) -- Tasking.Task#TASK
local SEADTask = TASK:New(
Mission,
SEADSet,
"SEAD Radars Vector 1",
"SEAD" ) -- Tasking.Task#TASK
-- This is now an important part of the Task process definition.
-- Each TASK contains a "Process Template".