Merge pull request #315 from FlightControl-Master/FlightControl

New tasking system
This commit is contained in:
Sven Van de Velde 2017-03-17 21:53:41 +01:00 committed by GitHub
commit 46e8f30c4a
268 changed files with 20621 additions and 12261 deletions

1
.gitignore vendored
View File

@ -222,3 +222,4 @@ _gsdata_/
#GITHUB #GITHUB
.gitattributes .gitattributes
.gitignore .gitignore
Moose Test Missions/MOOSE_Test_Template.miz

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType"> <launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc:}/Moose Mission Setup/Moose Mission Update\Moose_Update_Missions.bat"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc:}/Moose Mission Setup/Moose Mission Update\Moose_Update_Missions.bat"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;${project_loc:}&quot;/Moose Test Missions"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;${project_loc:}/Moose Test Missions&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc:}/Moose Mission Setup/Moose Mission Update"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc:}/Moose Mission Setup/Moose Mission Update"/>
</launchConfiguration> </launchConfiguration>

Binary file not shown.

View File

@ -161,11 +161,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @return #boolean Return false to cancel Transition. -- @return #boolean Return false to cancel Transition.
@ -176,11 +171,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- Synchronous Event Trigger for Event Engage. --- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAS_ZONE] Engage -- @function [parent=#AI_CAS_ZONE] Engage
@ -408,7 +398,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To )
if Detected == true then if Detected == true then
self:E( {"Target: ", DetectedUnit } ) self:E( {"Target: ", DetectedUnit } )
self.DetectedUnits[DetectedUnit] = false self.DetectedUnits[DetectedUnit] = false
local AttackTask = Controllable:EnRouteTaskEngageUnit( DetectedUnit, 1, true, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil )
self.Controllable:PushTask( AttackTask, 1 ) self.Controllable:PushTask( AttackTask, 1 )
end end
end end
@ -439,8 +429,8 @@ end
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
@ -480,28 +470,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
EngageRoute[#EngageRoute+1] = CurrentRoutePoint EngageRoute[#EngageRoute+1] = CurrentRoutePoint
if self.Controllable:IsNotInZone( self.EngageZone ) then -- if self.Controllable:IsNotInZone( self.EngageZone ) then
--
-- Find a random 2D point in EngageZone. -- -- Find a random 2D point in EngageZone.
local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() -- local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2()
self:T2( ToEngageZoneVec2 ) -- self:T2( ToEngageZoneVec2 )
--
-- Obtain a 3D @{Point} from the 2D point + altitude. -- -- Obtain a 3D @{Point} from the 2D point + altitude.
local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) -- local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y )
--
-- Create a route point of type air. -- -- Create a route point of type air.
local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( -- local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir(
self.PatrolAltType, -- self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint, -- POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint, -- POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed, -- self.EngageSpeed,
true -- true
) -- )
--
EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint -- EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint
--
end -- end
--
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone. --- Find a random 2D point in EngageZone.
@ -556,9 +546,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
--- NOW ROUTE THE GROUP! --- NOW ROUTE THE GROUP!
self.Controllable:WayPointExecute( 1 ) self.Controllable:WayPointExecute( 1 )
self:SetDetectionInterval( 10 ) self:SetDetectionInterval( 2 )
self:SetDetectionActivated() self:SetDetectionActivated()
self:__Target( -10 ) -- Start Targetting self:__Target( -2 ) -- Start Targetting
end end
end end

View File

@ -192,15 +192,6 @@ do -- ACT_ACCOUNT_DEADS
self.TaskName = FsmAccount.TaskName self.TaskName = FsmAccount.TaskName
end end
function ACT_ACCOUNT_DEADS:_Destructor()
self:E("_Destructor")
self:EventRemoveAll()
end
--- Process Events --- Process Events
--- StateMachine callback function --- StateMachine callback function
@ -231,7 +222,6 @@ do -- ACT_ACCOUNT_DEADS
if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
local TaskGroup = ProcessUnit:GetGroup() local TaskGroup = ProcessUnit:GetGroup()
self.TargetSetUnit:RemoveUnitsByName( EventData.IniUnitName )
self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
end end
end end
@ -244,7 +234,7 @@ do -- ACT_ACCOUNT_DEADS
-- @param #string To -- @param #string To
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData ) function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData )
if self.TargetSetUnit:Count() > 0 then if self.TargetSetUnit:Count() > 1 then
self:__More( 1 ) self:__More( 1 )
else else
self:__NoMore( 1 ) self:__NoMore( 1 )
@ -259,7 +249,7 @@ do -- ACT_ACCOUNT_DEADS
self:T( { "EventDead", EventData } ) self:T( { "EventDead", EventData } )
if EventData.IniDCSUnit then if EventData.IniDCSUnit then
self:__Event( 1, EventData ) self:Event( EventData )
end end
end end

View File

@ -175,7 +175,7 @@ do -- ACT_ASSIGN_ACCEPT
self:Message( "You are assigned to the task " .. self.Task:GetName() ) self:Message( "You are assigned to the task " .. self.Task:GetName() )
self.Task:Assign() self.Task:Assign( ProcessUnit, self.Task )
end end
end -- ACT_ASSIGN_ACCEPT end -- ACT_ASSIGN_ACCEPT

View File

@ -108,7 +108,7 @@ do -- ACT_ASSIST
function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To )
local ProcessGroup = ProcessUnit:GetGroup() local ProcessGroup = ProcessUnit:GetGroup()
local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
local function MenuSmoke( MenuParam ) local function MenuSmoke( MenuParam )
self:E( MenuParam ) self:E( MenuParam )
@ -125,6 +125,17 @@ do -- ACT_ASSIST
self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } ) self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } )
self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } )
end end
--- StateMachine callback function
-- @param #ACT_ASSIST self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
self.Menu:Remove() -- When stopped, remove the menus
end
end end

View File

@ -56,8 +56,7 @@ function PROCESS_JTAC:New( Task, ProcessUnit, TargetSetUnit, FACUnit )
endstates = { 'Failed' } endstates = { 'Failed' }
} ) } )
self:HandleEvent( EVENTS.Dead, self.EventDead )
_EVENTDISPATCHER:OnDead( self.EventDead, self )
return self return self
end end

View File

@ -81,7 +81,7 @@ do -- ACT_ROUTE
-- @type ACT_ROUTE -- @type ACT_ROUTE
-- @field Tasking.Task#TASK TASK -- @field Tasking.Task#TASK TASK
-- @field Wrapper.Unit#UNIT ProcessUnit -- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone -- @field Core.Zone#ZONE_BASE Zone
-- @extends Core.Fsm#FSM_PROCESS -- @extends Core.Fsm#FSM_PROCESS
ACT_ROUTE = { ACT_ROUTE = {
ClassName = "ACT_ROUTE", ClassName = "ACT_ROUTE",
@ -177,6 +177,115 @@ do -- ACT_ROUTE
end -- ACT_ROUTE end -- ACT_ROUTE
do -- ACT_ROUTE_POINT
--- ACT_ROUTE_POINT class
-- @type ACT_ROUTE_POINT
-- @field Tasking.Task#TASK TASK
-- @extends #ACT_ROUTE
ACT_ROUTE_POINT = {
ClassName = "ACT_ROUTE_POINT",
}
--- Creates a new routing state machine.
-- The task will route a controllable to a PointVec2 until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#POINT_VEC2 The PointVec2 to Target.
-- @param #number Range The Distance to Target.
-- @param Core.Zone#ZONE_BASE Zone
function ACT_ROUTE_POINT:New( PointVec2, Range )
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT
self.PointVec2 = PointVec2
self.Range = Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
return self
end
function ACT_ROUTE_POINT:Init( FsmRoute )
self.PointVec2 = FsmRoute.PointVec2
self.Range = FsmRoute.Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
end
--- Set PointVec2
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to.
function ACT_ROUTE_POINT:SetPointVec2( PointVec2 )
self:F2( { PointVec2 } )
self.PointVec2 = PointVec2
end
--- Get PointVec2
-- @param #ACT_ROUTE_POINT self
-- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to.
function ACT_ROUTE_POINT:GetPointVec2()
self:F2( { self.PointVec2 } )
return self.PointVec2
end
--- Set Range around PointVec2
-- @param #ACT_ROUTE_POINT self
-- @param #number Range The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:SetRange( Range )
self:F2( { self.Range } )
self.Range = Range or 10000
end
--- Get Range around PointVec2
-- @param #ACT_ROUTE_POINT self
-- @return #number The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:GetRange()
return self.Range
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @return #boolean
function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsAlive() then
local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() )
if Distance <= self.Range then
local RouteText = "You have arrived."
self:Message( RouteText )
return true
end
end
return false
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To )
local TaskUnitPointVec2 = ProcessUnit:GetPointVec2()
local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km."
self:Message( RouteText )
end
end -- ACT_ROUTE_POINT
do -- ACT_ROUTE_ZONE do -- ACT_ROUTE_ZONE
@ -184,7 +293,7 @@ do -- ACT_ROUTE_ZONE
-- @type ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE
-- @field Tasking.Task#TASK TASK -- @field Tasking.Task#TASK TASK
-- @field Wrapper.Unit#UNIT ProcessUnit -- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone -- @field Core.Zone#ZONE_BASE Zone
-- @extends #ACT_ROUTE -- @extends #ACT_ROUTE
ACT_ROUTE_ZONE = { ACT_ROUTE_ZONE = {
ClassName = "ACT_ROUTE_ZONE", ClassName = "ACT_ROUTE_ZONE",
@ -193,11 +302,11 @@ do -- ACT_ROUTE_ZONE
--- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE.
-- @param #ACT_ROUTE_ZONE self -- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE TargetZone -- @param Core.Zone#ZONE_BASE Zone
function ACT_ROUTE_ZONE:New( TargetZone ) function ACT_ROUTE_ZONE:New( Zone )
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
self.TargetZone = TargetZone self.Zone = Zone
self.DisplayInterval = 30 self.DisplayInterval = 30
self.DisplayCount = 30 self.DisplayCount = 30
@ -209,7 +318,7 @@ do -- ACT_ROUTE_ZONE
function ACT_ROUTE_ZONE:Init( FsmRoute ) function ACT_ROUTE_ZONE:Init( FsmRoute )
self.TargetZone = FsmRoute.TargetZone self.Zone = FsmRoute.Zone
self.DisplayInterval = 30 self.DisplayInterval = 30
self.DisplayCount = 30 self.DisplayCount = 30
@ -217,18 +326,32 @@ do -- ACT_ROUTE_ZONE
self.DisplayTime = 10 -- 10 seconds is the default self.DisplayTime = 10 -- 10 seconds is the default
end end
--- Set Zone
-- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
function ACT_ROUTE_ZONE:SetZone( Zone )
self.Zone = Zone
end
--- Get Zone
-- @param #ACT_ROUTE_ZONE self
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
function ACT_ROUTE_ZONE:GetZone()
return self.Zone
end
--- Method override to check if the controllable has arrived. --- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE self -- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @return #boolean -- @return #boolean
function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsInZone( self.TargetZone ) then if ProcessUnit:IsInZone( self.Zone ) then
local RouteText = "You have arrived within the zone." local RouteText = "You have arrived within the zone."
self:Message( RouteText ) self:Message( RouteText )
end end
return ProcessUnit:IsInZone( self.TargetZone ) return ProcessUnit:IsInZone( self.Zone )
end end
--- Task Events --- Task Events
@ -241,11 +364,11 @@ do -- ACT_ROUTE_ZONE
-- @param #string To -- @param #string To
function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To )
local ZoneVec2 = self.TargetZone:GetVec2() local ZoneVec2 = self.Zone:GetVec2()
local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y )
local TaskUnitVec2 = ProcessUnit:GetVec2() local TaskUnitVec2 = ProcessUnit:GetVec2()
local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y )
local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km to target." local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km."
self:Message( RouteText ) self:Message( RouteText )
end end

View File

@ -256,12 +256,14 @@ function BASE:_Destructor()
--self:EventRemoveAll() --self:EventRemoveAll()
end end
-- THIS IS WHY WE NEED LUA 5.2 ...
function BASE:_SetDestructor() function BASE:_SetDestructor()
-- TODO: Okay, this is really technical... -- TODO: Okay, this is really technical...
-- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
-- Therefore, I am parking this logic until I've properly discussed all this with the community. -- Therefore, I am parking this logic until I've properly discussed all this with the community.
--[[
local proxy = newproxy(true) local proxy = newproxy(true)
local proxyMeta = getmetatable(proxy) local proxyMeta = getmetatable(proxy)
@ -276,7 +278,7 @@ function BASE:_SetDestructor()
-- table is about to be garbage-collected - then the __gc hook -- table is about to be garbage-collected - then the __gc hook
-- will be invoked and the destructor called -- will be invoked and the destructor called
rawset( self, '__proxy', proxy ) rawset( self, '__proxy', proxy )
--]]
end end
--- This is the worker method to inherit from a parent class. --- This is the worker method to inherit from a parent class.
@ -292,7 +294,7 @@ function BASE:Inherit( Child, Parent )
setmetatable( Child, Parent ) setmetatable( Child, Parent )
Child.__index = Child Child.__index = Child
Child:_SetDestructor() --Child:_SetDestructor()
end end
--self:T( 'Inherited from ' .. Parent.ClassName ) --self:T( 'Inherited from ' .. Parent.ClassName )
return Child return Child

View File

@ -54,6 +54,8 @@ DATABASE = {
PLAYERSJOINED = {}, PLAYERSJOINED = {},
CLIENTS = {}, CLIENTS = {},
AIRBASES = {}, AIRBASES = {},
COUNTRY_ID = {},
COUNTRY_NAME = {},
NavPoints = {}, NavPoints = {},
} }
@ -761,6 +763,9 @@ function DATABASE:_RegisterTemplates()
local CountryName = string.upper(cntry_data.name) local CountryName = string.upper(cntry_data.name)
local CountryID = cntry_data.id local CountryID = cntry_data.id
self.COUNTRY_ID[CountryName] = CountryID
self.COUNTRY_NAME[CountryID] = CountryName
--self.Units[coa_name][countryName] = {} --self.Units[coa_name][countryName] = {}
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id --self.Units[coa_name][countryName]["countryId"] = cntry_data.id

View File

@ -284,7 +284,7 @@ local _EVENTMETA = {
}, },
[world.event.S_EVENT_TAKEOFF] = { [world.event.S_EVENT_TAKEOFF] = {
Order = 1, Order = 1,
Event = "OnEventTakeOff", Event = "OnEventTakeoff",
Text = "S_EVENT_TAKEOFF" Text = "S_EVENT_TAKEOFF"
}, },
[world.event.S_EVENT_LAND] = { [world.event.S_EVENT_LAND] = {
@ -425,11 +425,11 @@ function EVENT:Init( EventID, EventClass )
-- Each event has a subtable of EventClasses, ordered by EventPriority. -- Each event has a subtable of EventClasses, ordered by EventPriority.
local EventPriority = EventClass:GetEventPriority() local EventPriority = EventClass:GetEventPriority()
if not self.Events[EventID][EventPriority] then if not self.Events[EventID][EventPriority] then
self.Events[EventID][EventPriority] = {} self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } )
end end
if not self.Events[EventID][EventPriority][EventClass] then if not self.Events[EventID][EventPriority][EventClass] then
self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "k" } ) self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } )
end end
return self.Events[EventID][EventPriority][EventClass] return self.Events[EventID][EventPriority][EventClass]
end end
@ -499,11 +499,11 @@ end
-- @param EventClass The instance of the class for which the event is. -- @param EventClass The instance of the class for which the event is.
-- @param #function OnEventFunction -- @param #function OnEventFunction
-- @return #EVENT -- @return #EVENT
function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
for EventUnitID, EventUnit in pairs( EventTemplate.units ) do for EventUnitID, EventUnit in pairs( EventTemplate.units ) do
OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID )
end end
return self return self
end end
@ -517,9 +517,9 @@ end
function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
self:F2( { EventID } ) self:F2( { EventID } )
local Event = self:Init( EventID, EventClass ) local EventData = self:Init( EventID, EventClass )
Event.EventFunction = EventFunction EventData.EventFunction = EventFunction
Event.EventClass = EventClass EventData.EventClass = EventClass
return self return self
end end
@ -535,13 +535,13 @@ end
function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
self:F2( UnitName ) self:F2( UnitName )
local Event = self:Init( EventID, EventClass ) local EventData = self:Init( EventID, EventClass )
if not Event.EventUnit then if not EventData.EventUnit then
Event.EventUnit = {} EventData.EventUnit = {}
end end
Event.EventUnit[UnitName] = {} EventData.EventUnit[UnitName] = {}
Event.EventUnit[UnitName].EventFunction = EventFunction EventData.EventUnit[UnitName].EventFunction = EventFunction
Event.EventUnit[UnitName].EventClass = EventClass EventData.EventUnit[UnitName].EventClass = EventClass
return self return self
end end
@ -576,51 +576,11 @@ do -- OnBirth
function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth )
return self return self
end end
--- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnBirth( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_BIRTH )
return self
end
--- Set a new listener for an S_EVENT_BIRTH event.
-- @param #EVENT self
-- @param #string EventDCSUnitName The id of the unit for the event to be handled.
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_BIRTH )
return self
end
--- Stop listening to S_EVENT_BIRTH event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnBirthRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_BIRTH )
return self
end
end end
do -- OnCrash do -- OnCrash
@ -634,49 +594,10 @@ do -- OnCrash
function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash )
return self return self
end end
--- Set a new listener for an S_EVENT_CRASH event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnCrash( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_CRASH )
return self
end
--- Set a new listener for an S_EVENT_CRASH event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_CRASH )
return self
end
--- Stop listening to S_EVENT_CRASH event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnCrashRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_CRASH )
return self
end
end end
@ -691,96 +612,13 @@ do -- OnDead
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead )
return self return self
end end
--- Set a new listener for an S_EVENT_DEAD event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnDead( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_DEAD )
return self
end
--- Set a new listener for an S_EVENT_DEAD event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_DEAD )
return self
end
--- Stop listening to S_EVENT_DEAD event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnDeadRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_DEAD )
return self
end
end end
do -- OnPilotDead
--- Set a new listener for an S_EVENT_PILOT_DEAD event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnPilotDead( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD )
return self
end
--- Set a new listener for an S_EVENT_PILOT_DEAD event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_PILOT_DEAD )
return self
end
--- Stop listening to S_EVENT_PILOT_DEAD event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnPilotDeadRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_PILOT_DEAD )
return self
end
end
do -- OnLand do -- OnLand
--- Create an OnLand event handler for a group --- Create an OnLand event handler for a group
@ -792,38 +630,11 @@ do -- OnLand
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land )
return self return self
end end
--- Set a new listener for an S_EVENT_LAND event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_LAND )
return self
end
--- Stop listening to S_EVENT_LAND event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnLandRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_LAND )
return self
end
end end
do -- OnTakeOff do -- OnTakeOff
@ -836,38 +647,11 @@ do -- OnTakeOff
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff )
return self return self
end end
--- Set a new listener for an S_EVENT_TAKEOFF event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_TAKEOFF )
return self
end
--- Stop listening to S_EVENT_TAKEOFF event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnTakeOffRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_TAKEOFF )
return self
end
end end
do -- OnEngineShutDown do -- OnEngineShutDown
@ -881,210 +665,11 @@ do -- OnEngineShutDown
function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass )
self:F2( EventTemplate.name ) self:F2( EventTemplate.name )
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown )
return self return self
end end
--- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN )
return self
end
--- Stop listening to S_EVENT_ENGINE_SHUTDOWN event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnEngineShutDownRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_ENGINE_SHUTDOWN )
return self
end
end
do -- OnEngineStartUp
--- Set a new listener for an S_EVENT_ENGINE_STARTUP event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_ENGINE_STARTUP )
return self
end
--- Stop listening to S_EVENT_ENGINE_STARTUP event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnEngineStartUpRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_ENGINE_STARTUP )
return self
end
end
do -- OnShot
--- Set a new listener for an S_EVENT_SHOT event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnShot( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_SHOT )
return self
end
--- Set a new listener for an S_EVENT_SHOT event for a unit.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_SHOT )
return self
end
--- Stop listening to S_EVENT_SHOT event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnShotRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_SHOT )
return self
end
end
do -- OnHit
--- Set a new listener for an S_EVENT_HIT event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnHit( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_HIT )
return self
end
--- Set a new listener for an S_EVENT_HIT event.
-- @param #EVENT self
-- @param #string EventDCSUnitName
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventClass )
self:F2( EventDCSUnitName )
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventClass, world.event.S_EVENT_HIT )
return self
end
--- Stop listening to S_EVENT_HIT event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnHitRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_HIT )
return self
end
end
do -- OnPlayerEnterUnit
--- Set a new listener for an S_EVENT_PLAYER_ENTER_UNIT event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnPlayerEnterUnit( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT )
return self
end
--- Stop listening to S_EVENT_PLAYER_ENTER_UNIT event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnPlayerEnterRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_PLAYER_ENTER_UNIT )
return self
end
end
do -- OnPlayerLeaveUnit
--- Set a new listener for an S_EVENT_PLAYER_LEAVE_UNIT event.
-- @param #EVENT self
-- @param #function EventFunction The function to be called when the event occurs for the unit.
-- @param Base#BASE EventClass The self instance of the class for which the event is.
-- @return #EVENT
function EVENT:OnPlayerLeaveUnit( EventFunction, EventClass )
self:F2()
self:OnEventGeneric( EventFunction, EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT )
return self
end
--- Stop listening to S_EVENT_PLAYER_LEAVE_UNIT event.
-- @param #EVENT self
-- @param Base#BASE EventClass
-- @return #EVENT
function EVENT:OnPlayerLeaveRemove( EventClass )
self:F2()
self:Remove( EventClass, world.event.S_EVENT_PLAYER_LEAVE_UNIT )
return self
end
end end
@ -1207,7 +792,9 @@ function EVENT:onEvent( Event )
local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityBegin = PriorityOrder == -1 and 5 or 1
local PriorityEnd = PriorityOrder == -1 and 1 or 5 local PriorityEnd = PriorityOrder == -1 and 1 or 5
self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) if Event.IniObjectCategory ~= 3 then
self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
end
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
@ -1228,8 +815,10 @@ function EVENT:onEvent( Event )
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event )
@ -1242,8 +831,10 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event ) return EventFunction( EventClass, Event )
@ -1257,7 +848,9 @@ function EVENT:onEvent( Event )
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
@ -1271,8 +864,10 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event ) return EventFunction( EventClass, Event )
@ -1290,9 +885,11 @@ function EVENT:onEvent( Event )
if EventData.EventGroup[Event.IniGroupName] then if EventData.EventGroup[Event.IniGroupName] then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventGroup[Event.IniGroupName].EventFunction then if EventData.EventGroup[Event.IniGroupName].EventFunction then
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event )
@ -1305,8 +902,10 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event ) return EventFunction( EventClass, Event )
@ -1318,8 +917,10 @@ function EVENT:onEvent( Event )
if EventData.EventGroup[Event.TgtGroupName] then if EventData.EventGroup[Event.TgtGroupName] then
if EventData.EventGroup[Event.TgtGroupName].EventFunction then if EventData.EventGroup[Event.TgtGroupName].EventFunction then
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event )
@ -1332,7 +933,9 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
@ -1354,8 +957,9 @@ function EVENT:onEvent( Event )
if EventData.EventFunction then if EventData.EventFunction then
-- There is an EventFunction defined, so call the EventFunction. -- There is an EventFunction defined, so call the EventFunction.
self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventFunction( EventClass, Event ) return EventData.EventFunction( EventClass, Event )
@ -1367,11 +971,14 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event ) local Result, Value = EventFunction( EventClass, Event )
return Result, Value
end, ErrorHandler ) end, ErrorHandler )
end end
end end
@ -1385,6 +992,8 @@ function EVENT:onEvent( Event )
else else
self:E( { _EVENTMETA[Event.id].Text, Event } ) self:E( { _EVENTMETA[Event.id].Text, Event } )
end end
Event = nil
end end
--- The EVENTHANDLER structure --- The EVENTHANDLER structure

