diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index c8effaeee..32a2a5a9e 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -4024,6 +4024,29 @@ do -- AI_A2G_DISPATCHER end + --- Determine the distance as the keys of reference of the detected items. + -- @param #AI_A2G_DISPATCHER self + function AI_A2G_DISPATCHER:Keys( DetectedItem ) + + self:F( { DetectedItem = DetectedItem } ) + + local AttackCoordinate = self.Detection:GetDetectedItemCoordinate( DetectedItem ) + + local ShortestDistance = 999999999 + + for DefenseCoordinateName, DefenseCoordinate in pairs( self.DefenseCoordinates ) do + local DefenseCoordinate = DefenseCoordinate -- Core.Point#COORDINATE + + local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate ) + + if EvaluateDistance <= ShortestDistance then + ShortestDistance = EvaluateDistance + end + end + + return ShortestDistance + end + --- Assigns A2G AI Tasks in relation to the detected items. -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:Order( DetectedItem ) @@ -4082,7 +4105,7 @@ do -- AI_A2G_DISPATCHER -- Show tactical situation local ThreatLevel = DetectedItem.Set:CalculateThreatLevelA2G() - Report:Add( string.format( " - %1s%s ( %4s ): ( #%d - %4s ) %s" , ( DetectedItem.IsDetected == true ) and "!" or " ", DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Type or " --- ", string.rep( "■", ThreatLevel ) ) ) + Report:Add( string.format( " - %1s%s ( %04s ): ( #%02d - %-4s ) %s" , ( DetectedItem.IsDetected == true ) and "!" or " ", DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Type or " --- ", string.rep( "■", ThreatLevel ) ) ) for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do local Defender = Defender -- Wrapper.Group#GROUP if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then @@ -4203,7 +4226,7 @@ do -- AI_A2G_DISPATCHER -- Now that all obsolete tasks are removed, loop through the detected targets. --for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - for DetectedItemID, DetectedItem in UTILS.spairs( Detection:GetDetectedItems(), function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do + for DetectedDistance, DetectedItem in UTILS.kpairs( Detection:GetDetectedItems(), function( t ) return self:Keys( t ) end, function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do if not self.Detection:IsDetectedItemLocked( DetectedItem ) == true then local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem @@ -4225,44 +4248,43 @@ do -- AI_A2G_DISPATCHER -- Calculate if for this DetectedItem if a defense needs to be initiated. -- This calculation is based on the distance between the defense point and the attackers, and the defensiveness parameter. -- The attackers closest to the defense coordinates will be handled first, or course! - - local EngageCoordinate = nil - - for DefenseCoordinateName, DefenseCoordinate in pairs( self.DefenseCoordinates ) do - local DefenseCoordinate = DefenseCoordinate -- Core.Point#COORDINATE - - local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate ) + + local EngageDefenses = nil - if EvaluateDistance <= self.DefenseRadius then - - local DistanceProbability = ( self.DefenseRadius / EvaluateDistance * self.DefenseReactivity ) + self:F( { DetectedDistance = DetectedDistance, DefenseRadius = self.DefenseRadius } ) + if DetectedDistance <= self.DefenseRadius then + + self:F( { DetectedApproach = self._DefenseApproach } ) + if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Distance then + EngageDefenses = true + self:F( { EngageDefenses = EngageDefenses } ) + end + + if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Random then + local DistanceProbability = ( self.DefenseRadius / DetectedDistance * self.DefenseReactivity ) local DefenseProbability = math.random() - + self:F( { DistanceProbability = DistanceProbability, DefenseProbability = DefenseProbability } ) - - if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Random then - - if DefenseProbability <= DistanceProbability / ( 300 / 30 ) then - EngageCoordinate = DefenseCoordinate - break - end - end - if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Distance then - EngageCoordinate = DefenseCoordinate - break + + if DefenseProbability <= DistanceProbability / ( 300 / 30 ) then + EngageDefenses = true end end + + end + self:F( { EngageDefenses = EngageDefenses, DefenseLimit = self.DefenseLimit, DefenseTotal = DefenseTotal } ) + -- There needs to be an EngageCoordinate. -- If self.DefenseLimit is set (thus limit the amount of defenses to one zone), then only start a new defense if the maximum has not been reached. -- If self.DefenseLimit has not been set, there is an unlimited amount of zones to be defended. - if ( EngageCoordinate and ( self.DefenseLimit and DefenseTotal < self.DefenseLimit ) or not self.DefenseLimit ) then + if ( EngageDefenses and ( self.DefenseLimit and DefenseTotal < self.DefenseLimit ) or not self.DefenseLimit ) then do local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_SEAD( DetectedItem ) -- Returns a SET_UNIT with the SEAD targets to be engaged... if DefendersMissing > 0 then self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } ) - self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "SEAD", EngageCoordinate ) + self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "SEAD" ) end end @@ -4270,7 +4292,7 @@ do -- AI_A2G_DISPATCHER local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_CAS( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged... if DefendersMissing > 0 then self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } ) - self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "CAS", EngageCoordinate ) + self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "CAS" ) end end @@ -4278,7 +4300,7 @@ do -- AI_A2G_DISPATCHER local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_BAI( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged... if DefendersMissing > 0 then self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } ) - self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "BAI", EngageCoordinate ) + self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "BAI" ) end end end diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index b42744659..0673facb1 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -200,7 +200,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:SetAutoAssignTasks( false ) self:SetAutoAcceptTasks( true ) - self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Random ) + self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance ) self:SetFlashStatus( false ) self:HandleEvent( EVENTS.Birth, @@ -210,7 +210,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) if EventData.IniObjectCategory == 1 then local EventGroup = GROUP:Find( EventData.IniDCSGroup ) --self:E( { CommandCenter = self:GetName(), EventGroup = EventGroup:GetName(), HasGroup = self:HasGroup( EventGroup ), EventData = EventData } ) - if EventGroup and self:HasGroup( EventGroup ) then + if EventGroup and EventGroup:IsAlive() and self:HasGroup( EventGroup ) then local CommandCenterMenu = MENU_GROUP:New( EventGroup, self:GetText() ) local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", CommandCenterMenu ) local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportSummary, self, EventGroup ) diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index b0df5edfb..7efa0962d 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1232,12 +1232,16 @@ end --- Report the task status. -- @param #TASK self +-- @param Wrapper.Group#GROUP TaskGroup function TASK:MenuTaskStatus( TaskGroup ) - local ReportText = self:ReportDetails( TaskGroup ) - - self:T( ReportText ) - self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed ) + if TaskGroup:IsAlive() then + + local ReportText = self:ReportDetails( TaskGroup ) + + self:T( ReportText ) + self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed ) + end end diff --git a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua index 59e7d988f..3ca128b98 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua @@ -240,10 +240,13 @@ do -- TASK_CAPTURE_ZONE local DefenseTaskCaptureDispatcher = self.Dispatcher:GetDefenseTaskCaptureDispatcher() -- Tasking.Task_Capture_Dispatcher#TASK_CAPTURE_DISPATCHER if DefenseTaskCaptureDispatcher then - -- Loop through all zones of the Defenses, and check which zone has an assigned task! + -- Loop through all zones of the player Defenses, and check which zone has an assigned task! + -- The Zones collection contains a Task. This Task is checked if it is assigned. + -- If Assigned, then this task will be the task that is the closest to the defense zone. for TaskName, CaptureZone in pairs( DefenseTaskCaptureDispatcher.Zones or {} ) do local Task = CaptureZone.Task -- Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE - if Task then + if Task and Task:IsStateAssigned() then -- We also check assigned. + -- Now we register the defense player zone information to the task report. self.TaskInfo:AddInfo( "Defense Player Zone", Task.ZoneGoal:GetName(), 30, "MOD", Persist ) self.TaskInfo:AddCoordinate( Task.ZoneGoal:GetZone():GetCoordinate(), 31, "MOD", Persist, false, "Defense Player Coordinate" ) end @@ -252,7 +255,7 @@ do -- TASK_CAPTURE_ZONE local DefenseAIA2GDispatcher = self.Dispatcher:GetDefenseAIA2GDispatcher() -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER if DefenseAIA2GDispatcher then - -- Loop through all zones of the Defenses, and check which zone has an assigned task! + -- Loop through all the tasks of the AI Defenses, and check which zone is involved in the defenses and is active! for Defender, Task in pairs( DefenseAIA2GDispatcher:GetDefenderTasks() or {} ) do local DetectedItem = DefenseAIA2GDispatcher:GetDefenderTaskTarget( Defender ) if DetectedItem then diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index f32caa744..62f81945a 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -519,6 +519,32 @@ function UTILS.spairs( t, order ) end end + +-- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first. +function UTILS.kpairs( t, getkey, order ) + -- collect the keys + local keys = {} + local keyso = {} + for k, o in pairs(t) do keys[#keys+1] = k keyso[#keyso+1] = getkey( o ) end + + -- if order function given, sort by it by passing the table and keys a, b, + -- otherwise just sort the keys + if order then + table.sort(keys, function(a,b) return order(t, a, b) end) + else + table.sort(keys) + end + + -- return the iterator function + local i = 0 + return function() + i = i + 1 + if keys[i] then + return keyso[i], t[keys[i]] + end + end +end + -- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order. function UTILS.rpairs( t ) -- collect the keys