diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index c860f0a48..c3c408b85 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -434,6 +434,8 @@ end function AI_A2A:onafterRTB( AIGroup, From, Event, To ) self:F( { AIGroup, From, Event, To } ) + self:E( "Group " .. self.Controllable:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" ) + if AIGroup and AIGroup:IsAlive() then self.CheckStatus = false @@ -446,7 +448,7 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To ) local CurrentCoord = AIGroup:GetCoordinate() local ToTargetCoord = self.HomeAirbase:GetCoordinate() - local ToTargetSpeed = math.random( self.MinSpeed, self.MaxSpeed ) + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) local Distance = CurrentCoord:Get2DDistance( ToTargetCoord ) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 6249a0641..59b975f37 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -126,9 +126,11 @@ AI_A2A_CAP = { -- @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#Speed EngageMinSpeed The minimum speed of the @{Controllable} in km/h when engaging a target. +-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Controllable} in km/h when engaging a target. -- @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 ) +function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType ) -- Inherits from BASE local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP @@ -136,7 +138,10 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling self.Accomplished = false self.Engaging = false - self:AddTransition( { "Patrolling", "Engaging", "RTB" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + self.EngageMinSpeed = EngageMinSpeed + self.EngageMaxSpeed = EngageMaxSpeed + + self:AddTransition( { "Patrolling", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. --- OnBefore Transition Handler for Event Engage. -- @function [parent=#AI_A2A_CAP] OnBeforeEngage @@ -394,7 +399,7 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) --- Calculate the target route point. local CurrentCoord = AIGroup:GetCoordinate() local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate() - local ToTargetSpeed = math.random( self.MinSpeed, self.MaxSpeed ) + local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed ) local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) --- Create a route point of type air. diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index cf6f0bf31..8528630cc 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -302,7 +302,7 @@ do -- AI_A2A_DISPATCHER --- @param #AI_A2A_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_A2A_DISPATCHER:OnEventCrashOrDead( EventData ) - self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) + self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) end --- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. @@ -513,11 +513,13 @@ do -- AI_A2A_DISPATCHER -- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed. -- @param #number FloorAltitude The minimum altitude at which the cap can be executed. -- @param #number CeilingAltitude the maximum altitude at which the cap can be executed. - -- @param #number MinSpeed The minimum speed at which the cap can be executed. - -- @param #number MaxSpeed The maximum speed at which the cap can be executed. + -- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed. + -- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed. + -- @param #number EngageMinSpeed The minimum speed at which the engage can be executed. + -- @param #number EngageMaxSpeed The maximum speed at which the engage can be executed. -- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude. -- @return #AI_A2A_DISPATCHER - function AI_A2A_DISPATCHER:SetSquadronCap( SquadronName, Zone, FloorAltitude, CeilingAltitude, MinSpeed, MaxSpeed, AltType ) + function AI_A2A_DISPATCHER:SetSquadronCap( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType ) self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} @@ -529,8 +531,10 @@ do -- AI_A2A_DISPATCHER Cap.Zone = Zone Cap.FloorAltitude = FloorAltitude Cap.CeilingAltitude = CeilingAltitude - Cap.MinSpeed = MinSpeed - Cap.MaxSpeed = MaxSpeed + Cap.PatrolMinSpeed = PatrolMinSpeed + Cap.PatrolMaxSpeed = PatrolMaxSpeed + Cap.EngageMinSpeed = EngageMinSpeed + Cap.EngageMaxSpeed = EngageMaxSpeed Cap.AltType = AltType self:SetSquadronCapInterval( SquadronName, 2, 180, 600, 1 ) @@ -615,18 +619,18 @@ do -- AI_A2A_DISPATCHER --- -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. - -- @param #number MinSpeed The minimum speed at which the gci can be executed. - -- @param #number MaxSpeed The maximum speed at which the gci can be executed. + -- @param #number EngageMinSpeed The minimum speed at which the gci can be executed. + -- @param #number EngageMaxSpeed The maximum speed at which the gci can be executed. -- @return #AI_A2A_DISPATCHER - function AI_A2A_DISPATCHER:SetSquadronGci( SquadronName, MinSpeed, MaxSpeed ) + function AI_A2A_DISPATCHER:SetSquadronGci( SquadronName, EngageMinSpeed, EngageMaxSpeed ) self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Intercept = self.DefenderSquadrons[SquadronName].Intercept or {} local Intercept = self.DefenderSquadrons[SquadronName].Intercept Intercept.Name = SquadronName - Intercept.MinSpeed = MinSpeed - Intercept.MaxSpeed = MaxSpeed + Intercept.EngageMinSpeed = EngageMinSpeed + Intercept.EngageMaxSpeed = EngageMaxSpeed end --- Defines the amount of extra planes that will take-off as part of the defense system. @@ -725,7 +729,11 @@ do -- AI_A2A_DISPATCHER for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do if DefenderTask.Type == "CAP" then if AIGroup:IsAlive() then - CapCount = CapCount + 1 + -- Check if the CAP is patrolling or engaging. If not, this is not a valid CAP, even if it is alive! + -- The CAP could be damaged, lost control, or out of fuel! + if DefenderTask.Fsm:Is( "Patrolling" ) or DefenderTask.Fsm:Is( "Engaging" ) then + CapCount = CapCount + 1 + end end end end @@ -772,8 +780,6 @@ do -- AI_A2A_DISPATCHER if Friendly and Friendly:IsAlive() then -- Ok, so we have a friendly near the potential target. -- Now we need to check if the AIGroup has a Task. - self:F( { FriendlyName = Friendly:GetName() } ) - self:F( { FriendlyDistance = FriendlyDistance } ) local DefenderTask = self:GetDefenderTask( Friendly ) if DefenderTask then -- The Task should be CAP or INTERCEPT @@ -783,7 +789,7 @@ do -- AI_A2A_DISPATCHER Friendlies = Friendlies or {} Friendlies[Friendly] = Friendly DefenderCount = DefenderCount + Friendly:GetSize() - self:F( { Friendly = Friendly:GetName() } ) + self:F( { Friendly = Friendly:GetName(), FriendlyDistance = FriendlyDistance } ) end end end @@ -819,7 +825,7 @@ do -- AI_A2A_DISPATCHER if AIGroup then - local Fsm = AI_A2A_CAP:New( AIGroup, Cap.Zone, Cap.FloorAltitude, Cap.CeilingAltitude, Cap.MinSpeed, Cap.MaxSpeed, Cap.AltType ) + local Fsm = AI_A2A_CAP:New( AIGroup, Cap.Zone, Cap.FloorAltitude, Cap.CeilingAltitude, Cap.PatrolMinSpeed, Cap.PatrolMaxSpeed, Cap.EngageMinSpeed, Cap.EngageMaxSpeed, Cap.AltType ) Fsm:SetDispatcher( self ) Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) Fsm:Start() @@ -917,7 +923,7 @@ do -- AI_A2A_DISPATCHER DefendersCount = DefendersCount - AIGroup:GetSize() - local Fsm = AI_A2A_INTERCEPT:New( AIGroup, Intercept.MinSpeed, Intercept.MaxSpeed ) + local Fsm = AI_A2A_INTERCEPT:New( AIGroup, Intercept.EngageMinSpeed, Intercept.EngageMaxSpeed ) Fsm:SetDispatcher( self ) Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) Fsm:Start() @@ -954,6 +960,8 @@ do -- AI_A2A_DISPATCHER -- First, count the active AIGroups Units, targetting the DetectedSet local DefenderCount = self:CountDefendersEngaged( DetectedItem ) local DefenderGroups = self:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) + + self:F( { DefenderCount = DefenderCount } ) -- Only allow ENGAGE when: -- 1. There are friendly units near the detected attackers. @@ -982,6 +990,7 @@ do -- AI_A2A_DISPATCHER -- First, count the active AIGroups Units, targetting the DetectedSet local DefenderCount = self:CountDefendersEngaged( Target ) local DefendersMissing = AttackerCount - DefenderCount + self:F( { AttackerCount = AttackerCount, DefenderCount = DefenderCount, DefendersMissing = DefendersMissing } ) local Friendlies = self:CountDefendersToBeEngaged( Target, DefenderCount ) @@ -1057,9 +1066,9 @@ do -- AI_A2A_DISPATCHER -- Show tactical situation for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do local Defender = Defender -- Wrapper.Group#GROUP - local Message = string.format( "%s, %s", Defender:GetName(), DefenderTask.Type ) + local Message = string.format( "%s ( %s - %s )", Defender:GetName(), DefenderTask.Type, DefenderTask.Fsm:GetState() ) if DefenderTask.Target then - Message = Message .. " => " .. DefenderTask.Target.Index .. " : " .. DefenderTask.Target.Set:GetObjectNames() + Message = Message .. string.format( " => %s : %s", DefenderTask.Target.ItemID, DefenderTask.Target.Set:GetObjectNames() ) end self:F( { Tactical = Message } ) end diff --git a/Moose Development/Moose/AI/AI_A2A_Intercept.lua b/Moose Development/Moose/AI/AI_A2A_Intercept.lua index b1a6dbf76..28e9f8175 100644 --- a/Moose Development/Moose/AI/AI_A2A_Intercept.lua +++ b/Moose Development/Moose/AI/AI_A2A_Intercept.lua @@ -119,7 +119,7 @@ AI_A2A_INTERCEPT = { -- @param #AI_A2A_INTERCEPT self -- @param Wrapper.Group#GROUP AIGroup -- @return #AI_A2A_INTERCEPT -function AI_A2A_INTERCEPT:New( AIGroup, MinSpeed, MaxSpeed ) +function AI_A2A_INTERCEPT:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) -- Inherits from BASE local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_INTERCEPT @@ -127,12 +127,12 @@ function AI_A2A_INTERCEPT:New( AIGroup, MinSpeed, MaxSpeed ) self.Accomplished = false self.Engaging = false - self.MinSpeed = MinSpeed - self.MaxSpeed = MaxSpeed + self.EngageMinSpeed = EngageMinSpeed + self.EngageMaxSpeed = EngageMaxSpeed self.PatrolAltType = "RADIO" - self:AddTransition( { "Started", "Engaging", "RTB" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_INTERCEPT. + self:AddTransition( { "Started", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_INTERCEPT. --- OnBefore Transition Handler for Event Engage. -- @function [parent=#AI_A2A_INTERCEPT] OnBeforeEngage @@ -366,7 +366,7 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate() self:SetTargetDistance( ToTargetCoord ) -- For RTB status check - local ToTargetSpeed = math.random( self.MinSpeed, self.MaxSpeed ) + local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed ) local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) --- Create a route point of type air. @@ -379,7 +379,7 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } ) - self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } ) + self:T2( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } ) EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint @@ -403,7 +403,7 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit if #AttackTasks == 0 then self:E("No targets found -> Going RTB") self:Return() - self:__RTB( 1 ) + self:RTB() else AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_INTERCEPT.InterceptRoute" ) EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks ) @@ -419,7 +419,7 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit else self:E("No targets found -> Going RTB") self:Return() - self:__RTB( 1 ) + self:RTB() end end diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 8e74c793d..51358dee4 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -62,7 +62,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -154,7 +154,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self:Stop( Scheduler, CallID ) end else - self:E( "Scheduled obscolete call for CallID: " .. CallID ) + self:E( "Scheduled obsolete call for CallID: " .. CallID ) end return nil diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 28a5d53be..6844d5e62 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -84,11 +84,6 @@ function SET_BASE:New( Database ) self.TimeInterval = 0.001 self.Set = {} - - self.List = {} - self.List.__index = self.List - self.List = setmetatable( { Count = 0 }, self.List ) - self.Index = {} self.CallScheduler = SCHEDULER:New( self ) @@ -126,24 +121,8 @@ end function SET_BASE:Add( ObjectName, Object ) self:F( ObjectName ) - local t = { _ = Object } - - if self.List.last then - self.List.last._next = t - t._prev = self.List.last - self.List.last = t - else - -- this is the first node - self.List.first = t - self.List.last = t - end - - self.List.Count = self.List.Count + 1 - self.Set[ObjectName] = Object - table.insert( self.Index, ObjectName ) - end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -166,43 +145,19 @@ end -- @param #string ObjectName function SET_BASE:Remove( ObjectName ) - local t = self.Set[ObjectName] + local Object = self.Set[ObjectName] - self:F3( { ObjectName, t } ) + self:F3( { ObjectName, Object } ) - if t then - if t._next then - if t._prev then - t._next._prev = t._prev - t._prev._next = t._next - else - -- this was the first node - t._next._prev = nil - self.List._first = t._next - end - elseif t._prev then - -- this was the last node - t._prev._next = nil - self.List._last = t._prev - else - -- this was the only node - self.List._first = nil - self.List._last = nil - end - - t._next = nil - t._prev = nil - self.List.Count = self.List.Count - 1 - + if Object then for Index, Key in ipairs( self.Index ) do if Key == ObjectName then table.remove( self.Index, Index ) + self.Set[ObjectName] = nil break end end - self.Set[ObjectName] = nil - end end @@ -214,12 +169,10 @@ end function SET_BASE:Get( ObjectName ) self:F( ObjectName ) - local t = self.Set[ObjectName] - - self:T3( { ObjectName, t } ) - - return t + local Object = self.Set[ObjectName] + self:T3( { ObjectName, Object } ) + return Object end --- Gets the first object from the @{Set#SET_BASE} and derived classes. @@ -260,7 +213,7 @@ end -- @return #number Count function SET_BASE:Count() - return #self.Index or 0 + return self.Index and #self.Index or 0 end diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index f880598d0..56be1e97f 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: 20170606_1812' ) +env.info( 'Moose Generation Timestamp: 20170608_1332' ) local base = _G