View File

@ -530,10 +530,20 @@ do -- FSM
function FSM:_call_handler( handler, params, EventName ) function FSM:_call_handler( handler, params, EventName )
local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg )
if debug ~= nil then
env.info( debug.traceback() )
end
return errmsg
end
if self[handler] then if self[handler] then
self:T( "Calling " .. handler ) self:T( "Calling " .. handler )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
local Value = self[handler]( self, unpack(params) ) local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
return Value return Value
end end
end end
@ -732,8 +742,66 @@ do -- FSM_CONTROLLABLE
self:SetControllable( Controllable ) self:SetControllable( Controllable )
end end
self:AddTransition( "*", "Stop", "Stopped" )
--- OnBefore Transition Handler for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] OnAfterStop
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] Stop
-- @param #FSM_CONTROLLABLE self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] __Stop
-- @param #FSM_CONTROLLABLE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Stopped.
-- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Stopped.
-- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
return self return self
end end
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] OnAfterStop
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To)
-- Clear all pending schedules
self.CallScheduler:Clear()
end
--- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. --- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.
-- @param #FSM_CONTROLLABLE self -- @param #FSM_CONTROLLABLE self
@ -801,12 +869,34 @@ do -- FSM_PROCESS
function FSM_PROCESS:Init( FsmProcess ) function FSM_PROCESS:Init( FsmProcess )
self:T( "No Initialisation" ) self:T( "No Initialisation" )
end end
function FSM_PROCESS:_call_handler( handler, params, EventName )
local ErrorHandler = function( errmsg )
env.info( "Error in FSM_PROCESS call handler:" .. errmsg )
if debug ~= nil then
env.info( debug.traceback() )
end
return errmsg
end
if self[handler] then
self:F3( "Calling " .. handler )
self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler )
return Value
--return self[handler]( self, self.Controllable, unpack( params ) )
end
end
--- Creates a new FSM_PROCESS object based on this FSM_PROCESS. --- Creates a new FSM_PROCESS object based on this FSM_PROCESS.
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @return #FSM_PROCESS -- @return #FSM_PROCESS
function FSM_PROCESS:Copy( Controllable, Task ) function FSM_PROCESS:Copy( Controllable, Task )
self:T( { self:GetClassNameAndID() } ) self:T( { self:GetClassNameAndID() } )
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
@ -825,7 +915,7 @@ do -- FSM_PROCESS
-- Copy Processes -- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do for ProcessID, Process in pairs( self:GetProcesses() ) do
self:T( { Process} ) self:E( { Process} )
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
end end
@ -843,6 +933,22 @@ do -- FSM_PROCESS
return NewFsm return NewFsm
end end
--- Removes an FSM_PROCESS object.
-- @param #FSM_PROCESS self
-- @return #FSM_PROCESS
function FSM_PROCESS:Remove()
self:T( { self:GetClassNameAndID() } )
-- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do
self:E( { Process} )
Process.fsm:Remove()
Process.fsm = nil
end
return self
end
--- Sets the task of the process. --- Sets the task of the process.
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self

View File

@ -138,6 +138,8 @@ do -- MENU_BASE
} }
--- Consructor --- Consructor
-- @param #MENU_BASE
-- @return #MENU_BASE
function MENU_BASE:New( MenuText, ParentMenu ) function MENU_BASE:New( MenuText, ParentMenu )
local MenuParentPath = {} local MenuParentPath = {}
@ -150,10 +152,43 @@ do -- MENU_BASE
self.MenuPath = nil self.MenuPath = nil
self.MenuText = MenuText self.MenuText = MenuText
self.MenuParentPath = MenuParentPath self.MenuParentPath = MenuParentPath
self.Menus = {}
self.MenuCount = 0
self.MenuRemoveParent = false
self.MenuTime = timer.getTime()
return self return self
end end
--- Gets a @{Menu} from a parent @{Menu}
-- @param #MENU_BASE self
-- @param #string MenuText The text of the child menu.
-- @return #MENU_BASE
function MENU_BASE:GetMenu( MenuText )
self:F( { self.Menus, MenuText } )
return self.Menus[MenuText]
end
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
-- @param #MENU_BASE self
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
-- @return #MENU_BASE
function MENU_BASE:SetRemoveParent( RemoveParent )
self:F( { RemoveParent } )
self.MenuRemoveParent = RemoveParent
return self
end
--- Sets a time stamp for later prevention of menu removal.
-- @param #MENU_BASE self
-- @param MenuTime
-- @return #MENU_BASE
function MENU_BASE:SetTime( MenuTime )
self.MenuTime = MenuTime
return self
end
end end
do -- MENU_COMMAND_BASE do -- MENU_COMMAND_BASE
@ -161,7 +196,7 @@ do -- MENU_COMMAND_BASE
--- The MENU_COMMAND_BASE class --- The MENU_COMMAND_BASE class
-- @type MENU_COMMAND_BASE -- @type MENU_COMMAND_BASE
-- @field #function MenuCallHandler -- @field #function MenuCallHandler
-- @extends Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
MENU_COMMAND_BASE = { MENU_COMMAND_BASE = {
ClassName = "MENU_COMMAND_BASE", ClassName = "MENU_COMMAND_BASE",
CommandMenuFunction = nil, CommandMenuFunction = nil,
@ -170,6 +205,8 @@ do -- MENU_COMMAND_BASE
} }
--- Constructor --- Constructor
-- @param #MENU_COMMAND_BASE
-- @return #MENU_COMMAND_BASE
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
@ -189,7 +226,7 @@ do -- MENU_MISSION
--- The MENU_MISSION class --- The MENU_MISSION class
-- @type MENU_MISSION -- @type MENU_MISSION
-- @extends Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
MENU_MISSION = { MENU_MISSION = {
ClassName = "MENU_MISSION" ClassName = "MENU_MISSION"
} }
@ -198,7 +235,7 @@ do -- MENU_MISSION
-- @param #MENU_MISSION self -- @param #MENU_MISSION self
-- @param #string MenuText The text for the menu. -- @param #string MenuText The text for the menu.
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other).
-- @return #MENU_MISSION self -- @return #MENU_MISSION
function MENU_MISSION:New( MenuText, ParentMenu ) function MENU_MISSION:New( MenuText, ParentMenu )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
@ -225,7 +262,7 @@ do -- MENU_MISSION
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
-- @param #MENU_MISSION self -- @param #MENU_MISSION self
-- @return #MENU_MISSION self -- @return #MENU_MISSION
function MENU_MISSION:RemoveSubMenus() function MENU_MISSION:RemoveSubMenus()
self:F( self.MenuPath ) self:F( self.MenuPath )
@ -256,7 +293,7 @@ do -- MENU_MISSION_COMMAND
--- The MENU_MISSION_COMMAND class --- The MENU_MISSION_COMMAND class
-- @type MENU_MISSION_COMMAND -- @type MENU_MISSION_COMMAND
-- @extends Menu#MENU_COMMAND_BASE -- @extends Core.Menu#MENU_COMMAND_BASE
MENU_MISSION_COMMAND = { MENU_MISSION_COMMAND = {
ClassName = "MENU_MISSION_COMMAND" ClassName = "MENU_MISSION_COMMAND"
} }
@ -306,7 +343,7 @@ do -- MENU_COALITION
--- The MENU_COALITION class --- The MENU_COALITION class
-- @type MENU_COALITION -- @type MENU_COALITION
-- @extends Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
-- @usage -- @usage
-- -- This demo creates a menu structure for the planes within the red coalition. -- -- This demo creates a menu structure for the planes within the red coalition.
-- -- To test, join the planes, then look at the other radio menus (Option F10). -- -- To test, join the planes, then look at the other radio menus (Option F10).
@ -380,7 +417,7 @@ do -- MENU_COALITION
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
-- @param #MENU_COALITION self -- @param #MENU_COALITION self
-- @return #MENU_COALITION self -- @return #MENU_COALITION
function MENU_COALITION:RemoveSubMenus() function MENU_COALITION:RemoveSubMenus()
self:F( self.MenuPath ) self:F( self.MenuPath )
@ -411,7 +448,7 @@ do -- MENU_COALITION_COMMAND
--- The MENU_COALITION_COMMAND class --- The MENU_COALITION_COMMAND class
-- @type MENU_COALITION_COMMAND -- @type MENU_COALITION_COMMAND
-- @extends Menu#MENU_COMMAND_BASE -- @extends Core.Menu#MENU_COMMAND_BASE
MENU_COALITION_COMMAND = { MENU_COALITION_COMMAND = {
ClassName = "MENU_COALITION_COMMAND" ClassName = "MENU_COALITION_COMMAND"
} }
@ -423,7 +460,7 @@ do -- MENU_COALITION_COMMAND
-- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @param Menu#MENU_COALITION ParentMenu The parent menu.
-- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuFunction A function that is called when the menu key is pressed.
-- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this.
-- @return #MENU_COALITION_COMMAND self -- @return #MENU_COALITION_COMMAND
function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... )
local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
@ -468,7 +505,7 @@ do -- MENU_CLIENT
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
-- @type MENU_CLIENT -- @type MENU_CLIENT
-- @extends Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
-- @usage -- @usage
-- -- This demo creates a menu structure for the two clients of planes. -- -- This demo creates a menu structure for the two clients of planes.
-- -- Each client will receive a different menu structure. -- -- Each client will receive a different menu structure.
@ -609,7 +646,7 @@ do -- MENU_CLIENT
--- The MENU_CLIENT_COMMAND class --- The MENU_CLIENT_COMMAND class
-- @type MENU_CLIENT_COMMAND -- @type MENU_CLIENT_COMMAND
-- @extends Menu#MENU_COMMAND -- @extends Core.Menu#MENU_COMMAND
MENU_CLIENT_COMMAND = { MENU_CLIENT_COMMAND = {
ClassName = "MENU_CLIENT_COMMAND" ClassName = "MENU_CLIENT_COMMAND"
} }
@ -695,7 +732,7 @@ do
--- The MENU_GROUP class --- The MENU_GROUP class
-- @type MENU_GROUP -- @type MENU_GROUP
-- @extends Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
-- @usage -- @usage
-- -- This demo creates a menu structure for the two groups of planes. -- -- This demo creates a menu structure for the two groups of planes.
-- -- Each group will receive a different menu structure. -- -- Each group will receive a different menu structure.
@ -769,8 +806,6 @@ do
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MenuGroup._Menus[Path] = self MenuGroup._Menus[Path] = self
self.Menus = {}
self.MenuGroup = MenuGroup self.MenuGroup = MenuGroup
self.Path = Path self.Path = Path
self.MenuGroupID = MenuGroup:GetID() self.MenuGroupID = MenuGroup:GetID()
@ -780,8 +815,10 @@ do
self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self:T( { "Adding Menu ", MenuText, self.MenuParentPath } )
self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath )
if ParentMenu and ParentMenu.Menus then if self.ParentMenu and self.ParentMenu.Menus then
ParentMenu.Menus[self.MenuPath] = self self.ParentMenu.Menus[MenuText] = self
self:F( { self.ParentMenu.Menus, MenuText } )
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
end end
end end
@ -792,42 +829,56 @@ do
--- Removes the sub menus recursively of this MENU_GROUP. --- Removes the sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self -- @param #MENU_GROUP self
-- @param MenuTime
-- @return #MENU_GROUP self -- @return #MENU_GROUP self
function MENU_GROUP:RemoveSubMenus() function MENU_GROUP:RemoveSubMenus( MenuTime )
self:F( self.MenuPath ) self:F2( { self.MenuPath, MenuTime, self.MenuTime } )
for MenuID, Menu in pairs( self.Menus ) do self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } )
Menu:Remove() for MenuText, Menu in pairs( self.Menus ) do
Menu:Remove( MenuTime )
end end
end end
--- Removes the main menu and sub menus recursively of this MENU_GROUP. --- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self -- @param #MENU_GROUP self
-- @param MenuTime
-- @return #nil -- @return #nil
function MENU_GROUP:Remove() function MENU_GROUP:Remove( MenuTime )
self:F( { self.MenuGroupID, self.MenuPath } ) self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
self:RemoveSubMenus() self:RemoveSubMenus( MenuTime )
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) if not MenuTime or self.MenuTime ~= MenuTime then
if self.ParentMenu then if self.MenuGroup._Menus[self.Path] then
self.ParentMenu.Menus[self.MenuPath] = nil self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
if self.ParentMenu then
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T( "Removing Parent Menu " )
self.ParentMenu:Remove()
end
end
end
self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } )
self.MenuGroup._Menus[self.Path] = nil
self = nil
end end
self:E( self.MenuGroup._Menus[self.Path] )
self.MenuGroup._Menus[self.Path] = nil
self = nil
end end
return nil return nil
end end
--- The MENU_GROUP_COMMAND class --- The MENU_GROUP_COMMAND class
-- @type MENU_GROUP_COMMAND -- @type MENU_GROUP_COMMAND
-- @extends Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
MENU_GROUP_COMMAND = { MENU_GROUP_COMMAND = {
ClassName = "MENU_GROUP_COMMAND" ClassName = "MENU_GROUP_COMMAND"
} }
@ -839,13 +890,14 @@ do
-- @param ParentMenu The parent menu. -- @param ParentMenu The parent menu.
-- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuFunction A function that is called when the menu key is pressed.
-- @param CommandMenuArgument An argument for the function. -- @param CommandMenuArgument An argument for the function.
-- @return Menu#MENU_GROUP_COMMAND self -- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... )
MenuGroup._Menus = MenuGroup._Menus or {} MenuGroup._Menus = MenuGroup._Menus or {}
local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText
if MenuGroup._Menus[Path] then if MenuGroup._Menus[Path] then
self = MenuGroup._Menus[Path] self = MenuGroup._Menus[Path]
self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } )
else else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MenuGroup._Menus[Path] = self MenuGroup._Menus[Path] = self
@ -856,33 +908,45 @@ do
self.MenuText = MenuText self.MenuText = MenuText
self.ParentMenu = ParentMenu self.ParentMenu = ParentMenu
self:T( { "Adding Command Menu ", MenuText, self.MenuParentPath } ) self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } )
self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
if ParentMenu and ParentMenu.Menus then if self.ParentMenu and self.ParentMenu.Menus then
ParentMenu.Menus[self.MenuPath] = self self.ParentMenu.Menus[MenuText] = self
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
self:F( { ParentMenu.Menus, MenuText } )
end end
end end
--self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } )
return self return self
end end
--- Removes a menu structure for a group. --- Removes a menu structure for a group.
-- @param #MENU_GROUP_COMMAND self -- @param #MENU_GROUP_COMMAND self
-- @param MenuTime
-- @return #nil -- @return #nil
function MENU_GROUP_COMMAND:Remove() function MENU_GROUP_COMMAND:Remove( MenuTime )
self:F( { self.MenuGroupID, self.MenuPath } ) self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
if self.MenuGroup._Menus[self.Path] then if not MenuTime or self.MenuTime ~= MenuTime then
self = self.MenuGroup._Menus[self.Path] if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
self.ParentMenu.Menus[self.MenuPath] = nil missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
self:E( self.MenuGroup._Menus[self.Path] ) self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } )
self.MenuGroup._Menus[self.Path] = nil
self = nil self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T( "Removing Parent Menu " )
self.ParentMenu:Remove()
end
end
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
end end
return nil return nil

View File

