diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index fac8b4eaa..42c21731b 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -248,6 +248,7 @@ function AI_A2A:New( AIGroup ) -- @param #AI_A2A self -- @param #number Delay + self:AddTransition( "*", "Takeoff", "Airborne" ) self:AddTransition( "*", "Return", "Returning" ) self:AddTransition( "*", "Hold", "Holding" ) self:AddTransition( "*", "Home", "Home" ) @@ -263,6 +264,13 @@ function AI_A2A:New( AIGroup ) return self end +--- @param Wrapper.Group#GROUP self +-- @param Core.Event#EVENTDATA EventData +function GROUP:OnEventTakeoff( EventData, Fsm ) + Fsm:Takeoff() + self:UnHandleEvent( EVENTS.Takeoff ) +end + function AI_A2A:SetDispatcher( Dispatcher ) self.Dispatcher = Dispatcher end diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 2509721b9..7b4fb69c0 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -141,7 +141,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling self.EngageMinSpeed = EngageMinSpeed self.EngageMaxSpeed = EngageMaxSpeed - self:AddTransition( { "Patrolling", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + self:AddTransition( { "Patrolling", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. --- OnBefore Transition Handler for Event Engage. -- @function [parent=#AI_A2A_CAP] OnBeforeEngage @@ -302,6 +302,17 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling return self end +--- onafter State Transition for Event Patrol. +-- @param #AI_A2A_GCI 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_CAP:onafterStart( AIGroup, From, Event, To ) + + AIGroup:HandleEvent( EVENTS.Takeoff, nil, self ) + +end --- Set the Engage Zone which defines where the AI will engage bogies. -- @param #AI_A2A_CAP self diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 4b5c1496e..040268722 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -978,6 +978,7 @@ do -- AI_A2A_DISPATCHER -- This will avoid the detection to still "know" the shot unit until the next detection. -- Otherwise, a new intercept or engage may happen for an already shot plane! + self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead ) @@ -1360,6 +1361,8 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefenderTask( SquadronName, Defender, Type, Fsm, Target ) + self:F( { SquadronName = SquadronName, Defender = Defender:GetName() } ) + self.DefenderTasks[Defender] = self.DefenderTasks[Defender] or {} self.DefenderTasks[Defender].Type = Type self.DefenderTasks[Defender].Fsm = Fsm @@ -2526,15 +2529,18 @@ do -- AI_A2A_DISPATCHER DetectedSet:Flush() local DefenderTasks = self:GetDefenderTasks() - for Defender, DefenderTask in pairs( DefenderTasks ) do - local Defender = Defender -- Wrapper.Group#GROUP + for DefenderGroup, DefenderTask in pairs( DefenderTasks ) do + local Defender = DefenderGroup -- Wrapper.Group#GROUP local DefenderTaskTarget = DefenderTask.Target local DefenderSquadronName = DefenderTask.SquadronName + if DefenderTaskTarget and DefenderTaskTarget.Index == AttackerDetection.Index then local Squadron = self:GetSquadron( DefenderSquadronName ) - local SquadronOverhead = Squadron.Overhead or self.DefenderDefault.Overhead - DefenderCount = DefenderCount + Defender:GetSize() / SquadronOverhead - self:E( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. Defender:GetSize() ) + local SquadronOverhead = Squadron.Overhead or self.DefenderDefault.Overhead + + local DefenderSize = Defender:GetInitialSize() + DefenderCount = DefenderCount + DefenderSize / SquadronOverhead + self:F( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. DefenderSize ) end end @@ -2622,10 +2628,21 @@ do -- AI_A2A_DISPATCHER Fsm:SetDisengageRadius( self.DisengageRadius ) Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName ) Fsm:Start() - Fsm:__Patrol( 2 ) self:SetDefenderTask( SquadronName, DefenderCAP, "CAP", Fsm ) + function Fsm:onafterTakeoff( Defender, From, Event, To ) + self:F({"GCI Birth", Defender:GetName()}) + --self:GetParent(self).onafterBirth( self, Defender, From, Event, To ) + + local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER + local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) + + if Squadron then + Fsm:__Patrol( 2 ) -- Start Patrolling + end + end + function Fsm:onafterRTB( Defender, From, Event, To ) self:F({"CAP RTB", Defender:GetName()}) self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) @@ -2721,7 +2738,7 @@ do -- AI_A2A_DISPATCHER local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE local AttackerCoord = AttackerUnit:GetCoordinate() local InterceptCoord = AttackerDetection.InterceptCoord - self:F({InterceptCoord = InterceptCoord}) + self:F( { InterceptCoord = InterceptCoord } ) if InterceptCoord then local InterceptDistance = SpawnCoord:Get2DDistance( InterceptCoord ) local AirbaseDistance = SpawnCoord:Get2DDistance( AttackerCoord ) @@ -2757,6 +2774,11 @@ do -- AI_A2A_DISPATCHER self:F( { Grouping = DefenderGrouping, SquadronGrouping = DefenderSquadron.Grouping, DefaultGrouping = self.DefenderDefault.Grouping } ) self:F( { DefendersCount = DefenderCount, DefendersNeeded = DefendersNeeded } ) + if DefendersNeeded > DefenderSquadron.Resources then + DefendersNeeded = DefenderSquadron.Resources + BreakLoop = true + end + while ( DefendersNeeded > 0 ) do local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN @@ -2769,14 +2791,14 @@ do -- AI_A2A_DISPATCHER local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName ) local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP - self:F( { GCIDefender = DefenderGCI:GetName() } ) + self:E( { GCIDefender = DefenderGCI:GetName() } ) DefendersNeeded = DefendersNeeded - DefenderGrouping self:AddDefenderToSquadron( DefenderSquadron, DefenderGCI, DefenderGrouping ) if DefenderGCI then - + DefenderCount = DefenderCount - DefenderGrouping / DefenderOverhead local Fsm = AI_A2A_GCI:New( DefenderGCI, Gci.EngageMinSpeed, Gci.EngageMaxSpeed ) @@ -2786,12 +2808,24 @@ do -- AI_A2A_DISPATCHER Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) Fsm:SetDisengageRadius( self.DisengageRadius ) Fsm:Start() - Fsm:__Engage( 2, AttackerDetection.Set ) -- Engage on the TargetSetUnit self:SetDefenderTask( ClosestDefenderSquadronName, DefenderGCI, "GCI", Fsm, AttackerDetection ) + function Fsm:onafterTakeoff( Defender, From, Event, To ) + self:F({"GCI Birth", Defender:GetName()}) + --self:GetParent(self).onafterBirth( self, Defender, From, Event, To ) + + local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER + local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) + local DefenderTarget = Dispatcher:GetDefenderTaskTarget( Defender ) + + if DefenderTarget then + Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit + end + end + function Fsm:onafterRTB( Defender, From, Event, To ) self:F({"GCI RTB", Defender:GetName()}) self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) @@ -2920,7 +2954,11 @@ do -- AI_A2A_DISPATCHER for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do local AIGroup = AIGroup -- Wrapper.Group#GROUP if not AIGroup:IsAlive() then - self:ClearDefenderTask( AIGroup ) + local DefenderTaskFsm = self:GetDefenderTaskFsm( AIGroup ) + self:E( { Defender = AIGroup:GetName(), DefenderState = DefenderTaskFsm:GetState() } ) + if not DefenderTaskFsm:Is( "Started" ) then + self:ClearDefenderTask( AIGroup ) + end else if DefenderTask.Target then local AttackerItem = Detection:GetDetectedItem( DefenderTask.Target.Index ) diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 5d0c415c7..7791ea72d 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -134,7 +134,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) self.PatrolAltType = "RADIO" - self:AddTransition( { "Started", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. + self:AddTransition( { "Started", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. --- OnBefore Transition Handler for Event Engage. -- @function [parent=#AI_A2A_GCI] OnBeforeEngage @@ -295,6 +295,19 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) return self end +--- onafter State Transition for Event Patrol. +-- @param #AI_A2A_GCI 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_GCI:onafterStart( AIGroup, From, Event, To ) + + AIGroup:HandleEvent( EVENTS.Takeoff, nil, self ) + +end + + --- onafter State Transition for Event Patrol. -- @param #AI_A2A_GCI self diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 92cd522d3..fa9888584 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -179,7 +179,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil -- defafult PatrolAltType to "RADIO" if not specified self.PatrolAltType = PatrolAltType or "RADIO" - self:AddTransition( { "Started", "Refuelling" }, "Patrol", "Patrolling" ) + self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" ) --- OnBefore Transition Handler for Event Patrol. -- @function [parent=#AI_A2A_PATROL] OnBeforePatrol diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index b3853173d..d6f61b293 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -608,6 +608,22 @@ function BASE:CreateEventCrash( EventTime, Initiator ) world.onEvent( Event ) end +--- Creation of a Takeoff Event. +-- @param #BASE self +-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. +-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +function BASE:CreateEventTakeoff( EventTime, Initiator ) + self:F( { EventTime, Initiator } ) + + local Event = { + id = world.event.S_EVENT_TAKEOFF, + time = EventTime, + initiator = Initiator, + } + + world.onEvent( Event ) +end + -- TODO: Complete Dcs.DCSTypes#Event structure. --- The main event handling function... This function captures all events generated for the class. -- @param #BASE self diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 28a4ecf26..01235b650 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -561,12 +561,13 @@ end -- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param EventID -- @return #EVENT -function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID ) - self:F2( GroupName ) +function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... ) + self:E( GroupName ) local Event = self:Init( EventID, EventClass ) Event.EventGroup = true Event.EventFunction = EventFunction + Event.Params = arg return self end @@ -950,7 +951,7 @@ function EVENT:onEvent( Event ) local Result, Value = xpcall( function() - return EventData.EventFunction( EventClass, Event ) + return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) ) end, ErrorHandler ) else @@ -966,14 +967,14 @@ function EVENT:onEvent( Event ) local Result, Value = xpcall( function() - return EventFunction( EventClass, Event ) + return EventFunction( EventClass, Event, unpack( EventData.Params ) ) end, ErrorHandler ) end end end else -- The EventClass is not alive anymore, we remove it from the EventHandlers... - self:RemoveEvent( EventClass, Event.id ) + --self:RemoveEvent( EventClass, Event.id ) end else diff --git a/Moose Development/Moose/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index dfa15037d..6a99bdc11 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -1109,8 +1109,18 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2 SpawnTemplate.x = PointVec3.x SpawnTemplate.y = PointVec3.z + + local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex ) + + -- When spawned in the air, we need to generate a Takeoff Event + + if Takeoff == GROUP.Takeoff.Air then + for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do + SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 1 ) + end + end - return self:SpawnWithIndex( self.SpawnIndex ) + return GroupSpawned end end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 5b4bc4169..1089224be 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1161,9 +1161,9 @@ do -- Event Handling -- @param Core.Event#EVENTS Event -- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP. -- @return #GROUP - function GROUP:HandleEvent( Event, EventFunction ) + function GROUP:HandleEvent( Event, EventFunction, ... ) - self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event ) + self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event, ... ) return self end