From a3289205e6d642bb91fb84e9050517046bb92cbd Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 31 May 2017 18:58:34 +0200 Subject: [PATCH] Progress --- Moose Development/Moose/AI/AI_A2A_Cap.lua | 8 +- .../Moose/AI/AI_A2A_Dispatcher.lua | 436 +++++++++----- .../Moose/AI/AI_A2A_Intercept.lua | 458 ++++++++++++++ Moose Development/Moose/AI/AI_INTERCEPT.lua | 565 ------------------ .../Moose/Functional/Detection.lua | 2 +- Moose Development/Moose/Functional/Spawn.lua | 13 + .../Moose/Wrapper/Controllable.lua | 7 +- .../Moose/Wrapper/Positionable.lua | 32 +- Moose Mission Setup/Moose.files | 1 + Moose Mission Setup/Moose.lua | 3 +- 10 files changed, 814 insertions(+), 711 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_A2A_Intercept.lua delete mode 100644 Moose Development/Moose/AI/AI_INTERCEPT.lua diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index d86c6646e..f60dc492b 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -379,11 +379,11 @@ end -- @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 ) +function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) - self:F( { AIGroup, From, Event, To, AttackUnits} ) + self:F( { AIGroup, From, Event, To, AttackSetUnit} ) - self.AttackUnits = AttackUnits or self.AttackUnits + self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT if AIGroup:IsAlive() then @@ -435,7 +435,7 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackUnits ) local AttackTasks = {} - for AttackUnitID, AttackUnit in pairs( self.AttackUnits ) do + for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT self:T( { AttackUnit, AttackUnit:IsAlive(), AttackUnit:IsAir() } ) if AttackUnit:IsAlive() and AttackUnit:IsAir() then diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 8a36d2f75..7bff5dab1 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -59,6 +59,9 @@ do -- AI_A2A_DISPATCHER -- This table models the currently alive AI_A2A processes. self.AI_A2A = self.AI_A2A or {} + -- These two tables model the AIGroups having an assignment and the Targets having an AIGroup. + self.A2A_Targets = self.A2A_Targets or {} + self.A2A_Tasks = self.A2A_Tasks or {} -- TODO: Check detection through radar. self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) @@ -78,7 +81,7 @@ do -- AI_A2A_DISPATCHER -- @param #string PlayerName self:AddTransition( "*", "CAP", "*" ) - + --- CAP Handler OnBefore for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeCAP -- @param #AI_A2A_DISPATCHER self @@ -103,6 +106,57 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self -- @param #number Delay + self:AddTransition( "*", "INTERCEPT", "*" ) + + --- INTERCEPT Handler OnBefore for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeINTERCEPT + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- INTERCEPT Handler OnAfter for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterINTERCEPT + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- INTERCEPT Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] INTERCEPT + -- @param #AI_A2A_DISPATCHER self + + --- INTERCEPT Asynchronous Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] __INTERCEPT + -- @param #AI_A2A_DISPATCHER self + -- @param #number Delay + + self:AddTransition( "*", "ENGAGE", "*" ) + + --- ENGAGE Handler OnBefore for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeENGAGE + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- ENGAGE Handler OnAfter for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterENGAGE + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- ENGAGE Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] ENGAGE + -- @param #AI_A2A_DISPATCHER self + + --- ENGAGE Asynchronous Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] __ENGAGE + -- @param #AI_A2A_DISPATCHER self + -- @param #number Delay @@ -111,17 +165,112 @@ do -- AI_A2A_DISPATCHER return self end + function AI_A2A_DISPATCHER:onafterCAP() - for CAPName, CAP in pairs( self.AI_A2A["CAP"] ) do + + local A2AType = "CAP" + + for CAPName, CAP in pairs( self.AI_A2A[A2AType] or {} ) 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 ) + + local Fsm = AI_A2A_CAP:New( AIGroup, CAP.Zone, CAP.FloorAltitude, CAP.CeilingAltitude, CAP.MinSpeed, CAP.MaxSpeed, CAP.AltType ) + Fsm:__Patrol( 1 ) + + self.A2A_Tasks = self.A2A_Tasks or {} + self.A2A_Tasks[AIGroup] = self.A2A_Tasks[AIGroup] or {} + self.A2A_Tasks[AIGroup].Type = A2AType + self.A2A_Tasks[AIGroup].Fsm = Fsm end end end + + function AI_A2A_DISPATCHER:onafterENGAGE( From, Event, To, TargetSetUnit, TargetReference, AIGroups ) + + local A2AType = "CAP" + + self:F( { AIGroups = AIGroups } ) + + if AIGroups then + + for AIGroupID, AIGroup in pairs( AIGroups ) do + + local Fsm = self.A2A_Tasks[AIGroup].Fsm + Fsm:__Engage( 1, TargetSetUnit ) -- Engage on the TargetSetUnit + + self.A2A_Tasks[AIGroup].Target = TargetReference + + self.A2A_Targets[TargetReference] = self.A2A_Targets[TargetReference] or {} + self.A2A_Targets[TargetReference].Set = TargetSetUnit + self.A2A_Targets[TargetReference].Groups = self.A2A_Targets[TargetReference].Groups or {} + local Groups = self.A2A_Targets[TargetReference].Groups + local GroupName = AIGroup:GetName() + Groups[GroupName] = AIGroup + end + end + end + + + function AI_A2A_DISPATCHER:onafterINTERCEPT( From, Event, To, TargetSetUnit, TargetReference, DefendersMissing ) + + local A2AType = "INTERCEPT" + + local ClosestDistance = 0 + local ClosestINTERCEPTName = nil + + local AttackerCount = TargetSetUnit:Count() + DefendersMissing = AttackerCount - DefendersMissing + + while( DefendersMissing < AttackerCount ) do + + for INTERCEPTName, INTERCEPT in pairs( self.AI_A2A[A2AType] or {} ) do + + local SpawnCoord = INTERCEPT.Spawn:GetCoordinate() -- Core.Point#COORDINATE + local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate() + local Distance = SpawnCoord:Get2DDistance( TargetCoord ) + + if ClosestDistance == 0 or Distance < ClosestDistance then + ClosestDistance = Distance + ClosestINTERCEPTName = INTERCEPTName + end + end + + if ClosestINTERCEPTName then + + local INTERCEPT = self.AI_A2A[A2AType][ClosestINTERCEPTName] + + local AIGroup = INTERCEPT.Spawn:Spawn() + self:F( { AIGroup = AIGroup:GetName() } ) + + if AIGroup then + + DefendersMissing = DefendersMissing + AIGroup:GetSize() + + local Fsm = AI_A2A_INTERCEPT:New( AIGroup ) + Fsm:__Engage( 1, TargetSetUnit ) -- Engage on the TargetSetUnit + + self.A2A_Tasks = self.A2A_Tasks or {} + self.A2A_Tasks[AIGroup] = self.A2A_Tasks[AIGroup] or {} + self.A2A_Tasks[AIGroup].Type = A2AType + self.A2A_Tasks[AIGroup].Fsm = Fsm + self.A2A_Tasks[AIGroup].Target = TargetReference + + self.A2A_Targets[TargetReference] = self.A2A_Targets[TargetReference] or {} + self.A2A_Targets[TargetReference].Set = TargetSetUnit + self.A2A_Targets[TargetReference].Groups = self.A2A_Targets[TargetReference].Groups or {} + local Groups = self.A2A_Targets[TargetReference].Groups + local GroupName = AIGroup:GetName() + Groups[GroupName] = AIGroup + + end + end + end + end + + function AI_A2A_DISPATCHER:SetCAP( CAPName, CAPSpawn, CAPZone, CAPFloorAltitude, CAPCeilingAltitude, CAPMinSpeed, CAPMaxSpeed, CAPAltType ) @@ -140,47 +289,18 @@ do -- AI_A2A_DISPATCHER end - function AI_A2A_DISPATCHER:SetINTERCEPT( INTERCEPTName, INTERCEPTSpawn, CAPZone, CAPFloorAltitude, CAPCeilingAltitude, CAPMinSpeed, CAPMaxSpeed, CAPAltType ) + function AI_A2A_DISPATCHER:SetINTERCEPT( Name, Spawn, MinSpeed, MaxSpeed ) self.AI_A2A["INTERCEPT"] = self.AI_A2A["INTERCEPT"] or {} - self.AI_A2A["INTERCEPT"][INTERCEPTName] = self.AI_A2A["INTERCEPT"][INTERCEPTName] or {} + self.AI_A2A["INTERCEPT"][Name] = self.AI_A2A["INTERCEPT"][Name] 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 + local INTERCEPT = self.AI_A2A["INTERCEPT"][Name] + INTERCEPT.Name = Name + INTERCEPT.Spawn = Spawn + INTERCEPT.MinSpeed = MinSpeed + INTERCEPT.MaxSpeed = MaxSpeed 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. @@ -207,8 +327,64 @@ do -- AI_A2A_DISPATCHER return nil end - + function AI_A2A_DISPATCHER:CountDefendersEngaged( DetectedItem ) + + -- First, count the active AIGroups Units, targetting the DetectedSet + local AIUnitCount = 0 + + for AIGroupName, AIGroup in pairs( ( self.A2A_Targets[DetectedItem] and self.A2A_Targets[DetectedItem].Groups ) or {} ) do + local AIGroup = AIGroup -- Wrapper.Group#GROUP + if AIGroup:IsAlive() then + AIUnitCount = AIUnitCount + AIGroup:GetSize() + end + end + + return AIUnitCount + end + + function AI_A2A_DISPATCHER:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) + + local ResultAIGroups = nil + + local DetectedSet = DetectedItem.Set + local DetectedCount = DetectedSet:Count() + + local AIFriendlies = self:GetAIFriendliesNearBy( DetectedItem ) + + for AIName, AIFriendly in pairs( AIFriendlies ) do + -- We only allow to ENGAGE targets as long as the Units on both sides are balanced. + if DetectedCount > DefenderCount then + local AIGroup = AIFriendly :GetGroup() -- Wrapper.Group#GROUP + self:F( { AIFriendly = AIGroup } ) + if AIGroup:IsAlive() then + -- Ok, so we have a friendly near the potential target. + -- Now we need to check if the AIGroup has a Task. + local AIGroupTask = self.A2A_Tasks[AIGroup] + self:F({AIGroupTask = AIGroupTask}) + if AIGroupTask then + -- The Task should be CAP + self:F( { Type = AIGroupTask.Type } ) + if AIGroupTask.Type == "CAP" then + -- If there is no target, then add the AIGroup to the ResultAIGroups for Engagement to the TargetSet + self:F( { Target = AIGroupTask.Target } ) + if AIGroupTask.Target == nil then + ResultAIGroups = ResultAIGroups or {} + ResultAIGroups[AIGroup] = AIGroup + DefenderCount = DefenderCount + AIGroup:GetSize() + end + end + end + end + else + break + end + end + + return ResultAIGroups + 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 @@ -217,106 +393,50 @@ do -- AI_A2A_DISPATCHER 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 + -- First, count the active AIGroups Units, targetting the DetectedSet + local DefenderCount = self:CountDefendersEngaged( DetectedItem ) + local DefenderGroups = self:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) -- 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 + if DefenderGroups and DetectedItem.IsDetected == true then - self:E("ENGAGE") - return DetectedSet, ResultCAPName, ResultAIGroup + return DetectedSet, DefenderGroups end - return nil + return nil, 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". + --- Creates an INTERCEPT task when there are targets for it. -- @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 + -- @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 AttackerSet = DetectedItem.Set + AttackerSet:Flush() + local AttackerCount = AttackerSet:Count() - 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 + -- First, count the active AIGroups Units, targetting the DetectedSet + local DefenderCount = self:CountDefendersEngaged( DetectedItem ) + local DefendersMissing = AttackerCount - DefenderCount - 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 + -- 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 DetectedItem.IsDetected == true then + + return DetectedItem.Set, DefendersMissing end - return Task + return nil, nil end + + + --- Calculates which friendlies are nearby the area -- @param #AI_A2A_DISPATCHER self -- @param DetectedItem @@ -419,6 +539,41 @@ do -- AI_A2A_DISPATCHER 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( DetectedItem, A2A_Index ) + + local A2A_Target = self.A2A_Targets[A2A_Index] + + if A2A_Target then + + for AIGroupName, AIGroup in pairs( A2A_Target.Groups ) do + local AIGroup = AIGroup -- Wrapper.Group#GROUP + if not AIGroup:IsAlive() then + self.A2A_Tasks[AIGroup] = nil + self.A2A_Targets[A2A_Index].Groups[AIGroupName] = nil + end + end + + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + DetectedSet:Flush() + self:E( { DetectedSetCount = DetectedSet:Count() } ) + if DetectedSet:Count() == 0 then + self.A2A_Targets[A2A_Index] = nil + end + end + + return self.A2A_Targets[A2A_Index] + 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. @@ -451,22 +606,35 @@ do -- AI_A2A_DISPATCHER 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() + self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count() } ) + DetectedSet:Flush() local DetectedID = DetectedItem.ID - local AI_A2A_Index = DetectedItem.Index + local 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. + self:E( { A2A_Index = A2A_Index } ) + + + local A2A_Target = self:EvaluateRemoveTask( DetectedItem, A2A_Index ) + self:E( { A2A_Index = A2A_Index, A2A_Target = A2A_Target } ) + DetectedSet:Flush() -- 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 not A2A_Target then + local TargetSetUnit, AIGroups = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + self:F( { AIGroups = AIGroups } ) if TargetSetUnit then - local CAP = self.AI_A2A["CAP"][CAPName] - CAP.Fsm[AIGroup]:__Engage( 1, TargetSetUnit ) + self:ENGAGE( TargetSetUnit, A2A_Index, AIGroups ) + else + do + local AttackerSet, DefendersMissing = self:EvaluateINTERCEPT( DetectedItem ) + self:F( { DefendersMissing = DefendersMissing } ) + AttackerSet:Flush() + if AttackerSet then + self:INTERCEPT( AttackerSet, A2A_Index, DefendersMissing ) + end + end end end end diff --git a/Moose Development/Moose/AI/AI_A2A_Intercept.lua b/Moose Development/Moose/AI/AI_A2A_Intercept.lua new file mode 100644 index 000000000..d177ae7ae --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Intercept.lua @@ -0,0 +1,458 @@ +--- **AI** -- **Execute Interception of Intruders (CAP).** +-- +-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) +-- +-- === +-- +-- AI A2A_INTEREPT class makes AI Groups execute an Intercept. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_A2A_INTERCEPT}: Perform a CAP in a zone. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module AI_A2A_Intercept + + +--- @type AI_A2A_INTERCEPT +-- @extends AI.AI_A2A#AI_A2A + + +--- # AI_A2A_INTERCEPT class, extends @{AI_A2A#AI_A2A} +-- +-- The AI_A2A_INTERCEPT class implements the core functions to intercept intruders. The Engage function will intercept intruders. +-- +-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) +-- +-- The AI_A2A_INTERCEPT is assigned a @{Group} and this must be done before the AI_A2A_INTERCEPT 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_INTERCEPT constructor +-- +-- * @{#AI_A2A_INTERCEPT.New}(): Creates a new AI_A2A_INTERCEPT object. +-- +-- ## 2. AI_A2A_INTERCEPT is a FSM +-- +-- ![Process](..\Presentations\AI_CAP\Dia2.JPG) +-- +-- ### 2.1 AI_A2A_INTERCEPT 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_INTERCEPT 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_INTERCEPT.Engage}**: Let the AI engage the bogeys. +-- * **@{#AI_A2A_INTERCEPT.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_INTERCEPT.Destroy}**: The AI has destroyed a bogey @{Unit}. +-- * **@{#AI_A2A_INTERCEPT.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_INTERCEPT.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_INTERCEPT.SetEngageZone}() to define that Zone. +-- +-- === +-- +-- @field #AI_A2A_INTERCEPT +AI_A2A_INTERCEPT = { + ClassName = "AI_A2A_INTERCEPT", +} + + + +--- Creates a new AI_A2A_INTERCEPT object +-- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup +-- @return #AI_A2A_INTERCEPT +function AI_A2A_INTERCEPT:New( AIGroup, MinSpeed, MaxSpeed ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_INTERCEPT + + self.Accomplished = false + self.Engaging = false + + self.MinSpeed = MinSpeed + self.MaxSpeed = MaxSpeed + + self.PatrolAltType = "RADIO" + + self:AddTransition( { "Stopped", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_INTERCEPT. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_A2A_INTERCEPT] OnBeforeEngage + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] OnAfterEngage + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] Engage + -- @param #AI_A2A_INTERCEPT self + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_A2A_INTERCEPT] __Engage + -- @param #AI_A2A_INTERCEPT self + -- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_A2A_INTERCEPT] OnLeaveEngaging +-- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] OnEnterEngaging +-- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_A2A_INTERCEPT] OnBeforeFired + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] OnAfterFired + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] Fired + -- @param #AI_A2A_INTERCEPT self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_A2A_INTERCEPT] __Fired + -- @param #AI_A2A_INTERCEPT self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_INTERCEPT. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_A2A_INTERCEPT] OnBeforeDestroy + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] OnAfterDestroy + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] Destroy + -- @param #AI_A2A_INTERCEPT self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_A2A_INTERCEPT] __Destroy + -- @param #AI_A2A_INTERCEPT self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_INTERCEPT. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_A2A_INTERCEPT] OnBeforeAbort + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] OnAfterAbort + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] Abort + -- @param #AI_A2A_INTERCEPT self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_A2A_INTERCEPT] __Abort + -- @param #AI_A2A_INTERCEPT self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_INTERCEPT. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_A2A_INTERCEPT] OnBeforeAccomplish + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] OnAfterAccomplish + -- @param #AI_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT] Accomplish + -- @param #AI_A2A_INTERCEPT self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_A2A_INTERCEPT] __Accomplish + -- @param #AI_A2A_INTERCEPT self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- onafter State Transition for Event Patrol. +-- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP 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_INTERCEPT:onafterEngage( AIGroup, From, Event, To ) + + self:HandleEvent( EVENTS.Dead ) + +end + +-- todo: need to fix this global function + +--- @param Wrapper.Group#GROUP AIControllable +function _NewEngageInterceptRoute( AIControllable ) + + AIControllable:T( "NewEngageRoute" ) + local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_A2A_INTERCEPT + EngageZone:__Engage( 1 ) +end + +--- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT:onbeforeEngage( AIGroup, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP 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_INTERCEPT:onafterAbort( AIGroup, From, Event, To ) + AIGroup:ClearTasks() + self:__Route( 1 ) +end + + +--- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) + + self:F( { AIGroup, From, Event, To, AttackSetUnit} ) + + self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT + + if AIGroup:IsAlive() then + + local EngageRoute = {} + + --- Calculate the current route point. + local CurrentVec2 = AIGroup:GetVec2() + + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = AIGroup:GetUnit(1):GetAltitude() + local CurrentSpeed = AIGroup:GetUnit(1):GetVelocityKMH() + 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, + CurrentSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = CurrentRoutePoint + + + --- Find the target point. + local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate() + + local ToTargetSpeed = math.random( self.MinSpeed, self.MaxSpeed ) + self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = ToTargetCoord: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.AttackSetUnit:GetSet() ) 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... + AIGroup:WayPointInitialize( EngageRoute ) + + + if #AttackTasks == 0 then + self:E("No targets found -> Going back to Patrolling") + self:__RTB( 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 ... + AIGroup:SetState( AIGroup, "EngageZone", self ) + + AIGroup:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" ) + end + + --- NOW ROUTE THE GROUP! + AIGroup:WayPointExecute( 1, 2 ) + + end +end + +--- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT:onafterAccomplish( AIGroup, From, Event, To ) + self.Accomplished = true + self:SetDetectionOff() +end + +--- @param #AI_A2A_INTERCEPT self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup 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_INTERCEPT:onafterDestroy( AIGroup, From, Event, To, EventData ) + + if EventData.IniUnit then + self.AttackUnits[EventData.IniUnit] = nil + end +end + +--- @param #AI_A2A_INTERCEPT self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_INTERCEPT: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_INTERCEPT.lua b/Moose Development/Moose/AI/AI_INTERCEPT.lua deleted file mode 100644 index 1d14674c3..000000000 --- a/Moose Development/Moose/AI/AI_INTERCEPT.lua +++ /dev/null @@ -1,565 +0,0 @@ ---- **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/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 1d6e5fd40..d2e44ba63 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1159,7 +1159,7 @@ do -- DETECTION_BASE if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} DetectedItem.FriendliesNearBy[FoundUnitName] = UNIT:Find( FoundDCSUnit ) - return false + return true end return true diff --git a/Moose Development/Moose/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index 862cc6f07..dd0315fd5 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -1112,6 +1112,19 @@ function SPAWN:InitUnControlled( UnControlled ) end +--- Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object. +-- @param #SPAWN self +-- @return Core.Point#COORDINATE The Coordinate +function SPAWN:GetCoordinate() + + local LateGroup = GROUP:FindByName( self.SpawnTemplatePrefix ) + if LateGroup then + return LateGroup:GetCoordinate() + end + + return nil +end + --- Will return the SpawnGroupName either with with a specific count number or without any count. -- @param #SPAWN self diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 85a9f1edd..da203cb54 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -152,7 +152,7 @@ CONTROLLABLE = { -- @param Dcs.DCSWrapper.Controllable#Controllable ControllableName The DCS Controllable name -- @return #CONTROLLABLE self function CONTROLLABLE:New( ControllableName ) - local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) + local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) -- #CONTROLLABLE self:F2( ControllableName ) self.ControllableName = ControllableName @@ -166,12 +166,10 @@ end -- @param #CONTROLLABLE self -- @return Dcs.DCSController#Controller function CONTROLLABLE:_GetController() - self:F2( { self.ControllableName } ) local DCSControllable = self:GetDCSObject() if DCSControllable then local ControllableController = DCSControllable:getController() - self:T3( ControllableController ) return ControllableController end @@ -300,14 +298,13 @@ end -- @param #CONTROLLABLE self -- @return Wrapper.Controllable#CONTROLLABLE self function CONTROLLABLE:SetTask( DCSTask, WaitTime ) - self:F2( { DCSTask } ) + self:F2( { DCSTask = DCSTask } ) local DCSControllable = self:GetDCSObject() if DCSControllable then local Controller = self:_GetController() - self:T3( Controller ) -- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results. -- Therefore we schedule the functions to set the mission and options for the Controllable. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index c949da431..fbe5421b0 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -31,7 +31,18 @@ -- -- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance: -- --- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance. +-- * @{#POSITIONABLE.New}(): Create a POSITIONABLE instance. +-- +-- ## Get the current speed +-- +-- There are 3 methods that can be used to determine the speed. +-- Use @{#POSITIONABLE.GetVelocityKMH}() to retrieve the current speed in km/h. Use @{#POSITIONABLE.GetVelocityMPS}() to retrieve the speed in meters per second. +-- The method @{#POSITIONABLE.GetVelocity}() returns the speed vector (a Vec3). +-- +-- ## Get the current altitude +-- +-- Altitude can be retrieved using the method @{#POSITIONABLE.GetHeight}() and returns the current altitude in meters from the orthonormal plane. +-- -- -- @field #POSITIONABLE POSITIONABLE = { @@ -371,6 +382,25 @@ function POSITIONABLE:GetVelocityKMH() return nil end +--- Returns the POSITIONABLE velocity in meters per second. +-- @param Wrapper.Positionable#POSITIONABLE self +-- @return #number The velocity in meters per second. +-- @return #nil The POSITIONABLE is not existing or alive. +function POSITIONABLE:GetVelocityMPS() + self:F2( self.PositionableName ) + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + local VelocityVec3 = self:GetVelocity() + local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec + self:T3( Velocity ) + return Velocity + end + + return nil +end + --- Returns the message text with the callsign embedded (if there is one). -- @param #POSITIONABLE self diff --git a/Moose Mission Setup/Moose.files b/Moose Mission Setup/Moose.files index 35abf02e1..ba373e125 100644 --- a/Moose Mission Setup/Moose.files +++ b/Moose Mission Setup/Moose.files @@ -44,6 +44,7 @@ AI/AI_Balancer.lua AI/AI_A2A.lua AI/AI_A2A_Patrol.lua AI/AI_A2A_Cap.lua +AI/AI_A2A_Intercept.lua AI/AI_A2A_Dispatcher.lua AI/AI_Patrol.lua AI/AI_Cap.lua diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 41ea7f070..a51b79bc1 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: 20170530_1057' ) +env.info( 'Moose Generation Timestamp: 20170531_0925' ) local base = _G @@ -63,6 +63,7 @@ __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_Intercept.lua' ) __Moose.Include( 'AI/AI_A2A_Dispatcher.lua' ) __Moose.Include( 'AI/AI_Patrol.lua' ) __Moose.Include( 'AI/AI_Cap.lua' )