@ -64,7 +64,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
-- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {}
if Scheduler.MasterObject then if Scheduler.MasterObject then
self.ObjectSchedulers[self.CallID] = Scheduler self.ObjectSchedulers[self.CallID] = Scheduler
@ -181,11 +181,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
if CallID then if CallID then
local Schedule = self.Schedule[Scheduler] local Schedule = self.Schedule[Scheduler]
Schedule[CallID].ScheduleID = timer.scheduleFunction( -- Only start when there is no ScheduleID defined!
Schedule[CallID].CallHandler, -- This prevents to "Start" the scheduler twice with the same CallID...
CallID, if not Schedule[CallID].ScheduleID then
timer.getTime() + Schedule[CallID].Start Schedule[CallID].ScheduleID = timer.scheduleFunction(
) Schedule[CallID].CallHandler,
CallID,
timer.getTime() + Schedule[CallID].Start
)
end
else else
for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do
self:Start( Scheduler, CallID ) -- Recursive self:Start( Scheduler, CallID ) -- Recursive
@ -198,7 +202,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
if CallID then if CallID then
local Schedule = self.Schedule[Scheduler] local Schedule = self.Schedule[Scheduler]
timer.removeFunction( Schedule[CallID].ScheduleID ) -- Only stop when there is a ScheduleID defined for the CallID.
-- So, when the scheduler was stopped before, do nothing.
if Schedule[CallID].ScheduleID then
timer.removeFunction( Schedule[CallID].ScheduleID )
Schedule[CallID].ScheduleID = nil
end
else else
for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do
self:Stop( Scheduler, CallID ) -- Recursive self:Stop( Scheduler, CallID ) -- Recursive
@ -206,5 +215,13 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
end end
end end
function SCHEDULEDISPATCHER:Clear( Scheduler )
self:F2( { Scheduler = Scheduler } )
for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do
self:Stop( Scheduler, CallID ) -- Recursive
end
end

View File

@ -1,5 +1,9 @@
--- This module contains the SCHEDULER class. --- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**.
-- --
-- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG)
--
-- ===
--
-- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} -- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE}
-- --
-- The @{Scheduler#SCHEDULER} class creates schedule. -- The @{Scheduler#SCHEDULER} class creates schedule.
@ -147,6 +151,13 @@ function SCHEDULER:Remove( ScheduleID )
_SCHEDULEDISPATCHER:Remove( self, ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID )
end end
--- Clears all pending schedules.
-- @param #SCHEDULER self
function SCHEDULER:Clear()
self:F3( )
_SCHEDULEDISPATCHER:Clear( self )
end

View File

@ -240,6 +240,7 @@ SET_BASE = {
Filter = {}, Filter = {},
Set = {}, Set = {},
List = {}, List = {},
Index = {},
} }
--- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
@ -258,10 +259,14 @@ function SET_BASE:New( Database )
self.YieldInterval = 10 self.YieldInterval = 10
self.TimeInterval = 0.001 self.TimeInterval = 0.001
self.Set = {}
self.List = {} self.List = {}
self.List.__index = self.List self.List.__index = self.List
self.List = setmetatable( { Count = 0 }, self.List ) self.List = setmetatable( { Count = 0 }, self.List )
self.Index = {}
self.CallScheduler = SCHEDULER:New( self ) self.CallScheduler = SCHEDULER:New( self )
self:SetEventPriority( 2 ) self:SetEventPriority( 2 )
@ -313,6 +318,8 @@ function SET_BASE:Add( ObjectName, Object )
self.Set[ObjectName] = t._ self.Set[ObjectName] = t._
table.insert( self.Index, ObjectName )
end end
--- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index.
@ -364,7 +371,15 @@ function SET_BASE:Remove( ObjectName )
t._prev = nil t._prev = nil
self.List.Count = self.List.Count - 1 self.List.Count = self.List.Count - 1
for Index, Key in ipairs( self.Index ) do
if Key == ObjectName then
table.remove( self.Index, Index )
break
end
end
self.Set[ObjectName] = nil self.Set[ObjectName] = nil
end end
end end
@ -384,12 +399,50 @@ function SET_BASE:Get( ObjectName )
end end
--- Gets the first object from the @{Set#SET_BASE} and derived classes.
-- @param #SET_BASE self
-- @return Core.Base#BASE
function SET_BASE:GetFirst()
self:F()
local ObjectName = self.Index[1]
local FirstObject = self.Set[ObjectName]
self:T3( { FirstObject } )
return FirstObject
end
--- Gets the last object from the @{Set#SET_BASE} and derived classes.
-- @param #SET_BASE self
-- @return Core.Base#BASE
function SET_BASE:GetLast()
self:F()
local ObjectName = self.Index[#self.Index]
local LastObject = self.Set[ObjectName]
self:T3( { LastObject } )
return LastObject
end
--- Gets a random object from the @{Set#SET_BASE} and derived classes.
-- @param #SET_BASE self
-- @return Core.Base#BASE
function SET_BASE:GetRandom()
self:F()
local RandomItem = self.Set[self.Index[math.random(#self.Index)]]
self:T3( { RandomItem } )
return RandomItem
end
--- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. --- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes.
-- @param #SET_BASE self -- @param #SET_BASE self
-- @return #number Count -- @return #number Count
function SET_BASE:Count() function SET_BASE:Count()
return self.List.Count return #self.Index
end end
@ -652,7 +705,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen
return false return false
end end
self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 )
Schedule()
return self return self
end end
@ -725,7 +779,7 @@ end
--- SET_GROUP class --- SET_GROUP class
-- @type SET_GROUP -- @type SET_GROUP
-- @extends #SET_BASE -- @extends Core.Set#SET_BASE
SET_GROUP = { SET_GROUP = {
ClassName = "SET_GROUP", ClassName = "SET_GROUP",
Filter = { Filter = {

View File

@ -1,5 +1,7 @@
--- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. --- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**.
-- --
-- ![Banner Image](..\Presentations\ZONE\Dia1.JPG)
--
-- === -- ===
-- --
-- There are essentially two core functions that zones accomodate: -- There are essentially two core functions that zones accomodate:
@ -247,6 +249,58 @@ function ZONE_BASE:GetVec2()
return nil return nil
end end
--- Returns a @{Point#POINT_VEC2} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone.
function ZONE_BASE:GetPointVec2()
self:F2( self.ZoneName )
local Vec2 = self:GetVec2()
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
self:T2( { PointVec2 } )
return PointVec2
end
--- Returns the @{DCSTypes#Vec3} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone.
function ZONE_BASE:GetVec3( Height )
self:F2( self.ZoneName )
Height = Height or 0
local Vec2 = self:GetVec2()
local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y }
self:T2( { Vec3 } )
return Vec3
end
--- Returns a @{Point#POINT_VEC3} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone.
function ZONE_BASE:GetPointVec3( Height )
self:F2( self.ZoneName )
local Vec3 = self:GetVec3( Height )
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
self:T2( { PointVec3 } )
return PointVec3
end
--- Define a random @{DCSTypes#Vec2} within the zone. --- Define a random @{DCSTypes#Vec2} within the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates.
@ -261,6 +315,13 @@ function ZONE_BASE:GetRandomPointVec2()
return nil return nil
end end
--- Define a random @{Point#POINT_VEC3} within the zone.
-- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
function ZONE_BASE:GetRandomPointVec3()
return nil
end
--- Get the bounding square the zone. --- Get the bounding square the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return #nil The bounding square. -- @return #nil The bounding square.
@ -347,8 +408,9 @@ end
--- Bounds the zone with tires. --- Bounds the zone with tires.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number Points (optional) The amount of points in the circle. -- @param #number Points (optional) The amount of points in the circle.
-- @param #boolean UnBound If true the tyres will be destroyed.
-- @return #ZONE_RADIUS self -- @return #ZONE_RADIUS self
function ZONE_RADIUS:BoundZone( Points ) function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
local Point = {} local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
@ -364,8 +426,10 @@ function ZONE_RADIUS:BoundZone( Points )
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
local Tire = { local Tire = {
["country"] = "USA", ["country"] = CountryName,
["category"] = "Fortifications", ["category"] = "Fortifications",
["canCargo"] = false, ["canCargo"] = false,
["shape_name"] = "H-tyre_B_WF", ["shape_name"] = "H-tyre_B_WF",
@ -377,7 +441,10 @@ function ZONE_RADIUS:BoundZone( Points )
["heading"] = 0, ["heading"] = 0,
} -- end of ["group"] } -- end of ["group"]
coalition.addStaticObject( country.id.USA, Tire ) local Group = coalition.addStaticObject( CountryID, Tire )
if UnBound and UnBound == true then
Group:destroy()
end
end end
return self return self
@ -810,8 +877,9 @@ end
--- Smokes the zone boundaries in a color. --- Smokes the zone boundaries in a color.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @param #boolean UnBound If true, the tyres will be destroyed.
-- @return #ZONE_POLYGON_BASE self -- @return #ZONE_POLYGON_BASE self
function ZONE_POLYGON_BASE:BoundZone( ) function ZONE_POLYGON_BASE:BoundZone( UnBound )
local i local i
local j local j
@ -840,8 +908,11 @@ function ZONE_POLYGON_BASE:BoundZone( )
["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ),
["heading"] = 0, ["heading"] = 0,
} -- end of ["group"] } -- end of ["group"]
coalition.addStaticObject( country.id.USA, Tire ) local Group = coalition.addStaticObject( country.id.USA, Tire )
if UnBound and UnBound == true then
Group:destroy()
end
end end
j = i j = i

View File

@ -29,7 +29,9 @@ CLEANUP = {
-- or -- or
-- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
-- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, BASE:New() ) function CLEANUP:New( ZoneNames, TimeInterval )
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP
self:F( { ZoneNames, TimeInterval } ) self:F( { ZoneNames, TimeInterval } )
if type( ZoneNames ) == 'table' then if type( ZoneNames ) == 'table' then
@ -41,7 +43,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self,
self.TimeInterval = TimeInterval self.TimeInterval = TimeInterval
end end
_EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) self:HandleEvent( EVENTS.Birth )
self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval )
@ -102,32 +104,24 @@ function CLEANUP:_DestroyMissile( MissileObject )
end end
end end
function CLEANUP:_OnEventBirth( Event ) --- @param #CLEANUP self
self:F( { Event } ) -- @param Core.Event#EVENTDATA EventData
function CLEANUP:_OnEventBirth( EventData )
self:F( { EventData } )
self.CleanUpList[Event.IniDCSUnitName] = {} self.CleanUpList[EventData.IniDCSUnitName] = {}
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
_EVENTDISPATCHER:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
_EVENTDISPATCHER:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
_EVENTDISPATCHER:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
_EVENTDISPATCHER:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self )
_EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self )
_EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self )
_EVENTDISPATCHER:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self )
--self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp )
--self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp )
-- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp )
-- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp )
-- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash )
-- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot )
--
-- self:EnableEvents()
EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot )
end end

File diff suppressed because it is too large Load Diff

View File

