From 0af5e1428b7d7b8c14ed05b0c1cc3fe5f954a0ce Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 30 May 2017 19:38:56 +0200 Subject: [PATCH] Progress --- Moose Development/Moose/AI/AI_A2A.lua | 412 +++++++++++++ Moose Development/Moose/AI/AI_A2A_Cap.lua | 502 ++++++++++++++++ .../Moose/AI/AI_A2A_Dispatcher.lua | 477 +++++++++++++++ Moose Development/Moose/AI/AI_A2A_Patrol.lua | 427 +++++++++++++ Moose Development/Moose/AI/AI_INTERCEPT.lua | 565 ++++++++++++++++++ Moose Development/Moose/AI/AI_Patrol.lua | 1 + Moose Development/Moose/AI/AI_SWEEP.lua | 565 ++++++++++++++++++ .../Moose/Core/ScheduleDispatcher.lua | 6 +- .../Moose/Functional/Detection.lua | 2 +- .../Moose/Tasking/DetectionManager.lua | 53 ++ Moose Mission Setup/Moose.files | 4 + Moose Mission Setup/Moose.lua | 6 +- 12 files changed, 3015 insertions(+), 5 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_A2A.lua create mode 100644 Moose Development/Moose/AI/AI_A2A_Cap.lua create mode 100644 Moose Development/Moose/AI/AI_A2A_Dispatcher.lua create mode 100644 Moose Development/Moose/AI/AI_A2A_Patrol.lua create mode 100644 Moose Development/Moose/AI/AI_INTERCEPT.lua create mode 100644 Moose Development/Moose/AI/AI_SWEEP.lua diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua new file mode 100644 index 000000000..282bee9e4 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -0,0 +1,412 @@ +--- **AI** -- **AI A2A Air Patrolling or Staging.** +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ==== +-- +-- @module AI_A2A + + +--- @type AI_A2A +-- @extends Core.Fsm#FSM_CONTROLLABLE + +--- # AI_A2A class, extends @{Fsm#FSM_CONTROLLABLE} +-- +-- The AI_A2A class implements the core functions to operate an AI @{Group} A2A tasking. +-- +-- +-- ## AI_A2A constructor +-- +-- * @{#AI_A2A.New}(): Creates a new AI_A2A object. +-- +-- ## 2. AI_A2A is a FSM +-- +-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- +-- ### 2.1. AI_A2A States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Returning** ( Group ): The AI is returning to Base. +-- * **Stopped** ( Group ): The process is stopped. +-- * **Crashed** ( Group ): The AI has crashed or is dead. +-- +-- ### 2.2. AI_A2A Events +-- +-- * **Start** ( Group ): Start the process. +-- * **Stop** ( Group ): Stop the process. +-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone. +-- * **RTB** ( Group ): Route the AI to the home base. +-- * **Detect** ( Group ): The AI is detecting targets. +-- * **Detected** ( Group ): The AI has detected new targets. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set or Get the AI controllable +-- +-- * @{#AI_A2A.SetControllable}(): Set the AIControllable. +-- * @{#AI_A2A.GetControllable}(): Get the AIControllable. +-- +-- @field #AI_A2A +AI_A2A = { + ClassName = "AI_A2A", +} + +--- Creates a new AI_A2A object +-- @param #AI_A2A self +-- @param Wrapper.Group#GROUP AIGroup The GROUP object to receive the A2A Process. +-- @return #AI_A2A +function AI_A2A:New( AIGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_A2A + + self:SetControllable( AIGroup ) + + self:ManageFuel( .2, 60 ) + self:ManageDamage( 1 ) + + self:SetStartState( "Stopped" ) + + self:AddTransition( "*", "Stop", "Stopped" ) + +--- OnLeave Transition Handler for State Stopped. +-- @function [parent=#AI_A2A] OnLeaveStopped +-- @param #AI_A2A 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=#AI_A2A] OnEnterStopped +-- @param #AI_A2A 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. + +--- OnBefore Transition Handler for Event Stop. +-- @function [parent=#AI_A2A] OnBeforeStop +-- @param #AI_A2A 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=#AI_A2A] OnAfterStop +-- @param #AI_A2A 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=#AI_A2A] Stop +-- @param #AI_A2A self + +--- Asynchronous Event Trigger for Event Stop. +-- @function [parent=#AI_A2A] __Stop +-- @param #AI_A2A self +-- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A. + +--- OnBefore Transition Handler for Event Status. +-- @function [parent=#AI_A2A] OnBeforeStatus +-- @param #AI_A2A 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 Status. +-- @function [parent=#AI_A2A] OnAfterStatus +-- @param #AI_A2A 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 Status. +-- @function [parent=#AI_A2A] Status +-- @param #AI_A2A self + +--- Asynchronous Event Trigger for Event Status. +-- @function [parent=#AI_A2A] __Status +-- @param #AI_A2A self +-- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "RTB", "Returning" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A. + +--- OnBefore Transition Handler for Event RTB. +-- @function [parent=#AI_A2A] OnBeforeRTB +-- @param #AI_A2A 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 RTB. +-- @function [parent=#AI_A2A] OnAfterRTB +-- @param #AI_A2A 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 RTB. +-- @function [parent=#AI_A2A] RTB +-- @param #AI_A2A self + +--- Asynchronous Event Trigger for Event RTB. +-- @function [parent=#AI_A2A] __RTB +-- @param #AI_A2A self +-- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Returning. +-- @function [parent=#AI_A2A] OnLeaveReturning +-- @param #AI_A2A 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 Returning. +-- @function [parent=#AI_A2A] OnEnterReturning +-- @param #AI_A2A 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. + + self:AddTransition( "*", "Eject", "*" ) + self:AddTransition( "*", "Crash", "Crashed" ) + self:AddTransition( "*", "PilotDead", "*" ) + + return self +end + + + + +--- Sets (modifies) the minimum and maximum speed of the patrol. +-- @param #AI_A2A self +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @return #AI_A2A self +function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) + self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) + + self.PatrolMinSpeed = PatrolMinSpeed + self.PatrolMaxSpeed = PatrolMaxSpeed +end + + +--- Sets the floor and ceiling altitude of the patrol. +-- @param #AI_A2A self +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @return #AI_A2A self +function AI_A2A:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) + self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) + + self.PatrolFloorAltitude = PatrolFloorAltitude + self.PatrolCeilingAltitude = PatrolCeilingAltitude +end + + +--- Set the status checking off. +-- @param #AI_A2A self +-- @return #AI_A2A self +function AI_A2A:SetStatusOff() + self:F2() + + self.CheckStatus = false +end + + +--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. +-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A. +-- Once the time is finished, the old AI will return to the base. +-- @param #AI_A2A self +-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. +-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. +-- @return #AI_A2A self +function AI_A2A:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime ) + + self.PatrolManageFuel = true + self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage + self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime + + return self +end + +--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. +-- However, damage cannot be foreseen early on. +-- Therefore, when the damage treshold is reached, +-- the AI will return immediately to the home base (RTB). +-- Note that for groups, the average damage of the complete group will be calculated. +-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. +-- @param #AI_A2A self +-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. +-- @return #AI_A2A self +function AI_A2A:ManageDamage( PatrolDamageTreshold ) + + self.PatrolManageDamage = true + self.PatrolDamageTreshold = PatrolDamageTreshold + + return self +end + +--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. +-- @param #AI_A2A self +-- @return #AI_A2A 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 AI_A2A:onafterStart( Controllable, From, Event, To ) + self:F2() + + self:__Status( 60 ) -- Check status status every 30 seconds. + + self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead ) + self:HandleEvent( EVENTS.Crash, self.OnCrash ) + self:HandleEvent( EVENTS.Ejection, self.OnEjection ) + + Controllable:OptionROEHoldFire() + Controllable:OptionROTVertical() +end + + + +--- @param #AI_A2A self +function AI_A2A:onbeforeStatus() + + return self.CheckStatus +end + +--- @param #AI_A2A self +function AI_A2A:onafterStatus() + self:F2() + + if self.Controllable and self.Controllable:IsAlive() then + + local RTB = false + + local Fuel = self.Controllable:GetUnit(1):GetFuel() + if Fuel < self.PatrolFuelTresholdPercentage then + self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) + local OldAIControllable = self.Controllable + local AIControllableTemplate = self.Controllable:GetTemplate() + + local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) + local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) ) + OldAIControllable:SetTask( TimedOrbitTask, 10 ) + + RTB = true + else + end + + -- TODO: Check GROUP damage function. + local Damage = self.Controllable:GetLife() + if Damage <= self.PatrolDamageTreshold then + self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" ) + RTB = true + end + + if RTB == true then + self:RTB() + else + self:__Status( 60 ) -- Execute the Patrol event after 30 seconds. + end + end +end + + +--- @param #AI_A2A self +function AI_A2A:onafterRTB() + self:F2() + + if self.Controllable and self.Controllable:IsAlive() then + + self.CheckStatus = false + + local PatrolRoute = {} + + --- Calculate the current route point. + local CurrentVec2 = self.Controllable:GetVec2() + + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToPatrolZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToPatrolZoneSpeed, + true + ) + + PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + self.Controllable:WayPointInitialize( PatrolRoute ) + + --- NOW ROUTE THE GROUP! + self.Controllable:WayPointExecute( 1, 1 ) + + end + +end + + +--- @param #AI_A2A self +function AI_A2A:onafterDead() + self:SetStatusOff() +end + + +--- @param #AI_A2A self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A:OnCrash( EventData ) + + if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then + self:E( self.Controllable:GetUnits() ) + if #self.Controllable:GetUnits() == 1 then + self:__Crash( 1, EventData ) + end + end +end + +--- @param #AI_A2A self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A:OnEjection( EventData ) + + if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then + self:__Eject( 1, EventData ) + end +end + +--- @param #AI_A2A self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A:OnPilotDead( EventData ) + + if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then + self:__PilotDead( 1, EventData ) + end +end diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua new file mode 100644 index 000000000..d86c6646e --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -0,0 +1,502 @@ +--- **AI** -- **Execute Combat Air Patrol (CAP).** +-- +-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) +-- +-- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_A2A_CAP}: Perform a CAP in a zone. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ==== +-- +-- @module AI_A2A_Cap + + +--- @type AI_A2A_CAP +-- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL + + +--- # AI_A2A_CAP class, extends @{AI_CAP#AI_PATROL_ZONE} +-- +-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} +-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. +-- +-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) +-- +-- The AI_A2A_CAP is assigned a @{Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_CAP\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_CAP\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia9.JPG) +-- +-- When enemies are detected, the AI will automatically engage the enemy. +-- +-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) +-- +-- ## 1. AI_A2A_CAP constructor +-- +-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object. +-- +-- ## 2. AI_A2A_CAP is a FSM +-- +-- ![Process](..\Presentations\AI_CAP\Dia2.JPG) +-- +-- ### 2.1 AI_A2A_CAP States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Engaging** ( Group ): The AI is engaging the bogeys. +-- * **Returning** ( Group ): The AI is returning to Base.. +-- +-- ### 2.2 AI_A2A_CAP Events +-- +-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys. +-- * **@{#AI_A2A_CAP.Abort}**: Aborts the engagement and return patrolling in the patrol zone. +-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Unit}. +-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set the Range of Engagement +-- +-- ![Range](..\Presentations\AI_CAP\Dia11.JPG) +-- +-- An optional range can be set in meters, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- The range can be beyond or smaller than the range of the Patrol Zone. +-- The range is applied at the position of the AI. +-- Use the method @{AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range. +-- +-- ## 4. Set the Zone of Engagement +-- +-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) +-- +-- An optional @{Zone} can be set, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- Use the method @{AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone. +-- +-- === +-- +-- @field #AI_A2A_CAP +AI_A2A_CAP = { + ClassName = "AI_A2A_CAP", +} + + + +--- Creates a new AI_A2A_CAP object +-- @param #AI_A2A_CAP self +-- @param Wrapper.Group#GROUP AIGroup +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_A2A_CAP +function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP + + self.Accomplished = false + self.Engaging = false + + self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_A2A_CAP] OnBeforeEngage + -- @param #AI_A2A_CAP 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 Engage. + -- @function [parent=#AI_A2A_CAP] OnAfterEngage + -- @param #AI_A2A_CAP 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 Engage. + -- @function [parent=#AI_A2A_CAP] Engage + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_A2A_CAP] __Engage + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_A2A_CAP] OnLeaveEngaging +-- @param #AI_A2A_CAP 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 Engaging. +-- @function [parent=#AI_A2A_CAP] OnEnterEngaging +-- @param #AI_A2A_CAP 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. + + self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_A2A_CAP] OnBeforeFired + -- @param #AI_A2A_CAP 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 Fired. + -- @function [parent=#AI_A2A_CAP] OnAfterFired + -- @param #AI_A2A_CAP 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 Fired. + -- @function [parent=#AI_A2A_CAP] Fired + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_A2A_CAP] __Fired + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_A2A_CAP] OnBeforeDestroy + -- @param #AI_A2A_CAP 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 Destroy. + -- @function [parent=#AI_A2A_CAP] OnAfterDestroy + -- @param #AI_A2A_CAP 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 Destroy. + -- @function [parent=#AI_A2A_CAP] Destroy + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_A2A_CAP] __Destroy + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_A2A_CAP] OnBeforeAbort + -- @param #AI_A2A_CAP 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 Abort. + -- @function [parent=#AI_A2A_CAP] OnAfterAbort + -- @param #AI_A2A_CAP 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 Abort. + -- @function [parent=#AI_A2A_CAP] Abort + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_A2A_CAP] __Abort + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_A2A_CAP] OnBeforeAccomplish + -- @param #AI_A2A_CAP 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 Accomplish. + -- @function [parent=#AI_A2A_CAP] OnAfterAccomplish + -- @param #AI_A2A_CAP 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 Accomplish. + -- @function [parent=#AI_A2A_CAP] Accomplish + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_A2A_CAP] __Accomplish + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- Set the Engage Zone which defines where the AI will engage bogies. +-- @param #AI_A2A_CAP self +-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. +-- @return #AI_A2A_CAP self +function AI_A2A_CAP:SetEngageZone( EngageZone ) + self:F2() + + if EngageZone then + self.EngageZone = EngageZone + else + self.EngageZone = nil + end +end + +--- Set the Engage Range when the AI will engage with airborne enemies. +-- @param #AI_A2A_CAP self +-- @param #number EngageRange The Engage Range. +-- @return #AI_A2A_CAP self +function AI_A2A_CAP:SetEngageRange( EngageRange ) + self:F2() + + if EngageRange then + self.EngageRange = EngageRange + else + self.EngageRange = nil + end +end + +--- onafter State Transition for Event Patrol. +-- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group 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 AI_A2A_CAP:onafterPatrol( AIGroup, From, Event, To ) + + -- Call the parent Start event handler + self:GetParent(self).onafterPatrol( self, AIGroup, From, Event, To ) + self:HandleEvent( EVENTS.Dead ) + +end + +-- todo: need to fix this global function + +--- @param Wrapper.Controllable#CONTROLLABLE AIControllable +function _NewEngageCapRoute( AIControllable ) + + AIControllable:T( "NewEngageRoute" ) + local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_A2A_CAP + EngageZone:__Engage( 1 ) +end + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup 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 AI_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group 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 AI_A2A_CAP:onafterAbort( AIGroup, From, Event, To ) + AIGroup:ClearTasks() + self:__Route( 1 ) +end + + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup 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 AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackUnits ) + + self:F( { AIGroup, From, Event, To, AttackUnits} ) + + self.AttackUnits = AttackUnits or self.AttackUnits + + if AIGroup:IsAlive() then + + local EngageRoute = {} + + --- Calculate the current route point. + local CurrentVec2 = self.Controllable:GetVec2() + + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToEngageZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToEngageZoneSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = CurrentRoutePoint + + + --- Find a random 2D point in PatrolZone. + local ToTargetVec2 = self.PatrolZone:GetRandomVec2() + self:T2( ToTargetVec2 ) + + --- Define Speed and Altitude. + local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) + + --- Obtain a 3D @{Point} from the 2D point + altitude. + local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + + AIGroup:OptionROEOpenFire() + AIGroup:OptionROTPassiveDefense() + + local AttackTasks = {} + + for AttackUnitID, AttackUnit in pairs( self.AttackUnits ) do + local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT + self:T( { AttackUnit, AttackUnit:IsAlive(), AttackUnit:IsAir() } ) + if AttackUnit:IsAlive() and AttackUnit:IsAir() then + AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit ) + end + end + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + self.Controllable:WayPointInitialize( EngageRoute ) + + + if #AttackTasks == 0 then + self:E("No targets found -> Going back to Patrolling") + self:__Abort( 1 ) + self:__Route( 1 ) + else + EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks ) + + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + self.Controllable:SetState( self.Controllable, "EngageZone", self ) + + self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" ) + end + + --- NOW ROUTE THE GROUP! + self.Controllable:WayPointExecute( 1, 2 ) + + end +end + +--- @param #AI_A2A_CAP 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 AI_A2A_CAP:onafterAccomplish( Controllable, From, Event, To ) + self.Accomplished = true + self:SetDetectionOff() +end + +--- @param #AI_A2A_CAP 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. +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_CAP:onafterDestroy( Controllable, From, Event, To, EventData ) + + if EventData.IniUnit then + self.AttackUnits[EventData.IniUnit] = nil + end +end + +--- @param #AI_A2A_CAP self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_CAP:OnEventDead( EventData ) + self:F( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then + self:__Destroy( 1, EventData ) + end + end +end diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua new file mode 100644 index 000000000..8a36d2f75 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -0,0 +1,477 @@ +--- **Tasking** - The AI_A2A_DISPATCHER creates and manages AI_A2A tasks based on detected targets. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module AI_A2A_Dispatcher + +do -- AI_A2A_DISPATCHER + + --- AI_A2A_DISPATCHER class. + -- @type AI_A2A_DISPATCHER + -- @extends Tasking.DetectionManager#DETECTION_MANAGER + + --- # AI_A2A_DISPATCHER class, extends @{Tasking#DETECTION_MANAGER} + -- + -- The @{#AI_A2A_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. + -- The EWR will detect units, will group them, and will dispatch AI_A2A 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: + -- + -- * **INTERCEPT**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range. + -- * **SWEEP**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range. + -- * **CAP**: Is created during the mission. Targets are patrolling the zones outlined. + -- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne patrolling and in range, that will receive this task. + -- + -- Other task types will follow... + -- + -- # AI_A2A_DISPATCHER constructor: + -- -------------------------------------- + -- The @{#AI_A2A_DISPATCHER.New}() method creates a new AI_A2A_DISPATCHER instance. + -- + -- @field #AI_A2A_DISPATCHER + AI_A2A_DISPATCHER = { + ClassName = "AI_A2A_DISPATCHER", + Mission = nil, + Detection = nil, + Tasks = {}, + SweepZones = {}, + } + + + --- AI_A2A_DISPATCHER constructor. + -- @param #AI_A2A_DISPATCHER self + -- @param #string The Squadron Name. This name is used to control the squadron settings in the A2A dispatcher, and also in communication to human players. + -- @param Functional.Spawn#SPAWN SpawnA2A The SPAWN object to create groups that will receive the dispatched A2A Tasks. These spawn objects MUST contain all AI units. + -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks. + -- @return #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:New( Detection ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( nil, Detection ) ) -- #AI_A2A_DISPATCHER + + self.Detection = Detection + + -- This table models the currently alive AI_A2A processes. + self.AI_A2A = self.AI_A2A or {} + + + -- TODO: Check detection through radar. + self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) + --self.Detection:InitDetectRadar( true ) + self.Detection:SetDetectionInterval( 30 ) + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterAssign + -- @param #AI_A2A_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2A#AI_A2A Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:AddTransition( "*", "CAP", "*" ) + + --- CAP Handler OnBefore for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeCAP + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- CAP Handler OnAfter for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterCAP + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- CAP Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] CAP + -- @param #AI_A2A_DISPATCHER self + + --- CAP Asynchronous Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] __CAP + -- @param #AI_A2A_DISPATCHER self + -- @param #number Delay + + + + + self:__Start( 5 ) + + return self + end + + function AI_A2A_DISPATCHER:onafterCAP() + for CAPName, CAP in pairs( self.AI_A2A["CAP"] ) do + local AIGroup = CAP.Spawn:Spawn() + self:F( { AIGroup = AIGroup:GetName() } ) + if AIGroup then + CAP.Fsm = CAP.Fsm or {} + CAP.Fsm[AIGroup] = AI_A2A_CAP:New( AIGroup, CAP.Zone, CAP.FloorAltitude, CAP.CeilingAltitude, CAP.MinSpeed, CAP.MaxSpeed, CAP.AltType ) + CAP.Fsm[AIGroup]:__Patrol( 2 ) + end + end + end + + function AI_A2A_DISPATCHER:SetCAP( CAPName, CAPSpawn, CAPZone, CAPFloorAltitude, CAPCeilingAltitude, CAPMinSpeed, CAPMaxSpeed, CAPAltType ) + + self.AI_A2A["CAP"] = self.AI_A2A["CAP"] or {} + self.AI_A2A["CAP"][CAPName] = self.AI_A2A["CAP"][CAPName] or {} + + local CAP = self.AI_A2A["CAP"][CAPName] + CAP.Name = CAPName + CAP.Spawn = CAPSpawn -- Funtional.Spawn#SPAWN + CAP.Zone = CAPZone + CAP.FloorAltitude = CAPFloorAltitude + CAP.CeilingAltitude = CAPCeilingAltitude + CAP.MinSpeed = CAPMinSpeed + CAP.MaxSpeed = CAPMaxSpeed + CAP.AltType = CAPAltType + + end + + function AI_A2A_DISPATCHER:SetINTERCEPT( INTERCEPTName, INTERCEPTSpawn, CAPZone, CAPFloorAltitude, CAPCeilingAltitude, CAPMinSpeed, CAPMaxSpeed, CAPAltType ) + + self.AI_A2A["INTERCEPT"] = self.AI_A2A["INTERCEPT"] or {} + self.AI_A2A["INTERCEPT"][INTERCEPTName] = self.AI_A2A["INTERCEPT"][INTERCEPTName] or {} + + local INTERCEPT = self.AI_A2A["INTERCEPT"][INTERCEPTName] + INTERCEPT.Name = INTERCEPTName + INTERCEPT.Spawn = INTERCEPTSpawn + INTERCEPT.Zone = CAPZone + INTERCEPT.FloorAltitude = CAPFloorAltitude + INTERCEPT.CeilingAltitude = CAPCeilingAltitude + INTERCEPT.MinSpeed = CAPMinSpeed + INTERCEPT.MaxSpeed = CAPMaxSpeed + INTERCEPT.AltType = CAPAltType + end + + --- Creates an INTERCEPT task when there are targets for it. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function AI_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + -- Check if there is at least one UNIT in the DetectedSet is visible. + + if DetectedItem.IsDetected == true then + + -- Here we're doing something advanced... We're copying the DetectedSet. + 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 an SWEEP task when there are targets for it. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function AI_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + + if DetectedItem.IsDetected == false then + + -- Here we're doing something advanced... We're copying the DetectedSet. + 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 an ENGAGE task when there are human friendlies airborne near the targets. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function AI_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local ResultEngage = false + local ResultAIGroup = nil + local ResultCAPName = nil + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + for CAPName, CAP in pairs( self.AI_A2A["CAP"] ) do + for AIGroup, Fsm in pairs( CAP.Fsm ) do + self:F( { CAP = CAPName } ) + self:F( { CAPFsm = CAP.Fsm } ) + self:F( { AIGroup = tostring( AIGroup ), AIGroup:GetName() } ) + local CAPFsm = CAP.Fsm[AIGroup] + if CAPFsm then + self:F( { CAPState = CAPFsm:GetState() } ) + if CAPFsm:Is( "Patrolling" ) then + ResultEngage = true + ResultAIGroup = AIGroup + ResultCAPName = CAPName + end + end + break + end + end + + + -- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone. + if ResultEngage == true and DetectedItem.IsDetected == true then + + self:E("ENGAGE") + return DetectedSet, ResultCAPName, ResultAIGroup + 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 #AI_A2A_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission + -- @param Tasking.Task#TASK Task + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @param #boolean DetectedItemID + -- @param #boolean DetectedItemChange + -- @return Tasking.Task#TASK + function AI_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged ) + + if Task then + + if Task:IsStatePlanned() then + local TaskName = Task:GetName() + local TaskType = TaskName:match( "(%u+)%.%d+" ) + + self:T2( { TaskType = TaskType } ) + + local Remove = false + + local IsPlayers = Detection:IsPlayersNearBy( DetectedItem ) + if TaskType == "ENGAGE" then + if IsPlayers == false then + Remove = true + end + end + + if TaskType == "INTERCEPT" then + if IsPlayers == true then + Remove = true + end + if DetectedItem.IsDetected == false then + Remove = true + end + end + + if TaskType == "SWEEP" then + if DetectedItem.IsDetected == true then + Remove = true + end + end + + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + --DetectedSet:Flush() + --self:E( { DetectedSetCount = DetectedSet:Count() } ) + if DetectedSet:Count() == 0 then + Remove = true + end + + if DetectedItemChanged == true or Remove then + --self:E( "Removing Tasking: " .. Task:GetTaskName() ) + Mission:RemoveTask( Task ) + self.Tasks[DetectedItemIndex] = nil + end + end + end + + return Task + end + + --- Calculates which friendlies are nearby the area + -- @param #AI_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function AI_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) + + local DetectedSet = DetectedItem.Set + local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem ) + + local FriendlyTypes = {} + local FriendliesCount = 0 + + if FriendlyUnitsNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do + local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT + if FriendlyUnit:IsAirPlane() then + local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() + FriendliesCount = FriendliesCount + 1 + local FriendlyType = FriendlyUnit:GetTypeName() + FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1 + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + --self:E( { FriendliesCount = FriendliesCount } ) + + local FriendlyTypesReport = REPORT:New() + + if FriendliesCount > 0 then + for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do + FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) ) + end + else + FriendlyTypesReport:Add( "-" ) + end + + + return FriendliesCount, FriendlyTypesReport + end + + --- Calculates which HUMAN friendlies are nearby the area + -- @param #AI_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) + + local DetectedSet = DetectedItem.Set + local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) + + local PlayerTypes = {} + local PlayersCount = 0 + + if PlayersNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do + local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + --self:E( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) + if PlayerUnit:IsAirPlane() and PlayerName ~= nil then + local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() + PlayersCount = PlayersCount + 1 + local PlayerType = PlayerUnit:GetTypeName() + PlayerTypes[PlayerName] = PlayerType + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + --self:E( { PlayersCount = PlayersCount } ) + + local PlayerTypesReport = REPORT:New() + + if PlayersCount > 0 then + for PlayerName, PlayerType in pairs( PlayerTypes ) do + PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) ) + end + else + PlayerTypesReport:Add( "-" ) + end + + + return PlayersCount, PlayerTypesReport + end + + --- Calculates which AI friendlies are nearby the area + -- @param #AI_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function AI_A2A_DISPATCHER:GetAIFriendliesNearBy( DetectedItem ) + + local FriendliesNearBy = self.Detection:GetFriendliesNearBy( DetectedItem ) + + return FriendliesNearBy + end + + + --- Assigns A2A AI Tasks in relation to the detected items. + -- @param #AI_A2A_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 AI_A2A_DISPATCHER:ProcessDetected( Detection ) + self:E() + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local TaskReport = REPORT:New() + +-- -- Checking the Process queue for the dispatcher, and removing any obsolete task! +-- for AI_A2A_Index, AI_A2A_Process in pairs( self.AI_A2A_Processes ) do +-- +-- local AI_A2A_Process = AI_A2A_Process +-- if AI_A2A_Process:IsStatePlanned() then +-- local DetectedItem = Detection:GetDetectedItem( AI_A2A_Index ) +-- if not DetectedItem then +-- self.AI_A2A_Process[AI_A2A_Index] = nil +-- end +-- end +-- end + + -- Now that all obsolete tasks are removed, loop through the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do + + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedCount = DetectedSet:Count() + local DetectedZone = DetectedItem.Zone + --self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + --DetectedSet:Flush() + + local DetectedID = DetectedItem.ID + local AI_A2A_Index = DetectedItem.Index + local DetectedItemChanged = DetectedItem.Changed + +-- local AI_A2A_Process = self.AI_A2A_Processes[AI_A2A_Index] +-- AI_A2A_Process = self:EvaluateRemoveTask( AI_A2A_Process, Detection, DetectedItem, AI_A2A_Index, DetectedItemChanged ) -- Task will be removed if it is planned and changed. + + -- Evaluate A2A_Action + if DetectedCount > 0 then + local TargetSetUnit, CAPName, AIGroup = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + if TargetSetUnit then + local CAP = self.AI_A2A["CAP"][CAPName] + CAP.Fsm[AIGroup]:__Engage( 1, TargetSetUnit ) + end + end + end + + return true + end + +end diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua new file mode 100644 index 000000000..33a63f3c8 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -0,0 +1,427 @@ +--- **AI** -- **Air Patrolling or Staging.** +-- +-- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) +-- +-- === +-- +-- AI PATROL classes makes AI Controllables execute an Patrol. +-- +-- There are the following types of PATROL classes defined: +-- +-- * @{#AI_A2A_PATROL}: Perform a PATROL in a zone. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling) +-- +-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ==== +-- +-- @module AI_A2A_Patrol + + +--- @type AI_A2A_PATROL +-- @extends AI.AI_A2A#AI_A2A + +--- # AI_A2A_PATROL class, extends @{Fsm#FSM_CONTROLLABLE} +-- +-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) +-- +-- The AI_A2A_PATROL is assigned a @{Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia9.JPG) +-- +---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or +-- use derived AI_ classes to model AI offensive or defensive behaviour. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) +-- +-- ## 1. AI_A2A_PATROL constructor +-- +-- * @{#AI_A2A_PATROL.New}(): Creates a new AI_A2A_PATROL object. +-- +-- ## 2. AI_A2A_PATROL is a FSM +-- +-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- +-- ### 2.1. AI_A2A_PATROL States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Returning** ( Group ): The AI is returning to Base. +-- * **Stopped** ( Group ): The process is stopped. +-- * **Crashed** ( Group ): The AI has crashed or is dead. +-- +-- ### 2.2. AI_A2A_PATROL Events +-- +-- * **Start** ( Group ): Start the process. +-- * **Stop** ( Group ): Stop the process. +-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone. +-- * **RTB** ( Group ): Route the AI to the home base. +-- * **Detect** ( Group ): The AI is detecting targets. +-- * **Detected** ( Group ): The AI has detected new targets. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set or Get the AI controllable +-- +-- * @{#AI_A2A_PATROL.SetControllable}(): Set the AIControllable. +-- * @{#AI_A2A_PATROL.GetControllable}(): Get the AIControllable. +-- +-- ## 4. Set the Speed and Altitude boundaries of the AI controllable +-- +-- * @{#AI_A2A_PATROL.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol. +-- * @{#AI_A2A_PATROL.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol. +-- +-- ## 5. Manage the detection process of the AI controllable +-- +-- The detection process of the AI controllable can be manipulated. +-- Detection requires an amount of CPU power, which has an impact on your mission performance. +-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set. +-- +-- * @{#AI_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets. +-- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. +-- +-- The detection frequency can be set with @{#AI_A2A_PATROL.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. +-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. +-- +-- The detection can be filtered to potential targets in a specific zone. +-- Use the method @{#AI_A2A_PATROL.SetDetectionZone}() to set the zone where targets need to be detected. +-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected +-- according the weather conditions. +-- +-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL +-- +-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. +-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, +-- while a new AI is targetted to the AI_A2A_PATROL. +-- Once the time is finished, the old AI will return to the base. +-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place. +-- +-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL +-- +-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. +-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). +-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place. +-- +-- === +-- +-- @field #AI_A2A_PATROL +AI_A2A_PATROL = { + ClassName = "AI_A2A_PATROL", +} + +--- Creates a new AI_A2A_PATROL object +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Group#GROUP AIGroup +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_A2A_PATROL self +-- @usage +-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h. +-- PatrolZone = ZONE:New( 'PatrolZone' ) +-- PatrolSpawn = SPAWN:New( 'Patrol Group' ) +-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 ) +function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_PATROL + + self.PatrolZone = PatrolZone + self.PatrolFloorAltitude = PatrolFloorAltitude + self.PatrolCeilingAltitude = PatrolCeilingAltitude + self.PatrolMinSpeed = PatrolMinSpeed + self.PatrolMaxSpeed = PatrolMaxSpeed + + -- defafult PatrolAltType to "RADIO" if not specified + self.PatrolAltType = PatrolAltType or "RADIO" + + self:AddTransition( "Stopped", "Patrol", "Patrolling" ) + +--- OnBefore Transition Handler for Event Patrol. +-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol +-- @param #AI_A2A_PATROL 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 Patrol. +-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol +-- @param #AI_A2A_PATROL 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 Patrol. +-- @function [parent=#AI_A2A_PATROL] Patrol +-- @param #AI_A2A_PATROL self + +--- Asynchronous Event Trigger for Event Patrol. +-- @function [parent=#AI_A2A_PATROL] __Patrol +-- @param #AI_A2A_PATROL self +-- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Patrolling. +-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling +-- @param #AI_A2A_PATROL 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 Patrolling. +-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling +-- @param #AI_A2A_PATROL 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. + + self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL. + +--- OnBefore Transition Handler for Event Route. +-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute +-- @param #AI_A2A_PATROL 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 Route. +-- @function [parent=#AI_A2A_PATROL] OnAfterRoute +-- @param #AI_A2A_PATROL 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 Route. +-- @function [parent=#AI_A2A_PATROL] Route +-- @param #AI_A2A_PATROL self + +--- Asynchronous Event Trigger for Event Route. +-- @function [parent=#AI_A2A_PATROL] __Route +-- @param #AI_A2A_PATROL self +-- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL. + + return self +end + + + + +--- Sets (modifies) the minimum and maximum speed of the patrol. +-- @param #AI_A2A_PATROL self +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @return #AI_A2A_PATROL self +function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) + self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) + + self.PatrolMinSpeed = PatrolMinSpeed + self.PatrolMaxSpeed = PatrolMaxSpeed +end + + + +--- Sets the floor and ceiling altitude of the patrol. +-- @param #AI_A2A_PATROL self +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @return #AI_A2A_PATROL self +function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) + self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) + + self.PatrolFloorAltitude = PatrolFloorAltitude + self.PatrolCeilingAltitude = PatrolCeilingAltitude +end + + +--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. +-- @param #AI_A2A_PATROL self +-- @return #AI_A2A_PATROL 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 AI_A2A_PATROL:onafterPatrol( Controllable, From, Event, To ) + self:F2() + + self:__Route( 1 ) + + self.Controllable:OnReSpawn( + function( PatrolGroup ) + self:E( "ReSpawn" ) + self:__Reset( 1 ) + self:__Route( 5 ) + end + ) +end + + + +--- @param Wrapper.Controllable#CONTROLLABLE AIControllable +-- This statis method is called from the route path within the last task at the last waaypoint of the Controllable. +-- Note that this method is required, as triggers the next route when patrolling for the Controllable. +function AI_A2A_PATROL:_NewPatrolRoute( AIControllable ) + + local PatrolZone = AIControllable:GetState( AIControllable, "PatrolZone" ) -- PatrolCore.Zone#AI_A2A_PATROL + PatrolZone:__Route( 1 ) +end + + +--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. +-- @param #AI_A2A_PATROL 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 AI_A2A_PATROL:onafterRoute( Controllable, From, Event, To ) + + self:F2() + + -- When RTB, don't allow anymore the routing. + if From == "RTB" then + return + end + + + if self.Controllable:IsAlive() then + -- Determine if the AIControllable is within the PatrolZone. + -- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point. + + local PatrolRoute = {} + + -- Calculate the current route point of the controllable as the start point of the route. + -- However, when the controllable is not in the air, + -- the controllable current waypoint is probably the airbase... + -- Thus, if we would take the current waypoint as the startpoint, upon take-off, the controllable flies + -- immediately back to the airbase, and this is not correct. + -- Therefore, when on a runway, get as the current route point a random point within the PatrolZone. + -- This will make the plane fly immediately to the patrol zone. + + if self.Controllable:InAir() == false then + self:E( "Not in the air, finding route path within PatrolZone" ) + local CurrentVec2 = self.Controllable:GetVec2() + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToPatrolZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TakeOffParking, + POINT_VEC3.RoutePointAction.FromParkingArea, + ToPatrolZoneSpeed, + true + ) + PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint + else + self:E( "In the air, finding route path within PatrolZone" ) + local CurrentVec2 = self.Controllable:GetVec2() + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToPatrolZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToPatrolZoneSpeed, + true + ) + PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint + end + + + --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. + + --- Find a random 2D point in PatrolZone. + local ToTargetVec2 = self.PatrolZone:GetRandomVec2() + self:T2( ToTargetVec2 ) + + --- Define Speed and Altitude. + local ToTargetAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) + + --- Obtain a 3D @{Point} from the 2D point + altitude. + local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) + + --- Create a route point of type air. + local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + --self.CoordTest:SpawnFromVec3( ToTargetPointVec3:GetVec3() ) + + --ToTargetPointVec3:SmokeRed() + + PatrolRoute[#PatrolRoute+1] = ToTargetRoutePoint + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + self.Controllable:WayPointInitialize( PatrolRoute ) + + --- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ... + self.Controllable:SetState( self.Controllable, "PatrolZone", self ) + self.Controllable:WayPointFunction( #PatrolRoute, 1, "AI_A2A_PATROL:_NewPatrolRoute" ) + + --- NOW ROUTE THE GROUP! + self.Controllable:WayPointExecute( 1, 2 ) + end + +end + diff --git a/Moose Development/Moose/AI/AI_INTERCEPT.lua b/Moose Development/Moose/AI/AI_INTERCEPT.lua new file mode 100644 index 000000000..1d14674c3 --- /dev/null +++ b/Moose Development/Moose/AI/AI_INTERCEPT.lua @@ -0,0 +1,565 @@ +--- **AI** -- **Execute Combat Air Patrol (CAP).** +-- +-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) +-- +-- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_CAP Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) +-- +-- ### [AI_CAP Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_CAP YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ==== +-- +-- @module AI_Cap + + +--- @type AI_CAP_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + + +--- # AI_CAP_ZONE class, extends @{AI_CAP#AI_PATROL_ZONE} +-- +-- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} +-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. +-- +-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) +-- +-- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_CAP\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_CAP\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia9.JPG) +-- +-- When enemies are detected, the AI will automatically engage the enemy. +-- +-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) +-- +-- ## 1. AI_CAP_ZONE constructor +-- +-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object. +-- +-- ## 2. AI_CAP_ZONE is a FSM +-- +-- ![Process](..\Presentations\AI_CAP\Dia2.JPG) +-- +-- ### 2.1 AI_CAP_ZONE States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Engaging** ( Group ): The AI is engaging the bogeys. +-- * **Returning** ( Group ): The AI is returning to Base.. +-- +-- ### 2.2 AI_CAP_ZONE Events +-- +-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys. +-- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. +-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Unit}. +-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set the Range of Engagement +-- +-- ![Range](..\Presentations\AI_CAP\Dia11.JPG) +-- +-- An optional range can be set in meters, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- The range can be beyond or smaller than the range of the Patrol Zone. +-- The range is applied at the position of the AI. +-- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. +-- +-- ## 4. Set the Zone of Engagement +-- +-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) +-- +-- An optional @{Zone} can be set, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. +-- +-- === +-- +-- @field #AI_CAP_ZONE +AI_CAP_ZONE = { + ClassName = "AI_CAP_ZONE", +} + + + +--- Creates a new AI_CAP_ZONE object +-- @param #AI_CAP_ZONE self +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_CAP_ZONE self +function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAP_ZONE + + self.Accomplished = false + self.Engaging = false + + self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_CAP_ZONE] OnBeforeEngage + -- @param #AI_CAP_ZONE 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 Engage. + -- @function [parent=#AI_CAP_ZONE] OnAfterEngage + -- @param #AI_CAP_ZONE 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 Engage. + -- @function [parent=#AI_CAP_ZONE] Engage + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_CAP_ZONE] __Engage + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_CAP_ZONE] OnLeaveEngaging +-- @param #AI_CAP_ZONE 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 Engaging. +-- @function [parent=#AI_CAP_ZONE] OnEnterEngaging +-- @param #AI_CAP_ZONE 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. + + self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_CAP_ZONE] OnBeforeFired + -- @param #AI_CAP_ZONE 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 Fired. + -- @function [parent=#AI_CAP_ZONE] OnAfterFired + -- @param #AI_CAP_ZONE 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 Fired. + -- @function [parent=#AI_CAP_ZONE] Fired + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_CAP_ZONE] __Fired + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_CAP_ZONE] OnBeforeDestroy + -- @param #AI_CAP_ZONE 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 Destroy. + -- @function [parent=#AI_CAP_ZONE] OnAfterDestroy + -- @param #AI_CAP_ZONE 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 Destroy. + -- @function [parent=#AI_CAP_ZONE] Destroy + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_CAP_ZONE] __Destroy + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_CAP_ZONE] OnBeforeAbort + -- @param #AI_CAP_ZONE 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 Abort. + -- @function [parent=#AI_CAP_ZONE] OnAfterAbort + -- @param #AI_CAP_ZONE 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 Abort. + -- @function [parent=#AI_CAP_ZONE] Abort + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_CAP_ZONE] __Abort + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_CAP_ZONE] OnBeforeAccomplish + -- @param #AI_CAP_ZONE 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 Accomplish. + -- @function [parent=#AI_CAP_ZONE] OnAfterAccomplish + -- @param #AI_CAP_ZONE 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 Accomplish. + -- @function [parent=#AI_CAP_ZONE] Accomplish + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_CAP_ZONE] __Accomplish + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- Set the Engage Zone which defines where the AI will engage bogies. +-- @param #AI_CAP_ZONE self +-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. +-- @return #AI_CAP_ZONE self +function AI_CAP_ZONE:SetEngageZone( EngageZone ) + self:F2() + + if EngageZone then + self.EngageZone = EngageZone + else + self.EngageZone = nil + end +end + +--- Set the Engage Range when the AI will engage with airborne enemies. +-- @param #AI_CAP_ZONE self +-- @param #number EngageRange The Engage Range. +-- @return #AI_CAP_ZONE self +function AI_CAP_ZONE:SetEngageRange( EngageRange ) + self:F2() + + if EngageRange then + self.EngageRange = EngageRange + else + self.EngageRange = nil + end +end + +--- onafter State Transition for Event Start. +-- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterStart( Controllable, From, Event, To ) + + -- Call the parent Start event handler + self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) + self:HandleEvent( EVENTS.Dead ) + +end + +-- todo: need to fix this global function + +--- @param Wrapper.Controllable#CONTROLLABLE AIControllable +function _NewEngageCapRoute( AIControllable ) + + AIControllable:T( "NewEngageRoute" ) + local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE + EngageZone:__Engage( 1 ) +end + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To ) + + if From ~= "Engaging" then + + local Engage = false + + for DetectedUnit, Detected in pairs( self.DetectedUnits ) do + + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( DetectedUnit ) + if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then + Engage = true + break + end + end + + if Engage == true then + self:E( 'Detected -> Engaging' ) + self:__Engage( 1 ) + end + end +end + + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To ) + Controllable:ClearTasks() + self:__Route( 1 ) +end + + + + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) + + if Controllable:IsAlive() then + + local EngageRoute = {} + + --- Calculate the current route point. + local CurrentVec2 = self.Controllable:GetVec2() + + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToEngageZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToEngageZoneSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = CurrentRoutePoint + + + --- Find a random 2D point in PatrolZone. + local ToTargetVec2 = self.PatrolZone:GetRandomVec2() + self:T2( ToTargetVec2 ) + + --- Define Speed and Altitude. + local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) + + --- Obtain a 3D @{Point} from the 2D point + altitude. + local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + + Controllable:OptionROEOpenFire() + Controllable:OptionROTPassiveDefense() + + local AttackTasks = {} + + for DetectedUnit, Detected in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( { DetectedUnit, DetectedUnit:IsAlive(), DetectedUnit:IsAir() } ) + if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then + if self.EngageZone then + if DetectedUnit:IsInZone( self.EngageZone ) then + self:E( {"Within Zone and Engaging ", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) + end + else + if self.EngageRange then + if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then + self:E( {"Within Range and Engaging", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) + end + else + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) + end + end + else + self.DetectedUnits[DetectedUnit] = nil + end + end + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + self.Controllable:WayPointInitialize( EngageRoute ) + + + if #AttackTasks == 0 then + self:E("No targets found -> Going back to Patrolling") + self:__Abort( 1 ) + self:__Route( 1 ) + self:SetDetectionActivated() + else + EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) + + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + self.Controllable:SetState( self.Controllable, "EngageZone", self ) + + self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" ) + + self:SetDetectionDeactivated() + end + + --- NOW ROUTE THE GROUP! + self.Controllable:WayPointExecute( 1, 2 ) + + end +end + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) + self.Accomplished = true + self:SetDetectionOff() +end + +--- @param #AI_CAP_ZONE 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. +-- @param Core.Event#EVENTDATA EventData +function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) + + if EventData.IniUnit then + self.DetectedUnits[EventData.IniUnit] = nil + end +end + +--- @param #AI_CAP_ZONE self +-- @param Core.Event#EVENTDATA EventData +function AI_CAP_ZONE:OnEventDead( EventData ) + self:F( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then + self:__Destroy( 1, EventData ) + end + end +end diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index c391ce4cd..286332c87 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -38,6 +38,7 @@ -- -- @module AI_Patrol + --- AI_PATROL_ZONE class -- @type AI_PATROL_ZONE -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. diff --git a/Moose Development/Moose/AI/AI_SWEEP.lua b/Moose Development/Moose/AI/AI_SWEEP.lua new file mode 100644 index 000000000..1d14674c3 --- /dev/null +++ b/Moose Development/Moose/AI/AI_SWEEP.lua @@ -0,0 +1,565 @@ +--- **AI** -- **Execute Combat Air Patrol (CAP).** +-- +-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) +-- +-- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_CAP Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) +-- +-- ### [AI_CAP Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_CAP YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ==== +-- +-- @module AI_Cap + + +--- @type AI_CAP_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + + +--- # AI_CAP_ZONE class, extends @{AI_CAP#AI_PATROL_ZONE} +-- +-- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} +-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. +-- +-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) +-- +-- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_CAP\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_CAP\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia9.JPG) +-- +-- When enemies are detected, the AI will automatically engage the enemy. +-- +-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) +-- +-- ## 1. AI_CAP_ZONE constructor +-- +-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object. +-- +-- ## 2. AI_CAP_ZONE is a FSM +-- +-- ![Process](..\Presentations\AI_CAP\Dia2.JPG) +-- +-- ### 2.1 AI_CAP_ZONE States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Engaging** ( Group ): The AI is engaging the bogeys. +-- * **Returning** ( Group ): The AI is returning to Base.. +-- +-- ### 2.2 AI_CAP_ZONE Events +-- +-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys. +-- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. +-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Unit}. +-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set the Range of Engagement +-- +-- ![Range](..\Presentations\AI_CAP\Dia11.JPG) +-- +-- An optional range can be set in meters, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- The range can be beyond or smaller than the range of the Patrol Zone. +-- The range is applied at the position of the AI. +-- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. +-- +-- ## 4. Set the Zone of Engagement +-- +-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) +-- +-- An optional @{Zone} can be set, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. +-- +-- === +-- +-- @field #AI_CAP_ZONE +AI_CAP_ZONE = { + ClassName = "AI_CAP_ZONE", +} + + + +--- Creates a new AI_CAP_ZONE object +-- @param #AI_CAP_ZONE self +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_CAP_ZONE self +function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAP_ZONE + + self.Accomplished = false + self.Engaging = false + + self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_CAP_ZONE] OnBeforeEngage + -- @param #AI_CAP_ZONE 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 Engage. + -- @function [parent=#AI_CAP_ZONE] OnAfterEngage + -- @param #AI_CAP_ZONE 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 Engage. + -- @function [parent=#AI_CAP_ZONE] Engage + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_CAP_ZONE] __Engage + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_CAP_ZONE] OnLeaveEngaging +-- @param #AI_CAP_ZONE 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 Engaging. +-- @function [parent=#AI_CAP_ZONE] OnEnterEngaging +-- @param #AI_CAP_ZONE 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. + + self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_CAP_ZONE] OnBeforeFired + -- @param #AI_CAP_ZONE 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 Fired. + -- @function [parent=#AI_CAP_ZONE] OnAfterFired + -- @param #AI_CAP_ZONE 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 Fired. + -- @function [parent=#AI_CAP_ZONE] Fired + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_CAP_ZONE] __Fired + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_CAP_ZONE] OnBeforeDestroy + -- @param #AI_CAP_ZONE 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 Destroy. + -- @function [parent=#AI_CAP_ZONE] OnAfterDestroy + -- @param #AI_CAP_ZONE 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 Destroy. + -- @function [parent=#AI_CAP_ZONE] Destroy + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_CAP_ZONE] __Destroy + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_CAP_ZONE] OnBeforeAbort + -- @param #AI_CAP_ZONE 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 Abort. + -- @function [parent=#AI_CAP_ZONE] OnAfterAbort + -- @param #AI_CAP_ZONE 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 Abort. + -- @function [parent=#AI_CAP_ZONE] Abort + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_CAP_ZONE] __Abort + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_CAP_ZONE] OnBeforeAccomplish + -- @param #AI_CAP_ZONE 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 Accomplish. + -- @function [parent=#AI_CAP_ZONE] OnAfterAccomplish + -- @param #AI_CAP_ZONE 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 Accomplish. + -- @function [parent=#AI_CAP_ZONE] Accomplish + -- @param #AI_CAP_ZONE self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_CAP_ZONE] __Accomplish + -- @param #AI_CAP_ZONE self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- Set the Engage Zone which defines where the AI will engage bogies. +-- @param #AI_CAP_ZONE self +-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. +-- @return #AI_CAP_ZONE self +function AI_CAP_ZONE:SetEngageZone( EngageZone ) + self:F2() + + if EngageZone then + self.EngageZone = EngageZone + else + self.EngageZone = nil + end +end + +--- Set the Engage Range when the AI will engage with airborne enemies. +-- @param #AI_CAP_ZONE self +-- @param #number EngageRange The Engage Range. +-- @return #AI_CAP_ZONE self +function AI_CAP_ZONE:SetEngageRange( EngageRange ) + self:F2() + + if EngageRange then + self.EngageRange = EngageRange + else + self.EngageRange = nil + end +end + +--- onafter State Transition for Event Start. +-- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterStart( Controllable, From, Event, To ) + + -- Call the parent Start event handler + self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) + self:HandleEvent( EVENTS.Dead ) + +end + +-- todo: need to fix this global function + +--- @param Wrapper.Controllable#CONTROLLABLE AIControllable +function _NewEngageCapRoute( AIControllable ) + + AIControllable:T( "NewEngageRoute" ) + local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE + EngageZone:__Engage( 1 ) +end + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To ) + + if From ~= "Engaging" then + + local Engage = false + + for DetectedUnit, Detected in pairs( self.DetectedUnits ) do + + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( DetectedUnit ) + if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then + Engage = true + break + end + end + + if Engage == true then + self:E( 'Detected -> Engaging' ) + self:__Engage( 1 ) + end + end +end + + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To ) + Controllable:ClearTasks() + self:__Route( 1 ) +end + + + + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) + + if Controllable:IsAlive() then + + local EngageRoute = {} + + --- Calculate the current route point. + local CurrentVec2 = self.Controllable:GetVec2() + + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToEngageZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToEngageZoneSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = CurrentRoutePoint + + + --- Find a random 2D point in PatrolZone. + local ToTargetVec2 = self.PatrolZone:GetRandomVec2() + self:T2( ToTargetVec2 ) + + --- Define Speed and Altitude. + local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) + + --- Obtain a 3D @{Point} from the 2D point + altitude. + local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + + Controllable:OptionROEOpenFire() + Controllable:OptionROTPassiveDefense() + + local AttackTasks = {} + + for DetectedUnit, Detected in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + self:T( { DetectedUnit, DetectedUnit:IsAlive(), DetectedUnit:IsAir() } ) + if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then + if self.EngageZone then + if DetectedUnit:IsInZone( self.EngageZone ) then + self:E( {"Within Zone and Engaging ", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) + end + else + if self.EngageRange then + if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then + self:E( {"Within Range and Engaging", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) + end + else + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) + end + end + else + self.DetectedUnits[DetectedUnit] = nil + end + end + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + self.Controllable:WayPointInitialize( EngageRoute ) + + + if #AttackTasks == 0 then + self:E("No targets found -> Going back to Patrolling") + self:__Abort( 1 ) + self:__Route( 1 ) + self:SetDetectionActivated() + else + EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) + + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + self.Controllable:SetState( self.Controllable, "EngageZone", self ) + + self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" ) + + self:SetDetectionDeactivated() + end + + --- NOW ROUTE THE GROUP! + self.Controllable:WayPointExecute( 1, 2 ) + + end +end + +--- @param #AI_CAP_ZONE 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 AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) + self.Accomplished = true + self:SetDetectionOff() +end + +--- @param #AI_CAP_ZONE 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. +-- @param Core.Event#EVENTDATA EventData +function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) + + if EventData.IniUnit then + self.DetectedUnits[EventData.IniUnit] = nil + end +end + +--- @param #AI_CAP_ZONE self +-- @param Core.Event#EVENTDATA EventData +function AI_CAP_ZONE:OnEventDead( EventData ) + self:F( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then + self:__Destroy( 1, EventData ) + end + end +end diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 179019c14..8e74c793d 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -101,13 +101,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr Scheduler = self.PersistentSchedulers[CallID] end - self:T3( { Scheduler = Scheduler } ) + --self:T3( { Scheduler = Scheduler } ) if Scheduler then local Schedule = self.Schedule[Scheduler][CallID] - self:T3( { Schedule = Schedule } ) + --self:T3( { Schedule = Schedule } ) local ScheduleObject = Scheduler.SchedulerObject --local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID() @@ -145,7 +145,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr ( Randomize * Repeat / 2 ) ) + 0.01 - self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) + --self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) return ScheduleTime -- returns the next time the function needs to be called. else self:Stop( Scheduler, CallID ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 5256401d6..1d6e5fd40 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1154,7 +1154,7 @@ do -- DETECTION_BASE local EnemyUnitName = DetectedUnit:GetName() local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + self:F( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 097a5c95b..0812459af 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -70,7 +70,60 @@ do -- DETECTION MANAGER self:SetStartState( "Stopped" ) self:AddTransition( "Stopped", "Start", "Started" ) + + --- Start Handler OnBefore for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnBeforeStart + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Start Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterStart + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Start Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] Start + -- @param #DETECTION_MANAGER self + + --- Start Asynchronous Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] __Start + -- @param #DETECTION_MANAGER self + -- @param #number Delay + + + self:AddTransition( "Started", "Stop", "Stopped" ) + + --- Stop Handler OnBefore for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnBeforeStop + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Stop Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterStop + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Stop Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] Stop + -- @param #DETECTION_MANAGER self + + --- Stop Asynchronous Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] __Stop + -- @param #DETECTION_MANAGER self + -- @param #number Delay + + self:AddTransition( "Started", "Report", "Started" ) self:SetReportInterval( 30 ) diff --git a/Moose Mission Setup/Moose.files b/Moose Mission Setup/Moose.files index 55aba83d6..35abf02e1 100644 --- a/Moose Mission Setup/Moose.files +++ b/Moose Mission Setup/Moose.files @@ -41,6 +41,10 @@ Functional/Detection.lua Functional/Designate.lua AI/AI_Balancer.lua +AI/AI_A2A.lua +AI/AI_A2A_Patrol.lua +AI/AI_A2A_Cap.lua +AI/AI_A2A_Dispatcher.lua AI/AI_Patrol.lua AI/AI_Cap.lua AI/AI_Cas.lua diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 0e64ca6af..41ea7f070 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170522_1100' ) +env.info( 'Moose Generation Timestamp: 20170530_1057' ) local base = _G @@ -60,6 +60,10 @@ __Moose.Include( 'Functional/AirbasePolice.lua' ) __Moose.Include( 'Functional/Detection.lua' ) __Moose.Include( 'Functional/Designate.lua' ) __Moose.Include( 'AI/AI_Balancer.lua' ) +__Moose.Include( 'AI/AI_A2A.lua' ) +__Moose.Include( 'AI/AI_A2A_Patrol.lua' ) +__Moose.Include( 'AI/AI_A2A_Cap.lua' ) +__Moose.Include( 'AI/AI_A2A_Dispatcher.lua' ) __Moose.Include( 'AI/AI_Patrol.lua' ) __Moose.Include( 'AI/AI_Cap.lua' ) __Moose.Include( 'AI/AI_Cas.lua' )