diff --git a/.gitignore b/.gitignore index c8cd38031..3496f3e86 100644 --- a/.gitignore +++ b/.gitignore @@ -222,3 +222,4 @@ _gsdata_/ #GITHUB .gitattributes .gitignore +Moose Test Missions/MOOSE_Test_Template.miz diff --git a/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch b/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch index 1545357c6..1f0c932a8 100644 --- a/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch +++ b/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch @@ -1,6 +1,6 @@ - + diff --git a/Moose Development/Maths/Probability factor models for Detection.xlsx b/Moose Development/Maths/Probability factor models for Detection.xlsx new file mode 100644 index 000000000..300f24e1e Binary files /dev/null and b/Moose Development/Maths/Probability factor models for Detection.xlsx differ diff --git a/Moose Development/Maths/Task Templates.xlsx b/Moose Development/Maths/Task Templates.xlsx new file mode 100644 index 000000000..0c2e0acb2 Binary files /dev/null and b/Moose Development/Maths/Task Templates.xlsx differ diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index c5c37cf4c..e20b2ca8f 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -161,11 +161,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event 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. @@ -176,11 +171,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event 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. -- @function [parent=#AI_CAS_ZONE] Engage @@ -408,7 +398,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) if Detected == true then self:E( {"Target: ", DetectedUnit } ) 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 ) end end @@ -439,8 +429,8 @@ end -- @param #string Event The Event 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 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 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, @@ -480,28 +470,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageRoute[#EngageRoute+1] = CurrentRoutePoint - if self.Controllable:IsNotInZone( self.EngageZone ) then - - -- Find a random 2D point in EngageZone. - local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToEngageZoneVec2 ) - - -- Obtain a 3D @{Point} from the 2D point + altitude. - local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) - - -- Create a route point of type air. - local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint - - end - +-- if self.Controllable:IsNotInZone( self.EngageZone ) then +-- +-- -- Find a random 2D point in EngageZone. +-- local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() +-- self:T2( ToEngageZoneVec2 ) +-- +-- -- Obtain a 3D @{Point} from the 2D point + altitude. +-- local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) +-- +-- -- Create a route point of type air. +-- local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( +-- self.PatrolAltType, +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- self.EngageSpeed, +-- true +-- ) +-- +-- EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint +-- +-- end +-- --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Find a random 2D point in EngageZone. @@ -556,9 +546,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, --- NOW ROUTE THE GROUP! self.Controllable:WayPointExecute( 1 ) - self:SetDetectionInterval( 10 ) + self:SetDetectionInterval( 2 ) self:SetDetectionActivated() - self:__Target( -10 ) -- Start Targetting + self:__Target( -2 ) -- Start Targetting end end diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index 862079368..4fb1fb3f2 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -192,15 +192,6 @@ do -- ACT_ACCOUNT_DEADS self.TaskName = FsmAccount.TaskName end - - - function ACT_ACCOUNT_DEADS:_Destructor() - self:E("_Destructor") - - self:EventRemoveAll() - - end - --- Process Events --- StateMachine callback function @@ -231,7 +222,6 @@ do -- ACT_ACCOUNT_DEADS if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then 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." ) end end @@ -244,7 +234,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string To 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 ) else self:__NoMore( 1 ) @@ -259,7 +249,7 @@ do -- ACT_ACCOUNT_DEADS self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then - self:__Event( 1, EventData ) + self:Event( EventData ) end end diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index e78ca3b62..25a62656c 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -175,7 +175,7 @@ do -- ACT_ASSIGN_ACCEPT self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign() + self.Task:Assign( ProcessUnit, self.Task ) end end -- ACT_ASSIGN_ACCEPT diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index 23c5fc056..439abc49d 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -108,7 +108,7 @@ do -- ACT_ASSIST function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) + local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) local function MenuSmoke( 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.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) 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 diff --git a/Moose Development/Moose/Actions/Act_JTAC.lua b/Moose Development/Moose/Actions/Act_JTAC.lua index 668abfcb5..2dd363f58 100644 --- a/Moose Development/Moose/Actions/Act_JTAC.lua +++ b/Moose Development/Moose/Actions/Act_JTAC.lua @@ -56,8 +56,7 @@ function PROCESS_JTAC:New( Task, ProcessUnit, TargetSetUnit, FACUnit ) endstates = { 'Failed' } } ) - - _EVENTDISPATCHER:OnDead( self.EventDead, self ) + self:HandleEvent( EVENTS.Dead, self.EventDead ) return self end diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index 53fa5d9b9..ec5e9fff3 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -81,7 +81,7 @@ do -- ACT_ROUTE -- @type ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -177,6 +177,115 @@ do -- 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 @@ -184,7 +293,7 @@ do -- ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE 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. -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ROUTE_ZONE:New( TargetZone ) + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_ZONE:New( Zone ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - self.TargetZone = TargetZone + self.Zone = Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -209,7 +318,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:Init( FsmRoute ) - self.TargetZone = FsmRoute.TargetZone + self.Zone = FsmRoute.Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -217,18 +326,32 @@ do -- ACT_ROUTE_ZONE self.DisplayTime = 10 -- 10 seconds is the default 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. -- @param #ACT_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean 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." self:Message( RouteText ) end - return ProcessUnit:IsInZone( self.TargetZone ) + return ProcessUnit:IsInZone( self.Zone ) end --- Task Events @@ -241,11 +364,11 @@ do -- ACT_ROUTE_ZONE -- @param #string 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 TaskUnitVec2 = ProcessUnit:GetVec2() 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 ) end diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 8c1c6fad8..8d9840799 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -256,12 +256,14 @@ function BASE:_Destructor() --self:EventRemoveAll() end + +-- THIS IS WHY WE NEED LUA 5.2 ... function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- 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. - --[[ + local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -276,7 +278,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - --]] + end --- This is the worker method to inherit from a parent class. @@ -292,7 +294,7 @@ function BASE:Inherit( Child, Parent ) setmetatable( Child, Parent ) Child.__index = Child - Child:_SetDestructor() + --Child:_SetDestructor() end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 798c5e881..3e9eea077 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -54,6 +54,8 @@ DATABASE = { PLAYERSJOINED = {}, CLIENTS = {}, AIRBASES = {}, + COUNTRY_ID = {}, + COUNTRY_NAME = {}, NavPoints = {}, } @@ -761,6 +763,9 @@ function DATABASE:_RegisterTemplates() local CountryName = string.upper(cntry_data.name) 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]["countryId"] = cntry_data.id diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 58baff3d6..0e7d2b431 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -284,7 +284,7 @@ local _EVENTMETA = { }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, - Event = "OnEventTakeOff", + Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { @@ -425,11 +425,11 @@ function EVENT:Init( EventID, EventClass ) -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = {} + self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) end 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 return self.Events[EventID][EventPriority][EventClass] end @@ -499,11 +499,11 @@ end -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction -- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) + self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) end return self end @@ -517,9 +517,9 @@ end function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) self:F2( { EventID } ) - local Event = self:Init( EventID, EventClass ) - Event.EventFunction = EventFunction - Event.EventClass = EventClass + local EventData = self:Init( EventID, EventClass ) + EventData.EventFunction = EventFunction + EventData.EventClass = EventClass return self end @@ -535,13 +535,13 @@ end function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) - local Event = self:Init( EventID, EventClass ) - if not Event.EventUnit then - Event.EventUnit = {} + local EventData = self:Init( EventID, EventClass ) + if not EventData.EventUnit then + EventData.EventUnit = {} end - Event.EventUnit[UnitName] = {} - Event.EventUnit[UnitName].EventFunction = EventFunction - Event.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit[UnitName] = {} + EventData.EventUnit[UnitName].EventFunction = EventFunction + EventData.EventUnit[UnitName].EventClass = EventClass return self end @@ -576,51 +576,11 @@ do -- OnBirth function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) return self 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 do -- OnCrash @@ -634,49 +594,10 @@ do -- OnCrash function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) return self 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 @@ -691,96 +612,13 @@ do -- OnDead function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) return self 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 -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 --- Create an OnLand event handler for a group @@ -792,38 +630,11 @@ do -- OnLand function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) return self 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 do -- OnTakeOff @@ -836,38 +647,11 @@ do -- OnTakeOff function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) return self 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 do -- OnEngineShutDown @@ -881,210 +665,11 @@ do -- OnEngineShutDown function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) return self 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 @@ -1207,7 +792,9 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 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 @@ -1228,8 +815,10 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function 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( function() return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) @@ -1242,8 +831,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() 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 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( function() @@ -1271,8 +864,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -1290,9 +885,11 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.IniGroupName] then -- First test if a EventFunction is Set, otherwise search for the default function 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( function() return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) @@ -1305,8 +902,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -1318,8 +917,10 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.TgtGroupName] 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( function() return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) @@ -1332,7 +933,9 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() @@ -1354,8 +957,9 @@ function EVENT:onEvent( Event ) if EventData.EventFunction then -- 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( function() return EventData.EventFunction( EventClass, Event ) @@ -1367,11 +971,14 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() - return EventFunction( EventClass, Event ) + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value end, ErrorHandler ) end end @@ -1385,6 +992,8 @@ function EVENT:onEvent( Event ) else self:E( { _EVENTMETA[Event.id].Text, Event } ) end + + Event = nil end --- The EVENTHANDLER structure diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index c643f56bc..eb3cfe937 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -530,10 +530,20 @@ do -- FSM 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 self:T( "Calling " .. handler ) 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 end end @@ -732,8 +742,66 @@ do -- FSM_CONTROLLABLE self:SetControllable( Controllable ) 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 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. -- @param #FSM_CONTROLLABLE self @@ -801,12 +869,34 @@ do -- FSM_PROCESS function FSM_PROCESS:Init( FsmProcess ) self:T( "No Initialisation" ) 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. -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Copy( Controllable, Task ) self:T( { self:GetClassNameAndID() } ) + local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS @@ -825,7 +915,7 @@ do -- FSM_PROCESS -- Copy Processes 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 ) end @@ -843,6 +933,22 @@ do -- FSM_PROCESS return NewFsm 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. -- @param #FSM_PROCESS self diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 5dbab4fce..25ea2989e 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -138,6 +138,8 @@ do -- MENU_BASE } --- Consructor + -- @param #MENU_BASE + -- @return #MENU_BASE function MENU_BASE:New( MenuText, ParentMenu ) local MenuParentPath = {} @@ -150,10 +152,43 @@ do -- MENU_BASE self.MenuPath = nil self.MenuText = MenuText self.MenuParentPath = MenuParentPath + self.Menus = {} + self.MenuCount = 0 + self.MenuRemoveParent = false + self.MenuTime = timer.getTime() return self 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 do -- MENU_COMMAND_BASE @@ -161,7 +196,7 @@ do -- MENU_COMMAND_BASE --- The MENU_COMMAND_BASE class -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -170,6 +205,8 @@ do -- MENU_COMMAND_BASE } --- Constructor + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -189,7 +226,7 @@ do -- MENU_MISSION --- The MENU_MISSION class -- @type MENU_MISSION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -198,7 +235,7 @@ do -- MENU_MISSION -- @param #MENU_MISSION self -- @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). - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION: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! -- @param #MENU_MISSION self - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:RemoveSubMenus() self:F( self.MenuPath ) @@ -256,7 +293,7 @@ do -- MENU_MISSION_COMMAND --- The MENU_MISSION_COMMAND class -- @type MENU_MISSION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -306,7 +343,7 @@ do -- MENU_COALITION --- The MENU_COALITION class -- @type MENU_COALITION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- 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). @@ -380,7 +417,7 @@ do -- MENU_COALITION --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! -- @param #MENU_COALITION self - -- @return #MENU_COALITION self + -- @return #MENU_COALITION function MENU_COALITION:RemoveSubMenus() self:F( self.MenuPath ) @@ -411,7 +448,7 @@ do -- MENU_COALITION_COMMAND --- The MENU_COALITION_COMMAND class -- @type MENU_COALITION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -423,7 +460,7 @@ do -- MENU_COALITION_COMMAND -- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @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. - -- @return #MENU_COALITION_COMMAND self + -- @return #MENU_COALITION_COMMAND function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) 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. -- @type MENU_CLIENT - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -609,7 +646,7 @@ do -- MENU_CLIENT --- The MENU_CLIENT_COMMAND class -- @type MENU_CLIENT_COMMAND - -- @extends Menu#MENU_COMMAND + -- @extends Core.Menu#MENU_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -695,7 +732,7 @@ do --- The MENU_GROUP class -- @type MENU_GROUP - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -769,8 +806,6 @@ do self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) MenuGroup._Menus[Path] = self - self.Menus = {} - self.MenuGroup = MenuGroup self.Path = Path self.MenuGroupID = MenuGroup:GetID() @@ -780,8 +815,10 @@ do self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self:F( { self.ParentMenu.Menus, MenuText } ) + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end @@ -792,42 +829,56 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus() - self:F( self.MenuPath ) + function MENU_GROUP:RemoveSubMenus( MenuTime ) + self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + for MenuText, Menu in pairs( self.Menus ) do + Menu:Remove( MenuTime ) end end - + + --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #nil - function MENU_GROUP:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus() - - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] + self:RemoveSubMenus( MenuTime ) - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + 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 - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil end + return nil end --- The MENU_GROUP_COMMAND class -- @type MENU_GROUP_COMMAND - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -839,13 +890,14 @@ do -- @param ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @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, ... ) MenuGroup._Menus = MenuGroup._Menus or {} local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] + self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) MenuGroup._Menus[Path] = self @@ -856,33 +908,45 @@ do self.MenuText = MenuText 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 ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F( { ParentMenu.Menus, MenuText } ) end end - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - return self end --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self + -- @param MenuTime -- @return #nil - function MENU_GROUP_COMMAND:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP_COMMAND:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - 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 - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + + 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 return nil diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index a414bcc54..a7962e677 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -64,7 +64,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- 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. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -181,11 +181,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) + -- Only start when there is no ScheduleID defined! + -- This prevents to "Start" the scheduler twice with the same CallID... + if not Schedule[CallID].ScheduleID then + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Start( Scheduler, CallID ) -- Recursive @@ -198,7 +202,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then 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 for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Stop( Scheduler, CallID ) -- Recursive @@ -206,5 +215,13 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) 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 + diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index 1ade4a5de..2244d9b1b 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -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} -- -- The @{Scheduler#SCHEDULER} class creates schedule. @@ -147,6 +151,13 @@ function SCHEDULER:Remove( ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end +--- Clears all pending schedules. +-- @param #SCHEDULER self +function SCHEDULER:Clear() + self:F3( ) + + _SCHEDULEDISPATCHER:Clear( self ) +end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index f12e3312f..936a9c373 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -240,6 +240,7 @@ SET_BASE = { Filter = {}, Set = {}, 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. @@ -258,10 +259,14 @@ function SET_BASE:New( Database ) self.YieldInterval = 10 self.TimeInterval = 0.001 + self.Set = {} + self.List = {} self.List.__index = self.List self.List = setmetatable( { Count = 0 }, self.List ) + self.Index = {} + self.CallScheduler = SCHEDULER:New( self ) self:SetEventPriority( 2 ) @@ -313,6 +318,8 @@ function SET_BASE:Add( ObjectName, Object ) self.Set[ObjectName] = t._ + table.insert( self.Index, ObjectName ) + end --- 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 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 + end end @@ -384,12 +399,50 @@ function SET_BASE:Get( ObjectName ) 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. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - return self.List.Count + return #self.Index end @@ -652,7 +705,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen return false end - self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() return self end @@ -725,7 +779,7 @@ end --- SET_GROUP class -- @type SET_GROUP --- @extends #SET_BASE +-- @extends Core.Set#SET_BASE SET_GROUP = { ClassName = "SET_GROUP", Filter = { diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 4eb05b047..dedde1fb4 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1,5 +1,7 @@ --- **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: @@ -247,6 +249,58 @@ function ZONE_BASE:GetVec2() return nil 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. -- @param #ZONE_BASE self -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. @@ -261,6 +315,13 @@ function ZONE_BASE:GetRandomPointVec2() return nil 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. -- @param #ZONE_BASE self -- @return #nil The bounding square. @@ -347,8 +408,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @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 -function ZONE_RADIUS:BoundZone( Points ) +function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() @@ -364,8 +426,10 @@ function ZONE_RADIUS:BoundZone( Points ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local Tire = { - ["country"] = "USA", + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -377,7 +441,10 @@ function ZONE_RADIUS:BoundZone( Points ) ["heading"] = 0, } -- 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 return self @@ -810,8 +877,9 @@ end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self +-- @param #boolean UnBound If true, the tyres will be destroyed. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( ) +function ZONE_POLYGON_BASE:BoundZone( UnBound ) local i local j @@ -840,8 +908,11 @@ function ZONE_POLYGON_BASE:BoundZone( ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- 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 j = i diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index feb6a6d55..25c8742db 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -29,7 +29,9 @@ CLEANUP = { -- or -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- 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 } ) if type( ZoneNames ) == 'table' then @@ -41,7 +43,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - _EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) + self:HandleEvent( EVENTS.Birth ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) @@ -102,32 +104,24 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end -function CLEANUP:_OnEventBirth( Event ) - self:F( { Event } ) +--- @param #CLEANUP self +-- @param Core.Event#EVENTDATA EventData +function CLEANUP:_OnEventBirth( EventData ) + self:F( { EventData } ) - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.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() + self.CleanUpList[EventData.IniDCSUnitName] = {} + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName + 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 diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index e458e8cb5..1a722ddde 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -2,17 +2,17 @@ -- -- === -- --- 1) @{Detection#DETECTION_BASE} class, extends @{Base#BASE} --- ========================================================== --- The @{Detection#DETECTION_BASE} class defines the core functions to administer detected objects. --- The @{Detection#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- # 1) @{#DETECTION_BASE} class, extends @{Fsm#FSM} -- --- 1.1) DETECTION_BASE constructor --- ------------------------------- --- Construct a new DETECTION_BASE instance using the @{Detection#DETECTION_BASE.New}() method. +-- The @{#DETECTION_BASE} class defines the core functions to administer detected objects. +-- The @{#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- +-- ## 1.1) DETECTION_BASE constructor +-- +-- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. +-- +-- ## 1.2) DETECTION_BASE initialization -- --- 1.2) DETECTION_BASE initialization --- ---------------------------------- -- By default, detection will return detected objects with all the detection sensors available. -- However, you can ask how the objects were found with specific detection methods. -- If you use one of the below methods, the detection will work with the detection method specified. @@ -20,966 +20,2037 @@ -- -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: -- --- * @{Detection#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. --- * @{Detection#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. --- * @{Detection#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. --- * @{Detection#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. --- * @{Detection#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. --- * @{Detection#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. +-- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. +-- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. +-- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. +-- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. +-- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. +-- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. -- --- 1.3) Obtain objects detected by DETECTION_BASE --- ---------------------------------------------- --- DETECTION_BASE builds @{Set}s of objects detected. These @{Set#SET_BASE}s can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSets}(). --- The method will return a list (table) of @{Set#SET_BASE} objects. +-- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list +-- +-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later +-- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains +-- a SET_UNIT object that contains the detected units that belong to that group. +-- +-- Derived classes will apply different methods to group the detected units. +-- Examples are per area, per quadrant, per distance, per type. +-- See further the derived DETECTION classes on which grouping methods are currently supported. +-- +-- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: +-- +-- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. +-- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). +-- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information +-- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. +-- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). +-- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). +-- +-- ## 1.4) Apply additional Filters to fine-tune the detected objects +-- +-- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. +-- That being said, the DCS World detection algorithm can sometimes be unrealistic. +-- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. +-- Additionally, trees and other obstacles are not accounted during the DCS World detection. +-- +-- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. +-- For electronic detection, this filtering is not applied, only for visually detected targets. +-- +-- The following additional filtering can be applied for visual filtering: +-- +-- * A probability factor per kilometer distance. +-- * A probability factor based on the alpha angle between the detected object and the unit detecting. +-- A detection from a higher altitude allows for better detection than when on the ground. +-- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. +-- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. +-- +-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. +-- Only when you experience unrealistic behaviour in your missions, these filters could be applied. +-- +-- ### 1.4.1 ) Distance visual detection probability +-- +-- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. +-- Also, the speed of accurate detection plays a role. +-- +-- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. +-- +-- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +-- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. +-- +-- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! +-- +-- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. +-- +-- ### 1.4.2 ) Alpha Angle visual detection probability +-- +-- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. +-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. +-- +-- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. +-- +-- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +-- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% +-- +-- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. +-- +-- ### 1.4.3 ) Cloudy Zones detection probability +-- +-- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. +-- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +-- zones that reflect cloudy areas where detected units may not be so easily visually detected. +-- +-- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. +-- +-- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take +-- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. +-- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! +-- +-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for +-- AI not to detect so easily targets within a forrest or village rich area. +-- +-- ## 1.5 ) Accept / Reject detected units +-- +-- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, +-- if it is located in range or located inside or outside of specific zones. +-- +-- ### 1.5.1 ) Detection acceptance of within range limit +-- +-- A range can be set that will limit a successful detection for a unit. +-- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will accept detected units if the range is below 5000 meters. +-- Detection:SetAcceptRange( 5000 ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- +-- ### 1.5.2 ) Detection acceptance if within zone(s). +-- +-- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). +-- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Search fo the zones where units are to be accepted. +-- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) +-- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. +-- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- ### 1.5.3 ) Detection rejectance if within zone(s). +-- +-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). +-- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. +-- An example of how to use the method is shown below. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Search fo the zones where units are to be rejected. +-- local ZoneReject1 = ZONE:New( "RejectZone1" ) +-- local ZoneReject2 = ZONE:New( "RejectZone2" ) +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. +-- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- ## 1.6) DETECTION_BASE is a Finite State Machine +-- +-- Various Events and State Transitions can be tailored using DETECTION_BASE. +-- +-- ### 1.6.1) DETECTION_BASE States +-- +-- * **Detecting**: The detection is running. +-- * **Stopped**: The detection is stopped. +-- +-- ### 1.6.2) DETECTION_BASE Events +-- +-- * **Start**: Start the detection process. +-- * **Detect**: Detect new units. +-- * **Detected**: New units have been detected. +-- * **Stop**: Stop the detection process. -- -- === -- --- 2) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} --- =============================================================================== +-- # 2) @{Detection#DETECTION_UNITS} class, extends @{Detection#DETECTION_BASE} +-- +-- The @{Detection#DETECTION_UNITS} class will detect units within the battle zone. +-- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. +-- Beware that when the amount of units detected is large, the DetectedItems list will be large also. +-- +-- # 3) @{Detection#DETECTION_TYPES} class, extends @{Detection#DETECTION_BASE} +-- +-- The @{Detection#DETECTION_TYPES} class will detect units within the battle zone. +-- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. +-- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. +-- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. +-- +-- # 4) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} +-- -- The @{Detection#DETECTION_AREAS} class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. -- The class is group the detected units within zones given a DetectedZoneRange parameter. -- A set with multiple detected zones will be created as there are groups of units detected. -- --- 2.1) Retrieve the Detected Unit sets and Detected Zones --- ------------------------------------------------------- --- The DetectedUnitSets methods are implemented in @{Detection#DECTECTION_BASE} and the DetectedZones methods is implemented in @{Detection#DETECTION_AREAS}. +-- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones -- --- Retrieve the DetectedUnitSets with the method @{Detection#DETECTION_BASE.GetDetectedSets}(). A table will be return of @{Set#SET_UNIT}s. --- To understand the amount of sets created, use the method @{Detection#DETECTION_BASE.GetDetectedSetCount}(). --- If you want to obtain a specific set from the DetectedSets, use the method @{Detection#DETECTION_BASE.GetDetectedSet}() with a given index. +-- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and +-- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. +-- +-- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. -- -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. -- --- 1.4) Flare or Smoke detected units --- ---------------------------------- +-- ## 4.4) Flare or Smoke detected units +-- -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. -- --- 1.5) Flare or Smoke detected zones --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedZones}() or @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to flare or smoke the detected zones when a new detection has taken place. +-- ## 4.5) Flare or Smoke or Bound detected zones +-- +-- Use the methods: +-- +-- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color +-- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color +-- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag +-- +-- the detected zones when a new detection has taken place. -- -- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * Mechanist : Early concept of DETECTION_AREAS. -- -- ### Authors: -- --- * FlightControl : Design & Programming +-- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Detection +do -- DETECTION_BASE ---- DETECTION_BASE class --- @type DETECTION_BASE --- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. --- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. --- @field #number DetectionRun --- @extends Core.Base#BASE -DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, -} - ---- @type DETECTION_BASE.DetectedObjects --- @list <#DETECTION_BASE.DetectedObject> - ---- @type DETECTION_BASE.DetectedObject --- @field #string Name --- @field #boolean Visible --- @field #string Type --- @field #number Distance --- @field #boolean Identified - ---- DETECTION constructor. --- @param #DETECTION_BASE self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @return #DETECTION_BASE self -function DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + --- DETECTION_BASE class + -- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + DETECTION_BASE = { + ClassName = "DETECTION_BASE", + DetectionSetGroup = nil, + DetectionRange = nil, + DetectedObjects = {}, + DetectionRun = 0, + DetectedObjectsIdentified = {}, + DetectedItems = {}, + } - self.DetectionSetGroup = DetectionSetGroup - self.DetectionRange = DetectionRange + --- @type DETECTION_BASE.DetectedObjects + -- @list <#DETECTION_BASE.DetectedObject> - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + --- @type DETECTION_BASE.DetectedObject + -- @field #string Name + -- @field #boolean Visible + -- @field #string Type + -- @field #number Distance + -- @field #boolean Identified - return self -end - ---- Detect Visual. --- @param #DETECTION_BASE self --- @param #boolean DetectVisual --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual -end - ---- Detect Optical. --- @param #DETECTION_BASE self --- @param #boolean DetectOptical --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical -end - ---- Detect Radar. --- @param #DETECTION_BASE self --- @param #boolean DetectRadar --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar -end - ---- Detect IRST. --- @param #DETECTION_BASE self --- @param #boolean DetectIRST --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST -end - ---- Detect RWR. --- @param #DETECTION_BASE self --- @param #boolean DetectRWR --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR -end - ---- Detect DLINK. --- @param #DETECTION_BASE self --- @param #boolean DetectDLINK --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK -end - ---- Determines if a detected object has already been identified during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject --- @return #boolean true if already identified. -function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified -end - ---- Identifies a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true -end - ---- UnIdentify a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false -end - ---- UnIdentify all detected objects during detection processing. --- @param #DETECTION_BASE self -function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. -end - ---- Gets a detected object with a given name. --- @param #DETECTION_BASE self --- @param #string ObjectName --- @return #DETECTION_BASE.DetectedObject -function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F3( ObjectName ) + --- @type DETECTION_BASE.DetectedItems + -- @list <#DETECTION_BASE.DetectedItem> - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] + --- @type DETECTION_BASE.DetectedItem + -- @field Core.Set#SET_UNIT Set + -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. + -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. + -- @field #boolean Changed Documents if the detected area has changes. + -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). + -- @field #number ItemID -- The identifier of the detected area. + -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. + -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + --- DETECTION constructor. + -- @param #DETECTION_BASE self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return #DETECTION_BASE self + function DETECTION_BASE:New( DetectionSetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE + + self.DetectedItemCount = 0 + self.DetectedItemMax = 0 + self.DetectedItems = {} + + self.DetectionSetGroup = DetectionSetGroup + + self.DetectionInterval = 30 + + self:InitDetectVisual( true ) + self:InitDetectOptical( false ) + self:InitDetectRadar( false ) + self:InitDetectRWR( false ) + self:InitDetectIRST( false ) + self:InitDetectDLINK( false ) + + -- Create FSM transitions. + + self:SetStartState( "Stopped" ) + self.CountryID = DetectionSetGroup:GetFirst():GetCountry() + + self:AddTransition( "Stopped", "Start", "Detecting") + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE 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 Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnBeforeStart + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnAfterStart + -- @param #DETECTION_BASE 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=#DETECTION_BASE] Start + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] __Start + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnLeaveDetecting + -- @param #DETECTION_BASE 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 Detecting. + -- @function [parent=#DETECTION_BASE] OnEnterDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + self:AddTransition( "Detecting", "Detect", "Detecting" ) + self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + + --- OnBefore Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnBeforeDetect + -- @param #DETECTION_BASE 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 Detect. + -- @function [parent=#DETECTION_BASE] OnAfterDetect + -- @param #DETECTION_BASE 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 Detect. + -- @function [parent=#DETECTION_BASE] Detect + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] __Detect + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Detecting", "Detected", "Detecting" ) + + --- OnBefore Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnBeforeDetected + -- @param #DETECTION_BASE 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 Detected. + -- @function [parent=#DETECTION_BASE] OnAfterDetected + -- @param #DETECTION_BASE 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 Detected. + -- @function [parent=#DETECTION_BASE] Detected + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] __Detected + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnBeforeStop + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnAfterStop + -- @param #DETECTION_BASE 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=#DETECTION_BASE] Stop + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] __Stop + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE 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 Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + return self + end + + do -- State Transition Handling + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterStart(From,Event,To) + self:__Detect(0.1) + end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterDetect(From,Event,To) + self:E( {From,Event,To}) + + local DetectDelay = 0.1 + self.DetectionCount = 0 + self.DetectionRun = 0 + self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table + + self.DetectionSetGroup:Flush() + + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + self:E( {DetectionGroupData}) + self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + self.DetectionCount = self.DetectionCount + 1 + DetectDelay = DetectDelay + 0.1 end end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) + self:E( {From,Event,To}) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if DetectionGroup:IsAlive() then + + self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = DetectionGroup:GetName() + + local DetectedUnits = {} + + local DetectedTargets = DetectionGroup:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T( DetectedTargets ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + self:T2( DetectedObject ) + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) + + -- Calculate Acceptance + + if self.AcceptRange and Distance > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + DetectionAccepted = false + end + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:E({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = {} + end + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Visible = Detection.visible + self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].Distance = Distance + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + end + + self:T2( self.DetectedObjects ) + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + self:__Detect( self.DetectionInterval ) + + self:T( "--> Create Detection Sets" ) + self:CreateDetectionSets() + end + + end + + + end + + do -- Initialization methods + + --- Detect Visual. + -- @param #DETECTION_BASE self + -- @param #boolean DetectVisual + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectVisual( DetectVisual ) + + self.DetectVisual = DetectVisual + end + + --- Detect Optical. + -- @param #DETECTION_BASE self + -- @param #boolean DetectOptical + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectOptical( DetectOptical ) + self:F2() + + self.DetectOptical = DetectOptical + end + + --- Detect Radar. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRadar + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRadar( DetectRadar ) + self:F2() + + self.DetectRadar = DetectRadar + end + + --- Detect IRST. + -- @param #DETECTION_BASE self + -- @param #boolean DetectIRST + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectIRST( DetectIRST ) + self:F2() + + self.DetectIRST = DetectIRST + end + + --- Detect RWR. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRWR + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRWR( DetectRWR ) + self:F2() + + self.DetectRWR = DetectRWR + end + + --- Detect DLINK. + -- @param #DETECTION_BASE self + -- @param #boolean DetectDLINK + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) + self:F2() + + self.DetectDLINK = DetectDLINK + end + + end + + do + + --- Set the detection interval time in seconds. + -- @param #DETECTION_BASE self + -- @param #number DetectionInterval Interval in seconds. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + self:F2() + + self.DetectionInterval = DetectionInterval + + return self + end + + end + + do -- Accept / Reject detected units + + --- Accept detections if within a range in meters. + -- @param #DETECTION_BASE self + -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptRange( AcceptRange ) + self:F2() + + self.AcceptRange = AcceptRange + + return self + end + + --- Accept detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptZones( AcceptZones ) + self:F2() + + if type( AcceptZones ) == "table" then + self.AcceptZones = AcceptZones + else + self.AcceptZones = { AcceptZones } + end + + return self + end + + --- Reject detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRejectZones( RejectZones ) + self:F2() + + if type( RejectZones ) == "table" then + self.RejectZones = RejectZones + else + self.RejectZones = { RejectZones } + end + + return self + end + + end + + do -- Probability methods + + --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- @param #DETECTION_BASE self + -- @param DistanceProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) + self:F2() + + self.DistanceProbability = DistanceProbability + + return self + end + + + --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- @param #DETECTION_BASE self + -- @param AlphaAngleProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) + self:F2() + + self.AlphaAngleProbability = AlphaAngleProbability + + return self + end + + --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- @param #DETECTION_BASE self + -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetZoneProbability( ZoneArray ) + self:F2() + + self.ZoneProbability = ZoneArray + + return self + end + + + end + + do -- Change processing + + --- Accepts changes from the detected item. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #DETECTION_BASE self + function DETECTION_BASE:AcceptChanges( DetectedItem ) + + DetectedItem.Changed = false + DetectedItem.Changes = {} + + return self + end + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) + + return self + end + + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @param #string ChangeUnitType + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 + DetectedItem.Changes[ChangeCode].ItemID = ItemID + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + + return self + end + + + end + + do -- Threat + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Background worker function to determine if there are friendlies nearby ... + -- @param #DETECTION_BASE self + function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) + self:F2() + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() + + DetectedItem.FriendliesNearBy = false + + if DetectedUnit then + + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = DetectedUnit:GetVec3(), + radius = 6000, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = true + return false + end + + return true + end + + world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + end + end + + end + + --- Determines if a detected object has already been identified during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + -- @return #boolean true if already identified. + function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) + self:F3( DetectedObject.Name ) + + local DetectedObjectName = DetectedObject.Name + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + end + + --- Identifies a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) + self:F( { "Identified:", DetectedObject.Name } ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = true + end + + --- UnIdentify a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = false + end + + --- UnIdentify all detected objects during detection processing. + -- @param #DETECTION_BASE self + function DETECTION_BASE:UnIdentifyAllDetectedObjects() + + self.DetectedObjectsIdentified = {} -- Table will be garbage collected. + end + + --- Gets a detected object with a given name. + -- @param #DETECTION_BASE self + -- @param #string ObjectName + -- @return #DETECTION_BASE.DetectedObject + function DETECTION_BASE:GetDetectedObject( ObjectName ) + self:F( ObjectName ) + + if ObjectName then + local DetectedObject = self.DetectedObjects[ObjectName] + + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end + end + end + + return nil + end + + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + + local DetectedItem = {} + self.DetectedItemCount = self.DetectedItemCount + 1 + self.DetectedItemMax = self.DetectedItemMax + 1 + + if DetectedItemIndex then + self.DetectedItems[DetectedItemIndex] = DetectedItem + else + self.DetectedItems[self.DetectedItemCount] = DetectedItem + end + + DetectedItem.Set = Set or SET_UNIT:New() + DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Removed = false + + return DetectedItem + end + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) + + local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + + DetectedItem.Zone = Zone + + return DetectedItem + end + + --- Removes an existing DetectedItem from the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. + function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) + + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end + + + --- Get the detected @{Set#SET_BASE}s. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItems + function DETECTION_BASE:GetDetectedItems() + + return self.DetectedItems + end + + --- Get the amount of SETs with detected objects. + -- @param #DETECTION_BASE self + -- @return #number Count + function DETECTION_BASE:GetDetectedItemsCount() + + local DetectedCount = self.DetectedItemCount + return DetectedCount + end + + --- Get a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return DETECTION_BASE.DetectedItem + function DETECTION_BASE:GetDetectedItem( Index ) + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem + end + + return nil + end + + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Set#SET_UNIT DetectedSet + function DETECTION_BASE:GetDetectedSet( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSetUnit = DetectedItem.Set + if DetectedSetUnit then + return DetectedSetUnit + end + + return nil + end + + do -- Zones + + --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Zone#ZONE_UNIT DetectedZone + function DETECTION_BASE:GetDetectedZone( Index ) + + local DetectedZone = self.DetectedItems[Index].Zone + if DetectedZone then + return DetectedZone + end + + return nil + end + + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemReportSummary( Index ) + self:F( Index ) + return nil + end + + --- Report detailed of a detectedion result. + -- @param #DETECTION_BASE self + -- @return #string + function DETECTION_BASE:DetectedReportDetailed() + self:F() + return nil + end + + --- Get the detection Groups. + -- @param #DETECTION_BASE self + -- @return Wrapper.Group#GROUP + function DETECTION_BASE:GetDetectionSetGroup() + + local DetectionSetGroup = self.DetectionSetGroup + return DetectionSetGroup + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE self + function DETECTION_BASE:CreateDetectionSets() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + + end + + + --- Schedule the DETECTION construction. + -- @param #DETECTION_BASE self + -- @param #number DelayTime The delay in seconds to wait the reporting. + -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. + -- @return #DETECTION_BASE self + function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) + self:F2() + + self.ScheduleDelayTime = DelayTime + self.ScheduleRepeatInterval = RepeatInterval + + self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) + return self + end + +end + +do -- DETECTION_UNITS + + --- DETECTION_UNITS class + -- @type DETECTION_UNITS + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @extends #DETECTION_BASE + DETECTION_UNITS = { + ClassName = "DETECTION_UNITS", + DetectionRange = nil, + } + + --- DETECTION_UNITS constructor. + -- @param Functional.Detection#DETECTION_UNITS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return Functional.Detection#DETECTION_UNITS self + function DETECTION_UNITS:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_UNITS self + -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_UNITS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_UNITS self + -- @return #DETECTION_UNITS self + function DETECTION_UNITS:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + self:E( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedUnitName ) + if not DetectedItem then + self:T( "Added new DetectedItem" ) + DetectedItem = self:AddDetectedItem( DetectedUnitName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObjectData.Name + DetectedItem.Visible = DetectedObjectData.Visible + DetectedItem.Distance = DetectedObjectData.Distance + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @return #string + function DETECTION_UNITS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSet = self:GetDetectedSet( Index ) + + self:T( DetectedSet ) + if DetectedSet then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + + if DetectedItemUnit then + self:T(DetectedItemUnit) + + local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" + local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" + + if DetectedItem.Type and UnitCategoryName and UnitCategoryType then + UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + else + UnitCategoryText = "Unknown target at " + end + + if DetectedItem.Visible == false then + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" + else + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" + end + + ReportSummary = string.format( + "%s%s", + UnitCategoryText, + UnitDistanceText + ) + end + + self:T( ReportSummary ) + + return ReportSummary + end end - return nil + --- Report detailed of a detection result. + -- @param #DETECTION_UNITS self + -- @return #string + function DETECTION_UNITS:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected units:" ) + for DetectedItemID, DetectedItem in ipairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + end ---- Get the detected @{Set#SET_BASE}s. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE.DetectedSets DetectedSets -function DETECTION_BASE:GetDetectedSets() +do -- DETECTION_TYPES - local DetectionSets = self.DetectedSets - return DetectionSets -end - ---- Get the amount of SETs with detected objects. --- @param #DETECTION_BASE self --- @return #number Count -function DETECTION_BASE:GetDetectedSetCount() - - local DetectionSetCount = #self.DetectedSets - return DetectionSetCount -end - ---- Get a SET of detected objects using a given numeric index. --- @param #DETECTION_BASE self --- @param #number Index --- @return Core.Set#SET_BASE -function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectionSet = self.DetectedSets[Index] - if DetectionSet then - return DetectionSet + --- DETECTION_TYPES class + -- @type DETECTION_TYPES + -- @extends #DETECTION_BASE + DETECTION_TYPES = { + ClassName = "DETECTION_TYPES", + DetectionRange = nil, + } + + --- DETECTION_TYPES constructor. + -- @param Functional.Detection#DETECTION_TYPES self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. + -- @return Functional.Detection#DETECTION_TYPES self + function DETECTION_TYPES:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self end - return nil -end - ---- Get the detection Groups. --- @param #DETECTION_BASE self --- @return Wrapper.Group#GROUP -function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup -end - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE self -function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - -end - - ---- Schedule the DETECTION construction. --- @param #DETECTION_BASE self --- @param #number DelayTime The delay in seconds to wait the reporting. --- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. --- @return #DETECTION_BASE self -function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_TYPES self + -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_TYPES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self -end - - ---- Form @{Set}s of detected @{Unit#UNIT}s in an array of @{Set#SET_BASE}s. --- @param #DETECTION_BASE self -function DETECTION_BASE:_DetectionScheduler( SchedulerName ) - self:F2( { SchedulerName } ) + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end - self.DetectionRun = self.DetectionRun + 1 - - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - local DetectionGroup = DetectionGroupData -- Wrapper.Group#GROUP - - if DetectionGroup:IsAlive() then - - local DetectionGroupName = DetectionGroup:GetName() + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end - local DetectionDetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_TYPES self + -- @return #DETECTION_TYPES self + function DETECTION_TYPES:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem:GetSet() -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + if not DetectedItem then + DetectedItem = self:AddDetectedItem( DetectedTypeName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @return #string + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() + + local ReportSummary = string.format( + "Type #%s - Threat Level [%s] (%2d)", + DetectedItem.Type, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G + ) + self:T( ReportSummary ) + + return ReportSummary + end + end + + --- Report detailed of a detection result. + -- @param #DETECTION_TYPES self + -- @return #string + function DETECTION_TYPES:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected types:" ) + for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + +end + + +do -- DETECTION_AREAS + + --- DETECTION_AREAS class + -- @type DETECTION_AREAS + -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends #DETECTION_BASE + DETECTION_AREAS = { + ClassName = "DETECTION_AREAS", + DetectionZoneRange = nil, + } + + + --- DETECTION_AREAS constructor. + -- @param #DETECTION_AREAS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @return #DETECTION_AREAS + function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) + + self.DetectionZoneRange = DetectionZoneRange + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_AREAS self + -- @param Index + -- @return #string + function DETECTION_AREAS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedZone( Index ) + local DetectedItemPointVec3 = DetectedZone:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + local ReportSummary = string.format( + "%s - Threat Level [%s] (%2d)", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G ) - for DetectionDetectedTargetID, DetectionDetectedTarget in pairs( DetectionDetectedTargets ) do - local DetectionObject = DetectionDetectedTarget.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectionObject ) + return ReportSummary + end + + return nil + end + + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_AREAS self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Calculate the maxium A2G threat level of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) + + local MaxThreatLevelA2G = 0 + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local ThreatUnit = UnitData -- Wrapper.Unit#UNIT + local ThreatLevelA2G = ThreatUnit:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + end + end + + self:T3( MaxThreatLevelA2G ) + DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + + end + + --- Find the nearest FAC of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return Wrapper.Unit#UNIT The nearest FAC unit + function DETECTION_AREAS:NearestFAC( DetectedItem ) + + local NearestFAC = nil + local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + + for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do + local FACUnit = FACUnitData -- Wrapper.Unit#UNIT + if FACUnit:IsActive() then + local Vec3 = FACUnit:GetVec3() + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) + if Distance < MinDistance then + MinDistance = Distance + NearestFAC = FACUnit + end + end + end + end + + DetectedItem.NearestFAC = NearestFAC + + end + + --- Returns the A2G threat level of the units in the DetectedItem + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #number a scale from 0 to 10. + function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) + + self:T3( DetectedItem.MaxThreatLevelA2G ) + return DetectedItem.MaxThreatLevelA2G + end + + + + --- Smoke the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_AREAS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:CreateDetectionSets() + self:F2() + + + self:T( "Checking Detected Items for new Detected Units ..." ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + + self:T( { "Detected Item ID:", DetectedItemID } ) - if DetectionObject and DetectionObject:isExist() and DetectionObject.id_ < 50000000 then - - local DetectionDetectedObjectName = DetectionObject:getName() - - local DetectionDetectedObjectPositionVec3 = DetectionObject:getPoint() - local DetectionGroupVec3 = DetectionGroup:GetVec3() - - local Distance = ( ( DetectionDetectedObjectPositionVec3.x - DetectionGroupVec3.x )^2 + - ( DetectionDetectedObjectPositionVec3.y - DetectionGroupVec3.y )^2 + - ( DetectionDetectedObjectPositionVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T2( { DetectionGroupName, DetectionDetectedObjectName, Distance } ) - - if Distance <= self.DetectionRange then - - if not self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = {} - end - self.DetectedObjects[DetectionDetectedObjectName].Name = DetectionDetectedObjectName - self.DetectedObjects[DetectionDetectedObjectName].Visible = DetectionDetectedTarget.visible - self.DetectedObjects[DetectionDetectedObjectName].Type = DetectionDetectedTarget.type - self.DetectedObjects[DetectionDetectedObjectName].Distance = DetectionDetectedTarget.distance - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = nil - end - end - end - end - self:T2( self.DetectedObjects ) - - -- okay, now we have a list of detected object names ... - -- Sort the table based on distance ... - table.sort( self.DetectedObjects, function( a, b ) return a.Distance < b.Distance end ) - end - end - - if self.DetectedObjects then - self:CreateDetectionSets() - end - - return true -end - - - ---- DETECTION_AREAS class --- @type DETECTION_AREAS --- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @field #DETECTION_AREAS.DetectedAreas DetectedAreas A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. --- @extends Functional.Detection#DETECTION_BASE -DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectedAreas = { n = 0 }, - DetectionZoneRange = nil, -} - ---- @type DETECTION_AREAS.DetectedAreas --- @list <#DETECTION_AREAS.DetectedArea> - ---- @type DETECTION_AREAS.DetectedArea --- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @field #boolean Changed Documents if the detected area has changes. --- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). --- @field #number AreaID -- The identifier of the detected area. --- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. --- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - ---- DETECTION_AREAS constructor. --- @param Functional.Detection#DETECTION_AREAS self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @return Functional.Detection#DETECTION_AREAS self -function DETECTION_AREAS:New( DetectionSetGroup, DetectionRange, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - - self:Schedule( 10, 10 ) - - return self -end - ---- Add a detected @{#DETECTION_AREAS.DetectedArea}. --- @param Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @param Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @return #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:AddDetectedArea( Set, Zone ) - local DetectedAreas = self:GetDetectedAreas() - DetectedAreas.n = self:GetDetectedAreaCount() + 1 - DetectedAreas[DetectedAreas.n] = {} - local DetectedArea = DetectedAreas[DetectedAreas.n] - DetectedArea.Set = Set - DetectedArea.Zone = Zone - DetectedArea.Removed = false - DetectedArea.AreaID = DetectedAreas.n - - return DetectedArea -end - ---- Remove a detected @{#DETECTION_AREAS.DetectedArea} with a given Index. --- @param #DETECTION_AREAS self --- @param #number Index The Index of the detection are to be removed. --- @return #nil -function DETECTION_AREAS:RemoveDetectedArea( Index ) - local DetectedAreas = self:GetDetectedAreas() - local DetectedAreaCount = self:GetDetectedAreaCount() - local DetectedArea = DetectedAreas[Index] - local DetectedAreaSet = DetectedArea.Set - DetectedArea[Index] = nil - return nil -end - - ---- Get the detected @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS.DetectedAreas DetectedAreas -function DETECTION_AREAS:GetDetectedAreas() - - local DetectedAreas = self.DetectedAreas - return DetectedAreas -end - ---- Get the amount of @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #number DetectedAreaCount -function DETECTION_AREAS:GetDetectedAreaCount() - - local DetectedAreaCount = self.DetectedAreas.n - return DetectedAreaCount -end - ---- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Set#SET_UNIT DetectedSet -function DETECTION_AREAS:GetDetectedSet( Index ) - - local DetectedSetUnit = self.DetectedAreas[Index].Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil -end - ---- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Zone#ZONE_UNIT DetectedZone -function DETECTION_AREAS:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedAreas[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil -end - ---- Background worker function to determine if there are friendlies nearby ... --- @param #DETECTION_AREAS self --- @param Wrapper.Unit#UNIT ReportUnit -function DETECTION_AREAS:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT - - DetectedArea.FriendliesNearBy = false - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedZoneUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedZoneUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedZoneUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedArea.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - -end - - - ---- Returns if there are friendlies nearby the FAC units ... --- @param #DETECTION_AREAS self --- @return #boolean trhe if there are friendlies nearby -function DETECTION_AREAS:IsFriendliesNearBy( DetectedArea ) - - self:T3( DetectedArea.FriendliesNearBy ) - return DetectedArea.FriendliesNearBy or false -end - ---- Calculate the maxium A2G threat level of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedArea ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedArea.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedArea.MaxThreatLevelA2G = MaxThreatLevelA2G - -end - ---- Find the nearest FAC of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return Wrapper.Unit#UNIT The nearest FAC unit -function DETECTION_AREAS:NearestFAC( DetectedArea ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedArea.NearestFAC = NearestFAC - -end - ---- Returns the A2G threat level of the units in the DetectedArea --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #number a scale from 0 to 10. -function DETECTION_AREAS:GetTreatLevelA2G( DetectedArea ) - - self:T3( DetectedArea.MaxThreatLevelA2G ) - return DetectedArea.MaxThreatLevelA2G -end - - - ---- Smoke the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self -end - ---- Flare the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self -end - ---- Smoke the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self -end - ---- Flare the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self -end - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeArea( DetectedArea, ChangeCode, AreaUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode].AreaID = AreaID - DetectedArea.Changes[ChangeCode].AreaUnitType = AreaUnitType - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, AreaUnitType } ) - - return self -end - - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @param #string ChangeUnitType --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeUnit( DetectedArea, ChangeCode, ChangeUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedArea.Changes[ChangeCode].AreaID = AreaID - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, ChangeUnitType } ) - - return self -end - ---- Make text documenting the changes of the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #string The Changes text -function DETECTION_AREAS:GetChangeText( DetectedArea ) - self:F( DetectedArea ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedArea.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.AreaID .. ". The center target is a " .. ChangeData.AreaUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". The new center target is a " .. ChangeData.AreaUnitType "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.AreaID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.AreaID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.AreaID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - -end - - ---- Accepts changes from the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AcceptChanges( DetectedArea ) - - DetectedArea.Changed = false - DetectedArea.Changes = {} - - return self -end - - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - - local DetectedSet = DetectedArea.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( DetectedArea.Zone.ZoneUNIT.UnitName ) - local DetectedZoneObject = self:GetDetectedObject( DetectedArea.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detecting Zone Object", DetectedArea.AreaID, DetectedArea.Zone, DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedArea.Zone.ZoneUNIT.UnitName ) - - self:AddChangeArea( DetectedArea, 'RAU', "Dummy" ) + local DetectedSet = DetectedItem.Set - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - -- Assign the Unit as the new center unit of the detected area. - DetectedArea.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeArea( DetectedArea, "AAU", DetectedArea.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedArea.Zone - if DetectedUnit:IsInZone( DetectedArea.Zone ) then + local AreaExists = false -- This flag will determine of the detected area is still existing. - -- Yes, the DetectedUnit is within the DetectedArea.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedArea.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedArea, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedArea, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - self:RemoveDetectedArea( DetectedAreaID ) - self:AddChangeArea( DetectedArea, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do + -- First test if the center unit is detected in the detection area. + self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) + local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) + self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - self:T( "Detection Area #" .. DetectedArea.AreaID ) - local DetectedSet = DetectedArea.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedArea.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedArea, "AU", DetectedUnit:GetTypeName() ) + if DetectedZoneObject then + + --self:IdentifyDetectedObject( DetectedZoneObject ) + AreaExists = true + + + + else + -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. + -- First remove the center unit from the set. + DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) + + self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + + -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + + -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. + -- If the DetectedUnit was already identified, DetectedObject will be nil. + if DetectedObject then + self:IdentifyDetectedObject( DetectedObject ) + AreaExists = true + + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + + -- Assign the Unit as the new center unit of the detected area. + DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) + + self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + + -- We don't need to add the DetectedObject to the area set, because it is already there ... + break + end end end + + -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. + -- Note that the position of the area may have moved due to the center unit repositioning. + -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. + if AreaExists then + + -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... + -- Those units within the zone are flagged as Identified. + -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Check if the DetectedUnit is within the DetectedItem.Zone + if DetectedUnit:IsInZone( DetectedItem.Zone ) then + + -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. + self:IdentifyDetectedObject( DetectedObject ) + + else + -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + end + + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) + DetectedSet:Remove( DetectedUnitName ) + + -- The DetectedObject has been identified, because it does not exist ... + -- self:IdentifyDetectedObject( DetectedObject ) + end + end + else + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + self:RemoveDetectedItem( DetectedItemID ) + self:AddChangeItem( DetectedItem, "RA" ) + end end + end - if AddedToDetectionArea == false then + -- We iterated through the existing detection areas and: + -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. + -- - We recentered the detection area to new center units where it was needed. + -- + -- Now we need to loop through the unidentified detected units and see where they belong: + -- - They can be added to a new detection area and become the new center unit. + -- - They can be added to a new detection area. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - -- New detection area - local DetectedArea = self:AddDetectedArea( - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedArea.Zone.ZoneUNIT.UnitName ) - DetectedArea.Set:AddUnit( DetectedUnit ) - self:AddChangeArea( DetectedArea, "AA", DetectedUnit:GetTypeName() ) - end - end - end + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + + if DetectedObject then - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - self:ReportFriendliesNearBy( { DetectedArea = DetectedArea, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedArea ) -- Calculate A2G threat level - self:NearestFAC( DetectedArea ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedArea.AreaID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() + -- We found an unidentified unit outside of any existing detection area. + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + local AddedToDetectionArea = false + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + self:T( "Detection Area #" .. DetectedItem.ItemID ) + local DetectedSet = DetectedItem.Set + if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then + self:IdentifyDetectedObject( DetectedObject ) + DetectedSet:AddUnit( DetectedUnit ) + AddedToDetectionArea = true + self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + end end end + + if AddedToDetectionArea == false then + + -- New detection area + local DetectedItem = self:AddDetectedItemZone( nil, + SET_UNIT:New(), + ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) + ) + --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + end end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:NearestFAC( DetectedItem ) + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + DetectedZone:BoundZone( 12, self.CountryID ) + end end + end - -end - - + +end diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 2f159a561..dc2d62c44 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -84,7 +84,7 @@ -- -- 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.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.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. -- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission +-- @field Functional.Detection#DETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", EscortName = nil, -- The Escort Name @@ -176,14 +177,22 @@ ESCORT = { -- -- 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." ) 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.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortName = EscortName 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. if not self.EscortClient._EscortGroups then self.EscortClient._EscortGroups = {} @@ -193,7 +202,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection end self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) @@ -218,13 +227,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.FollowDistance = 100 self.CT1 = 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 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 allows to visualize where the escort is flying to. -- @param #ESCORT self @@ -282,7 +308,7 @@ function ESCORT:MenuFollowAt( Distance ) self.EscortMenuJoinUpAndFollow = {} 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 end @@ -340,11 +366,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuHold, ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortGroup, - ParamHeight = Height, - ParamSeconds = Seconds - } + self, + self.EscortGroup, + Height, + Seconds ) end @@ -461,9 +486,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuScan, ESCORT._ScanTargets, - { ParamSelf = self, - ParamScanDuration = 30 - } + self, + 30 ) end @@ -493,11 +517,11 @@ function ESCORT:MenuFlare( MenuTextFormat ) end if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = 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.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.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.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.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, self, FLARECOLOR.Green, "Released a green 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, self, FLARECOLOR.White, "Released a white flare!" ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) end return self @@ -526,12 +550,12 @@ function ESCORT:MenuSmoke( MenuTextFormat ) end if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = 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.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.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.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.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.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, self, SMOKECOLOR.Green, "Releasing green 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, self, SMOKECOLOR.White, "Releasing white 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, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) end end @@ -556,9 +580,9 @@ function ESCORT:MenuReportTargets( Seconds ) end -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + 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, self, true ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) @@ -595,16 +619,16 @@ function ESCORT:MenuROE( MenuTextFormat ) -- Rules of Engagement self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) 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 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 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 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 @@ -624,16 +648,16 @@ function ESCORT:MenuEvasion( MenuTextFormat ) -- Reaction to Threats self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) 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 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 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 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 @@ -658,18 +682,14 @@ end --- @param #MENUPARAM MenuParam -function ESCORT._HoldPosition( MenuParam ) +function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP 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 GroupVec3 = EscortGroup:GetUnit(1):GetVec3() @@ -702,13 +722,12 @@ function ESCORT._HoldPosition( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._JoinUpAndFollow( MenuParam ) +function ESCORT:_JoinUpAndFollow( Distance ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.Distance = MenuParam.ParamDistance + self.Distance = Distance self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end @@ -721,7 +740,7 @@ end function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() @@ -730,44 +749,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Flare( MenuParam ) +function ESCORT:_Flare( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Smoke( MenuParam ) +function ESCORT:_Smoke( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ReportNearbyTargetsNow( MenuParam ) +function ESCORT:_ReportNearbyTargetsNow() - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient @@ -775,17 +785,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) end -function ESCORT._SwitchReportNearbyTargets( MenuParam ) +function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.ReportTargets = MenuParam.ParamReportTargets + self.ReportTargets = ReportTargets if self.ReportTargets then if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) + self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else routines.removeFunction( self.ReportTargetsScheduler ) @@ -794,40 +803,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._ScanTargets( MenuParam ) +function ESCORT:_ScanTargets( ScanDuration ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP local EscortClient = self.EscortClient - local ScanDuration = MenuParam.ParamScanDuration - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsHelicopter() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) elseif EscortGroup:IsAirPlane() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) end EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) end end @@ -844,124 +844,157 @@ function _Resume( EscortGroup ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AttackTarget( MenuParam ) +--- @param #ESCORT self +-- @param #number DetectedItemID +function ESCORT:_AttackTarget( DetectedItemID ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP + self:E( EscortGroup ) local EscortClient = self.EscortClient - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() EscortGroup:OptionROTPassiveDefense() EscortGroup:SetState( EscortGroup, "Escort", self ) - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @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 EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) 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 EscortClient = self.EscortClient - local EscortGroupAttack = MenuParam.ParamEscortGroup - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - SCHDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskAttackUnit( AttackUnit ), - EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 - SCHEDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 + EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROE( MenuParam ) +function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROEFunction = MenuParam.ParamFunction - local EscortROEMessage = MenuParam.ParamMessage - pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROT( MenuParam ) +function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROTFunction = MenuParam.ParamFunction - local EscortROTMessage = MenuParam.ParamMessage - pcall( function() EscortROTFunction() end ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ResumeMission( MenuParam ) +function ESCORT:_ResumeMission( WayPoint ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local WayPoint = MenuParam.ParamWayPoint - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) @@ -1105,176 +1138,244 @@ function ESCORT:_ReportTargetsScheduler() self:F( self.EscortGroup:GetName() ) 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 = "" - 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 EscortGroupName = self.EscortGroup:GetName() + + self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - 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 + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() 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 } ) + local DetectedItems = self.Detection:GetDetectedItems() + self:E( DetectedItems ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() + local DetectedTargets = false + + local DetectedMsgs = {} + + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end + local ClientEscortTargets = EscortGroupData.Detection - --for MenuIndex = 1, #self.EscortMenuAttackTargets do - -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) - -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() - --end + for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + self:E( { DetectedItemID, DetectedItem } ) + -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. + + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - 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, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - end - end + if ClientEscortGroupName == EscortGroupName then + + DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + self, + DetectedItemID + ) 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 + + DetectedTargets = true + end end - - if EscortTargetMessages ~= "" and self.ReportTargets == true then - self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) + self:E( DetectedMsgs ) + if DetectedTargets then + self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else - self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) 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 - - 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 return false diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 556690aed..511cbca44 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -171,7 +171,7 @@ function MISSILETRAINER:New( Distance, Briefing ) self.Distance = Distance / 1000 - _EVENTDISPATCHER:OnShot( self._EventShot, self ) + self:HandleEvent( EVENTS.Shot ) 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. -- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA Event -function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) +-- @param Core.Event#EVENTDATA EventData +function MISSILETRAINER:OnEventShot( EVentData ) + self:F( { EVentData } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = EVentData.IniDCSUnit + local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName + local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired + local TrainerWeaponName = EVentData.WeaponName -- return weapon type self:T( "Missile Launched = " .. TrainerWeaponName ) diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index 9ea15f0ae..72bc21c62 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -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 -- 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). --- @module MOVEMENT +-- @module Movement --- the MOVEMENT class --- @type +-- @type MOVEMENT +-- @extends Core.Base#BASE 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 ) function MOVEMENT:New( MovePrefixes, MoveMaximum ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT self:F( { MovePrefixes, MoveMaximum } ) 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.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 ) -- @@ -60,24 +61,26 @@ end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnBirth( Event ) - self:F( { Event } ) +-- @param #MOVEMENT self +-- @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 Event.IniDCSUnit then - self:T( "Birth object : " .. Event.IniDCSUnitName ) - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + if EventData.IniDCSUnit then + self:T( "Birth object : " .. EventData.IniDCSUnitName ) + if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then 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.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName + self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName self:T( self.AliveUnits ) 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 diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index e58adeac2..1a50fb4ef 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1022,9 +1022,10 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - + local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 3b05ac9a0..da4bf2d0f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -36,25 +36,28 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - _EVENTDISPATCHER:OnShot( self.EventShot, self ) + + self:HandleEvent( EVENTS.Shot ) return self 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. -- @see SEAD -function SEAD:EventShot( Event ) - self:F( { Event } ) +-- @param #SEAD +-- @param Core.Event#EVENTDATA EventData +function SEAD:OnEventShot( EventData ) + self:F( { EventData } ) - local SEADUnit = Event.IniDCSUnit - local SEADUnitName = Event.IniDCSUnitName - local SEADWeapon = Event.Weapon -- Identify the weapon fired - local SEADWeaponName = Event.WeaponName -- return weapon type + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeaponName = EventData.WeaponName -- return weapon type -- Start of the 2nd loop 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 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 _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 8b715ee9f..0b7d4658b 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -59,7 +59,7 @@ Include.File( "Tasking/CommandCenter" ) Include.File( "Tasking/Mission" ) Include.File( "Tasking/Task" ) Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_SEAD" ) +Include.File( "Tasking/Task_A2G_Dispatcher") Include.File( "Tasking/Task_A2G" ) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 967aa84b1..705c9b7ab 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -20,7 +20,9 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) self.Report = {} - self.Report[#self.Report+1] = Title + if Title then + self.Report[#self.Report+1] = Title + end return self end @@ -31,7 +33,7 @@ end -- @return #REPORT function REPORT:Add( Text ) self.Report[#self.Report+1] = Text - return self.Report[#self.Report+1] + return self.Report[#self.Report] end function REPORT:Text() @@ -68,22 +70,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:HandleEvent( EVENTS.Birth, --- @param #COMMANDCENTER self - --- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function( self, EventData ) - self:E( { EventData } ) - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - 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 MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + if EventData.IniObjectCategory == 1 then + local EventGroup = GROUP:Find( EventData.IniDCSGroup ) + if EventGroup and self:HasGroup( EventGroup ) then + 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 MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + self:ReportSummary( EventGroup ) + end + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + Mission:ReportDetails() + end end end @@ -193,17 +196,26 @@ function COMMANDCENTER:SetMenu() 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 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 - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu() - 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. -- @param #COMMANDCENTER self @@ -224,6 +236,14 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has 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. -- @param #COMMANDCENTER self -- @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. 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 self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) @@ -247,6 +268,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) end + --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index b7bcc07b5..ddbaa4301 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -34,23 +34,6 @@ -- ------------------------------- -- 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:SetReportDisplayTime( 25 ) + Detection:__Start( 5 ) + return self end @@ -250,259 +235,3 @@ do -- DETECTION_REPORTING 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 \ No newline at end of file diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 12fd65689..10e194ff2 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -12,22 +12,6 @@ MISSION = { ClassName = "MISSION", Name = "", 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. @@ -45,10 +29,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self:SetStartState( "Idle" ) 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" ) + + --- 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" ) + + --- 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" ) + --- 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.CommandCenter = CommandCenter @@ -60,14 +218,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self.MissionCoalition = MissionCoalition self.Tasks = {} + + -- Private implementations + + return self end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onbeforeComplete( From, Event, To ) @@ -80,10 +242,10 @@ function MISSION:onbeforeComplete( From, Event, To ) return true -- Allow Mission completion. end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onenterCompleted( From, Event, To ) @@ -201,23 +363,25 @@ end --- Sets the Planned Task menu. -- @param #MISSION self -function MISSION:SetMenu() +-- @param #number MenuTime +function MISSION:SetMenu( MenuTime ) self:F() - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:SetMenu() + for _, TaskData in pairs( self:GetTasks() ) do + local Task = TaskData -- Tasking.Task#TASK + Task:SetMenu( MenuTime ) end end --- Removes the Planned Task menu. -- @param #MISSION self -function MISSION:RemoveMenu() +-- @param #number MenuTime +function MISSION:RemoveMenu( MenuTime ) self:F() for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() + Task:RemoveMenu( MenuTime ) end end @@ -229,20 +393,6 @@ function MISSION:GetCommandCenter() return self.CommandCenter 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. -- @param #MISSION self @@ -258,28 +408,18 @@ end -- @param #MISSION self -- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMissionMenu( TaskGroup ) +function MISSION:GetMenu( TaskGroup ) local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter.CommandCenterMenu + local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - - local TaskGroupName = TaskGroup:GetName() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ) + local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) return MissionMenu 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. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task @@ -350,76 +490,44 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end - - ---- old stuff - ---- Returns if a Mission has completed. --- @return bool +--- Is the @{Mission} **Completed**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsCompleted() - self:F() - return self.MissionStatus == "ACCOMPLISHED" + return self:Is( "Completed" ) end ---- Set a Mission to completed. -function MISSION:Completed() - self:F() - self.MissionStatus = "ACCOMPLISHED" - self:StatusToClients() +--- Is the @{Mission} **Idle**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsIdle() + return self:Is( "Idle" ) end ---- Returns if a Mission is ongoing. --- treturn bool +--- Is the @{Mission} **Ongoing**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsOngoing() - self:F() - return self.MissionStatus == "ONGOING" + return self:Is( "Ongoing" ) end ---- Set a Mission to ongoing. -function MISSION:Ongoing() - self:F() - self.MissionStatus = "ONGOING" - --self:StatusToClients() +--- Is the @{Mission} **Failed**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsFailed() + return self:Is( "Failed" ) end ---- Returns if a Mission is pending. --- treturn bool -function MISSION:IsPending() - self:F() - return self.MissionStatus == "PENDING" -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 +--- Is the @{Mission} **Hold**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsHold() + return self:Is( "Hold" ) end +--- Validates if the Mission has a Group +-- @param #MISSION +-- @return #boolean true if the Mission has a Group. function MISSION:HasGroup( TaskGroup ) local Has = false @@ -512,107 +620,6 @@ function MISSION:ReportDetails() return Report:Text() 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. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @usage @@ -626,330 +633,3 @@ function MISSION:GetTasks() 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 - diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 4151d6e98..566b237c2 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -185,7 +185,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - Mission:AddTask( self ) return self end @@ -193,9 +192,13 @@ end --- Get the Task FSM Process Template -- @param #TASK self -- @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 --- 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 self:IsStatePlanned() or self:IsStateReplanned() then 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 if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) @@ -270,10 +273,11 @@ function TASK:AbortUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then + self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end - self:PlayerAborted( PlayerUnit ) + self:Abort() end end end @@ -339,7 +343,7 @@ end ---- Assign the @{Task}to a @{Group}. +--- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -350,7 +354,11 @@ function TASK:AssignToGroup( TaskGroup ) 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 ) local TaskUnits = TaskGroup:GetUnits() @@ -390,6 +398,7 @@ function TASK:AssignToUnit( TaskUnit ) self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self @@ -400,7 +409,7 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit ) + self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) @@ -447,28 +456,37 @@ function TASK:SendBriefingToAssignedGroups() end ---- Assign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() 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() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) end end end + + --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -479,10 +497,12 @@ function TASK:IsAssignedToGroup( TaskGroup ) if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end @@ -511,37 +531,36 @@ end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -function TASK:SetMenu() +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup ) + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if self:IsStatePlanned() or self:IsStateReplanned() then + self:SetMenuForGroup( TaskGroup, MenuTime ) + 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 -- @param #TASK self -function TASK:SetMenuForGroup( TaskGroup ) +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else - self:SetAssignedMenuForGroup( TaskGroup ) + if not self:IsAssignedToGroup( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) + end end end @@ -550,16 +569,24 @@ end -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. +-- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) 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 TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) + 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 } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -567,32 +594,84 @@ end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup ) +function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { 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, TaskGroup ):SetTime( MenuTime ) return self 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}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:RemoveMenuForGroup( TaskGroup ) +function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + self:F() local Mission = self:GetMission() 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 ) - MissionMenu:Remove() +--- Remove the assigned menu option of the @{Task} for a @{Group}. +-- @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 function TASK.MenuAssignToGroup( MenuParam ) @@ -605,19 +684,21 @@ function TASK.MenuAssignToGroup( MenuParam ) self:AssignToGroup( TaskGroup ) end -function TASK.MenuTaskStatus( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskStatus( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup + local ReportText = self:ReportDetails() - --self:AssignToGroup( TaskGroup ) + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) + 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() end @@ -662,15 +743,26 @@ end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit +-- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self 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 return Fsm 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} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -678,9 +770,15 @@ end function TASK:RemoveStateMachine( TaskUnit ) 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 + collectgarbage() - self:T( "Garbage Collected, Processes should be finalized now ...") + self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} @@ -795,6 +893,32 @@ function TASK:IsStatePlanned() return self:Is( "Planned" ) 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**. -- @param #TASK self function TASK:StateAssigned() @@ -875,7 +999,7 @@ function TASK:onenterSuccess( From, Event, To ) 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:GetMission():__Complete( 1 ) @@ -1009,24 +1133,19 @@ function TASK:ReportDetails() -- Determine the status of the Task. local State = self:GetState() - -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} - for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local Player = PlayerGroup -- Wrapper.Group#GROUP - for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - if PlayerUnit and PlayerUnit:IsAlive() then - local PlayerName = PlayerUnit:GetPlayerName() - PlayerNames[#PlayerNames+1] = PlayerName - end + local PlayerReport = REPORT:New( " - Players:" ) + for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP + PlayerNames = PlayerGroup:GetPlayerNames() + if PlayerNames then + PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end - local PlayerNameText = table.concat( PlayerNames, ", " ) - Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText ) end -- 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() end diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 3eee41d0d..785f11e48 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -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} --- ================================================= --- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, --- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. +-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, +-- based on the tasking capabilities defined in @{Task#TASK}. -- The TASK_A2G 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. +-- * **Planned**: The A2G task is planned. +-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2G task is successfully completed. +-- * **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 - do -- TASK_A2G --- The TASK_A2G class -- @type TASK_A2G + -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK TASK_A2G = { ClassName = "TASK_A2G", @@ -33,52 +58,312 @@ do -- TASK_A2G -- @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 #string TaskType BAI or CAS -- @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 - function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G self:F() self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - self.FACUnit = FACUnit - - local A2GUnitProcess = self:GetUnitProcess() + self.TaskType = TaskType - A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) - A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" ) - A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" ) - A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } ) - A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - --Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) ) - A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" ) - A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" ) + Mission:AddTask( self ) - function A2GUnitProcess:onenterUpdated( TaskUnit ) - self:E( { self } ) - self:Account() - self:Smoke() + local Fsm = self:GetUnitProcess() + + + 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 + --- 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 + end - --- @param #TASK_A2G self + --- @param #TASK_A2G self function TASK_A2G:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" 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 + --- @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 diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua new file mode 100644 index 000000000..06c3a4234 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -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 \ No newline at end of file diff --git a/Moose Development/Moose/Tasking/Task_Pickup.lua b/Moose Development/Moose/Tasking/Task_Pickup.lua index 9754ca417..0e1b103c5 100644 --- a/Moose Development/Moose/Tasking/Task_Pickup.lua +++ b/Moose Development/Moose/Tasking/Task_Pickup.lua @@ -41,11 +41,6 @@ do -- TASK_PICKUP local self = BASE:Inherit( self, TASK:New( Mission, AssignedSetGroup, TaskName, TaskType, "PICKUP" ) ) self:F() - _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self ) - _EVENTDISPATCHER:OnDead( self._EventDead, self ) - _EVENTDISPATCHER:OnCrash( self._EventDead, self ) - _EVENTDISPATCHER:OnPilotDead( self._EventDead, self ) - return self end diff --git a/Moose Development/Moose/Tasking/Task_SEAD.lua b/Moose Development/Moose/Tasking/Task_SEAD.lua deleted file mode 100644 index cd40cd71e..000000000 --- a/Moose Development/Moose/Tasking/Task_SEAD.lua +++ /dev/null @@ -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 diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index dd8fabff0..85e152ed4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -536,48 +536,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At return DCSTask end - --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @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 Wrapper.Unit#UNIT AttackUnit The UNIT. +-- @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 #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 #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. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) - self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) - - -- AttackUnit = { - -- id = 'AttackUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- } - -- } +function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack ) + self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } ) local DCSTask DCSTask = { id = 'AttackUnit', params = { - altitudeEnabled = true, unitId = AttackUnit:GetID(), - attackQtyLimit = AttackQtyLimit or false, - attackQty = AttackQty or 2, + groupAttack = GroupAttack or false, + visible = Visible or false, expend = WeaponExpend or "Auto", - altitude = 2000, - directionEnabled = true, - groupAttack = true, - --weaponType = WeaponType or 1073741822, - direction = Direction or 0, - } + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + weaponType = 1073741822, + }, } self:E( DCSTask ) @@ -1191,7 +1178,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, end ---- (AIR) Attack the Unit. +--- (AIR) Search and attack the Unit. -- @param #CONTROLLABLE self -- @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. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 4669911e3..df263264c 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -113,7 +113,7 @@ GROUP = { -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @return #GROUP self function GROUP:Register( GroupName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) + self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName @@ -231,7 +231,7 @@ function GROUP:GetCategory() return nil end ---- Returns the category name of the DCS Group. +--- Returns the category name of the #GROUP. -- @param #GROUP self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship function GROUP:GetCategoryName() @@ -926,3 +926,29 @@ do -- Event Handling 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 \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index ea457ace0..0bedb8918 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -56,7 +56,7 @@ function STATIC:FindByName( StaticName, RaiseError ) self.StaticName = StaticName if StaticFound then - StaticFound:F( { StaticName } ) + StaticFound:F3( { StaticName } ) return StaticFound end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index ad0fc58be..56d26c9d9 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -522,6 +522,31 @@ function UNIT:GetLife0() return nil 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 ... -- The following threat levels are foreseen: -- @@ -540,14 +565,14 @@ end function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes - self:E( Attributes ) + self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" if self:IsGround() then - self:E( "Ground" ) + self:T( "Ground" ) local ThreatLevels = { "Unarmed", @@ -585,7 +610,7 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:E( "Air" ) + self:T( "Air" ) local ThreatLevels = { "Unarmed", @@ -619,7 +644,7 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:E( "Ship" ) + self:T( "Ship" ) --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, diff --git a/Moose Development/ReleaseNotes.txt b/Moose Development/ReleaseNotes.txt index 4b2258255..64a580998 100644 --- a/Moose Development/ReleaseNotes.txt +++ b/Moose Development/ReleaseNotes.txt @@ -1,4 +1,4 @@ -2017-02-08 +2017-02-18 - Reworked some vector functions. -- POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added. @@ -6,9 +6,13 @@ -- ZONE_RADIUS:GetRandomPointVec3( inner, outer ) added. -- ZONE_POLYGON_BASE:GetRandomPointVec2() 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 - Renamed the documentation references following the structure of the files. diff --git a/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz b/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz index af6c3b818..5a3142fba 100644 Binary files a/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz and b/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz differ diff --git a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua index fcb1e53a6..91fd7f218 100644 --- a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua +++ b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170315_1916' ) +env.info( 'Moose Generation Timestamp: 20170317_2147' ) local base = _G Include = {} @@ -3048,12 +3048,14 @@ function BASE:_Destructor() --self:EventRemoveAll() end + +-- THIS IS WHY WE NEED LUA 5.2 ... function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- 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. - --[[ + local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -3068,7 +3070,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - --]] + end --- This is the worker method to inherit from a parent class. @@ -3084,7 +3086,7 @@ function BASE:Inherit( Child, Parent ) setmetatable( Child, Parent ) Child.__index = Child - Child:_SetDestructor() + --Child:_SetDestructor() end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child @@ -3696,8 +3698,12 @@ end ---- 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} -- -- The @{Scheduler#SCHEDULER} class creates schedule. @@ -3845,6 +3851,13 @@ function SCHEDULER:Remove( ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end +--- Clears all pending schedules. +-- @param #SCHEDULER self +function SCHEDULER:Clear() + self:F3( ) + + _SCHEDULEDISPATCHER:Clear( self ) +end @@ -3925,7 +3938,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- 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. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -4042,11 +4055,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) + -- Only start when there is no ScheduleID defined! + -- This prevents to "Start" the scheduler twice with the same CallID... + if not Schedule[CallID].ScheduleID then + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Start( Scheduler, CallID ) -- Recursive @@ -4059,7 +4076,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then 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 for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Stop( Scheduler, CallID ) -- Recursive @@ -4067,6 +4089,14 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) 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 + --- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. @@ -4355,7 +4385,7 @@ local _EVENTMETA = { }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, - Event = "OnEventTakeOff", + Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { @@ -4496,11 +4526,11 @@ function EVENT:Init( EventID, EventClass ) -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = {} + self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) end 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 return self.Events[EventID][EventPriority][EventClass] end @@ -4570,11 +4600,11 @@ end -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction -- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) + self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) end return self end @@ -4588,9 +4618,9 @@ end function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) self:F2( { EventID } ) - local Event = self:Init( EventID, EventClass ) - Event.EventFunction = EventFunction - Event.EventClass = EventClass + local EventData = self:Init( EventID, EventClass ) + EventData.EventFunction = EventFunction + EventData.EventClass = EventClass return self end @@ -4606,13 +4636,13 @@ end function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) - local Event = self:Init( EventID, EventClass ) - if not Event.EventUnit then - Event.EventUnit = {} + local EventData = self:Init( EventID, EventClass ) + if not EventData.EventUnit then + EventData.EventUnit = {} end - Event.EventUnit[UnitName] = {} - Event.EventUnit[UnitName].EventFunction = EventFunction - Event.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit[UnitName] = {} + EventData.EventUnit[UnitName].EventFunction = EventFunction + EventData.EventUnit[UnitName].EventClass = EventClass return self end @@ -4647,51 +4677,11 @@ do -- OnBirth function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) return self 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 do -- OnCrash @@ -4705,49 +4695,10 @@ do -- OnCrash function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) return self 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 @@ -4762,96 +4713,13 @@ do -- OnDead function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) return self 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 -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 --- Create an OnLand event handler for a group @@ -4863,38 +4731,11 @@ do -- OnLand function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) return self 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 do -- OnTakeOff @@ -4907,38 +4748,11 @@ do -- OnTakeOff function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) return self 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 do -- OnEngineShutDown @@ -4952,210 +4766,11 @@ do -- OnEngineShutDown function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) return self 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 @@ -5278,7 +4893,9 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 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 @@ -5299,8 +4916,10 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function 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( function() return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) @@ -5313,8 +4932,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -5328,7 +4949,9 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function 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( function() @@ -5342,8 +4965,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -5361,9 +4986,11 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.IniGroupName] then -- First test if a EventFunction is Set, otherwise search for the default function 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( function() return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) @@ -5376,8 +5003,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -5389,8 +5018,10 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.TgtGroupName] 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( function() return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) @@ -5403,7 +5034,9 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() @@ -5425,8 +5058,9 @@ function EVENT:onEvent( Event ) if EventData.EventFunction then -- 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( function() return EventData.EventFunction( EventClass, Event ) @@ -5438,11 +5072,14 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() - return EventFunction( EventClass, Event ) + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value end, ErrorHandler ) end end @@ -5456,6 +5093,8 @@ function EVENT:onEvent( Event ) else self:E( { _EVENTMETA[Event.id].Text, Event } ) end + + Event = nil end --- The EVENTHANDLER structure @@ -5613,6 +5252,8 @@ do -- MENU_BASE } --- Consructor + -- @param #MENU_BASE + -- @return #MENU_BASE function MENU_BASE:New( MenuText, ParentMenu ) local MenuParentPath = {} @@ -5625,10 +5266,43 @@ do -- MENU_BASE self.MenuPath = nil self.MenuText = MenuText self.MenuParentPath = MenuParentPath + self.Menus = {} + self.MenuCount = 0 + self.MenuRemoveParent = false + self.MenuTime = timer.getTime() return self 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 do -- MENU_COMMAND_BASE @@ -5636,7 +5310,7 @@ do -- MENU_COMMAND_BASE --- The MENU_COMMAND_BASE class -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -5645,6 +5319,8 @@ do -- MENU_COMMAND_BASE } --- Constructor + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5664,7 +5340,7 @@ do -- MENU_MISSION --- The MENU_MISSION class -- @type MENU_MISSION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -5673,7 +5349,7 @@ do -- MENU_MISSION -- @param #MENU_MISSION self -- @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). - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:New( MenuText, ParentMenu ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5700,7 +5376,7 @@ do -- MENU_MISSION --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! -- @param #MENU_MISSION self - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5731,7 +5407,7 @@ do -- MENU_MISSION_COMMAND --- The MENU_MISSION_COMMAND class -- @type MENU_MISSION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -5781,7 +5457,7 @@ do -- MENU_COALITION --- The MENU_COALITION class -- @type MENU_COALITION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- 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). @@ -5855,7 +5531,7 @@ do -- MENU_COALITION --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! -- @param #MENU_COALITION self - -- @return #MENU_COALITION self + -- @return #MENU_COALITION function MENU_COALITION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5886,7 +5562,7 @@ do -- MENU_COALITION_COMMAND --- The MENU_COALITION_COMMAND class -- @type MENU_COALITION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -5898,7 +5574,7 @@ do -- MENU_COALITION_COMMAND -- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @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. - -- @return #MENU_COALITION_COMMAND self + -- @return #MENU_COALITION_COMMAND function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) @@ -5943,7 +5619,7 @@ do -- MENU_CLIENT --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @type MENU_CLIENT - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -6084,7 +5760,7 @@ do -- MENU_CLIENT --- The MENU_CLIENT_COMMAND class -- @type MENU_CLIENT_COMMAND - -- @extends Menu#MENU_COMMAND + -- @extends Core.Menu#MENU_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -6170,7 +5846,7 @@ do --- The MENU_GROUP class -- @type MENU_GROUP - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -6244,8 +5920,6 @@ do self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) MenuGroup._Menus[Path] = self - self.Menus = {} - self.MenuGroup = MenuGroup self.Path = Path self.MenuGroupID = MenuGroup:GetID() @@ -6255,8 +5929,10 @@ do self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self:F( { self.ParentMenu.Menus, MenuText } ) + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end @@ -6267,42 +5943,56 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus() - self:F( self.MenuPath ) + function MENU_GROUP:RemoveSubMenus( MenuTime ) + self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + for MenuText, Menu in pairs( self.Menus ) do + Menu:Remove( MenuTime ) end end - + + --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #nil - function MENU_GROUP:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus() - - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] + self:RemoveSubMenus( MenuTime ) - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + 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 - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil end + return nil end --- The MENU_GROUP_COMMAND class -- @type MENU_GROUP_COMMAND - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -6314,13 +6004,14 @@ do -- @param ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @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, ... ) MenuGroup._Menus = MenuGroup._Menus or {} local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] + self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) MenuGroup._Menus[Path] = self @@ -6331,33 +6022,45 @@ do self.MenuText = MenuText 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 ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F( { ParentMenu.Menus, MenuText } ) end end - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - return self end --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self + -- @param MenuTime -- @return #nil - function MENU_GROUP_COMMAND:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP_COMMAND:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - 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 - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + + 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 return nil @@ -6367,6 +6070,8 @@ end --- **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: @@ -6614,6 +6319,58 @@ function ZONE_BASE:GetVec2() return nil 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. -- @param #ZONE_BASE self -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. @@ -6628,6 +6385,13 @@ function ZONE_BASE:GetRandomPointVec2() return nil 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. -- @param #ZONE_BASE self -- @return #nil The bounding square. @@ -6714,8 +6478,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @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 -function ZONE_RADIUS:BoundZone( Points ) +function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() @@ -6731,8 +6496,10 @@ function ZONE_RADIUS:BoundZone( Points ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local Tire = { - ["country"] = "USA", + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -6744,7 +6511,10 @@ function ZONE_RADIUS:BoundZone( Points ) ["heading"] = 0, } -- 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 return self @@ -7177,8 +6947,9 @@ end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self +-- @param #boolean UnBound If true, the tyres will be destroyed. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( ) +function ZONE_POLYGON_BASE:BoundZone( UnBound ) local i local j @@ -7207,8 +6978,11 @@ function ZONE_POLYGON_BASE:BoundZone( ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- 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 j = i @@ -7444,6 +7218,8 @@ DATABASE = { PLAYERSJOINED = {}, CLIENTS = {}, AIRBASES = {}, + COUNTRY_ID = {}, + COUNTRY_NAME = {}, NavPoints = {}, } @@ -8151,6 +7927,9 @@ function DATABASE:_RegisterTemplates() local CountryName = string.upper(cntry_data.name) 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]["countryId"] = cntry_data.id @@ -8434,6 +8213,7 @@ SET_BASE = { Filter = {}, Set = {}, 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. @@ -8452,10 +8232,14 @@ function SET_BASE:New( Database ) self.YieldInterval = 10 self.TimeInterval = 0.001 + self.Set = {} + self.List = {} self.List.__index = self.List self.List = setmetatable( { Count = 0 }, self.List ) + self.Index = {} + self.CallScheduler = SCHEDULER:New( self ) self:SetEventPriority( 2 ) @@ -8507,6 +8291,8 @@ function SET_BASE:Add( ObjectName, Object ) self.Set[ObjectName] = t._ + table.insert( self.Index, ObjectName ) + end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -8558,7 +8344,15 @@ function SET_BASE:Remove( ObjectName ) t._prev = nil 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 + end end @@ -8578,12 +8372,50 @@ function SET_BASE:Get( ObjectName ) 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. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - return self.List.Count + return #self.Index end @@ -8846,7 +8678,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen return false end - self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() return self end @@ -8919,7 +8752,7 @@ end --- SET_GROUP class -- @type SET_GROUP --- @extends #SET_BASE +-- @extends Core.Set#SET_BASE SET_GROUP = { ClassName = "SET_GROUP", Filter = { @@ -12178,10 +12011,20 @@ do -- FSM 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 self:T( "Calling " .. handler ) 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 end end @@ -12380,8 +12223,66 @@ do -- FSM_CONTROLLABLE self:SetControllable( Controllable ) 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 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. -- @param #FSM_CONTROLLABLE self @@ -12449,12 +12350,34 @@ do -- FSM_PROCESS function FSM_PROCESS:Init( FsmProcess ) self:T( "No Initialisation" ) 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. -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Copy( Controllable, Task ) self:T( { self:GetClassNameAndID() } ) + local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS @@ -12473,7 +12396,7 @@ do -- FSM_PROCESS -- Copy Processes 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 ) end @@ -12491,6 +12414,22 @@ do -- FSM_PROCESS return NewFsm 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. -- @param #FSM_PROCESS self @@ -13985,48 +13924,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At return DCSTask end - --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @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 Wrapper.Unit#UNIT AttackUnit The UNIT. +-- @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 #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 #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. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) - self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) - - -- AttackUnit = { - -- id = 'AttackUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- } - -- } +function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack ) + self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } ) local DCSTask DCSTask = { id = 'AttackUnit', params = { - altitudeEnabled = true, unitId = AttackUnit:GetID(), - attackQtyLimit = AttackQtyLimit or false, - attackQty = AttackQty or 2, + groupAttack = GroupAttack or false, + visible = Visible or false, expend = WeaponExpend or "Auto", - altitude = 2000, - directionEnabled = true, - groupAttack = true, - --weaponType = WeaponType or 1073741822, - direction = Direction or 0, - } + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + weaponType = 1073741822, + }, } self:E( DCSTask ) @@ -14640,7 +14566,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, end ---- (AIR) Attack the Unit. +--- (AIR) Search and attack the Unit. -- @param #CONTROLLABLE self -- @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. @@ -15872,7 +15798,7 @@ GROUP = { -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @return #GROUP self function GROUP:Register( GroupName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) + self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName @@ -15990,7 +15916,7 @@ function GROUP:GetCategory() return nil end ---- Returns the category name of the DCS Group. +--- Returns the category name of the #GROUP. -- @param #GROUP self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship function GROUP:GetCategoryName() @@ -16685,7 +16611,32 @@ do -- Event Handling end end ---- This module contains the UNIT class. + +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--- This module contains the UNIT class. -- -- 1) @{#UNIT} class, extends @{Controllable#CONTROLLABLE} -- =========================================================== @@ -17209,6 +17160,31 @@ function UNIT:GetLife0() return nil 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 ... -- The following threat levels are foreseen: -- @@ -17227,14 +17203,14 @@ end function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes - self:E( Attributes ) + self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" if self:IsGround() then - self:E( "Ground" ) + self:T( "Ground" ) local ThreatLevels = { "Unarmed", @@ -17272,7 +17248,7 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:E( "Air" ) + self:T( "Air" ) local ThreatLevels = { "Unarmed", @@ -17306,7 +17282,7 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:E( "Ship" ) + self:T( "Ship" ) --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, @@ -18165,7 +18141,7 @@ function STATIC:FindByName( StaticName, RaiseError ) self.StaticName = StaticName if StaticFound then - StaticFound:F( { StaticName } ) + StaticFound:F3( { StaticName } ) return StaticFound end @@ -19369,9 +19345,10 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - + local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) @@ -20009,7 +19986,9 @@ CLEANUP = { -- or -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- 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 } ) if type( ZoneNames ) == 'table' then @@ -20021,7 +20000,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - _EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) + self:HandleEvent( EVENTS.Birth ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) @@ -20082,32 +20061,24 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end -function CLEANUP:_OnEventBirth( Event ) - self:F( { Event } ) +--- @param #CLEANUP self +-- @param Core.Event#EVENTDATA EventData +function CLEANUP:_OnEventBirth( EventData ) + self:F( { EventData } ) - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.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() + self.CleanUpList[EventData.IniDCSUnitName] = {} + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName + 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 @@ -21987,10 +21958,11 @@ end -- 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 -- on defined intervals (currently every minute). --- @module MOVEMENT +-- @module Movement --- the MOVEMENT class --- @type +-- @type MOVEMENT +-- @extends Core.Base#BASE MOVEMENT = { ClassName = "MOVEMENT", } @@ -22004,7 +21976,7 @@ MOVEMENT = { -- 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 ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT self:F( { MovePrefixes, MoveMaximum } ) if type( MovePrefixes ) == 'table' then @@ -22017,7 +21989,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum ) 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. - _EVENTDISPATCHER:OnBirth( self.OnBirth, self ) + self:HandleEvent( EVENTS.Birth ) -- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) -- @@ -22044,24 +22016,26 @@ end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnBirth( Event ) - self:F( { Event } ) +-- @param #MOVEMENT self +-- @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 Event.IniDCSUnit then - self:T( "Birth object : " .. Event.IniDCSUnitName ) - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + if EventData.IniDCSUnit then + self:T( "Birth object : " .. EventData.IniDCSUnitName ) + if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then 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.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName + self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName self:T( self.AliveUnits ) 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 @@ -22148,25 +22122,28 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - _EVENTDISPATCHER:OnShot( self.EventShot, self ) + + self:HandleEvent( EVENTS.Shot ) return self 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. -- @see SEAD -function SEAD:EventShot( Event ) - self:F( { Event } ) +-- @param #SEAD +-- @param Core.Event#EVENTDATA EventData +function SEAD:OnEventShot( EventData ) + self:F( { EventData } ) - local SEADUnit = Event.IniDCSUnit - local SEADUnitName = Event.IniDCSUnitName - local SEADWeapon = Event.Weapon -- Identify the weapon fired - local SEADWeaponName = Event.WeaponName -- return weapon type + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeaponName = EventData.WeaponName -- return weapon type -- Start of the 2nd loop 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 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 _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() @@ -22325,7 +22302,7 @@ end -- -- 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.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. @@ -22369,6 +22346,7 @@ end -- @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 Core.Menu#MENU_CLIENT EscortMenuResumeMission +-- @field Functional.Detection#DETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", EscortName = nil, -- The Escort Name @@ -22417,14 +22395,22 @@ ESCORT = { -- -- 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." ) 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.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortName = EscortName 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. if not self.EscortClient._EscortGroups then self.EscortClient._EscortGroups = {} @@ -22434,7 +22420,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection end self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) @@ -22459,13 +22445,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.FollowDistance = 100 self.CT1 = 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 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 allows to visualize where the escort is flying to. -- @param #ESCORT self @@ -22523,7 +22526,7 @@ function ESCORT:MenuFollowAt( Distance ) self.EscortMenuJoinUpAndFollow = {} 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 end @@ -22581,11 +22584,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuHold, ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortGroup, - ParamHeight = Height, - ParamSeconds = Seconds - } + self, + self.EscortGroup, + Height, + Seconds ) end @@ -22702,9 +22704,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuScan, ESCORT._ScanTargets, - { ParamSelf = self, - ParamScanDuration = 30 - } + self, + 30 ) end @@ -22734,11 +22735,11 @@ function ESCORT:MenuFlare( MenuTextFormat ) end if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = 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.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.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.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.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, self, FLARECOLOR.Green, "Released a green 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, self, FLARECOLOR.White, "Released a white flare!" ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) end return self @@ -22767,12 +22768,12 @@ function ESCORT:MenuSmoke( MenuTextFormat ) end if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = 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.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.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.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.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.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, self, SMOKECOLOR.Green, "Releasing green 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, self, SMOKECOLOR.White, "Releasing white 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, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) end end @@ -22797,9 +22798,9 @@ function ESCORT:MenuReportTargets( Seconds ) end -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + 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, self, true ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) @@ -22836,16 +22837,16 @@ function ESCORT:MenuROE( MenuTextFormat ) -- Rules of Engagement self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) 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 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 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 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 @@ -22865,16 +22866,16 @@ function ESCORT:MenuEvasion( MenuTextFormat ) -- Reaction to Threats self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) 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 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 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 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 @@ -22899,18 +22900,14 @@ end --- @param #MENUPARAM MenuParam -function ESCORT._HoldPosition( MenuParam ) +function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP 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 GroupVec3 = EscortGroup:GetUnit(1):GetVec3() @@ -22943,13 +22940,12 @@ function ESCORT._HoldPosition( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._JoinUpAndFollow( MenuParam ) +function ESCORT:_JoinUpAndFollow( Distance ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.Distance = MenuParam.ParamDistance + self.Distance = Distance self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end @@ -22962,7 +22958,7 @@ end function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() @@ -22971,44 +22967,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Flare( MenuParam ) +function ESCORT:_Flare( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Smoke( MenuParam ) +function ESCORT:_Smoke( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ReportNearbyTargetsNow( MenuParam ) +function ESCORT:_ReportNearbyTargetsNow() - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient @@ -23016,17 +23003,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) end -function ESCORT._SwitchReportNearbyTargets( MenuParam ) +function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.ReportTargets = MenuParam.ParamReportTargets + self.ReportTargets = ReportTargets if self.ReportTargets then if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) + self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else routines.removeFunction( self.ReportTargetsScheduler ) @@ -23035,40 +23021,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._ScanTargets( MenuParam ) +function ESCORT:_ScanTargets( ScanDuration ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP local EscortClient = self.EscortClient - local ScanDuration = MenuParam.ParamScanDuration - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsHelicopter() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) elseif EscortGroup:IsAirPlane() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) end EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) end end @@ -23085,124 +23062,157 @@ function _Resume( EscortGroup ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AttackTarget( MenuParam ) +--- @param #ESCORT self +-- @param #number DetectedItemID +function ESCORT:_AttackTarget( DetectedItemID ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP + self:E( EscortGroup ) local EscortClient = self.EscortClient - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() EscortGroup:OptionROTPassiveDefense() EscortGroup:SetState( EscortGroup, "Escort", self ) - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @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 EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) 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 EscortClient = self.EscortClient - local EscortGroupAttack = MenuParam.ParamEscortGroup - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - SCHDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskAttackUnit( AttackUnit ), - EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 - SCHEDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 + EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROE( MenuParam ) +function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROEFunction = MenuParam.ParamFunction - local EscortROEMessage = MenuParam.ParamMessage - pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROT( MenuParam ) +function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROTFunction = MenuParam.ParamFunction - local EscortROTMessage = MenuParam.ParamMessage - pcall( function() EscortROTFunction() end ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ResumeMission( MenuParam ) +function ESCORT:_ResumeMission( WayPoint ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local WayPoint = MenuParam.ParamWayPoint - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) @@ -23346,176 +23356,244 @@ function ESCORT:_ReportTargetsScheduler() self:F( self.EscortGroup:GetName() ) 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 = "" - 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 EscortGroupName = self.EscortGroup:GetName() + + self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - 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 + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() 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 } ) + local DetectedItems = self.Detection:GetDetectedItems() + self:E( DetectedItems ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() + local DetectedTargets = false + + local DetectedMsgs = {} + + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end + local ClientEscortTargets = EscortGroupData.Detection - --for MenuIndex = 1, #self.EscortMenuAttackTargets do - -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) - -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() - --end + for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + self:E( { DetectedItemID, DetectedItem } ) + -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. + + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - 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, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - end - end + if ClientEscortGroupName == EscortGroupName then + + DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + self, + DetectedItemID + ) 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 + + DetectedTargets = true + end end - - if EscortTargetMessages ~= "" and self.ReportTargets == true then - self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) + self:E( DetectedMsgs ) + if DetectedTargets then + self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else - self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) 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 - - 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 return false @@ -23693,7 +23771,7 @@ function MISSILETRAINER:New( Distance, Briefing ) self.Distance = Distance / 1000 - _EVENTDISPATCHER:OnShot( self._EventShot, self ) + self:HandleEvent( EVENTS.Shot ) self.DBClients = SET_CLIENT:New():FilterStart() @@ -23971,14 +24049,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. -- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA Event -function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) +-- @param Core.Event#EVENTDATA EventData +function MISSILETRAINER:OnEventShot( EVentData ) + self:F( { EVentData } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = EVentData.IniDCSUnit + local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName + local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired + local TrainerWeaponName = EVentData.WeaponName -- return weapon type self:T( "Missile Launched = " .. TrainerWeaponName ) @@ -25426,17 +25504,17 @@ end -- -- === -- --- 1) @{Detection#DETECTION_BASE} class, extends @{Base#BASE} --- ========================================================== --- The @{Detection#DETECTION_BASE} class defines the core functions to administer detected objects. --- The @{Detection#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- # 1) @{#DETECTION_BASE} class, extends @{Fsm#FSM} -- --- 1.1) DETECTION_BASE constructor --- ------------------------------- --- Construct a new DETECTION_BASE instance using the @{Detection#DETECTION_BASE.New}() method. +-- The @{#DETECTION_BASE} class defines the core functions to administer detected objects. +-- The @{#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- +-- ## 1.1) DETECTION_BASE constructor +-- +-- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. +-- +-- ## 1.2) DETECTION_BASE initialization -- --- 1.2) DETECTION_BASE initialization --- ---------------------------------- -- By default, detection will return detected objects with all the detection sensors available. -- However, you can ask how the objects were found with specific detection methods. -- If you use one of the below methods, the detection will work with the detection method specified. @@ -25444,969 +25522,2040 @@ end -- -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: -- --- * @{Detection#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. --- * @{Detection#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. --- * @{Detection#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. --- * @{Detection#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. --- * @{Detection#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. --- * @{Detection#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. +-- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. +-- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. +-- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. +-- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. +-- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. +-- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. -- --- 1.3) Obtain objects detected by DETECTION_BASE --- ---------------------------------------------- --- DETECTION_BASE builds @{Set}s of objects detected. These @{Set#SET_BASE}s can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSets}(). --- The method will return a list (table) of @{Set#SET_BASE} objects. +-- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list +-- +-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later +-- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains +-- a SET_UNIT object that contains the detected units that belong to that group. +-- +-- Derived classes will apply different methods to group the detected units. +-- Examples are per area, per quadrant, per distance, per type. +-- See further the derived DETECTION classes on which grouping methods are currently supported. +-- +-- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: +-- +-- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. +-- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). +-- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information +-- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. +-- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). +-- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). +-- +-- ## 1.4) Apply additional Filters to fine-tune the detected objects +-- +-- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. +-- That being said, the DCS World detection algorithm can sometimes be unrealistic. +-- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. +-- Additionally, trees and other obstacles are not accounted during the DCS World detection. +-- +-- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. +-- For electronic detection, this filtering is not applied, only for visually detected targets. +-- +-- The following additional filtering can be applied for visual filtering: +-- +-- * A probability factor per kilometer distance. +-- * A probability factor based on the alpha angle between the detected object and the unit detecting. +-- A detection from a higher altitude allows for better detection than when on the ground. +-- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. +-- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. +-- +-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. +-- Only when you experience unrealistic behaviour in your missions, these filters could be applied. +-- +-- ### 1.4.1 ) Distance visual detection probability +-- +-- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. +-- Also, the speed of accurate detection plays a role. +-- +-- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. +-- +-- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +-- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. +-- +-- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! +-- +-- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. +-- +-- ### 1.4.2 ) Alpha Angle visual detection probability +-- +-- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. +-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. +-- +-- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. +-- +-- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +-- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% +-- +-- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. +-- +-- ### 1.4.3 ) Cloudy Zones detection probability +-- +-- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. +-- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +-- zones that reflect cloudy areas where detected units may not be so easily visually detected. +-- +-- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. +-- +-- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take +-- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. +-- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! +-- +-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for +-- AI not to detect so easily targets within a forrest or village rich area. +-- +-- ## 1.5 ) Accept / Reject detected units +-- +-- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, +-- if it is located in range or located inside or outside of specific zones. +-- +-- ### 1.5.1 ) Detection acceptance of within range limit +-- +-- A range can be set that will limit a successful detection for a unit. +-- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will accept detected units if the range is below 5000 meters. +-- Detection:SetAcceptRange( 5000 ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- +-- ### 1.5.2 ) Detection acceptance if within zone(s). +-- +-- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). +-- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Search fo the zones where units are to be accepted. +-- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) +-- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. +-- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- ### 1.5.3 ) Detection rejectance if within zone(s). +-- +-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). +-- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. +-- An example of how to use the method is shown below. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Search fo the zones where units are to be rejected. +-- local ZoneReject1 = ZONE:New( "RejectZone1" ) +-- local ZoneReject2 = ZONE:New( "RejectZone2" ) +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. +-- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- ## 1.6) DETECTION_BASE is a Finite State Machine +-- +-- Various Events and State Transitions can be tailored using DETECTION_BASE. +-- +-- ### 1.6.1) DETECTION_BASE States +-- +-- * **Detecting**: The detection is running. +-- * **Stopped**: The detection is stopped. +-- +-- ### 1.6.2) DETECTION_BASE Events +-- +-- * **Start**: Start the detection process. +-- * **Detect**: Detect new units. +-- * **Detected**: New units have been detected. +-- * **Stop**: Stop the detection process. -- -- === -- --- 2) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} --- =============================================================================== +-- # 2) @{Detection#DETECTION_UNITS} class, extends @{Detection#DETECTION_BASE} +-- +-- The @{Detection#DETECTION_UNITS} class will detect units within the battle zone. +-- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. +-- Beware that when the amount of units detected is large, the DetectedItems list will be large also. +-- +-- # 3) @{Detection#DETECTION_TYPES} class, extends @{Detection#DETECTION_BASE} +-- +-- The @{Detection#DETECTION_TYPES} class will detect units within the battle zone. +-- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. +-- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. +-- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. +-- +-- # 4) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} +-- -- The @{Detection#DETECTION_AREAS} class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. -- The class is group the detected units within zones given a DetectedZoneRange parameter. -- A set with multiple detected zones will be created as there are groups of units detected. -- --- 2.1) Retrieve the Detected Unit sets and Detected Zones --- ------------------------------------------------------- --- The DetectedUnitSets methods are implemented in @{Detection#DECTECTION_BASE} and the DetectedZones methods is implemented in @{Detection#DETECTION_AREAS}. +-- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones -- --- Retrieve the DetectedUnitSets with the method @{Detection#DETECTION_BASE.GetDetectedSets}(). A table will be return of @{Set#SET_UNIT}s. --- To understand the amount of sets created, use the method @{Detection#DETECTION_BASE.GetDetectedSetCount}(). --- If you want to obtain a specific set from the DetectedSets, use the method @{Detection#DETECTION_BASE.GetDetectedSet}() with a given index. +-- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and +-- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. +-- +-- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. -- -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. -- --- 1.4) Flare or Smoke detected units --- ---------------------------------- +-- ## 4.4) Flare or Smoke detected units +-- -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. -- --- 1.5) Flare or Smoke detected zones --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedZones}() or @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to flare or smoke the detected zones when a new detection has taken place. +-- ## 4.5) Flare or Smoke or Bound detected zones +-- +-- Use the methods: +-- +-- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color +-- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color +-- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag +-- +-- the detected zones when a new detection has taken place. -- -- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * Mechanist : Early concept of DETECTION_AREAS. -- -- ### Authors: -- --- * FlightControl : Design & Programming +-- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Detection +do -- DETECTION_BASE ---- DETECTION_BASE class --- @type DETECTION_BASE --- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. --- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. --- @field #number DetectionRun --- @extends Core.Base#BASE -DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, -} - ---- @type DETECTION_BASE.DetectedObjects --- @list <#DETECTION_BASE.DetectedObject> - ---- @type DETECTION_BASE.DetectedObject --- @field #string Name --- @field #boolean Visible --- @field #string Type --- @field #number Distance --- @field #boolean Identified - ---- DETECTION constructor. --- @param #DETECTION_BASE self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @return #DETECTION_BASE self -function DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + --- DETECTION_BASE class + -- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + DETECTION_BASE = { + ClassName = "DETECTION_BASE", + DetectionSetGroup = nil, + DetectionRange = nil, + DetectedObjects = {}, + DetectionRun = 0, + DetectedObjectsIdentified = {}, + DetectedItems = {}, + } - self.DetectionSetGroup = DetectionSetGroup - self.DetectionRange = DetectionRange + --- @type DETECTION_BASE.DetectedObjects + -- @list <#DETECTION_BASE.DetectedObject> - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + --- @type DETECTION_BASE.DetectedObject + -- @field #string Name + -- @field #boolean Visible + -- @field #string Type + -- @field #number Distance + -- @field #boolean Identified - return self -end - ---- Detect Visual. --- @param #DETECTION_BASE self --- @param #boolean DetectVisual --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual -end - ---- Detect Optical. --- @param #DETECTION_BASE self --- @param #boolean DetectOptical --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical -end - ---- Detect Radar. --- @param #DETECTION_BASE self --- @param #boolean DetectRadar --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar -end - ---- Detect IRST. --- @param #DETECTION_BASE self --- @param #boolean DetectIRST --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST -end - ---- Detect RWR. --- @param #DETECTION_BASE self --- @param #boolean DetectRWR --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR -end - ---- Detect DLINK. --- @param #DETECTION_BASE self --- @param #boolean DetectDLINK --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK -end - ---- Determines if a detected object has already been identified during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject --- @return #boolean true if already identified. -function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified -end - ---- Identifies a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true -end - ---- UnIdentify a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false -end - ---- UnIdentify all detected objects during detection processing. --- @param #DETECTION_BASE self -function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. -end - ---- Gets a detected object with a given name. --- @param #DETECTION_BASE self --- @param #string ObjectName --- @return #DETECTION_BASE.DetectedObject -function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F3( ObjectName ) + --- @type DETECTION_BASE.DetectedItems + -- @list <#DETECTION_BASE.DetectedItem> - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] + --- @type DETECTION_BASE.DetectedItem + -- @field Core.Set#SET_UNIT Set + -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. + -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. + -- @field #boolean Changed Documents if the detected area has changes. + -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). + -- @field #number ItemID -- The identifier of the detected area. + -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. + -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + --- DETECTION constructor. + -- @param #DETECTION_BASE self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return #DETECTION_BASE self + function DETECTION_BASE:New( DetectionSetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE + + self.DetectedItemCount = 0 + self.DetectedItemMax = 0 + self.DetectedItems = {} + + self.DetectionSetGroup = DetectionSetGroup + + self.DetectionInterval = 30 + + self:InitDetectVisual( true ) + self:InitDetectOptical( false ) + self:InitDetectRadar( false ) + self:InitDetectRWR( false ) + self:InitDetectIRST( false ) + self:InitDetectDLINK( false ) + + -- Create FSM transitions. + + self:SetStartState( "Stopped" ) + self.CountryID = DetectionSetGroup:GetFirst():GetCountry() + + self:AddTransition( "Stopped", "Start", "Detecting") + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE 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 Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnBeforeStart + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnAfterStart + -- @param #DETECTION_BASE 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=#DETECTION_BASE] Start + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] __Start + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnLeaveDetecting + -- @param #DETECTION_BASE 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 Detecting. + -- @function [parent=#DETECTION_BASE] OnEnterDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + self:AddTransition( "Detecting", "Detect", "Detecting" ) + self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + + --- OnBefore Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnBeforeDetect + -- @param #DETECTION_BASE 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 Detect. + -- @function [parent=#DETECTION_BASE] OnAfterDetect + -- @param #DETECTION_BASE 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 Detect. + -- @function [parent=#DETECTION_BASE] Detect + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] __Detect + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Detecting", "Detected", "Detecting" ) + + --- OnBefore Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnBeforeDetected + -- @param #DETECTION_BASE 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 Detected. + -- @function [parent=#DETECTION_BASE] OnAfterDetected + -- @param #DETECTION_BASE 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 Detected. + -- @function [parent=#DETECTION_BASE] Detected + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] __Detected + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnBeforeStop + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnAfterStop + -- @param #DETECTION_BASE 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=#DETECTION_BASE] Stop + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] __Stop + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE 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 Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + return self + end + + do -- State Transition Handling + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterStart(From,Event,To) + self:__Detect(0.1) + end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterDetect(From,Event,To) + self:E( {From,Event,To}) + + local DetectDelay = 0.1 + self.DetectionCount = 0 + self.DetectionRun = 0 + self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table + + self.DetectionSetGroup:Flush() + + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + self:E( {DetectionGroupData}) + self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + self.DetectionCount = self.DetectionCount + 1 + DetectDelay = DetectDelay + 0.1 end end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) + self:E( {From,Event,To}) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if DetectionGroup:IsAlive() then + + self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = DetectionGroup:GetName() + + local DetectedUnits = {} + + local DetectedTargets = DetectionGroup:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T( DetectedTargets ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + self:T2( DetectedObject ) + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) + + -- Calculate Acceptance + + if self.AcceptRange and Distance > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + DetectionAccepted = false + end + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:E({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = {} + end + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Visible = Detection.visible + self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].Distance = Distance + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + end + + self:T2( self.DetectedObjects ) + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + self:__Detect( self.DetectionInterval ) + + self:T( "--> Create Detection Sets" ) + self:CreateDetectionSets() + end + + end + + + end + + do -- Initialization methods + + --- Detect Visual. + -- @param #DETECTION_BASE self + -- @param #boolean DetectVisual + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectVisual( DetectVisual ) + + self.DetectVisual = DetectVisual + end + + --- Detect Optical. + -- @param #DETECTION_BASE self + -- @param #boolean DetectOptical + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectOptical( DetectOptical ) + self:F2() + + self.DetectOptical = DetectOptical + end + + --- Detect Radar. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRadar + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRadar( DetectRadar ) + self:F2() + + self.DetectRadar = DetectRadar + end + + --- Detect IRST. + -- @param #DETECTION_BASE self + -- @param #boolean DetectIRST + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectIRST( DetectIRST ) + self:F2() + + self.DetectIRST = DetectIRST + end + + --- Detect RWR. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRWR + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRWR( DetectRWR ) + self:F2() + + self.DetectRWR = DetectRWR + end + + --- Detect DLINK. + -- @param #DETECTION_BASE self + -- @param #boolean DetectDLINK + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) + self:F2() + + self.DetectDLINK = DetectDLINK + end + + end + + do + + --- Set the detection interval time in seconds. + -- @param #DETECTION_BASE self + -- @param #number DetectionInterval Interval in seconds. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + self:F2() + + self.DetectionInterval = DetectionInterval + + return self + end + + end + + do -- Accept / Reject detected units + + --- Accept detections if within a range in meters. + -- @param #DETECTION_BASE self + -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptRange( AcceptRange ) + self:F2() + + self.AcceptRange = AcceptRange + + return self + end + + --- Accept detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptZones( AcceptZones ) + self:F2() + + if type( AcceptZones ) == "table" then + self.AcceptZones = AcceptZones + else + self.AcceptZones = { AcceptZones } + end + + return self + end + + --- Reject detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRejectZones( RejectZones ) + self:F2() + + if type( RejectZones ) == "table" then + self.RejectZones = RejectZones + else + self.RejectZones = { RejectZones } + end + + return self + end + + end + + do -- Probability methods + + --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- @param #DETECTION_BASE self + -- @param DistanceProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) + self:F2() + + self.DistanceProbability = DistanceProbability + + return self + end + + + --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- @param #DETECTION_BASE self + -- @param AlphaAngleProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) + self:F2() + + self.AlphaAngleProbability = AlphaAngleProbability + + return self + end + + --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- @param #DETECTION_BASE self + -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetZoneProbability( ZoneArray ) + self:F2() + + self.ZoneProbability = ZoneArray + + return self + end + + + end + + do -- Change processing + + --- Accepts changes from the detected item. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #DETECTION_BASE self + function DETECTION_BASE:AcceptChanges( DetectedItem ) + + DetectedItem.Changed = false + DetectedItem.Changes = {} + + return self + end + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) + + return self + end + + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @param #string ChangeUnitType + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 + DetectedItem.Changes[ChangeCode].ItemID = ItemID + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + + return self + end + + + end + + do -- Threat + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Background worker function to determine if there are friendlies nearby ... + -- @param #DETECTION_BASE self + function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) + self:F2() + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() + + DetectedItem.FriendliesNearBy = false + + if DetectedUnit then + + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = DetectedUnit:GetVec3(), + radius = 6000, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = true + return false + end + + return true + end + + world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + end + end + + end + + --- Determines if a detected object has already been identified during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + -- @return #boolean true if already identified. + function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) + self:F3( DetectedObject.Name ) + + local DetectedObjectName = DetectedObject.Name + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + end + + --- Identifies a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) + self:F( { "Identified:", DetectedObject.Name } ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = true + end + + --- UnIdentify a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = false + end + + --- UnIdentify all detected objects during detection processing. + -- @param #DETECTION_BASE self + function DETECTION_BASE:UnIdentifyAllDetectedObjects() + + self.DetectedObjectsIdentified = {} -- Table will be garbage collected. + end + + --- Gets a detected object with a given name. + -- @param #DETECTION_BASE self + -- @param #string ObjectName + -- @return #DETECTION_BASE.DetectedObject + function DETECTION_BASE:GetDetectedObject( ObjectName ) + self:F( ObjectName ) + + if ObjectName then + local DetectedObject = self.DetectedObjects[ObjectName] + + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end + end + end + + return nil + end + + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + + local DetectedItem = {} + self.DetectedItemCount = self.DetectedItemCount + 1 + self.DetectedItemMax = self.DetectedItemMax + 1 + + if DetectedItemIndex then + self.DetectedItems[DetectedItemIndex] = DetectedItem + else + self.DetectedItems[self.DetectedItemCount] = DetectedItem + end + + DetectedItem.Set = Set or SET_UNIT:New() + DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Removed = false + + return DetectedItem + end + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) + + local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + + DetectedItem.Zone = Zone + + return DetectedItem + end + + --- Removes an existing DetectedItem from the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. + function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) + + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end + + + --- Get the detected @{Set#SET_BASE}s. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItems + function DETECTION_BASE:GetDetectedItems() + + return self.DetectedItems + end + + --- Get the amount of SETs with detected objects. + -- @param #DETECTION_BASE self + -- @return #number Count + function DETECTION_BASE:GetDetectedItemsCount() + + local DetectedCount = self.DetectedItemCount + return DetectedCount + end + + --- Get a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return DETECTION_BASE.DetectedItem + function DETECTION_BASE:GetDetectedItem( Index ) + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem + end + + return nil + end + + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Set#SET_UNIT DetectedSet + function DETECTION_BASE:GetDetectedSet( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSetUnit = DetectedItem.Set + if DetectedSetUnit then + return DetectedSetUnit + end + + return nil + end + + do -- Zones + + --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Zone#ZONE_UNIT DetectedZone + function DETECTION_BASE:GetDetectedZone( Index ) + + local DetectedZone = self.DetectedItems[Index].Zone + if DetectedZone then + return DetectedZone + end + + return nil + end + + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemReportSummary( Index ) + self:F( Index ) + return nil + end + + --- Report detailed of a detectedion result. + -- @param #DETECTION_BASE self + -- @return #string + function DETECTION_BASE:DetectedReportDetailed() + self:F() + return nil + end + + --- Get the detection Groups. + -- @param #DETECTION_BASE self + -- @return Wrapper.Group#GROUP + function DETECTION_BASE:GetDetectionSetGroup() + + local DetectionSetGroup = self.DetectionSetGroup + return DetectionSetGroup + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE self + function DETECTION_BASE:CreateDetectionSets() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + + end + + + --- Schedule the DETECTION construction. + -- @param #DETECTION_BASE self + -- @param #number DelayTime The delay in seconds to wait the reporting. + -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. + -- @return #DETECTION_BASE self + function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) + self:F2() + + self.ScheduleDelayTime = DelayTime + self.ScheduleRepeatInterval = RepeatInterval + + self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) + return self + end + +end + +do -- DETECTION_UNITS + + --- DETECTION_UNITS class + -- @type DETECTION_UNITS + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @extends #DETECTION_BASE + DETECTION_UNITS = { + ClassName = "DETECTION_UNITS", + DetectionRange = nil, + } + + --- DETECTION_UNITS constructor. + -- @param Functional.Detection#DETECTION_UNITS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return Functional.Detection#DETECTION_UNITS self + function DETECTION_UNITS:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_UNITS self + -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_UNITS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_UNITS self + -- @return #DETECTION_UNITS self + function DETECTION_UNITS:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + self:E( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedUnitName ) + if not DetectedItem then + self:T( "Added new DetectedItem" ) + DetectedItem = self:AddDetectedItem( DetectedUnitName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObjectData.Name + DetectedItem.Visible = DetectedObjectData.Visible + DetectedItem.Distance = DetectedObjectData.Distance + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @return #string + function DETECTION_UNITS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSet = self:GetDetectedSet( Index ) + + self:T( DetectedSet ) + if DetectedSet then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + + if DetectedItemUnit then + self:T(DetectedItemUnit) + + local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" + local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" + + if DetectedItem.Type and UnitCategoryName and UnitCategoryType then + UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + else + UnitCategoryText = "Unknown target at " + end + + if DetectedItem.Visible == false then + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" + else + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" + end + + ReportSummary = string.format( + "%s%s", + UnitCategoryText, + UnitDistanceText + ) + end + + self:T( ReportSummary ) + + return ReportSummary + end end - return nil + --- Report detailed of a detection result. + -- @param #DETECTION_UNITS self + -- @return #string + function DETECTION_UNITS:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected units:" ) + for DetectedItemID, DetectedItem in ipairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + end ---- Get the detected @{Set#SET_BASE}s. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE.DetectedSets DetectedSets -function DETECTION_BASE:GetDetectedSets() +do -- DETECTION_TYPES - local DetectionSets = self.DetectedSets - return DetectionSets -end - ---- Get the amount of SETs with detected objects. --- @param #DETECTION_BASE self --- @return #number Count -function DETECTION_BASE:GetDetectedSetCount() - - local DetectionSetCount = #self.DetectedSets - return DetectionSetCount -end - ---- Get a SET of detected objects using a given numeric index. --- @param #DETECTION_BASE self --- @param #number Index --- @return Core.Set#SET_BASE -function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectionSet = self.DetectedSets[Index] - if DetectionSet then - return DetectionSet + --- DETECTION_TYPES class + -- @type DETECTION_TYPES + -- @extends #DETECTION_BASE + DETECTION_TYPES = { + ClassName = "DETECTION_TYPES", + DetectionRange = nil, + } + + --- DETECTION_TYPES constructor. + -- @param Functional.Detection#DETECTION_TYPES self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. + -- @return Functional.Detection#DETECTION_TYPES self + function DETECTION_TYPES:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self end - return nil -end - ---- Get the detection Groups. --- @param #DETECTION_BASE self --- @return Wrapper.Group#GROUP -function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup -end - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE self -function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - -end - - ---- Schedule the DETECTION construction. --- @param #DETECTION_BASE self --- @param #number DelayTime The delay in seconds to wait the reporting. --- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. --- @return #DETECTION_BASE self -function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_TYPES self + -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_TYPES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self -end - - ---- Form @{Set}s of detected @{Unit#UNIT}s in an array of @{Set#SET_BASE}s. --- @param #DETECTION_BASE self -function DETECTION_BASE:_DetectionScheduler( SchedulerName ) - self:F2( { SchedulerName } ) + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end - self.DetectionRun = self.DetectionRun + 1 - - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - local DetectionGroup = DetectionGroupData -- Wrapper.Group#GROUP - - if DetectionGroup:IsAlive() then - - local DetectionGroupName = DetectionGroup:GetName() + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end - local DetectionDetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_TYPES self + -- @return #DETECTION_TYPES self + function DETECTION_TYPES:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem:GetSet() -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + if not DetectedItem then + DetectedItem = self:AddDetectedItem( DetectedTypeName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @return #string + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() + + local ReportSummary = string.format( + "Type #%s - Threat Level [%s] (%2d)", + DetectedItem.Type, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G + ) + self:T( ReportSummary ) + + return ReportSummary + end + end + + --- Report detailed of a detection result. + -- @param #DETECTION_TYPES self + -- @return #string + function DETECTION_TYPES:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected types:" ) + for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + +end + + +do -- DETECTION_AREAS + + --- DETECTION_AREAS class + -- @type DETECTION_AREAS + -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends #DETECTION_BASE + DETECTION_AREAS = { + ClassName = "DETECTION_AREAS", + DetectionZoneRange = nil, + } + + + --- DETECTION_AREAS constructor. + -- @param #DETECTION_AREAS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @return #DETECTION_AREAS + function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) + + self.DetectionZoneRange = DetectionZoneRange + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_AREAS self + -- @param Index + -- @return #string + function DETECTION_AREAS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedZone( Index ) + local DetectedItemPointVec3 = DetectedZone:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + local ReportSummary = string.format( + "%s - Threat Level [%s] (%2d)", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G ) - for DetectionDetectedTargetID, DetectionDetectedTarget in pairs( DetectionDetectedTargets ) do - local DetectionObject = DetectionDetectedTarget.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectionObject ) + return ReportSummary + end + + return nil + end + + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_AREAS self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Calculate the maxium A2G threat level of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) + + local MaxThreatLevelA2G = 0 + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local ThreatUnit = UnitData -- Wrapper.Unit#UNIT + local ThreatLevelA2G = ThreatUnit:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + end + end + + self:T3( MaxThreatLevelA2G ) + DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + + end + + --- Find the nearest FAC of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return Wrapper.Unit#UNIT The nearest FAC unit + function DETECTION_AREAS:NearestFAC( DetectedItem ) + + local NearestFAC = nil + local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + + for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do + local FACUnit = FACUnitData -- Wrapper.Unit#UNIT + if FACUnit:IsActive() then + local Vec3 = FACUnit:GetVec3() + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) + if Distance < MinDistance then + MinDistance = Distance + NearestFAC = FACUnit + end + end + end + end + + DetectedItem.NearestFAC = NearestFAC + + end + + --- Returns the A2G threat level of the units in the DetectedItem + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #number a scale from 0 to 10. + function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) + + self:T3( DetectedItem.MaxThreatLevelA2G ) + return DetectedItem.MaxThreatLevelA2G + end + + + + --- Smoke the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_AREAS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:CreateDetectionSets() + self:F2() + + + self:T( "Checking Detected Items for new Detected Units ..." ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + + self:T( { "Detected Item ID:", DetectedItemID } ) - if DetectionObject and DetectionObject:isExist() and DetectionObject.id_ < 50000000 then - - local DetectionDetectedObjectName = DetectionObject:getName() - - local DetectionDetectedObjectPositionVec3 = DetectionObject:getPoint() - local DetectionGroupVec3 = DetectionGroup:GetVec3() - - local Distance = ( ( DetectionDetectedObjectPositionVec3.x - DetectionGroupVec3.x )^2 + - ( DetectionDetectedObjectPositionVec3.y - DetectionGroupVec3.y )^2 + - ( DetectionDetectedObjectPositionVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T2( { DetectionGroupName, DetectionDetectedObjectName, Distance } ) - - if Distance <= self.DetectionRange then - - if not self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = {} - end - self.DetectedObjects[DetectionDetectedObjectName].Name = DetectionDetectedObjectName - self.DetectedObjects[DetectionDetectedObjectName].Visible = DetectionDetectedTarget.visible - self.DetectedObjects[DetectionDetectedObjectName].Type = DetectionDetectedTarget.type - self.DetectedObjects[DetectionDetectedObjectName].Distance = DetectionDetectedTarget.distance - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = nil - end - end - end - end - self:T2( self.DetectedObjects ) - - -- okay, now we have a list of detected object names ... - -- Sort the table based on distance ... - table.sort( self.DetectedObjects, function( a, b ) return a.Distance < b.Distance end ) - end - end - - if self.DetectedObjects then - self:CreateDetectionSets() - end - - return true -end - - - ---- DETECTION_AREAS class --- @type DETECTION_AREAS --- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @field #DETECTION_AREAS.DetectedAreas DetectedAreas A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. --- @extends Functional.Detection#DETECTION_BASE -DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectedAreas = { n = 0 }, - DetectionZoneRange = nil, -} - ---- @type DETECTION_AREAS.DetectedAreas --- @list <#DETECTION_AREAS.DetectedArea> - ---- @type DETECTION_AREAS.DetectedArea --- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @field #boolean Changed Documents if the detected area has changes. --- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). --- @field #number AreaID -- The identifier of the detected area. --- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. --- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - ---- DETECTION_AREAS constructor. --- @param Functional.Detection#DETECTION_AREAS self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @return Functional.Detection#DETECTION_AREAS self -function DETECTION_AREAS:New( DetectionSetGroup, DetectionRange, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - - self:Schedule( 10, 10 ) - - return self -end - ---- Add a detected @{#DETECTION_AREAS.DetectedArea}. --- @param Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @param Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @return #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:AddDetectedArea( Set, Zone ) - local DetectedAreas = self:GetDetectedAreas() - DetectedAreas.n = self:GetDetectedAreaCount() + 1 - DetectedAreas[DetectedAreas.n] = {} - local DetectedArea = DetectedAreas[DetectedAreas.n] - DetectedArea.Set = Set - DetectedArea.Zone = Zone - DetectedArea.Removed = false - DetectedArea.AreaID = DetectedAreas.n - - return DetectedArea -end - ---- Remove a detected @{#DETECTION_AREAS.DetectedArea} with a given Index. --- @param #DETECTION_AREAS self --- @param #number Index The Index of the detection are to be removed. --- @return #nil -function DETECTION_AREAS:RemoveDetectedArea( Index ) - local DetectedAreas = self:GetDetectedAreas() - local DetectedAreaCount = self:GetDetectedAreaCount() - local DetectedArea = DetectedAreas[Index] - local DetectedAreaSet = DetectedArea.Set - DetectedArea[Index] = nil - return nil -end - - ---- Get the detected @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS.DetectedAreas DetectedAreas -function DETECTION_AREAS:GetDetectedAreas() - - local DetectedAreas = self.DetectedAreas - return DetectedAreas -end - ---- Get the amount of @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #number DetectedAreaCount -function DETECTION_AREAS:GetDetectedAreaCount() - - local DetectedAreaCount = self.DetectedAreas.n - return DetectedAreaCount -end - ---- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Set#SET_UNIT DetectedSet -function DETECTION_AREAS:GetDetectedSet( Index ) - - local DetectedSetUnit = self.DetectedAreas[Index].Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil -end - ---- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Zone#ZONE_UNIT DetectedZone -function DETECTION_AREAS:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedAreas[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil -end - ---- Background worker function to determine if there are friendlies nearby ... --- @param #DETECTION_AREAS self --- @param Wrapper.Unit#UNIT ReportUnit -function DETECTION_AREAS:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT - - DetectedArea.FriendliesNearBy = false - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedZoneUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedZoneUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedZoneUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedArea.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - -end - - - ---- Returns if there are friendlies nearby the FAC units ... --- @param #DETECTION_AREAS self --- @return #boolean trhe if there are friendlies nearby -function DETECTION_AREAS:IsFriendliesNearBy( DetectedArea ) - - self:T3( DetectedArea.FriendliesNearBy ) - return DetectedArea.FriendliesNearBy or false -end - ---- Calculate the maxium A2G threat level of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedArea ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedArea.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedArea.MaxThreatLevelA2G = MaxThreatLevelA2G - -end - ---- Find the nearest FAC of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return Wrapper.Unit#UNIT The nearest FAC unit -function DETECTION_AREAS:NearestFAC( DetectedArea ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedArea.NearestFAC = NearestFAC - -end - ---- Returns the A2G threat level of the units in the DetectedArea --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #number a scale from 0 to 10. -function DETECTION_AREAS:GetTreatLevelA2G( DetectedArea ) - - self:T3( DetectedArea.MaxThreatLevelA2G ) - return DetectedArea.MaxThreatLevelA2G -end - - - ---- Smoke the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self -end - ---- Flare the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self -end - ---- Smoke the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self -end - ---- Flare the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self -end - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeArea( DetectedArea, ChangeCode, AreaUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode].AreaID = AreaID - DetectedArea.Changes[ChangeCode].AreaUnitType = AreaUnitType - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, AreaUnitType } ) - - return self -end - - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @param #string ChangeUnitType --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeUnit( DetectedArea, ChangeCode, ChangeUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedArea.Changes[ChangeCode].AreaID = AreaID - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, ChangeUnitType } ) - - return self -end - ---- Make text documenting the changes of the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #string The Changes text -function DETECTION_AREAS:GetChangeText( DetectedArea ) - self:F( DetectedArea ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedArea.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.AreaID .. ". The center target is a " .. ChangeData.AreaUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". The new center target is a " .. ChangeData.AreaUnitType "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.AreaID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.AreaID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.AreaID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - -end - - ---- Accepts changes from the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AcceptChanges( DetectedArea ) - - DetectedArea.Changed = false - DetectedArea.Changes = {} - - return self -end - - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - - local DetectedSet = DetectedArea.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( DetectedArea.Zone.ZoneUNIT.UnitName ) - local DetectedZoneObject = self:GetDetectedObject( DetectedArea.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detecting Zone Object", DetectedArea.AreaID, DetectedArea.Zone, DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedArea.Zone.ZoneUNIT.UnitName ) - - self:AddChangeArea( DetectedArea, 'RAU', "Dummy" ) + local DetectedSet = DetectedItem.Set - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - -- Assign the Unit as the new center unit of the detected area. - DetectedArea.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeArea( DetectedArea, "AAU", DetectedArea.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedArea.Zone - if DetectedUnit:IsInZone( DetectedArea.Zone ) then + local AreaExists = false -- This flag will determine of the detected area is still existing. - -- Yes, the DetectedUnit is within the DetectedArea.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedArea.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedArea, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedArea, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - self:RemoveDetectedArea( DetectedAreaID ) - self:AddChangeArea( DetectedArea, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do + -- First test if the center unit is detected in the detection area. + self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) + local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) + self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - self:T( "Detection Area #" .. DetectedArea.AreaID ) - local DetectedSet = DetectedArea.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedArea.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedArea, "AU", DetectedUnit:GetTypeName() ) + if DetectedZoneObject then + + --self:IdentifyDetectedObject( DetectedZoneObject ) + AreaExists = true + + + + else + -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. + -- First remove the center unit from the set. + DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) + + self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + + -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + + -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. + -- If the DetectedUnit was already identified, DetectedObject will be nil. + if DetectedObject then + self:IdentifyDetectedObject( DetectedObject ) + AreaExists = true + + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + + -- Assign the Unit as the new center unit of the detected area. + DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) + + self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + + -- We don't need to add the DetectedObject to the area set, because it is already there ... + break + end end end + + -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. + -- Note that the position of the area may have moved due to the center unit repositioning. + -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. + if AreaExists then + + -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... + -- Those units within the zone are flagged as Identified. + -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Check if the DetectedUnit is within the DetectedItem.Zone + if DetectedUnit:IsInZone( DetectedItem.Zone ) then + + -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. + self:IdentifyDetectedObject( DetectedObject ) + + else + -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + end + + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) + DetectedSet:Remove( DetectedUnitName ) + + -- The DetectedObject has been identified, because it does not exist ... + -- self:IdentifyDetectedObject( DetectedObject ) + end + end + else + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + self:RemoveDetectedItem( DetectedItemID ) + self:AddChangeItem( DetectedItem, "RA" ) + end end + end - if AddedToDetectionArea == false then + -- We iterated through the existing detection areas and: + -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. + -- - We recentered the detection area to new center units where it was needed. + -- + -- Now we need to loop through the unidentified detected units and see where they belong: + -- - They can be added to a new detection area and become the new center unit. + -- - They can be added to a new detection area. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - -- New detection area - local DetectedArea = self:AddDetectedArea( - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedArea.Zone.ZoneUNIT.UnitName ) - DetectedArea.Set:AddUnit( DetectedUnit ) - self:AddChangeArea( DetectedArea, "AA", DetectedUnit:GetTypeName() ) - end - end - end + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + + if DetectedObject then - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - self:ReportFriendliesNearBy( { DetectedArea = DetectedArea, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedArea ) -- Calculate A2G threat level - self:NearestFAC( DetectedArea ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedArea.AreaID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() + -- We found an unidentified unit outside of any existing detection area. + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + local AddedToDetectionArea = false + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + self:T( "Detection Area #" .. DetectedItem.ItemID ) + local DetectedSet = DetectedItem.Set + if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then + self:IdentifyDetectedObject( DetectedObject ) + DetectedSet:AddUnit( DetectedUnit ) + AddedToDetectionArea = true + self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + end end end + + if AddedToDetectionArea == false then + + -- New detection area + local DetectedItem = self:AddDetectedItemZone( nil, + SET_UNIT:New(), + ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) + ) + --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + end end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:NearestFAC( DetectedItem ) + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + DetectedZone:BoundZone( 12, self.CountryID ) + end end + end - -end - - + +end --- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions -- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, -- even when there are hardly any players in the mission.** @@ -27815,11 +28964,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event 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. @@ -27830,11 +28974,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event 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. -- @function [parent=#AI_CAS_ZONE] Engage @@ -28062,7 +29201,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) if Detected == true then self:E( {"Target: ", DetectedUnit } ) 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 ) end end @@ -28093,8 +29232,8 @@ end -- @param #string Event The Event 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 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 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, @@ -28134,28 +29273,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageRoute[#EngageRoute+1] = CurrentRoutePoint - if self.Controllable:IsNotInZone( self.EngageZone ) then - - -- Find a random 2D point in EngageZone. - local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToEngageZoneVec2 ) - - -- Obtain a 3D @{Point} from the 2D point + altitude. - local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) - - -- Create a route point of type air. - local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint - - end - +-- if self.Controllable:IsNotInZone( self.EngageZone ) then +-- +-- -- Find a random 2D point in EngageZone. +-- local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() +-- self:T2( ToEngageZoneVec2 ) +-- +-- -- Obtain a 3D @{Point} from the 2D point + altitude. +-- local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) +-- +-- -- Create a route point of type air. +-- local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( +-- self.PatrolAltType, +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- self.EngageSpeed, +-- true +-- ) +-- +-- EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint +-- +-- end +-- --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Find a random 2D point in EngageZone. @@ -28210,9 +29349,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, --- NOW ROUTE THE GROUP! self.Controllable:WayPointExecute( 1 ) - self:SetDetectionInterval( 10 ) + self:SetDetectionInterval( 2 ) self:SetDetectionActivated() - self:__Target( -10 ) -- Start Targetting + self:__Target( -2 ) -- Start Targetting end end @@ -30014,7 +31153,7 @@ do -- ACT_ASSIGN_ACCEPT self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign() + self.Task:Assign( ProcessUnit, self.Task ) end end -- ACT_ASSIGN_ACCEPT @@ -30213,7 +31352,7 @@ do -- ACT_ROUTE -- @type ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -30309,6 +31448,115 @@ do -- 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 @@ -30316,7 +31564,7 @@ do -- ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE ACT_ROUTE_ZONE = { ClassName = "ACT_ROUTE_ZONE", @@ -30325,11 +31573,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. -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ROUTE_ZONE:New( TargetZone ) + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_ZONE:New( Zone ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - self.TargetZone = TargetZone + self.Zone = Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30341,7 +31589,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:Init( FsmRoute ) - self.TargetZone = FsmRoute.TargetZone + self.Zone = FsmRoute.Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30349,18 +31597,32 @@ do -- ACT_ROUTE_ZONE self.DisplayTime = 10 -- 10 seconds is the default 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. -- @param #ACT_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean 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." self:Message( RouteText ) end - return ProcessUnit:IsInZone( self.TargetZone ) + return ProcessUnit:IsInZone( self.Zone ) end --- Task Events @@ -30373,11 +31635,11 @@ do -- ACT_ROUTE_ZONE -- @param #string 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 TaskUnitVec2 = ProcessUnit:GetVec2() 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 ) end @@ -30576,15 +31838,6 @@ do -- ACT_ACCOUNT_DEADS self.TaskName = FsmAccount.TaskName end - - - function ACT_ACCOUNT_DEADS:_Destructor() - self:E("_Destructor") - - self:EventRemoveAll() - - end - --- Process Events --- StateMachine callback function @@ -30615,7 +31868,6 @@ do -- ACT_ACCOUNT_DEADS if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then 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." ) end end @@ -30628,7 +31880,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string To 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 ) else self:__NoMore( 1 ) @@ -30643,7 +31895,7 @@ do -- ACT_ACCOUNT_DEADS self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then - self:__Event( 1, EventData ) + self:Event( EventData ) end end @@ -30758,7 +32010,7 @@ do -- ACT_ASSIST function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) + local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) local function MenuSmoke( MenuParam ) self:E( MenuParam ) @@ -30775,6 +32027,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.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) 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 @@ -30875,7 +32138,9 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) self.Report = {} - self.Report[#self.Report+1] = Title + if Title then + self.Report[#self.Report+1] = Title + end return self end @@ -30886,7 +32151,7 @@ end -- @return #REPORT function REPORT:Add( Text ) self.Report[#self.Report+1] = Text - return self.Report[#self.Report+1] + return self.Report[#self.Report] end function REPORT:Text() @@ -30923,22 +32188,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:HandleEvent( EVENTS.Birth, --- @param #COMMANDCENTER self - --- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function( self, EventData ) - self:E( { EventData } ) - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - 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 MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + if EventData.IniObjectCategory == 1 then + local EventGroup = GROUP:Find( EventData.IniDCSGroup ) + if EventGroup and self:HasGroup( EventGroup ) then + 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 MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + self:ReportSummary( EventGroup ) + end + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + Mission:ReportDetails() + end end end @@ -31048,17 +32314,26 @@ function COMMANDCENTER:SetMenu() 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 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 - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu() - 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. -- @param #COMMANDCENTER self @@ -31079,6 +32354,14 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has 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. -- @param #COMMANDCENTER self -- @param #string Message @@ -31086,7 +32369,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. 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 self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) @@ -31102,6 +32386,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) end + --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self @@ -31149,22 +32434,6 @@ MISSION = { ClassName = "MISSION", Name = "", 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. @@ -31182,10 +32451,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self:SetStartState( "Idle" ) 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" ) + + --- 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" ) + + --- 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" ) + --- 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.CommandCenter = CommandCenter @@ -31197,14 +32640,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self.MissionCoalition = MissionCoalition self.Tasks = {} + + -- Private implementations + + return self end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onbeforeComplete( From, Event, To ) @@ -31217,10 +32664,10 @@ function MISSION:onbeforeComplete( From, Event, To ) return true -- Allow Mission completion. end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onenterCompleted( From, Event, To ) @@ -31338,23 +32785,25 @@ end --- Sets the Planned Task menu. -- @param #MISSION self -function MISSION:SetMenu() +-- @param #number MenuTime +function MISSION:SetMenu( MenuTime ) self:F() - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:SetMenu() + for _, TaskData in pairs( self:GetTasks() ) do + local Task = TaskData -- Tasking.Task#TASK + Task:SetMenu( MenuTime ) end end --- Removes the Planned Task menu. -- @param #MISSION self -function MISSION:RemoveMenu() +-- @param #number MenuTime +function MISSION:RemoveMenu( MenuTime ) self:F() for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() + Task:RemoveMenu( MenuTime ) end end @@ -31366,20 +32815,6 @@ function MISSION:GetCommandCenter() return self.CommandCenter 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. -- @param #MISSION self @@ -31395,28 +32830,18 @@ end -- @param #MISSION self -- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMissionMenu( TaskGroup ) +function MISSION:GetMenu( TaskGroup ) local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter.CommandCenterMenu + local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - - local TaskGroupName = TaskGroup:GetName() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ) + local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) return MissionMenu 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. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task @@ -31487,76 +32912,44 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end - - ---- old stuff - ---- Returns if a Mission has completed. --- @return bool +--- Is the @{Mission} **Completed**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsCompleted() - self:F() - return self.MissionStatus == "ACCOMPLISHED" + return self:Is( "Completed" ) end ---- Set a Mission to completed. -function MISSION:Completed() - self:F() - self.MissionStatus = "ACCOMPLISHED" - self:StatusToClients() +--- Is the @{Mission} **Idle**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsIdle() + return self:Is( "Idle" ) end ---- Returns if a Mission is ongoing. --- treturn bool +--- Is the @{Mission} **Ongoing**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsOngoing() - self:F() - return self.MissionStatus == "ONGOING" + return self:Is( "Ongoing" ) end ---- Set a Mission to ongoing. -function MISSION:Ongoing() - self:F() - self.MissionStatus = "ONGOING" - --self:StatusToClients() +--- Is the @{Mission} **Failed**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsFailed() + return self:Is( "Failed" ) end ---- Returns if a Mission is pending. --- treturn bool -function MISSION:IsPending() - self:F() - return self.MissionStatus == "PENDING" -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 +--- Is the @{Mission} **Hold**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsHold() + return self:Is( "Hold" ) end +--- Validates if the Mission has a Group +-- @param #MISSION +-- @return #boolean true if the Mission has a Group. function MISSION:HasGroup( TaskGroup ) local Has = false @@ -31649,107 +33042,6 @@ function MISSION:ReportDetails() return Report:Text() 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. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @usage @@ -31763,333 +33055,6 @@ function MISSION:GetTasks() 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 - --- This module contains the TASK class. -- -- 1) @{#TASK} class, extends @{Base#BASE} @@ -32277,7 +33242,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - Mission:AddTask( self ) return self end @@ -32285,9 +33249,13 @@ end --- Get the Task FSM Process Template -- @param #TASK self -- @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 --- Sets the Task FSM Process Template @@ -32320,7 +33288,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 self:IsStatePlanned() or self:IsStateReplanned() then 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 if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) @@ -32362,10 +33330,11 @@ function TASK:AbortUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then + self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end - self:PlayerAborted( PlayerUnit ) + self:Abort() end end end @@ -32431,7 +33400,7 @@ end ---- Assign the @{Task}to a @{Group}. +--- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -32442,7 +33411,11 @@ function TASK:AssignToGroup( TaskGroup ) 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 ) local TaskUnits = TaskGroup:GetUnits() @@ -32482,6 +33455,7 @@ function TASK:AssignToUnit( TaskUnit ) self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self @@ -32492,7 +33466,7 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit ) + self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) @@ -32539,28 +33513,37 @@ function TASK:SendBriefingToAssignedGroups() end ---- Assign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() 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() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) end end end + + --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -32571,10 +33554,12 @@ function TASK:IsAssignedToGroup( TaskGroup ) if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end @@ -32603,37 +33588,36 @@ end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -function TASK:SetMenu() +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup ) + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if self:IsStatePlanned() or self:IsStateReplanned() then + self:SetMenuForGroup( TaskGroup, MenuTime ) + 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 -- @param #TASK self -function TASK:SetMenuForGroup( TaskGroup ) +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else - self:SetAssignedMenuForGroup( TaskGroup ) + if not self:IsAssignedToGroup( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) + end end end @@ -32642,16 +33626,24 @@ end -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. +-- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) 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 TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) + 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 } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -32659,32 +33651,84 @@ end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup ) +function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { 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, TaskGroup ):SetTime( MenuTime ) return self 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}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:RemoveMenuForGroup( TaskGroup ) +function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + self:F() local Mission = self:GetMission() 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 ) - MissionMenu:Remove() +--- Remove the assigned menu option of the @{Task} for a @{Group}. +-- @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 function TASK.MenuAssignToGroup( MenuParam ) @@ -32697,19 +33741,21 @@ function TASK.MenuAssignToGroup( MenuParam ) self:AssignToGroup( TaskGroup ) end -function TASK.MenuTaskStatus( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskStatus( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup + local ReportText = self:ReportDetails() - --self:AssignToGroup( TaskGroup ) + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) + 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() end @@ -32754,15 +33800,26 @@ end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit +-- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self 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 return Fsm 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} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -32770,9 +33827,15 @@ end function TASK:RemoveStateMachine( TaskUnit ) 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 + collectgarbage() - self:T( "Garbage Collected, Processes should be finalized now ...") + self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} @@ -32887,6 +33950,32 @@ function TASK:IsStatePlanned() return self:Is( "Planned" ) 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**. -- @param #TASK self function TASK:StateAssigned() @@ -32967,7 +34056,7 @@ function TASK:onenterSuccess( From, Event, To ) 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:GetMission():__Complete( 1 ) @@ -33101,24 +34190,19 @@ function TASK:ReportDetails() -- Determine the status of the Task. local State = self:GetState() - -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} - for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local Player = PlayerGroup -- Wrapper.Group#GROUP - for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - if PlayerUnit and PlayerUnit:IsAlive() then - local PlayerName = PlayerUnit:GetPlayerName() - PlayerNames[#PlayerNames+1] = PlayerName - end + local PlayerReport = REPORT:New( " - Players:" ) + for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP + PlayerNames = PlayerGroup:GetPlayerNames() + if PlayerNames then + PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end - local PlayerNameText = table.concat( PlayerNames, ", " ) - Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText ) end -- 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() end @@ -33160,23 +34244,6 @@ end -- Reporting -- ------------------------------- -- 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. -- -- === -- @@ -33214,6 +34281,8 @@ do -- DETECTION MANAGER self:SetReportInterval( 30 ) self:SetReportDisplayTime( 25 ) + Detection:__Start( 5 ) + return self end @@ -33376,35 +34445,78 @@ do -- DETECTION_REPORTING end -do -- DETECTION_DISPATCHER +--- **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 - --- DETECTION_DISPATCHER class. - -- @type DETECTION_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 - -- @field Wrapper.Group#GROUP CommandCenter -- @extends Tasking.DetectionManager#DETECTION_MANAGER - DETECTION_DISPATCHER = { - ClassName = "DETECTION_DISPATCHER", + TASK_A2G_DISPATCHER = { + ClassName = "TASK_A2G_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 ) + --- 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 ) ) -- #DETECTION_DISPATCHER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER self.Detection = Detection - self.CommandCenter = CommandCenter self.Mission = Mission self:Schedule( 30 ) @@ -33413,15 +34525,15 @@ do -- DETECTION_DISPATCHER --- Creates a SEAD task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @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 DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + 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() @@ -33441,19 +34553,19 @@ do -- DETECTION_DISPATCHER end --- Creates a CAS task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + 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( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == true then @@ -33469,19 +34581,19 @@ do -- DETECTION_DISPATCHER end --- Creates a BAI task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + 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( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == false then @@ -33497,16 +34609,16 @@ do -- DETECTION_DISPATCHER 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 + -- 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.DetectedArea DetectedArea + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea ) + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) if Task then - if Task:IsStatePlanned() and DetectedArea.Changed == true then + if Task:IsStatePlanned() and DetectedItem.Changed == true then self:E( "Removing Tasking: " .. Task:GetTaskName() ) Task = Mission:RemoveTask( Task ) end @@ -33517,10 +34629,10 @@ do -- DETECTION_DISPATCHER --- 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. + -- @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 DETECTION_DISPATCHER:ProcessDetected( Detection ) + function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) self:F2() local AreaMsg = {} @@ -33528,129 +34640,377 @@ do -- DETECTION_DISPATCHER 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 DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) 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 ) } ) + 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 AreaID = DetectedArea.AreaID + local ItemID = DetectedItem.ItemID -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( "SEAD." .. AreaID ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea ) + local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) + SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- 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 ) ) + 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 - self:E( "Planned" ) - --SEADTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText() + ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( "CAS." .. AreaID ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea ) + local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) + CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- 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 ) ) + 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 - --CASTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText() + ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( "BAI." .. AreaID ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea ) + local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) + BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():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 ) ) + 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 - --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 + 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( DetectedArea ) + Detection:AcceptChanges( DetectedItem ) 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 + 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--- This module contains the TASK_SEAD classes. +end--- This module contains the TASK_A2G 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, +-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_SEAD 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 --- * **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. +-- * **Planned**: The A2G task is planned. +-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2G task is successfully completed. +-- * **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 --- --- @module Task_SEAD +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[WingThor]**: Concept, Advice & Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G +do -- TASK_A2G + + --- The TASK_A2G class + -- @type TASK_A2G + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_A2G = { + ClassName = "TASK_A2G", + } + + --- Instantiates a new TASK_A2G. + -- @param #TASK_A2G 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_A2G self + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G + self:F() + + self.TargetSetUnit = TargetSetUnit + self.TaskType = TaskType + + Mission:AddTask( self ) + + local Fsm = self:GetUnitProcess() + + + 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 + + --- 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 + + return self + + end + + --- @param #TASK_A2G self + function TASK_A2G:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + 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 + + + --- @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 @@ -33669,130 +35029,76 @@ do -- TASK_SEAD -- @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 + -- @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, TargetZone ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD + 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() - - 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 ---- (AI) (SP) (MP) Tasking for Air to Ground Processes. --- --- 1) @{#TASK_A2G} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, --- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G 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_A2G + end +end -do -- TASK_A2G +do -- TASK_BAI - --- The TASK_A2G class - -- @type TASK_A2G + --- The TASK_BAI class + -- @type TASK_BAI + -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_A2G = { - ClassName = "TASK_A2G", + TASK_BAI = { + ClassName = "TASK_BAI", } - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G self + --- 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 #string TaskType BAI or CAS -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) + -- @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() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - self.FACUnit = FACUnit - local A2GUnitProcess = self:GetUnitProcess() - - A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) - A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" ) - A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" ) - A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } ) - A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - --Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) ) - A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" ) - A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" ) - - function A2GUnitProcess: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 + 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", + } - --- @param #TASK_A2G self - function TASK_A2G:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - end - - + --- 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 --- The main include file for the MOOSE system. -- Test of permissions @@ -33854,7 +35160,7 @@ Include.File( "Tasking/CommandCenter" ) Include.File( "Tasking/Mission" ) Include.File( "Tasking/Task" ) Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_SEAD" ) +Include.File( "Tasking/Task_A2G_Dispatcher") Include.File( "Tasking/Task_A2G" ) diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index fcb1e53a6..91fd7f218 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170315_1916' ) +env.info( 'Moose Generation Timestamp: 20170317_2147' ) local base = _G Include = {} @@ -3048,12 +3048,14 @@ function BASE:_Destructor() --self:EventRemoveAll() end + +-- THIS IS WHY WE NEED LUA 5.2 ... function BASE:_SetDestructor() -- TODO: Okay, this is really technical... -- 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. - --[[ + local proxy = newproxy(true) local proxyMeta = getmetatable(proxy) @@ -3068,7 +3070,7 @@ function BASE:_SetDestructor() -- table is about to be garbage-collected - then the __gc hook -- will be invoked and the destructor called rawset( self, '__proxy', proxy ) - --]] + end --- This is the worker method to inherit from a parent class. @@ -3084,7 +3086,7 @@ function BASE:Inherit( Child, Parent ) setmetatable( Child, Parent ) Child.__index = Child - Child:_SetDestructor() + --Child:_SetDestructor() end --self:T( 'Inherited from ' .. Parent.ClassName ) return Child @@ -3696,8 +3698,12 @@ end ---- 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} -- -- The @{Scheduler#SCHEDULER} class creates schedule. @@ -3845,6 +3851,13 @@ function SCHEDULER:Remove( ScheduleID ) _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) end +--- Clears all pending schedules. +-- @param #SCHEDULER self +function SCHEDULER:Clear() + self:F3( ) + + _SCHEDULEDISPATCHER:Clear( self ) +end @@ -3925,7 +3938,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- 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. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -4042,11 +4055,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) if CallID then local Schedule = self.Schedule[Scheduler] - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) + -- Only start when there is no ScheduleID defined! + -- This prevents to "Start" the scheduler twice with the same CallID... + if not Schedule[CallID].ScheduleID then + Schedule[CallID].ScheduleID = timer.scheduleFunction( + Schedule[CallID].CallHandler, + CallID, + timer.getTime() + Schedule[CallID].Start + ) + end else for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Start( Scheduler, CallID ) -- Recursive @@ -4059,7 +4076,12 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then 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 for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do self:Stop( Scheduler, CallID ) -- Recursive @@ -4067,6 +4089,14 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) 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 + --- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. @@ -4355,7 +4385,7 @@ local _EVENTMETA = { }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, - Event = "OnEventTakeOff", + Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { @@ -4496,11 +4526,11 @@ function EVENT:Init( EventID, EventClass ) -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = {} + self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) end 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 return self.Events[EventID][EventPriority][EventClass] end @@ -4570,11 +4600,11 @@ end -- @param EventClass The instance of the class for which the event is. -- @param #function OnEventFunction -- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, OnEventFunction ) +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - OnEventFunction( self, EventUnit.name, EventFunction, EventClass ) + self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) end return self end @@ -4588,9 +4618,9 @@ end function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) self:F2( { EventID } ) - local Event = self:Init( EventID, EventClass ) - Event.EventFunction = EventFunction - Event.EventClass = EventClass + local EventData = self:Init( EventID, EventClass ) + EventData.EventFunction = EventFunction + EventData.EventClass = EventClass return self end @@ -4606,13 +4636,13 @@ end function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) - local Event = self:Init( EventID, EventClass ) - if not Event.EventUnit then - Event.EventUnit = {} + local EventData = self:Init( EventID, EventClass ) + if not EventData.EventUnit then + EventData.EventUnit = {} end - Event.EventUnit[UnitName] = {} - Event.EventUnit[UnitName].EventFunction = EventFunction - Event.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit[UnitName] = {} + EventData.EventUnit[UnitName].EventFunction = EventFunction + EventData.EventUnit[UnitName].EventClass = EventClass return self end @@ -4647,51 +4677,11 @@ do -- OnBirth function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) return self 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 do -- OnCrash @@ -4705,49 +4695,10 @@ do -- OnCrash function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) return self 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 @@ -4762,96 +4713,13 @@ do -- OnDead function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnDeadForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) return self 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 -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 --- Create an OnLand event handler for a group @@ -4863,38 +4731,11 @@ do -- OnLand function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) return self 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 do -- OnTakeOff @@ -4907,38 +4748,11 @@ do -- OnTakeOff function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) return self 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 do -- OnEngineShutDown @@ -4952,210 +4766,11 @@ do -- OnEngineShutDown function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) return self 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 @@ -5278,7 +4893,9 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 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 @@ -5299,8 +4916,10 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function 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( function() return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) @@ -5313,8 +4932,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -5328,7 +4949,9 @@ function EVENT:onEvent( Event ) -- First test if a EventFunction is Set, otherwise search for the default function 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( function() @@ -5342,8 +4965,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -5361,9 +4986,11 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.IniGroupName] then -- First test if a EventFunction is Set, otherwise search for the default function 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( function() return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) @@ -5376,8 +5003,10 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() return EventFunction( EventClass, Event ) @@ -5389,8 +5018,10 @@ function EVENT:onEvent( Event ) if EventData.EventGroup[Event.TgtGroupName] 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( function() return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) @@ -5403,7 +5034,9 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() @@ -5425,8 +5058,9 @@ function EVENT:onEvent( Event ) if EventData.EventFunction then -- 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( function() return EventData.EventFunction( EventClass, Event ) @@ -5438,11 +5072,14 @@ function EVENT:onEvent( Event ) if EventFunction and type( EventFunction ) == "function" then -- 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( function() - return EventFunction( EventClass, Event ) + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value end, ErrorHandler ) end end @@ -5456,6 +5093,8 @@ function EVENT:onEvent( Event ) else self:E( { _EVENTMETA[Event.id].Text, Event } ) end + + Event = nil end --- The EVENTHANDLER structure @@ -5613,6 +5252,8 @@ do -- MENU_BASE } --- Consructor + -- @param #MENU_BASE + -- @return #MENU_BASE function MENU_BASE:New( MenuText, ParentMenu ) local MenuParentPath = {} @@ -5625,10 +5266,43 @@ do -- MENU_BASE self.MenuPath = nil self.MenuText = MenuText self.MenuParentPath = MenuParentPath + self.Menus = {} + self.MenuCount = 0 + self.MenuRemoveParent = false + self.MenuTime = timer.getTime() return self 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 do -- MENU_COMMAND_BASE @@ -5636,7 +5310,7 @@ do -- MENU_COMMAND_BASE --- The MENU_COMMAND_BASE class -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -5645,6 +5319,8 @@ do -- MENU_COMMAND_BASE } --- Constructor + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5664,7 +5340,7 @@ do -- MENU_MISSION --- The MENU_MISSION class -- @type MENU_MISSION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -5673,7 +5349,7 @@ do -- MENU_MISSION -- @param #MENU_MISSION self -- @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). - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:New( MenuText, ParentMenu ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) @@ -5700,7 +5376,7 @@ do -- MENU_MISSION --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! -- @param #MENU_MISSION self - -- @return #MENU_MISSION self + -- @return #MENU_MISSION function MENU_MISSION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5731,7 +5407,7 @@ do -- MENU_MISSION_COMMAND --- The MENU_MISSION_COMMAND class -- @type MENU_MISSION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -5781,7 +5457,7 @@ do -- MENU_COALITION --- The MENU_COALITION class -- @type MENU_COALITION - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- 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). @@ -5855,7 +5531,7 @@ do -- MENU_COALITION --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! -- @param #MENU_COALITION self - -- @return #MENU_COALITION self + -- @return #MENU_COALITION function MENU_COALITION:RemoveSubMenus() self:F( self.MenuPath ) @@ -5886,7 +5562,7 @@ do -- MENU_COALITION_COMMAND --- The MENU_COALITION_COMMAND class -- @type MENU_COALITION_COMMAND - -- @extends Menu#MENU_COMMAND_BASE + -- @extends Core.Menu#MENU_COMMAND_BASE MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -5898,7 +5574,7 @@ do -- MENU_COALITION_COMMAND -- @param Menu#MENU_COALITION ParentMenu The parent menu. -- @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. - -- @return #MENU_COALITION_COMMAND self + -- @return #MENU_COALITION_COMMAND function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) @@ -5943,7 +5619,7 @@ do -- MENU_CLIENT --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @type MENU_CLIENT - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -6084,7 +5760,7 @@ do -- MENU_CLIENT --- The MENU_CLIENT_COMMAND class -- @type MENU_CLIENT_COMMAND - -- @extends Menu#MENU_COMMAND + -- @extends Core.Menu#MENU_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -6170,7 +5846,7 @@ do --- The MENU_GROUP class -- @type MENU_GROUP - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -6244,8 +5920,6 @@ do self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) MenuGroup._Menus[Path] = self - self.Menus = {} - self.MenuGroup = MenuGroup self.Path = Path self.MenuGroupID = MenuGroup:GetID() @@ -6255,8 +5929,10 @@ do self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self:F( { self.ParentMenu.Menus, MenuText } ) + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end @@ -6267,42 +5943,56 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus() - self:F( self.MenuPath ) + function MENU_GROUP:RemoveSubMenus( MenuTime ) + self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + for MenuText, Menu in pairs( self.Menus ) do + Menu:Remove( MenuTime ) end end - + + --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self + -- @param MenuTime -- @return #nil - function MENU_GROUP:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus() - - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] + self:RemoveSubMenus( MenuTime ) - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + 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 - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil end + return nil end --- The MENU_GROUP_COMMAND class -- @type MENU_GROUP_COMMAND - -- @extends Menu#MENU_BASE + -- @extends Core.Menu#MENU_BASE MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -6314,13 +6004,14 @@ do -- @param ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @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, ... ) MenuGroup._Menus = MenuGroup._Menus or {} local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] + self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) MenuGroup._Menus[Path] = self @@ -6331,33 +6022,45 @@ do self.MenuText = MenuText 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 ) - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F( { ParentMenu.Menus, MenuText } ) end end - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - return self end --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self + -- @param MenuTime -- @return #nil - function MENU_GROUP_COMMAND:Remove() - self:F( { self.MenuGroupID, self.MenuPath } ) + function MENU_GROUP_COMMAND:Remove( MenuTime ) + self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - 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 - self:E( self.MenuGroup._Menus[self.Path] ) - self.MenuGroup._Menus[self.Path] = nil - self = nil + if not MenuTime or self.MenuTime ~= MenuTime then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + + 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 return nil @@ -6367,6 +6070,8 @@ end --- **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: @@ -6614,6 +6319,58 @@ function ZONE_BASE:GetVec2() return nil 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. -- @param #ZONE_BASE self -- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. @@ -6628,6 +6385,13 @@ function ZONE_BASE:GetRandomPointVec2() return nil 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. -- @param #ZONE_BASE self -- @return #nil The bounding square. @@ -6714,8 +6478,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @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 -function ZONE_RADIUS:BoundZone( Points ) +function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() @@ -6731,8 +6496,10 @@ function ZONE_RADIUS:BoundZone( Points ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local Tire = { - ["country"] = "USA", + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -6744,7 +6511,10 @@ function ZONE_RADIUS:BoundZone( Points ) ["heading"] = 0, } -- 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 return self @@ -7177,8 +6947,9 @@ end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self +-- @param #boolean UnBound If true, the tyres will be destroyed. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( ) +function ZONE_POLYGON_BASE:BoundZone( UnBound ) local i local j @@ -7207,8 +6978,11 @@ function ZONE_POLYGON_BASE:BoundZone( ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- 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 j = i @@ -7444,6 +7218,8 @@ DATABASE = { PLAYERSJOINED = {}, CLIENTS = {}, AIRBASES = {}, + COUNTRY_ID = {}, + COUNTRY_NAME = {}, NavPoints = {}, } @@ -8151,6 +7927,9 @@ function DATABASE:_RegisterTemplates() local CountryName = string.upper(cntry_data.name) 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]["countryId"] = cntry_data.id @@ -8434,6 +8213,7 @@ SET_BASE = { Filter = {}, Set = {}, 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. @@ -8452,10 +8232,14 @@ function SET_BASE:New( Database ) self.YieldInterval = 10 self.TimeInterval = 0.001 + self.Set = {} + self.List = {} self.List.__index = self.List self.List = setmetatable( { Count = 0 }, self.List ) + self.Index = {} + self.CallScheduler = SCHEDULER:New( self ) self:SetEventPriority( 2 ) @@ -8507,6 +8291,8 @@ function SET_BASE:Add( ObjectName, Object ) self.Set[ObjectName] = t._ + table.insert( self.Index, ObjectName ) + end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -8558,7 +8344,15 @@ function SET_BASE:Remove( ObjectName ) t._prev = nil 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 + end end @@ -8578,12 +8372,50 @@ function SET_BASE:Get( ObjectName ) 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. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - return self.List.Count + return #self.Index end @@ -8846,7 +8678,8 @@ function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArgumen return false end - self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() return self end @@ -8919,7 +8752,7 @@ end --- SET_GROUP class -- @type SET_GROUP --- @extends #SET_BASE +-- @extends Core.Set#SET_BASE SET_GROUP = { ClassName = "SET_GROUP", Filter = { @@ -12178,10 +12011,20 @@ do -- FSM 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 self:T( "Calling " .. handler ) 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 end end @@ -12380,8 +12223,66 @@ do -- FSM_CONTROLLABLE self:SetControllable( Controllable ) 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 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. -- @param #FSM_CONTROLLABLE self @@ -12449,12 +12350,34 @@ do -- FSM_PROCESS function FSM_PROCESS:Init( FsmProcess ) self:T( "No Initialisation" ) 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. -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Copy( Controllable, Task ) self:T( { self:GetClassNameAndID() } ) + local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS @@ -12473,7 +12396,7 @@ do -- FSM_PROCESS -- Copy Processes 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 ) end @@ -12491,6 +12414,22 @@ do -- FSM_PROCESS return NewFsm 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. -- @param #FSM_PROCESS self @@ -13985,48 +13924,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At return DCSTask end - --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @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 Wrapper.Unit#UNIT AttackUnit The UNIT. +-- @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 #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 #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. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack ) - self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } ) - - -- AttackUnit = { - -- id = 'AttackUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- } - -- } +function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack ) + self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } ) local DCSTask DCSTask = { id = 'AttackUnit', params = { - altitudeEnabled = true, unitId = AttackUnit:GetID(), - attackQtyLimit = AttackQtyLimit or false, - attackQty = AttackQty or 2, + groupAttack = GroupAttack or false, + visible = Visible or false, expend = WeaponExpend or "Auto", - altitude = 2000, - directionEnabled = true, - groupAttack = true, - --weaponType = WeaponType or 1073741822, - direction = Direction or 0, - } + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + weaponType = 1073741822, + }, } self:E( DCSTask ) @@ -14640,7 +14566,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, end ---- (AIR) Attack the Unit. +--- (AIR) Search and attack the Unit. -- @param #CONTROLLABLE self -- @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. @@ -15872,7 +15798,7 @@ GROUP = { -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name -- @return #GROUP self function GROUP:Register( GroupName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) + self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName @@ -15990,7 +15916,7 @@ function GROUP:GetCategory() return nil end ---- Returns the category name of the DCS Group. +--- Returns the category name of the #GROUP. -- @param #GROUP self -- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship function GROUP:GetCategoryName() @@ -16685,7 +16611,32 @@ do -- Event Handling end end ---- This module contains the UNIT class. + +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--- This module contains the UNIT class. -- -- 1) @{#UNIT} class, extends @{Controllable#CONTROLLABLE} -- =========================================================== @@ -17209,6 +17160,31 @@ function UNIT:GetLife0() return nil 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 ... -- The following threat levels are foreseen: -- @@ -17227,14 +17203,14 @@ end function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes - self:E( Attributes ) + self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" if self:IsGround() then - self:E( "Ground" ) + self:T( "Ground" ) local ThreatLevels = { "Unarmed", @@ -17272,7 +17248,7 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:E( "Air" ) + self:T( "Air" ) local ThreatLevels = { "Unarmed", @@ -17306,7 +17282,7 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:E( "Ship" ) + self:T( "Ship" ) --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, @@ -18165,7 +18141,7 @@ function STATIC:FindByName( StaticName, RaiseError ) self.StaticName = StaticName if StaticFound then - StaticFound:F( { StaticName } ) + StaticFound:F3( { StaticName } ) return StaticFound end @@ -19369,9 +19345,10 @@ function SCORING:_EventOnDeadOrCrash( Event ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end + self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - + local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) @@ -20009,7 +19986,9 @@ CLEANUP = { -- or -- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) -- 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 } ) if type( ZoneNames ) == 'table' then @@ -20021,7 +20000,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - _EVENTDISPATCHER:OnBirth( self._OnEventBirth, self ) + self:HandleEvent( EVENTS.Birth ) self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) @@ -20082,32 +20061,24 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end -function CLEANUP:_OnEventBirth( Event ) - self:F( { Event } ) +--- @param #CLEANUP self +-- @param Core.Event#EVENTDATA EventData +function CLEANUP:_OnEventBirth( EventData ) + self:F( { EventData } ) - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.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() + self.CleanUpList[EventData.IniDCSUnitName] = {} + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName + 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 @@ -21987,10 +21958,11 @@ end -- 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 -- on defined intervals (currently every minute). --- @module MOVEMENT +-- @module Movement --- the MOVEMENT class --- @type +-- @type MOVEMENT +-- @extends Core.Base#BASE MOVEMENT = { ClassName = "MOVEMENT", } @@ -22004,7 +21976,7 @@ MOVEMENT = { -- 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 ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT self:F( { MovePrefixes, MoveMaximum } ) if type( MovePrefixes ) == 'table' then @@ -22017,7 +21989,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum ) 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. - _EVENTDISPATCHER:OnBirth( self.OnBirth, self ) + self:HandleEvent( EVENTS.Birth ) -- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) -- @@ -22044,24 +22016,26 @@ end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnBirth( Event ) - self:F( { Event } ) +-- @param #MOVEMENT self +-- @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 Event.IniDCSUnit then - self:T( "Birth object : " .. Event.IniDCSUnitName ) - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + if EventData.IniDCSUnit then + self:T( "Birth object : " .. EventData.IniDCSUnitName ) + if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then 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.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName + self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName self:T( self.AliveUnits ) 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 @@ -22148,25 +22122,28 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - _EVENTDISPATCHER:OnShot( self.EventShot, self ) + + self:HandleEvent( EVENTS.Shot ) return self 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. -- @see SEAD -function SEAD:EventShot( Event ) - self:F( { Event } ) +-- @param #SEAD +-- @param Core.Event#EVENTDATA EventData +function SEAD:OnEventShot( EventData ) + self:F( { EventData } ) - local SEADUnit = Event.IniDCSUnit - local SEADUnitName = Event.IniDCSUnitName - local SEADWeapon = Event.Weapon -- Identify the weapon fired - local SEADWeaponName = Event.WeaponName -- return weapon type + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeaponName = EventData.WeaponName -- return weapon type -- Start of the 2nd loop 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 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 _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() @@ -22325,7 +22302,7 @@ end -- -- 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.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. @@ -22369,6 +22346,7 @@ end -- @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 Core.Menu#MENU_CLIENT EscortMenuResumeMission +-- @field Functional.Detection#DETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", EscortName = nil, -- The Escort Name @@ -22417,14 +22395,22 @@ ESCORT = { -- -- 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." ) 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.EscortClient = EscortClient -- Wrapper.Client#CLIENT self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP self.EscortName = EscortName 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. if not self.EscortClient._EscortGroups then self.EscortClient._EscortGroups = {} @@ -22434,7 +22420,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection end self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) @@ -22459,13 +22445,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) self.FollowDistance = 100 self.CT1 = 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 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 allows to visualize where the escort is flying to. -- @param #ESCORT self @@ -22523,7 +22526,7 @@ function ESCORT:MenuFollowAt( Distance ) self.EscortMenuJoinUpAndFollow = {} 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 end @@ -22581,11 +22584,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuHold, ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortGroup, - ParamHeight = Height, - ParamSeconds = Seconds - } + self, + self.EscortGroup, + Height, + Seconds ) end @@ -22702,9 +22704,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) MenuText, self.EscortMenuScan, ESCORT._ScanTargets, - { ParamSelf = self, - ParamScanDuration = 30 - } + self, + 30 ) end @@ -22734,11 +22735,11 @@ function ESCORT:MenuFlare( MenuTextFormat ) end if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = 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.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.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.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.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, self, FLARECOLOR.Green, "Released a green 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, self, FLARECOLOR.White, "Released a white flare!" ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) end return self @@ -22767,12 +22768,12 @@ function ESCORT:MenuSmoke( MenuTextFormat ) end if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = 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.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.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.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.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.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, self, SMOKECOLOR.Green, "Releasing green 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, self, SMOKECOLOR.White, "Releasing white 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, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) end end @@ -22797,9 +22798,9 @@ function ESCORT:MenuReportTargets( Seconds ) end -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + 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, self, true ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) @@ -22836,16 +22837,16 @@ function ESCORT:MenuROE( MenuTextFormat ) -- Rules of Engagement self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) 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 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 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 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 @@ -22865,16 +22866,16 @@ function ESCORT:MenuEvasion( MenuTextFormat ) -- Reaction to Threats self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) 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 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 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 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 @@ -22899,18 +22900,14 @@ end --- @param #MENUPARAM MenuParam -function ESCORT._HoldPosition( MenuParam ) +function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP 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 GroupVec3 = EscortGroup:GetUnit(1):GetVec3() @@ -22943,13 +22940,12 @@ function ESCORT._HoldPosition( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._JoinUpAndFollow( MenuParam ) +function ESCORT:_JoinUpAndFollow( Distance ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.Distance = MenuParam.ParamDistance + self.Distance = Distance self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end @@ -22962,7 +22958,7 @@ end function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() @@ -22971,44 +22967,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self.CT1 = 0 self.GT1 = 0 - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Flare( MenuParam ) +function ESCORT:_Flare( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Flare( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._Smoke( MenuParam ) +function ESCORT:_Smoke( Color, Message ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local Color = MenuParam.ParamColor - local Message = MenuParam.ParamMessage - EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ReportNearbyTargetsNow( MenuParam ) +function ESCORT:_ReportNearbyTargetsNow() - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient @@ -23016,17 +23003,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) end -function ESCORT._SwitchReportNearbyTargets( MenuParam ) +function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - self.ReportTargets = MenuParam.ParamReportTargets + self.ReportTargets = ReportTargets if self.ReportTargets then if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 ) + self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else routines.removeFunction( self.ReportTargetsScheduler ) @@ -23035,40 +23021,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) end --- @param #MENUPARAM MenuParam -function ESCORT._ScanTargets( MenuParam ) +function ESCORT:_ScanTargets( ScanDuration ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP local EscortClient = self.EscortClient - local ScanDuration = MenuParam.ParamScanDuration - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsHelicopter() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) elseif EscortGroup:IsAirPlane() then - SCHEDULER:New( EscortGroup, EscortGroup.PushTask, - { EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - 1 - ) + EscortGroup:PushTask( + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ), 1 ) end EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start() + self.FollowScheduler:Start( self.FollowSchedule ) end end @@ -23085,124 +23062,157 @@ function _Resume( EscortGroup ) end ---- @param #MENUPARAM MenuParam -function ESCORT._AttackTarget( MenuParam ) +--- @param #ESCORT self +-- @param #number DetectedItemID +function ESCORT:_AttackTarget( DetectedItemID ) - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup + local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP + self:E( EscortGroup ) local EscortClient = self.EscortClient - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() EscortGroup:OptionROTPassiveDefense() EscortGroup:SetState( EscortGroup, "Escort", self ) - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 - SCHEDULER:New( EscortGroup, - EscortGroup.PushTask, - { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @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 EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) 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 EscortClient = self.EscortClient - local EscortGroupAttack = MenuParam.ParamEscortGroup - local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT - self.FollowScheduler:Stop() - - self:T( AttackUnit ) + self.FollowScheduler:Stop( self.FollowSchedule ) if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - SCHDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskAttackUnit( AttackUnit ), - EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - } - ) - }, 10 + + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 - SCHEDULER:New( EscortGroupAttack, - EscortGroupAttack.PushTask, - { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) - } - ) - }, 10 + local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) + + local Tasks = {} + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + 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 + EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROE( MenuParam ) +function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROEFunction = MenuParam.ParamFunction - local EscortROEMessage = MenuParam.ParamMessage - pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ROT( MenuParam ) +function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local EscortROTFunction = MenuParam.ParamFunction - local EscortROTMessage = MenuParam.ParamMessage - pcall( function() EscortROTFunction() end ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end --- @param #MENUPARAM MenuParam -function ESCORT._ResumeMission( MenuParam ) +function ESCORT:_ResumeMission( WayPoint ) - local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - local WayPoint = MenuParam.ParamWayPoint - - self.FollowScheduler:Stop() + self.FollowScheduler:Stop( self.FollowSchedule ) local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) @@ -23346,176 +23356,244 @@ function ESCORT:_ReportTargetsScheduler() self:F( self.EscortGroup:GetName() ) 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 = "" - 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 EscortGroupName = self.EscortGroup:GetName() + + self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - 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 + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() 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 } ) + local DetectedItems = self.Detection:GetDetectedItems() + self:E( DetectedItems ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() + local DetectedTargets = false + + local DetectedMsgs = {} + + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end + local ClientEscortTargets = EscortGroupData.Detection - --for MenuIndex = 1, #self.EscortMenuAttackTargets do - -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) - -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() - --end + for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + self:E( { DetectedItemID, DetectedItem } ) + -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. + + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - 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, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - end - end + if ClientEscortGroupName == EscortGroupName then + + DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + + MENU_CLIENT_COMMAND:New( self.EscortClient, + DetectedItemReportSummary, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + self, + DetectedItemID + ) 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 + + DetectedTargets = true + end end - - if EscortTargetMessages ~= "" and self.ReportTargets == true then - self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) + self:E( DetectedMsgs ) + if DetectedTargets then + self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else - self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) 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 - - 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 return false @@ -23693,7 +23771,7 @@ function MISSILETRAINER:New( Distance, Briefing ) self.Distance = Distance / 1000 - _EVENTDISPATCHER:OnShot( self._EventShot, self ) + self:HandleEvent( EVENTS.Shot ) self.DBClients = SET_CLIENT:New():FilterStart() @@ -23971,14 +24049,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. -- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA Event -function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) +-- @param Core.Event#EVENTDATA EventData +function MISSILETRAINER:OnEventShot( EVentData ) + self:F( { EVentData } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = EVentData.IniDCSUnit + local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName + local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired + local TrainerWeaponName = EVentData.WeaponName -- return weapon type self:T( "Missile Launched = " .. TrainerWeaponName ) @@ -25426,17 +25504,17 @@ end -- -- === -- --- 1) @{Detection#DETECTION_BASE} class, extends @{Base#BASE} --- ========================================================== --- The @{Detection#DETECTION_BASE} class defines the core functions to administer detected objects. --- The @{Detection#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- # 1) @{#DETECTION_BASE} class, extends @{Fsm#FSM} -- --- 1.1) DETECTION_BASE constructor --- ------------------------------- --- Construct a new DETECTION_BASE instance using the @{Detection#DETECTION_BASE.New}() method. +-- The @{#DETECTION_BASE} class defines the core functions to administer detected objects. +-- The @{#DETECTION_BASE} class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). +-- +-- ## 1.1) DETECTION_BASE constructor +-- +-- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. +-- +-- ## 1.2) DETECTION_BASE initialization -- --- 1.2) DETECTION_BASE initialization --- ---------------------------------- -- By default, detection will return detected objects with all the detection sensors available. -- However, you can ask how the objects were found with specific detection methods. -- If you use one of the below methods, the detection will work with the detection method specified. @@ -25444,969 +25522,2040 @@ end -- -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: -- --- * @{Detection#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. --- * @{Detection#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. --- * @{Detection#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. --- * @{Detection#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. --- * @{Detection#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. --- * @{Detection#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. +-- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. +-- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. +-- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. +-- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. +-- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. +-- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. -- --- 1.3) Obtain objects detected by DETECTION_BASE --- ---------------------------------------------- --- DETECTION_BASE builds @{Set}s of objects detected. These @{Set#SET_BASE}s can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSets}(). --- The method will return a list (table) of @{Set#SET_BASE} objects. +-- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list +-- +-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later +-- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains +-- a SET_UNIT object that contains the detected units that belong to that group. +-- +-- Derived classes will apply different methods to group the detected units. +-- Examples are per area, per quadrant, per distance, per type. +-- See further the derived DETECTION classes on which grouping methods are currently supported. +-- +-- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: +-- +-- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. +-- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). +-- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information +-- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. +-- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). +-- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). +-- +-- ## 1.4) Apply additional Filters to fine-tune the detected objects +-- +-- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. +-- That being said, the DCS World detection algorithm can sometimes be unrealistic. +-- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. +-- Additionally, trees and other obstacles are not accounted during the DCS World detection. +-- +-- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. +-- For electronic detection, this filtering is not applied, only for visually detected targets. +-- +-- The following additional filtering can be applied for visual filtering: +-- +-- * A probability factor per kilometer distance. +-- * A probability factor based on the alpha angle between the detected object and the unit detecting. +-- A detection from a higher altitude allows for better detection than when on the ground. +-- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. +-- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. +-- +-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. +-- Only when you experience unrealistic behaviour in your missions, these filters could be applied. +-- +-- ### 1.4.1 ) Distance visual detection probability +-- +-- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. +-- Also, the speed of accurate detection plays a role. +-- +-- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. +-- +-- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +-- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. +-- +-- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! +-- +-- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. +-- +-- ### 1.4.2 ) Alpha Angle visual detection probability +-- +-- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. +-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. +-- +-- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. +-- +-- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +-- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% +-- +-- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. +-- +-- ### 1.4.3 ) Cloudy Zones detection probability +-- +-- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. +-- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +-- zones that reflect cloudy areas where detected units may not be so easily visually detected. +-- +-- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. +-- +-- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take +-- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. +-- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! +-- +-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for +-- AI not to detect so easily targets within a forrest or village rich area. +-- +-- ## 1.5 ) Accept / Reject detected units +-- +-- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, +-- if it is located in range or located inside or outside of specific zones. +-- +-- ### 1.5.1 ) Detection acceptance of within range limit +-- +-- A range can be set that will limit a successful detection for a unit. +-- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will accept detected units if the range is below 5000 meters. +-- Detection:SetAcceptRange( 5000 ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- +-- ### 1.5.2 ) Detection acceptance if within zone(s). +-- +-- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). +-- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Search fo the zones where units are to be accepted. +-- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) +-- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. +-- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- ### 1.5.3 ) Detection rejectance if within zone(s). +-- +-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). +-- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. +-- An example of how to use the method is shown below. +-- +-- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. +-- +-- -- Search fo the zones where units are to be rejected. +-- local ZoneReject1 = ZONE:New( "RejectZone1" ) +-- local ZoneReject2 = ZONE:New( "RejectZone2" ) +-- +-- -- Build a detect object. +-- local Detection = DETECTION_BASE:New( SetGroup ) +-- +-- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. +-- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) +-- +-- -- Start the Detection. +-- Detection:Start() +-- +-- ## 1.6) DETECTION_BASE is a Finite State Machine +-- +-- Various Events and State Transitions can be tailored using DETECTION_BASE. +-- +-- ### 1.6.1) DETECTION_BASE States +-- +-- * **Detecting**: The detection is running. +-- * **Stopped**: The detection is stopped. +-- +-- ### 1.6.2) DETECTION_BASE Events +-- +-- * **Start**: Start the detection process. +-- * **Detect**: Detect new units. +-- * **Detected**: New units have been detected. +-- * **Stop**: Stop the detection process. -- -- === -- --- 2) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} --- =============================================================================== +-- # 2) @{Detection#DETECTION_UNITS} class, extends @{Detection#DETECTION_BASE} +-- +-- The @{Detection#DETECTION_UNITS} class will detect units within the battle zone. +-- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. +-- Beware that when the amount of units detected is large, the DetectedItems list will be large also. +-- +-- # 3) @{Detection#DETECTION_TYPES} class, extends @{Detection#DETECTION_BASE} +-- +-- The @{Detection#DETECTION_TYPES} class will detect units within the battle zone. +-- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. +-- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. +-- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. +-- +-- # 4) @{Detection#DETECTION_AREAS} class, extends @{Detection#DETECTION_BASE} +-- -- The @{Detection#DETECTION_AREAS} class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. -- The class is group the detected units within zones given a DetectedZoneRange parameter. -- A set with multiple detected zones will be created as there are groups of units detected. -- --- 2.1) Retrieve the Detected Unit sets and Detected Zones --- ------------------------------------------------------- --- The DetectedUnitSets methods are implemented in @{Detection#DECTECTION_BASE} and the DetectedZones methods is implemented in @{Detection#DETECTION_AREAS}. +-- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones -- --- Retrieve the DetectedUnitSets with the method @{Detection#DETECTION_BASE.GetDetectedSets}(). A table will be return of @{Set#SET_UNIT}s. --- To understand the amount of sets created, use the method @{Detection#DETECTION_BASE.GetDetectedSetCount}(). --- If you want to obtain a specific set from the DetectedSets, use the method @{Detection#DETECTION_BASE.GetDetectedSet}() with a given index. +-- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and +-- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. +-- +-- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. -- -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. -- --- 1.4) Flare or Smoke detected units --- ---------------------------------- +-- ## 4.4) Flare or Smoke detected units +-- -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. -- --- 1.5) Flare or Smoke detected zones --- ---------------------------------- --- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedZones}() or @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to flare or smoke the detected zones when a new detection has taken place. +-- ## 4.5) Flare or Smoke or Bound detected zones +-- +-- Use the methods: +-- +-- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color +-- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color +-- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag +-- +-- the detected zones when a new detection has taken place. -- -- === -- -- ### Contributions: -- --- * Mechanist : Concept & Testing +-- * Mechanist : Early concept of DETECTION_AREAS. -- -- ### Authors: -- --- * FlightControl : Design & Programming +-- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Detection +do -- DETECTION_BASE ---- DETECTION_BASE class --- @type DETECTION_BASE --- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. --- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. --- @field #number DetectionRun --- @extends Core.Base#BASE -DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, -} - ---- @type DETECTION_BASE.DetectedObjects --- @list <#DETECTION_BASE.DetectedObject> - ---- @type DETECTION_BASE.DetectedObject --- @field #string Name --- @field #boolean Visible --- @field #string Type --- @field #number Distance --- @field #boolean Identified - ---- DETECTION constructor. --- @param #DETECTION_BASE self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @return #DETECTION_BASE self -function DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + --- DETECTION_BASE class + -- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + DETECTION_BASE = { + ClassName = "DETECTION_BASE", + DetectionSetGroup = nil, + DetectionRange = nil, + DetectedObjects = {}, + DetectionRun = 0, + DetectedObjectsIdentified = {}, + DetectedItems = {}, + } - self.DetectionSetGroup = DetectionSetGroup - self.DetectionRange = DetectionRange + --- @type DETECTION_BASE.DetectedObjects + -- @list <#DETECTION_BASE.DetectedObject> - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + --- @type DETECTION_BASE.DetectedObject + -- @field #string Name + -- @field #boolean Visible + -- @field #string Type + -- @field #number Distance + -- @field #boolean Identified - return self -end - ---- Detect Visual. --- @param #DETECTION_BASE self --- @param #boolean DetectVisual --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual -end - ---- Detect Optical. --- @param #DETECTION_BASE self --- @param #boolean DetectOptical --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical -end - ---- Detect Radar. --- @param #DETECTION_BASE self --- @param #boolean DetectRadar --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar -end - ---- Detect IRST. --- @param #DETECTION_BASE self --- @param #boolean DetectIRST --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST -end - ---- Detect RWR. --- @param #DETECTION_BASE self --- @param #boolean DetectRWR --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR -end - ---- Detect DLINK. --- @param #DETECTION_BASE self --- @param #boolean DetectDLINK --- @return #DETECTION_BASE self -function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK -end - ---- Determines if a detected object has already been identified during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject --- @return #boolean true if already identified. -function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified -end - ---- Identifies a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true -end - ---- UnIdentify a detected object during detection processing. --- @param #DETECTION_BASE self --- @param #DETECTION_BASE.DetectedObject DetectedObject -function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false -end - ---- UnIdentify all detected objects during detection processing. --- @param #DETECTION_BASE self -function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. -end - ---- Gets a detected object with a given name. --- @param #DETECTION_BASE self --- @param #string ObjectName --- @return #DETECTION_BASE.DetectedObject -function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F3( ObjectName ) + --- @type DETECTION_BASE.DetectedItems + -- @list <#DETECTION_BASE.DetectedItem> - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] + --- @type DETECTION_BASE.DetectedItem + -- @field Core.Set#SET_UNIT Set + -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. + -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. + -- @field #boolean Changed Documents if the detected area has changes. + -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). + -- @field #number ItemID -- The identifier of the detected area. + -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. + -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + --- DETECTION constructor. + -- @param #DETECTION_BASE self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return #DETECTION_BASE self + function DETECTION_BASE:New( DetectionSetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE + + self.DetectedItemCount = 0 + self.DetectedItemMax = 0 + self.DetectedItems = {} + + self.DetectionSetGroup = DetectionSetGroup + + self.DetectionInterval = 30 + + self:InitDetectVisual( true ) + self:InitDetectOptical( false ) + self:InitDetectRadar( false ) + self:InitDetectRWR( false ) + self:InitDetectIRST( false ) + self:InitDetectDLINK( false ) + + -- Create FSM transitions. + + self:SetStartState( "Stopped" ) + self.CountryID = DetectionSetGroup:GetFirst():GetCountry() + + self:AddTransition( "Stopped", "Start", "Detecting") + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE 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 Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnBeforeStart + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnAfterStart + -- @param #DETECTION_BASE 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=#DETECTION_BASE] Start + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Start. + -- @function [parent=#DETECTION_BASE] __Start + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Detecting. + -- @function [parent=#DETECTION_BASE] OnLeaveDetecting + -- @param #DETECTION_BASE 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 Detecting. + -- @function [parent=#DETECTION_BASE] OnEnterDetecting + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + self:AddTransition( "Detecting", "Detect", "Detecting" ) + self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + + --- OnBefore Transition Handler for Event Detect. + -- @function [parent=#DETECTION_BASE] OnBeforeDetect + -- @param #DETECTION_BASE 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 Detect. + -- @function [parent=#DETECTION_BASE] OnAfterDetect + -- @param #DETECTION_BASE 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 Detect. + -- @function [parent=#DETECTION_BASE] Detect + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detect. + -- @function [parent=#DETECTION_BASE] __Detect + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Detecting", "Detected", "Detecting" ) + + --- OnBefore Transition Handler for Event Detected. + -- @function [parent=#DETECTION_BASE] OnBeforeDetected + -- @param #DETECTION_BASE 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 Detected. + -- @function [parent=#DETECTION_BASE] OnAfterDetected + -- @param #DETECTION_BASE 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 Detected. + -- @function [parent=#DETECTION_BASE] Detected + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Detected. + -- @function [parent=#DETECTION_BASE] __Detected + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "*", "Stop", "Stopped" ) + + --- OnBefore Transition Handler for Event Stop. + -- @function [parent=#DETECTION_BASE] OnBeforeStop + -- @param #DETECTION_BASE 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=#DETECTION_BASE] OnAfterStop + -- @param #DETECTION_BASE 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=#DETECTION_BASE] Stop + -- @param #DETECTION_BASE self + + --- Asynchronous Event Trigger for Event Stop. + -- @function [parent=#DETECTION_BASE] __Stop + -- @param #DETECTION_BASE self + -- @param #number Delay The delay in seconds. + + --- OnLeave Transition Handler for State Stopped. + -- @function [parent=#DETECTION_BASE] OnLeaveStopped + -- @param #DETECTION_BASE 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 Stopped. + -- @function [parent=#DETECTION_BASE] OnEnterStopped + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + return self + end + + do -- State Transition Handling + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterStart(From,Event,To) + self:__Detect(0.1) + end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + function DETECTION_BASE:onafterDetect(From,Event,To) + self:E( {From,Event,To}) + + local DetectDelay = 0.1 + self.DetectionCount = 0 + self.DetectionRun = 0 + self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table + + self.DetectionSetGroup:Flush() + + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + self:E( {DetectionGroupData}) + self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + self.DetectionCount = self.DetectionCount + 1 + DetectDelay = DetectDelay + 0.1 end end + + --- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) + self:E( {From,Event,To}) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if DetectionGroup:IsAlive() then + + self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = DetectionGroup:GetName() + + local DetectedUnits = {} + + local DetectedTargets = DetectionGroup:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T( DetectedTargets ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + self:T2( DetectedObject ) + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) + + -- Calculate Acceptance + + if self.AcceptRange and Distance > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + DetectionAccepted = false + end + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:E({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = {} + end + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Visible = Detection.visible + self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].Distance = Distance + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + end + + self:T2( self.DetectedObjects ) + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + self:__Detect( self.DetectionInterval ) + + self:T( "--> Create Detection Sets" ) + self:CreateDetectionSets() + end + + end + + + end + + do -- Initialization methods + + --- Detect Visual. + -- @param #DETECTION_BASE self + -- @param #boolean DetectVisual + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectVisual( DetectVisual ) + + self.DetectVisual = DetectVisual + end + + --- Detect Optical. + -- @param #DETECTION_BASE self + -- @param #boolean DetectOptical + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectOptical( DetectOptical ) + self:F2() + + self.DetectOptical = DetectOptical + end + + --- Detect Radar. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRadar + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRadar( DetectRadar ) + self:F2() + + self.DetectRadar = DetectRadar + end + + --- Detect IRST. + -- @param #DETECTION_BASE self + -- @param #boolean DetectIRST + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectIRST( DetectIRST ) + self:F2() + + self.DetectIRST = DetectIRST + end + + --- Detect RWR. + -- @param #DETECTION_BASE self + -- @param #boolean DetectRWR + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectRWR( DetectRWR ) + self:F2() + + self.DetectRWR = DetectRWR + end + + --- Detect DLINK. + -- @param #DETECTION_BASE self + -- @param #boolean DetectDLINK + -- @return #DETECTION_BASE self + function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) + self:F2() + + self.DetectDLINK = DetectDLINK + end + + end + + do + + --- Set the detection interval time in seconds. + -- @param #DETECTION_BASE self + -- @param #number DetectionInterval Interval in seconds. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + self:F2() + + self.DetectionInterval = DetectionInterval + + return self + end + + end + + do -- Accept / Reject detected units + + --- Accept detections if within a range in meters. + -- @param #DETECTION_BASE self + -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptRange( AcceptRange ) + self:F2() + + self.AcceptRange = AcceptRange + + return self + end + + --- Accept detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAcceptZones( AcceptZones ) + self:F2() + + if type( AcceptZones ) == "table" then + self.AcceptZones = AcceptZones + else + self.AcceptZones = { AcceptZones } + end + + return self + end + + --- Reject detections if within the specified zone(s). + -- @param #DETECTION_BASE self + -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRejectZones( RejectZones ) + self:F2() + + if type( RejectZones ) == "table" then + self.RejectZones = RejectZones + else + self.RejectZones = { RejectZones } + end + + return self + end + + end + + do -- Probability methods + + --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. + -- Also, the speed of accurate detection plays a role. + -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. + -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. + -- @param #DETECTION_BASE self + -- @param DistanceProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) + self:F2() + + self.DistanceProbability = DistanceProbability + + return self + end + + + --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. + -- + -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. + -- + -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% + -- @param #DETECTION_BASE self + -- @param AlphaAngleProbability The probability factor. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) + self:F2() + + self.AlphaAngleProbability = AlphaAngleProbability + + return self + end + + --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + -- zones that reflect cloudy areas where detected units may not be so easily visually detected. + -- @param #DETECTION_BASE self + -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetZoneProbability( ZoneArray ) + self:F2() + + self.ZoneProbability = ZoneArray + + return self + end + + + end + + do -- Change processing + + --- Accepts changes from the detected item. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #DETECTION_BASE self + function DETECTION_BASE:AcceptChanges( DetectedItem ) + + DetectedItem.Changed = false + DetectedItem.Changes = {} + + return self + end + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) + + return self + end + + + --- Add a change to the detected zone. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @param #string ChangeCode + -- @param #string ChangeUnitType + -- @return #DETECTION_BASE self + function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) + + DetectedItem.Changed = true + local ItemID = DetectedItem.ItemID + + DetectedItem.Changes = DetectedItem.Changes or {} + DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 + DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 + DetectedItem.Changes[ChangeCode].ItemID = ItemID + + self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + + return self + end + + + end + + do -- Threat + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Background worker function to determine if there are friendlies nearby ... + -- @param #DETECTION_BASE self + function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) + self:F2() + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() + + DetectedItem.FriendliesNearBy = false + + if DetectedUnit then + + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = DetectedUnit:GetVec3(), + radius = 6000, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = true + return false + end + + return true + end + + world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + end + end + + end + + --- Determines if a detected object has already been identified during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + -- @return #boolean true if already identified. + function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) + self:F3( DetectedObject.Name ) + + local DetectedObjectName = DetectedObject.Name + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + end + + --- Identifies a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) + self:F( { "Identified:", DetectedObject.Name } ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = true + end + + --- UnIdentify a detected object during detection processing. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedObject DetectedObject + function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) + + local DetectedObjectName = DetectedObject.Name + self.DetectedObjectsIdentified[DetectedObjectName] = false + end + + --- UnIdentify all detected objects during detection processing. + -- @param #DETECTION_BASE self + function DETECTION_BASE:UnIdentifyAllDetectedObjects() + + self.DetectedObjectsIdentified = {} -- Table will be garbage collected. + end + + --- Gets a detected object with a given name. + -- @param #DETECTION_BASE self + -- @param #string ObjectName + -- @return #DETECTION_BASE.DetectedObject + function DETECTION_BASE:GetDetectedObject( ObjectName ) + self:F( ObjectName ) + + if ObjectName then + local DetectedObject = self.DetectedObjects[ObjectName] + + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end + end + end + + return nil + end + + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + + local DetectedItem = {} + self.DetectedItemCount = self.DetectedItemCount + 1 + self.DetectedItemMax = self.DetectedItemMax + 1 + + if DetectedItemIndex then + self.DetectedItems[DetectedItemIndex] = DetectedItem + else + self.DetectedItems[self.DetectedItemCount] = DetectedItem + end + + DetectedItem.Set = Set or SET_UNIT:New() + DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Removed = false + + return DetectedItem + end + + --- Adds a new DetectedItem to the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #string DetectedItemIndex The index of the DetectedItem. + -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. + -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. + -- @return #DETECTION_BASE.DetectedItem + function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) + + local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + + DetectedItem.Zone = Zone + + return DetectedItem + end + + --- Removes an existing DetectedItem from the DetectedItems list. + -- The DetectedItem is a table and contains a SET_UNIT in the field Set. + -- @param #DETECTION_BASE self + -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. + function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) + + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end + + + --- Get the detected @{Set#SET_BASE}s. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItems + function DETECTION_BASE:GetDetectedItems() + + return self.DetectedItems + end + + --- Get the amount of SETs with detected objects. + -- @param #DETECTION_BASE self + -- @return #number Count + function DETECTION_BASE:GetDetectedItemsCount() + + local DetectedCount = self.DetectedItemCount + return DetectedCount + end + + --- Get a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return DETECTION_BASE.DetectedItem + function DETECTION_BASE:GetDetectedItem( Index ) + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem + end + + return nil + end + + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Set#SET_UNIT DetectedSet + function DETECTION_BASE:GetDetectedSet( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSetUnit = DetectedItem.Set + if DetectedSetUnit then + return DetectedSetUnit + end + + return nil + end + + do -- Zones + + --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Zone#ZONE_UNIT DetectedZone + function DETECTION_BASE:GetDetectedZone( Index ) + + local DetectedZone = self.DetectedItems[Index].Zone + if DetectedZone then + return DetectedZone + end + + return nil + end + + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemReportSummary( Index ) + self:F( Index ) + return nil + end + + --- Report detailed of a detectedion result. + -- @param #DETECTION_BASE self + -- @return #string + function DETECTION_BASE:DetectedReportDetailed() + self:F() + return nil + end + + --- Get the detection Groups. + -- @param #DETECTION_BASE self + -- @return Wrapper.Group#GROUP + function DETECTION_BASE:GetDetectionSetGroup() + + local DetectionSetGroup = self.DetectionSetGroup + return DetectionSetGroup + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE self + function DETECTION_BASE:CreateDetectionSets() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + + end + + + --- Schedule the DETECTION construction. + -- @param #DETECTION_BASE self + -- @param #number DelayTime The delay in seconds to wait the reporting. + -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. + -- @return #DETECTION_BASE self + function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) + self:F2() + + self.ScheduleDelayTime = DelayTime + self.ScheduleRepeatInterval = RepeatInterval + + self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) + return self + end + +end + +do -- DETECTION_UNITS + + --- DETECTION_UNITS class + -- @type DETECTION_UNITS + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @extends #DETECTION_BASE + DETECTION_UNITS = { + ClassName = "DETECTION_UNITS", + DetectionRange = nil, + } + + --- DETECTION_UNITS constructor. + -- @param Functional.Detection#DETECTION_UNITS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @return Functional.Detection#DETECTION_UNITS self + function DETECTION_UNITS:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_UNITS self + -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_UNITS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_UNITS self + -- @return #DETECTION_UNITS self + function DETECTION_UNITS:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + self:E( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedUnitName ) + if not DetectedItem then + self:T( "Added new DetectedItem" ) + DetectedItem = self:AddDetectedItem( DetectedUnitName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObjectData.Name + DetectedItem.Visible = DetectedObjectData.Visible + DetectedItem.Distance = DetectedObjectData.Distance + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @return #string + function DETECTION_UNITS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedSet = self:GetDetectedSet( Index ) + + self:T( DetectedSet ) + if DetectedSet then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + + if DetectedItemUnit then + self:T(DetectedItemUnit) + + local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" + local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" + + if DetectedItem.Type and UnitCategoryName and UnitCategoryType then + UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + else + UnitCategoryText = "Unknown target at " + end + + if DetectedItem.Visible == false then + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" + else + UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" + end + + ReportSummary = string.format( + "%s%s", + UnitCategoryText, + UnitDistanceText + ) + end + + self:T( ReportSummary ) + + return ReportSummary + end end - return nil + --- Report detailed of a detection result. + -- @param #DETECTION_UNITS self + -- @return #string + function DETECTION_UNITS:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected units:" ) + for DetectedItemID, DetectedItem in ipairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + end ---- Get the detected @{Set#SET_BASE}s. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE.DetectedSets DetectedSets -function DETECTION_BASE:GetDetectedSets() +do -- DETECTION_TYPES - local DetectionSets = self.DetectedSets - return DetectionSets -end - ---- Get the amount of SETs with detected objects. --- @param #DETECTION_BASE self --- @return #number Count -function DETECTION_BASE:GetDetectedSetCount() - - local DetectionSetCount = #self.DetectedSets - return DetectionSetCount -end - ---- Get a SET of detected objects using a given numeric index. --- @param #DETECTION_BASE self --- @param #number Index --- @return Core.Set#SET_BASE -function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectionSet = self.DetectedSets[Index] - if DetectionSet then - return DetectionSet + --- DETECTION_TYPES class + -- @type DETECTION_TYPES + -- @extends #DETECTION_BASE + DETECTION_TYPES = { + ClassName = "DETECTION_TYPES", + DetectionRange = nil, + } + + --- DETECTION_TYPES constructor. + -- @param Functional.Detection#DETECTION_TYPES self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. + -- @return Functional.Detection#DETECTION_TYPES self + function DETECTION_TYPES:New( DetectionSetGroup ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self end - return nil -end - ---- Get the detection Groups. --- @param #DETECTION_BASE self --- @return Wrapper.Group#GROUP -function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup -end - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_BASE self --- @return #DETECTION_BASE self -function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - -end - - ---- Schedule the DETECTION construction. --- @param #DETECTION_BASE self --- @param #number DelayTime The delay in seconds to wait the reporting. --- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. --- @return #DETECTION_BASE self -function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_TYPES self + -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_TYPES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self -end - - ---- Form @{Set}s of detected @{Unit#UNIT}s in an array of @{Set#SET_BASE}s. --- @param #DETECTION_BASE self -function DETECTION_BASE:_DetectionScheduler( SchedulerName ) - self:F2( { SchedulerName } ) + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." + end - self.DetectionRun = self.DetectionRun + 1 - - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - local DetectionGroup = DetectionGroupData -- Wrapper.Group#GROUP - - if DetectionGroup:IsAlive() then - - local DetectionGroupName = DetectionGroup:GetName() + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." + end - local DetectionDetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK + end + + return table.concat( MT, "\n" ) + + end + + + --- Create the DetectedItems list from the DetectedObjects table. + -- For each DetectedItem, a one field array is created containing the Unit detected. + -- @param #DETECTION_TYPES self + -- @return #DETECTION_TYPES self + function DETECTION_TYPES:CreateDetectionSets() + self:F2( #self.DetectedObjects ) + + -- Loop the current detected items, and check if each object still exists and is detected. + + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + + local DetectedItemSet = DetectedItem:GetSet() -- Core.Set#SET_UNIT + local DetectedTypeName = DetectedItem.Type + + for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Yes, the DetectedUnit is still detected or exists. Flag as identified. + self:IdentifyDetectedObject( DetectedObject ) + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) + DetectedItemSet:Remove( DetectedUnitName ) + end + end + end + + + -- Now we need to loop through the unidentified detected units and add these... These are all new items. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + if DetectedObject then + self:T( { "Detected Unit #", DetectedUnitName } ) + + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + if DetectedUnit then + local DetectedTypeName = DetectedUnit:GetTypeName() + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + if not DetectedItem then + DetectedItem = self:AddDetectedItem( DetectedTypeName ) + DetectedItem.Type = DetectedUnit:GetTypeName() + end + + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) + end + end + end + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:NearestFAC( DetectedItem ) + end + + end + + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @return #string + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() + + local ReportSummary = string.format( + "Type #%s - Threat Level [%s] (%2d)", + DetectedItem.Type, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G + ) + self:T( ReportSummary ) + + return ReportSummary + end + end + + --- Report detailed of a detection result. + -- @param #DETECTION_TYPES self + -- @return #string + function DETECTION_TYPES:DetectedReportDetailed() + self:F() + + local Report = REPORT:New( "Detected types:" ) + for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + +end + + +do -- DETECTION_AREAS + + --- DETECTION_AREAS class + -- @type DETECTION_AREAS + -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends #DETECTION_BASE + DETECTION_AREAS = { + ClassName = "DETECTION_AREAS", + DetectionZoneRange = nil, + } + + + --- DETECTION_AREAS constructor. + -- @param #DETECTION_AREAS self + -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @return #DETECTION_AREAS + function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) + + self.DetectionZoneRange = DetectionZoneRange + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_AREAS self + -- @param Index + -- @return #string + function DETECTION_AREAS:DetectedItemReportSummary( Index ) + self:F( Index ) + + local DetectedItem = self:GetDetectedItem( Index ) + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedZone( Index ) + local DetectedItemPointVec3 = DetectedZone:GetPointVec3() + local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) + local ReportSummary = string.format( + "%s - Threat Level [%s] (%2d)", + DetectedItemPointLL, + string.rep( "■", ThreatLevelA2G ), + ThreatLevelA2G ) - for DetectionDetectedTargetID, DetectionDetectedTarget in pairs( DetectionDetectedTargets ) do - local DetectionObject = DetectionDetectedTarget.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectionObject ) + return ReportSummary + end + + return nil + end + + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_AREAS self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) + + self:T3( DetectedItem.FriendliesNearBy ) + return DetectedItem.FriendliesNearBy or false + end + + --- Calculate the maxium A2G threat level of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) + + local MaxThreatLevelA2G = 0 + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local ThreatUnit = UnitData -- Wrapper.Unit#UNIT + local ThreatLevelA2G = ThreatUnit:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + end + end + + self:T3( MaxThreatLevelA2G ) + DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + + end + + --- Find the nearest FAC of the DetectedItem. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return Wrapper.Unit#UNIT The nearest FAC unit + function DETECTION_AREAS:NearestFAC( DetectedItem ) + + local NearestFAC = nil + local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + + for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do + local FACUnit = FACUnitData -- Wrapper.Unit#UNIT + if FACUnit:IsActive() then + local Vec3 = FACUnit:GetVec3() + local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) + local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) + if Distance < MinDistance then + MinDistance = Distance + NearestFAC = FACUnit + end + end + end + end + + DetectedItem.NearestFAC = NearestFAC + + end + + --- Returns the A2G threat level of the units in the DetectedItem + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #number a scale from 0 to 10. + function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) + + self:T3( DetectedItem.MaxThreatLevelA2G ) + return DetectedItem.MaxThreatLevelA2G + end + + + + --- Smoke the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_AREAS self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_AREAS:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ItemID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_AREAS self + -- @return #DETECTION_AREAS self + function DETECTION_AREAS:CreateDetectionSets() + self:F2() + + + self:T( "Checking Detected Items for new Detected Units ..." ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + + self:T( { "Detected Item ID:", DetectedItemID } ) - if DetectionObject and DetectionObject:isExist() and DetectionObject.id_ < 50000000 then - - local DetectionDetectedObjectName = DetectionObject:getName() - - local DetectionDetectedObjectPositionVec3 = DetectionObject:getPoint() - local DetectionGroupVec3 = DetectionGroup:GetVec3() - - local Distance = ( ( DetectionDetectedObjectPositionVec3.x - DetectionGroupVec3.x )^2 + - ( DetectionDetectedObjectPositionVec3.y - DetectionGroupVec3.y )^2 + - ( DetectionDetectedObjectPositionVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T2( { DetectionGroupName, DetectionDetectedObjectName, Distance } ) - - if Distance <= self.DetectionRange then - - if not self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = {} - end - self.DetectedObjects[DetectionDetectedObjectName].Name = DetectionDetectedObjectName - self.DetectedObjects[DetectionDetectedObjectName].Visible = DetectionDetectedTarget.visible - self.DetectedObjects[DetectionDetectedObjectName].Type = DetectionDetectedTarget.type - self.DetectedObjects[DetectionDetectedObjectName].Distance = DetectionDetectedTarget.distance - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectionDetectedObjectName] then - self.DetectedObjects[DetectionDetectedObjectName] = nil - end - end - end - end - self:T2( self.DetectedObjects ) - - -- okay, now we have a list of detected object names ... - -- Sort the table based on distance ... - table.sort( self.DetectedObjects, function( a, b ) return a.Distance < b.Distance end ) - end - end - - if self.DetectedObjects then - self:CreateDetectionSets() - end - - return true -end - - - ---- DETECTION_AREAS class --- @type DETECTION_AREAS --- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @field #DETECTION_AREAS.DetectedAreas DetectedAreas A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. --- @extends Functional.Detection#DETECTION_BASE -DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectedAreas = { n = 0 }, - DetectionZoneRange = nil, -} - ---- @type DETECTION_AREAS.DetectedAreas --- @list <#DETECTION_AREAS.DetectedArea> - ---- @type DETECTION_AREAS.DetectedArea --- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @field #boolean Changed Documents if the detected area has changes. --- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). --- @field #number AreaID -- The identifier of the detected area. --- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. --- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - ---- DETECTION_AREAS constructor. --- @param Functional.Detection#DETECTION_AREAS self --- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. --- @param Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. --- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. --- @return Functional.Detection#DETECTION_AREAS self -function DETECTION_AREAS:New( DetectionSetGroup, DetectionRange, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup, DetectionRange ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - - self:Schedule( 10, 10 ) - - return self -end - ---- Add a detected @{#DETECTION_AREAS.DetectedArea}. --- @param Core.Set#SET_UNIT Set -- The Set of Units in the detected area. --- @param Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. --- @return #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:AddDetectedArea( Set, Zone ) - local DetectedAreas = self:GetDetectedAreas() - DetectedAreas.n = self:GetDetectedAreaCount() + 1 - DetectedAreas[DetectedAreas.n] = {} - local DetectedArea = DetectedAreas[DetectedAreas.n] - DetectedArea.Set = Set - DetectedArea.Zone = Zone - DetectedArea.Removed = false - DetectedArea.AreaID = DetectedAreas.n - - return DetectedArea -end - ---- Remove a detected @{#DETECTION_AREAS.DetectedArea} with a given Index. --- @param #DETECTION_AREAS self --- @param #number Index The Index of the detection are to be removed. --- @return #nil -function DETECTION_AREAS:RemoveDetectedArea( Index ) - local DetectedAreas = self:GetDetectedAreas() - local DetectedAreaCount = self:GetDetectedAreaCount() - local DetectedArea = DetectedAreas[Index] - local DetectedAreaSet = DetectedArea.Set - DetectedArea[Index] = nil - return nil -end - - ---- Get the detected @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS.DetectedAreas DetectedAreas -function DETECTION_AREAS:GetDetectedAreas() - - local DetectedAreas = self.DetectedAreas - return DetectedAreas -end - ---- Get the amount of @{#DETECTION_AREAS.DetectedAreas}. --- @param #DETECTION_AREAS self --- @return #number DetectedAreaCount -function DETECTION_AREAS:GetDetectedAreaCount() - - local DetectedAreaCount = self.DetectedAreas.n - return DetectedAreaCount -end - ---- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Set#SET_UNIT DetectedSet -function DETECTION_AREAS:GetDetectedSet( Index ) - - local DetectedSetUnit = self.DetectedAreas[Index].Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil -end - ---- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. --- @param #DETECTION_AREAS self --- @param #number Index --- @return Core.Zone#ZONE_UNIT DetectedZone -function DETECTION_AREAS:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedAreas[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil -end - ---- Background worker function to determine if there are friendlies nearby ... --- @param #DETECTION_AREAS self --- @param Wrapper.Unit#UNIT ReportUnit -function DETECTION_AREAS:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT - - DetectedArea.FriendliesNearBy = false - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedZoneUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedArea = ReportGroupData.DetectedArea -- Functional.Detection#DETECTION_AREAS.DetectedArea - local DetectedSet = ReportGroupData.DetectedArea.Set - local DetectedZone = ReportGroupData.DetectedArea.Zone - local DetectedZoneUnit = DetectedZone.ZoneUNIT -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedZoneUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedZoneUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedArea.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - -end - - - ---- Returns if there are friendlies nearby the FAC units ... --- @param #DETECTION_AREAS self --- @return #boolean trhe if there are friendlies nearby -function DETECTION_AREAS:IsFriendliesNearBy( DetectedArea ) - - self:T3( DetectedArea.FriendliesNearBy ) - return DetectedArea.FriendliesNearBy or false -end - ---- Calculate the maxium A2G threat level of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea -function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedArea ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedArea.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedArea.MaxThreatLevelA2G = MaxThreatLevelA2G - -end - ---- Find the nearest FAC of the DetectedArea. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return Wrapper.Unit#UNIT The nearest FAC unit -function DETECTION_AREAS:NearestFAC( DetectedArea ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedArea.NearestFAC = NearestFAC - -end - ---- Returns the A2G threat level of the units in the DetectedArea --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #number a scale from 0 to 10. -function DETECTION_AREAS:GetTreatLevelA2G( DetectedArea ) - - self:T3( DetectedArea.MaxThreatLevelA2G ) - return DetectedArea.MaxThreatLevelA2G -end - - - ---- Smoke the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self -end - ---- Flare the detected units --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self -end - ---- Smoke the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self -end - ---- Flare the detected zones --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self -end - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeArea( DetectedArea, ChangeCode, AreaUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode].AreaID = AreaID - DetectedArea.Changes[ChangeCode].AreaUnitType = AreaUnitType - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, AreaUnitType } ) - - return self -end - - ---- Add a change to the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @param #string ChangeCode --- @param #string ChangeUnitType --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AddChangeUnit( DetectedArea, ChangeCode, ChangeUnitType ) - - DetectedArea.Changed = true - local AreaID = DetectedArea.AreaID - - DetectedArea.Changes = DetectedArea.Changes or {} - DetectedArea.Changes[ChangeCode] = DetectedArea.Changes[ChangeCode] or {} - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedArea.Changes[ChangeCode][ChangeUnitType] = DetectedArea.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedArea.Changes[ChangeCode].AreaID = AreaID - - self:T( { "Change on Detection Area:", DetectedArea.AreaID, ChangeCode, ChangeUnitType } ) - - return self -end - ---- Make text documenting the changes of the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #string The Changes text -function DETECTION_AREAS:GetChangeText( DetectedArea ) - self:F( DetectedArea ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedArea.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.AreaID .. ". The center target is a " .. ChangeData.AreaUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.AreaID .. ". The new center target is a " .. ChangeData.AreaUnitType "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.AreaID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.AreaID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "AreaID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.AreaID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - -end - - ---- Accepts changes from the detected zone. --- @param #DETECTION_AREAS self --- @param #DETECTION_AREAS.DetectedArea DetectedArea --- @return #DETECTION_AREAS self -function DETECTION_AREAS:AcceptChanges( DetectedArea ) - - DetectedArea.Changed = false - DetectedArea.Changes = {} - - return self -end - - ---- Make a DetectionSet table. This function will be overridden in the derived clsses. --- @param #DETECTION_AREAS self --- @return #DETECTION_AREAS self -function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - - local DetectedSet = DetectedArea.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( DetectedArea.Zone.ZoneUNIT.UnitName ) - local DetectedZoneObject = self:GetDetectedObject( DetectedArea.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detecting Zone Object", DetectedArea.AreaID, DetectedArea.Zone, DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedArea.Zone.ZoneUNIT.UnitName ) - - self:AddChangeArea( DetectedArea, 'RAU', "Dummy" ) + local DetectedSet = DetectedItem.Set - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - -- Assign the Unit as the new center unit of the detected area. - DetectedArea.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeArea( DetectedArea, "AAU", DetectedArea.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedArea.Zone - if DetectedUnit:IsInZone( DetectedArea.Zone ) then + local AreaExists = false -- This flag will determine of the detected area is still existing. - -- Yes, the DetectedUnit is within the DetectedArea.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedArea.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedArea, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedArea, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - self:RemoveDetectedArea( DetectedAreaID ) - self:AddChangeArea( DetectedArea, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do + -- First test if the center unit is detected in the detection area. + self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) + local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) + self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - if DetectedArea then - self:T( "Detection Area #" .. DetectedArea.AreaID ) - local DetectedSet = DetectedArea.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedArea.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedArea, "AU", DetectedUnit:GetTypeName() ) + if DetectedZoneObject then + + --self:IdentifyDetectedObject( DetectedZoneObject ) + AreaExists = true + + + + else + -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. + -- First remove the center unit from the set. + DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) + + self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + + -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + + -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. + -- If the DetectedUnit was already identified, DetectedObject will be nil. + if DetectedObject then + self:IdentifyDetectedObject( DetectedObject ) + AreaExists = true + + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + + -- Assign the Unit as the new center unit of the detected area. + DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) + + self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + + -- We don't need to add the DetectedObject to the area set, because it is already there ... + break + end end end + + -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. + -- Note that the position of the area may have moved due to the center unit repositioning. + -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. + if AreaExists then + + -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... + -- Those units within the zone are flagged as Identified. + -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. + for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do + + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedObject = nil + if DetectedUnit:IsAlive() then + --self:E(DetectedUnit:GetName()) + DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) + end + if DetectedObject then + + -- Check if the DetectedUnit is within the DetectedItem.Zone + if DetectedUnit:IsInZone( DetectedItem.Zone ) then + + -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. + self:IdentifyDetectedObject( DetectedObject ) + + else + -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + end + + else + -- There was no DetectedObject, remove DetectedUnit from the Set. + self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) + DetectedSet:Remove( DetectedUnitName ) + + -- The DetectedObject has been identified, because it does not exist ... + -- self:IdentifyDetectedObject( DetectedObject ) + end + end + else + DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + self:RemoveDetectedItem( DetectedItemID ) + self:AddChangeItem( DetectedItem, "RA" ) + end end + end - if AddedToDetectionArea == false then + -- We iterated through the existing detection areas and: + -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. + -- - We recentered the detection area to new center units where it was needed. + -- + -- Now we need to loop through the unidentified detected units and see where they belong: + -- - They can be added to a new detection area and become the new center unit. + -- - They can be added to a new detection area. + for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - -- New detection area - local DetectedArea = self:AddDetectedArea( - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedArea.Zone.ZoneUNIT.UnitName ) - DetectedArea.Set:AddUnit( DetectedUnit ) - self:AddChangeArea( DetectedArea, "AA", DetectedUnit:GetTypeName() ) - end - end - end + local DetectedObject = self:GetDetectedObject( DetectedUnitName ) + + if DetectedObject then - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedAreaID, DetectedAreaData in ipairs( self.DetectedAreas ) do - - local DetectedArea = DetectedAreaData -- #DETECTION_AREAS.DetectedArea - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone - - self:ReportFriendliesNearBy( { DetectedArea = DetectedArea, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedArea ) -- Calculate A2G threat level - self:NearestFAC( DetectedArea ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedArea.AreaID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() + -- We found an unidentified unit outside of any existing detection area. + local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + + local AddedToDetectionArea = false + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then + self:T( "Detection Area #" .. DetectedItem.ItemID ) + local DetectedSet = DetectedItem.Set + if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then + self:IdentifyDetectedObject( DetectedObject ) + DetectedSet:AddUnit( DetectedUnit ) + AddedToDetectionArea = true + self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + end end end + + if AddedToDetectionArea == false then + + -- New detection area + local DetectedItem = self:AddDetectedItemZone( nil, + SET_UNIT:New(), + ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) + ) + --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) + DetectedItem.Set:AddUnit( DetectedUnit ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + end end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:NearestFAC( DetectedItem ) + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + DetectedZone:BoundZone( 12, self.CountryID ) + end end + end - -end - - + +end --- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions -- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, -- even when there are hardly any players in the mission.** @@ -27815,11 +28964,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event 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. @@ -27830,11 +28974,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param #string From The From State string. -- @param #string Event The Event 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. -- @function [parent=#AI_CAS_ZONE] Engage @@ -28062,7 +29201,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) if Detected == true then self:E( {"Target: ", DetectedUnit } ) 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 ) end end @@ -28093,8 +29232,8 @@ end -- @param #string Event The Event 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 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 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, @@ -28134,28 +29273,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageRoute[#EngageRoute+1] = CurrentRoutePoint - if self.Controllable:IsNotInZone( self.EngageZone ) then - - -- Find a random 2D point in EngageZone. - local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToEngageZoneVec2 ) - - -- Obtain a 3D @{Point} from the 2D point + altitude. - local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) - - -- Create a route point of type air. - local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint - - end - +-- if self.Controllable:IsNotInZone( self.EngageZone ) then +-- +-- -- Find a random 2D point in EngageZone. +-- local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2() +-- self:T2( ToEngageZoneVec2 ) +-- +-- -- Obtain a 3D @{Point} from the 2D point + altitude. +-- local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y ) +-- +-- -- Create a route point of type air. +-- local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir( +-- self.PatrolAltType, +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- self.EngageSpeed, +-- true +-- ) +-- +-- EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint +-- +-- end +-- --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Find a random 2D point in EngageZone. @@ -28210,9 +29349,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, --- NOW ROUTE THE GROUP! self.Controllable:WayPointExecute( 1 ) - self:SetDetectionInterval( 10 ) + self:SetDetectionInterval( 2 ) self:SetDetectionActivated() - self:__Target( -10 ) -- Start Targetting + self:__Target( -2 ) -- Start Targetting end end @@ -30014,7 +31153,7 @@ do -- ACT_ASSIGN_ACCEPT self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign() + self.Task:Assign( ProcessUnit, self.Task ) end end -- ACT_ASSIGN_ACCEPT @@ -30213,7 +31352,7 @@ do -- ACT_ROUTE -- @type ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -30309,6 +31448,115 @@ do -- 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 @@ -30316,7 +31564,7 @@ do -- ACT_ROUTE_ZONE -- @type ACT_ROUTE_ZONE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone + -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE ACT_ROUTE_ZONE = { ClassName = "ACT_ROUTE_ZONE", @@ -30325,11 +31573,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. -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ROUTE_ZONE:New( TargetZone ) + -- @param Core.Zone#ZONE_BASE Zone + function ACT_ROUTE_ZONE:New( Zone ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - self.TargetZone = TargetZone + self.Zone = Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30341,7 +31589,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:Init( FsmRoute ) - self.TargetZone = FsmRoute.TargetZone + self.Zone = FsmRoute.Zone self.DisplayInterval = 30 self.DisplayCount = 30 @@ -30349,18 +31597,32 @@ do -- ACT_ROUTE_ZONE self.DisplayTime = 10 -- 10 seconds is the default 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. -- @param #ACT_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean 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." self:Message( RouteText ) end - return ProcessUnit:IsInZone( self.TargetZone ) + return ProcessUnit:IsInZone( self.Zone ) end --- Task Events @@ -30373,11 +31635,11 @@ do -- ACT_ROUTE_ZONE -- @param #string 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 TaskUnitVec2 = ProcessUnit:GetVec2() 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 ) end @@ -30576,15 +31838,6 @@ do -- ACT_ACCOUNT_DEADS self.TaskName = FsmAccount.TaskName end - - - function ACT_ACCOUNT_DEADS:_Destructor() - self:E("_Destructor") - - self:EventRemoveAll() - - end - --- Process Events --- StateMachine callback function @@ -30615,7 +31868,6 @@ do -- ACT_ACCOUNT_DEADS if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then 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." ) end end @@ -30628,7 +31880,7 @@ do -- ACT_ACCOUNT_DEADS -- @param #string To 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 ) else self:__NoMore( 1 ) @@ -30643,7 +31895,7 @@ do -- ACT_ACCOUNT_DEADS self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then - self:__Event( 1, EventData ) + self:Event( EventData ) end end @@ -30758,7 +32010,7 @@ do -- ACT_ASSIST function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup ) + local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) local function MenuSmoke( MenuParam ) self:E( MenuParam ) @@ -30775,6 +32027,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.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) 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 @@ -30875,7 +32138,9 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) self.Report = {} - self.Report[#self.Report+1] = Title + if Title then + self.Report[#self.Report+1] = Title + end return self end @@ -30886,7 +32151,7 @@ end -- @return #REPORT function REPORT:Add( Text ) self.Report[#self.Report+1] = Text - return self.Report[#self.Report+1] + return self.Report[#self.Report] end function REPORT:Text() @@ -30923,22 +32188,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:HandleEvent( EVENTS.Birth, --- @param #COMMANDCENTER self - --- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function( self, EventData ) - self:E( { EventData } ) - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - 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 MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + if EventData.IniObjectCategory == 1 then + local EventGroup = GROUP:Find( EventData.IniDCSGroup ) + if EventGroup and self:HasGroup( EventGroup ) then + 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 MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + self:ReportSummary( EventGroup ) + end + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + Mission:ReportDetails() + end end end @@ -31048,17 +32314,26 @@ function COMMANDCENTER:SetMenu() 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 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 - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu() - 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. -- @param #COMMANDCENTER self @@ -31079,6 +32354,14 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has 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. -- @param #COMMANDCENTER self -- @param #string Message @@ -31086,7 +32369,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. 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 self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) @@ -31102,6 +32386,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) end + --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self @@ -31149,22 +32434,6 @@ MISSION = { ClassName = "MISSION", Name = "", 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. @@ -31182,10 +32451,184 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self:SetStartState( "Idle" ) 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" ) + + --- 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" ) + + --- 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" ) + --- 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.CommandCenter = CommandCenter @@ -31197,14 +32640,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi self.MissionCoalition = MissionCoalition self.Tasks = {} + + -- Private implementations + + return self end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onbeforeComplete( From, Event, To ) @@ -31217,10 +32664,10 @@ function MISSION:onbeforeComplete( From, Event, To ) return true -- Allow Mission completion. end ---- FSM function for a MISSION +-- FSM function for a MISSION -- @param #MISSION self --- @param #string Event -- @param #string From +-- @param #string Event -- @param #string To function MISSION:onenterCompleted( From, Event, To ) @@ -31338,23 +32785,25 @@ end --- Sets the Planned Task menu. -- @param #MISSION self -function MISSION:SetMenu() +-- @param #number MenuTime +function MISSION:SetMenu( MenuTime ) self:F() - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:SetMenu() + for _, TaskData in pairs( self:GetTasks() ) do + local Task = TaskData -- Tasking.Task#TASK + Task:SetMenu( MenuTime ) end end --- Removes the Planned Task menu. -- @param #MISSION self -function MISSION:RemoveMenu() +-- @param #number MenuTime +function MISSION:RemoveMenu( MenuTime ) self:F() for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu() + Task:RemoveMenu( MenuTime ) end end @@ -31366,20 +32815,6 @@ function MISSION:GetCommandCenter() return self.CommandCenter 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. -- @param #MISSION self @@ -31395,28 +32830,18 @@ end -- @param #MISSION self -- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMissionMenu( TaskGroup ) +function MISSION:GetMenu( TaskGroup ) local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter.CommandCenterMenu + local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - - local TaskGroupName = TaskGroup:GetName() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ) + local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) return MissionMenu 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. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task @@ -31487,76 +32912,44 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end - - ---- old stuff - ---- Returns if a Mission has completed. --- @return bool +--- Is the @{Mission} **Completed**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsCompleted() - self:F() - return self.MissionStatus == "ACCOMPLISHED" + return self:Is( "Completed" ) end ---- Set a Mission to completed. -function MISSION:Completed() - self:F() - self.MissionStatus = "ACCOMPLISHED" - self:StatusToClients() +--- Is the @{Mission} **Idle**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsIdle() + return self:Is( "Idle" ) end ---- Returns if a Mission is ongoing. --- treturn bool +--- Is the @{Mission} **Ongoing**. +-- @param #MISSION self +-- @return #boolean function MISSION:IsOngoing() - self:F() - return self.MissionStatus == "ONGOING" + return self:Is( "Ongoing" ) end ---- Set a Mission to ongoing. -function MISSION:Ongoing() - self:F() - self.MissionStatus = "ONGOING" - --self:StatusToClients() +--- Is the @{Mission} **Failed**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsFailed() + return self:Is( "Failed" ) end ---- Returns if a Mission is pending. --- treturn bool -function MISSION:IsPending() - self:F() - return self.MissionStatus == "PENDING" -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 +--- Is the @{Mission} **Hold**. +-- @param #MISSION self +-- @return #boolean +function MISSION:IsHold() + return self:Is( "Hold" ) end +--- Validates if the Mission has a Group +-- @param #MISSION +-- @return #boolean true if the Mission has a Group. function MISSION:HasGroup( TaskGroup ) local Has = false @@ -31649,107 +33042,6 @@ function MISSION:ReportDetails() return Report:Text() 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. -- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. -- @usage @@ -31763,333 +33055,6 @@ function MISSION:GetTasks() 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 - --- This module contains the TASK class. -- -- 1) @{#TASK} class, extends @{Base#BASE} @@ -32277,7 +33242,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - Mission:AddTask( self ) return self end @@ -32285,9 +33249,13 @@ end --- Get the Task FSM Process Template -- @param #TASK self -- @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 --- Sets the Task FSM Process Template @@ -32320,7 +33288,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 self:IsStatePlanned() or self:IsStateReplanned() then 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 if self:IsStateAssigned() then local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) @@ -32362,10 +33330,11 @@ function TASK:AbortUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) if #PlayerGroup:GetUnits() == 1 then + self:UnAssignFromGroup( PlayerGroup ) PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) self:RemoveMenuForGroup( PlayerGroup ) end - self:PlayerAborted( PlayerUnit ) + self:Abort() end end end @@ -32431,7 +33400,7 @@ end ---- Assign the @{Task}to a @{Group}. +--- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -32442,7 +33411,11 @@ function TASK:AssignToGroup( TaskGroup ) 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 ) local TaskUnits = TaskGroup:GetUnits() @@ -32482,6 +33455,7 @@ function TASK:AssignToUnit( TaskUnit ) self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) + FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self @@ -32492,7 +33466,7 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit ) + self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) @@ -32539,28 +33513,37 @@ function TASK:SendBriefingToAssignedGroups() end ---- Assign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() 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() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) end end end + + --- Returns if the @{Task} is assigned to the Group. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -32571,10 +33554,12 @@ function TASK:IsAssignedToGroup( TaskGroup ) if self:IsStateAssigned() then if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) return true end end + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) return false end @@ -32603,37 +33588,36 @@ end --- Set the menu options of the @{Task} to all the groups in the SetGroup. -- @param #TASK self -function TASK:SetMenu() +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenu( MenuTime ) self:F() self.SetGroup:Flush() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup ) + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then + if self:IsStatePlanned() or self:IsStateReplanned() then + self:SetMenuForGroup( TaskGroup, MenuTime ) + 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 -- @param #TASK self -function TASK:SetMenuForGroup( TaskGroup ) +-- @param #number MenuTime +-- @return #TASK +function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName() ) + if not TaskGroup:GetState( TaskGroup, "Assigned" ) then + self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) else - self:SetAssignedMenuForGroup( TaskGroup ) + if not self:IsAssignedToGroup( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) + end end end @@ -32642,16 +33626,24 @@ end -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #string MenuText The menu text. +-- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) self:E( TaskGroup:GetName() ) 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 TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ) + 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 } ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -32659,32 +33651,84 @@ end --- Set the assigned menu options of the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup ) +function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() - local MissionMenu = Mission:GetMissionMenu( TaskGroup ) + local MissionMenu = Mission:GetMenu( TaskGroup ) self:E( { MissionMenu = MissionMenu } ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, { self = self, TaskGroup = TaskGroup } ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, { 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, TaskGroup ):SetTime( MenuTime ) return self 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}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup +-- @param #number MenuTime -- @return #TASK self -function TASK:RemoveMenuForGroup( TaskGroup ) +function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) + self:F() local Mission = self:GetMission() 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 ) - MissionMenu:Remove() +--- Remove the assigned menu option of the @{Task} for a @{Group}. +-- @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 function TASK.MenuAssignToGroup( MenuParam ) @@ -32697,19 +33741,21 @@ function TASK.MenuAssignToGroup( MenuParam ) self:AssignToGroup( TaskGroup ) end -function TASK.MenuTaskStatus( MenuParam ) +--- Report the task status. +-- @param #TASK self +function TASK:MenuTaskStatus( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup + local ReportText = self:ReportDetails() - --self:AssignToGroup( TaskGroup ) + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) + 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() end @@ -32754,15 +33800,26 @@ end --- Add a FiniteStateMachine to @{Task} with key Task@{Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit +-- @param Core.Fsm#FSM_PROCESS Fsm -- @return #TASK self 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 return Fsm 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} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -32770,9 +33827,15 @@ end function TASK:RemoveStateMachine( TaskUnit ) 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 + collectgarbage() - self:T( "Garbage Collected, Processes should be finalized now ...") + self:E( "Garbage Collected, Processes should be finalized now ...") end --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} @@ -32887,6 +33950,32 @@ function TASK:IsStatePlanned() return self:Is( "Planned" ) 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**. -- @param #TASK self function TASK:StateAssigned() @@ -32967,7 +34056,7 @@ function TASK:onenterSuccess( From, Event, To ) 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:GetMission():__Complete( 1 ) @@ -33101,24 +34190,19 @@ function TASK:ReportDetails() -- Determine the status of the Task. local State = self:GetState() - -- Loop each Unit active in the Task, and find Player Names. local PlayerNames = {} - for PlayerGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local Player = PlayerGroup -- Wrapper.Group#GROUP - for PlayerUnitID, PlayerUnit in pairs( PlayerGroup:GetUnits() ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - if PlayerUnit and PlayerUnit:IsAlive() then - local PlayerName = PlayerUnit:GetPlayerName() - PlayerNames[#PlayerNames+1] = PlayerName - end + local PlayerReport = REPORT:New( " - Players:" ) + for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP + PlayerNames = PlayerGroup:GetPlayerNames() + if PlayerNames then + PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) end - local PlayerNameText = table.concat( PlayerNames, ", " ) - Report:Add( "Task " .. Name .. " - State '" .. State .. "' - Players " .. PlayerNameText ) end -- 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() end @@ -33160,23 +34244,6 @@ end -- Reporting -- ------------------------------- -- 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. -- -- === -- @@ -33214,6 +34281,8 @@ do -- DETECTION MANAGER self:SetReportInterval( 30 ) self:SetReportDisplayTime( 25 ) + Detection:__Start( 5 ) + return self end @@ -33376,35 +34445,78 @@ do -- DETECTION_REPORTING end -do -- DETECTION_DISPATCHER +--- **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 - --- DETECTION_DISPATCHER class. - -- @type DETECTION_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 - -- @field Wrapper.Group#GROUP CommandCenter -- @extends Tasking.DetectionManager#DETECTION_MANAGER - DETECTION_DISPATCHER = { - ClassName = "DETECTION_DISPATCHER", + TASK_A2G_DISPATCHER = { + ClassName = "TASK_A2G_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 ) + --- 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 ) ) -- #DETECTION_DISPATCHER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER self.Detection = Detection - self.CommandCenter = CommandCenter self.Mission = Mission self:Schedule( 30 ) @@ -33413,15 +34525,15 @@ do -- DETECTION_DISPATCHER --- Creates a SEAD task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @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 DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + 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() @@ -33441,19 +34553,19 @@ do -- DETECTION_DISPATCHER end --- Creates a CAS task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + 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( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == true then @@ -33469,19 +34581,19 @@ do -- DETECTION_DISPATCHER end --- Creates a BAI task when there are targets for it. - -- @param #DETECTION_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea + -- @param #TASK_A2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition ) - self:F( { DetectedArea.AreaID } ) + function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) + self:F( { DetectedItem.ItemID } ) - local DetectedSet = DetectedArea.Set - local DetectedZone = DetectedArea.Zone + 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( DetectedArea ) + local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) if GroundUnitCount > 0 and FriendliesNearBy == false then @@ -33497,16 +34609,16 @@ do -- DETECTION_DISPATCHER 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 + -- 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.DetectedArea DetectedArea + -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @return Tasking.Task#TASK - function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea ) + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) if Task then - if Task:IsStatePlanned() and DetectedArea.Changed == true then + if Task:IsStatePlanned() and DetectedItem.Changed == true then self:E( "Removing Tasking: " .. Task:GetTaskName() ) Task = Mission:RemoveTask( Task ) end @@ -33517,10 +34629,10 @@ do -- DETECTION_DISPATCHER --- 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. + -- @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 DETECTION_DISPATCHER:ProcessDetected( Detection ) + function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) self:F2() local AreaMsg = {} @@ -33528,129 +34640,377 @@ do -- DETECTION_DISPATCHER 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 DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) 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 ) } ) + 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 AreaID = DetectedArea.AreaID + local ItemID = DetectedItem.ItemID -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( "SEAD." .. AreaID ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea ) + local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) + SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- 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 ) ) + 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 - self:E( "Planned" ) - --SEADTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText() + ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( "CAS." .. AreaID ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea ) + local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) + CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- 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 ) ) + 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 - --CASTask:SetPlannedMenu() - TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText() + ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( "BAI." .. AreaID ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea ) + local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) + BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():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 ) ) + 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 - --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 + 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( DetectedArea ) + Detection:AcceptChanges( DetectedItem ) 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 + 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--- This module contains the TASK_SEAD classes. +end--- This module contains the TASK_A2G 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, +-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_SEAD 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 --- * **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. +-- * **Planned**: The A2G task is planned. +-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2G task is successfully completed. +-- * **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 --- --- @module Task_SEAD +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **[WingThor]**: Concept, Advice & Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2G +do -- TASK_A2G + + --- The TASK_A2G class + -- @type TASK_A2G + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_A2G = { + ClassName = "TASK_A2G", + } + + --- Instantiates a new TASK_A2G. + -- @param #TASK_A2G 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_A2G self + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G + self:F() + + self.TargetSetUnit = TargetSetUnit + self.TaskType = TaskType + + Mission:AddTask( self ) + + local Fsm = self:GetUnitProcess() + + + 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 + + --- 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 + + return self + + end + + --- @param #TASK_A2G self + function TASK_A2G:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + 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 + + + --- @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 @@ -33669,130 +35029,76 @@ do -- TASK_SEAD -- @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 + -- @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, TargetZone ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD + 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() - - 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 ---- (AI) (SP) (MP) Tasking for Air to Ground Processes. --- --- 1) @{#TASK_A2G} class, extends @{Task#TASK} --- ================================================= --- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units, --- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G 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_A2G + end +end -do -- TASK_A2G +do -- TASK_BAI - --- The TASK_A2G class - -- @type TASK_A2G + --- The TASK_BAI class + -- @type TASK_BAI + -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_A2G = { - ClassName = "TASK_A2G", + TASK_BAI = { + ClassName = "TASK_BAI", } - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G self + --- 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 #string TaskType BAI or CAS -- @param Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) + -- @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() - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - self.FACUnit = FACUnit - local A2GUnitProcess = self:GetUnitProcess() - - A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } ) - A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } ) - A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" ) - A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" ) - A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } ) - A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) ) - --Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) ) - A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" ) - A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" ) - - function A2GUnitProcess: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 + 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", + } - --- @param #TASK_A2G self - function TASK_A2G:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - end - - + --- 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 --- The main include file for the MOOSE system. -- Test of permissions @@ -33854,7 +35160,7 @@ Include.File( "Tasking/CommandCenter" ) Include.File( "Tasking/Mission" ) Include.File( "Tasking/Task" ) Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_SEAD" ) +Include.File( "Tasking/Task_A2G_Dispatcher") Include.File( "Tasking/Task_A2G" ) diff --git a/Moose Mission Setup/Moose_Create.bat b/Moose Mission Setup/Moose_Create.bat index 4e3a251d4..1c2850150 100644 --- a/Moose Mission Setup/Moose_Create.bat +++ b/Moose Mission Setup/Moose_Create.bat @@ -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\Task.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\Moose.lua Moose.lua diff --git a/Moose Presentations/AI_PATROL.pptx b/Moose Presentations/AI_PATROL.pptx index bf425bd57..a7a9ff112 100644 Binary files a/Moose Presentations/AI_PATROL.pptx and b/Moose Presentations/AI_PATROL.pptx differ diff --git a/Moose Presentations/DETECTION.pptx b/Moose Presentations/DETECTION.pptx new file mode 100644 index 000000000..a55a01ca4 Binary files /dev/null and b/Moose Presentations/DETECTION.pptx differ diff --git a/Moose Presentations/SCHEDULER.pptx b/Moose Presentations/SCHEDULER.pptx new file mode 100644 index 000000000..6dab2d58c Binary files /dev/null and b/Moose Presentations/SCHEDULER.pptx differ diff --git a/Moose Presentations/TASK_DISPATCHER.pptx b/Moose Presentations/TASK_DISPATCHER.pptx new file mode 100644 index 000000000..b3ca1f566 Binary files /dev/null and b/Moose Presentations/TASK_DISPATCHER.pptx differ diff --git a/Moose Presentations/ZONE.pptx b/Moose Presentations/ZONE.pptx new file mode 100644 index 000000000..7bf099ec4 Binary files /dev/null and b/Moose Presentations/ZONE.pptx differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz index 60ea1a363..79e4ceca2 100644 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz and b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz index 6dee91ca6..7d860e3ce 100644 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz and b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz differ diff --git a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz index 43f3146ac..3f4f45bff 100644 Binary files a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz and b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz index ffbfefc2d..b68fca534 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz index d408d2460..f77ba0887 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz index d4aa83919..ae8d5cdc8 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz index f96b06726..ce5fca1b6 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz index 40cba9afb..e95443857 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz index e52064780..ddd23e5ab 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz index 780623ab9..8ea372869 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz index a6938e562..1f3e6f91f 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz index b76e7b544..f69d9f45a 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz index 7a313adc9..c23b8d426 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz index ba01dffb1..dcee754dd 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz index 2dc6e2986..9cb5a0bea 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz index f275834fd..e23c8e5b7 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz index 3d9094a8a..473daba0f 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz index 9a992e009..6f0c4904e 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz index 278dfbbb9..2ee20a52b 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz index 9582c7911..3f111689d 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz index e48dbac86..07b2793b5 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz index ba7638ba4..64c1b1c2e 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz index 01991c8b1..5d00e5e0c 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz index 1e56b34c8..79a2cd9de 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz index 57f5b0f19..bd11353cc 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz index 95d225922..032ded1be 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz and b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz index 11f36b9a3..547355160 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz index 17676e2c4..0afe84298 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz index e51b1b35a..e2227cb63 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz and b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz index bd3ea746e..3270afb48 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz index cc3066d43..6bf06aea7 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua index be56c16f9..fb0faffdc 100644 --- a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua +++ b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua @@ -19,5 +19,6 @@ 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 ) \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz index b4af1d6a3..b9d813466 100644 Binary files a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz and b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua new file mode 100644 index 000000000..96c9d8def --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua @@ -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 \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz new file mode 100644 index 000000000..67bc0fcf0 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz index 5790dec96..a21f91898 100644 Binary files a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz and b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz new file mode 100644 index 000000000..41c872ad9 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua new file mode 100644 index 000000000..8238880f8 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua @@ -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() diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz new file mode 100644 index 000000000..530bd87c9 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua new file mode 100644 index 000000000..874c7166e --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua @@ -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 + diff --git a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz new file mode 100644 index 000000000..3b2f65ba2 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua new file mode 100644 index 000000000..4196009ef --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua @@ -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 + diff --git a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz new file mode 100644 index 000000000..33d56adb1 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua new file mode 100644 index 000000000..1b7dc6c89 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua @@ -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 \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz new file mode 100644 index 000000000..e9e10ba8a Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua new file mode 100644 index 000000000..81a0b4930 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua @@ -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 + ) \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz new file mode 100644 index 000000000..1eaded535 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua new file mode 100644 index 000000000..3b4d8fff5 --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua @@ -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 \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz new file mode 100644 index 000000000..410efc780 Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua new file mode 100644 index 000000000..aef0c629e --- /dev/null +++ b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua @@ -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 ) diff --git a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz new file mode 100644 index 000000000..404d682cf Binary files /dev/null and b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz differ diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua index 58912b1bf..054b2325c 100644 --- a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua +++ b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua @@ -1,9 +1,21 @@ - - - - - - +--- +-- Name: ESC-001 - Escorting Helicopters +-- Author: FlightControl +-- Date Created: 10 Mar 2017 +-- +-- # Situation: +-- +-- Your client helicopter is flying in the battle field. +-- It is escorted by an MI-28N, which you can command... +-- Use the menu options to: +-- - Make the escort follow you. +-- - Report detected targets. +-- - Attack targets +-- - Flare +-- +-- # Test cases: +-- +-- 1. When executing the commands, observe the MI-28N reactions. do local function EventAliveHelicopter( Client ) @@ -20,6 +32,8 @@ do :MenuResumeMission() :MenuROE() :MenuAssistedAttack() + + EscortHeli1:SetDetection( EscortHeliDetection ) local EscortGroupArtillery = SpawnEscortArtillery:ReSpawn(1) local EscortArtillery = ESCORT @@ -59,6 +73,12 @@ do SpawnEscortGround = SPAWN:New( "Escort Ground" ) SpawnEscortShip = SPAWN:New( "Escort Ship" ) SpawnEscortArtillery = SPAWN:New( "Ground Attack Assistance" ) + + EscortHeliSetGroup = SET_GROUP:New():FilterPrefixes("Escort Helicopter"):FilterStart() + EscortHeliDetection = DETECTION_AREAS:New( EscortHeliSetGroup, 1000, 500 ) + + EscortHeliDetection:BoundDetectedZones() + EscortHeliDetection:SetDetectionInterval( 15 ) EscortClientHeli = CLIENT:FindByName( "Lead Helicopter", "Fly around and observe the behaviour of the escort helicopter" ):Alive( EventAliveHelicopter ) EscortClientPlane = CLIENT:FindByName( "Lead Plane", "Fly around and observe the behaviour of the escort airplane. Select Navigate->Joun-Up and airplane should follow you. Change speed and directions." ) @@ -66,9 +86,4 @@ do end --- MISSION SCHEDULER STARTUP -MISSIONSCHEDULER.Start() -MISSIONSCHEDULER.ReportMenu() -MISSIONSCHEDULER.ReportMissionsHide() - env.info( "Test Mission loaded" ) diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz index cc28f1a8d..1ccf77b6f 100644 Binary files a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz and b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz index 6d127352e..6b6bff213 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz and b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz index 2d5fc77b2..296fd2c64 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz index 730bfc5b5..1c843f4a8 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz index 1ab1cfd56..67ab35299 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz index 30d9ac5fa..d44750b0a 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz index 18f3591de..e1f94a40f 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz index 16bd5254f..f585c18ca 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz index e56eefbb2..dd79a640c 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz index 7eeeab10a..e836ea187 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz index 8df791219..6f4300bc7 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz and b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz index 636a552ae..4fc2f59ed 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz and b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz index ed45cdd89..112962d69 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz and b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz differ diff --git a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz index 88139c229..1b8a47501 100644 Binary files a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz and b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz index 7e21b9059..dcac387c2 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz and b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua new file mode 100644 index 000000000..101ffafee --- /dev/null +++ b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua @@ -0,0 +1,30 @@ +--- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. + +local HeliGroup = GROUP:FindByName( "Helicopter" ) + +local AttackGroup = GROUP:FindByName( "AttackGroup" ) + +local AttackUnits = AttackGroup:GetUnits() + +local Tasks = {} + +for i = 1, #AttackUnits do + + local AttackUnit = AttackGroup:GetUnit( i ) + Tasks[#Tasks+1] = HeliGroup:TaskAttackUnit( AttackUnit ) +end + +Tasks[#Tasks+1] = HeliGroup:TaskFunction( 1, 7, "_Resume", { "''" } ) + +--- @param Wrapper.Group#GROUP HeliGroup +function _Resume( HeliGroup ) + env.info( '_Resume' ) + + HeliGroup:MessageToAll( "Resuming",10,"Info") +end + +HeliGroup:PushTask( + HeliGroup:TaskCombo( + Tasks + ), 30 +) \ No newline at end of file diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz new file mode 100644 index 000000000..f672bd945 Binary files /dev/null and b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz index 394ec7b4c..a12d5eba6 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz and b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz index c366f3726..6221c7beb 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz and b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz index 1c050743b..d26d661c5 100644 Binary files a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz and b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz index a278af36e..e8c45d491 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz and b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz index 95a075d58..d2a2e8b8f 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz and b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz index c32270f70..9916e7e6f 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz and b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz differ diff --git a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz index c924e6c33..2dc2f2d37 100644 Binary files a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz and b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz differ diff --git a/Moose Test Missions/MOOSE_Header.lua b/Moose Test Missions/MOOSE_Header.lua new file mode 100644 index 000000000..daaa33103 --- /dev/null +++ b/Moose Test Missions/MOOSE_Header.lua @@ -0,0 +1,14 @@ +--- +-- Name: +-- Author: +-- Date Created: +-- +-- # Situation: +-- +-- . +-- +-- # Test cases: +-- +-- 1. +-- 2. +-- diff --git a/Moose Test Missions/MOOSE_Template.miz b/Moose Test Missions/MOOSE_Template.miz new file mode 100644 index 000000000..208db3b31 Binary files /dev/null and b/Moose Test Missions/MOOSE_Template.miz differ diff --git a/Moose Test Missions/MOOSE_Test_Template.miz b/Moose Test Missions/MOOSE_Test_Template.miz index 5c106af38..02595a899 100644 Binary files a/Moose Test Missions/MOOSE_Test_Template.miz and b/Moose Test Missions/MOOSE_Test_Template.miz differ diff --git a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz index 12f3721ad..6fb011b82 100644 Binary files a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz and b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz index fcc030cb8..31b41e37c 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz index ef0e4f5e7..c7a5a3030 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz index 87e561815..d056ff279 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz index 45f4f52d8..5cec3b38c 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz index c76856795..e1524971c 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz and b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz index 021db24c9..971968060 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz index 57e883f55..865d518f3 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz index 180ace4f8..75121cdc7 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz and b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz index 6a4747585..9a34dbcf6 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz and b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz index 73a8cf94a..30e41f87c 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz and b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz index e59c196a9..b19e452d5 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz index 57d09b219..443d48156 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz index d602aab0a..1ef9208ea 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz differ diff --git a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz index 0d94e23e1..f121340d3 100644 Binary files a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz and b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz index f0ebe022f..66993a038 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz and b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz index 016d31e66..31ad85ba3 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz index d2d9e7c64..818e9a37e 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz and b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz index 1f2a05b52..b22d2adc3 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz and b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz index 64543f947..3331bfad7 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz and b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz index 60237ed1a..047703acf 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz and b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz index 6a6d5eb08..508b12d61 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz and b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz index 5d7bb0c01..f17a95cdd 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz index 28a43b629..17065d0ef 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz and b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz index 63e2a9968..dbe2616c7 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz and b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz index 8c8cfb1c3..dfa305866 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz and b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz b/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz index 85d9ddf3c..ec28b424d 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz and b/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz index 2f9fac4c0..9c3af3ac9 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz and b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz index dd7ef4dec..e9016d9f4 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz index c9ea6db27..767c15b1c 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz and b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz new file mode 100644 index 000000000..4af063727 Binary files /dev/null and b/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz index 88abe9a00..fba73a6ec 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz and b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz index 4b05c7255..3b2365512 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz index 93e4cbacd..b1f0c35ff 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz and b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz index 14c338f08..353fb1540 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz and b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz index f6c18bec7..28589c975 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz and b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz index 204d65f8c..5cb52322b 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz and b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz index 07d54d5d2..b10a69c7a 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz and b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz index 25b4bec77..297ea46dc 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz and b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz b/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz index 6cf5ce6df..0c32c25c9 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz and b/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua new file mode 100644 index 000000000..0d9f23224 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua @@ -0,0 +1,46 @@ +--- +-- Name: TAD-010 - A2G Task Dispatching Destroy Test +-- Author: FlightControl +-- Date Created: 17 Mar 2017 +-- +-- # Situation: +-- +-- This tests if an accepted task successful completion does finish the processes correctly. +-- +-- # Test cases: +-- +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_UNITS:New( FACSet ) + + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +local TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004 and Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz new file mode 100644 index 000000000..8e2093701 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.lua deleted file mode 100644 index 855ed008d..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.lua +++ /dev/null @@ -1,15 +0,0 @@ - -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -local Scoring = SCORING:New( "Detect Demo" ) - -local Mission = MISSION:New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ):AddScoring( Scoring ) - -local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() -local FACDetection = DETECTION_AREAS:New( FACSet, 10000, 3000 ) - -local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() -local TaskAssign = DETECTION_DISPATCHER:New( Mission, HQ, AttackGroups, FACDetection ) - diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz deleted file mode 100644 index bd664e2a6..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua new file mode 100644 index 000000000..b3a47e140 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua @@ -0,0 +1,35 @@ + --- +-- Name: TAD-100 - A2G Task Dispatching DETECTION_AREAS +-- Author: FlightControl +-- Date Created: 06 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle zone, while detecting targets. +-- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- For test, each zone will have a circle of tyres, that are visible on the map too. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_AREAS:New( FACSet, 500 ) +FACAreas:BoundDetectedZones() + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() +local TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) + diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz new file mode 100644 index 000000000..529ba4c1e Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua new file mode 100644 index 000000000..1ca290240 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua @@ -0,0 +1,35 @@ + --- +-- Name: TAD-105 - A2G Task Dispatching DETECTION_AREAS +-- Author: FlightControl +-- Date Created: 12 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle zone, while detecting targets. +-- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- For test, each zone will have a circle of tyres, that are visible on the map too. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_AREAS:New( FACSet, 500 ) +FACAreas:BoundDetectedZones() + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() +local TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) + diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz new file mode 100644 index 000000000..0114a942d Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua new file mode 100644 index 000000000..8370f9932 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua @@ -0,0 +1,35 @@ + --- +-- Name: TAD-100 - A2G Task Dispatching DETECTION_AREAS +-- Author: FlightControl +-- Date Created: 06 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle zone, while detecting targets. +-- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- For test, each zone will have a circle of tyres, that are visible on the map too. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_TYPES:New( FACSet ) + + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() +local TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) + diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz new file mode 100644 index 000000000..cdeb0f2c9 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua new file mode 100644 index 000000000..3fedee289 --- /dev/null +++ b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua @@ -0,0 +1,50 @@ +--- +-- Name: TAD-120 - A2G Task Dispatching DETECTION_UNITS +-- Author: FlightControl +-- Date Created: 13 Mar 2017 +-- +-- # Situation: +-- +-- This mission demonstrates the dynamic task dispatching for Air to Ground operations. +-- FACA's and FAC's are patrolling around the battle field, while detecting targets. +-- The detection method used is the DETECTION_UNITS method, which groups detected targets per detected unit. +-- +-- # Test cases: +-- +-- 1. Observe the FAC(A)'s detecting targets and grouping them. +-- 2. Check that the HQ provides menus to engage on a task set by the FACs. +-- +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +Scoring = SCORING:New( "Detect Demo" ) + +local Mission = MISSION + :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) + :AddScoring( Scoring ) + +local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() + +local FACAreas = DETECTION_UNITS:New( FACSet ) + + +local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() + +local TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) + +-- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. +-- This is just an example, but many more examples can follow... + +-- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. +-- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion +-- too early! + +function Mission:OnBeforeComplete( From, Event, To ) + local Group004 = GROUP:FindByName( "Target #004" ) + if Group004:IsAlive() == false then + Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) + return true + end + return false +end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz new file mode 100644 index 000000000..a48444f87 Binary files /dev/null and b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua index f9b7e8f7f..d51287027 100644 --- a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua +++ b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua @@ -88,7 +88,10 @@ SEADSet:Flush() -- Define the set of units that are the targets. -- Note that I use FilterOnce, which means that the set will be defined only once, -- and will not be continuously updated! -local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterOnce() +local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterStart() + +-- Define the RendezVous Zone where the pilot needs to RendezVous with other players before engaging. +local RendezVousZone = ZONE:New( "RendezVous Zone" ) -- Define the zone to where the pilot needs to navigate. local TargetZone = ZONE:New( "Target Zone" ) @@ -103,12 +106,29 @@ local TargetZone = ZONE:New( "Target Zone" ) -- 1. The Mission for which the Task needs to be achieved. -- 2. The set of groups of planes that pilots can join. -- 3. The name of the Task... This can be any name, and will be provided when the Pilot joins the task. --- 4. A type of the Task. When Tasks are in state Planned, then a menu can be provided that group the task based on this given type. -local SEADTask = TASK:New( - Mission, - SEADSet, - "SEAD Radars Vector 1", - "SEAD" ) -- Tasking.Task#TASK +-- 4. A set of Targets for the Task. + +-- We are going to create a couple of variations of TASK_SEAD... + +-- A TASK_SEAD that will route the player towards a Rendez-Vous point, and once arrived, route the player to the Target Zone. +local SEADTaskRendezVousPoint = TASK_SEAD:New( Mission, SEADSet, "Route to Rendez-Vous Point, then route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD +SEADTaskRendezVousPoint:SetRendezVousPointVec2( RendezVousZone:GetPointVec2(), 6000 ) -- Done to test the RendezVousPointVec2 mechanism. +SEADTaskRendezVousPoint:SetTargetZone( TargetZone ) + +-- A TASK_SEAD that will route the player towards a Rendez-Vous zone, +-- and once arrived, route the player to the Target Zone. +local SEADTaskRendezVousZone = TASK_SEAD:New( Mission, SEADSet, "Route to Rendez-Vous Zone, then route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD +SEADTaskRendezVousZone:SetRendezVousZone( RendezVousZone ) -- Done to test the Route to Zone mechanism. +SEADTaskRendezVousZone:SetTargetZone( TargetZone ) + +-- A TASK_SEAD that has no Rendez_vous, +-- and routes the player straight to the Target Zone. +local SEADTaskToTargetZone = TASK_SEAD:New( Mission, SEADSet, "Route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD +SEADTaskToTargetZone:SetTargetZone( TargetZone ) + +-- A TASK_SEAD that has no Rendez_vous, +-- and routes the player to each Target in the Set. +local SEADTaskToTargetNoZone = TASK_SEAD:New( Mission, SEADSet, "Route to Target per Target", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD -- This is now an important part of the Task process definition. -- Each TASK contains a "Process Template". @@ -120,78 +140,36 @@ local SEADTask = TASK:New( -- The reason why this is done, is that each unit as a role within the Task, and can have different status. -- Therefore, the FsmSEAD is a TEMPLATE PROCESS of the TASK, and must be designed as a UNIT with a player is executing that PROCESS. -local SEADProcess = SEADTask:GetUnitProcess() -- #SEADProcess +local SEADProcess = SEADTaskToTargetNoZone:GetUnitProcess() -- #SEADProcess --- Adding a new sub-process to the Task Template. --- At first, the task needs to be accepted by a pilot. --- We use for this the SUB-PROCESS ACT_ASSIGN_ACCEPT. --- The method on the FsmSEAD AddProcess accepts the following parameters: --- 1. State From "Planned". When the Fsm is in state "Planned", allow the event "Accept". --- 2. Event "Accept". This event can be triggered through FsmSEAD:Accept() or FsmSEAD:__Accept( 1 ). See documentation on state machines. --- 3. The PROCESS derived class. In this case, we use the ACT_ASSIGN_ACCEPT to accept the task and provide a briefing. So, when the event "Accept" is fired, this process is executed. --- 4. A table with the "return" states of the ACT_ASSIGN_ACCEPT process. This table indicates that for a certain return state, a further event needs to be called. --- 4.1 When the return state is Assigned, fire the event in the Task FsmSEAD:Route() --- 4.2 When the return state is Rejected, fire the event in the Task FsmSEAD:Eject() --- All other AddProcess calls are working in a similar manner. - -SEADProcess:AddProcess( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } ) -- FSM SUB for type SEADProcess. - -SEADProcess:AddProcess( "Assigned", "Route", ACT_ROUTE_ZONE:New( TargetZone ), { Arrived = "Update" } ) -- FSM SUB for type SEADProcess. - --- Adding a new Action... --- Actions define also the flow of the Task, but the actions will need to be programmed within your script. --- See the state machine explanation for further details. --- The AddTransition received a couple of parameters: --- 1. State From "Rejected". When the FsmSEAD is in state "Rejected", the event "Eject" can be fired. --- 2. Event "Eject". This event can be triggered synchronously through FsmSEAD:Eject() or asynchronously through FsmSEAD:__Eject(secs). --- 3. State To "Planned". After the event has been fired, the FsmSEAD will transition to Planned. - -SEADProcess:AddTransition( "Rejected", "Eject", "Planned" ) - -SEADProcess:AddTransition( "Arrived", "Update", "Updated" ) - -SEADProcess:AddProcess( "Updated", "Account", ACT_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Accounted = "Success" } ) - -SEADProcess:AddProcess( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) ) - -SEADProcess:AddTransition( "Accounted", "Success", "Success" ) - -SEADProcess:AddTransition( "*", "Fail", "Failed" ) - -SEADProcess:AddScoreProcess( "Updated", "Account", "Account", "destroyed a radar", 25 ) -SEADProcess:AddScoreProcess( "Updated", "Account", "Failed", "failed to destroy a radar", -10 ) +SEADProcess:AddScoreProcess( "Engaging", "Account", "Account", "destroyed a radar", 25 ) +SEADProcess:AddScoreProcess( "Engaging", "Account", "Failed", "failed to destroy a radar", -10 ) -- Now we will set the SCORING. Scoring is set using the TaskSEAD object. -- Scores can be set on the status of the Task, and on Process level. SEADProcess:AddScore( "Success", "Destroyed all target radars", 250 ) SEADProcess:AddScore( "Failed", "Failed to destroy all target radars", -100 ) -function SEADProcess:OnEnterUpdated( Controllable, From, Event, To ) - self:E( { self } ) - self:Account() - self:Smoke() -end - -- Here we handle the PlayerAborted event, which is fired when a Player leaves the unit while being assigned to the Task. -- Within the event handler, which is passed the PlayerUnit and PlayerName parameter, -- we check if the SEADTask has still AlivePlayers assigned to the Task. -- If not, the Task will Abort. -- And it will be Replanned within 30 seconds. -function SEADTask:OnEnterPlayerCrashed( PlayerUnit, PlayerName ) - if not SEADTask:HasAliveUnits() then - SEADTask:__Abort() +function SEADTaskToTargetNoZone:OnEnterPlayerCrashed( PlayerUnit, PlayerName ) + if not SEADTaskToTargetNoZone:HasAliveUnits() then + SEADTaskToTargetNoZone:__Abort() end end -local TaskSEAD2 = TASK:New( Mission, SEADSet, "SEAD Radars Vector 2", "SEAD" ) -- Tasking.Task#TASK -TaskSEAD2:SetUnitProcess( SEADTask:GetUnitProcess():Copy() ) -Mission:AddTask( TaskSEAD2 ) - -Mission:RemoveTask( SEADTask ) - -SEADTask = nil -SEADProcess = nil +--local TaskSEAD2 = TASK:New( Mission, SEADSet, "SEAD Radars Vector 2", "SEAD" ) -- Tasking.Task#TASK +--TaskSEAD2:SetUnitProcess( SEADTask:GetUnitProcess():Copy() ) +--Mission:AddTask( TaskSEAD2 ) +-- +--Mission:RemoveTask( SEADTask ) +-- +--SEADTask = nil +--SEADProcess = nil collectgarbage() diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz index 3912d4a9c..621a8c993 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz index c5601b2b5..c39420a22 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz index 45bf7e431..ec3f31c77 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz index a732c6b44..455731b93 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz index 29ac59298..c1efc56fa 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz and b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz index 183dd2550..fd85c3273 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz index 0aced7bfe..aadce4267 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz index 6d7a52222..315034b7d 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz index cfded0ba3..7e3943572 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz index f949bb170..7ff77e397 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz index c71270a4d..b7a9d6a24 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz index 9beaf5c5d..20fb2b36f 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz index 221bf4ee2..47c093426 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz index 5e365217c..e698e5414 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz and b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz index 53974b022..9f6065e1a 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz and b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz differ diff --git a/docs/Documentation/AI_Balancer.html b/docs/Documentation/AI_Balancer.html index 4a168b45f..c5b24c824 100644 --- a/docs/Documentation/AI_Balancer.html +++ b/docs/Documentation/AI_Balancer.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/AI_Cap.html b/docs/Documentation/AI_Cap.html index d6441cd2c..f1305bb2a 100644 --- a/docs/Documentation/AI_Cap.html +++ b/docs/Documentation/AI_Cap.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/AI_Cas.html b/docs/Documentation/AI_Cas.html index 31534e9eb..9ec5cfd1d 100644 --- a/docs/Documentation/AI_Cas.html +++ b/docs/Documentation/AI_Cas.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -335,7 +335,7 @@ It can be notified to go RTB through the RTB event.

    - AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) + AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To)

    OnAfter Transition Handler for Event Engage.

    @@ -365,7 +365,7 @@ It can be notified to go RTB through the RTB event.

    - AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) + AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To)

    OnBefore Transition Handler for Event Engage.

    @@ -455,7 +455,7 @@ It can be notified to go RTB through the RTB event.

    - AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) + AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) @@ -932,7 +932,7 @@ The To State string.

    -AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To)
    @@ -964,36 +964,6 @@ The Event string.

    #string To : The To State string.

    - -
  • - -

    #number EngageSpeed : -(optional) The speed the Group will hold when engaging to the target zone.

    - -
  • -
  • - -

    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.

    - -
  • -
  • - -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    - -
  • -
  • - -

    #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.

    - -
  • -
  • - -

    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.

    -
  • @@ -1177,7 +1147,7 @@ Return false to cancel Transition.

    -AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To)
    @@ -1209,36 +1179,6 @@ The Event string.

    #string To : The To State string.

    - -
  • - -

    #number EngageSpeed : -(optional) The speed the Group will hold when engaging to the target zone.

    - -
  • -
  • - -

    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.

    - -
  • -
  • - -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    - -
  • -
  • - -

    #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.

    - -
  • -
  • - -

    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.

    -
  • @@ -1707,7 +1647,7 @@ The To State string.

    -AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageWeaponExpend, EngageAltitude, EngageAttackQty, EngageDirection) +AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection)
    @@ -1748,14 +1688,14 @@ The To State string.

  • -

    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.

    +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

  • -

    Dcs.DCSTypes#Distance EngageAltitude : -(optional) Desired altitude to perform the unit engagement.

    +

    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.

  • diff --git a/docs/Documentation/AI_Patrol.html b/docs/Documentation/AI_Patrol.html index a07fbb643..2235f2a59 100644 --- a/docs/Documentation/AI_Patrol.html +++ b/docs/Documentation/AI_Patrol.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Account.html b/docs/Documentation/Account.html index ca5687216..92e518f35 100644 --- a/docs/Documentation/Account.html +++ b/docs/Documentation/Account.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -276,12 +276,6 @@ Each successful dead will trigger an Account state transition that can be scored ACT_ACCOUNT_DEADS.TaskName - - - - ACT_ACCOUNT_DEADS:_Destructor() - - @@ -683,19 +677,6 @@ Each successful dead will trigger an Account state transition that can be scored -
    - -
    -
    - - -ACT_ACCOUNT_DEADS:_Destructor() - -
    -
    - - -
    diff --git a/docs/Documentation/Airbase.html b/docs/Documentation/Airbase.html index b1cb49a7f..6410f78fd 100644 --- a/docs/Documentation/Airbase.html +++ b/docs/Documentation/Airbase.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/AirbasePolice.html b/docs/Documentation/AirbasePolice.html index e80898903..c123060d6 100644 --- a/docs/Documentation/AirbasePolice.html +++ b/docs/Documentation/AirbasePolice.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Assign.html b/docs/Documentation/Assign.html index fd741ec8a..5b47c54ca 100644 --- a/docs/Documentation/Assign.html +++ b/docs/Documentation/Assign.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Base.html b/docs/Documentation/Base.html index ccdb2b19e..1a38dc885 100644 --- a/docs/Documentation/Base.html +++ b/docs/Documentation/Base.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -2216,6 +2216,9 @@ A #table or any field.

    + +

    THIS IS WHY WE NEED LUA 5.2 ...

    +
    diff --git a/docs/Documentation/Cargo.html b/docs/Documentation/Cargo.html index 59202c294..057ed8705 100644 --- a/docs/Documentation/Cargo.html +++ b/docs/Documentation/Cargo.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/CleanUp.html b/docs/Documentation/CleanUp.html index 0fc27c9c6..b25737cbe 100644 --- a/docs/Documentation/CleanUp.html +++ b/docs/Documentation/CleanUp.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -101,9 +101,15 @@ - CLEANUP:New(ZoneNames, TimeInterval) + CLEANUP.CleanUpScheduler + + + + CLEANUP:New(ZoneNames, TimeInterval) + +

    Creates the main object which is handling the cleaning of the debris within the given Zone Names.

    @@ -173,7 +179,7 @@ - CLEANUP:_OnEventBirth(Event) + CLEANUP:_OnEventBirth(EventData) @@ -233,33 +239,61 @@
    - -CLEANUP:New(ZoneNames, TimeInterval) + + +CLEANUP.CleanUpScheduler
    -

    Parameters

    -
      -
    • - -

      ZoneNames :

      - -
    • -
    • - -

      TimeInterval :

      - -
    • -
    - #number + +CLEANUP:New(ZoneNames, TimeInterval) + +
    +
    + +

    Creates the main object which is handling the cleaning of the debris within the given Zone Names.

    + +

    Parameters

    +
      +
    • + +

      #table ZoneNames : +Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.

      + +
    • +
    • + +

      #number TimeInterval : +The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.

      + +
    • +
    +

    Return value

    + +

    #CLEANUP:

    + + +

    Usage:

    +
     -- Clean these Zones.
    +CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
    +or
    +CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
    +CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
    + +
    +
    +
    +
    + + CLEANUP.TimeInterval @@ -526,7 +560,7 @@ The Unit name ...

    -CLEANUP:_OnEventBirth(Event) +CLEANUP:_OnEventBirth(EventData)
    @@ -537,7 +571,7 @@ The Unit name ...

    diff --git a/docs/Documentation/Client.html b/docs/Documentation/Client.html index 799a07533..81cdc7555 100644 --- a/docs/Documentation/Client.html +++ b/docs/Documentation/Client.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/CommandCenter.html b/docs/Documentation/CommandCenter.html index 2dddeca01..fba8e0fc0 100644 --- a/docs/Documentation/CommandCenter.html +++ b/docs/Documentation/CommandCenter.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -128,6 +128,12 @@ COMMANDCENTER.CommandCenterPositionable + + + + COMMANDCENTER:GetMenu() + +

    Gets the commandcenter menu structure governed by the HQ command center.

    @@ -158,6 +164,12 @@ COMMANDCENTER:HasGroup(Wrapper, MissionGroup)

    Checks of the COMMANDCENTER has a GROUP.

    + + + + COMMANDCENTER:MessageToAll(Message) + +

    Send a CC message to the coalition of the CC.

    @@ -366,6 +378,24 @@ +
    +
    +
    +
    + + +COMMANDCENTER:GetMenu() + +
    +
    + +

    Gets the commandcenter menu structure governed by the HQ command center.

    + +

    Return value

    + +

    Core.Menu#MENU_COALITION:

    + +
    @@ -471,6 +501,27 @@ Group#GROUP

    + +COMMANDCENTER:MessageToAll(Message) + +
    +
    + +

    Send a CC message to the coalition of the CC.

    + +

    Parameter

    +
      +
    • + +

      Message :

      + +
    • +
    +
    +
    +
    +
    + COMMANDCENTER:MessageToCoalition(Message) diff --git a/docs/Documentation/Controllable.html b/docs/Documentation/Controllable.html index 93075e7d0..51a3ecde6 100644 --- a/docs/Documentation/Controllable.html +++ b/docs/Documentation/Controllable.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -301,7 +301,7 @@ This is different from the EnRoute tasks, where the targets of the task need to CONTROLLABLE:EnRouteTaskEngageUnit(EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack) -

    (AIR) Attack the Unit.

    +

    (AIR) Search and attack the Unit.

    @@ -528,7 +528,7 @@ A speed can be given in km/h.

    - CONTROLLABLE:TaskAttackUnit(AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack) + CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack, Altitude, Visible)

    (AIR) Attack the Unit.

    @@ -1094,7 +1094,7 @@ The DCS task structure.

    -

    (AIR) Attack the Unit.

    +

    (AIR) Search and attack the Unit.

    Parameters

    Return value

    diff --git a/docs/Documentation/Database.html b/docs/Documentation/Database.html index be41b649d..4edfad744 100644 --- a/docs/Documentation/Database.html +++ b/docs/Documentation/Database.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -169,6 +169,18 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE.CLIENTS + + + + DATABASE.COUNTRY_ID + + + + + + DATABASE.COUNTRY_NAME + + @@ -628,6 +640,34 @@ The following iterator methods are currently available within the DATABASE:

    +
    +
    +
    +
    + + + +DATABASE.COUNTRY_ID + +
    +
    + + + +
    +
    +
    +
    + + + +DATABASE.COUNTRY_NAME + +
    +
    + + +
    diff --git a/docs/Documentation/Detection.html b/docs/Documentation/Detection.html index 4c3e89eab..43b39e832 100644 --- a/docs/Documentation/Detection.html +++ b/docs/Documentation/Detection.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -77,14 +77,17 @@

    -

    1) Detection#DETECTION_BASE class, extends Base#BASE

    -

    The Detection#DETECTION_BASE class defines the core functions to administer detected objects. - The Detection#DETECTION_BASE class will detect objects within the battle zone for a list of Groups detecting targets following (a) detection method(s).

    +

    # 1) #DETECTION_BASE class, extends Fsm#FSM

    -

    1.1) DETECTION_BASE constructor

    -

    Construct a new DETECTION_BASE instance using the Detection#DETECTION_BASE.New() method.

    +

    The #DETECTION_BASE class defines the core functions to administer detected objects. + The #DETECTION_BASE class will detect objects within the battle zone for a list of Groups detecting targets following (a) detection method(s).

    + +

    ## 1.1) DETECTION_BASE constructor

    + +

    Construct a new DETECTION_BASE instance using the DETECTION_BASE.New() method.

    + +

    ## 1.2) DETECTION_BASE initialization

    -

    1.2) DETECTION_BASE initialization

    By default, detection will return detected objects with all the detection sensors available. However, you can ask how the objects were found with specific detection methods. If you use one of the below methods, the detection will work with the detection method specified. @@ -93,55 +96,246 @@

    Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK:

    -

    1.3) Obtain objects detected by DETECTION_BASE

    -

    DETECTION_BASE builds Sets of objects detected. These Set#SET_BASEs can be retrieved using the method Detection#DETECTION_BASE.GetDetectedSets(). - The method will return a list (table) of Set#SET_BASE objects.

    +

    ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list

    + +

    DETECTIONBASE derived classes build a list called DetectedItems[], which is essentially a first later + of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains + a SETUNIT object that contains the detected units that belong to that group.

    + +

    Derived classes will apply different methods to group the detected units. + Examples are per area, per quadrant, per distance, per type. + See further the derived DETECTION classes on which grouping methods are currently supported.

    + +

    Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class:

    + +
      +
    • The method Detection#DETECTION_BASE.GetDetectedItems() retrieves the DetectedItems[] list.
    • +
    • A DetectedItem from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedItem( DetectedItemIndex ). + Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information + about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem.
    • +
    • A DetectedSet from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedSet( DetectedItemIndex ). + This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ).
    • +
    + +

    ## 1.4) Apply additional Filters to fine-tune the detected objects

    + +

    By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. + That being said, the DCS World detection algorithm can sometimes be unrealistic. + Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. + Additionally, trees and other obstacles are not accounted during the DCS World detection.

    + +

    Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. + For electronic detection, this filtering is not applied, only for visually detected targets.

    + +

    The following additional filtering can be applied for visual filtering:

    + +
      +
    • A probability factor per kilometer distance.
    • +
    • A probability factor based on the alpha angle between the detected object and the unit detecting. + A detection from a higher altitude allows for better detection than when on the ground.
    • +
    • Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. + The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone.
    • +
    + +

    I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. + Only when you experience unrealistic behaviour in your missions, these filters could be applied.

    + +

    ### 1.4.1 ) Distance visual detection probability

    + +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly. + Also, the speed of accurate detection plays a role.

    + +

    A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance.

    + +

    For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: + 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%.

    + +

    Note that based on this probability factor, not only the detection but also the type of the unit will be applied!

    + +

    Use the method Detection#DETECTION_BASE.SetDistanceProbability() to set the probability factor upon a 10 km distance.

    + +

    ### 1.4.2 ) Alpha Angle visual detection probability

    + +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. + A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.

    + +

    A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle.

    + +

    For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: + 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100%

    + +

    Use the method Detection#DETECTION_BASE.SetAlphaAngleProbability() to set the probability factor if 0°.

    + +

    ### 1.4.3 ) Cloudy Zones detection probability

    + +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. + The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission + zones that reflect cloudy areas where detected units may not be so easily visually detected.

    + +

    Use the method Detection#DETECTION_BASE.SetZoneProbability() to set for a defined number of zones, the probability factors.

    + +

    Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take + from the DETECTIONBASE to calculate the presence of the detected unit within each zone. + Expecially for ZONEPOLYGON, try to limit the amount of nodes of the polygon!

    + +

    Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for + AI not to detect so easily targets within a forrest or village rich area.

    + +

    ## 1.5 ) Accept / Reject detected units

    + +

    DETECTION_BASE can accept or reject successful detections based on the location of the detected object, + if it is located in range or located inside or outside of specific zones.

    + +

    ### 1.5.1 ) Detection acceptance of within range limit

    + +

    A range can be set that will limit a successful detection for a unit. + Use the method Detection#DETECTION_BASE.SetAcceptRange() to apply a range in meters till where detected units will be accepted.

    + +
      local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    +  -- Build a detect object.
    +  local Detection = DETECTION_BASE:New( SetGroup )
    +
    +  -- This will accept detected units if the range is below 5000 meters.
    +  Detection:SetAcceptRange( 5000 ) 
    +
    +  -- Start the Detection.
    +  Detection:Start()
    +
    + + +

    ### 1.5.2 ) Detection acceptance if within zone(s).

    + +

    Specific ZONEBASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONEBASE object(s). + Use the method Detection#DETECTION_BASE.SetAcceptZones() will accept detected units if they are within the specified zones.

    + +
      local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    +  -- Search fo the zones where units are to be accepted.
    +  local ZoneAccept1 = ZONE:New( "AcceptZone1" )
    +  local ZoneAccept2 = ZONE:New( "AcceptZone2" )
    +
    +  -- Build a detect object.
    +  local Detection = DETECTION_BASE:New( SetGroup )
    +
    +  -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2.
    +  Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) 
    +
    +  -- Start the Detection.
    +  Detection:Start()
    +
    + +

    ### 1.5.3 ) Detection rejectance if within zone(s).

    + +

    Specific ZONEBASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONEBASE object(s). + Use the method Detection#DETECTION_BASE.SetRejectZones() will reject detected units if they are within the specified zones. + An example of how to use the method is shown below.

    + +
      local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    +  -- Search fo the zones where units are to be rejected.
    +  local ZoneReject1 = ZONE:New( "RejectZone1" )
    +  local ZoneReject2 = ZONE:New( "RejectZone2" )
    +
    +  -- Build a detect object.
    +  local Detection = DETECTION_BASE:New( SetGroup )
    +
    +  -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2.
    +  Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) 
    +
    +  -- Start the Detection.
    +  Detection:Start()
    +
    + +

    ## 1.6) DETECTION_BASE is a Finite State Machine

    + +

    Various Events and State Transitions can be tailored using DETECTION_BASE.

    + +

    ### 1.6.1) DETECTION_BASE States

    + +
      +
    • Detecting: The detection is running.
    • +
    • Stopped: The detection is stopped.
    • +
    + +

    ### 1.6.2) DETECTION_BASE Events

    + +
      +
    • Start: Start the detection process.
    • +
    • Detect: Detect new units.
    • +
    • Detected: New units have been detected.
    • +
    • Stop: Stop the detection process.
    • +

    -

    2) Detection#DETECTION_AREAS class, extends Detection#DETECTION_BASE

    +

    # 2) Detection#DETECTION_UNITS class, extends Detection#DETECTION_BASE

    + +

    The Detection#DETECTION_UNITS class will detect units within the battle zone. + It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a Set#SET_UNIT containing ONE UNIT object reference. + Beware that when the amount of units detected is large, the DetectedItems list will be large also.

    + +

    # 3) Detection#DETECTION_TYPES class, extends Detection#DETECTION_BASE

    + +

    The Detection#DETECTION_TYPES class will detect units within the battle zone. + It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. + Each DetectedItem will contain a field Set, which contains a Set#SET_UNIT containing ONE UNIT object reference. + Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also.

    + +

    # 4) Detection#DETECTION_AREAS class, extends Detection#DETECTION_BASE

    +

    The Detection#DETECTION_AREAS class will detect units within the battle zone for a list of Groups detecting targets following (a) detection method(s), and will build a list (table) of Set#SET_UNITs containing the Unit#UNITs detected. The class is group the detected units within zones given a DetectedZoneRange parameter. A set with multiple detected zones will be created as there are groups of units detected.

    -

    2.1) Retrieve the Detected Unit sets and Detected Zones

    -

    The DetectedUnitSets methods are implemented in Detection#DECTECTION_BASE and the DetectedZones methods is implemented in Detection#DETECTION_AREAS.

    +

    ## 4.1) Retrieve the Detected Unit Sets and Detected Zones

    -

    Retrieve the DetectedUnitSets with the method Detection#DETECTION_BASE.GetDetectedSets(). A table will be return of Set#SET_UNITs. - To understand the amount of sets created, use the method Detection#DETECTION_BASE.GetDetectedSetCount(). - If you want to obtain a specific set from the DetectedSets, use the method Detection#DETECTION_BASE.GetDetectedSet() with a given index.

    +

    The methods to manage the DetectedItems[].Set(s) are implemented in Detection#DECTECTION_BASE and + the methods to manage the DetectedItems[].Zone(s) is implemented in Detection#DETECTION_AREAS.

    + +

    Retrieve the DetectedItems[].Set with the method Detection#DETECTION_BASE.GetDetectedSet(). A Set#SET_UNIT object will be returned.

    Retrieve the formed Zones as a result of the grouping the detected units within the DetectionZoneRange, use the method Detection#DETECTION_BASE.GetDetectionZones(). To understand the amount of zones created, use the method Detection#DETECTION_BASE.GetDetectionZoneCount(). If you want to obtain a specific zone from the DetectedZones, use the method Detection#DETECTION_BASE.GetDetectionZone() with a given index.

    -

    1.4) Flare or Smoke detected units

    +

    ## 4.4) Flare or Smoke detected units

    +

    Use the methods Detection#DETECTION_AREAS.FlareDetectedUnits() or Detection#DETECTION_AREAS.SmokeDetectedUnits() to flare or smoke the detected units when a new detection has taken place.

    -

    1.5) Flare or Smoke detected zones

    -

    Use the methods Detection#DETECTION_AREAS.FlareDetectedZones() or Detection#DETECTION_AREAS.SmokeDetectedZones() to flare or smoke the detected zones when a new detection has taken place.

    +

    ## 4.5) Flare or Smoke or Bound detected zones

    + +

    Use the methods:

    + + + +

    the detected zones when a new detection has taken place.


    ### Contributions:

      -
    • Mechanist : Concept & Testing
    • +
    • Mechanist : Early concept of DETECTION_AREAS.

    ### Authors:

      -
    • FlightControl : Design & Programming +
    • FlightControl : Analysis, Design, Programming, Testing
    @@ -157,39 +351,33 @@ DETECTION_BASE + + + + DETECTION_TYPES + + + + + + DETECTION_UNITS + +

    Type DETECTION_AREAS

    - + - + - - - - - - - - - - - - @@ -205,7 +393,13 @@ - + + + + + @@ -229,69 +423,33 @@ - + - + - - - - - - - - - - - - - - - - - + - + - + - - - - - - - - @@ -304,6 +462,12 @@ + + + + @@ -328,58 +492,6 @@ - -
    DETECTION_AREAS:AcceptChanges(DetectedArea)DETECTION_AREAS:BoundDetectedZones() -

    Accepts changes from the detected zone.

    +

    Bound the detected zones

    DETECTION_AREAS:AddChangeArea(DetectedArea, ChangeCode, AreaUnitType)DETECTION_AREAS:CalculateThreatLevelA2G(DetectedItem) -

    Add a change to the detected zone.

    -
    DETECTION_AREAS:AddChangeUnit(DetectedArea, ChangeCode, ChangeUnitType) -

    Add a change to the detected zone.

    -
    DETECTION_AREAS.AddDetectedArea(Set, Zone, self) -

    Add a detected DETECTION_AREAS.DetectedArea.

    -
    DETECTION_AREAS:CalculateThreatLevelA2G(DetectedArea) -

    Calculate the maxium A2G threat level of the DetectedArea.

    +

    Calculate the maxium A2G threat level of the DetectedItem.

    DETECTION_AREAS.DetectedAreasDETECTION_AREAS:DetectedItemReportSummary(Index) +

    Report summary of a detected item using a given numeric index.

    +
    DETECTION_AREAS.DetectedItems

    A list of areas containing the set of Units, Zones, the center Unit within the zone, and ID of each area that was detected within a DetectionZoneRange.

    DETECTION_AREAS:GetChangeText(DetectedArea)DETECTION_AREAS:GetChangeText(DetectedItem)

    Make text documenting the changes of the detected zone.

    DETECTION_AREAS:GetDetectedAreaCount()DETECTION_AREAS:GetTreatLevelA2G(DetectedItem) -

    Get the amount of DETECTION_AREAS.DetectedAreas.

    +

    Returns the A2G threat level of the units in the DetectedItem

    DETECTION_AREAS:GetDetectedAreas() -

    Get the detected DETECTION_AREAS.DetectedAreas.

    -
    DETECTION_AREAS:GetDetectedSet(Index) -

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    -
    DETECTION_AREAS:GetDetectedZone(Index) -

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    -
    DETECTION_AREAS:GetTreatLevelA2G(DetectedArea) -

    Returns the A2G threat level of the units in the DetectedArea

    -
    DETECTION_AREAS:IsFriendliesNearBy(DetectedArea)DETECTION_AREAS:IsFriendliesNearBy(DetectedItem)

    Returns if there are friendlies nearby the FAC units ...

    DETECTION_AREAS:NearestFAC(DetectedArea)DETECTION_AREAS:NearestFAC(DetectedItem) -

    Find the nearest FAC of the DetectedArea.

    +

    Find the nearest FAC of the DetectedItem.

    DETECTION_AREAS:New(DetectionSetGroup, DetectionRange, DetectionZoneRange)DETECTION_AREAS:New(DetectionSetGroup, DetectionZoneRange)

    DETECTION_AREAS constructor.

    -
    DETECTION_AREAS:RemoveDetectedArea(Index) -

    Remove a detected DETECTION_AREAS.DetectedArea with a given Index.

    -
    DETECTION_AREAS:ReportFriendliesNearBy(ReportUnit, ReportGroupData) -

    Background worker function to determine if there are friendlies nearby ...

    DETECTION_AREAS:SmokeDetectedZones()

    Smoke the detected zones

    +
    DETECTION_AREAS._BoundDetectedZones +
    DETECTION_AREAS._SmokeDetectedZones -
    - -

    Type DETECTION_AREAS.DetectedArea

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DETECTION_AREAS.DetectedArea.AreaID -

    -- The identifier of the detected area.

    -
    DETECTION_AREAS.DetectedArea.Changed -

    Documents if the detected area has changes.

    -
    DETECTION_AREAS.DetectedArea.Changes -

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    -
    DETECTION_AREAS.DetectedArea.FriendliesNearBy -

    Indicates if there are friendlies within the detected area.

    -
    DETECTION_AREAS.DetectedArea.MaxThreatLevelA2G - -
    DETECTION_AREAS.DetectedArea.NearestFAC -

    The nearest FAC near the Area.

    -
    DETECTION_AREAS.DetectedArea.Set -

    -- The Set of Units in the detected area.

    -
    DETECTION_AREAS.DetectedArea.Zone -

    -- The Zone of the detected area.

    @@ -387,15 +499,75 @@

    Type DETECTION_BASE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -432,6 +604,36 @@ + + + + + + + + + + + + + + + + + + + + @@ -444,6 +646,24 @@ + + + + + + + + + + + + @@ -468,6 +688,30 @@ + + + + + + + + + + + + + + + + @@ -479,19 +723,13 @@ - + - - - - @@ -549,9 +787,105 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -570,6 +904,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -585,9 +973,103 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_BASE:AcceptChanges(DetectedItem) +

    Accepts changes from the detected item.

    +
    DETECTION_BASE.AcceptRange + +
    DETECTION_BASE.AcceptZones + +
    DETECTION_BASE:AddChangeItem(DetectedItem, ChangeCode, ItemUnitType) +

    Add a change to the detected zone.

    +
    DETECTION_BASE:AddChangeUnit(DetectedItem, ChangeCode, ChangeUnitType) +

    Add a change to the detected zone.

    +
    DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) +

    Adds a new DetectedItem to the DetectedItems list.

    +
    DETECTION_BASE:AddDetectedItemZone(DetectedItemIndex, Set, Zone) +

    Adds a new DetectedItem to the DetectedItems list.

    +
    DETECTION_BASE.AlphaAngleProbability + +
    DETECTION_BASE.ClassName +
    DETECTION_BASE.CountryID +
    DETECTION_BASE:CreateDetectionSets()

    Make a DetectionSet table.

    +
    DETECTION_BASE:Detect() +

    Synchronous Event Trigger for Event Detect.

    DETECTION_BASE.DetectVisual +
    DETECTION_BASE:Detected() +

    Synchronous Event Trigger for Event Detected.

    +
    DETECTION_BASE.DetectedItemCount + +
    DETECTION_BASE.DetectedItemMax + +
    DETECTION_BASE:DetectedItemReportSummary(Index) +

    Report summary of a detected item using a given numeric index.

    +
    DETECTION_BASE.DetectedItems +
    DETECTION_BASE.DetectedObjectsIdentified

    Map of the DetectedObjects identified.

    +
    DETECTION_BASE:DetectedReportDetailed() +

    Report detailed of a detectedion result.

    +
    DETECTION_BASE.DetectionCount + +
    DETECTION_BASE.DetectionInterval +
    DETECTION_BASE.DetectionSetGroup

    The Set of GROUPs in the Forward Air Controller role.

    +
    DETECTION_BASE.DistanceProbability + +
    DETECTION_BASE:GetDetectedItem(Index) +

    Get a detected item using a given numeric index.

    +
    DETECTION_BASE:GetDetectedItems() +

    Get the detected Set#SET_BASEs.

    +
    DETECTION_BASE:GetDetectedItemsCount() +

    Get the amount of SETs with detected objects.

    DETECTION_BASE:GetDetectedSet(Index) -

    Get a SET of detected objects using a given numeric index.

    +

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    DETECTION_BASE:GetDetectedSetCount()DETECTION_BASE:GetDetectedZone(Index) -

    Get the amount of SETs with detected objects.

    -
    DETECTION_BASE:GetDetectedSets() -

    Get the detected Set#SET_BASEs.

    +

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    DETECTION_BASE:New(DetectionSetGroup, DetectionRange)DETECTION_BASE:IsFriendliesNearBy(DetectedItem) +

    Returns if there are friendlies nearby the FAC units ...

    +
    DETECTION_BASE:New(DetectionSetGroup)

    DETECTION constructor.

    +
    DETECTION_BASE:OnAfterDetect(From, Event, To) +

    OnAfter Transition Handler for Event Detect.

    +
    DETECTION_BASE:OnAfterDetected(From, Event, To) +

    OnAfter Transition Handler for Event Detected.

    +
    DETECTION_BASE:OnAfterStart(From, Event, To) +

    OnAfter Transition Handler for Event Start.

    +
    DETECTION_BASE:OnAfterStop(From, Event, To) +

    OnAfter Transition Handler for Event Stop.

    +
    DETECTION_BASE:OnBeforeDetect(From, Event, To) +

    OnBefore Transition Handler for Event Detect.

    +
    DETECTION_BASE:OnBeforeDetected(From, Event, To) +

    OnBefore Transition Handler for Event Detected.

    +
    DETECTION_BASE:OnBeforeStart(From, Event, To) +

    OnBefore Transition Handler for Event Start.

    +
    DETECTION_BASE:OnBeforeStop(From, Event, To) +

    OnBefore Transition Handler for Event Stop.

    +
    DETECTION_BASE:OnEnterDetecting(From, Event, To) +

    OnEnter Transition Handler for State Detecting.

    +
    DETECTION_BASE:OnEnterStopped(From, Event, To) +

    OnEnter Transition Handler for State Stopped.

    +
    DETECTION_BASE:OnLeaveDetecting(From, Event, To) +

    OnLeave Transition Handler for State Detecting.

    +
    DETECTION_BASE:OnLeaveStopped(From, Event, To) +

    OnLeave Transition Handler for State Stopped.

    +
    DETECTION_BASE.RejectZones + +
    DETECTION_BASE:RemoveDetectedItem(DetectedItemIndex) +

    Removes an existing DetectedItem from the DetectedItems list.

    +
    DETECTION_BASE:ReportFriendliesNearBy(ReportGroupData) +

    Background worker function to determine if there are friendlies nearby ...

    DETECTION_BASE.ScheduleRepeatInterval +
    DETECTION_BASE:SetAcceptRange(AcceptRange) +

    Accept detections if within a range in meters.

    +
    DETECTION_BASE:SetAcceptZones(AcceptZones) +

    Accept detections if within the specified zone(s).

    +
    DETECTION_BASE:SetAlphaAngleProbability(AlphaAngleProbability) +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.

    +
    DETECTION_BASE:SetDetectionInterval(DetectionInterval) +

    Set the detection interval time in seconds.

    +
    DETECTION_BASE:SetDistanceProbability(DistanceProbability) +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly.

    +
    DETECTION_BASE:SetRejectZones(RejectZones) +

    Reject detections if within the specified zone(s).

    +
    DETECTION_BASE:SetZoneProbability(ZoneArray) +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully.

    +
    DETECTION_BASE:Start() +

    Synchronous Event Trigger for Event Start.

    +
    DETECTION_BASE:Stop() +

    Synchronous Event Trigger for Event Stop.

    DETECTION_BASE:_DetectionScheduler(SchedulerName)DETECTION_BASE.ZoneProbability -

    Form Sets of detected Unit#UNITs in an array of Set#SET_BASEs.

    + +
    DETECTION_BASE:__Detect(Delay) +

    Asynchronous Event Trigger for Event Detect.

    +
    DETECTION_BASE:__Detected(Delay) +

    Asynchronous Event Trigger for Event Detected.

    +
    DETECTION_BASE:__Start(Delay) +

    Asynchronous Event Trigger for Event Start.

    +
    DETECTION_BASE:__Stop(Delay) +

    Asynchronous Event Trigger for Event Stop.

    +
    DETECTION_BASE:onafterDetect(From, Event, To) + +
    DETECTION_BASE:onafterDetectionGroup(From, Event, To, DetectionGroup) + +
    DETECTION_BASE:onafterStart(From, Event, To) + +
    + +

    Type DETECTION_BASE.DetectedItem

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_BASE.DetectedItem.Changed +

    Documents if the detected area has changes.

    +
    DETECTION_BASE.DetectedItem.Changes +

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    +
    DETECTION_BASE.DetectedItem.FriendliesNearBy +

    Indicates if there are friendlies within the detected area.

    +
    DETECTION_BASE.DetectedItem.ItemID +

    -- The identifier of the detected area.

    +
    DETECTION_BASE.DetectedItem.MaxThreatLevelA2G + +
    DETECTION_BASE.DetectedItem.NearestFAC +

    The nearest FAC near the Area.

    +
    DETECTION_BASE.DetectedItem.Set +

    -- The Set of Units in the detected area.

    +
    DETECTION_BASE.DetectedItem.Zone +

    -- The Zone of the detected area.

    @@ -622,6 +1104,158 @@ DETECTION_BASE.DetectedObject.Visible + + + + +

    Type DETECTION_TYPES

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_TYPES.ClassName + +
    DETECTION_TYPES:CreateDetectionSets() +

    Create the DetectedItems list from the DetectedObjects table.

    +
    DETECTION_TYPES:DetectedItemReportSummary(Index, DetectedTypeName) +

    Report summary of a DetectedItem using a given numeric index.

    +
    DETECTION_TYPES:DetectedReportDetailed() +

    Report detailed of a detection result.

    +
    DETECTION_TYPES.DetectionRange + +
    DETECTION_TYPES:GetChangeText(DetectedItem) +

    Make text documenting the changes of the detected zone.

    +
    DETECTION_TYPES:New(DetectionSetGroup) +

    DETECTION_TYPES constructor.

    +
    DETECTION_TYPES._BoundDetectedZones + +
    DETECTION_TYPES._FlareDetectedUnits + +
    DETECTION_TYPES._FlareDetectedZones + +
    DETECTION_TYPES._SmokeDetectedUnits + +
    DETECTION_TYPES._SmokeDetectedZones + +
    + +

    Type DETECTION_UNITS

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DETECTION_UNITS.ClassName + +
    DETECTION_UNITS:CreateDetectionSets() +

    Create the DetectedItems list from the DetectedObjects table.

    +
    DETECTION_UNITS:DetectedItemReportSummary(Index) +

    Report summary of a DetectedItem using a given numeric index.

    +
    DETECTION_UNITS:DetectedReportDetailed() +

    Report detailed of a detection result.

    +
    DETECTION_UNITS.DetectionRange +

    The range till which targets are detected.

    +
    DETECTION_UNITS:GetChangeText(DetectedItem) +

    Make text documenting the changes of the detected zone.

    +
    DETECTION_UNITS:New(DetectionSetGroup) +

    DETECTION_UNITS constructor.

    +
    DETECTION_UNITS._BoundDetectedZones + +
    DETECTION_UNITS._FlareDetectedUnits + +
    DETECTION_UNITS._FlareDetectedZones + +
    DETECTION_UNITS._SmokeDetectedUnits + +
    DETECTION_UNITS._SmokeDetectedZones +
    @@ -653,6 +1287,34 @@ + +
    +
    +
    + + #DETECTION_TYPES + +DETECTION_TYPES + +
    +
    + + + +
    +
    +
    +
    + + #DETECTION_UNITS + +DETECTION_UNITS + +
    +
    + + +

    Type Detection

    @@ -665,22 +1327,14 @@
    - -DETECTION_AREAS:AcceptChanges(DetectedArea) + +DETECTION_AREAS:BoundDetectedZones()
    -

    Accepts changes from the detected zone.

    +

    Bound the detected zones

    -

    Parameter

    -

    Return value

    #DETECTION_AREAS: @@ -691,129 +1345,19 @@ self

    - -DETECTION_AREAS:AddChangeArea(DetectedArea, ChangeCode, AreaUnitType) - -
    -
    - -

    Add a change to the detected zone.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_AREAS: -self

    - -
    -
    -
    -
    - - -DETECTION_AREAS:AddChangeUnit(DetectedArea, ChangeCode, ChangeUnitType) - -
    -
    - -

    Add a change to the detected zone.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_AREAS: -self

    - -
    -
    -
    -
    - - -DETECTION_AREAS.AddDetectedArea(Set, Zone, self) - -
    -
    - -

    Add a detected DETECTION_AREAS.DetectedArea.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_AREAS.DetectedArea: -DetectedArea

    - -
    -
    -
    -
    - -DETECTION_AREAS:CalculateThreatLevelA2G(DetectedArea) +DETECTION_AREAS:CalculateThreatLevelA2G(DetectedItem)
    -

    Calculate the maxium A2G threat level of the DetectedArea.

    +

    Calculate the maxium A2G threat level of the DetectedItem.

    Parameter

    @@ -857,9 +1401,35 @@ self

    - #DETECTION_AREAS.DetectedAreas - -DETECTION_AREAS.DetectedAreas + +DETECTION_AREAS:DetectedItemReportSummary(Index) + +
    +
    + +

    Report summary of a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + #DETECTION_BASE.DetectedItems + +DETECTION_AREAS.DetectedItems
    @@ -922,7 +1492,7 @@ self

    -DETECTION_AREAS:GetChangeText(DetectedArea) +DETECTION_AREAS:GetChangeText(DetectedItem)
    @@ -933,7 +1503,7 @@ self

    @@ -947,107 +1517,19 @@ The Changes text

    - -DETECTION_AREAS:GetDetectedAreaCount() - -
    -
    - -

    Get the amount of DETECTION_AREAS.DetectedAreas.

    - -

    Return value

    - -

    #number: -DetectedAreaCount

    - -
    -
    -
    -
    - - -DETECTION_AREAS:GetDetectedAreas() - -
    -
    - -

    Get the detected DETECTION_AREAS.DetectedAreas.

    - -

    Return value

    - -

    #DETECTION_AREAS.DetectedAreas: -DetectedAreas

    - -
    -
    -
    -
    - - -DETECTION_AREAS:GetDetectedSet(Index) - -
    -
    - -

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    - -

    Parameter

    -
      -
    • - -

      #number Index :

      - -
    • -
    -

    Return value

    - -

    Core.Set#SET_UNIT: -DetectedSet

    - -
    -
    -
    -
    - - -DETECTION_AREAS:GetDetectedZone(Index) - -
    -
    - -

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    - -

    Parameter

    -
      -
    • - -

      #number Index :

      - -
    • -
    -

    Return value

    - -

    Core.Zone#ZONE_UNIT: -DetectedZone

    - -
    -
    -
    -
    - -DETECTION_AREAS:GetTreatLevelA2G(DetectedArea) +DETECTION_AREAS:GetTreatLevelA2G(DetectedItem)
    -

    Returns the A2G threat level of the units in the DetectedArea

    +

    Returns the A2G threat level of the units in the DetectedItem

    Parameter

    @@ -1062,7 +1544,7 @@ a scale from 0 to 10.

    -DETECTION_AREAS:IsFriendliesNearBy(DetectedArea) +DETECTION_AREAS:IsFriendliesNearBy(DetectedItem)
    @@ -1073,7 +1555,7 @@ a scale from 0 to 10.

    • -

      DetectedArea :

      +

      DetectedItem :

    @@ -1088,18 +1570,18 @@ trhe if there are friendlies nearby

    -DETECTION_AREAS:NearestFAC(DetectedArea) +DETECTION_AREAS:NearestFAC(DetectedItem)
    -

    Find the nearest FAC of the DetectedArea.

    +

    Find the nearest FAC of the DetectedItem.

    Parameter

    @@ -1114,7 +1596,7 @@ The nearest FAC unit

    -DETECTION_AREAS:New(DetectionSetGroup, DetectionRange, DetectionZoneRange) +DETECTION_AREAS:New(DetectionSetGroup, DetectionZoneRange)
    @@ -1131,12 +1613,6 @@ The Set of GROUPs in the Forward Air Controller role.

  • -

    Dcs.DCSTypes#Distance DetectionRange : -The range till which targets are accepted to be detected.

    - -
  • -
  • -

    Dcs.DCSTypes#Distance DetectionZoneRange : The range till which targets are grouped upon the first detected target.

    @@ -1144,34 +1620,7 @@ The range till which targets are grouped upon the first detected target.

    Return value

    -

    Functional.Detection#DETECTION_AREAS: -self

    - -
  • -
    -
    -
    - - -DETECTION_AREAS:RemoveDetectedArea(Index) - -
    -
    - -

    Remove a detected DETECTION_AREAS.DetectedArea with a given Index.

    - -

    Parameter

    -
      -
    • - -

      #number Index : -The Index of the detection are to be removed.

      - -
    • -
    -

    Return value

    - -

    #nil:

    +

    #DETECTION_AREAS:

    @@ -1179,32 +1628,6 @@ The Index of the detection are to be removed.

    - -DETECTION_AREAS:ReportFriendliesNearBy(ReportUnit, ReportGroupData) - -
    -
    - -

    Background worker function to determine if there are friendlies nearby ...

    - -

    Parameters

    - -
    -
    -
    -
    - DETECTION_AREAS:SmokeDetectedUnits() @@ -1236,6 +1659,20 @@ self

    #DETECTION_AREAS: self

    +
    +
    +
    +
    + + #boolean + +DETECTION_AREAS._BoundDetectedZones + +
    +
    + + +
    @@ -1295,123 +1732,6 @@ self

    -

    Type DETECTION_AREAS.DetectedArea

    -

    Field(s)

    -
    -
    - - #number - -DETECTION_AREAS.DetectedArea.AreaID - -
    -
    - -

    -- The identifier of the detected area.

    - -
    -
    -
    -
    - - #boolean - -DETECTION_AREAS.DetectedArea.Changed - -
    -
    - -

    Documents if the detected area has changes.

    - -
    -
    -
    -
    - - #table - -DETECTION_AREAS.DetectedArea.Changes - -
    -
    - -

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    - -
    -
    -
    -
    - - #boolean - -DETECTION_AREAS.DetectedArea.FriendliesNearBy - -
    -
    - -

    Indicates if there are friendlies within the detected area.

    - -
    -
    -
    -
    - - - -DETECTION_AREAS.DetectedArea.MaxThreatLevelA2G - -
    -
    - - - -
    -
    -
    -
    - - Wrapper.Unit#UNIT - -DETECTION_AREAS.DetectedArea.NearestFAC - -
    -
    - -

    The nearest FAC near the Area.

    - -
    -
    -
    -
    - - Core.Set#SET_UNIT - -DETECTION_AREAS.DetectedArea.Set - -
    -
    - -

    -- The Set of Units in the detected area.

    - -
    -
    -
    -
    - - Core.Zone#ZONE_UNIT - -DETECTION_AREAS.DetectedArea.Zone - -
    -
    - -

    -- The Zone of the detected area.

    - -
    -
    - -

    Type DETECTION_AREAS.DetectedAreas

    -

    Type DETECTION_BASE

    DETECTION_BASE class

    @@ -1420,6 +1740,224 @@ self

    + +DETECTION_BASE:AcceptChanges(DetectedItem) + +
    +
    + +

    Accepts changes from the detected item.

    + +

    Parameter

    + +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + + +DETECTION_BASE.AcceptRange + +
    +
    + + + +
    +
    +
    +
    + + + +DETECTION_BASE.AcceptZones + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:AddChangeItem(DetectedItem, ChangeCode, ItemUnitType) + +
    +
    + +

    Add a change to the detected zone.

    + +

    Parameters

    + +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:AddChangeUnit(DetectedItem, ChangeCode, ChangeUnitType) + +
    +
    + +

    Add a change to the detected zone.

    + +

    Parameters

    + +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) + +
    +
    + +

    Adds a new DetectedItem to the DetectedItems list.

    + + +

    The DetectedItem is a table and contains a SET_UNIT in the field Set.

    + +

    Parameters

    +
      +
    • + +

      #string DetectedItemIndex : +The index of the DetectedItem.

      + +
    • +
    • + +

      Core.Set#SET_UNIT Set : +(optional) The Set of Units to be added.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE.DetectedItem:

    + + +
    +
    +
    +
    + + +DETECTION_BASE:AddDetectedItemZone(DetectedItemIndex, Set, Zone) + +
    +
    + +

    Adds a new DetectedItem to the DetectedItems list.

    + + +

    The DetectedItem is a table and contains a SET_UNIT in the field Set.

    + +

    Parameters

    +
      +
    • + +

      #string DetectedItemIndex : +The index of the DetectedItem.

      + +
    • +
    • + +

      Core.Set#SET_UNIT Set : +(optional) The Set of Units to be added.

      + +
    • +
    • + +

      Core.Zone#ZONE_UNIT Zone : +(optional) The Zone to be added where the Units are located.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE.DetectedItem:

    + + +
    +
    +
    +
    + + + +DETECTION_BASE.AlphaAngleProbability + +
    +
    + + + +
    +
    +
    +
    + #string DETECTION_BASE.ClassName @@ -1429,6 +1967,20 @@ self

    +
    +
    +
    +
    + + + +DETECTION_BASE.CountryID + +
    +
    + + +
    @@ -1450,6 +2002,19 @@ self

    #DETECTION_BASE: self

    + +
    +
    +
    + + +DETECTION_BASE:Detect() + +
    +
    + +

    Synchronous Event Trigger for Event Detect.

    +
    @@ -1534,6 +2099,86 @@ self

    + +
    +
    +
    + + +DETECTION_BASE:Detected() + +
    +
    + +

    Synchronous Event Trigger for Event Detected.

    + +
    +
    +
    +
    + + +DETECTION_BASE.DetectedItemCount + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedItemMax + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:DetectedItemReportSummary(Index) + +
    +
    + +

    Report summary of a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + + +DETECTION_BASE.DetectedItems + +
    +
    + + +
    @@ -1562,6 +2207,52 @@ self

    Map of the DetectedObjects identified.

    + +
    +
    +
    + + +DETECTION_BASE:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detectedion result.

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectionCount + +
    +
    + + + +
    +
    +
    +
    + + + +DETECTION_BASE.DetectionInterval + +
    +
    + + +
    @@ -1618,6 +2309,82 @@ self

    The Set of GROUPs in the Forward Air Controller role.

    + +
    +
    +
    + + + +DETECTION_BASE.DistanceProbability + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItem(Index) + +
    +
    + +

    Get a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + + +

    DETECTION_BASE.DetectedItem

    + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItems() + +
    +
    + +

    Get the detected Set#SET_BASEs.

    + +

    Return value

    + +

    #DETECTION_BASE.DetectedItems:

    + + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemsCount() + +
    +
    + +

    Get the amount of SETs with detected objects.

    + +

    Return value

    + +

    #number: +Count

    +
    @@ -1655,7 +2422,7 @@ self

    -

    Get a SET of detected objects using a given numeric index.

    +

    Get the Set#SET_UNIT of a detecttion area using a given numeric index.

    Parameter

      @@ -1667,44 +2434,34 @@ self

    Return value

    -

    Core.Set#SET_BASE:

    - +

    Core.Set#SET_UNIT: +DetectedSet

    - -DETECTION_BASE:GetDetectedSetCount() + +DETECTION_BASE:GetDetectedZone(Index)
    -

    Get the amount of SETs with detected objects.

    +

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +

    Return value

    -

    #number: -Count

    - -
    -
    -
    -
    - - -DETECTION_BASE:GetDetectedSets() - -
    -
    - -

    Get the detected Set#SET_BASEs.

    - -

    Return value

    - -

    #DETECTION_BASE.DetectedSets: -DetectedSets

    +

    Core.Zone#ZONE_UNIT: +DetectedZone

    @@ -1932,27 +2689,47 @@ true if already identified.

    + +DETECTION_BASE:IsFriendliesNearBy(DetectedItem) + +
    +
    + +

    Returns if there are friendlies nearby the FAC units ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #boolean: +trhe if there are friendlies nearby

    + +
    +
    +
    +
    + -DETECTION_BASE:New(DetectionSetGroup, DetectionRange) +DETECTION_BASE:New(DetectionSetGroup)

    DETECTION constructor.

    -

    Parameters

    +

    Parameter

    Return value

    @@ -1965,6 +2742,504 @@ self

    + +DETECTION_BASE:OnAfterDetect(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Detect.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnAfterDetected(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Detected.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnAfterStart(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Start.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnAfterStop(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeDetect(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Detect.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeDetected(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Detected.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeStart(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Start.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnBeforeStop(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnEnterDetecting(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Detecting.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnEnterStopped(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:OnLeaveDetecting(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Detecting.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +DETECTION_BASE:OnLeaveStopped(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + + +DETECTION_BASE.RejectZones + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:RemoveDetectedItem(DetectedItemIndex) + +
    +
    + +

    Removes an existing DetectedItem from the DetectedItems list.

    + + +

    The DetectedItem is a table and contains a SET_UNIT in the field Set.

    + +

    Parameter

    +
      +
    • + +

      #number DetectedItemIndex : +The index or position in the DetectedItems list where the item needs to be removed.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:ReportFriendliesNearBy(ReportGroupData) + +
    +
    + +

    Background worker function to determine if there are friendlies nearby ...

    + +

    Parameter

    +
      +
    • + +

      ReportGroupData :

      + +
    • +
    +
    +
    +
    +
    + DETECTION_BASE:Schedule(DelayTime, RepeatInterval) @@ -2021,6 +3296,239 @@ self

    +
    +
    +
    +
    + + +DETECTION_BASE:SetAcceptRange(AcceptRange) + +
    +
    + +

    Accept detections if within a range in meters.

    + +

    Parameter

    +
      +
    • + +

      #number AcceptRange : +Accept a detection if the unit is within the AcceptRange in meters.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetAcceptZones(AcceptZones) + +
    +
    + +

    Accept detections if within the specified zone(s).

    + +

    Parameter

    +
      +
    • + +

      AcceptZones : +Can be a list or ZONEBASE objects, or a single ZONEBASE object.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetAlphaAngleProbability(AlphaAngleProbability) + +
    +
    + +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.

    + + +

    A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.

    + +

    A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle.

    + +

    For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100%

    + +

    Parameter

    +
      +
    • + +

      AlphaAngleProbability : +The probability factor.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetDetectionInterval(DetectionInterval) + +
    +
    + +

    Set the detection interval time in seconds.

    + +

    Parameter

    +
      +
    • + +

      #number DetectionInterval : +Interval in seconds.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetDistanceProbability(DistanceProbability) + +
    +
    + +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly.

    + + +

    Also, the speed of accurate detection plays a role. +A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. +For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%.

    + +

    Parameter

    +
      +
    • + +

      DistanceProbability : +The probability factor.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetRejectZones(RejectZones) + +
    +
    + +

    Reject detections if within the specified zone(s).

    + +

    Parameter

    +
      +
    • + +

      RejectZones : +Can be a list or ZONEBASE objects, or a single ZONEBASE object.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:SetZoneProbability(ZoneArray) + +
    +
    + +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully.

    + + +

    The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +zones that reflect cloudy areas where detected units may not be so easily visually detected.

    + +

    Parameter

    +
      +
    • + +

      ZoneArray : +Aray of a The ZONE_BASE object and a ZoneProbability pair..

      + +
    • +
    +

    Return value

    + +

    #DETECTION_BASE: +self

    + +
    +
    +
    +
    + + +DETECTION_BASE:Start() + +
    +
    + +

    Synchronous Event Trigger for Event Start.

    + +
    +
    +
    +
    + + +DETECTION_BASE:Stop() + +
    +
    + +

    Synchronous Event Trigger for Event Stop.

    +
    @@ -2060,25 +3568,331 @@ self

    - -DETECTION_BASE:_DetectionScheduler(SchedulerName) + + +DETECTION_BASE.ZoneProbability
    -

    Form Sets of detected Unit#UNITs in an array of Set#SET_BASEs.

    + + +
    +
    +
    +
    + + +DETECTION_BASE:__Detect(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Detect.

    Parameter

    • -

      SchedulerName :

      +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:__Detected(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Detected.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:__Start(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Start.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:__Stop(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Stop.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:onafterDetect(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:onafterDetectionGroup(From, Event, To, DetectionGroup) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Wrapper.Group#GROUP DetectionGroup : +The Group detecting.

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_BASE:onafterStart(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

    +

    Type DETECTION_BASE.DetectedItem

    +

    Field(s)

    +
    +
    + + #boolean + +DETECTION_BASE.DetectedItem.Changed + +
    +
    + +

    Documents if the detected area has changes.

    + +
    +
    +
    +
    + + #table + +DETECTION_BASE.DetectedItem.Changes + +
    +
    + +

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedItem.FriendliesNearBy + +
    +
    + +

    Indicates if there are friendlies within the detected area.

    + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedItem.ItemID + +
    +
    + +

    -- The identifier of the detected area.

    + +
    +
    +
    +
    + + + +DETECTION_BASE.DetectedItem.MaxThreatLevelA2G + +
    +
    + + + +
    +
    +
    +
    + + Wrapper.Unit#UNIT + +DETECTION_BASE.DetectedItem.NearestFAC + +
    +
    + +

    The nearest FAC near the Area.

    + +
    +
    +
    +
    + + Core.Set#SET_UNIT + +DETECTION_BASE.DetectedItem.Set + +
    +
    + +

    -- The Set of Units in the detected area.

    + +
    +
    +
    +
    + + Core.Zone#ZONE_UNIT + +DETECTION_BASE.DetectedItem.Zone + +
    +
    + +

    -- The Zone of the detected area.

    + +
    +
    + +

    Type DETECTION_BASE.DetectedItems

    +

    Type DETECTION_BASE.DetectedObject

    Field(s)

    @@ -2154,7 +3968,457 @@ self

    Type DETECTION_BASE.DetectedObjects

    -

    Type DETECTION_BASE.DetectedSets

    +

    Type DETECTION_TYPES

    + +

    DETECTION_TYPES class

    + +

    Field(s)

    +
    +
    + + #string + +DETECTION_TYPES.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_TYPES:CreateDetectionSets() + +
    +
    + +

    Create the DetectedItems list from the DetectedObjects table.

    + + +

    For each DetectedItem, a one field array is created containing the Unit detected.

    + +

    Return value

    + +

    #DETECTION_TYPES: +self

    + +
    +
    +
    +
    + + +DETECTION_TYPES:DetectedItemReportSummary(Index, DetectedTypeName) + +
    +
    + +

    Report summary of a DetectedItem using a given numeric index.

    + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      DetectedTypeName :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +DETECTION_TYPES:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detection result.

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +DETECTION_TYPES.DetectionRange + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_TYPES:GetChangeText(DetectedItem) + +
    +
    + +

    Make text documenting the changes of the detected zone.

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The Changes text

    + +
    +
    +
    +
    + + +DETECTION_TYPES:New(DetectionSetGroup) + +
    +
    + +

    DETECTION_TYPES constructor.

    + +

    Parameter

    + +

    Return value

    + +

    Functional.Detection#DETECTION_TYPES: +self

    + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._BoundDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._FlareDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._FlareDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._SmokeDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_TYPES._SmokeDetectedZones + +
    +
    + + + +
    +
    + +

    Type DETECTION_TYPES.DetectedItem

    + +

    Type DETECTION_UNITS

    + +

    DETECTION_UNITS class

    + +

    Field(s)

    +
    +
    + + #string + +DETECTION_UNITS.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_UNITS:CreateDetectionSets() + +
    +
    + +

    Create the DetectedItems list from the DetectedObjects table.

    + + +

    For each DetectedItem, a one field array is created containing the Unit detected.

    + +

    Return value

    + +

    #DETECTION_UNITS: +self

    + +
    +
    +
    +
    + + +DETECTION_UNITS:DetectedItemReportSummary(Index) + +
    +
    + +

    Report summary of a DetectedItem using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +DETECTION_UNITS:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detection result.

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + Dcs.DCSTypes#Distance + +DETECTION_UNITS.DetectionRange + +
    +
    + +

    The range till which targets are detected.

    + +
    +
    +
    +
    + + +DETECTION_UNITS:GetChangeText(DetectedItem) + +
    +
    + +

    Make text documenting the changes of the detected zone.

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The Changes text

    + +
    +
    +
    +
    + + +DETECTION_UNITS:New(DetectionSetGroup) + +
    +
    + +

    DETECTION_UNITS constructor.

    + +

    Parameter

    +
      +
    • + +

      Core.Set#SET_GROUP DetectionSetGroup : +The Set of GROUPs in the Forward Air Controller role.

      + +
    • +
    +

    Return value

    + +

    Functional.Detection#DETECTION_UNITS: +self

    + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._BoundDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._FlareDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._FlareDetectedZones + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._SmokeDetectedUnits + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_UNITS._SmokeDetectedZones + +
    +
    + + + +
    +
    + +

    Type DETECTION_UNITS.DetectedItem

    diff --git a/docs/Documentation/DetectionManager.html b/docs/Documentation/DetectionManager.html index 0c48152fb..4828a274a 100644 --- a/docs/Documentation/DetectionManager.html +++ b/docs/Documentation/DetectionManager.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -105,23 +105,6 @@ If an ad-hoc report is requested, use the method 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 Tasks 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.


    @@ -131,12 +114,6 @@ Find a summary below describing for which situation a task type is created:

    Global(s)

    - - - -
    DETECTION_DISPATCHER - -
    DETECTION_MANAGER @@ -150,76 +127,6 @@ Find a summary below describing for which situation a task type is created:

    -

    Type DETECTION_DISPATCHER

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DETECTION_DISPATCHER.ClassName - -
    DETECTION_DISPATCHER.CommandCenter - -
    DETECTION_DISPATCHER.Detection -

    The DETECTION_BASE object that is used to report the detected objects.

    -
    DETECTION_DISPATCHER:EvaluateBAI(DetectedArea, FriendlyCoalition) -

    Creates a BAI task when there are targets for it.

    -
    DETECTION_DISPATCHER:EvaluateCAS(DetectedArea) -

    Creates a CAS task when there are targets for it.

    -
    DETECTION_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedArea) -

    Evaluates the removal of the Task from the Mission.

    -
    DETECTION_DISPATCHER:EvaluateSEAD(DetectedArea) -

    Creates a SEAD task when there are targets for it.

    -
    DETECTION_DISPATCHER.Mission - -
    DETECTION_DISPATCHER:New(SetGroup, Detection, Mission, CommandCenter) -

    DETECTION_DISPATCHER constructor.

    -
    DETECTION_DISPATCHER:ProcessDetected(Detection) -

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    -
    DETECTION_DISPATCHER.SetGroup -

    The groups to which the FAC will report to.

    -
    -

    Type DETECTION_MANAGER

    @@ -352,20 +259,6 @@ Find a summary below describing for which situation a task type is created:

    - #DETECTION_DISPATCHER - -DETECTION_DISPATCHER - -
    -
    - - - -
    -
    -
    -
    - #DETECTION_MANAGER DETECTION_MANAGER @@ -393,282 +286,6 @@ Find a summary below describing for which situation a task type is created:

    Type DetectionManager

    -

    Type DETECTION_DISPATCHER

    - -

    DETECTION_DISPATCHER class.

    - -

    Field(s)

    -
    -
    - - #string - -DETECTION_DISPATCHER.ClassName - -
    -
    - - - -
    -
    -
    -
    - - Wrapper.Group#GROUP - -DETECTION_DISPATCHER.CommandCenter - -
    -
    - - - -
    -
    -
    -
    - - Functional.Detection#DETECTION_BASE - -DETECTION_DISPATCHER.Detection - -
    -
    - -

    The DETECTION_BASE object that is used to report the detected objects.

    - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateBAI(DetectedArea, FriendlyCoalition) - -
    -
    - -

    Creates a BAI task when there are targets for it.

    - -

    Parameters

    - -

    Return value

    - -

    Tasking.Task#TASK:

    - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateCAS(DetectedArea) - -
    -
    - -

    Creates a CAS task when there are targets for it.

    - -

    Parameter

    - -

    Return value

    - -

    Tasking.Task#TASK:

    - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedArea) - -
    -
    - -

    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".

    - -

    Parameters

    - -

    Return value

    - -

    Tasking.Task#TASK:

    - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:EvaluateSEAD(DetectedArea) - -
    -
    - -

    Creates a SEAD task when there are targets for it.

    - -

    Parameter

    - -

    Return values

    -
      -
    1. - -

      Set#SET_UNIT: -TargetSetUnit: The target set of units.

      - -
    2. -
    3. - -

      #nil: -If there are no targets to be set.

      - -
    4. -
    -
    -
    -
    -
    - - Tasking.Mission#MISSION - -DETECTION_DISPATCHER.Mission - -
    -
    - - - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:New(SetGroup, Detection, Mission, CommandCenter) - -
    -
    - -

    DETECTION_DISPATCHER constructor.

    - -

    Parameters

    - -

    Return value

    - -

    #DETECTION_DISPATCHER: -self

    - -
    -
    -
    -
    - - -DETECTION_DISPATCHER:ProcessDetected(Detection) - -
    -
    - -

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    - -

    Parameter

    - -

    Return value

    - -

    #boolean: -Return true if you want the task assigning to continue... false will cancel the loop.

    - -
    -
    -
    -
    - - Set#SET_GROUP - -DETECTION_DISPATCHER.SetGroup - -
    -
    - -

    The groups to which the FAC will report to.

    - -
    -
    -

    Type DETECTION_MANAGER

    DETECTION_MANAGER class.

    diff --git a/docs/Documentation/Escort.html b/docs/Documentation/Escort.html index e2da40c9e..b3a03693c 100644 --- a/docs/Documentation/Escort.html +++ b/docs/Documentation/Escort.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -161,7 +161,7 @@ Note that this is really fantastic, as you now have the dynamic of taking contro

    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.
    • @@ -208,9 +208,27 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the

      Type ESCORT

    + + + + + + + + + + + + @@ -223,6 +241,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -445,6 +469,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -457,6 +487,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -577,6 +613,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + + + + @@ -604,19 +646,19 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the - + - + - + @@ -628,31 +670,31 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the - + - + - + - + - + @@ -664,25 +706,25 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the - + - + - + - + @@ -708,6 +750,12 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the

    Type MENUPARAM

    ESCORT.CT1 + +
    ESCORT.ClassName +
    ESCORT.Detection + +
    ESCORT.EscortBriefing +
    ESCORT.EscortGroup +
    ESCORT.EscortMenu +
    ESCORT.EscortName +
    ESCORT.EscortSetGroup +
    ESCORT.FollowScheduler

    The instance of the SCHEDULER class.

    +
    ESCORT.GT1 +
    ESCORT.ReportTargetsScheduler +
    ESCORT:SetDetection(Detection) +

    Set a Detection method for the EscortClient to be reported upon.

    ESCORT._AssistTarget(MenuParam)ESCORT._AssistTarget(DetectedItemID, self, EscortGroupAttack)
    ESCORT._AttackTarget(MenuParam)ESCORT:_AttackTarget(DetectedItemID)
    ESCORT._Flare(MenuParam)ESCORT._Flare(MenuParam, self, Color, Message)
    ESCORT._HoldPosition(MenuParam)ESCORT._HoldPosition(MenuParam, self, OrbitGroup, OrbitHeight, OrbitSeconds)
    ESCORT._JoinUpAndFollow(MenuParam)ESCORT._JoinUpAndFollow(MenuParam, self, Distance)
    ESCORT._ROE(MenuParam)ESCORT._ROE(MenuParam, self, EscortROEFunction, EscortROEMessage)
    ESCORT._ROT(MenuParam)ESCORT._ROT(MenuParam, self, EscortROTFunction, EscortROTMessage)
    ESCORT._ReportNearbyTargetsNow(MenuParam)ESCORT._ReportNearbyTargetsNow(MenuParam, self)
    ESCORT._ResumeMission(MenuParam)ESCORT._ResumeMission(MenuParam, self, WayPoint)
    ESCORT._ScanTargets(MenuParam)ESCORT._ScanTargets(MenuParam, self, ScanDuration)
    ESCORT._Smoke(MenuParam)ESCORT._Smoke(MenuParam, self, Color, Message)
    ESCORT._SwitchReportNearbyTargets(MenuParam)ESCORT:_SwitchReportNearbyTargets(ReportTargets)
    + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -443,7 +365,7 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - + @@ -458,120 +380,18 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1140,38 +960,6 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - - -
    -
    - - -EVENT:OnBirth(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTBIRTH event, and registers the unit born.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - -
    @@ -1215,102 +1003,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnBirthForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTBIRTH event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName : -The id of the unit for the event to be handled.

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnBirthRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTBIRTH event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnCrash(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTCRASH event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - -
    @@ -1354,102 +1046,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnCrashForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTCRASH event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnCrashRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTCRASH event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnDead(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTDEAD event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - -
    @@ -1493,70 +1089,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnDeadForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTDEAD event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnDeadRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTDEAD event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - -
    @@ -1595,134 +1127,6 @@ The self instance of the class for which the event is.

    #EVENT:

    - -
    -
    -
    - - -EVENT:OnEngineShutDownForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTENGINE_SHUTDOWN event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnEngineShutDownRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTENGINE_SHUTDOWN event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnEngineStartUpForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTENGINE_STARTUP event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnEngineStartUpRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTENGINE_STARTUP event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - -
    @@ -1773,7 +1177,7 @@ The self instance of the class for which the event is.

    -EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction) +EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction, EventID)
    @@ -1803,6 +1207,11 @@ The instance of the class for which the event is.

    #function OnEventFunction :

    + +
  • + +

    EventID :

    +
  • Return value

    @@ -1892,103 +1301,6 @@ The self instance of the class for which the event is captured. When the event h

    #EVENT:

    -
    -
    -
    -
    - - -EVENT:OnHit(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTHIT event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnHitForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTHIT event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnHitRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTHIT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - -
    @@ -2025,367 +1337,6 @@ The self instance of the class for which the event is.

    - -EVENT:OnLandForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTLAND event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnLandRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTLAND event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPilotDead(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTPILOT_DEAD event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass :

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPilotDeadForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTPILOT_DEAD event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPilotDeadRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTPILOT_DEAD event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerEnterRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTPLAYERENTERUNIT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerEnterUnit(EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTPLAYERENTERUNIT event.

    - -

    Parameters

    -
      -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerLeaveRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTPLAYERLEAVEUNIT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnPlayerLeaveUnit(EventFunction, EventClass) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      EventFunction :

      - -
    • -
    • - -

      EventClass :

      - -
    • -
    -
    -
    -
    -
    - - -EVENT:OnShot(EventFunction, EventClass) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      EventFunction :

      - -
    • -
    • - -

      EventClass :

      - -
    • -
    -
    -
    -
    -
    - - -EVENT:OnShotForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTSHOT event for a unit.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnShotRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTSHOT event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - EVENT:OnTakeOffForTemplate(EventTemplate, EventFunction, EventClass) @@ -2417,70 +1368,6 @@ The self instance of the class for which the event is.

    - -EVENT:OnTakeOffForUnit(EventDCSUnitName, EventFunction, EventClass) - -
    -
    - -

    Set a new listener for an SEVENTTAKEOFF event.

    - -

    Parameters

    -
      -
    • - -

      #string EventDCSUnitName :

      - -
    • -
    • - -

      #function EventFunction : -The function to be called when the event occurs for the unit.

      - -
    • -
    • - -

      Base#BASE EventClass : -The self instance of the class for which the event is.

      - -
    • -
    -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - - -EVENT:OnTakeOffRemove(EventClass) - -
    -
    - -

    Stop listening to SEVENTTAKEOFF event.

    - -

    Parameter

    - -

    Return value

    - -

    #EVENT:

    - - -
    -
    -
    -
    - EVENT:Remove(EventClass, EventID) diff --git a/docs/Documentation/Fsm.html b/docs/Documentation/Fsm.html index 1518eb4af..a809388d5 100644 --- a/docs/Documentation/Fsm.html +++ b/docs/Documentation/Fsm.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -726,12 +726,48 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + + + + + + + + + + + + + + + + + + + + + @@ -796,6 +832,12 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + @@ -808,6 +850,12 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + @@ -1960,6 +2008,176 @@ Finite State Machine Table

    #FSM_CONTROLLABLE:

    + + +
    +
    + + +FSM_CONTROLLABLE:OnAfterStop(Controllable, From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +FSM_CONTROLLABLE:OnBeforeStop(Controllable, From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +FSM_CONTROLLABLE:OnEnterStopped(Controllable, From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +FSM_CONTROLLABLE:OnLeaveStopped(Controllable, From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Stopped.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

      + +
    • +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    +
    @@ -1991,6 +2209,41 @@ Finite State Machine Table

    + +FSM_CONTROLLABLE:Stop() + +
    +
    + +

    Synchronous Event Trigger for Event Stop.

    + +
    +
    +
    +
    + + +FSM_CONTROLLABLE:__Stop(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Stop.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + FSM_CONTROLLABLE:_call_handler(handler, params, EventName) @@ -2226,6 +2479,24 @@ self

    #FSM_PROCESS:

    + +
    +
    +
    + + +FSM_PROCESS:Remove() + +
    +
    + +

    Removes an FSM_PROCESS object.

    + +

    Return value

    + +

    #FSM_PROCESS:

    + +
    @@ -2266,6 +2537,37 @@ self

    + +
    +
    +
    + + +FSM_PROCESS:_call_handler(handler, params, EventName) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      handler :

      + +
    • +
    • + +

      params :

      + +
    • +
    • + +

      EventName :

      + +
    • +
    diff --git a/docs/Documentation/Group.html b/docs/Documentation/Group.html index eb3d67814..c957c67c5 100644 --- a/docs/Documentation/Group.html +++ b/docs/Documentation/Group.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -262,7 +262,7 @@ Use the following Zone validation methods on the group:

    @@ -319,6 +319,12 @@ Use the following Zone validation methods on the group:

    + + + + @@ -748,7 +754,7 @@ The category ID

    -

    Returns the category name of the DCS Group.

    +

    Returns the category name of the #GROUP.

    Return value

    @@ -958,6 +964,34 @@ Minimum height found.

    + +GROUP:GetPlayerNames() + +
    +
    + +

    Get player names

    + +

    Return values

    +
      +
    1. + +

      #table: +The group has players, an array of player names is returned.

      + +
    2. +
    3. + +

      #nil: +The group has no players

      + +
    4. +
    +
    +
    +
    +
    + GROUP:GetPositionVec3() diff --git a/docs/Documentation/Identifiable.html b/docs/Documentation/Identifiable.html index 33086e9bf..919816a08 100644 --- a/docs/Documentation/Identifiable.html +++ b/docs/Documentation/Identifiable.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/MOVEMENT.html b/docs/Documentation/MOVEMENT.html index aed446e5c..f81accb1c 100644 --- a/docs/Documentation/MOVEMENT.html +++ b/docs/Documentation/MOVEMENT.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -70,7 +70,7 @@
    -

    Module MOVEMENT

    +

    Module Movement

    Limit the simultaneous movement of Groups within a running Mission.

    @@ -89,30 +89,60 @@ on defined intervals (currently every minute).

    MENUPARAM.Distance + +
    MENUPARAM.ParamDistance @@ -781,6 +829,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the
    + #number + +ESCORT.CT1 + +
    +
    + + + +
    +
    +
    +
    + #string ESCORT.ClassName @@ -790,6 +852,34 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + Functional.Detection#DETECTION_BASE + +ESCORT.Detection + +
    +
    + + + +
    +
    +
    +
    + + + +ESCORT.EscortBriefing + +
    +
    + + +
    @@ -818,6 +908,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + + +ESCORT.EscortMenu + +
    +
    + + +
    @@ -1352,6 +1456,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + + +ESCORT.EscortSetGroup + +
    +
    + + +
    @@ -1380,6 +1498,20 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the

    The instance of the SCHEDULER class.

    + +
    +
    +
    + + #number + +ESCORT.GT1 + +
    +
    + + +
    @@ -1923,6 +2055,30 @@ EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the + +
    +
    +
    + + +ESCORT:SetDetection(Detection) + +
    +
    + +

    Set a Detection method for the EscortClient to be reported upon.

    + + +

    Detection methods are based on the derived classes from DETECTION_BASE.

    + +

    Parameter

    +
    @@ -1995,18 +2151,28 @@ If true, then the direction vector will be smoked.

    -ESCORT._AssistTarget(MenuParam) +ESCORT._AssistTarget(DetectedItemID, self, EscortGroupAttack)
    -

    Parameter

    +

    Parameters

    • -

      #MENUPARAM MenuParam :

      +

      #number DetectedItemID :

      + +
    • +
    • + +

      self :

      + +
    • +
    • + +

      EscortGroupAttack :

    @@ -2016,7 +2182,7 @@ If true, then the direction vector will be smoked.

    -ESCORT._AttackTarget(MenuParam) +ESCORT:_AttackTarget(DetectedItemID)
    @@ -2027,7 +2193,7 @@ If true, then the direction vector will be smoked.

    • -

      #MENUPARAM MenuParam :

      +

      #number DetectedItemID :

    @@ -2037,19 +2203,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._Flare(MenuParam) +ESCORT._Flare(MenuParam, self, Color, Message)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      Color :

      + +
    • +
    • + +

      Message :

      +
    @@ -2071,19 +2252,39 @@ If true, then the direction vector will be smoked.

    -ESCORT._HoldPosition(MenuParam) +ESCORT._HoldPosition(MenuParam, self, OrbitGroup, OrbitHeight, OrbitSeconds)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      OrbitGroup :

      + +
    • +
    • + +

      OrbitHeight :

      + +
    • +
    • + +

      OrbitSeconds :

      +
    @@ -2092,19 +2293,29 @@ If true, then the direction vector will be smoked.

    -ESCORT._JoinUpAndFollow(MenuParam) +ESCORT._JoinUpAndFollow(MenuParam, self, Distance)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      Distance :

      +
    @@ -2113,19 +2324,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._ROE(MenuParam) +ESCORT._ROE(MenuParam, self, EscortROEFunction, EscortROEMessage)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      EscortROEFunction :

      + +
    • +
    • + +

      EscortROEMessage :

      +
    @@ -2134,19 +2360,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._ROT(MenuParam) +ESCORT._ROT(MenuParam, self, EscortROTFunction, EscortROTMessage)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      EscortROTFunction :

      + +
    • +
    • + +

      EscortROTMessage :

      +
    @@ -2155,19 +2396,24 @@ If true, then the direction vector will be smoked.

    -ESCORT._ReportNearbyTargetsNow(MenuParam) +ESCORT._ReportNearbyTargetsNow(MenuParam, self)
    -

    Parameter

    +

    Parameters

    @@ -2189,19 +2435,29 @@ If true, then the direction vector will be smoked.

    -ESCORT._ResumeMission(MenuParam) +ESCORT._ResumeMission(MenuParam, self, WayPoint)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      WayPoint :

      +
    @@ -2210,19 +2466,29 @@ If true, then the direction vector will be smoked.

    -ESCORT._ScanTargets(MenuParam) +ESCORT._ScanTargets(MenuParam, self, ScanDuration)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      ScanDuration :

      +
    @@ -2231,19 +2497,34 @@ If true, then the direction vector will be smoked.

    -ESCORT._Smoke(MenuParam) +ESCORT._Smoke(MenuParam, self, Color, Message)
    -

    Parameter

    +

    Parameters

    • #MENUPARAM MenuParam :

      +
    • +
    • + +

      self :

      + +
    • +
    • + +

      Color :

      + +
    • +
    • + +

      Message :

      +
    @@ -2252,7 +2533,7 @@ If true, then the direction vector will be smoked.

    -ESCORT._SwitchReportNearbyTargets(MenuParam) +ESCORT:_SwitchReportNearbyTargets(ReportTargets)
    @@ -2263,7 +2544,7 @@ If true, then the direction vector will be smoked.

    • -

      MenuParam :

      +

      ReportTargets :

    @@ -2312,6 +2593,20 @@ If true, then the direction vector will be smoked.

    + + +MENUPARAM.Distance + +
    +
    + + + +
    +
    +
    +
    + #Distance MENUPARAM.ParamDistance diff --git a/docs/Documentation/Event.html b/docs/Documentation/Event.html index d4bdd4c3d..aa688f7c1 100644 --- a/docs/Documentation/Event.html +++ b/docs/Documentation/Event.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -332,108 +332,30 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    EVENT:New() -
    EVENT:OnBirth(EventFunction, EventClass) -

    Set a new listener for an SEVENTBIRTH event, and registers the unit born.

    EVENT:OnBirthForTemplate(EventGroup, EventFunction, EventClass, EventTemplate)

    Create an OnBirth event handler for a group

    -
    EVENT:OnBirthForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTBIRTH event.

    -
    EVENT:OnBirthRemove(EventClass) -

    Stop listening to SEVENTBIRTH event.

    -
    EVENT:OnCrash(EventFunction, EventClass) -

    Set a new listener for an SEVENTCRASH event.

    EVENT:OnCrashForTemplate(EventGroup, EventFunction, EventClass, EventTemplate)

    Create an OnCrash event handler for a group

    -
    EVENT:OnCrashForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTCRASH event.

    -
    EVENT:OnCrashRemove(EventClass) -

    Stop listening to SEVENTCRASH event.

    -
    EVENT:OnDead(EventFunction, EventClass) -

    Set a new listener for an SEVENTDEAD event.

    EVENT:OnDeadForTemplate(EventGroup, EventFunction, EventClass, EventTemplate)

    Create an OnDead event handler for a group

    -
    EVENT:OnDeadForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTDEAD event.

    -
    EVENT:OnDeadRemove(EventClass) -

    Stop listening to SEVENTDEAD event.

    EVENT:OnEngineShutDownForTemplate(EventTemplate, EventFunction, EventClass)

    Create an OnDead event handler for a group

    -
    EVENT:OnEngineShutDownForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTENGINE_SHUTDOWN event.

    -
    EVENT:OnEngineShutDownRemove(EventClass) -

    Stop listening to SEVENTENGINE_SHUTDOWN event.

    -
    EVENT:OnEngineStartUpForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTENGINE_STARTUP event.

    -
    EVENT:OnEngineStartUpRemove(EventClass) -

    Stop listening to SEVENTENGINE_STARTUP event.

    EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction)EVENT:OnEventForTemplate(EventTemplate, EventFunction, EventClass, OnEventFunction, EventID)

    Create an OnDead event handler for a group

    EVENT:OnEventGeneric(EventFunction, EventClass, EventID)

    Set a new listener for an SEVENTX event independent from a unit or a weapon.

    -
    EVENT:OnHit(EventFunction, EventClass) -

    Set a new listener for an SEVENTHIT event.

    -
    EVENT:OnHitForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTHIT event.

    -
    EVENT:OnHitRemove(EventClass) -

    Stop listening to SEVENTHIT event.

    EVENT:OnLandForTemplate(EventTemplate, EventFunction, EventClass) -
    EVENT:OnLandForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTLAND event.

    -
    EVENT:OnLandRemove(EventClass) -

    Stop listening to SEVENTLAND event.

    -
    EVENT:OnPilotDead(EventFunction, EventClass) -

    Set a new listener for an SEVENTPILOT_DEAD event.

    -
    EVENT:OnPilotDeadForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTPILOT_DEAD event.

    -
    EVENT:OnPilotDeadRemove(EventClass) -

    Stop listening to SEVENTPILOT_DEAD event.

    -
    EVENT:OnPlayerEnterRemove(EventClass) -

    Stop listening to SEVENTPLAYERENTERUNIT event.

    -
    EVENT:OnPlayerEnterUnit(EventFunction, EventClass) -

    Set a new listener for an SEVENTPLAYERENTERUNIT event.

    -
    EVENT:OnPlayerLeaveRemove(EventClass) -

    Stop listening to SEVENTPLAYERLEAVEUNIT event.

    -
    EVENT:OnPlayerLeaveUnit(EventFunction, EventClass) - -
    EVENT:OnShot(EventFunction, EventClass) - -
    EVENT:OnShotForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTSHOT event for a unit.

    -
    EVENT:OnShotRemove(EventClass) -

    Stop listening to SEVENTSHOT event.

    EVENT:OnTakeOffForTemplate(EventTemplate, EventFunction, EventClass) -
    EVENT:OnTakeOffForUnit(EventDCSUnitName, EventFunction, EventClass) -

    Set a new listener for an SEVENTTAKEOFF event.

    -
    EVENT:OnTakeOffRemove(EventClass) -

    Stop listening to SEVENTTAKEOFF event.

    FSM_CONTROLLABLE:New(FSMT, Controllable)

    Creates a new FSM_CONTROLLABLE object.

    +
    FSM_CONTROLLABLE:OnAfterStop(Controllable, From, Event, To) +

    OnAfter Transition Handler for Event Stop.

    +
    FSM_CONTROLLABLE:OnBeforeStop(Controllable, From, Event, To) +

    OnBefore Transition Handler for Event Stop.

    +
    FSM_CONTROLLABLE:OnEnterStopped(Controllable, From, Event, To) +

    OnEnter Transition Handler for State Stopped.

    +
    FSM_CONTROLLABLE:OnLeaveStopped(Controllable, From, Event, To) +

    OnLeave Transition Handler for State Stopped.

    FSM_CONTROLLABLE:SetControllable(FSMControllable)

    Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.

    +
    FSM_CONTROLLABLE:Stop() +

    Synchronous Event Trigger for Event Stop.

    +
    FSM_CONTROLLABLE:__Stop(Delay) +

    Asynchronous Event Trigger for Event Stop.

    FSM_PROCESS:New(Controllable, Task)

    Creates a new FSM_PROCESS object.

    +
    FSM_PROCESS:Remove() +

    Removes an FSM_PROCESS object.

    FSM_PROCESS.Task +
    FSM_PROCESS:_call_handler(handler, params, EventName) +
    GROUP:GetCategoryName() -

    Returns the category name of the DCS Group.

    +

    Returns the category name of the #GROUP.

    GROUP:GetMinHeight()

    Returns the current minimum height of the group.

    +
    GROUP:GetPlayerNames() +

    Get player names

    -

    Type MOVEMENT

    - +

    Type MOVEMENT

    +
    + + + + + + + + + + + + + + + + + + + + - - - - + + + + @@ -150,8 +180,26 @@ on defined intervals (currently every minute).

    -

    Type MOVEMENT

    -

    Field(s)

    +

    Type Movement

    + +

    Type MOVEMENT

    + +

    the MOVEMENT class

    + +

    Field(s)

    +
    +
    + + +MOVEMENT.AliveUnits + +
    +
    + + + +
    +
    @@ -164,6 +212,71 @@ on defined intervals (currently every minute).

    + +
    +
    +
    + + #number + +MOVEMENT.MoveCount + +
    +
    + + + + +

    The internal counter of the amount of Moveing the has happened since MoveStart.

    + +
    +
    +
    +
    + + + +MOVEMENT.MoveMaximum + +
    +
    + + + + +

    Contains the Maximum amount of units that are allowed to move...

    + +
    +
    +
    +
    + + + +MOVEMENT.MovePrefixes + +
    +
    + + + +
    +
    +
    +
    + + + +MOVEMENT.MoveUnits + +
    +
    + + + + +

    Reflects if the Moving for this MovePrefixes is going to be scheduled or not.

    +
    @@ -195,13 +308,13 @@ on defined intervals (currently every minute).

    - -MOVEMENT:OnBirth(Event) + +MOVEMENT:OnDeadOrCrash(Event)
    -

    Captures the birth events when new Units were spawned.

    +

    Captures the Dead or Crash events when Units crash or are destroyed.

    Parameter

    + + + + @@ -269,6 +275,12 @@ Using this object reference, you can then remove ALL the menus and submenus unde + + + + @@ -278,9 +290,27 @@ Using this object reference, you can then remove ALL the menus and submenus unde - + + + + + + + + + + + + +
    MOVEMENT.AliveUnits + +
    MOVEMENT.ClassName +
    MOVEMENT.MoveCount + +
    MOVEMENT.MoveMaximum + +
    MOVEMENT.MovePrefixes + +
    MOVEMENT.MoveUnits +
    MOVEMENT:New(MovePrefixes, MoveMaximum) -
    MOVEMENT:OnBirth(Event) -

    Captures the birth events when new Units were spawned.

    MOVEMENT:OnDeadOrCrash(Event)

    Captures the Dead or Crash events when Units crash or are destroyed.

    +
    MOVEMENT:OnEventBirth(self, EventData) +

    Captures the birth events when new Units were spawned.

    MENU_BASE.ClassName +
    MENU_BASE:GetMenu(MenuText) +

    Gets a Menu from a parent Menu

    MENU_BASE.MenuPath +
    MENU_BASE.MenuRemoveParent +
    MENU_BASE:New(MenuText, ParentMenu)MENU_BASE.MenuTime + +
    MENU_BASE.New(#, self, MenuText, ParentMenu)

    Consructor

    +
    MENU_BASE:SetRemoveParent(RemoveParent) +

    Sets a Menu to remove automatically the parent menu when the menu removed is the last child menu of that parent Menu.

    +
    MENU_BASE:SetTime(MenuTime) +

    Sets a time stamp for later prevention of menu removal.

    @@ -412,7 +442,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde - MENU_COMMAND_BASE:New(MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments) + MENU_COMMAND_BASE.New(#, self, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments)

    Constructor

    @@ -449,12 +479,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde MENU_GROUP.MenuText - - - - MENU_GROUP.Menus - - @@ -476,13 +500,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde - MENU_GROUP:Remove() + MENU_GROUP:Remove(MenuTime)

    Removes the main menu and sub menus recursively of this MENU_GROUP.

    - MENU_GROUP:RemoveSubMenus() + MENU_GROUP:RemoveSubMenus(MenuTime)

    Removes the sub menus recursively of this MENU_GROUP.

    @@ -540,7 +564,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde - MENU_GROUP_COMMAND:Remove() + MENU_GROUP_COMMAND:Remove(MenuTime)

    Removes a menu structure for a group.

    @@ -757,6 +781,33 @@ Using this object reference, you can then remove ALL the menus and submenus unde + +
    +
    +
    + + +MENU_BASE:GetMenu(MenuText) + +
    +
    + +

    Gets a Menu from a parent Menu

    + +

    Parameter

    +
      +
    • + +

      #string MenuText : +The text of the child menu.

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + +
    @@ -783,6 +834,20 @@ Using this object reference, you can then remove ALL the menus and submenus unde + +
    +
    +
    + + + +MENU_BASE.MenuRemoveParent + +
    +
    + + +
    @@ -797,13 +862,27 @@ Using this object reference, you can then remove ALL the menus and submenus unde + +
    +
    +
    + + + +MENU_BASE.MenuTime + +
    +
    + + +
    -MENU_BASE:New(MenuText, ParentMenu) +MENU_BASE.New(#, self, MenuText, ParentMenu)
    @@ -814,6 +893,17 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    • +

      # : +ENU_BASE

      + +
    • +
    • + +

      self :

      + +
    • +
    • +

      MenuText :

    • @@ -823,6 +913,64 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    Return value

    + +

    #MENU_BASE:

    + + +
    +
    +
    +
    + + +MENU_BASE:SetRemoveParent(RemoveParent) + +
    +
    + +

    Sets a Menu to remove automatically the parent menu when the menu removed is the last child menu of that parent Menu.

    + +

    Parameter

    +
      +
    • + +

      #boolean RemoveParent : +If true, the parent menu is automatically removed when this menu is the last child menu of that parent Menu.

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + + +
    +
    +
    +
    + + +MENU_BASE:SetTime(MenuTime) + +
    +
    + +

    Sets a time stamp for later prevention of menu removal.

    + +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + +
    @@ -1200,8 +1348,8 @@ self

    Return value

    -

    #MENU_COALITION: -self

    +

    #MENU_COALITION:

    +
    @@ -1279,8 +1427,8 @@ An argument for the function. There can only be ONE argument given. So multiple

    Return value

    -

    #MENUCOALITIONCOMMAND: -self

    +

    #MENUCOALITIONCOMMAND:

    +
    @@ -1366,7 +1514,7 @@ self

    -MENU_COMMAND_BASE:New(MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments) +MENU_COMMAND_BASE.New(#, self, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments)
    @@ -1377,6 +1525,17 @@ self

    • +

      # : +ENUCOMMANDBASE

      + +
    • +
    • + +

      self :

      + +
    • +
    • +

      MenuText :

    • @@ -1396,6 +1555,11 @@ self

    +

    Return value

    + +

    #MENUCOMMANDBASE:

    + +
    @@ -1523,20 +1687,6 @@ self

    - - -
    -
    - - - -MENU_GROUP.Menus - -
    -
    - - -
    @@ -1613,13 +1763,21 @@ self

    -MENU_GROUP:Remove() +MENU_GROUP:Remove(MenuTime)

    Removes the main menu and sub menus recursively of this MENU_GROUP.

    +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +

    Return value

    #nil:

    @@ -1631,13 +1789,21 @@ self

    -MENU_GROUP:RemoveSubMenus() +MENU_GROUP:RemoveSubMenus(MenuTime)

    Removes the sub menus recursively of this MENU_GROUP.

    +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +

    Return value

    #MENU_GROUP: @@ -1772,8 +1938,8 @@ An argument for the function.

    Return value

    -

    Menu#MENUGROUPCOMMAND: -self

    +

    #MENUGROUPCOMMAND:

    +
    @@ -1809,13 +1975,21 @@ self

    -MENU_GROUP_COMMAND:Remove() +MENU_GROUP_COMMAND:Remove(MenuTime)

    Removes a menu structure for a group.

    +

    Parameter

    +
      +
    • + +

      MenuTime :

      + +
    • +

    Return value

    #nil:

    @@ -1874,8 +2048,8 @@ The parent menu. This parameter can be ignored if you want the menu to be locate

    Return value

    -

    #MENU_MISSION: -self

    +

    #MENU_MISSION:

    +
    @@ -1913,8 +2087,8 @@ self

    Return value

    -

    #MENU_MISSION: -self

    +

    #MENU_MISSION:

    + diff --git a/docs/Documentation/Message.html b/docs/Documentation/Message.html index 2da7809e7..935b571f5 100644 --- a/docs/Documentation/Message.html +++ b/docs/Documentation/Message.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/MissileTrainer.html b/docs/Documentation/MissileTrainer.html index b51e024ab..735bef024 100644 --- a/docs/Documentation/MissileTrainer.html +++ b/docs/Documentation/MissileTrainer.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -297,6 +297,12 @@ Together with the 476 virtual team, we tested the MISSILETRAINE MISSILETRAINER:New(Distance, Briefing)

    Creates the main object which is handling missile tracking.

    + + + + MISSILETRAINER:OnEventShot(EventData, EVentData) + +

    Detects if an SA site was shot with an anti radiation missile.

    @@ -339,12 +345,6 @@ Together with the 476 virtual team, we tested the MISSILETRAINE MISSILETRAINER._Alive(Client, self) - - - - MISSILETRAINER:_EventShot(Event) - -

    Detects if an SA site was shot with an anti radiation missile.

    @@ -821,6 +821,35 @@ The distance in meters when a tracked missile needs to be destroyed when close t
    + +MISSILETRAINER:OnEventShot(EventData, EVentData) + +
    +
    + +

    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.

    + +

    Parameters

    + +
    +
    +
    +
    + MISSILETRAINER.TrackingFrequency @@ -954,30 +983,6 @@ The distance in meters when a tracked missile needs to be destroyed when close t
    - -MISSILETRAINER:_EventShot(Event) - -
    -
    - -

    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.

    - -

    Parameter

    - -
    -
    -
    -
    - MISSILETRAINER._MenuMessages(MenuParameters) diff --git a/docs/Documentation/Mission.html b/docs/Documentation/Mission.html index 1ad2c406d..ee06d4ddb 100644 --- a/docs/Documentation/Mission.html +++ b/docs/Documentation/Mission.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -84,18 +84,6 @@ A CLIENT needs to be registered within the MISSION - - - - MISSIONSCHEDULER - - - - - - _TransportExecuteStage - - @@ -105,18 +93,6 @@ A CLIENT needs to be registered within the MISSION:AbortUnit(PlayerUnit)

    Aborts a PlayerUnit from the Mission.

    - - - - MISSION.AddClient(CLIENT, self, Client) - -

    Register a new CLIENT to participate within the mission.

    - - - - MISSION.AddGoalFunction(function, self, GoalFunction) - -

    Add a goal function to a MISSION.

    @@ -138,15 +114,9 @@ A CLIENT needs to be registered within the MISSION:ClearMissionMenu() + MISSION:Complete() -

    Clears the mission menu for the coalition.

    - - - - MISSION:Completed() - -

    Set a Mission to completed.

    +

    Synchronous Event Trigger for Event Complete.

    @@ -156,21 +126,9 @@ A CLIENT needs to be registered within the MISSION.FAILED + MISSION:Fail() - - - - - MISSION:Failed() - -

    Set a Mission to failed.

    - - - - MISSION.FindClient(CLIENT, self, ClientName) - -

    Find a CLIENT object within the MISSION by its ClientName.

    +

    Synchronous Event Trigger for Event Fail.

    @@ -186,7 +144,7 @@ A CLIENT needs to be registered within the MISSION:GetMissionMenu(TaskGroup) + MISSION:GetMenu(TaskGroup)

    Gets the mission menu for the coalition.

    @@ -222,39 +180,39 @@ A CLIENT needs to be registered within the MISSION.GoalFunction + MISSION.HasGroup(#, self, TaskGroup) - - - - - MISSION:HasGroup(TaskGroup) - - +

    Validates if the Mission has a Group

    MISSION:IsCompleted() -

    Returns if a Mission has completed.

    +

    Is the Mission Completed.

    MISSION:IsFailed() -

    Returns if a Mission has failed.

    +

    Is the Mission Failed.

    + + + + MISSION:IsHold() + +

    Is the Mission Hold.

    + + + + MISSION:IsIdle() + +

    Is the Mission Idle.

    MISSION:IsOngoing() -

    Returns if a Mission is ongoing.

    - - - - MISSION:IsPending() - -

    Returns if a Mission is pending.

    +

    Is the Mission Ongoing.

    @@ -267,54 +225,18 @@ A CLIENT needs to be registered within the MISSION.MissionBriefing - - - - MISSION.MissionCoalition - - MISSION.MissionMenu - - - - MISSION.MissionProgressTrigger - - - - - - MISSION.MissionReportFlash - - - - - - MISSION.MissionReportShow - - - - - - MISSION.MissionReportTrigger - - MISSION.MissionStatus - - - - MISSION.MissionTimeInterval - - @@ -330,25 +252,103 @@ A CLIENT needs to be registered within the MISSION:Ongoing() + MISSION:OnAfterComplete(From, Event, To) -

    Set a Mission to ongoing.

    +

    OnAfter Transition Handler for Event Complete.

    - MISSION:Pending() + MISSION:OnAfterFail(From, Event, To) -

    Set a Mission to pending.

    +

    OnAfter Transition Handler for Event Fail.

    - MISSION.REPEAT + MISSION:OnAfterStart(From, Event, To) - +

    OnAfter Transition Handler for Event Start.

    - MISSION:RemoveMenu() + MISSION:OnAfterStop(From, Event, To) + +

    OnAfter Transition Handler for Event Stop.

    + + + + MISSION:OnBeforeComplete(From, Event, To) + +

    OnBefore Transition Handler for Event Complete.

    + + + + MISSION:OnBeforeFail(From, Event, To) + +

    OnBefore Transition Handler for Event Fail.

    + + + + MISSION:OnBeforeStart(From, Event, To) + +

    OnBefore Transition Handler for Event Start.

    + + + + MISSION:OnBeforeStop(From, Event, To) + +

    OnBefore Transition Handler for Event Stop.

    + + + + MISSION:OnEnterCompleted(From, Event, To) + +

    OnEnter Transition Handler for State Completed.

    + + + + MISSION:OnEnterFailed(From, Event, To) + +

    OnEnter Transition Handler for State Failed.

    + + + + MISSION:OnEnterIdle(From, Event, To) + +

    OnEnter Transition Handler for State Idle.

    + + + + MISSION:OnEnterOngoing(From, Event, To) + +

    OnEnter Transition Handler for State Ongoing.

    + + + + MISSION:OnLeaveCompleted(From, Event, To) + +

    OnLeave Transition Handler for State Completed.

    + + + + MISSION:OnLeaveFailed(From, Event, To) + +

    OnLeave Transition Handler for State Failed.

    + + + + MISSION:OnLeaveIdle(From, Event, To) + +

    OnLeave Transition Handler for State Idle.

    + + + + MISSION:OnLeaveOngoing(From, Event, To) + +

    OnLeave Transition Handler for State Ongoing.

    + + + + MISSION:RemoveMenu(MenuTime)

    Removes the Planned Task menu.

    @@ -381,18 +381,6 @@ A CLIENT needs to be registered within the MISSION:ReportSummary()

    Create a summary report of the Mission (one line).

    - - - - MISSION:ReportToAll() - -

    Report the status of all MISSIONs to all active Clients.

    - - - - MISSION.SUCCESS - - @@ -402,45 +390,21 @@ A CLIENT needs to be registered within the MISSION:SetAssignedMenu(Task, MenuText) - -

    Sets the Assigned Task menu.

    - - - - MISSION:SetMenu() + MISSION:SetMenu(MenuTime)

    Sets the Planned Task menu.

    - MISSION:StatusToClients() + MISSION:Start() -

    Send the status of the MISSION to all Clients.

    +

    Synchronous Event Trigger for Event Start.

    - MISSION.TaskCategoryMenus + MISSION:Stop() - - - - - MISSION.TaskMenus - - - - - - MISSION.TaskTypeMenus - - - - - - MISSION._ActiveTasks - - +

    Synchronous Event Trigger for Event Stop.

    @@ -450,137 +414,37 @@ A CLIENT needs to be registered within the MISSION._GoalTasks + MISSION:__Complete(Delay) + +

    Asynchronous Event Trigger for Event Complete.

    + + + + MISSION:__Fail(Delay) + +

    Asynchronous Event Trigger for Event Fail.

    + + + + MISSION:__Start(Delay) + +

    Asynchronous Event Trigger for Event Start.

    + + + + MISSION:__Stop(Delay) + +

    Asynchronous Event Trigger for Event Stop.

    + + + + MISSION:onbeforeComplete(From, Event, To) - MISSION:onbeforeComplete(Event, From, To) - -

    FSM function for a MISSION

    - - - - MISSION:onenterCompleted(Event, From, To) - -

    FSM function for a MISSION

    - - - - -

    Type MISSIONSCHEDULER

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -600,45 +464,6 @@ A CLIENT needs to be registered within the -
    - - #MISSIONSCHEDULER - -MISSIONSCHEDULER - -
    -
    - - - -
    - -
    -
    - - - -_TransportExecuteStage - -
    -
    - - - - -

    _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
    • -
    - -

    --

    -

    Type Mission

    @@ -683,120 +508,6 @@ true if Unit is part of a Task in the Mission.

    - -MISSION.AddClient(CLIENT, self, Client) - -
    -
    - -

    Register a new CLIENT to participate within the mission.

    - -

    Parameters

    -
      -
    • - -

      CLIENT : -Client is the CLIENT object. The object must have been instantiated with CLIENT.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      Client :

      - -
    • -
    -

    Return value

    - - -

    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() )
    - -
    -
    -
    -
    - - -MISSION.AddGoalFunction(function, self, GoalFunction) - -
    -
    - -

    Add a goal function to a MISSION.

    - - -

    Goal functions are called when a TASK within a mission has been completed.

    - -

    Parameters

    -
      -
    • - -

      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.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      GoalFunction :

      - -
    • -
    -

    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 )
    - -
    -
    -
    -
    - MISSION:AddScoring(Scoring) @@ -868,31 +579,13 @@ The task added.

    - -MISSION:ClearMissionMenu() + +MISSION:Complete()
    -

    Clears the mission menu for the coalition.

    - -

    Return value

    - -

    #MISSION: -self

    - -
    -
    -
    -
    - - -MISSION:Completed() - -
    -
    - -

    Set a Mission to completed.

    +

    Synchronous Event Trigger for Event Complete.

    @@ -931,68 +624,13 @@ true if Unit is part of a Task in the Mission.

    - #number - -MISSION.FAILED + +MISSION:Fail()
    - - -
    -
    -
    -
    - - -MISSION:Failed() - -
    -
    - -

    Set a Mission to failed.

    - -
    -
    -
    -
    - - -MISSION.FindClient(CLIENT, self, ClientName) - -
    -
    - -

    Find a CLIENT object within the MISSION by its ClientName.

    - -

    Parameters

    -
      -
    • - -

      CLIENT : -ClientName is a string defining the Client Group as defined within the ME.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      ClientName :

      - -
    • -
    -

    Return value

    - - -

    CLIENT

    - -

    Usage:

    -
    -- Seach for Client "Bomber" within the Mission.
    -local BomberClient = Mission:FindClient( "Bomber" )
    +

    Synchronous Event Trigger for Event Fail.

    @@ -1035,8 +673,8 @@ local BomberClient = Mission:FindClient( "Bomber" )
    - -MISSION:GetMissionMenu(TaskGroup) + +MISSION:GetMenu(TaskGroup)
    @@ -1190,40 +828,43 @@ Returns nil if no task was found.

    Tasks = Mission:GetTasks() env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) -
    -
    -
    -
    - - -MISSION.GoalFunction - -
    -
    - - -
    -MISSION:HasGroup(TaskGroup) +MISSION.HasGroup(#, self, TaskGroup)
    +

    Validates if the Mission has a Group

    - -

    Parameter

    +

    Parameters

    • +

      # : +ISSION

      + +
    • +
    • + +

      self :

      + +
    • +
    • +

      TaskGroup :

    +

    Return value

    + +

    #boolean: +true if the Mission has a Group.

    +
    @@ -1235,12 +876,12 @@ env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    -

    Returns if a Mission has completed.

    +

    Is the Mission Completed.

    Return value

    +

    #boolean:

    -

    bool

    @@ -1253,10 +894,48 @@ env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    -

    Returns if a Mission has failed.

    +

    Is the Mission Failed.

    +

    Return value

    + +

    #boolean:

    + + +
    + +
    +
    + + +MISSION:IsHold() + +
    +
    -

    treturn bool

    +

    Is the Mission Hold.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +MISSION:IsIdle() + +
    +
    + +

    Is the Mission Idle.

    + +

    Return value

    + +

    #boolean:

    +
    @@ -1269,26 +948,12 @@ env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    -

    Returns if a Mission is ongoing.

    +

    Is the Mission Ongoing.

    - -

    treturn bool

    +

    Return value

    + +

    #boolean:

    -
    - -
    -
    - - -MISSION:IsPending() - -
    -
    - -

    Returns if a Mission is pending.

    - - -

    treturn bool

    @@ -1342,20 +1007,6 @@ true if Unit is part of a Task in the Mission.

    - - -
    -
    - - #string - -MISSION.MissionCoalition - -
    -
    - - -
    @@ -1370,62 +1021,6 @@ true if Unit is part of a Task in the Mission.

    - -
    -
    -
    - - #number - -MISSION.MissionProgressTrigger - -
    -
    - - - -
    -
    -
    -
    - - #boolean - -MISSION.MissionReportFlash - -
    -
    - - - -
    -
    -
    -
    - - #boolean - -MISSION.MissionReportShow - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSION.MissionReportTrigger - -
    -
    - - -
    @@ -1440,20 +1035,6 @@ true if Unit is part of a Task in the Mission.

    - -
    -
    -
    - - #number - -MISSION.MissionTimeInterval - -
    -
    - - -
    @@ -1526,40 +1107,584 @@ self

    - -MISSION:Ongoing() + +MISSION:OnAfterComplete(From, Event, To)
    -

    Set a Mission to ongoing.

    +

    OnAfter Transition Handler for Event Complete.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnAfterFail(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Fail.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnAfterStart(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Start.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnAfterStop(From, Event, To) + +
    +
    + +

    OnAfter Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnBeforeComplete(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Complete.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    - -MISSION:Pending() + +MISSION:OnBeforeFail(From, Event, To)
    -

    Set a Mission to pending.

    +

    OnBefore Transition Handler for Event Fail.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    - #number - -MISSION.REPEAT + +MISSION:OnBeforeStart(From, Event, To)
    +

    OnBefore Transition Handler for Event Start.

    +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnBeforeStop(From, Event, To) + +
    +
    + +

    OnBefore Transition Handler for Event Stop.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnEnterCompleted(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Completed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnEnterFailed(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Failed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnEnterIdle(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Idle.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnEnterOngoing(From, Event, To) + +
    +
    + +

    OnEnter Transition Handler for State Ongoing.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:OnLeaveCompleted(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Completed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnLeaveFailed(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Failed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnLeaveIdle(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Idle.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +MISSION:OnLeaveOngoing(From, Event, To) + +
    +
    + +

    OnLeave Transition Handler for State Ongoing.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    @@ -1567,13 +1692,21 @@ self

    -MISSION:RemoveMenu() +MISSION:RemoveMenu(MenuTime)

    Removes the Planned Task menu.

    +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    @@ -1685,33 +1818,6 @@ self

    #string:

    - -
    -
    -
    - - -MISSION:ReportToAll() - -
    -
    - -

    Report the status of all MISSIONs to all active Clients.

    - -
    -
    -
    -
    - - #number - -MISSION.SUCCESS - -
    -
    - - -
    @@ -1726,119 +1832,52 @@ self

    - -
    -
    -
    - - -MISSION:SetAssignedMenu(Task, MenuText) - -
    -
    - -

    Sets the Assigned Task menu.

    - -

    Parameters

    - -

    Return value

    - -

    #MISSION: -self

    -
    -MISSION:SetMenu() +MISSION:SetMenu(MenuTime)

    Sets the Planned Task menu.

    +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    - -MISSION:StatusToClients() + +MISSION:Start()
    -

    Send the status of the MISSION to all Clients.

    +

    Synchronous Event Trigger for Event Start.

    - - -MISSION.TaskCategoryMenus + +MISSION:Stop()
    - - -
    -
    -
    -
    - - - -MISSION.TaskMenus - -
    -
    - - - -
    -
    -
    -
    - - - -MISSION.TaskTypeMenus - -
    -
    - - - -
    -
    -
    -
    - - - -MISSION._ActiveTasks - -
    -
    - - +

    Synchronous Event Trigger for Event Stop.

    @@ -1859,43 +1898,124 @@ self

    - - -MISSION._GoalTasks + +MISSION:__Complete(Delay)
    +

    Asynchronous Event Trigger for Event Complete.

    +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      +
    • +
    +
    +
    +
    +
    + + +MISSION:__Fail(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Fail.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:__Start(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Start.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:__Stop(Delay) + +
    +
    + +

    Asynchronous Event Trigger for Event Stop.

    + +

    Parameter

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    -MISSION:onbeforeComplete(Event, From, To) +MISSION:onbeforeComplete(From, Event, To)
    -

    FSM function for a MISSION

    + + + +

    FSM function for a MISSION + @param #MISSION self + @param #string From + @param #string Event + @param #string To

    Parameters

    • -

      #string Event :

      +

      From :

    • -

      #string From :

      +

      Event :

    • -

      #string To :

      +

      To :

    @@ -1905,28 +2025,35 @@ self

    -MISSION:onenterCompleted(Event, From, To) +MISSION:onenterCompleted(From, Event, To)
    -

    FSM function for a MISSION

    + + + +

    FSM function for a MISSION + @param #MISSION self + @param #string From + @param #string Event + @param #string To

    Parameters

    • -

      #string Event :

      +

      From :

    • -

      #string From :

      +

      Event :

    • -

      #string To :

      +

      To :

    @@ -1935,402 +2062,6 @@ self

    Type MISSION.Clients

    -

    Type MISSIONSCHEDULER

    - -

    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.

    - -

    Field(s)

    -
    -
    - - -MISSIONSCHEDULER.AddMission(Mission) - -
    -
    - -

    This is the main MISSION declaration method.

    - - -

    Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc.

    - -

    Parameter

    -
      -
    • - -

      Mission : -is the MISSION object instantiated by MISSION.

      - -
    • -
    -

    Return value

    - - -

    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 )
    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.FindMission(MissionName) - -
    -
    - -

    Find a MISSION within the MISSIONSCHEDULER.

    - -

    Parameter

    -
      -
    • - -

      MissionName : -is the name of the MISSION given at declaration using AddMission.

      - -
    • -
    -

    Return value

    - - -

    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' )
    - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.MissionCount - -
    -
    - - - -
    -
    -
    -
    - - #MISSIONSCHEDULER.MISSIONS - -MISSIONSCHEDULER.Missions - -
    -
    - - - -
    -
    -
    -
    - - -MISSIONSCHEDULER.RemoveMission(MissionName) - -
    -
    - -

    Remove a MISSION from the MISSIONSCHEDULER.

    - -

    Parameter

    -
      -
    • - -

      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' )
    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMenu() - -
    -
    - -

    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.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMissionsFlash(TimeInterval) - -
    -
    - - - - -

    Internal function used by the MISSIONSCHEDULER menu.

    - -

    Parameter

    -
      -
    • - -

      TimeInterval :

      - -
    • -
    -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMissionsHide(Prm) - -
    -
    - - - - -

    Internal function used by the MISSIONSCHEDULER menu.

    - -

    Parameter

    -
      -
    • - -

      Prm :

      - -
    • -
    -
    -
    -
    -
    - - -MISSIONSCHEDULER.ReportMissionsShow() - -
    -
    - - - - -

    Internal function used by the MISSIONSCHEDULER menu.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.Scheduler() - -
    -
    - -

    This is the main MISSIONSCHEDULER Scheduler function.

    - - -

    It is considered internal and is automatically created when the Mission.lua file is included.

    - -
    -
    -
    -
    - - - -MISSIONSCHEDULER.SchedulerId - -
    -
    - - - - -

    MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 )

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER:Scoring(Scoring) - -
    -
    - -

    Adds a mission scoring to the game.

    - -

    Parameter

    -
      -
    • - -

      Scoring :

      - -
    • -
    -
    -
    -
    -
    - - -MISSIONSCHEDULER.Start() - -
    -
    - -

    Start the MISSIONSCHEDULER.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER.Stop() - -
    -
    - -

    Stop the MISSIONSCHEDULER.

    - -
    -
    -
    -
    - - -MISSIONSCHEDULER:Time(TimeSeconds, TimeIntervalShow, TimeShow) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      TimeSeconds :

      - -
    • -
    • - -

      TimeIntervalShow :

      - -
    • -
    • - -

      TimeShow :

      - -
    • -
    -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeIntervalCount - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeIntervalShow - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeSeconds - -
    -
    - - - -
    -
    -
    -
    - - #number - -MISSIONSCHEDULER.TimeShow - -
    -
    - - - -
    -
    - -

    Type MISSIONSCHEDULER.MISSIONS

    -

    Type SCORING

    diff --git a/docs/Documentation/Object.html b/docs/Documentation/Object.html index 6be6e7aa6..99c3c8465 100644 --- a/docs/Documentation/Object.html +++ b/docs/Documentation/Object.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Point.html b/docs/Documentation/Point.html index b5a28e4a9..63ef7724d 100644 --- a/docs/Documentation/Point.html +++ b/docs/Documentation/Point.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -1346,7 +1346,6 @@ The new calculated POINT_VEC2.

    - POINT_VEC2.z diff --git a/docs/Documentation/Positionable.html b/docs/Documentation/Positionable.html index 0aeaabf15..0fbf409de 100644 --- a/docs/Documentation/Positionable.html +++ b/docs/Documentation/Positionable.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Process_JTAC.html b/docs/Documentation/Process_JTAC.html index 16ebd156c..a6f640ee1 100644 --- a/docs/Documentation/Process_JTAC.html +++ b/docs/Documentation/Process_JTAC.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Process_Pickup.html b/docs/Documentation/Process_Pickup.html index c39875791..7738af995 100644 --- a/docs/Documentation/Process_Pickup.html +++ b/docs/Documentation/Process_Pickup.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Route.html b/docs/Documentation/Route.html index 3425dc182..7a204a4c5 100644 --- a/docs/Documentation/Route.html +++ b/docs/Documentation/Route.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -164,6 +164,12 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil
    + + + + @@ -206,7 +212,7 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil - + @@ -227,6 +233,106 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil + +
    MISSIONSCHEDULER.AddMission(Mission) -

    This is the main MISSION declaration method.

    -
    MISSIONSCHEDULER.FindMission(MissionName) -

    Find a MISSION within the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER.MissionCount - -
    MISSIONSCHEDULER.Missions - -
    MISSIONSCHEDULER.RemoveMission(MissionName) -

    Remove a MISSION from the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER.ReportMenu() -

    Enables a MENU option in the communications menu under F10 to control the status of the active missions.

    -
    MISSIONSCHEDULER.ReportMissionsFlash(TimeInterval) - -
    MISSIONSCHEDULER.ReportMissionsHide(Prm) - -
    MISSIONSCHEDULER.ReportMissionsShow() - -
    MISSIONSCHEDULER.Scheduler() -

    This is the main MISSIONSCHEDULER Scheduler function.

    -
    MISSIONSCHEDULER.SchedulerId - -
    MISSIONSCHEDULER:Scoring(Scoring) -

    Adds a mission scoring to the game.

    -
    MISSIONSCHEDULER.Start() -

    Start the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER.Stop() -

    Stop the MISSIONSCHEDULER.

    -
    MISSIONSCHEDULER:Time(TimeSeconds, TimeIntervalShow, TimeShow) - -
    MISSIONSCHEDULER.TimeIntervalCount - -
    MISSIONSCHEDULER.TimeIntervalShow - -
    MISSIONSCHEDULER.TimeSeconds - -
    MISSIONSCHEDULER.TimeShowMISSION:onenterCompleted(From, Event, To) ACT_ROUTE +
    ACT_ROUTE_POINT +
    ACT_ROUTE.TargetZoneACT_ROUTE.Zone ACT_ROUTE:onfuncHasArrived(ProcessUnit)

    Check if the controllable has arrived.

    +
    + +

    Type ACT_ROUTE_POINT

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ACT_ROUTE_POINT.ClassName + +
    ACT_ROUTE_POINT.DisplayCount + +
    ACT_ROUTE_POINT.DisplayInterval + +
    ACT_ROUTE_POINT.DisplayMessage + +
    ACT_ROUTE_POINT.DisplayTime + +
    ACT_ROUTE_POINT:GetPointVec2() +

    Get PointVec2

    +
    ACT_ROUTE_POINT:GetRange() +

    Get Range around PointVec2

    +
    ACT_ROUTE_POINT:Init(FsmRoute) + +
    ACT_ROUTE_POINT:New(The, Range, Zone, PointVec2) +

    Creates a new routing state machine.

    +
    ACT_ROUTE_POINT.PointVec2 + +
    ACT_ROUTE_POINT.Range + +
    ACT_ROUTE_POINT:SetPointVec2(PointVec2) +

    Set PointVec2

    +
    ACT_ROUTE_POINT:SetRange(Range) +

    Set Range around PointVec2

    +
    ACT_ROUTE_POINT.TASK + +
    ACT_ROUTE_POINT:onenterReporting(ProcessUnit, Event, From, To) +

    StateMachine callback function

    +
    ACT_ROUTE_POINT:onfuncHasArrived(ProcessUnit) +

    Method override to check if the controllable has arrived.

    @@ -261,6 +367,12 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil ACT_ROUTE_ZONE.DisplayTime + + + + ACT_ROUTE_ZONE:GetZone() + +

    Get Zone

    @@ -270,7 +382,7 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil - ACT_ROUTE_ZONE:New(TargetZone) + ACT_ROUTE_ZONE:New(Zone)

    Creates a new routing state machine.

    @@ -279,6 +391,12 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil ACT_ROUTE_ZONE.ProcessUnit + + + + ACT_ROUTE_ZONE:SetZone(Zone) + +

    Set Zone

    @@ -288,7 +406,7 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil - ACT_ROUTE_ZONE.TargetZone + ACT_ROUTE_ZONE.Zone @@ -320,6 +438,20 @@ Upon arrival at the zone, a confirmation of arrival is sent, and the process wil + +
    +
    +
    + + #ACT_ROUTE_POINT + +ACT_ROUTE_POINT + +
    +
    + + +
    @@ -424,8 +556,8 @@ self

    Core.Zone#ZONE_BASE - -ACT_ROUTE.TargetZone + +ACT_ROUTE.Zone
    @@ -530,6 +662,330 @@ self

    #boolean:

    +
    +
    + +

    Type ACT_ROUTE_POINT

    + +

    ACTROUTEPOINT class

    + +

    Field(s)

    +
    +
    + + #string + +ACT_ROUTE_POINT.ClassName + +
    +
    + + + +
    +
    +
    +
    + + #number + +ACT_ROUTE_POINT.DisplayCount + +
    +
    + + + +
    +
    +
    +
    + + #number + +ACT_ROUTE_POINT.DisplayInterval + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +ACT_ROUTE_POINT.DisplayMessage + +
    +
    + + + +
    +
    +
    +
    + + #number + +ACT_ROUTE_POINT.DisplayTime + +
    +
    + + + + +

    10 seconds is the default

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:GetPointVec2() + +
    +
    + +

    Get PointVec2

    + +

    Return value

    + +

    Core.Point#POINT_VEC2: +PointVec2 The PointVec2 to route to.

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:GetRange() + +
    +
    + +

    Get Range around PointVec2

    + +

    Return value

    + +

    #number: +The Range to consider the arrival. Default is 10000 meters.

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:Init(FsmRoute) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      FsmRoute :

      + +
    • +
    +
    +
    +
    +
    + + +ACT_ROUTE_POINT:New(The, Range, Zone, PointVec2) + +
    +
    + +

    Creates a new routing state machine.

    + + +

    The task will route a controllable to a PointVec2 until the controllable is within the Range.

    + +

    Parameters

    + +
    +
    +
    +
    + + + +ACT_ROUTE_POINT.PointVec2 + +
    +
    + + + +
    +
    +
    +
    + + +ACT_ROUTE_POINT.Range + +
    +
    + + + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:SetPointVec2(PointVec2) + +
    +
    + +

    Set PointVec2

    + +

    Parameter

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:SetRange(Range) + +
    +
    + +

    Set Range around PointVec2

    + +

    Parameter

    +
      +
    • + +

      #number Range : +The Range to consider the arrival. Default is 10000 meters.

      + +
    • +
    +
    +
    +
    +
    + + Tasking.Task#TASK + +ACT_ROUTE_POINT.TASK + +
    +
    + + + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:onenterReporting(ProcessUnit, Event, From, To) + +
    +
    + +

    StateMachine callback function

    + +

    Parameters

    + +
    +
    +
    +
    + + +ACT_ROUTE_POINT:onfuncHasArrived(ProcessUnit) + +
    +
    + +

    Method override to check if the controllable has arrived.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean:

    + +
    @@ -614,6 +1070,24 @@ self

    + +ACT_ROUTE_ZONE:GetZone() + +
    +
    + +

    Get Zone

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +Zone The Zone object where to route to.

    + +
    +
    +
    +
    + ACT_ROUTE_ZONE:Init(FsmRoute) @@ -636,7 +1110,7 @@ self

    -ACT_ROUTE_ZONE:New(TargetZone) +ACT_ROUTE_ZONE:New(Zone)
    @@ -650,7 +1124,7 @@ self

    @@ -668,6 +1142,28 @@ self

    +
    +
    +
    +
    + + +ACT_ROUTE_ZONE:SetZone(Zone) + +
    +
    + +

    Set Zone

    + +

    Parameter

    +
    @@ -688,8 +1184,8 @@ self

    Core.Zone#ZONE_BASE - -ACT_ROUTE_ZONE.TargetZone + +ACT_ROUTE_ZONE.Zone
    diff --git a/docs/Documentation/Scenery.html b/docs/Documentation/Scenery.html index f7258b5b4..b9e8b4a3d 100644 --- a/docs/Documentation/Scenery.html +++ b/docs/Documentation/Scenery.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/ScheduleDispatcher.html b/docs/Documentation/ScheduleDispatcher.html index df99aa154..c2bc197cc 100644 --- a/docs/Documentation/ScheduleDispatcher.html +++ b/docs/Documentation/ScheduleDispatcher.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -139,6 +139,12 @@ The Schedule() method returns the CallID that is the reference ID for each plann SCHEDULEDISPATCHER.ClassName + + + + SCHEDULEDISPATCHER:Clear(Scheduler) + + @@ -288,6 +294,27 @@ Nothing of this code should be modified without testing it thoroughly.

    +
    +
    +
    +
    + + +SCHEDULEDISPATCHER:Clear(Scheduler) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      Scheduler :

      + +
    • +
    @@ -315,7 +342,7 @@ Nothing of this code should be modified without testing it thoroughly.

    -

    setmetatable( {}, { __mode = "v" } )

    +

    or {}

    diff --git a/docs/Documentation/Scheduler.html b/docs/Documentation/Scheduler.html index eb654041b..a975d1109 100644 --- a/docs/Documentation/Scheduler.html +++ b/docs/Documentation/Scheduler.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,10 +72,14 @@

    Module Scheduler

    -

    This module contains the SCHEDULER class.

    +

    Core - SCHEDULER prepares and handles the execution of functions over scheduled time (intervals).

    +

    Banner Image

    + +
    +

    1) Scheduler#SCHEDULER class, extends Base#BASE

    The Scheduler#SCHEDULER class creates schedule.

    @@ -142,6 +146,12 @@ SCHEDULER.ClassName + + + + SCHEDULER:Clear() + +

    Clears all pending schedules.

    @@ -228,6 +238,19 @@ + +
    +
    +
    + + +SCHEDULER:Clear() + +
    +
    + +

    Clears all pending schedules.

    +
    diff --git a/docs/Documentation/Scoring.html b/docs/Documentation/Scoring.html index 0ffe32d75..c869150bd 100644 --- a/docs/Documentation/Scoring.html +++ b/docs/Documentation/Scoring.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Sead.html b/docs/Documentation/Sead.html index be4e3bf31..d2afd1e92 100644 --- a/docs/Documentation/Sead.html +++ b/docs/Documentation/Sead.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -89,18 +89,18 @@ SEAD.ClassName - - - - SEAD:EventShot(Event) - -

    Detects if an SA site was shot with an anti radiation missile.

    SEAD.New(table, self, SEADGroupPrefixes)

    Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.

    + + + + SEAD.OnEventShot(#, EventData, self) + +

    Detects if an SA site was shot with an anti radiation missile.

    @@ -151,30 +151,6 @@ - -
    -
    -
    - - -SEAD:EventShot(Event) - -
    -
    - -

    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.

    - -

    Parameter

    -
      -
    • - -

      Event :

      - -
    • -
    @@ -221,6 +197,41 @@ string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Grou -- Defends the Russian SA installations from SEAD attacks. SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) + +
    +
    +
    + + +SEAD.OnEventShot(#, EventData, self) + +
    +
    + +

    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.

    + +

    Parameters

    +
    diff --git a/docs/Documentation/Set.html b/docs/Documentation/Set.html index 8c6884967..11ff450fd 100644 --- a/docs/Documentation/Set.html +++ b/docs/Documentation/Set.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -540,12 +540,36 @@ The following iterator methods are currently available within the SETAIRBAS SET_BASE:Get(ObjectName)

    Gets a Base#BASE object from the Set#SET_BASE and derived classes, based on the Object Name.

    + + + + SET_BASE:GetFirst() + +

    Gets the first object from the Set#SET_BASE and derived classes.

    + + + + SET_BASE:GetLast() + +

    Gets the last object from the Set#SET_BASE and derived classes.

    + + + + SET_BASE:GetRandom() + +

    Gets a random object from the Set#SET_BASE and derived classes.

    SET_BASE:GetSet()

    Gets the Set.

    + + + + SET_BASE.Index + + @@ -1844,6 +1868,60 @@ self

    Core.Base#BASE:

    + +
    +
    +
    + + +SET_BASE:GetFirst() + +
    +
    + +

    Gets the first object from the Set#SET_BASE and derived classes.

    + +

    Return value

    + +

    Core.Base#BASE:

    + + +
    +
    +
    +
    + + +SET_BASE:GetLast() + +
    +
    + +

    Gets the last object from the Set#SET_BASE and derived classes.

    + +

    Return value

    + +

    Core.Base#BASE:

    + + +
    +
    +
    +
    + + +SET_BASE:GetRandom() + +
    +
    + +

    Gets a random object from the Set#SET_BASE and derived classes.

    + +

    Return value

    + +

    Core.Base#BASE:

    + +
    @@ -1862,6 +1940,20 @@ self

    #SET_BASE: self

    + +
    +
    +
    + + + +SET_BASE.Index + +
    +
    + + +
    diff --git a/docs/Documentation/Smoke.html b/docs/Documentation/Smoke.html index 0089621d3..f21743f78 100644 --- a/docs/Documentation/Smoke.html +++ b/docs/Documentation/Smoke.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -231,6 +231,12 @@ At random intervals, a new target is smoked.

    ACT_ASSIST:onafterStart(ProcessUnit, Event, From, To)

    StateMachine callback function

    + + + + ACT_ASSIST:onafterStop(ProcessUnit, Event, From, To) + +

    StateMachine callback function

    @@ -467,6 +473,42 @@ At random intervals, a new target is smoked.

    +

    StateMachine callback function

    + +

    Parameters

    + +
    +
    +
    +
    + + +ACT_ASSIST:onafterStop(ProcessUnit, Event, From, To) + +
    +
    +

    StateMachine callback function

    Parameters

    diff --git a/docs/Documentation/Spawn.html b/docs/Documentation/Spawn.html index bf5889313..c97824726 100644 --- a/docs/Documentation/Spawn.html +++ b/docs/Documentation/Spawn.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -883,6 +883,12 @@ and any spaces before and after the resulting name are removed.

    SPAWN:_TranslateRotate(SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle) + + + + SPAWN.uncontrolled + + @@ -2323,6 +2329,9 @@ when nothing was spawned.

    + +

    Overwrite unit names by default with group name.

    +
    @@ -2337,9 +2346,6 @@ when nothing was spawned.

    - -

    By default, no InitLimit

    -
    @@ -2375,7 +2381,7 @@ when nothing was spawned.

    - #number + SPAWN.SpawnMaxGroups @@ -2392,7 +2398,7 @@ when nothing was spawned.

    - #number + SPAWN.SpawnMaxUnitsAlive @@ -3373,6 +3379,20 @@ True = Continue Scheduler

    + +
    +
    +
    + + + +SPAWN.uncontrolled + +
    +
    + + +
    diff --git a/docs/Documentation/Static.html b/docs/Documentation/Static.html index cdcb75c70..ebd47c91e 100644 --- a/docs/Documentation/Static.html +++ b/docs/Documentation/Static.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Task.html b/docs/Documentation/Task.html index 9befab2b3..3ea8ea160 100644 --- a/docs/Documentation/Task.html +++ b/docs/Documentation/Task.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -152,7 +152,7 @@ Use the method TASK.AddScore() to add scores whe TASK:AssignToGroup(TaskGroup) -

    Assign the Taskto a Group.

    +

    Assign the Task to a Group.

    @@ -237,6 +237,12 @@ Use the method TASK.AddScore() to add scores whe TASK:GetScoring()

    Gets the Scoring of the task

    + + + + TASK:GetStateMachine(TaskUnit) + +

    Gets the FiniteStateMachine of Task with key TaskUnit

    @@ -264,7 +270,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:GetUnitProcess() + TASK:GetUnitProcess(TaskUnit)

    Get the Task FSM Process Template

    @@ -291,12 +297,24 @@ Use the method TASK.AddScore() to add scores whe TASK:IsAssignedToGroup(TaskGroup)

    Returns if the Task is assigned to the Group.

    + + + + TASK:IsStateAborted() + +

    Is the Task status Aborted.

    TASK:IsStateAssigned()

    Is the Task status Assigned.

    + + + + TASK:IsStateCancelled() + +

    Is the Task status Cancelled.

    @@ -348,15 +366,15 @@ Use the method TASK.AddScore() to add scores whe - TASK.MenuTaskAbort(MenuParam) + TASK:MenuTaskAbort(TaskGroup) - +

    Report the task status.

    - TASK.MenuTaskStatus(MenuParam) + TASK:MenuTaskStatus(TaskGroup) - +

    Report the task status.

    @@ -414,13 +432,19 @@ Use the method TASK.AddScore() to add scores whe - TASK:RemoveMenu() + TASK:RemoveAssignedMenuForGroup(TaskGroup, MenuTime) + +

    Remove the assigned menu option of the Task for a Group.

    + + + + TASK:RemoveMenu(MenuTime)

    Remove the menu options of the Task to all the groups in the SetGroup.

    - TASK:RemoveMenuForGroup(TaskGroup) + TASK:RemovePlannedMenuForGroup(TaskGroup, MenuTime)

    Remove the menu option of the Task for a Group.

    @@ -462,7 +486,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:SetAssignedMenuForGroup(TaskGroup) + TASK:SetAssignedMenuForGroup(TaskGroup, MenuTime)

    Set the assigned menu options of the Task.

    @@ -486,13 +510,13 @@ Use the method TASK.AddScore() to add scores whe - TASK:SetMenu() + TASK:SetMenu(MenuTime)

    Set the menu options of the Task to all the groups in the SetGroup.

    - TASK:SetMenuForGroup(TaskGroup) + TASK:SetMenuForGroup(MenuTime, TaskGroup)

    Set the Menu for a Group

    @@ -504,7 +528,7 @@ Use the method TASK.AddScore() to add scores whe - TASK:SetPlannedMenuForGroup(TaskGroup, MenuText) + TASK:SetPlannedMenuForGroup(TaskGroup, MenuText, MenuTime)

    Set the planned menu option of the Task.

    @@ -531,12 +555,24 @@ Use the method TASK.AddScore() to add scores whe TASK:SetUnitProcess(Core, FsmTemplate)

    Sets the Task FSM Process Template

    + + + + TASK:StateAborted() + +

    Sets a Task to status Aborted.

    TASK:StateAssigned()

    Sets a Task to status Assigned.

    + + + + TASK:StateCancelled() + +

    Sets a Task to status Cancelled.

    @@ -609,12 +645,18 @@ Use the method TASK.AddScore() to add scores whe TASK.TimeOut + + + + TASK:UnAssignFromGroup(TaskGroup) + +

    UnAssign the Task from a Group.

    TASK:UnAssignFromGroups() -

    Assign the Task from the Groups.

    +

    UnAssign the Task from the Groups.

    @@ -781,7 +823,7 @@ true if Unit is part of the Task.

    -

    Assign the Taskto a Group.

    +

    Assign the Task to a Group.

    Parameter

    +
    +
    +
    + + +TASK:GetStateMachine(TaskUnit) + +
    +
    + +

    Gets the FiniteStateMachine of Task with key TaskUnit

    + +

    Parameter

    + +

    Return value

    + +

    Core.Fsm#FSM_PROCESS:

    + +
    @@ -1145,13 +1213,21 @@ TaskType

    -TASK:GetUnitProcess() +TASK:GetUnitProcess(TaskUnit)

    Get the Task FSM Process Template

    +

    Parameter

    +
      +
    • + +

      TaskUnit :

      + +
    • +

    Return value

    Core.Fsm#FSM_PROCESS:

    @@ -1253,6 +1329,19 @@ self

    #boolean:

    +
    +
    +
    +
    + + +TASK:IsStateAborted() + +
    +
    + +

    Is the Task status Aborted.

    +
    @@ -1271,6 +1360,19 @@ self

    + +TASK:IsStateCancelled() + +
    +
    + +

    Is the Task status Cancelled.

    + +
    +
    +
    +
    + TASK:IsStateFailed() @@ -1410,18 +1512,18 @@ true if Unit is part of the Task.

    -TASK.MenuTaskAbort(MenuParam) +TASK:MenuTaskAbort(TaskGroup)
    - +

    Report the task status.

    Parameter

    • -

      MenuParam :

      +

      TaskGroup :

    @@ -1431,18 +1533,18 @@ true if Unit is part of the Task.

    -TASK.MenuTaskStatus(MenuParam) +TASK:MenuTaskStatus(TaskGroup)
    - +

    Report the task status.

    Parameter

    • -

      MenuParam :

      +

      TaskGroup :

    @@ -1659,14 +1761,27 @@ The name of the Player.

    - -TASK:RemoveMenu() + +TASK:RemoveAssignedMenuForGroup(TaskGroup, MenuTime)
    -

    Remove the menu options of the Task to all the groups in the SetGroup.

    +

    Remove the assigned menu option of the Task for a Group.

    +

    Parameters

    +

    Return value

    #TASK: @@ -1677,20 +1792,51 @@ self

    - -TASK:RemoveMenuForGroup(TaskGroup) + +TASK:RemoveMenu(MenuTime) + +
    +
    + +

    Remove the menu options of the Task to all the groups in the SetGroup.

    + +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + + +
    +
    +
    +
    + + +TASK:RemovePlannedMenuForGroup(TaskGroup, MenuTime)

    Remove the menu option of the Task for a Group.

    -

    Parameter

    +

    Parameters

    Return value

    @@ -1815,19 +1961,24 @@ self

    -TASK:SetAssignedMenuForGroup(TaskGroup) +TASK:SetAssignedMenuForGroup(TaskGroup, MenuTime)

    Set the assigned menu options of the Task.

    -

    Parameter

    +

    Parameters

    Return value

    @@ -1902,34 +2053,57 @@ self

    -TASK:SetMenu() +TASK:SetMenu(MenuTime)

    Set the menu options of the Task to all the groups in the SetGroup.

    +

    Parameter

    +
      +
    • + +

      #number MenuTime :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + +
    -TASK:SetMenuForGroup(TaskGroup) +TASK:SetMenuForGroup(MenuTime, TaskGroup)

    Set the Menu for a Group

    -

    Parameter

    +

    Parameters

    • +

      #number MenuTime :

      + +
    • +
    • +

      TaskGroup :

    +

    Return value

    + +

    #TASK:

    + +
    @@ -1957,7 +2131,7 @@ self

    -TASK:SetPlannedMenuForGroup(TaskGroup, MenuText) +TASK:SetPlannedMenuForGroup(TaskGroup, MenuText, MenuTime)
    @@ -1976,6 +2150,11 @@ self

    #string MenuText : The menu text.

    + +
  • + +

    #number MenuTime :

    +
  • Return value

    @@ -2005,7 +2184,7 @@ self

  • -

    Fsm :

    +

    Core.Fsm#FSM_PROCESS Fsm :

  • @@ -2097,6 +2276,19 @@ Fsm#FSM_PROCESS

    + +TASK:StateAborted() + +
    +
    + +

    Sets a Task to status Aborted.

    + +
    +
    +
    +
    + TASK:StateAssigned() @@ -2110,6 +2302,19 @@ Fsm#FSM_PROCESS

    + +TASK:StateCancelled() + +
    +
    + +

    Sets a Task to status Cancelled.

    + +
    +
    +
    +
    + TASK:StateFailed() @@ -2270,6 +2475,27 @@ Fsm#FSM_PROCESS

    +
    +
    +
    +
    + + +TASK:UnAssignFromGroup(TaskGroup) + +
    +
    + +

    UnAssign the Task from a Group.

    + +

    Parameter

    +
      +
    • + +

      TaskGroup :

      + +
    • +
    @@ -2281,7 +2507,7 @@ Fsm#FSM_PROCESS

    -

    Assign the Task from the Groups.

    +

    UnAssign the Task from the Groups.

    diff --git a/docs/Documentation/Task_A2G.html b/docs/Documentation/Task_A2G.html index 97aa39bb4..8b3b168d3 100644 --- a/docs/Documentation/Task_A2G.html +++ b/docs/Documentation/Task_A2G.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -72,27 +72,59 @@

    Module Task_A2G

    -

    (AI) (SP) (MP) Tasking for Air to Ground Processes.

    +

    This module contains the TASK_A2G classes.

    -

    1) #TASK_A2G class, extends Task#TASK

    -

    The #TASK_A2G class defines a CAS or BAI task of a Set of Target Units, -located at a Target Zone, based on the tasking capabilities defined in Task#TASK. +

    1) TaskA2G#TASKA2G class, extends Task#TASK

    + +

    The #TASK_A2G class defines Air To Ground tasks for a Set of Target Units, +based on the tasking capabilities defined in Task#TASK. The TASK_A2G 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 ProcessFsm.Assign#ACTASSIGN_ACCEPT is started to accept the task.
    • -
    • Assigned: The SEAD task is assigned to a Group#GROUP. Upon Assigned, the sub-process ProcessFsm.Route#ACTROUTE 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 ProcessSEAD#PROCESSSEAD 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.
    • +
    • Planned: The A2G task is planned.
    • +
    • Assigned: The A2G task is assigned to a Group#GROUP.
    • +
    • Success: The A2G task is successfully completed.
    • +
    • Failed: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    +

    1) TaskA2G#TASKSEAD class, extends TaskA2G#TASKA2G

    + +

    The #TASK_SEAD class defines a SEAD task for a Set of Target Units.

    +
    -

    Authors: FlightControl - Design and Programming

    +

    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 and CONTRIBUTIONS

    + +

    Contributions:

    + +
      +
    • [WingThor]: Concept, Advice & Testing.
    • +
    + +

    Authors:

    + +
      +
    • FlightControl: Concept, Design & Programming. +
    • +

    Global(s)

    @@ -100,6 +132,24 @@ The TASK_A2G is implemented using a Stat + + + + + + + + + + + +
    TASK_A2G +
    TASK_BAI + +
    TASK_CAS + +
    TASK_SEAD +
    @@ -118,9 +168,129 @@ The TASK_A2G is implemented using a Stat - TASK_A2G:New(Mission, SetGroup, TaskName, TaskType, UnitSetTargets, TargetZone, TargetSetUnit, FACUnit) + TASK_A2G:GetRendezVousPointVec2(TaskUnit) + + + + + + TASK_A2G:GetRendezVousZone(TaskUnit) + + + + + + TASK_A2G:GetTargetPointVec2(TaskUnit) + + + + + + TASK_A2G:GetTargetZone(TaskUnit) + + + + + + TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType)

    Instantiates a new TASK_A2G.

    + + + + TASK_A2G:SetRendezVousPointVec2(RendezVousPointVec2, RendezVousRange, TaskUnit) + + + + + + TASK_A2G:SetRendezVousZone(RendezVousZone, TaskUnit) + + + + + + TASK_A2G:SetTargetPointVec2(TargetPointVec2, TaskUnit) + + + + + + TASK_A2G:SetTargetZone(TargetZone, TaskUnit) + + + + + + TASK_A2G.TargetSetUnit + + + + + + +

    Type TASK_BAI

    + + + + + + + + + + + + + +
    TASK_BAI.ClassName + +
    TASK_BAI:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) +

    Instantiates a new TASK_BAI.

    +
    TASK_BAI.TargetSetUnit + +
    + +

    Type TASK_CAS

    + + + + + + + + + + + + + +
    TASK_CAS.ClassName + +
    TASK_CAS:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) +

    Instantiates a new TASK_CAS.

    +
    TASK_CAS.TargetSetUnit + +
    + +

    Type TASK_SEAD

    + + + + + + + + + + + +
    TASK_SEAD.ClassName + +
    TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) +

    Instantiates a new TASK_SEAD.

    +
    TASK_SEAD.TargetSetUnit +
    @@ -138,10 +308,54 @@ The TASK_A2G is implemented using a Stat +
    +
    +
    +
    + + #TASK_BAI + +TASK_BAI + +
    +
    + + + +
    +
    +
    +
    + + #TASK_CAS + +TASK_CAS + +
    +
    + + + +
    +
    +
    +
    + + #TASK_SEAD + +TASK_SEAD + +
    +
    + + +

    Type Task_A2G

    +

    Type FSM_PROCESS

    +

    Type TASK_A2G

    The TASK_A2G class

    @@ -172,13 +386,127 @@ The TASK_A2G is implemented using a Stat +
    +
    +
    +
    + + +TASK_A2G:GetRendezVousPointVec2(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Core.Point#POINT_VEC2: +The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.

      + +
    2. +
    3. + +

      #number: +The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.

      + +
    4. +
    +
    +
    +
    +
    + + +TASK_A2G:GetRendezVousZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the RendezVous is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2G:GetTargetPointVec2(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Point#POINT_VEC2: +The PointVec2 object where the Target is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2G:GetTargetZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the Target is located on the map.

    +
    -TASK_A2G:New(Mission, SetGroup, TaskName, TaskType, UnitSetTargets, TargetZone, TargetSetUnit, FACUnit) +TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType)
    @@ -206,18 +534,20 @@ The name of the Task.

  • -

    #string TaskType : -BAI or CAS

    - -
  • -
  • -

    Set#SET_UNIT UnitSetTargets :

  • -

    Core.Zone#ZONE_BASE TargetZone :

    +

    #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

    + +
  • +
  • + +

    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.

  • @@ -227,7 +557,7 @@ BAI or CAS

  • -

    FACUnit :

    +

    TaskType :

  • @@ -236,6 +566,419 @@ BAI or CAS

    #TASK_A2G: self

    +
    +
    +
    +
    + + +TASK_A2G:SetRendezVousPointVec2(RendezVousPointVec2, RendezVousRange, TaskUnit) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Core.Point#POINT_VEC2 RendezVousPointVec2 : +The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.

      + +
    • +
    • + +

      #number RendezVousRange : +The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2G:SetRendezVousZone(RendezVousZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +TASK_A2G:SetTargetPointVec2(TargetPointVec2, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +TASK_A2G:SetTargetZone(TargetZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_A2G.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_BAI

    + +

    The TASK_BAI class

    + +

    Field(s)

    +
    +
    + + #string + +TASK_BAI.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_BAI:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) + +
    +
    + +

    Instantiates a new TASK_BAI.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      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.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_BAI: +self

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_BAI.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_CAS

    + +

    The TASK_CAS class

    + +

    Field(s)

    +
    +
    + + #string + +TASK_CAS.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CAS:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) + +
    +
    + +

    Instantiates a new TASK_CAS.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      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.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_CAS: +self

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_CAS.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_SEAD

    + +

    The TASK_SEAD class

    + +

    Field(s)

    +
    +
    + + #string + +TASK_SEAD.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit) + +
    +
    + +

    Instantiates a new TASK_SEAD.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      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.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_SEAD: +self

    + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_SEAD.TargetSetUnit + +
    +
    + + +
    diff --git a/docs/Documentation/Task_A2G_Dispatcher.html b/docs/Documentation/Task_A2G_Dispatcher.html new file mode 100644 index 000000000..b653d16ca --- /dev/null +++ b/docs/Documentation/Task_A2G_Dispatcher.html @@ -0,0 +1,485 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Task_A2G_Dispatcher

    + +

    Tasking - The TASKA2GDISPATCHER creates and manages player TASK_A2G tasks based on detected targets.

    + + + +
    + +

    1) #TASKA2GDISPATCHER class, extends #DETECTION_MANAGER

    + +

    The #TASKA2GDISPATCHER 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 Tasks 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) TASKA2GDISPATCHER constructor:

    +

    The TASKA2GDISPATCHER.New() method creates a new TASKA2GDISPATCHER 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.
    • +
    + + +

    Global(s)

    + + + + + +
    TASK_A2G_DISPATCHER + +
    +

    Type TASK_A2G_DISPATCHER

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2G_DISPATCHER.ClassName + +
    TASK_A2G_DISPATCHER.Detection +

    The DETECTION_BASE object that is used to report the detected objects.

    +
    TASK_A2G_DISPATCHER:EvaluateBAI(DetectedItem, FriendlyCoalition) +

    Creates a BAI task when there are targets for it.

    +
    TASK_A2G_DISPATCHER:EvaluateCAS(DetectedItem) +

    Creates a CAS task when there are targets for it.

    +
    TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItem) +

    Evaluates the removal of the Task from the Mission.

    +
    TASK_A2G_DISPATCHER:EvaluateSEAD(DetectedItem) +

    Creates a SEAD task when there are targets for it.

    +
    TASK_A2G_DISPATCHER.Mission + +
    TASK_A2G_DISPATCHER:New(The, SetGroup, Detection, Mission) +

    TASKA2GDISPATCHER constructor.

    +
    TASK_A2G_DISPATCHER:ProcessDetected(Detection) +

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    +
    TASK_A2G_DISPATCHER.SetGroup +

    The groups to which the FAC will report to.

    +
    + +

    Global(s)

    +
    +
    + + #TASK_A2G_DISPATCHER + +TASK_A2G_DISPATCHER + +
    +
    + + + +
    +
    +

    Type Task_A2G_Dispatcher

    + +

    Type TASK_A2G_DISPATCHER

    + +

    TASKA2GDISPATCHER class.

    + +

    Field(s)

    +
    +
    + + #string + +TASK_A2G_DISPATCHER.ClassName + +
    +
    + + + +
    +
    +
    +
    + + Functional.Detection#DETECTION_BASE + +TASK_A2G_DISPATCHER.Detection + +
    +
    + +

    The DETECTION_BASE object that is used to report the detected objects.

    + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateBAI(DetectedItem, FriendlyCoalition) + +
    +
    + +

    Creates a BAI task when there are targets for it.

    + +

    Parameters

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateCAS(DetectedItem) + +
    +
    + +

    Creates a CAS task when there are targets for it.

    + +

    Parameter

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItem) + +
    +
    + +

    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".

    + +

    Parameters

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:EvaluateSEAD(DetectedItem) + +
    +
    + +

    Creates a SEAD task when there are targets for it.

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Set#SET_UNIT: +TargetSetUnit: The target set of units.

      + +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      + +
    4. +
    +
    +
    +
    +
    + + Tasking.Mission#MISSION + +TASK_A2G_DISPATCHER.Mission + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:New(The, SetGroup, Detection, Mission) + +
    +
    + +

    TASKA2GDISPATCHER constructor.

    + +

    Parameters

    + +

    Return value

    + +

    #TASKA2GDISPATCHER: +self

    + +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:ProcessDetected(Detection) + +
    +
    + +

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +Return true if you want the task assigning to continue... false will cancel the loop.

    + +
    +
    +
    +
    + + Set#SET_GROUP + +TASK_A2G_DISPATCHER.SetGroup + +
    +
    + +

    The groups to which the FAC will report to.

    + +
    +
    + +
    + +
    + + diff --git a/docs/Documentation/Task_PICKUP.html b/docs/Documentation/Task_PICKUP.html index fd8241229..3cd5f394a 100644 --- a/docs/Documentation/Task_PICKUP.html +++ b/docs/Documentation/Task_PICKUP.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Task_SEAD.html b/docs/Documentation/Task_SEAD.html index e443fdc31..b4befe7f4 100644 --- a/docs/Documentation/Task_SEAD.html +++ b/docs/Documentation/Task_SEAD.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • diff --git a/docs/Documentation/Unit.html b/docs/Documentation/Unit.html index 39cf4d1d7..c87b8a3e7 100644 --- a/docs/Documentation/Unit.html +++ b/docs/Documentation/Unit.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -214,6 +214,12 @@ If you want to obtain the complete 3D position including ori� UNIT:GetCallsign()

    Returns the Unit's callsign - the localized string.

    + + + + UNIT:GetCategoryName() + +

    Returns the category name of the #UNIT.

    @@ -693,6 +699,24 @@ The DCS Unit is not existing or alive.

    + +UNIT:GetCategoryName() + +
    +
    + +

    Returns the category name of the #UNIT.

    + +

    Return value

    + +

    #string: +Category name = Helicopter, Airplane, Ground Unit, Ship

    + +
    +
    +
    +
    + UNIT:GetDCSObject() diff --git a/docs/Documentation/Utils.html b/docs/Documentation/Utils.html index 33c940e11..ca23f3aa4 100644 --- a/docs/Documentation/Utils.html +++ b/docs/Documentation/Utils.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Documentation/Zone.html b/docs/Documentation/Zone.html index 9c92cf3da..7931424c8 100644 --- a/docs/Documentation/Zone.html +++ b/docs/Documentation/Zone.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -76,6 +76,8 @@ +

    Banner Image

    +

    There are essentially two core functions that zones accomodate:

    @@ -363,12 +365,30 @@ This class implements the inherited functions from ZONE_BASE:GetName()

    Returns the name of the zone.

    + + + + ZONE_BASE:GetPointVec2(Height) + +

    Returns a Point#POINT_VEC2 of the zone.

    + + + + ZONE_BASE:GetPointVec3(Height) + +

    Returns a Point#POINT_VEC3 of the zone.

    ZONE_BASE:GetRandomPointVec2()

    Define a random Point#POINT_VEC2 within the zone.

    + + + + ZONE_BASE:GetRandomPointVec3() + +

    Define a random Point#POINT_VEC3 within the zone.

    @@ -381,6 +401,12 @@ This class implements the inherited functions from ZONE_BASE:GetVec2()

    Returns the DCSTypes#Vec2 coordinate of the zone.

    + + + + ZONE_BASE:GetVec3(Height) + +

    Returns the DCSTypes#Vec3 of the zone.

    @@ -520,7 +546,7 @@ This class implements the inherited functions from Type ZONE_POLYGON_BASE - + @@ -590,7 +616,7 @@ This class implements the inherited functions from Type ZONE_RADIUS
    ZONE_POLYGON_BASE:BoundZone()ZONE_POLYGON_BASE:BoundZone(UnBound)

    Smokes the zone boundaries in a color.

    - + @@ -961,6 +987,60 @@ The name of the zone.

    + +ZONE_BASE:GetPointVec2(Height) + +
    +
    + +

    Returns a Point#POINT_VEC2 of the zone.

    + +

    Parameter

    +
      +
    • + +

      Dcs.DCSTypes#Distance Height : +The height to add to the land height where the center of the zone is located.

      + +
    • +
    +

    Return value

    + +

    Core.Point#POINT_VEC2: +The PointVec2 of the zone.

    + +
    +
    +
    +
    + + +ZONE_BASE:GetPointVec3(Height) + +
    +
    + +

    Returns a Point#POINT_VEC3 of the zone.

    + +

    Parameter

    +
      +
    • + +

      Dcs.DCSTypes#Distance Height : +The height to add to the land height where the center of the zone is located.

      + +
    • +
    +

    Return value

    + +

    Core.Point#POINT_VEC3: +The PointVec3 of the zone.

    + +
    +
    +
    +
    + ZONE_BASE:GetRandomPointVec2() @@ -979,6 +1059,24 @@ The PointVec2 coordinates.

    + +ZONE_BASE:GetRandomPointVec3() + +
    +
    + +

    Define a random Point#POINT_VEC3 within the zone.

    + +

    Return value

    + +

    Core.Point#POINT_VEC3: +The PointVec3 coordinates.

    + +
    +
    +
    +
    + ZONE_BASE:GetRandomVec2() @@ -1010,6 +1108,33 @@ The Vec2 coordinates.

    #nil:

    + +
    +
    +
    + + +ZONE_BASE:GetVec3(Height) + +
    +
    + +

    Returns the DCSTypes#Vec3 of the zone.

    + +

    Parameter

    +
      +
    • + +

      Dcs.DCSTypes#Distance Height : +The height to add to the land height where the center of the zone is located.

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTypes#Vec3: +The Vec3 of the zone.

    +
    @@ -1448,13 +1573,22 @@ self

    -ZONE_POLYGON_BASE:BoundZone() +ZONE_POLYGON_BASE:BoundZone(UnBound)

    Smokes the zone boundaries in a color.

    +

    Parameter

    +
      +
    • + +

      #boolean UnBound : +If true, the tyres will be destroyed.

      + +
    • +

    Return value

    #ZONEPOLYGONBASE: @@ -1690,20 +1824,31 @@ self

    -ZONE_RADIUS:BoundZone(Points) +ZONE_RADIUS:BoundZone(Points, UnBound, CountryID)

    Bounds the zone with tires.

    -

    Parameter

    +

    Parameters

    • #number Points : (optional) The amount of points in the circle.

      +
    • +
    • + +

      #boolean UnBound : +If true the tyres will be destroyed.

      + +
    • +
    • + +

      CountryID :

      +

    Return value

    diff --git a/docs/Documentation/index.html b/docs/Documentation/index.html index 124efee34..2bda7fee8 100644 --- a/docs/Documentation/index.html +++ b/docs/Documentation/index.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • @@ -252,12 +252,6 @@ are design patterns allowing efficient (long-lasting) processes and workflows.Identifiable
    - - - - @@ -282,6 +276,12 @@ are design patterns allowing efficient (long-lasting) processes and workflows.Mission + + + + @@ -335,7 +335,7 @@ are design patterns allowing efficient (long-lasting) processes and workflows. @@ -402,19 +402,19 @@ and creates a CSV file logging the scoring events and results for use at team or + + + + - - - - diff --git a/docs/Documentation/routines.html b/docs/Documentation/routines.html index 3c5b6891b..70d5e3c47 100644 --- a/docs/Documentation/routines.html +++ b/docs/Documentation/routines.html @@ -39,11 +39,11 @@
  • Fsm
  • Group
  • Identifiable
  • -
  • MOVEMENT
  • Menu
  • Message
  • MissileTrainer
  • Mission
  • +
  • Movement
  • Object
  • Point
  • Positionable
  • @@ -61,8 +61,8 @@
  • Static
  • Task
  • Task_A2G
  • +
  • Task_A2G_Dispatcher
  • Task_PICKUP
  • -
  • Task_SEAD
  • Unit
  • Utils
  • Zone
  • diff --git a/docs/Presentations/SCHEDULER/Dia1.JPG b/docs/Presentations/SCHEDULER/Dia1.JPG new file mode 100644 index 000000000..81cfd2575 Binary files /dev/null and b/docs/Presentations/SCHEDULER/Dia1.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia1.JPG b/docs/Presentations/TASK_DISPATCHER/Dia1.JPG new file mode 100644 index 000000000..2501578e9 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia1.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia10.JPG b/docs/Presentations/TASK_DISPATCHER/Dia10.JPG new file mode 100644 index 000000000..55f545f95 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia10.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia11.JPG b/docs/Presentations/TASK_DISPATCHER/Dia11.JPG new file mode 100644 index 000000000..37f538608 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia11.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia12.JPG b/docs/Presentations/TASK_DISPATCHER/Dia12.JPG new file mode 100644 index 000000000..d69f65f24 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia12.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia13.JPG b/docs/Presentations/TASK_DISPATCHER/Dia13.JPG new file mode 100644 index 000000000..4c4e25a5a Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia13.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia14.JPG b/docs/Presentations/TASK_DISPATCHER/Dia14.JPG new file mode 100644 index 000000000..99f45e145 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia14.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia15.JPG b/docs/Presentations/TASK_DISPATCHER/Dia15.JPG new file mode 100644 index 000000000..09a00e4eb Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia15.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia16.JPG b/docs/Presentations/TASK_DISPATCHER/Dia16.JPG new file mode 100644 index 000000000..c74c69956 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia16.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia17.JPG b/docs/Presentations/TASK_DISPATCHER/Dia17.JPG new file mode 100644 index 000000000..ab23e55da Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia17.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia18.JPG b/docs/Presentations/TASK_DISPATCHER/Dia18.JPG new file mode 100644 index 000000000..9ad40904a Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia18.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia2.JPG b/docs/Presentations/TASK_DISPATCHER/Dia2.JPG new file mode 100644 index 000000000..edfc790e5 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia2.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia3.JPG b/docs/Presentations/TASK_DISPATCHER/Dia3.JPG new file mode 100644 index 000000000..2e129761a Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia3.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia4.JPG b/docs/Presentations/TASK_DISPATCHER/Dia4.JPG new file mode 100644 index 000000000..6adebc4f2 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia4.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia5.JPG b/docs/Presentations/TASK_DISPATCHER/Dia5.JPG new file mode 100644 index 000000000..057a68877 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia5.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia6.JPG b/docs/Presentations/TASK_DISPATCHER/Dia6.JPG new file mode 100644 index 000000000..9387e30e8 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia6.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia7.JPG b/docs/Presentations/TASK_DISPATCHER/Dia7.JPG new file mode 100644 index 000000000..66adb011f Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia7.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia8.JPG b/docs/Presentations/TASK_DISPATCHER/Dia8.JPG new file mode 100644 index 000000000..cf0b93e30 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia8.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia9.JPG b/docs/Presentations/TASK_DISPATCHER/Dia9.JPG new file mode 100644 index 000000000..49335ef21 Binary files /dev/null and b/docs/Presentations/TASK_DISPATCHER/Dia9.JPG differ diff --git a/docs/Presentations/ZONE/Dia1.JPG b/docs/Presentations/ZONE/Dia1.JPG new file mode 100644 index 000000000..ca4dd2b3f Binary files /dev/null and b/docs/Presentations/ZONE/Dia1.JPG differ
    ZONE_RADIUS:BoundZone(Points)ZONE_RADIUS:BoundZone(Points, UnBound, CountryID)

    Bounds the zone with tires.

    This module contains the IDENTIFIABLE class.

    -
    MOVEMENT -

    Limit the simultaneous movement of Groups within a running Mission.

    A MISSION is the main owner of a Mission orchestration within MOOSE .

    +
    Movement +

    Limit the simultaneous movement of Groups within a running Mission.

    Scheduler -

    This module contains the SCHEDULER class.

    +

    Core - SCHEDULER prepares and handles the execution of functions over scheduled time (intervals).

    Task_A2G -

    (AI) (SP) (MP) Tasking for Air to Ground Processes.

    +

    This module contains the TASK_A2G classes.

    +
    Task_A2G_Dispatcher +

    Tasking - The TASKA2GDISPATCHER creates and manages player TASK_A2G tasks based on detected targets.

    Task_PICKUP

    This module contains the TASK_PICKUP classes.

    -
    Task_SEAD -

    This module contains the TASK_SEAD classes.