@ -84,7 +84,7 @@
-- --
-- ESCORT initialization methods. -- ESCORT initialization methods.
-- ============================== -- ==============================
-- The following menus are created within the RADIO MENU of an active unit hosted by a player: -- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player:
-- --
-- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. -- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client.
-- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. -- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position.
@ -128,6 +128,7 @@
-- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. -- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup.
-- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. -- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup.
-- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission -- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission
-- @field Functional.Detection#DETECTION_BASE Detection
ESCORT = { ESCORT = {
ClassName = "ESCORT", ClassName = "ESCORT",
EscortName = nil, -- The Escort Name EscortName = nil, -- The Escort Name
@ -176,14 +177,22 @@ ESCORT = {
-- -- Now use these 2 objects to construct the new EscortPlanes object. -- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) -- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT
self:F( { EscortClient, EscortGroup, EscortName } ) self:F( { EscortClient, EscortGroup, EscortName } )
self.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortClient = EscortClient -- Wrapper.Client#CLIENT
self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP
self.EscortName = EscortName self.EscortName = EscortName
self.EscortBriefing = EscortBriefing self.EscortBriefing = EscortBriefing
self.EscortSetGroup = SET_GROUP:New()
self.EscortSetGroup:AddObject( self.EscortGroup )
self.EscortSetGroup:Flush()
self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 )
self.EscortGroup.Detection = self.Detection
-- Set EscortGroup known at EscortClient. -- Set EscortGroup known at EscortClient.
if not self.EscortClient._EscortGroups then if not self.EscortClient._EscortGroups then
self.EscortClient._EscortGroups = {} self.EscortClient._EscortGroups = {}
@ -193,7 +202,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()] = {}
self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup
self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName
self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection
end end
self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName )
@ -218,13 +227,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
self.FollowDistance = 100 self.FollowDistance = 100
self.CT1 = 0 self.CT1 = 0
self.GT1 = 0 self.GT1 = 0
self.FollowScheduler = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 )
self.EscortMode = ESCORT.MODE.MISSION
self.FollowScheduler:Stop()
self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 )
self.FollowScheduler:Stop( self.FollowSchedule )
self.EscortMode = ESCORT.MODE.MISSION
return self return self
end end
--- Set a Detection method for the EscortClient to be reported upon.
-- Detection methods are based on the derived classes from DETECTION_BASE.
-- @param #ESCORT self
-- @param Function.Detection#DETECTION_BASE Detection
function ESCORT:SetDetection( Detection )
self.Detection = Detection
self.EscortGroup.Detection = self.Detection
self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection
Detection:__Start( 1 )
end
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. --- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
-- This allows to visualize where the escort is flying to. -- This allows to visualize where the escort is flying to.
-- @param #ESCORT self -- @param #ESCORT self
@ -282,7 +308,7 @@ function ESCORT:MenuFollowAt( Distance )
self.EscortMenuJoinUpAndFollow = {} self.EscortMenuJoinUpAndFollow = {}
end end
self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = Distance } ) self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance )
self.EscortMode = ESCORT.MODE.FOLLOW self.EscortMode = ESCORT.MODE.FOLLOW
end end
@ -340,11 +366,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat )
MenuText, MenuText,
self.EscortMenuHold, self.EscortMenuHold,
ESCORT._HoldPosition, ESCORT._HoldPosition,
{ ParamSelf = self, self,
ParamOrbitGroup = self.EscortGroup, self.EscortGroup,
ParamHeight = Height, Height,
ParamSeconds = Seconds Seconds
}
) )
end end
@ -461,9 +486,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat )
MenuText, MenuText,
self.EscortMenuScan, self.EscortMenuScan,
ESCORT._ScanTargets, ESCORT._ScanTargets,
{ ParamSelf = self, self,
ParamScanDuration = 30 30
}
) )
end end
@ -493,11 +517,11 @@ function ESCORT:MenuFlare( MenuTextFormat )
end end
if not self.EscortMenuFlare then if not self.EscortMenuFlare then
self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } ) self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self )
self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Green, ParamMessage = "Released a green flare!" } ) self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" )
self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Red, ParamMessage = "Released a red flare!" } ) self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" )
self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.White, ParamMessage = "Released a white flare!" } ) self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" )
self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Yellow, ParamMessage = "Released a yellow flare!" } ) self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" )
end end
return self return self
@ -526,12 +550,12 @@ function ESCORT:MenuSmoke( MenuTextFormat )
end end
if not self.EscortMenuSmoke then if not self.EscortMenuSmoke then
self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } ) self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self )
self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } ) self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" )
self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } ) self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" )
self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } ) self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" )
self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } ) self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" )
self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } ) self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" )
end end
end end
@ -556,9 +580,9 @@ function ESCORT:MenuReportTargets( Seconds )
end end
-- Report Targets -- Report Targets
self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self )
self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true )
self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false )
-- Attack Targets -- Attack Targets
self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu )
@ -595,16 +619,16 @@ function ESCORT:MenuROE( MenuTextFormat )
-- Rules of Engagement -- Rules of Engagement
self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu )
if self.EscortGroup:OptionROEHoldFirePossible() then if self.EscortGroup:OptionROEHoldFirePossible() then
self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } ) self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" )
end end
if self.EscortGroup:OptionROEReturnFirePossible() then if self.EscortGroup:OptionROEReturnFirePossible() then
self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } ) self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" )
end end
if self.EscortGroup:OptionROEOpenFirePossible() then if self.EscortGroup:OptionROEOpenFirePossible() then
self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } ) self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" )
end end
if self.EscortGroup:OptionROEWeaponFreePossible() then if self.EscortGroup:OptionROEWeaponFreePossible() then
self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } ) self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" )
end end
end end
@ -624,16 +648,16 @@ function ESCORT:MenuEvasion( MenuTextFormat )
-- Reaction to Threats -- Reaction to Threats
self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu )
if self.EscortGroup:OptionROTNoReactionPossible() then if self.EscortGroup:OptionROTNoReactionPossible() then
self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } ) self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" )
end end
if self.EscortGroup:OptionROTPassiveDefensePossible() then if self.EscortGroup:OptionROTPassiveDefensePossible() then
self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } ) self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" )
end end
if self.EscortGroup:OptionROTEvadeFirePossible() then if self.EscortGroup:OptionROTEvadeFirePossible() then
self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } ) self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" )
end end
if self.EscortGroup:OptionROTVerticalPossible() then if self.EscortGroup:OptionROTVerticalPossible() then
self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } ) self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" )
end end
end end
end end
@ -658,18 +682,14 @@ end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._HoldPosition( MenuParam ) function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP
local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT
local OrbitHeight = MenuParam.ParamHeight
local OrbitSeconds = MenuParam.ParamSeconds -- Not implemented yet
self.FollowScheduler:Stop() self.FollowScheduler:Stop( self.FollowSchedule )
local PointFrom = {} local PointFrom = {}
local GroupVec3 = EscortGroup:GetUnit(1):GetVec3() local GroupVec3 = EscortGroup:GetUnit(1):GetVec3()
@ -702,13 +722,12 @@ function ESCORT._HoldPosition( MenuParam )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._JoinUpAndFollow( MenuParam ) function ESCORT:_JoinUpAndFollow( Distance )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
self.Distance = MenuParam.ParamDistance self.Distance = Distance
self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance )
end end
@ -721,7 +740,7 @@ end
function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance )
self:F( { EscortGroup, EscortClient, Distance } ) self:F( { EscortGroup, EscortClient, Distance } )
self.FollowScheduler:Stop() self.FollowScheduler:Stop( self.FollowSchedule )
EscortGroup:OptionROEHoldFire() EscortGroup:OptionROEHoldFire()
EscortGroup:OptionROTPassiveDefense() EscortGroup:OptionROTPassiveDefense()
@ -730,44 +749,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance )
self.CT1 = 0 self.CT1 = 0
self.GT1 = 0 self.GT1 = 0
self.FollowScheduler:Start() self.FollowScheduler:Start( self.FollowSchedule )
EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._Flare( MenuParam ) function ESCORT:_Flare( Color, Message )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local Color = MenuParam.ParamColor
local Message = MenuParam.ParamMessage
EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:GetUnit(1):Flare( Color )
EscortGroup:MessageToClient( Message, 10, EscortClient ) EscortGroup:MessageToClient( Message, 10, EscortClient )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._Smoke( MenuParam ) function ESCORT:_Smoke( Color, Message )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local Color = MenuParam.ParamColor
local Message = MenuParam.ParamMessage
EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:GetUnit(1):Smoke( Color )
EscortGroup:MessageToClient( Message, 10, EscortClient ) EscortGroup:MessageToClient( Message, 10, EscortClient )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._ReportNearbyTargetsNow( MenuParam ) function ESCORT:_ReportNearbyTargetsNow()
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
@ -775,17 +785,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam )
end end
function ESCORT._SwitchReportNearbyTargets( MenuParam ) function ESCORT:_SwitchReportNearbyTargets( ReportTargets )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
self.ReportTargets = MenuParam.ParamReportTargets self.ReportTargets = ReportTargets
if self.ReportTargets then if self.ReportTargets then
if not self.ReportTargetsScheduler then if not self.ReportTargetsScheduler then
self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 )
end end
else else
routines.removeFunction( self.ReportTargetsScheduler ) routines.removeFunction( self.ReportTargetsScheduler )
@ -794,40 +803,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._ScanTargets( MenuParam ) function ESCORT:_ScanTargets( ScanDuration )
local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local ScanDuration = MenuParam.ParamScanDuration self.FollowScheduler:Stop( self.FollowSchedule )
self.FollowScheduler:Stop()
if EscortGroup:IsHelicopter() then if EscortGroup:IsHelicopter() then
SCHEDULER:New( EscortGroup, EscortGroup.PushTask, EscortGroup:PushTask(
{ EscortGroup:TaskControlled( EscortGroup:TaskControlled(
EscortGroup:TaskOrbitCircle( 200, 20 ), EscortGroup:TaskOrbitCircle( 200, 20 ),
EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil )
) ), 1 )
},
1
)
elseif EscortGroup:IsAirPlane() then elseif EscortGroup:IsAirPlane() then
SCHEDULER:New( EscortGroup, EscortGroup.PushTask, EscortGroup:PushTask(
{ EscortGroup:TaskControlled( EscortGroup:TaskControlled(
EscortGroup:TaskOrbitCircle( 1000, 500 ), EscortGroup:TaskOrbitCircle( 1000, 500 ),
EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil )
) ), 1 )
},
1
)
end end
EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient )
if self.EscortMode == ESCORT.MODE.FOLLOW then if self.EscortMode == ESCORT.MODE.FOLLOW then
self.FollowScheduler:Start() self.FollowScheduler:Start( self.FollowSchedule )
end end
end end
@ -844,124 +844,157 @@ function _Resume( EscortGroup )
end end
--- @param #MENUPARAM MenuParam --- @param #ESCORT self
function ESCORT._AttackTarget( MenuParam ) -- @param #number DetectedItemID
function ESCORT:_AttackTarget( DetectedItemID )
local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP
local EscortGroup = self.EscortGroup self:E( EscortGroup )
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT
self.FollowScheduler:Stop() self.FollowScheduler:Stop( self.FollowSchedule )
self:T( AttackUnit )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
EscortGroup:OptionROEOpenFire() EscortGroup:OptionROEOpenFire()
EscortGroup:OptionROTPassiveDefense() EscortGroup:OptionROTPassiveDefense()
EscortGroup:SetState( EscortGroup, "Escort", self ) EscortGroup:SetState( EscortGroup, "Escort", self )
SCHEDULER:New( EscortGroup,
EscortGroup.PushTask, local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
{ EscortGroup:TaskCombo(
{ EscortGroup:TaskAttackUnit( AttackUnit ), local Tasks = {}
EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
} DetectedSet:ForEachUnit(
) --- @param Wrapper.Unit#UNIT DetectedUnit
}, 10 function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit )
end
end, Tasks
)
Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
EscortGroup:SetTask(
EscortGroup:TaskCombo(
Tasks
), 1
) )
else else
SCHEDULER:New( EscortGroup,
EscortGroup.PushTask, local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
{ EscortGroup:TaskCombo(
{ EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) local Tasks = {}
}
) DetectedSet:ForEachUnit(
}, 10 --- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
end
end, Tasks
)
EscortGroup:SetTask(
EscortGroup:TaskCombo(
Tasks
), 1
) )
end end
EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient )
end end
--- @param #MENUPARAM MenuParam ---
function ESCORT._AssistTarget( MenuParam ) -- @param #number DetectedItemID
function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local EscortGroupAttack = MenuParam.ParamEscortGroup
local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT
self.FollowScheduler:Stop() self.FollowScheduler:Stop( self.FollowSchedule )
self:T( AttackUnit )
if EscortGroupAttack:IsAir() then if EscortGroupAttack:IsAir() then
EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROEOpenFire()
EscortGroupAttack:OptionROTVertical() EscortGroupAttack:OptionROTVertical()
SCHDULER:New( EscortGroupAttack,
EscortGroupAttack.PushTask, local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
{ EscortGroupAttack:TaskCombo(
{ EscortGroupAttack:TaskAttackUnit( AttackUnit ), local Tasks = {}
EscortGroupAttack:TaskOrbitCircle( 500, 350 )
} DetectedSet:ForEachUnit(
) --- @param Wrapper.Unit#UNIT DetectedUnit
}, 10 function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit )
end
end, Tasks
)
Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 )
EscortGroupAttack:SetTask(
EscortGroupAttack:TaskCombo(
Tasks
), 1
) )
else else
SCHEDULER:New( EscortGroupAttack, local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
EscortGroupAttack.PushTask,
{ EscortGroupAttack:TaskCombo( local Tasks = {}
{ EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 )
} DetectedSet:ForEachUnit(
) --- @param Wrapper.Unit#UNIT DetectedUnit
}, 10 function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
end
end, Tasks
)
EscortGroupAttack:SetTask(
EscortGroupAttack:TaskCombo(
Tasks
), 1
) )
end end
EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._ROE( MenuParam ) function ESCORT:_ROE( EscortROEFunction, EscortROEMessage )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local EscortROEFunction = MenuParam.ParamFunction
local EscortROEMessage = MenuParam.ParamMessage
pcall( function() EscortROEFunction() end ) pcall( function() EscortROEFunction() end )
EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._ROT( MenuParam ) function ESCORT:_ROT( EscortROTFunction, EscortROTMessage )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local EscortROTFunction = MenuParam.ParamFunction
local EscortROTMessage = MenuParam.ParamMessage
pcall( function() EscortROTFunction() end ) pcall( function() EscortROTFunction() end )
EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient )
end end
--- @param #MENUPARAM MenuParam --- @param #MENUPARAM MenuParam
function ESCORT._ResumeMission( MenuParam ) function ESCORT:_ResumeMission( WayPoint )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient local EscortClient = self.EscortClient
local WayPoint = MenuParam.ParamWayPoint self.FollowScheduler:Stop( self.FollowSchedule )
self.FollowScheduler:Stop()
local WayPoints = EscortGroup:GetTaskRoute() local WayPoints = EscortGroup:GetTaskRoute()
self:T( WayPoint, WayPoints ) self:T( WayPoint, WayPoints )
@ -1105,176 +1138,244 @@ function ESCORT:_ReportTargetsScheduler()
self:F( self.EscortGroup:GetName() ) self:F( self.EscortGroup:GetName() )
if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then
local EscortGroupName = self.EscortGroup:GetName()
local EscortTargets = self.EscortGroup:GetDetectedTargets()
local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets if true then
local EscortTargetMessages = "" local EscortGroupName = self.EscortGroup:GetName()
for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
local EscortObject = EscortTarget.object self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
self:T( EscortObject )
if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
local EscortTargetUnit = UNIT:Find( EscortObject ) if self.EscortMenuTargetAssistance then
local EscortTargetUnitName = EscortTargetUnit:GetName() self.EscortMenuTargetAssistance:RemoveSubMenus()
-- local EscortTargetIsDetected,
-- EscortTargetIsVisible,
-- EscortTargetLastTime,
-- EscortTargetKnowType,
-- EscortTargetKnowDistance,
-- EscortTargetLastPos,
-- EscortTargetLastVelocity
-- = self.EscortGroup:IsTargetDetected( EscortObject )
--
-- self:T( { EscortTargetIsDetected,
-- EscortTargetIsVisible,
-- EscortTargetLastTime,
-- EscortTargetKnowType,
-- EscortTargetKnowDistance,
-- EscortTargetLastPos,
-- EscortTargetLastVelocity } )
local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
local EscortVec3 = self.EscortGroup:GetVec3()
local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
( EscortTargetUnitVec3.z - EscortVec3.z )^2
) ^ 0.5 / 1000
self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
if Distance <= 15 then
if not ClientEscortTargets[EscortTargetUnitName] then
ClientEscortTargets[EscortTargetUnitName] = {}
end
ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
else
if ClientEscortTargets[EscortTargetUnitName] then
ClientEscortTargets[EscortTargetUnitName] = nil
end
end
end end
end
self:T( { "Sorting Targets Table:", ClientEscortTargets } ) local DetectedItems = self.Detection:GetDetectedItems()
table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) self:E( DetectedItems )
self:T( { "Sorted Targets Table:", ClientEscortTargets } )
-- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. local DetectedTargets = false
self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
local DetectedMsgs = {}
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
if self.EscortMenuTargetAssistance then local ClientEscortTargets = EscortGroupData.Detection
self.EscortMenuTargetAssistance:RemoveSubMenus()
end
--for MenuIndex = 1, #self.EscortMenuAttackTargets do for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do
-- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) self:E( { DetectedItemID, DetectedItem } )
-- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
--end
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID )
if ClientEscortGroupName == EscortGroupName then
if ClientEscortTargets then
for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do MENU_CLIENT_COMMAND:New( self.EscortClient,
DetectedItemReportSummary,
if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then self.EscortMenuAttackNearbyTargets,
ESCORT._AttackTarget,
local EscortTargetMessage = "" self,
local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() DetectedItemID
local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() )
if ClientEscortTargetData.type then
EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
else
EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
end
local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
local EscortVec3 = self.EscortGroup:GetVec3()
local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
( EscortTargetUnitVec3.z - EscortVec3.z )^2
) ^ 0.5 / 1000
self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
if ClientEscortTargetData.visible == false then
EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
else
EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
end
if ClientEscortTargetData.visible then
EscortTargetMessage = EscortTargetMessage .. ", visual"
end
if ClientEscortGroupName == EscortGroupName then
MENU_CLIENT_COMMAND:New( self.EscortClient,
EscortTargetMessage,
self.EscortMenuAttackNearbyTargets,
ESCORT._AttackTarget,
{ ParamSelf = self,
ParamUnit = ClientEscortTargetData.AttackUnit
}
)
EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
else
if self.EscortMenuTargetAssistance then
local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
MENU_CLIENT_COMMAND:New( self.EscortClient,
EscortTargetMessage,
MenuTargetAssistance,
ESCORT._AssistTarget,
{ ParamSelf = self,
ParamEscortGroup = EscortGroupData.EscortGroup,
ParamUnit = ClientEscortTargetData.AttackUnit
}
)
end
end
else else
ClientEscortTargetData = nil if self.EscortMenuTargetAssistance then
self:T( DetectedItemReportSummary )
local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
MENU_CLIENT_COMMAND:New( self.EscortClient,
DetectedItemReportSummary,
MenuTargetAssistance,
ESCORT._AssistTarget,
self,
EscortGroupData.EscortGroup,
DetectedItemID
)
end
end end
DetectedTargets = true
end end
end end
self:E( DetectedMsgs )
if EscortTargetMessages ~= "" and self.ReportTargets == true then if DetectedTargets then
self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
else else
self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient )
end end
return true
else
-- local EscortGroupName = self.EscortGroup:GetName()
-- local EscortTargets = self.EscortGroup:GetDetectedTargets()
--
-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets
--
-- local EscortTargetMessages = ""
-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
-- local EscortObject = EscortTarget.object
-- self:T( EscortObject )
-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
--
-- local EscortTargetUnit = UNIT:Find( EscortObject )
-- local EscortTargetUnitName = EscortTargetUnit:GetName()
--
--
--
-- -- local EscortTargetIsDetected,
-- -- EscortTargetIsVisible,
-- -- EscortTargetLastTime,
-- -- EscortTargetKnowType,
-- -- EscortTargetKnowDistance,
-- -- EscortTargetLastPos,
-- -- EscortTargetLastVelocity
-- -- = self.EscortGroup:IsTargetDetected( EscortObject )
-- --
-- -- self:T( { EscortTargetIsDetected,
-- -- EscortTargetIsVisible,
-- -- EscortTargetLastTime,
-- -- EscortTargetKnowType,
-- -- EscortTargetKnowDistance,
-- -- EscortTargetLastPos,
-- -- EscortTargetLastVelocity } )
--
--
-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
--
-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
--
-- if Distance <= 15 then
--
-- if not ClientEscortTargets[EscortTargetUnitName] then
-- ClientEscortTargets[EscortTargetUnitName] = {}
-- end
-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
-- else
-- if ClientEscortTargets[EscortTargetUnitName] then
-- ClientEscortTargets[EscortTargetUnitName] = nil
-- end
-- end
-- end
-- end
--
-- self:T( { "Sorting Targets Table:", ClientEscortTargets } )
-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end )
-- self:T( { "Sorted Targets Table:", ClientEscortTargets } )
--
-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
--
-- if self.EscortMenuTargetAssistance then
-- self.EscortMenuTargetAssistance:RemoveSubMenus()
-- end
--
-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do
-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } )
-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove()
-- --end
--
--
-- if ClientEscortTargets then
-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do
--
-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
--
-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then
--
-- local EscortTargetMessage = ""
-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName()
-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName()
-- if ClientEscortTargetData.type then
-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
-- else
-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
-- end
--
-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
--
-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
-- if ClientEscortTargetData.visible == false then
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
-- else
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
-- end
--
-- if ClientEscortTargetData.visible then
-- EscortTargetMessage = EscortTargetMessage .. ", visual"
-- end
--
-- if ClientEscortGroupName == EscortGroupName then
--
-- MENU_CLIENT_COMMAND:New( self.EscortClient,
-- EscortTargetMessage,
-- self.EscortMenuAttackNearbyTargets,
-- ESCORT._AttackTarget,
-- { ParamSelf = self,
-- ParamUnit = ClientEscortTargetData.AttackUnit
-- }
-- )
-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
-- else
-- if self.EscortMenuTargetAssistance then
-- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
-- MENU_CLIENT_COMMAND:New( self.EscortClient,
-- EscortTargetMessage,
-- MenuTargetAssistance,
-- ESCORT._AssistTarget,
-- self,
-- EscortGroupData.EscortGroup,
-- ClientEscortTargetData.AttackUnit
-- )
-- end
-- end
-- else
-- ClientEscortTargetData = nil
-- end
-- end
-- end
--
-- if EscortTargetMessages ~= "" and self.ReportTargets == true then
-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient )
-- else
-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient )
-- end
-- end
--
-- if self.EscortMenuResumeMission then
-- self.EscortMenuResumeMission:RemoveSubMenus()
--
-- -- if self.EscortMenuResumeWayPoints then
-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
-- -- end
-- -- end
--
-- local TaskPoints = self:RegisterRoute()
-- for WayPointID, WayPoint in pairs( TaskPoints ) do
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
-- ( WayPoint.y - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
-- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
-- end
-- end
--
-- return true
end end
if self.EscortMenuResumeMission then
self.EscortMenuResumeMission:RemoveSubMenus()
-- if self.EscortMenuResumeWayPoints then
-- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
-- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
-- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
-- end
-- end
local TaskPoints = self:RegisterRoute()
for WayPointID, WayPoint in pairs( TaskPoints ) do
local EscortVec3 = self.EscortGroup:GetVec3()
local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
( WayPoint.y - EscortVec3.z )^2
) ^ 0.5 / 1000
MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
end
end
return true
end end
return false return false

View File

@ -171,7 +171,7 @@ function MISSILETRAINER:New( Distance, Briefing )
self.Distance = Distance / 1000 self.Distance = Distance / 1000
_EVENTDISPATCHER:OnShot( self._EventShot, self ) self:HandleEvent( EVENTS.Shot )
self.DBClients = SET_CLIENT:New():FilterStart() self.DBClients = SET_CLIENT:New():FilterStart()
@ -449,14 +449,14 @@ end
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @param #MISSILETRAINER self -- @param #MISSILETRAINER self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA EventData
function MISSILETRAINER:_EventShot( Event ) function MISSILETRAINER:OnEventShot( EVentData )
self:F( { Event } ) self:F( { EVentData } )
local TrainerSourceDCSUnit = Event.IniDCSUnit local TrainerSourceDCSUnit = EVentData.IniDCSUnit
local TrainerSourceDCSUnitName = Event.IniDCSUnitName local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName
local TrainerWeapon = Event.Weapon -- Identify the weapon fired local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired
local TrainerWeaponName = Event.WeaponName -- return weapon type local TrainerWeaponName = EVentData.WeaponName -- return weapon type
self:T( "Missile Launched = " .. TrainerWeaponName ) self:T( "Missile Launched = " .. TrainerWeaponName )

View File

@ -3,10 +3,11 @@
-- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if -- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if
-- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units
-- on defined intervals (currently every minute). -- on defined intervals (currently every minute).
-- @module MOVEMENT -- @module Movement
--- the MOVEMENT class --- the MOVEMENT class
-- @type -- @type MOVEMENT
-- @extends Core.Base#BASE
MOVEMENT = { MOVEMENT = {
ClassName = "MOVEMENT", ClassName = "MOVEMENT",
} }
@ -20,7 +21,7 @@ MOVEMENT = {
-- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) -- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 )
function MOVEMENT:New( MovePrefixes, MoveMaximum ) function MOVEMENT:New( MovePrefixes, MoveMaximum )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT
self:F( { MovePrefixes, MoveMaximum } ) self:F( { MovePrefixes, MoveMaximum } )
if type( MovePrefixes ) == 'table' then if type( MovePrefixes ) == 'table' then
@ -33,7 +34,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum )
self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.AliveUnits = 0 -- Contains the counter how many units are currently alive
self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not.
_EVENTDISPATCHER:OnBirth( self.OnBirth, self ) self:HandleEvent( EVENTS.Birth )
-- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) -- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth )
-- --
@ -60,24 +61,26 @@ end
--- Captures the birth events when new Units were spawned. --- Captures the birth events when new Units were spawned.
-- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration.
function MOVEMENT:OnBirth( Event ) -- @param #MOVEMENT self
self:F( { Event } ) -- @param Core.Event#EVENTDATA self
function MOVEMENT:OnEventBirth( EventData )
self:F( { EventData } )
if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line
if Event.IniDCSUnit then if EventData.IniDCSUnit then
self:T( "Birth object : " .. Event.IniDCSUnitName ) self:T( "Birth object : " .. EventData.IniDCSUnitName )
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then
for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do
if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then
self.AliveUnits = self.AliveUnits + 1 self.AliveUnits = self.AliveUnits + 1
self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName
self:T( self.AliveUnits ) self:T( self.AliveUnits )
end end
end end
end end
end end
_EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self )
_EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash )
end end
end end

View File

@ -1022,9 +1022,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
end end
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else else
local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel()
local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1
local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 )

View File

@ -36,25 +36,28 @@ function SEAD:New( SEADGroupPrefixes )
else else
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
end end
_EVENTDISPATCHER:OnShot( self.EventShot, self )
self:HandleEvent( EVENTS.Shot )
return self return self
end end
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @see SEAD -- @see SEAD
function SEAD:EventShot( Event ) -- @param #SEAD
self:F( { Event } ) -- @param Core.Event#EVENTDATA EventData
function SEAD:OnEventShot( EventData )
self:F( { EventData } )
local SEADUnit = Event.IniDCSUnit local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = Event.IniDCSUnitName local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = Event.Weapon -- Identify the weapon fired local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = Event.WeaponName -- return weapon type local SEADWeaponName = EventData.WeaponName -- return weapon type
-- Start of the 2nd loop -- Start of the 2nd loop
self:T( "Missile Launched = " .. SEADWeaponName ) self:T( "Missile Launched = " .. SEADWeaponName )
if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
local _evade = math.random (1,100) -- random number for chance of evading action local _evade = math.random (1,100) -- random number for chance of evading action
local _targetMim = Event.Weapon:getTarget() -- Identify target local _targetMim = EventData.Weapon:getTarget() -- Identify target
local _targetMimname = Unit.getName(_targetMim) local _targetMimname = Unit.getName(_targetMim)
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
local _targetMimgroupName = _targetMimgroup:getName() local _targetMimgroupName = _targetMimgroup:getName()

View File

@ -59,7 +59,7 @@ Include.File( "Tasking/CommandCenter" )
Include.File( "Tasking/Mission" ) Include.File( "Tasking/Mission" )
Include.File( "Tasking/Task" ) Include.File( "Tasking/Task" )
Include.File( "Tasking/DetectionManager" ) Include.File( "Tasking/DetectionManager" )
Include.File( "Tasking/Task_SEAD" ) Include.File( "Tasking/Task_A2G_Dispatcher")
Include.File( "Tasking/Task_A2G" ) Include.File( "Tasking/Task_A2G" )

View File

