From 0f9591464a67d9ff87e207d734033318b1649482 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 15 Mar 2019 09:57:50 +0100 Subject: [PATCH] First version --- Moose Development/Moose/Core/Zone.lua | 4 +- .../Moose/Functional/Detection.lua | 647 +++++++++++++++++- 2 files changed, 636 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 2c00f48f5..70fb83ca3 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -631,8 +631,8 @@ end -- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty. -- @{#ZONE_RADIUS. -- @param #ZONE_RADIUS self --- @param ObjectCategories --- @param UnitCategories +-- @param ObjectCategories An array of categories of the objects to find in the zone. +-- @param UnitCategories An array of unit categories of the objects to find in the zone. -- @usage -- self.Zone:Scan() -- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index c3eb65ccd..31f148f36 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -305,9 +305,9 @@ do -- DETECTION_BASE --- DETECTION constructor. -- @param #DETECTION_BASE self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @param Core.Set#SET_BASE DetectionSet The @{Set} that is used to detect the units. -- @return #DETECTION_BASE self - function DETECTION_BASE:New( DetectionSetGroup ) + function DETECTION_BASE:New( DetectionSet ) -- Inherits from BASE local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE @@ -316,7 +316,7 @@ do -- DETECTION_BASE self.DetectedItemMax = 0 self.DetectedItems = {} - self.DetectionSetGroup = DetectionSetGroup + self.DetectionSet = DetectionSet self.RefreshTimeInterval = 30 @@ -398,7 +398,7 @@ do -- DETECTION_BASE -- @param #string To The To State string. self:AddTransition( "Detecting", "Detect", "Detecting" ) - self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) + self:AddTransition( "Detecting", "Detection", "Detecting" ) --- OnBefore Transition Handler for Event Detect. -- @function [parent=#DETECTION_BASE] OnBeforeDetect @@ -540,10 +540,11 @@ do -- DETECTION_BASE self.DetectedObjects[DetectionObjectName].Distance = 10000000 end - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do + + for DetectionID, DetectionData in pairs( self.DetectionSet:GetSet() ) do --self:F( { DetectionGroupData } ) - self:F( { DetectionGroup = DetectionGroupData:GetName() } ) - self:__DetectionGroup( DetectDelay, DetectionGroupData, DetectionTimeStamp ) -- Process each detection asynchronously. + self:F( { DetectionGroup = DetectionData:GetName() } ) + self:__Detection( DetectDelay, DetectionData, DetectionTimeStamp ) -- Process each detection asynchronously. self.DetectionCount = self.DetectionCount + 1 DetectDelay = DetectDelay + 1 end @@ -555,7 +556,7 @@ do -- DETECTION_BASE -- @param #string To The To State string. -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. -- @param #number DetectionTimeStamp Time stamp of detection event. - function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp ) + function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp ) --self:F( { DetectedObjects = self.DetectedObjects } ) @@ -563,16 +564,16 @@ do -- DETECTION_BASE local HasDetectedObjects = false - if DetectionGroup:IsAlive() then + if Detection:IsAlive() then --self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) - local DetectionGroupName = DetectionGroup:GetName() - local DetectionUnit = DetectionGroup:GetUnit(1) + local DetectionGroupName = Detection:GetName() + local DetectionUnit = Detection:GetUnit(1) local DetectedUnits = {} - local DetectedTargets = DetectionGroup:GetDetectedTargets( + local DetectedTargets = Detection:GetDetectedTargets( self.DetectVisual, self.DetectOptical, self.DetectRadar, @@ -624,7 +625,7 @@ do -- DETECTION_BASE local DetectedObjectVec3 = DetectedObject:getPoint() local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } - local DetectionGroupVec3 = DetectionGroup:GetVec3() + local DetectionGroupVec3 = Detection:GetVec3() local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + @@ -2819,3 +2820,623 @@ do -- DETECTION_AREAS end end + +do -- DETECTION_ZONES + + --- @type DETECTION_ZONES + -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends Functional.Detection#DETECTION_BASE + + --- Detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), + -- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Wrapper.Unit#UNIT}s detected. + -- The class is group the detected units within zones given a DetectedZoneRange parameter. + -- A set with multiple detected zones will be created as there are groups of units detected. + -- + -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones + -- + -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DECTECTION_BASE} and + -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Functional.Detection#DETECTION_ZONES}. + -- + -- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned. + -- + -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZones}(). + -- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZoneCount}(). + -- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZone}() with a given index. + -- + -- ## 4.4) Flare or Smoke detected units + -- + -- Use the methods @{Functional.Detection#DETECTION_ZONES.FlareDetectedUnits}() or @{Functional.Detection#DETECTION_ZONES.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. + -- + -- ## 4.5) Flare or Smoke or Bound detected zones + -- + -- Use the methods: + -- + -- * @{Functional.Detection#DETECTION_ZONES.FlareDetectedZones}() to flare in a color + -- * @{Functional.Detection#DETECTION_ZONES.SmokeDetectedZones}() to smoke in a color + -- * @{Functional.Detection#DETECTION_ZONES.SmokeDetectedZones}() to bound with a tire with a white flag + -- + -- the detected zones when a new detection has taken place. + -- + -- @field #DETECTION_ZONES + DETECTION_ZONES = { + ClassName = "DETECTION_ZONES", + DetectionZoneRange = nil, + } + + + --- DETECTION_ZONES constructor. + -- @param #DETECTION_ZONES self + -- @param Core.Set#SET_ZONE_RADIUS DetectionSetZoneRadius The @{Set} of ZONE_RADIUS. + -- @param DCS#Coalition.side DetectionCoalition The coalition of the detection. + -- @return #DETECTION_ZONES + function DETECTION_ZONES:New( DetectionSetZoneRadius, DetectionCoalition ) + + -- Inherits from DETECTION_BASE + local self = BASE:Inherit( self, DETECTION_BASE:New() ) + + self.DetectionSetZoneRadius = DetectionSetZoneRadius + self.DetectionCoalition = DetectionCoalition + + self._SmokeDetectedUnits = false + self._FlareDetectedUnits = false + self._SmokeDetectedZones = false + self._FlareDetectedZones = false + self._BoundDetectedZones = false + + return self + end + + + --- Report summary of a detected item using a given numeric index. + -- @param #DETECTION_ZONES self + -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. + -- @param Wrapper.Group#GROUP AttackGroup The group to get the settings for. + -- @param Core.Settings#SETTINGS Settings (Optional) Message formatting settings to use. + -- @return Core.Report#REPORT The report of the detection items. + function DETECTION_ZONES:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings ) + self:F( { DetectedItem = DetectedItem } ) + + local DetectedItemID = self:GetDetectedItemID( DetectedItem ) + + if DetectedItem then + local DetectedSet = self:GetDetectedSet( DetectedItem ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedItemZone( DetectedItem ) + local DetectedItemCoordinate = DetectedZone:GetCoordinate() + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings ) + + local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem ) + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemsTypes = DetectedSet:GetTypeNames() + + local Report = REPORT:New() + Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText) + Report:Add( string.format( "Threat: [%s]", string.rep( "■", ThreatLevelA2G ), string.rep( "□", 10-ThreatLevelA2G ) ) ) + Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemsTypes ) ) + Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) ) + + return Report + end + + return nil + end + + --- Report detailed of a detection result. + -- @param #DETECTION_ZONES self + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. + -- @return #string + function DETECTION_ZONES:DetectedReportDetailed( AttackGroup ) --R2.1 Fixed missing report + self:F() + + local Report = REPORT:New() + for DetectedItemIndex, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItem, AttackGroup ) + Report:SetTitle( "Detected areas:" ) + Report:Add( ReportSummary:Text() ) + end + + local ReportText = Report:Text() + + return ReportText + end + + + --- Calculate the optimal intercept point of the DetectedItem. + -- @param #DETECTION_ZONES self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + function DETECTION_ZONES:CalculateIntercept( DetectedItem ) + + local DetectedCoord = DetectedItem.Coordinate + local DetectedSpeed = DetectedCoord:GetVelocity() + local DetectedHeading = DetectedCoord:GetHeading() + + if self.Intercept then + local DetectedSet = DetectedItem.Set + -- todo: speed + + local TranslateDistance = DetectedSpeed * self.InterceptDelay + + local InterceptCoord = DetectedCoord:Translate( TranslateDistance, DetectedHeading ) + + DetectedItem.InterceptCoord = InterceptCoord + else + DetectedItem.InterceptCoord = DetectedCoord + end + + end + + + + --- Smoke the detected units + -- @param #DETECTION_ZONES self + -- @return #DETECTION_ZONES self + function DETECTION_ZONES:SmokeDetectedUnits() + self:F2() + + self._SmokeDetectedUnits = true + return self + end + + --- Flare the detected units + -- @param #DETECTION_ZONES self + -- @return #DETECTION_ZONES self + function DETECTION_ZONES:FlareDetectedUnits() + self:F2() + + self._FlareDetectedUnits = true + return self + end + + --- Smoke the detected zones + -- @param #DETECTION_ZONES self + -- @return #DETECTION_ZONES self + function DETECTION_ZONES:SmokeDetectedZones() + self:F2() + + self._SmokeDetectedZones = true + return self + end + + --- Flare the detected zones + -- @param #DETECTION_ZONES self + -- @return #DETECTION_ZONES self + function DETECTION_ZONES:FlareDetectedZones() + self:F2() + + self._FlareDetectedZones = true + return self + end + + --- Bound the detected zones + -- @param #DETECTION_ZONES self + -- @return #DETECTION_ZONES self + function DETECTION_ZONES:BoundDetectedZones() + self:F2() + + self._BoundDetectedZones = true + return self + end + + --- Make text documenting the changes of the detected zone. + -- @param #DETECTION_ZONES self + -- @param #DETECTION_BASE.DetectedItem DetectedItem + -- @return #string The Changes text + function DETECTION_ZONES:GetChangeText( DetectedItem ) + self:F( DetectedItem ) + + local MT = {} + + for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do + + if ChangeCode == "AA" then + MT[#MT+1] = "Detected new area " .. ChangeData.ID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". Removed the center target." + end + + if ChangeCode == "AAU" then + MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." + end + + if ChangeCode == "RA" then + MT[#MT+1] = "Removed old area " .. ChangeData.ID .. ". No more targets in this area." + end + + if ChangeCode == "AU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Detected for area " .. ChangeData.ID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + if ChangeCode == "RU" then + local MTUT = {} + for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do + if ChangeUnitType ~= "ID" then + MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType + end + end + MT[#MT+1] = "Removed for area " .. ChangeData.ID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + end + + end + + return table.concat( MT, "\n" ) + + end + + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_ZONES self + -- @return #DETECTION_ZONES self + function DETECTION_ZONES:CreateDetectionItems() + + + self:F( "Checking Detected Items for new Detected Units ..." ) + --self:F( { DetectedObjects = self.DetectedObjects } ) + + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. + -- Regroup when needed, split groups when needed. + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + + if DetectedItem then + + self:T2( { "Detected Item ID: ", DetectedItemID } ) + + local Zone = DetectedItem.Zone -- Core.Zone#ZONE_RADIUS + + -- Scan the zone + Zone:Scan( { Object.Category.UNIT }, { Unit.Category.GROUND_UNIT } ) + + local ZoneUnits = Zone:GetScannedUnits() + for DCSUnitID, DCSUnit in pairs( ZoneUnits ) do + local UnitName = DCSUnit:getName() + local ZoneUnit = UNIT:FindByName( UnitName ) + local ZoneUnitCoalition = ZoneUnit:GetCoalition() + if ZoneUnitCoalition == self.DetectionCoalition then + DetectedItem.Set:AddUnit( ZoneUnit ) + end + end + end + end + + + -- Now all the tests should have been build, now make some smoke and flares... + -- We also report here the friendlies within the detected areas. + + for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do + + local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set + local DetectedFirstUnit = DetectedSet:GetFirst() + local DetectedZone = DetectedItem.Zone + + -- Set the last known coordinate to the detection item. + local DetectedZoneCoord = DetectedZone:GetCoordinate() + self:SetDetectedItemCoordinate( DetectedItem, DetectedZoneCoord, DetectedFirstUnit ) + + self:CalculateIntercept( DetectedItem ) + + -- We search for friendlies nearby. + -- If there weren't any friendlies nearby, and now there are friendlies nearby, we flag the area as "changed". + -- If there were friendlies nearby, and now there aren't any friendlies nearby, we flag the area as "changed". + -- This is for the A2G dispatcher to detect if there is a change in the tactical situation. + local OldFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT ) + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + local NewFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT ) + if OldFriendliesNearbyGround ~= NewFriendliesNearbyGround then + DetectedItem.Changed = true + end + + self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level + self:NearestRecce( DetectedItem ) + + + if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedZone.ZoneUNIT:SmokeRed() + end + + --DetectedSet:Flush( self ) + + DetectedSet:ForEachUnit( + --- @param Wrapper.Unit#UNIT DetectedUnit + function( DetectedUnit ) + if DetectedUnit:IsAlive() then + --self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) + if DETECTION_ZONES._FlareDetectedUnits or self._FlareDetectedUnits then + DetectedUnit:FlareGreen() + end + if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then + DetectedUnit:SmokeGreen() + end + end + end + ) + if DETECTION_ZONES._FlareDetectedZones or self._FlareDetectedZones then + DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) + end + if DETECTION_ZONES._SmokeDetectedZones or self._SmokeDetectedZones then + DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) + end + + if DETECTION_ZONES._BoundDetectedZones or self._BoundDetectedZones then + self.CountryID = DetectedSet:GetFirst():GetCountry() + DetectedZone:BoundZone( 12, self.CountryID ) + end + end + + end + + --- @param #DETECTION_ZONES self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Detection The element on which the detection is based. + -- @param #number DetectionTimeStamp Time stamp of detection event. + function DETECTION_ZONES:onafterDetection( From, Event, To, Detection, DetectionTimeStamp ) + + --self:F( { DetectedObjects = self.DetectedObjects } ) + + self.DetectionRun = self.DetectionRun + 1 + + local HasDetectedObjects = false + + if Detection:IsAlive() then + + --self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + + local DetectionGroupName = Detection:GetName() + local DetectionUnit = Detection:GetUnit(1) + + local DetectedUnits = {} + + local DetectedTargets = Detection:GetDetectedTargets( + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:F( { DetectedTargets = DetectedTargets } ) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do + local DetectedObject = Detection.object -- DCS#Object + + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then + local DetectedObjectName = DetectedObject:getName() + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Object = DetectedObject + end + end + end + + for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = DetectedObjectData.Object + + if DetectedObject:isExist() then + + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected( + DetectedObject, + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + --self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) + + -- Only process if the target is visible. Detection also returns invisible units. + --if Detection.visible == true then + + local DetectionAccepted = true + + local DetectedObjectName = DetectedObject:getName() + local DetectedObjectType = DetectedObject:getTypeName() + + local DetectedObjectVec3 = DetectedObject:getPoint() + local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } + local DetectionGroupVec3 = Detection:GetVec3() + local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } + + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 + ) ^ 0.5 / 1000 + + local DetectedUnitCategory = DetectedObject:getDesc().category + + --self:F( { "Detected Target:", DetectionGroupName, DetectedObjectName, DetectedObjectType, Distance, DetectedUnitCategory } ) + + -- Calculate Acceptance + + DetectionAccepted = self._.FilterCategories[DetectedUnitCategory] ~= nil and DetectionAccepted or false + + -- if Distance > 15000 then + -- if DetectedUnitCategory == Unit.Category.GROUND_UNIT or DetectedUnitCategory == Unit.Category.SHIP then + -- if DetectedObject:hasSensors( Unit.SensorType.RADAR, Unit.RadarType.AS ) == false then + -- DetectionAccepted = false + -- end + -- end + -- end + + if self.AcceptRange and Distance * 1000 > self.AcceptRange then + DetectionAccepted = false + end + + if self.AcceptZones then + local AnyZoneDetection = false + for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do + local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE + if AcceptZone:IsVec2InZone( DetectedObjectVec2 ) then + AnyZoneDetection = true + end + end + if not AnyZoneDetection then + DetectionAccepted = false + end + end + + if self.RejectZones then + for RejectZoneID, RejectZone in pairs( self.RejectZones ) do + local RejectZone = RejectZone -- Core.Zone#ZONE_BASE + if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then + DetectionAccepted = false + end + end + end + + -- Calculate additional probabilities + + if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then + local DistanceFactor = Distance / 4 + local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor + local DistanceProbability = 1 - DistanceProbabilityReversed + DistanceProbability = DistanceProbability * 30 / 300 + local Probability = math.random() -- Selects a number between 0 and 1 + --self:T( { Probability, DistanceProbability } ) + if Probability > DistanceProbability then + DetectionAccepted = false + end + end + + if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.AlphaAngleProbability then + local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } + local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) + local Sinus = math.sin( AlphaAngle ) + local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) + local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed + + AlphaAngleProbability = AlphaAngleProbability * 30 / 300 + + local Probability = math.random() -- Selects a number between 0 and 1 + --self:T( { Probability, AlphaAngleProbability } ) + if Probability > AlphaAngleProbability then + DetectionAccepted = false + end + + end + + if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.ZoneProbability then + + for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do + self:F({ZoneData}) + local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE + local ZoneProbability = ZoneData[2] -- #number + ZoneProbability = ZoneProbability * 30 / 300 + + if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then + local Probability = math.random() -- Selects a number between 0 and 1 + --self:T( { Probability, ZoneProbability } ) + if Probability > ZoneProbability then + DetectionAccepted = false + break + end + end + end + end + + if DetectionAccepted then + + HasDetectedObjects = true + + self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + + if TargetIsDetected and TargetIsDetected == true then + self.DetectedObjects[DetectedObjectName].IsDetected = TargetIsDetected + end + + if TargetIsDetected and TargetIsVisible and TargetIsVisible == true then + self.DetectedObjects[DetectedObjectName].IsVisible = TargetIsDetected and TargetIsVisible + end + + if TargetIsDetected and not self.DetectedObjects[DetectedObjectName].KnowType then + self.DetectedObjects[DetectedObjectName].KnowType = TargetIsDetected and TargetKnowType + end + self.DetectedObjects[DetectedObjectName].KnowDistance = TargetKnowDistance -- Detection.distance -- TargetKnowDistance + self.DetectedObjects[DetectedObjectName].LastTime = ( TargetIsDetected and TargetIsVisible == false ) and TargetLastTime + self.DetectedObjects[DetectedObjectName].LastPos = ( TargetIsDetected and TargetIsVisible == false ) and TargetLastPos + self.DetectedObjects[DetectedObjectName].LastVelocity = ( TargetIsDetected and TargetIsVisible == false ) and TargetLastVelocity + + if not self.DetectedObjects[DetectedObjectName].Distance or ( Distance and self.DetectedObjects[DetectedObjectName].Distance > Distance ) then + self.DetectedObjects[DetectedObjectName].Distance = Distance + end + + self.DetectedObjects[DetectedObjectName].DetectionTimeStamp = DetectionTimeStamp + + self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) + + local DetectedUnit = UNIT:FindByName( DetectedObjectName ) + + DetectedUnits[DetectedObjectName] = DetectedUnit + else + -- if beyond the DetectionRange then nullify... + self:F( { DetectedObject = "No more detection for " .. DetectedObjectName } ) + if self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = nil + end + end + + --self:T2( self.DetectedObjects ) + else + -- The previously detected object does not exist anymore, delete from the cache. + self:F( "Removing from DetectedObjects: " .. DetectionObjectName ) + self.DetectedObjects[DetectionObjectName] = nil + end + end + + if HasDetectedObjects then + self:__Detected( 0.1, DetectedUnits ) + end + + end + + if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then + + -- First check if all DetectedObjects were detected. + -- This is important. When there are DetectedObjects in the list, but were not detected, + -- And these remain undetected for more than 60 seconds, then these DetectedObjects will be flagged as not Detected. + -- IsDetected = false! + -- This is used in A2A_TASK_DISPATCHER to initiate fighter sweeping! The TASK_A2A_INTERCEPT tasks will be replaced with TASK_A2A_SWEEP tasks. + for DetectedObjectName, DetectedObject in pairs( self.DetectedObjects ) do + if self.DetectedObjects[DetectedObjectName].IsDetected == true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp + 300 <= DetectionTimeStamp then + self.DetectedObjects[DetectedObjectName].IsDetected = false + end + end + + self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method. + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + self:UpdateDetectedItemDetection( DetectedItem ) + self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list. + if DetectedItem then + self:__DetectedItem( 0.1, DetectedItem ) + end + end + + self:__Detect( self.RefreshTimeInterval ) + end + + end + + + end + + +end