diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 94a7f43b4..985f80f62 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -56,6 +56,7 @@ DATABASE = { GROUPS = {}, PLAYERS = {}, PLAYERSJOINED = {}, + PLAYERUNITS = {}, CLIENTS = {}, CARGOS = {}, AIRBASES = {}, @@ -319,17 +320,19 @@ function DATABASE:AddPlayer( UnitName, PlayerName ) if PlayerName then self:E( { "Add player for unit:", UnitName, PlayerName } ) self.PLAYERS[PlayerName] = UnitName + self.PLAYERUNITS[UnitName] = PlayerName self.PLAYERSJOINED[PlayerName] = PlayerName end end --- Deletes a player from the DATABASE based on the Player Name. -- @param #DATABASE self -function DATABASE:DeletePlayer( PlayerName ) +function DATABASE:DeletePlayer( UnitName, PlayerName ) if PlayerName then self:E( { "Clean player:", PlayerName } ) self.PLAYERS[PlayerName] = nil + self.PLAYERUNITS[UnitName] = PlayerName end end @@ -734,7 +737,7 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event ) if self.PLAYERS[PlayerName] then local Settings = SETTINGS:Set( PlayerName ) Settings:RemovePlayerMenu( Event.IniUnit ) - self:DeletePlayer( PlayerName ) + self:DeletePlayer( Event.IniUnit, PlayerName ) end end end @@ -819,10 +822,10 @@ end -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter. -- @return #DATABASE self -function DATABASE:ForEachGroup( IteratorFunction, ... ) +function DATABASE:ForEachGroup( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.GROUPS ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.GROUPS ) return self end @@ -832,10 +835,10 @@ end -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name. -- @return #DATABASE self -function DATABASE:ForEachPlayer( IteratorFunction, ... ) +function DATABASE:ForEachPlayer( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.PLAYERS ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERS ) return self end @@ -845,14 +848,27 @@ end -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @return #DATABASE self -function DATABASE:ForEachPlayerJoined( IteratorFunction, ... ) +function DATABASE:ForEachPlayerJoined( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.PLAYERSJOINED ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERSJOINED ) return self end +--- Iterate the DATABASE and call an iterator function for each **ALIVE** player UNIT, providing the player UNIT and optional parameters. +-- @param #DATABASE self +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name. +-- @return #DATABASE self +function DATABASE:ForEachPlayerUnit( IteratorFunction, FinalizeFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERUNITS ) + + return self +end + + --- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters. -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter. diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 79b362f01..6ea88c0cb 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1083,6 +1083,22 @@ do -- DETECTION_BASE return DetectedItem.FriendliesNearBy end + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies nearby + function DETECTION_BASE:IsPlayersNearBy( DetectedItem ) + + return DetectedItem.PlayersNearBy ~= nil or false + end + + --- Returns friendly units nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs. + function DETECTION_BASE:GetPlayersNearBy( DetectedItem ) + + return DetectedItem.PlayersNearBy + end + --- Background worker function to determine if there are friendlies nearby ... -- @param #DETECTION_BASE self function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) @@ -1106,36 +1122,52 @@ do -- DETECTION_BASE } - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} - DetectedItem.FriendliesNearBy[FoundUnitName] = UNIT:Find( FoundDCSUnit ) - return false - end - - return true + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + + self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} + DetectedItem.FriendliesNearBy[FoundUnitName] = UNIT:Find( FoundDCSUnit ) + return false + end + + return true end world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + + DetectedItem.PlayersNearBy = nil + DetectedItem.PlayersNearBy = DetectedItem.PlayersNearBy or {} + local DetectionZone = ZONE_UNIT:New( "DetectionPlayers", DetectedUnit, self.FriendliesRange ) + + _DATABASE:ForEachPlayer( + --- @param Wrapper.Unit#UNIT PlayerUnit + function( PlayerUnitName ) + local PlayerUnit = UNIT:FindByName( PlayerUnitName ) + if PlayerUnit:IsInZone(DetectionZone) then + DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} + local PlayerUnitName = PlayerUnit:GetName() + DetectedItem.PlayersNearBy[PlayerUnitName] = PlayerUnit + end + end + ) end end diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index a7de46555..c0484b05f 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -7,7 +7,7 @@ _EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER --- Declare the main database object, which is used internally by the MOOSE classes. -_DATABASE = DATABASE:New() -- Database#DATABASE +_DATABASE = DATABASE:New() -- Core.Database#DATABASE _SETTINGS = SETTINGS:Set() _SETTINGS:SetSystemMenu( nil ) diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 7283d50d9..097a5c95b 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -77,7 +77,7 @@ do -- DETECTION MANAGER self:SetReportDisplayTime( 25 ) self:E( { Detection = Detection } ) - Detection:__Start( 1 ) + Detection:__Start( 3 ) return self end diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 68396bbac..c5cb49ebd 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -339,28 +339,28 @@ do -- TASK_A2A end -do -- TASK_INTERCEPT +do -- TASK_A2A_INTERCEPT - --- The TASK_INTERCEPT class - -- @type TASK_INTERCEPT + --- The TASK_A2A_INTERCEPT class + -- @type TASK_A2A_INTERCEPT -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_INTERCEPT = { - ClassName = "TASK_INTERCEPT", + TASK_A2A_INTERCEPT = { + ClassName = "TASK_A2A_INTERCEPT", } - --- Instantiates a new TASK_INTERCEPT. - -- @param #TASK_INTERCEPT self + --- Instantiates a new TASK_A2A_INTERCEPT. + -- @param #TASK_A2A_INTERCEPT self -- @param Tasking.Mission#MISSION Mission -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Core.Set#SET_UNIT TargetSetUnit -- @param #string TaskBriefing The briefing of the task. - -- @return #TASK_INTERCEPT self - function TASK_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) - local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_INTERCEPT + -- @return #TASK_A2A_INTERCEPT self + function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT self:F() Mission:AddTask( self ) @@ -386,3 +386,51 @@ do -- TASK_INTERCEPT end + +do -- TASK_A2A_ENGAGE + + --- The TASK_A2A_ENGAGE class + -- @type TASK_A2A_ENGAGE + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_A2A_ENGAGE = { + ClassName = "TASK_A2A_ENGAGE", + } + + + + --- Instantiates a new TASK_A2A_ENGAGE. + -- @param #TASK_A2A_ENGAGE self + -- @param Tasking.Mission#MISSION Mission + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2A_ENGAGE self + function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE + self:F() + + Mission:AddTask( self ) + + --TODO: Add BR, Altitude, type of planes... + + self:SetBriefing( + TaskBriefing or + "Bogeys are nearby! Those players who are near to the intruders are requested to ENGAGE!\n" + ) + + local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate() + TargetCoordinate:SetModeA2A() + self:SetInfo( "Coordinates", TargetCoordinate ) + + self:SetInfo( "ThreatLevel", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" ) + local DetectedItemsCount = TargetSetUnit:Count() + local DetectedItemsTypes = TargetSetUnit:GetTypeNames() + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) ) + + return self + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 0967846c9..d4bf117dc 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -104,6 +104,7 @@ do -- TASK_A2A_DISPATCHER local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone + if true then -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. @@ -117,6 +118,34 @@ do -- TASK_A2A_DISPATCHER return nil end + --- Creates an ENGAGE task when there are human friendlies airborne near the targets. + -- @param #TASK_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 TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) + + + if PlayersCount > 0 then + + -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. + 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 + + --- Evaluates the removal of the Task from the Mission. @@ -187,6 +216,53 @@ do -- TASK_A2A_DISPATCHER return FriendliesCount, FriendlyTypesReport end + --- Calculates which HUMAN friendlies are nearby the area + -- @param #TASK_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) + + local DetectedSet = DetectedItem.Set + local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) + + local PlayerTypes = {} + local PlayersCount = 0 + + if PlayersNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do + local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + self:E( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) + if PlayerUnit:IsAirPlane() and PlayerName ~= nil then + local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() + PlayersCount = PlayersCount + 1 + local PlayerType = PlayerUnit:GetTypeName() + PlayerTypes[PlayerName] = PlayerType + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + self:E( { PlayersCount = PlayersCount } ) + + local PlayerTypesReport = REPORT:New() + + if PlayersCount > 0 then + for PlayerName, PlayerType in pairs( PlayerTypes ) do + PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) ) + end + else + PlayerTypesReport:Add( "-" ) + end + + + return PlayersCount, PlayerTypesReport + end + + --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. @@ -222,10 +298,14 @@ do -- TASK_A2A_DISPATCHER -- Evaluate INTERCEPT if not Task then - local TaskName = string.format( "INTERCEPT.%03d", DetectedID ) - local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... if TargetSetUnit then - Task = TASK_INTERCEPT:New( Mission, self.SetGroup, TaskName, TargetSetUnit ) + Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit ) + else + local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + if TargetSetUnit then + Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit ) + end end if Task then @@ -243,6 +323,9 @@ do -- TASK_A2A_DISPATCHER local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem ) Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ) ) + + local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) + Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ) ) -- OK, so the tasking has been done, now delete the changes reported for the area. Detection:AcceptChanges( DetectedItem ) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index f65693db6..d4e7a6148 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -315,7 +315,8 @@ function UNIT:GetPlayerName() return PlayerName end - return nil + return nil + end --- Returns the unit's number in the group.