@ -20,7 +20,9 @@ function REPORT:New( Title )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self.Report = {} self.Report = {}
self.Report[#self.Report+1] = Title if Title then
self.Report[#self.Report+1] = Title
end
return self return self
end end
@ -31,7 +33,7 @@ end
-- @return #REPORT -- @return #REPORT
function REPORT:Add( Text ) function REPORT:Add( Text )
self.Report[#self.Report+1] = Text self.Report[#self.Report+1] = Text
return self.Report[#self.Report+1] return self.Report[#self.Report]
end end
function REPORT:Text() function REPORT:Text()
@ -68,22 +70,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
self:HandleEvent( EVENTS.Birth, self:HandleEvent( EVENTS.Birth,
--- @param #COMMANDCENTER self --- @param #COMMANDCENTER self
--- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function( self, EventData ) function( self, EventData )
self:E( { EventData } ) if EventData.IniObjectCategory == 1 then
local EventGroup = GROUP:Find( EventData.IniDCSGroup ) local EventGroup = GROUP:Find( EventData.IniDCSGroup )
if EventGroup and self:HasGroup( EventGroup ) then if EventGroup and self:HasGroup( EventGroup ) then
local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu )
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup )
local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup )
self:ReportSummary( EventGroup ) self:ReportSummary( EventGroup )
end end
local PlayerUnit = EventData.IniUnit local PlayerUnit = EventData.IniUnit
for MissionID, Mission in pairs( self:GetMissions() ) do for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION local Mission = Mission -- Tasking.Mission#MISSION
local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled!
Mission:JoinUnit( PlayerUnit, PlayerGroup ) Mission:JoinUnit( PlayerUnit, PlayerGroup )
Mission:ReportDetails() Mission:ReportDetails()
end
end end
end end
@ -193,17 +196,26 @@ function COMMANDCENTER:SetMenu()
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" )
local MenuTime = timer.getTime()
for MissionID, Mission in pairs( self:GetMissions() ) do for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu() Mission:SetMenu( MenuTime )
end
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu( MenuTime )
end end
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:SetMenu()
end
end end
--- Gets the commandcenter menu structure governed by the HQ command center.
-- @param #COMMANDCENTER self
-- @return Core.Menu#MENU_COALITION
function COMMANDCENTER:GetMenu()
self:F()
return self.CommandCenterMenu
end
--- Checks of the COMMANDCENTER has a GROUP. --- Checks of the COMMANDCENTER has a GROUP.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self
@ -224,6 +236,14 @@ function COMMANDCENTER:HasGroup( MissionGroup )
return Has return Has
end end
--- Send a CC message to the coalition of the CC.
-- @param #COMMANDCENTER self
function COMMANDCENTER:MessageToAll( Message )
self:GetPositionable():MessageToAll( Message, 20, self:GetName() )
end
--- Send a CC message to a GROUP. --- Send a CC message to a GROUP.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self
-- @param #string Message -- @param #string Message
@ -231,7 +251,8 @@ end
-- @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. -- @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 ) function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name )
local Prefix = Name and "@ Group (" .. Name .. "): " or '' local Prefix = "@ Group"
Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' )
Message = Prefix .. Message Message = Prefix .. Message
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() )
@ -247,6 +268,7 @@ function COMMANDCENTER:MessageToCoalition( Message )
end end
--- Report the status of all MISSIONs to a GROUP. --- Report the status of all MISSIONs to a GROUP.
-- Each Mission is listed, with an indication how many Tasks are still to be completed. -- Each Mission is listed, with an indication how many Tasks are still to be completed.
-- @param #COMMANDCENTER self -- @param #COMMANDCENTER self

View File

@ -34,23 +34,6 @@
-- ------------------------------- -- -------------------------------
-- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. -- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance.
-- --
-- ===
--
-- 3) @{#DETECTION_DISPATCHER} class, extends @{#DETECTION_MANAGER}
-- ================================================================
-- The @{#DETECTION_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups).
-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
-- Find a summary below describing for which situation a task type is created:
--
-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
--
-- Other task types will follow...
--
-- 3.1) DETECTION_DISPATCHER constructor:
-- --------------------------------------
-- The @{#DETECTION_DISPATCHER.New}() method creates a new DETECTION_DISPATCHER instance.
-- --
-- === -- ===
-- --
@ -88,6 +71,8 @@ do -- DETECTION MANAGER
self:SetReportInterval( 30 ) self:SetReportInterval( 30 )
self:SetReportDisplayTime( 25 ) self:SetReportDisplayTime( 25 )
Detection:__Start( 5 )
return self return self
end end
@ -250,259 +235,3 @@ do -- DETECTION_REPORTING
end end
do -- DETECTION_DISPATCHER
--- DETECTION_DISPATCHER class.
-- @type DETECTION_DISPATCHER
-- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to.
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
-- @field Tasking.Mission#MISSION Mission
-- @field Wrapper.Group#GROUP CommandCenter
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
DETECTION_DISPATCHER = {
ClassName = "DETECTION_DISPATCHER",
Mission = nil,
CommandCenter = nil,
Detection = nil,
}
--- DETECTION_DISPATCHER constructor.
-- @param #DETECTION_DISPATCHER self
-- @param Set#SET_GROUP SetGroup
-- @param Functional.Detection#DETECTION_BASE Detection
-- @return #DETECTION_DISPATCHER self
function DETECTION_DISPATCHER:New( Mission, CommandCenter, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_DISPATCHER
self.Detection = Detection
self.CommandCenter = CommandCenter
self.Mission = Mission
self:Schedule( 30 )
return self
end
--- Creates a SEAD task when there are targets for it.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea )
self:F( { DetectedArea.AreaID } )
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local RadarCount = DetectedSet:HasSEAD()
if RadarCount > 0 then
-- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it.
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterHasSEAD()
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a CAS task when there are targets for it.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Tasking.Task#TASK
function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea )
self:F( { DetectedArea.AreaID } )
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea )
if GroundUnitCount > 0 and FriendliesNearBy == true then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a BAI task when there are targets for it.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Tasking.Task#TASK
function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition )
self:F( { DetectedArea.AreaID } )
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea )
if GroundUnitCount > 0 and FriendliesNearBy == false then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedArea is Changed AND the state of the Task is "Planned".
-- @param #DETECTION_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission
-- @param Tasking.Task#TASK Task
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Tasking.Task#TASK
function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea )
if Task then
if Task:IsStatePlanned() and DetectedArea.Changed == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Task = Mission:RemoveTask( Task )
end
end
return Task
end
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_AREAS} object.
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function DETECTION_DISPATCHER:ProcessDetected( Detection )
self:F2()
local AreaMsg = {}
local TaskMsg = {}
local ChangeMsg = {}
local Mission = self.Mission
--- First we need to the detected targets.
for DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do
local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
self:E( { "Targets in DetectedArea", DetectedArea.AreaID, DetectedSet:Count(), tostring( DetectedArea ) } )
DetectedSet:Flush()
local AreaID = DetectedArea.AreaID
-- Evaluate SEAD Tasking
local SEADTask = Mission:GetTask( "SEAD." .. AreaID )
SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea )
if not SEADTask then
local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
SEADTask = Mission:AddTask( TASK_SEAD:New( Mission, self.SetGroup, "SEAD." .. AreaID, TargetSetUnit , DetectedZone ) )
end
end
if SEADTask and SEADTask:IsStatePlanned() then
self:E( "Planned" )
--SEADTask:SetPlannedMenu()
TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText()
end
-- Evaluate CAS Tasking
local CASTask = Mission:GetTask( "CAS." .. AreaID )
CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea )
if not CASTask then
local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
CASTask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "CAS." .. AreaID, "CAS", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) )
end
end
if CASTask and CASTask:IsStatePlanned() then
--CASTask:SetPlannedMenu()
TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText()
end
-- Evaluate BAI Tasking
local BAITask = Mission:GetTask( "BAI." .. AreaID )
BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea )
if not BAITask then
local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
BAITask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "BAI." .. AreaID, "BAI", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) )
end
end
if BAITask and BAITask:IsStatePlanned() then
--BAITask:SetPlannedMenu()
TaskMsg[#TaskMsg+1] = " - " .. BAITask:GetStateString() .. " BAI " .. AreaID .. " - " .. BAITask.TargetSetUnit:GetUnitTypesText()
end
if #TaskMsg > 0 then
local ThreatLevel = Detection:GetTreatLevelA2G( DetectedArea )
local DetectedAreaVec3 = DetectedZone:GetVec3()
local DetectedAreaPointVec3 = POINT_VEC3:New( DetectedAreaVec3.x, DetectedAreaVec3.y, DetectedAreaVec3.z )
local DetectedAreaPointLL = DetectedAreaPointVec3:ToStringLL( 3, true )
AreaMsg[#AreaMsg+1] = string.format( " - Area #%d - %s - Threat Level [%s] (%2d)",
DetectedAreaID,
DetectedAreaPointLL,
string.rep( "", ThreatLevel ),
ThreatLevel
)
-- Loop through the changes ...
local ChangeText = Detection:GetChangeText( DetectedArea )
if ChangeText ~= "" then
ChangeMsg[#ChangeMsg+1] = string.gsub( string.gsub( ChangeText, "\n", "%1 - " ), "^.", " - %1" )
end
end
-- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedArea )
end
-- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu()
if #AreaMsg > 0 then
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if not TaskGroup:GetState( TaskGroup, "Assigned" ) then
self.CommandCenter:MessageToGroup(
string.format( "HQ Reporting - Target areas for mission '%s':\nAreas:\n%s\n\nTasks:\n%s\n\nChanges:\n%s ",
self.Mission:GetName(),
table.concat( AreaMsg, "\n" ),
table.concat( TaskMsg, "\n" ),
table.concat( ChangeMsg, "\n" )
), self:GetReportDisplayTime(), TaskGroup
)
end
end
end
return true
end
end

View File

@ -12,22 +12,6 @@ MISSION = {
ClassName = "MISSION", ClassName = "MISSION",
Name = "", Name = "",
MissionStatus = "PENDING", MissionStatus = "PENDING",
_Clients = {},
TaskMenus = {},
TaskCategoryMenus = {},
TaskTypeMenus = {},
_ActiveTasks = {},
GoalFunction = nil,
MissionReportTrigger = 0,
MissionProgressTrigger = 0,
MissionReportShow = false,
MissionReportFlash = false,
MissionTimeInterval = 0,
MissionCoalition = "",
SUCCESS = 1,
FAILED = 2,
REPEAT = 3,
_GoalTasks = {}
} }
--- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc.
@ -45,10 +29,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi
self:SetStartState( "Idle" ) self:SetStartState( "Idle" )
self:AddTransition( "Idle", "Start", "Ongoing" ) self:AddTransition( "Idle", "Start", "Ongoing" )
--- OnLeave Transition Handler for State Idle.
-- @function [parent=#MISSION] OnLeaveIdle
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Idle.
-- @function [parent=#MISSION] OnEnterIdle
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnLeave Transition Handler for State Ongoing.
-- @function [parent=#MISSION] OnLeaveOngoing
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Ongoing.
-- @function [parent=#MISSION] OnEnterOngoing
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Start.
-- @function [parent=#MISSION] OnBeforeStart
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Start.
-- @function [parent=#MISSION] OnAfterStart
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Start.
-- @function [parent=#MISSION] Start
-- @param #MISSION self
--- Asynchronous Event Trigger for Event Start.
-- @function [parent=#MISSION] __Start
-- @param #MISSION self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Ongoing", "Stop", "Idle" ) self:AddTransition( "Ongoing", "Stop", "Idle" )
--- OnLeave Transition Handler for State Idle.
-- @function [parent=#MISSION] OnLeaveIdle
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Idle.
-- @function [parent=#MISSION] OnEnterIdle
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Stop.
-- @function [parent=#MISSION] OnBeforeStop
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#MISSION] OnAfterStop
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Stop.
-- @function [parent=#MISSION] Stop
-- @param #MISSION self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#MISSION] __Stop
-- @param #MISSION self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Ongoing", "Complete", "Completed" ) self:AddTransition( "Ongoing", "Complete", "Completed" )
--- OnLeave Transition Handler for State Completed.
-- @function [parent=#MISSION] OnLeaveCompleted
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Completed.
-- @function [parent=#MISSION] OnEnterCompleted
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Complete.
-- @function [parent=#MISSION] OnBeforeComplete
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Complete.
-- @function [parent=#MISSION] OnAfterComplete
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Complete.
-- @function [parent=#MISSION] Complete
-- @param #MISSION self
--- Asynchronous Event Trigger for Event Complete.
-- @function [parent=#MISSION] __Complete
-- @param #MISSION self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Fail", "Failed" ) self:AddTransition( "*", "Fail", "Failed" )
--- OnLeave Transition Handler for State Failed.
-- @function [parent=#MISSION] OnLeaveFailed
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Failed.
-- @function [parent=#MISSION] OnEnterFailed
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Fail.
-- @function [parent=#MISSION] OnBeforeFail
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fail.
-- @function [parent=#MISSION] OnAfterFail
-- @param #MISSION self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fail.
-- @function [parent=#MISSION] Fail
-- @param #MISSION self
--- Asynchronous Event Trigger for Event Fail.
-- @function [parent=#MISSION] __Fail
-- @param #MISSION self
-- @param #number Delay The delay in seconds.
self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } )
self.CommandCenter = CommandCenter self.CommandCenter = CommandCenter
@ -60,14 +218,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi
self.MissionCoalition = MissionCoalition self.MissionCoalition = MissionCoalition
self.Tasks = {} self.Tasks = {}
-- Private implementations
return self return self
end end
--- FSM function for a MISSION -- FSM function for a MISSION
-- @param #MISSION self -- @param #MISSION self
-- @param #string Event
-- @param #string From -- @param #string From
-- @param #string Event
-- @param #string To -- @param #string To
function MISSION:onbeforeComplete( From, Event, To ) function MISSION:onbeforeComplete( From, Event, To )
@ -80,10 +242,10 @@ function MISSION:onbeforeComplete( From, Event, To )
return true -- Allow Mission completion. return true -- Allow Mission completion.
end end
--- FSM function for a MISSION -- FSM function for a MISSION
-- @param #MISSION self -- @param #MISSION self
-- @param #string Event
-- @param #string From -- @param #string From
-- @param #string Event
-- @param #string To -- @param #string To
function MISSION:onenterCompleted( From, Event, To ) function MISSION:onenterCompleted( From, Event, To )
@ -201,23 +363,25 @@ end
--- Sets the Planned Task menu. --- Sets the Planned Task menu.
-- @param #MISSION self -- @param #MISSION self
function MISSION:SetMenu() -- @param #number MenuTime
function MISSION:SetMenu( MenuTime )
self:F() self:F()
for _, Task in pairs( self:GetTasks() ) do for _, TaskData in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK local Task = TaskData -- Tasking.Task#TASK
Task:SetMenu() Task:SetMenu( MenuTime )
end end
end end
--- Removes the Planned Task menu. --- Removes the Planned Task menu.
-- @param #MISSION self -- @param #MISSION self
function MISSION:RemoveMenu() -- @param #number MenuTime
function MISSION:RemoveMenu( MenuTime )
self:F() self:F()
for _, Task in pairs( self:GetTasks() ) do for _, Task in pairs( self:GetTasks() ) do
local Task = Task -- Tasking.Task#TASK local Task = Task -- Tasking.Task#TASK
Task:RemoveMenu() Task:RemoveMenu( MenuTime )
end end
end end
@ -229,20 +393,6 @@ function MISSION:GetCommandCenter()
return self.CommandCenter return self.CommandCenter
end end
--- Sets the Assigned Task menu.
-- @param #MISSION self
-- @param Tasking.Task#TASK Task
-- @param #string MenuText The menu text.
-- @return #MISSION self
function MISSION:SetAssignedMenu( Task )
for _, Task in pairs( self.Tasks ) do
local Task = Task -- Tasking.Task#TASK
Task:RemoveMenu()
Task:SetAssignedMenu()
end
end
--- Removes a Task menu. --- Removes a Task menu.
-- @param #MISSION self -- @param #MISSION self
@ -258,28 +408,18 @@ end
-- @param #MISSION self -- @param #MISSION self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
-- @return Core.Menu#MENU_COALITION self -- @return Core.Menu#MENU_COALITION self
function MISSION:GetMissionMenu( TaskGroup ) function MISSION:GetMenu( TaskGroup )
local CommandCenter = self:GetCommandCenter() local CommandCenter = self:GetCommandCenter()
local CommandCenterMenu = CommandCenter.CommandCenterMenu local CommandCenterMenu = CommandCenter:GetMenu()
local MissionName = self:GetName() local MissionName = self:GetName()
local MissionMenu = CommandCenterMenu:GetMenu( MissionName )
local TaskGroupName = TaskGroup:GetName()
local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu )
return MissionMenu return MissionMenu
end end
--- Clears the mission menu for the coalition.
-- @param #MISSION self
-- @return #MISSION self
function MISSION:ClearMissionMenu()
self.MissionMenu:Remove()
self.MissionMenu = nil
end
--- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. --- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions.
-- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @param #string TaskName The Name of the @{Task} within the @{Mission}.
-- @return Tasking.Task#TASK The Task -- @return Tasking.Task#TASK The Task
@ -350,76 +490,44 @@ function MISSION:GetNextTaskID( Task )
return self.Tasks[TaskName].n return self.Tasks[TaskName].n
end end
--- Is the @{Mission} **Completed**.
-- @param #MISSION self
--- old stuff -- @return #boolean
--- Returns if a Mission has completed.
-- @return bool
function MISSION:IsCompleted() function MISSION:IsCompleted()
self:F() return self:Is( "Completed" )
return self.MissionStatus == "ACCOMPLISHED"
end end
--- Set a Mission to completed. --- Is the @{Mission} **Idle**.
function MISSION:Completed() -- @param #MISSION self
self:F() -- @return #boolean
self.MissionStatus = "ACCOMPLISHED" function MISSION:IsIdle()
self:StatusToClients() return self:Is( "Idle" )
end end
--- Returns if a Mission is ongoing. --- Is the @{Mission} **Ongoing**.
-- treturn bool -- @param #MISSION self
-- @return #boolean
function MISSION:IsOngoing() function MISSION:IsOngoing()
self:F() return self:Is( "Ongoing" )
return self.MissionStatus == "ONGOING"
end end
--- Set a Mission to ongoing. --- Is the @{Mission} **Failed**.
function MISSION:Ongoing() -- @param #MISSION self
self:F() -- @return #boolean
self.MissionStatus = "ONGOING" function MISSION:IsFailed()
--self:StatusToClients() return self:Is( "Failed" )
end end
--- Returns if a Mission is pending. --- Is the @{Mission} **Hold**.
-- treturn bool -- @param #MISSION self
function MISSION:IsPending() -- @return #boolean
self:F() function MISSION:IsHold()
return self.MissionStatus == "PENDING" return self:Is( "Hold" )
end
--- Set a Mission to pending.
function MISSION:Pending()
self:F()
self.MissionStatus = "PENDING"
self:StatusToClients()
end
--- Returns if a Mission has failed.
-- treturn bool
function MISSION:IsFailed()
self:F()
return self.MissionStatus == "FAILED"
end
--- Set a Mission to failed.
function MISSION:Failed()
self:F()
self.MissionStatus = "FAILED"
self:StatusToClients()
end
--- Send the status of the MISSION to all Clients.
function MISSION:StatusToClients()
self:F()
if self.MissionReportFlash then
for ClientID, Client in pairs( self._Clients ) do
Client:Message( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. '! ( ' .. self.MissionPriority .. ' mission ) ', 10, "Mission Command: Mission Status")
end
end
end end
--- Validates if the Mission has a Group
-- @param #MISSION
-- @return #boolean true if the Mission has a Group.
function MISSION:HasGroup( TaskGroup ) function MISSION:HasGroup( TaskGroup )
local Has = false local Has = false
@ -512,107 +620,6 @@ function MISSION:ReportDetails()
return Report:Text() return Report:Text()
end end
--- Report the status of all MISSIONs to all active Clients.
function MISSION:ReportToAll()
self:F()
local AlivePlayers = ''
for ClientID, Client in pairs( self._Clients ) do
if Client:GetDCSGroup() then
if Client:GetClientGroupDCSUnit() then
if Client:GetClientGroupDCSUnit():getLife() > 0.0 then
if AlivePlayers == '' then
AlivePlayers = ' Players: ' .. Client:GetClientGroupDCSUnit():getPlayerName()
else
AlivePlayers = AlivePlayers .. ' / ' .. Client:GetClientGroupDCSUnit():getPlayerName()
end
end
end
end
end
local Tasks = self:GetTasks()
local TaskText = ""
for TaskID, TaskData in pairs( Tasks ) do
TaskText = TaskText .. " - Task " .. TaskID .. ": " .. TaskData.Name .. ": " .. TaskData:GetGoalProgress() .. "\n"
end
MESSAGE:New( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. ' ( ' .. self.MissionPriority .. ' mission )' .. AlivePlayers .. "\n" .. TaskText:gsub("\n$",""), 10, "Mission Command: Mission Report" ):ToAll()
end
--- Add a goal function to a MISSION. Goal functions are called when a @{TASK} within a mission has been completed.
-- @param function GoalFunction is the function defined by the mission designer to evaluate whether a certain goal has been reached after a @{TASK} finishes within the @{MISSION}. A GoalFunction must accept 2 parameters: Mission, Client, which contains the current MISSION object and the current CLIENT object respectively.
-- @usage
-- PatriotActivation = {
-- { "US SAM Patriot Zerti", false },
-- { "US SAM Patriot Zegduleti", false },
-- { "US SAM Patriot Gvleti", false }
-- }
--
-- function DeployPatriotTroopsGoal( Mission, Client )
--
--
-- -- Check if the cargo is all deployed for mission success.
-- for CargoID, CargoData in pairs( Mission._Cargos ) do
-- if Group.getByName( CargoData.CargoGroupName ) then
-- CargoGroup = Group.getByName( CargoData.CargoGroupName )
-- if CargoGroup then
-- -- Check if the cargo is ready to activate
-- CurrentLandingZoneID = routines.IsUnitInZones( CargoGroup:getUnits()[1], Mission:GetTask( 2 ).LandingZones ) -- The second task is the Deploytask to measure mission success upon
-- if CurrentLandingZoneID then
-- if PatriotActivation[CurrentLandingZoneID][2] == false then
-- -- Now check if this is a new Mission Task to be completed...
-- trigger.action.setGroupAIOn( Group.getByName( PatriotActivation[CurrentLandingZoneID][1] ) )
-- PatriotActivation[CurrentLandingZoneID][2] = true
-- MessageToBlue( "Mission Command: Message to all airborne units! The " .. PatriotActivation[CurrentLandingZoneID][1] .. " is armed. Our air defenses are now stronger.", 60, "BLUE/PatriotDefense" )
-- MessageToRed( "Mission Command: Our satellite systems are detecting additional NATO air defenses. To all airborne units: Take care!!!", 60, "RED/PatriotDefense" )
-- Mission:GetTask( 2 ):AddGoalCompletion( "Patriots activated", PatriotActivation[CurrentLandingZoneID][1], 1 ) -- Register Patriot activation as part of mission goal.
-- end
-- end
-- end
-- end
-- end
-- end
--
-- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' )
-- Mission:AddGoalFunction( DeployPatriotTroopsGoal )
function MISSION:AddGoalFunction( GoalFunction )
self:F()
self.GoalFunction = GoalFunction
end
--- Register a new @{CLIENT} to participate within the mission.
-- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}.
-- @return CLIENT
-- @usage
-- Add a number of Client objects to the Mission.
-- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 1', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 3', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
function MISSION:AddClient( Client )
self:F( { Client } )
local Valid = true
if Valid then
self._Clients[Client.ClientName] = Client
end
return Client
end
--- Find a @{CLIENT} object within the @{MISSION} by its ClientName.
-- @param CLIENT ClientName is a string defining the Client Group as defined within the ME.
-- @return CLIENT
-- @usage
-- -- Seach for Client "Bomber" within the Mission.
-- local BomberClient = Mission:FindClient( "Bomber" )
function MISSION:FindClient( ClientName )
self:F( { self._Clients[ClientName] } )
return self._Clients[ClientName]
end
--- Get all the TASKs from the Mission. This function is useful in GoalFunctions. --- Get all the TASKs from the Mission. This function is useful in GoalFunctions.
-- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key.
-- @usage -- @usage
@ -626,330 +633,3 @@ function MISSION:GetTasks()
end end
--[[
_TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing.
- _TransportExecuteStage.EXECUTING
- _TransportExecuteStage.SUCCESS
- _TransportExecuteStage.FAILED
--]]
_TransportExecuteStage = {
NONE = 0,
EXECUTING = 1,
SUCCESS = 2,
FAILED = 3
}
--- The MISSIONSCHEDULER is an OBJECT and is the main scheduler of ALL active MISSIONs registered within this scheduler. It's workings are considered internal and is automatically created when the Mission.lua file is included.
-- @type MISSIONSCHEDULER
-- @field #MISSIONSCHEDULER.MISSIONS Missions
MISSIONSCHEDULER = {
Missions = {},
MissionCount = 0,
TimeIntervalCount = 0,
TimeIntervalShow = 150,
TimeSeconds = 14400,
TimeShow = 5
}
--- @type MISSIONSCHEDULER.MISSIONS
-- @list <#MISSION> Mission
--- This is the main MISSIONSCHEDULER Scheduler function. It is considered internal and is automatically created when the Mission.lua file is included.
function MISSIONSCHEDULER.Scheduler()
-- loop through the missions in the TransportTasks
for MissionName, MissionData in pairs( MISSIONSCHEDULER.Missions ) do
local Mission = MissionData -- #MISSION
if not Mission:IsCompleted() then
-- This flag will monitor if for this mission, there are clients alive. If this flag is still false at the end of the loop, the mission status will be set to Pending (if not Failed or Completed).
local ClientsAlive = false
for ClientID, ClientData in pairs( Mission._Clients ) do
local Client = ClientData -- Wrapper.Client#CLIENT
if Client:IsAlive() then
-- There is at least one Client that is alive... So the Mission status is set to Ongoing.
ClientsAlive = true
-- If this Client was not registered as Alive before:
-- 1. We register the Client as Alive.
-- 2. We initialize the Client Tasks and make a link to the original Mission Task.
-- 3. We initialize the Cargos.
-- 4. We flag the Mission as Ongoing.
if not Client.ClientAlive then
Client.ClientAlive = true
Client.ClientBriefingShown = false
for TaskNumber, Task in pairs( Mission._Tasks ) do
-- Note that this a deepCopy. Each client must have their own Tasks with own Stages!!!
Client._Tasks[TaskNumber] = routines.utils.deepCopy( Mission._Tasks[TaskNumber] )
-- Each MissionTask must point to the original Mission.
Client._Tasks[TaskNumber].MissionTask = Mission._Tasks[TaskNumber]
Client._Tasks[TaskNumber].Cargos = Mission._Tasks[TaskNumber].Cargos
Client._Tasks[TaskNumber].LandingZones = Mission._Tasks[TaskNumber].LandingZones
end
Mission:Ongoing()
end
-- For each Client, check for each Task the state and evolve the mission.
-- This flag will indicate if the Task of the Client is Complete.
local TaskComplete = false
for TaskNumber, Task in pairs( Client._Tasks ) do
if not Task.Stage then
Task:SetStage( 1 )
end
local TransportTime = timer.getTime()
if not Task:IsDone() then
if Task:Goal() then
Task:ShowGoalProgress( Mission, Client )
end
--env.info( 'Scheduler: Mission = ' .. Mission.Name .. ' / Client = ' .. Client.ClientName .. ' / Task = ' .. Task.Name .. ' / Stage = ' .. Task.ActiveStage .. ' - ' .. Task.Stage.Name .. ' - ' .. Task.Stage.StageType )
-- Action
if Task:StageExecute() then
Task.Stage:Execute( Mission, Client, Task )
end
-- Wait until execution is finished
if Task.ExecuteStage == _TransportExecuteStage.EXECUTING then
Task.Stage:Executing( Mission, Client, Task )
end
-- Validate completion or reverse to earlier stage
if Task.Time + Task.Stage.WaitTime <= TransportTime then
Task:SetStage( Task.Stage:Validate( Mission, Client, Task ) )
end
if Task:IsDone() then
--env.info( 'Scheduler: Mission '.. Mission.Name .. ' Task ' .. Task.Name .. ' Stage ' .. Task.Stage.Name .. ' done. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) )
TaskComplete = true -- when a task is not yet completed, a mission cannot be completed
else
-- break only if this task is not yet done, so that future task are not yet activated.
TaskComplete = false -- when a task is not yet completed, a mission cannot be completed
--env.info( 'Scheduler: Mission "'.. Mission.Name .. '" Task "' .. Task.Name .. '" Stage "' .. Task.Stage.Name .. '" break. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) )
break
end
if TaskComplete then
if Mission.GoalFunction ~= nil then
Mission.GoalFunction( Mission, Client )
end
if MISSIONSCHEDULER.Scoring then
MISSIONSCHEDULER.Scoring:_AddMissionTaskScore( Client:GetClientGroupDCSUnit(), Mission.Name, 25 )
end
-- if not Mission:IsCompleted() then
-- end
end
end
end
local MissionComplete = true
for TaskNumber, Task in pairs( Mission._Tasks ) do
if Task:Goal() then
-- Task:ShowGoalProgress( Mission, Client )
if Task:IsGoalReached() then
else
MissionComplete = false
end
else
MissionComplete = false -- If there is no goal, the mission should never be ended. The goal status will be set somewhere else.
end
end
if MissionComplete then
Mission:Completed()
if MISSIONSCHEDULER.Scoring then
MISSIONSCHEDULER.Scoring:_AddMissionScore( Mission.Name, 100 )
end
else
if TaskComplete then
-- Reset for new tasking of active client
Client.ClientAlive = false -- Reset the client tasks.
end
end
else
if Client.ClientAlive then
env.info( 'Scheduler: Client "' .. Client.ClientName .. '" is inactive.' )
Client.ClientAlive = false
-- This is tricky. If we sanitize Client._Tasks before sanitizing Client._Tasks[TaskNumber].MissionTask, then the original MissionTask will be sanitized, and will be lost within the garbage collector.
-- So first sanitize Client._Tasks[TaskNumber].MissionTask, after that, sanitize only the whole _Tasks structure...
--Client._Tasks[TaskNumber].MissionTask = nil
--Client._Tasks = nil
end
end
end
-- If all Clients of this Mission are not activated, then the Mission status needs to be put back into Pending status.
-- But only if the Mission was Ongoing. In case the Mission is Completed or Failed, the Mission status may not be changed. In these cases, this will be the last run of this Mission in the Scheduler.
if ClientsAlive == false then
if Mission:IsOngoing() then
-- Mission status back to pending...
Mission:Pending()
end
end
end
Mission:StatusToClients()
if Mission:ReportTrigger() then
Mission:ReportToAll()
end
end
return true
end
--- Start the MISSIONSCHEDULER.
function MISSIONSCHEDULER.Start()
if MISSIONSCHEDULER ~= nil then
--MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 )
MISSIONSCHEDULER.SchedulerId = SCHEDULER:New( nil, MISSIONSCHEDULER.Scheduler, { }, 0, 2 )
end
end
--- Stop the MISSIONSCHEDULER.
function MISSIONSCHEDULER.Stop()
if MISSIONSCHEDULER.SchedulerId then
routines.removeFunction(MISSIONSCHEDULER.SchedulerId)
MISSIONSCHEDULER.SchedulerId = nil
end
end
--- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc.
-- @param Mission is the MISSION object instantiated by @{MISSION:New}.
-- @return MISSION
-- @usage
-- -- Declare a mission.
-- Mission = MISSION:New( 'Russia Transport Troops SA-6',
-- 'Operational',
-- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.',
-- 'Russia' )
-- MISSIONSCHEDULER:AddMission( Mission )
function MISSIONSCHEDULER.AddMission( Mission )
MISSIONSCHEDULER.Missions[Mission.Name] = Mission
MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount + 1
-- Add an overall AI Client for the AI tasks... This AI Client will facilitate the Events in the background for each Task.
--MissionAdd:AddClient( CLIENT:Register( 'AI' ) )
return Mission
end
--- Remove a MISSION from the MISSIONSCHEDULER.
-- @param MissionName is the name of the MISSION given at declaration using @{AddMission}.
-- @usage
-- -- Declare a mission.
-- Mission = MISSION:New( 'Russia Transport Troops SA-6',
-- 'Operational',
-- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.',
-- 'Russia' )
-- MISSIONSCHEDULER:AddMission( Mission )
--
-- -- Now remove the Mission.
-- MISSIONSCHEDULER:RemoveMission( 'Russia Transport Troops SA-6' )
function MISSIONSCHEDULER.RemoveMission( MissionName )
MISSIONSCHEDULER.Missions[MissionName] = nil
MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount - 1
end
--- Find a MISSION within the MISSIONSCHEDULER.
-- @param MissionName is the name of the MISSION given at declaration using @{AddMission}.
-- @return MISSION
-- @usage
-- -- Declare a mission.
-- Mission = MISSION:New( 'Russia Transport Troops SA-6',
-- 'Operational',
-- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.',
-- 'Russia' )
-- MISSIONSCHEDULER:AddMission( Mission )
--
-- -- Now find the Mission.
-- MissionFind = MISSIONSCHEDULER:FindMission( 'Russia Transport Troops SA-6' )
function MISSIONSCHEDULER.FindMission( MissionName )
return MISSIONSCHEDULER.Missions[MissionName]
end
-- Internal function used by the MISSIONSCHEDULER menu.
function MISSIONSCHEDULER.ReportMissionsShow( )
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
Mission.MissionReportShow = true
Mission.MissionReportFlash = false
end
end
-- Internal function used by the MISSIONSCHEDULER menu.
function MISSIONSCHEDULER.ReportMissionsFlash( TimeInterval )
local Count = 0
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
Mission.MissionReportShow = false
Mission.MissionReportFlash = true
Mission.MissionReportTrigger = timer.getTime() + Count * TimeInterval
Mission.MissionTimeInterval = MISSIONSCHEDULER.MissionCount * TimeInterval
env.info( "TimeInterval = " .. Mission.MissionTimeInterval )
Count = Count + 1
end
end
-- Internal function used by the MISSIONSCHEDULER menu.
function MISSIONSCHEDULER.ReportMissionsHide( Prm )
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
Mission.MissionReportShow = false
Mission.MissionReportFlash = false
end
end
--- Enables a MENU option in the communications menu under F10 to control the status of the active missions.
-- This function should be called only once when starting the MISSIONSCHEDULER.
function MISSIONSCHEDULER.ReportMenu()
local ReportMenu = SUBMENU:New( 'Status' )
local ReportMenuShow = COMMANDMENU:New( 'Show Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsShow, 0 )
local ReportMenuFlash = COMMANDMENU:New('Flash Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsFlash, 120 )
local ReportMenuHide = COMMANDMENU:New( 'Hide Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsHide, 0 )
end
--- Show the remaining mission time.
function MISSIONSCHEDULER:TimeShow()
self.TimeIntervalCount = self.TimeIntervalCount + 1
if self.TimeIntervalCount >= self.TimeTriggerShow then
local TimeMsg = string.format("%00d", ( self.TimeSeconds / 60 ) - ( timer.getTime() / 60 )) .. ' minutes left until mission reload.'
MESSAGE:New( TimeMsg, self.TimeShow, "Mission time" ):ToAll()
self.TimeIntervalCount = 0
end
end
function MISSIONSCHEDULER:Time( TimeSeconds, TimeIntervalShow, TimeShow )
self.TimeIntervalCount = 0
self.TimeSeconds = TimeSeconds
self.TimeIntervalShow = TimeIntervalShow
self.TimeShow = TimeShow
end
--- Adds a mission scoring to the game.
function MISSIONSCHEDULER:Scoring( Scoring )
self.Scoring = Scoring
end

View File

@ -185,7 +185,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType )
self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New()
Mission:AddTask( self )
return self return self
end end
@ -193,9 +192,13 @@ end
--- Get the Task FSM Process Template --- Get the Task FSM Process Template
-- @param #TASK self -- @param #TASK self
-- @return Core.Fsm#FSM_PROCESS -- @return Core.Fsm#FSM_PROCESS
function TASK:GetUnitProcess() function TASK:GetUnitProcess( TaskUnit )
return self.FsmTemplate if TaskUnit then
return self:GetStateMachine( TaskUnit )
else
return self.FsmTemplate
end
end end
--- Sets the Task FSM Process Template --- Sets the Task FSM Process Template
@ -228,7 +231,7 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup )
-- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader.
if self:IsStatePlanned() or self:IsStateReplanned() then if self:IsStatePlanned() or self:IsStateReplanned() then
self:SetMenuForGroup( PlayerGroup ) self:SetMenuForGroup( PlayerGroup )
self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() )
end end
if self:IsStateAssigned() then if self:IsStateAssigned() then
local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup )
@ -270,10 +273,11 @@ function TASK:AbortUnit( PlayerUnit )
self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() )
self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } )
if #PlayerGroup:GetUnits() == 1 then if #PlayerGroup:GetUnits() == 1 then
self:UnAssignFromGroup( PlayerGroup )
PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil )
self:RemoveMenuForGroup( PlayerGroup ) self:RemoveMenuForGroup( PlayerGroup )
end end
self:PlayerAborted( PlayerUnit ) self:Abort()
end end
end end
end end
@ -339,7 +343,7 @@ end
--- Assign the @{Task}to a @{Group}. --- Assign the @{Task} to a @{Group}.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
-- @return #TASK -- @return #TASK
@ -350,7 +354,11 @@ function TASK:AssignToGroup( TaskGroup )
TaskGroup:SetState( TaskGroup, "Assigned", self ) TaskGroup:SetState( TaskGroup, "Assigned", self )
self:RemoveMenuForGroup( TaskGroup ) local Mission = self:GetMission()
local MissionMenu = Mission:GetMenu( TaskGroup )
MissionMenu:RemoveSubMenus()
--self:RemoveMenuForGroup( TaskGroup )
self:SetAssignedMenuForGroup( TaskGroup ) self:SetAssignedMenuForGroup( TaskGroup )
local TaskUnits = TaskGroup:GetUnits() local TaskUnits = TaskGroup:GetUnits()
@ -390,6 +398,7 @@ function TASK:AssignToUnit( TaskUnit )
self:E({"Address FsmUnit", tostring( FsmUnit ) } ) self:E({"Address FsmUnit", tostring( FsmUnit ) } )
FsmUnit:SetStartState( "Planned" ) FsmUnit:SetStartState( "Planned" )
FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow.
return self return self
@ -400,7 +409,7 @@ end
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK self -- @return #TASK self
function TASK:UnAssignFromUnit( TaskUnit ) function TASK:UnAssignFromUnit( TaskUnit )
self:F( TaskUnit ) self:F( TaskUnit:GetName() )
self:RemoveStateMachine( TaskUnit ) self:RemoveStateMachine( TaskUnit )
@ -447,28 +456,37 @@ function TASK:SendBriefingToAssignedGroups()
end end
--- Assign the @{Task} from the @{Group}s. --- UnAssign the @{Task} from the @{Group}s.
-- @param #TASK self -- @param #TASK self
function TASK:UnAssignFromGroups() function TASK:UnAssignFromGroups()
self:F2() self:F2()
for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do
self:UnAssignFromGroup( TaskGroup )
end
end
TaskGroup:SetState( TaskGroup, "Assigned", nil ) --- UnAssign the @{Task} from a @{Group}.
-- @param #TASK self
function TASK:UnAssignFromGroup( TaskGroup )
self:F2( { TaskGroup } )
TaskGroup:SetState( TaskGroup, "Assigned", nil )
self:RemoveMenuForGroup( TaskGroup ) self:RemoveAssignedMenuForGroup( TaskGroup )
local TaskUnits = TaskGroup:GetUnits() local TaskUnits = TaskGroup:GetUnits()
for UnitID, UnitData in pairs( TaskUnits ) do for UnitID, UnitData in pairs( TaskUnits ) do
local TaskUnit = UnitData -- Wrapper.Unit#UNIT local TaskUnit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = TaskUnit:GetPlayerName() local PlayerName = TaskUnit:GetPlayerName()
if PlayerName ~= nil or PlayerName ~= "" then if PlayerName ~= nil or PlayerName ~= "" then
self:UnAssignFromUnit( TaskUnit ) self:UnAssignFromUnit( TaskUnit )
end
end end
end end
end end
--- Returns if the @{Task} is assigned to the Group. --- Returns if the @{Task} is assigned to the Group.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
@ -479,10 +497,12 @@ function TASK:IsAssignedToGroup( TaskGroup )
if self:IsStateAssigned() then if self:IsStateAssigned() then
if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then
self:T( { "Task is assigned to:", TaskGroup:GetName() } )
return true return true
end end
end end
self:T( { "Task is not assigned to:", TaskGroup:GetName() } )
return false return false
end end
@ -511,37 +531,36 @@ end
--- Set the menu options of the @{Task} to all the groups in the SetGroup. --- Set the menu options of the @{Task} to all the groups in the SetGroup.
-- @param #TASK self -- @param #TASK self
function TASK:SetMenu() -- @param #number MenuTime
-- @return #TASK
function TASK:SetMenu( MenuTime )
self:F() self:F()
self.SetGroup:Flush() self.SetGroup:Flush()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do
if self:IsStatePlanned() or self:IsStateReplanned() then local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP
self:SetMenuForGroup( TaskGroup ) if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then
if self:IsStatePlanned() or self:IsStateReplanned() then
self:SetMenuForGroup( TaskGroup, MenuTime )
end
end end
end end
end end
--- Remove the menu options of the @{Task} to all the groups in the SetGroup.
-- @param #TASK self
-- @return #TASK self
function TASK:RemoveMenu()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
self:RemoveMenuForGroup( TaskGroup )
end
end
--- Set the Menu for a Group --- Set the Menu for a Group
-- @param #TASK self -- @param #TASK self
function TASK:SetMenuForGroup( TaskGroup ) -- @param #number MenuTime
-- @return #TASK
function TASK:SetMenuForGroup( TaskGroup, MenuTime )
if not self:IsAssignedToGroup( TaskGroup ) then if not TaskGroup:GetState( TaskGroup, "Assigned" ) then
self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime )
else else
self:SetAssignedMenuForGroup( TaskGroup ) if not self:IsAssignedToGroup( TaskGroup ) then
self:SetAssignedMenuForGroup( TaskGroup, MenuTime )
end
end end
end end
@ -550,16 +569,24 @@ end
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
-- @param #string MenuText The menu text. -- @param #string MenuText The menu text.
-- @param #number MenuTime
-- @return #TASK self -- @return #TASK self
function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime )
self:E( TaskGroup:GetName() ) self:E( TaskGroup:GetName() )
local Mission = self:GetMission() local Mission = self:GetMission()
local MissionMenu = Mission:GetMissionMenu( TaskGroup ) local MissionName = Mission:GetName()
local CommandCenter = Mission:GetCommandCenter()
local CommandCenterMenu = CommandCenter:GetMenu()
local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime )
local MissionMenu = Mission:GetMenu( TaskGroup )
local TaskType = self:GetType() local TaskType = self:GetType()
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime )
local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true )
return self return self
end end
@ -567,32 +594,84 @@ end
--- Set the assigned menu options of the @{Task}. --- Set the assigned menu options of the @{Task}.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
-- @param #number MenuTime
-- @return #TASK self -- @return #TASK self
function TASK:SetAssignedMenuForGroup( TaskGroup ) function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime )
self:E( TaskGroup:GetName() ) self:E( TaskGroup:GetName() )
local Mission = self:GetMission() local Mission = self:GetMission()
local MissionMenu = Mission:GetMissionMenu( TaskGroup ) local MissionMenu = Mission:GetMenu( TaskGroup )
self:E( { MissionMenu = MissionMenu } ) self:E( { MissionMenu = MissionMenu } )
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime )
local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { self = self, TaskGroup = TaskGroup } ) local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime )
return self return self
end end
--- Remove the menu options of the @{Task} to all the groups in the SetGroup.
-- @param #TASK self
-- @param #number MenuTime
-- @return #TASK
function TASK:RemoveMenu( MenuTime )
self:F()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then
if not self:IsAssignedToGroup( TaskGroup ) then
self:RemovePlannedMenuForGroup( TaskGroup, MenuTime )
end
end
end
end
--- Remove the menu option of the @{Task} for a @{Group}. --- Remove the menu option of the @{Task} for a @{Group}.
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup -- @param Wrapper.Group#GROUP TaskGroup
-- @param #number MenuTime
-- @return #TASK self -- @return #TASK self
function TASK:RemoveMenuForGroup( TaskGroup ) function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime )
self:F()
local Mission = self:GetMission() local Mission = self:GetMission()
local MissionName = Mission:GetName() local MissionName = Mission:GetName()
local MissionMenu = Mission:GetMenu( TaskGroup )
if MissionMenu then
local TaskType = self:GetType()
local TypeMenu = MissionMenu:GetMenu( TaskType )
if TypeMenu then
local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() )
if TaskMenu then
TaskMenu:Remove( MenuTime )
end
end
end
end
local MissionMenu = Mission:GetMissionMenu( TaskGroup ) --- Remove the assigned menu option of the @{Task} for a @{Group}.
MissionMenu:Remove() -- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup
-- @param #number MenuTime
-- @return #TASK self
function TASK:RemoveAssignedMenuForGroup( TaskGroup )
self:F()
local Mission = self:GetMission()
local MissionName = Mission:GetName()
local MissionMenu = Mission:GetMenu( TaskGroup )
if MissionMenu then
MissionMenu:RemoveSubMenus()
end
end end
function TASK.MenuAssignToGroup( MenuParam ) function TASK.MenuAssignToGroup( MenuParam )
@ -605,19 +684,21 @@ function TASK.MenuAssignToGroup( MenuParam )
self:AssignToGroup( TaskGroup ) self:AssignToGroup( TaskGroup )
end end
function TASK.MenuTaskStatus( MenuParam ) --- Report the task status.
-- @param #TASK self
function TASK:MenuTaskStatus( TaskGroup )
local self = MenuParam.self local ReportText = self:ReportDetails()
local TaskGroup = MenuParam.TaskGroup
--self:AssignToGroup( TaskGroup ) self:T( ReportText )
self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup )
end end
function TASK.MenuTaskAbort( MenuParam ) --- Report the task status.
-- @param #TASK self
function TASK:MenuTaskAbort( TaskGroup )
local self = MenuParam.self
local TaskGroup = MenuParam.TaskGroup
self:Abort() self:Abort()
end end
@ -662,15 +743,26 @@ end
--- Add a FiniteStateMachine to @{Task} with key Task@{Unit} --- Add a FiniteStateMachine to @{Task} with key Task@{Unit}
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Core.Fsm#FSM_PROCESS Fsm
-- @return #TASK self -- @return #TASK self
function TASK:SetStateMachine( TaskUnit, Fsm ) function TASK:SetStateMachine( TaskUnit, Fsm )
self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } )
self.Fsm[TaskUnit] = Fsm self.Fsm[TaskUnit] = Fsm
return Fsm return Fsm
end end
--- Gets the FiniteStateMachine of @{Task} with key Task@{Unit}
-- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Fsm#FSM_PROCESS
function TASK:GetStateMachine( TaskUnit )
self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } )
return self.Fsm[TaskUnit]
end
--- Remove FiniteStateMachines from @{Task} with key Task@{Unit} --- Remove FiniteStateMachines from @{Task} with key Task@{Unit}
-- @param #TASK self -- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@ -678,9 +770,15 @@ end
function TASK:RemoveStateMachine( TaskUnit ) function TASK:RemoveStateMachine( TaskUnit )
self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } )
self:E( self.Fsm )
for TaskUnitT, Fsm in pairs( self.Fsm ) do
self:E( TaskUnitT )
end
self.Fsm[TaskUnit] = nil self.Fsm[TaskUnit] = nil
collectgarbage() collectgarbage()
self:T( "Garbage Collected, Processes should be finalized now ...") self:E( "Garbage Collected, Processes should be finalized now ...")
end end
--- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task}
@ -795,6 +893,32 @@ function TASK:IsStatePlanned()
return self:Is( "Planned" ) return self:Is( "Planned" )
end end
--- Sets a @{Task} to status **Aborted**.
-- @param #TASK self
function TASK:StateAborted()
self:SetState( self, "State", "Aborted" )
return self
end
--- Is the @{Task} status **Aborted**.
-- @param #TASK self
function TASK:IsStateAborted()
return self:Is( "Aborted" )
end
--- Sets a @{Task} to status **Cancelled**.
-- @param #TASK self
function TASK:StateCancelled()
self:SetState( self, "State", "Cancelled" )
return self
end
--- Is the @{Task} status **Cancelled**.
-- @param #TASK self
function TASK:IsStateCancelled()
return self:Is( "Cancelled" )
end
--- Sets a @{Task} to status **Assigned**. --- Sets a @{Task} to status **Assigned**.
-- @param #TASK self -- @param #TASK self
function TASK:StateAssigned() function TASK:StateAssigned()
@ -875,7 +999,7 @@ function TASK:onenterSuccess( From, Event, To )
self:E( "Task Success" ) self:E( "Task Success" )
self:MessageToGroups( "Task " .. self:GetName() .. " is successful! Good job!" ) self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" )
self:UnAssignFromGroups() self:UnAssignFromGroups()
self:GetMission():__Complete( 1 ) self:GetMission():__Complete( 1 )
@ -1009,24 +1133,19 @@ function TASK:ReportDetails()
-- Determine the status of the Task. -- Determine the status of the Task.
local State = self:GetState() local State = self:GetState()
-- Loop each Unit active in the Task, and find Player Names. -- Loop each Unit active in the Task, and find Player Names.
local PlayerNames = {} local PlayerNames = {}
for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do local PlayerReport = REPORT:New( " - Players:" )
local Player = PlayerGroup -- Wrapper.Group#GROUP for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do
for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP
local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT PlayerNames = PlayerGroup:GetPlayerNames()
if PlayerUnit and PlayerUnit:IsAlive() then if PlayerNames then
local PlayerName = PlayerUnit:GetPlayerName() PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) )
PlayerNames[#PlayerNames+1] = PlayerName
end
end end
local PlayerNameText = table.concat( PlayerNames, ", " )
Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText )
end end
-- Loop each Process in the Task, and find Reporting Details. -- Loop each Process in the Task, and find Reporting Details.
Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) )
return Report:Text() return Report:Text()
end end

View File

@ -1,28 +1,53 @@
--- (AI) (SP) (MP) Tasking for Air to Ground Processes. --- This module contains the TASK_A2G classes.
-- --
-- 1) @{#TASK_A2G} class, extends @{Task#TASK} -- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK}
-- ================================================= --
-- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, -- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units,
-- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. -- based on the tasking capabilities defined in @{Task#TASK}.
-- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: -- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses:
-- --
-- * **None**: Start of the process -- * **None**: Start of the process
-- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. -- * **Planned**: The A2G task is planned.
-- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. -- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}.
-- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. -- * **Success**: The A2G task is successfully completed.
-- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
-- --
-- # 1) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G}
--
-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units.
--
-- ====
--
-- # **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:
--
-- 2017-03-09: Revised version.
--
-- === -- ===
-- --
-- ### Authors: FlightControl - Design and Programming -- # **AUTHORS and CONTRIBUTIONS**
-- --
-- ### Contributions:
--
-- * **[WingThor]**: Concept, Advice & Testing.
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module Task_A2G -- @module Task_A2G
do -- TASK_A2G do -- TASK_A2G
--- The TASK_A2G class --- The TASK_A2G class
-- @type TASK_A2G -- @type TASK_A2G
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK -- @extends Tasking.Task#TASK
TASK_A2G = { TASK_A2G = {
ClassName = "TASK_A2G", ClassName = "TASK_A2G",
@ -33,52 +58,312 @@ do -- TASK_A2G
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task. -- @param #string TaskName The name of the Task.
-- @param #string TaskType BAI or CAS
-- @param Set#SET_UNIT UnitSetTargets -- @param Set#SET_UNIT UnitSetTargets
-- @param Core.Zone#ZONE_BASE TargetZone -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range.
-- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known.
-- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.
-- @return #TASK_A2G self -- @return #TASK_A2G self
function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G
self:F() self:F()
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone self.TaskType = TaskType
self.FACUnit = FACUnit
local A2GUnitProcess = self:GetUnitProcess()
A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) Mission:AddTask( self )
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 ) )
A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" )
A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" )
function A2GUnitProcess:onenterUpdated( TaskUnit ) local Fsm = self:GetUnitProcess()
self:E( { self } )
self:Account()
self:Smoke() Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } )
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } )
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.RendezVousSetUnit
if Task:GetRendezVousZone( TaskUnit ) then
self:__RouteToRendezVousZone( 0.1 )
else
if Task:GetRendezVousPointVec2( TaskUnit ) then
self:__RouteToRendezVousPoint( 0.1 )
else
self:__ArriveAtRendezVous( 0.1 )
end
end
end end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2G Task
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit
self:__Engage( 0.1 )
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2G Task
function Fsm:onafterEngage( TaskUnit, Task )
self:E( { self } )
self:__Account( 0.1 )
self:__RouteToTarget(0.1 )
self:__RouteToTargets( -10 )
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToTarget( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit
if Task:GetTargetZone( TaskUnit ) then
self:__RouteToTargetZone( 0.1 )
else
local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if TargetUnit then
local PointVec2 = TargetUnit:GetPointVec2()
self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } )
Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit )
end
self:__RouteToTargetPoint( 0.1 )
end
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToTargets( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if TargetUnit then
Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit )
end
self:__RouteToTargets( -10 )
end
--_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self )
--_EVENTDISPATCHER:OnDead( self._EventDead, self )
--_EVENTDISPATCHER:OnCrash( self._EventDead, self )
--_EVENTDISPATCHER:OnPilotDead( self._EventDead, self )
return self return self
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
function TASK_A2G:GetPlannedMenuText() function TASK_A2G:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
end end
--- @param #TASK_A2G self
-- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 )
ActRouteRendezVous:SetRange( RendezVousRange )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
function TASK_A2G:GetRendezVousPointVec2( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange()
end
--- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteRendezVous:SetZone( RendezVousZone )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map.
function TASK_A2G:GetRendezVousZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteRendezVous:GetZone()
end
--- @param #TASK_A2G self
-- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteTarget:SetPointVec2( TargetPointVec2 )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map.
function TASK_A2G:GetTargetPointVec2( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
return ActRouteTarget:GetPointVec2()
end end
--- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetTargetZone( TargetZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteTarget:SetZone( TargetZone )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
function TASK_A2G:GetTargetZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteTarget:GetZone()
end
end
do -- TASK_SEAD
--- The TASK_SEAD class
-- @type TASK_SEAD
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_SEAD = {
ClassName = "TASK_SEAD",
}
--- Instantiates a new TASK_SEAD.
-- @param #TASK_SEAD self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Set#SET_UNIT UnitSetTargets
-- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range.
-- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known.
-- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.
-- @return #TASK_SEAD self
function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD
self:F()
return self
end
end
do -- TASK_BAI
--- The TASK_BAI class
-- @type TASK_BAI
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_BAI = {
ClassName = "TASK_BAI",
}
--- Instantiates a new TASK_BAI.
-- @param #TASK_BAI self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Set#SET_UNIT UnitSetTargets
-- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range.
-- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known.
-- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.
-- @return #TASK_BAI self
function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI
self:F()
return self
end
end
do -- TASK_CAS
--- The TASK_CAS class
-- @type TASK_CAS
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_CAS = {
ClassName = "TASK_CAS",
}
--- Instantiates a new TASK_CAS.
-- @param #TASK_CAS self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Set#SET_UNIT UnitSetTargets
-- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range.
-- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known.
-- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.
-- @return #TASK_CAS self
function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS
self:F()
return self
end
end

View File

@ -0,0 +1,286 @@
--- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets.
--
-- ===
--
-- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER}
--
-- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups).
-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
-- Find a summary below describing for which situation a task type is created:
--
-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
--
-- Other task types will follow...
--
-- 3.1) TASK_A2G_DISPATCHER constructor:
-- --------------------------------------
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance.
--
-- ===
--
-- # **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:
--
-- 2017-03-09: Initial class and API.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module Task_A2G_Dispatcher
do -- TASK_A2G_DISPATCHER
--- TASK_A2G_DISPATCHER class.
-- @type TASK_A2G_DISPATCHER
-- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to.
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
-- @field Tasking.Mission#MISSION Mission
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
TASK_A2G_DISPATCHER = {
ClassName = "TASK_A2G_DISPATCHER",
Mission = nil,
Detection = nil,
}
--- TASK_A2G_DISPATCHER constructor.
-- @param #TASK_A2G_DISPATCHER self
-- @param Tasking.Mission#MISSION The mission for which the task dispatching is done.
-- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission.
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
-- @return #TASK_A2G_DISPATCHER self
function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER
self.Detection = Detection
self.Mission = Mission
self:Schedule( 30 )
return self
end
--- Creates a SEAD task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local RadarCount = DetectedSet:HasSEAD()
if RadarCount > 0 then
-- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it.
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterHasSEAD()
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a CAS task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
if GroundUnitCount > 0 and FriendliesNearBy == true then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a BAI task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
if GroundUnitCount > 0 and FriendliesNearBy == false then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
-- @param #TASK_A2G_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission
-- @param Tasking.Task#TASK Task
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem )
if Task then
if Task:IsStatePlanned() and DetectedItem.Changed == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Task = Mission:RemoveTask( Task )
end
end
return Task
end
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object.
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function TASK_A2G_DISPATCHER:ProcessDetected( Detection )
self:F2()
local AreaMsg = {}
local TaskMsg = {}
local ChangeMsg = {}
local Mission = self.Mission
local ReportSEAD = REPORT:New( " - SEAD Tasks:")
local ReportCAS = REPORT:New( " - CAS Tasks:")
local ReportBAI = REPORT:New( " - BAI Tasks:")
local ReportChanges = REPORT:New( " - Changes:" )
--- First we need to the detected targets.
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet
local DetectedZone = DetectedItem.Zone
self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
DetectedSet:Flush()
local ItemID = DetectedItem.ItemID
-- Evaluate SEAD Tasking
local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) )
SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem )
if not SEADTask then
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit )
Task:SetTargetZone( DetectedZone )
SEADTask = Mission:AddTask( Task )
end
end
if SEADTask and SEADTask:IsStatePlanned() then
ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) )
end
-- Evaluate CAS Tasking
local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) )
CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem )
if not CASTask then
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit )
--Task:SetTargetZone( DetectedZone )
CASTask = Mission:AddTask( Task )
end
end
if CASTask and CASTask:IsStatePlanned() then
ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) )
end
-- Evaluate BAI Tasking
local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) )
BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem )
if not BAITask then
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit )
Task:SetTargetZone( DetectedZone )
BAITask = Mission:AddTask( Task )
end
end
if BAITask and BAITask:IsStatePlanned() then
ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) )
end
-- Loop through the changes ...
local ChangeText = Detection:GetChangeText( DetectedItem )
ReportChanges:Add( ChangeText )
-- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedItem )
end
-- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if not TaskGroup:GetState( TaskGroup, "Assigned" ) then
Mission:GetCommandCenter():MessageToGroup(
string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n",
self.Mission:GetName(),
string.format( "%s\n%s\n%s\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text()
)
), TaskGroup
)
end
end
return true
end
end

View File

@ -41,11 +41,6 @@ do -- TASK_PICKUP
local self = BASE:Inherit( self, TASK:New( Mission, AssignedSetGroup, TaskName, TaskType, "PICKUP" ) ) local self = BASE:Inherit( self, TASK:New( Mission, AssignedSetGroup, TaskName, TaskType, "PICKUP" ) )
self:F() self:F()
_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self )
_EVENTDISPATCHER:OnDead( self._EventDead, self )
_EVENTDISPATCHER:OnCrash( self._EventDead, self )
_EVENTDISPATCHER:OnPilotDead( self._EventDead, self )
return self return self
end end

View File

@ -1,78 +0,0 @@
--- This module contains the TASK_SEAD classes.
--
-- 1) @{#TASK_SEAD} class, extends @{Task#TASK}
-- =================================================
-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units, located at a Target Zone,
-- based on the tasking capabilities defined in @{Task#TASK}.
-- The TASK_SEAD is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses:
--
-- * **None**: Start of the process
-- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task.
-- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone.
-- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task.
-- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
--
-- ===
--
-- ### Authors: FlightControl - Design and Programming
--
-- @module Task_SEAD
do -- TASK_SEAD
--- The TASK_SEAD class
-- @type TASK_SEAD
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_SEAD = {
ClassName = "TASK_SEAD",
}
--- Instantiates a new TASK_SEAD.
-- @param #TASK_SEAD self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Set#SET_UNIT UnitSetTargets
-- @param Core.Zone#ZONE_BASE TargetZone
-- @return #TASK_SEAD self
function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TargetZone )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD
self:F()
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
local Fsm = self:GetUnitProcess()
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: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:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()
end
-- _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self )
-- _EVENTDISPATCHER:OnDead( self._EventDead, self )
-- _EVENTDISPATCHER:OnCrash( self._EventDead, self )
-- _EVENTDISPATCHER:OnPilotDead( self._EventDead, self )
return self
end
--- @param #TASK_SEAD self
function TASK_SEAD:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
end
end

View File

@ -536,48 +536,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At
return DCSTask return DCSTask
end end
--- (AIR) Attack the Unit. --- (AIR) Attack the Unit.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Wrapper.Unit#UNIT AttackUnit The unit. -- @param Wrapper.Unit#UNIT AttackUnit The UNIT.
-- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. -- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
-- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. -- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure. -- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack )
self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } )
-- AttackUnit = {
-- id = 'AttackUnit',
-- params = {
-- unitId = Unit.ID,
-- weaponType = number,
-- expend = enum AI.Task.WeaponExpend
-- attackQty = number,
-- direction = Azimuth,
-- attackQtyLimit = boolean,
-- controllableAttack = boolean,
-- }
-- }
local DCSTask local DCSTask
DCSTask = { DCSTask = {
id = 'AttackUnit', id = 'AttackUnit',
params = { params = {
altitudeEnabled = true,
unitId = AttackUnit:GetID(), unitId = AttackUnit:GetID(),
attackQtyLimit = AttackQtyLimit or false, groupAttack = GroupAttack or false,
attackQty = AttackQty or 2, visible = Visible or false,
expend = WeaponExpend or "Auto", expend = WeaponExpend or "Auto",
altitude = 2000, directionEnabled = Direction and true or false,
directionEnabled = true, direction = Direction,
groupAttack = true, altitudeEnabled = Altitude and true or false,
--weaponType = WeaponType or 1073741822, altitude = Altitude or 30,
direction = Direction or 0, attackQtyLimit = AttackQty and true or false,
} attackQty = AttackQty,
weaponType = 1073741822,
},
} }
self:E( DCSTask ) self:E( DCSTask )
@ -1191,7 +1178,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType,
end end
--- (AIR) Attack the Unit. --- (AIR) Search and attack the Unit.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Wrapper.Unit#UNIT EngageUnit The UNIT. -- @param Wrapper.Unit#UNIT EngageUnit The UNIT.
-- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. -- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.

View File

@ -113,7 +113,7 @@ GROUP = {
-- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name
-- @return #GROUP self -- @return #GROUP self
function GROUP:Register( GroupName ) function GROUP:Register( GroupName )
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self:F2( GroupName ) self:F2( GroupName )
self.GroupName = GroupName self.GroupName = GroupName
@ -231,7 +231,7 @@ function GROUP:GetCategory()
return nil return nil
end end
--- Returns the category name of the DCS Group. --- Returns the category name of the #GROUP.
-- @param #GROUP self -- @param #GROUP self
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
function GROUP:GetCategoryName() function GROUP:GetCategoryName()
@ -926,3 +926,29 @@ do -- Event Handling
end end
end end
do -- Players
--- Get player names
-- @param #GROUP self
-- @return #table The group has players, an array of player names is returned.
-- @return #nil The group has no players
function GROUP:GetPlayerNames()
local PlayerNames = nil
local Units = self:GetUnits()
for UnitID, UnitData in pairs( Units ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = Unit:GetPlayerName()
if PlayerName and PlayerName ~= "" then
PlayerNames = PlayerNames or {}
table.insert( PlayerNames, PlayerName )
end
end
self:F( PlayerNames )
return PlayerNames
end
end

View File

@ -56,7 +56,7 @@ function STATIC:FindByName( StaticName, RaiseError )
self.StaticName = StaticName self.StaticName = StaticName
if StaticFound then if StaticFound then
StaticFound:F( { StaticName } ) StaticFound:F3( { StaticName } )
return StaticFound return StaticFound
end end

View File

@ -522,6 +522,31 @@ function UNIT:GetLife0()
return nil return nil
end end
--- Returns the category name of the #UNIT.
-- @param #UNIT self
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
function UNIT:GetCategoryName()
self:F3( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local CategoryNames = {
[Unit.Category.AIRPLANE] = "Airplane",
[Unit.Category.HELICOPTER] = "Helicopter",
[Unit.Category.GROUND_UNIT] = "Ground Unit",
[Unit.Category.SHIP] = "Ship",
[Unit.Category.STRUCTURE] = "Structure",
}
local UnitCategory = DCSUnit:getDesc().category
self:T3( UnitCategory )
return CategoryNames[UnitCategory]
end
return nil
end
--- Returns the Unit's A2G threat level on a scale from 1 to 10 ... --- Returns the Unit's A2G threat level on a scale from 1 to 10 ...
-- The following threat levels are foreseen: -- The following threat levels are foreseen:
-- --
@ -540,14 +565,14 @@ end
function UNIT:GetThreatLevel() function UNIT:GetThreatLevel()
local Attributes = self:GetDesc().attributes local Attributes = self:GetDesc().attributes
self:E( Attributes ) self:T( Attributes )
local ThreatLevel = 0 local ThreatLevel = 0
local ThreatText = "" local ThreatText = ""
if self:IsGround() then if self:IsGround() then
self:E( "Ground" ) self:T( "Ground" )
local ThreatLevels = { local ThreatLevels = {
"Unarmed", "Unarmed",
@ -585,7 +610,7 @@ function UNIT:GetThreatLevel()
if self:IsAir() then if self:IsAir() then
self:E( "Air" ) self:T( "Air" )
local ThreatLevels = { local ThreatLevels = {
"Unarmed", "Unarmed",
@ -619,7 +644,7 @@ function UNIT:GetThreatLevel()
if self:IsShip() then if self:IsShip() then
self:E( "Ship" ) self:T( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",}, --["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",},

View File

@ -1,4 +1,4 @@
2017-02-08 2017-02-18
- Reworked some vector functions. - Reworked some vector functions.
-- POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added. -- POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added.
@ -6,9 +6,13 @@
-- ZONE_RADIUS:GetRandomPointVec3( inner, outer ) added. -- ZONE_RADIUS:GetRandomPointVec3( inner, outer ) added.
-- ZONE_POLYGON_BASE:GetRandomPointVec2() added. -- ZONE_POLYGON_BASE:GetRandomPointVec2() added.
-- ZONE_POLYGON_BASE:GetRandomPointVec3() added. -- ZONE_POLYGON_BASE:GetRandomPointVec3() added.
2017-02-17
- Added ACT_ROUTE_POINT
-- Routes a controllable to a point with a defined distance.
-- Upon arrived within the engagement distance, an arrival text is shown.
2016-12-06 2016-12-06
- Renamed the documentation references following the structure of the files. - Renamed the documentation references following the structure of the files.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -97,7 +97,7 @@ COPY /b Moose.lua + %1\Tasking\CommandCenter.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Mission.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Mission.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Task.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Task.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\DetectionManager.lua Moose.lua COPY /b Moose.lua + %1\Tasking\DetectionManager.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Task_SEAD.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Task_A2G_Dispatcher.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Task_A2G.lua Moose.lua COPY /b Moose.lua + %1\Tasking\Task_A2G.lua Moose.lua
COPY /b Moose.lua + %1\Moose.lua Moose.lua COPY /b Moose.lua + %1\Moose.lua Moose.lua

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -19,5 +19,6 @@
local FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() local FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart()
local FACDetection = DETECTION_AREAS:New( FACSetGroup, 1000, 250 ):FlareDetectedZones():SmokeDetectedUnits() local FACDetection = DETECTION_AREAS:New( FACSetGroup, 150, 250 ):BoundDetectedZones():SmokeDetectedUnits()
FACDetection:__Start( 5 )

View File

@ -0,0 +1,56 @@
---
-- Name: DET-100 - Detection Probability Distance
-- Author: FlightControl
-- Date Created: 04 Feb 2017
--
-- # Situation:
--
-- Demonstrates the DistanceProbability factor during the detection of units.
--
-- Two JTAC are detecting 4 units, which are 10 km away.
-- The first JTAC has no DistanceProbability set.
-- The second JTAC has a DistanceProbability set.
--
-- # Test cases:
--
-- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first.
-- 2. Eventually all units should be detected by both JTAC.
local RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart()
local RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart()
local HQ = GROUP:FindByName( "HQ" )
local CC = COMMANDCENTER:New( HQ, "HQ" )
local RecceDetection1 = DETECTION_UNITS:New( RecceSetGroup1 )
local RecceDetection2 = DETECTION_UNITS:New( RecceSetGroup2 )
RecceDetection2:SetDistanceProbability( 0.2 ) -- Set a 20% probability that a vehicle can be detected at 4km distance.
RecceDetection1:Start()
RecceDetection2:Start()
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection1:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection1:DetectedReportDetailed()
HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No distance Probability" )
end
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection2:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection2:DetectedReportDetailed()
HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Distance Probability" )
end

View File

@ -0,0 +1,62 @@
---
-- Name: DET-120 - Detection Probability Zones
-- Author: FlightControl
-- Date Created: 04 Feb 2017
--
-- # Situation:
--
-- Demonstrates the DistanceProbability factor during the detection of units.
--
-- Two JTAC are detecting 4 units, which are 10 km away.
-- The first JTAC has no DistanceProbability set.
-- The second JTAC has a DistanceProbability set.
--
-- # Test cases:
--
-- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first.
-- 2. Eventually all units should be detected by both JTAC.
local RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart()
local RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart()
local HQ = GROUP:FindByName( "HQ" )
local CC = COMMANDCENTER:New( HQ, "HQ" )
local RecceDetection1 = DETECTION_UNITS:New( RecceSetGroup1 )
local RecceDetection2 = DETECTION_UNITS:New( RecceSetGroup2 )
local ForestZone = ZONE_POLYGON:New( "ForestZone", GROUP:FindByName( "ForestZone" ) )
RecceDetection2:SetZoneProbability( { { ForestZone, 0.1 } } ) -- Set a 10% probability that a vehicle can be detected within the forest.
RecceDetection1:Start()
RecceDetection2:Start()
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection1:OnAfterDetect(From,Event,To)
local DetectionReport = self:DetectedReportDetailed()
HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No Zone Probability" )
end
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection2:OnAfterDetect(From,Event,To)
local DetectionReport = self:DetectedReportDetailed()
HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Forest Zone Probability" )
end
garbagecollect()

View File

@ -0,0 +1,40 @@
---
-- Name: DET-200 - Detection UNITS
-- Author: FlightControl
-- Date Created: 13 Feb 2017
--
-- # Situation:
--
-- Demonstrates the detection of units.
--
-- A Set of Recce are detecting a large group of units, which are 5 km away.
-- Select one of the blue Recce, and press F7. Watch the reporting of the detection evolve.
-- The enemy is approaching.
--
-- # Test cases:
--
-- 1. Observe the detection reporting of both the Recce.
-- 2. Eventually all units should be detected by both Recce.
local RecceSetGroup = SET_GROUP:New():FilterPrefixes( "Recce" ):FilterStart()
local HQ = GROUP:FindByName( "HQ" )
local CC = COMMANDCENTER:New( HQ, "HQ" )
local RecceDetection = DETECTION_UNITS:New( RecceSetGroup )
RecceDetection:Start()
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection:DetectedReportDetailed()
CC:MessageToAll( DetectionReport, 15, "" )
end

View File

@ -0,0 +1,42 @@
---
-- Name: DET-210 - Detection TYPES
-- Author: FlightControl
-- Date Created: 13 Feb 2017
--
-- # Situation:
--
-- Demonstrates the detection of units.
--
-- A Set of Recce are detecting a large group of units, which are 5 km away.
-- Select one of the blue Recce, and press F7. Watch the reporting of the detection evolve.
-- The enemy is approaching.
--
-- The blue Recce will report the detected units grouped per vehicle type!
--
-- # Test cases:
--
-- 1. Observe the detection reporting of both the Recce.
-- 2. Eventually all units should be detected by both Recce.
local RecceSetGroup = SET_GROUP:New():FilterPrefixes( "Recce" ):FilterStart()
local HQ = GROUP:FindByName( "HQ" )
local CC = COMMANDCENTER:New( HQ, "HQ" )
local RecceDetection = DETECTION_TYPES:New( RecceSetGroup )
RecceDetection:Start()
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection:DetectedReportDetailed()
CC:MessageToAll( DetectionReport, 15, "" )
end

View File

@ -0,0 +1,58 @@
---
-- Name: DET-100 - Detection Probability Distance
-- Author: FlightControl
-- Date Created: 04 Feb 2017
--
-- # Situation:
--
-- Demonstrates the DistanceProbability factor during the detection of units.
--
-- Two JTAC are detecting 4 units, which are 10 km away.
-- The first JTAC has no DistanceProbability set.
-- The second JTAC has a DistanceProbability set.
--
-- # Test cases:
--
-- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first.
-- 2. Eventually all units should be detected by both JTAC.
local RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart()
local RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart()
local HQ = GROUP:FindByName( "HQ" )
local CC = COMMANDCENTER:New( HQ, "HQ" )
local RecceDetection1 = DETECTION_AREAS:New( RecceSetGroup1, 1000 )
RecceDetection1:BoundDetectedZones()
local RecceDetection2 = DETECTION_AREAS:New( RecceSetGroup2, 1000 )
RecceDetection2:SetDistanceProbability( 0.2 ) -- Set a 20% probability that a vehicle can be detected at 4km distance.
RecceDetection1:BoundDetectedZones()
RecceDetection1:Start()
RecceDetection2:Start()
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection1:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection1:DetectedReportDetailed()
HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No distance Probability" )
end
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection2:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection2:DetectedReportDetailed()
HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Distance Probability" )
end

View File

@ -0,0 +1,46 @@
---
-- Name: DET-255 - Detection AEAS with Destroys
-- Author: FlightControl
-- Date Created: 06 Mar 2017
--
-- # Situation:
--
-- A small blue vehicle with laser detection methods is detecting targets.
-- Targets are grouped within areas. A detection range and zone range is given to group the detected units.
-- This demo will group red vehicles in areas. One vehicle is diving from one group to the other.
-- After 30 seconds, one vehicle is destroyed in a zone.
-- After 60 seconds, a vehicle is destroyed that is a leader of a zone.
-- After 90 seconds, all vehicles are destroyed in a zone.
--
-- # Test cases:
--
-- 1. Observe the flaring of the areas formed
-- 2. Observe the smoking of the units detected
-- 3. Observe the areas being flexibly changed very detection run.
-- 4. The truck driving from the one group to the other, will leave the first area, and will join the second.
-- 5. While driving in between the areas, it will have a separate area.
-- 6. Observe the correct removal or relocation of the ZONEs.
local FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart()
local FACDetection = DETECTION_AREAS:New( FACSetGroup, 150, 250 ):BoundDetectedZones():SmokeDetectedUnits()
FACDetection:__Start( 5 )
SCHEDULER:New( nil,function()
local Target = UNIT:FindByName( "Target #004")
Target:Destroy()
end, {}, 30
)
SCHEDULER:New( nil,function()
local Target = UNIT:FindByName( "Target #006")
Target:Destroy()
end, {}, 60
)
SCHEDULER:New( nil,function()
local Target = UNIT:FindByName( "Target #007")
Target:Destroy()
end, {}, 90
)

View File

@ -0,0 +1,70 @@
---
-- Name: DET-500 - Handle Detected Event - Govern Artillery Demo
-- Author: FlightControl
-- Date Created: 13 Feb 2017
--
-- # Situation:
--
-- Demonstrates the detection of units.
--
-- A Set of Recces are detecting a large group of units, which are 5 km away.
-- Once the Recces detect the enemy, the artilley units are controlled and will fire a missile to the target.
--
-- # Test cases:
--
-- 1. Observe the detected reporting of the recces.
-- 2. When one Recce group detects a target, it will select an artillery unit and fire a missile.
-- 3. This will run until all Recces have eliminated the targets.
local RecceSetGroup = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Recce" ):FilterStart()
local ArtillerySetGroup = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Artillery" ):FilterStart()
local HQ = GROUP:FindByName( "HQ" )
local CC = COMMANDCENTER:New( HQ, "HQ" )
local RecceDetection = DETECTION_UNITS:New( RecceSetGroup )
RecceDetection:SetDetectionInterval( 5 )
RecceDetection:Start()
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function RecceDetection:OnAfterDetect(From,Event,To)
local DetectionReport = RecceDetection:DetectedReportDetailed()
CC:GetPositionable():MessageToAll( DetectionReport, 15, "" )
end
local ArtilleryTime = {}
local ArtilleryAim = 180
--- OnAfter Transition Handler for Event Detect.
-- @param Functional.Detection#DETECTION_UNITS self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT DetectedUnits
function RecceDetection:OnAfterDetected( From, Event, To, DetectedUnits )
self:E( { From, Event, To, DetectedUnits } )
for DetectedUnitID, DetectedUnit in pairs( DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
local Artillery = ArtillerySetGroup:GetRandom() -- Wrapper.Group#GROUP
if ArtilleryTime[Artillery] and ArtilleryTime[Artillery] <= timer.getTime() - ArtilleryAim then
ArtilleryTime[Artillery] = nil
end
if not ArtilleryTime[Artillery] then
local Task = Artillery:TaskFireAtPoint( DetectedUnit:GetVec2(), 500, 4 ) -- Fire 2 rockets to the target point.
Artillery:SetTask( Task, 0.5 )
ArtilleryTime[Artillery] = timer.getTime()
end
end
end

View File

@ -0,0 +1,25 @@
---
-- Name: DET-900 - Detection Test with RED FACA
-- Author: FlightControl
-- Date Created: 06 Mar 2017
--
-- # Situation:
--
-- A red FACA is detecting targets while airborne.
-- Targets are grouped within areas. A detection range and zone range is given to group the detected units.
-- This demo will group blue vehicles in areas.
-- Upon the detection capabilities of the red FACA, the blue vehicles will be grouped when detected.
-- All blue vehicles have ROE on hold.
--
-- # Test cases:
--
-- 1. Observe the tyres put around the detected areas formed
-- 2. Observe the smoking of the units detected
-- 3. Observe the areas being flexibly changed very detection run.
local FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart()
local FACDetection = DETECTION_AREAS:New( FACSetGroup, 2000, 250 ):BoundDetectedZones():SmokeDetectedUnits()
FACDetection:__Start( 5 )

Some files were not shown because too many files have changed in this diff Show More