diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 6b281d920..3b85d0d6b 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -81,6 +81,7 @@ DATABASE = { HITS = {}, DESTROYS = {}, ZONES = {}, + ZONES_GOAL = {}, } local _DATABASECoalition = @@ -338,6 +339,39 @@ do -- Zones end -- zone +do -- Zone_Goal + + --- Finds a @{Zone} based on the zone name. + -- @param #DATABASE self + -- @param #string ZoneName The name of the zone. + -- @return Core.Zone#ZONE_BASE The found ZONE. + function DATABASE:FindZoneGoal( ZoneName ) + + local ZoneFound = self.ZONES_GOAL[ZoneName] + return ZoneFound + end + + --- Adds a @{Zone} based on the zone name in the DATABASE. + -- @param #DATABASE self + -- @param #string ZoneName The name of the zone. + -- @param Core.Zone#ZONE_BASE Zone The zone. + function DATABASE:AddZoneGoal( ZoneName, Zone ) + + if not self.ZONES_GOAL[ZoneName] then + self.ZONES_GOAL[ZoneName] = Zone + end + end + + + --- Deletes a @{Zone} from the DATABASE based on the zone name. + -- @param #DATABASE self + -- @param #string ZoneName The name of the zone. + function DATABASE:DeleteZoneGoal( ZoneName ) + + self.ZONES_GOAL[ZoneName] = nil + end + +end -- Zone_Goal do -- cargo --- Adds a Cargo based on the Cargo Name in the DATABASE. diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 0729dd498..4a6b96448 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -189,7 +189,9 @@ world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001 world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1002 world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003 -world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1004 +world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004 +world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005 +world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006 --- The different types of events supported by MOOSE. @@ -226,6 +228,8 @@ EVENTS = { DeleteCargo = world.event.S_EVENT_DELETE_CARGO, NewZone = world.event.S_EVENT_NEW_ZONE, DeleteZone = world.event.S_EVENT_DELETE_ZONE, + NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL, + DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL, RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, } @@ -462,6 +466,16 @@ local _EVENTMETA = { Event = "OnEventDeleteZone", Text = "S_EVENT_DELETE_ZONE" }, + [EVENTS.NewZoneGoal] = { + Order = 1, + Event = "OnEventNewZoneGoal", + Text = "S_EVENT_NEW_ZONE_GOAL" + }, + [EVENTS.DeleteZoneGoal] = { + Order = 1, + Event = "OnEventDeleteZoneGoal", + Text = "S_EVENT_DELETE_ZONE_GOAL" + }, [EVENTS.RemoveUnit] = { Order = -1, Event = "OnEventRemoveUnit", @@ -800,6 +814,38 @@ do -- Event Creation world.onEvent( Event ) end + --- Creation of a New ZoneGoal Event. + -- @param #EVENT self + -- @param Core.Functional#ZONE_GOAL ZoneGoal The ZoneGoal created. + function EVENT:CreateEventNewZoneGoal( ZoneGoal ) + self:F( { ZoneGoal } ) + + local Event = { + id = EVENTS.NewZoneGoal, + time = timer.getTime(), + ZoneGoal = ZoneGoal, + } + + world.onEvent( Event ) + end + + + --- Creation of a ZoneGoal Deletion Event. + -- @param #EVENT self + -- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created. + function EVENT:CreateEventDeleteZoneGoal( ZoneGoal ) + self:F( { ZoneGoal } ) + + local Event = { + id = EVENTS.DeleteZoneGoal, + time = timer.getTime(), + ZoneGoal = ZoneGoal, + } + + world.onEvent( Event ) + end + + --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. -- @param #EVENT self -- @param Wrapper.Unit#UNIT PlayerUnit. diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 8834dcb0b..4084e6deb 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -5497,4 +5497,324 @@ do -- SET_ZONE return nil end -end \ No newline at end of file +end + +do -- SET_ZONE_GOAL + + --- @type SET_ZONE_GOAL + -- @extends Core.Set#SET_BASE + + --- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types. + -- + -- ## SET_ZONE_GOAL constructor + -- + -- Create a new SET_ZONE_GOAL object with the @{#SET_ZONE_GOAL.New} method: + -- + -- * @{#SET_ZONE_GOAL.New}: Creates a new SET_ZONE_GOAL object. + -- + -- ## Add or Remove ZONEs from SET_ZONE_GOAL + -- + -- ZONEs can be added and removed using the @{Core.Set#SET_ZONE_GOAL.AddZonesByName} and @{Core.Set#SET_ZONE_GOAL.RemoveZonesByName} respectively. + -- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE_GOAL. + -- + -- ## SET_ZONE_GOAL filter criteria + -- + -- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL. + -- Filter criteria are defined by: + -- + -- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern of prefix. + -- + -- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using: + -- + -- * @{#SET_ZONE_GOAL.FilterStart}: Starts the filtering of the zones within the SET_ZONE_GOAL. + -- + -- ## SET_ZONE_GOAL iterators + -- + -- Once the filters have been defined and the SET_ZONE_GOAL has been built, you can iterate the SET_ZONE_GOAL with the available iterator methods. + -- The iterator methods will walk the SET_ZONE_GOAL set, and call for each airbase within the set a function that you provide. + -- The following iterator methods are currently available within the SET_ZONE_GOAL: + -- + -- * @{#SET_ZONE_GOAL.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE_GOAL. + -- + -- === + -- @field #SET_ZONE_GOAL SET_ZONE_GOAL + SET_ZONE_GOAL = { + ClassName = "SET_ZONE_GOAL", + Zones = {}, + Filter = { + Prefixes = nil, + }, + FilterMeta = { + }, + } + + + --- Creates a new SET_ZONE_GOAL object, building a set of zones. + -- @param #SET_ZONE_GOAL self + -- @return #SET_ZONE_GOAL self + -- @usage + -- -- Define a new SET_ZONE_GOAL Object. The DatabaseSet will contain a reference to all Zones. + -- DatabaseSet = SET_ZONE_GOAL:New() + function SET_ZONE_GOAL:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES_GOAL ) ) + + return self + end + + --- Add ZONEs to SET_ZONE_GOAL. + -- @param Core.Set#SET_ZONE_GOAL self + -- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object. + -- @return self + function SET_ZONE_GOAL:AddZone( Zone ) + + self:Add( Zone:GetName(), Zone ) + + return self + end + + + --- Remove ZONEs from SET_ZONE_GOAL. + -- @param Core.Set#SET_ZONE_GOAL self + -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. + -- @return self + function SET_ZONE_GOAL:RemoveZonesByName( RemoveZoneNames ) + + local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } + + for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do + self:Remove( RemoveZoneName ) + end + + return self + end + + + --- Finds a Zone based on the Zone Name. + -- @param #SET_ZONE_GOAL self + -- @param #string ZoneName + -- @return Core.Zone#ZONE_BASE The found Zone. + function SET_ZONE_GOAL:FindZone( ZoneName ) + + local ZoneFound = self.Set[ZoneName] + return ZoneFound + end + + + --- Get a random zone from the set. + -- @param #SET_ZONE_GOAL self + -- @return Core.Zone#ZONE_BASE The random Zone. + -- @return #nil if no zone in the collection. + function SET_ZONE_GOAL:GetRandomZone() + + if self:Count() ~= 0 then + + local Index = self.Index + local ZoneFound = nil -- Core.Zone#ZONE_BASE + + -- Loop until a zone has been found. + -- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected. + -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! + while not ZoneFound do + local ZoneRandom = math.random( 1, #Index ) + ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() + end + + return ZoneFound + end + + return nil + end + + + --- Set a zone probability. + -- @param #SET_ZONE_GOAL self + -- @param #string ZoneName The name of the zone. + function SET_ZONE_GOAL:SetZoneProbability( ZoneName, ZoneProbability ) + local Zone = self:FindZone( ZoneName ) + Zone:SetZoneProbability( ZoneProbability ) + end + + + + + --- Builds a set of zones of defined zone prefixes. + -- All the zones starting with the given prefixes will be included within the set. + -- @param #SET_ZONE_GOAL self + -- @param #string Prefixes The prefix of which the zone name starts with. + -- @return #SET_ZONE_GOAL self + function SET_ZONE_GOAL:FilterPrefixes( Prefixes ) + if not self.Filter.Prefixes then + self.Filter.Prefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.Prefixes[Prefix] = Prefix + end + return self + end + + + --- Starts the filtering. + -- @param #SET_ZONE_GOAL self + -- @return #SET_ZONE_GOAL self + function SET_ZONE_GOAL:FilterStart() + + if _DATABASE then + + -- We initialize the first set. + for ObjectName, Object in pairs( self.Database ) do + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:RemoveZonesByName( ObjectName ) + end + end + end + + self:HandleEvent( EVENTS.NewZoneGoal ) + self:HandleEvent( EVENTS.DeleteZoneGoal ) + + return self + end + + --- Stops the filtering for the defined collection. + -- @param #SET_ZONE_GOAL self + -- @return #SET_ZONE_GOAL self + function SET_ZONE_GOAL:FilterStop() + + self:UnHandleEvent( EVENTS.NewZoneGoal ) + self:UnHandleEvent( EVENTS.DeleteZoneGoal ) + + return self + end + + --- Handles the Database to check on an event (birth) that the Object was added in the Database. + -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! + -- @param #SET_ZONE_GOAL self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the AIRBASE + -- @return #table The AIRBASE + function SET_ZONE_GOAL:AddInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end + + --- Handles the Database to check on any event that Object exists in the Database. + -- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! + -- @param #SET_ZONE_GOAL self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the AIRBASE + -- @return #table The AIRBASE + function SET_ZONE_GOAL:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end + + --- Iterate the SET_ZONE_GOAL and call an interator function for each ZONE, providing the ZONE and optional parameters. + -- @param #SET_ZONE_GOAL self + -- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE_GOAL. The function needs to accept a AIRBASE parameter. + -- @return #SET_ZONE_GOAL self + function SET_ZONE_GOAL:ForEachZone( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self + end + + + --- + -- @param #SET_ZONE_GOAL self + -- @param Core.Zone#ZONE_BASE MZone + -- @return #SET_ZONE_GOAL self + function SET_ZONE_GOAL:IsIncludeObject( MZone ) + self:F2( MZone ) + + local MZoneInclude = true + + if MZone then + local MZoneName = MZone:GetName() + + if self.Filter.Prefixes then + local MZonePrefix = false + for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do + self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) + if string.find( MZoneName, ZonePrefix, 1 ) then + MZonePrefix = true + end + end + self:T( { "Evaluated Prefix", MZonePrefix } ) + MZoneInclude = MZoneInclude and MZonePrefix + end + end + + self:T2( MZoneInclude ) + return MZoneInclude + end + + --- Handles the OnEventNewZone event for the Set. + -- @param #SET_ZONE_GOAL self + -- @param Core.Event#EVENTDATA EventData + function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData ) + + self:I( { "New Zone Capture Coalition", EventData } ) + self:I( { "Zone Capture Coalition", EventData.ZoneGoal } ) + + if EventData.ZoneGoal then + if EventData.ZoneGoal and self:IsIncludeObject( EventData.ZoneGoal ) then + self:I( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } ) + self:Add( EventData.ZoneGoal.ZoneName , EventData.ZoneGoal ) + end + end + end + + --- Handles the OnDead or OnCrash event for alive units set. + -- @param #SET_ZONE_GOAL self + -- @param Core.Event#EVENTDATA EventData + function SET_ZONE_GOAL:OnEventDeleteZoneGoal( EventData ) --R2.1 + self:F3( { EventData } ) + + if EventData.ZoneGoal then + local Zone = _DATABASE:FindZone( EventData.ZoneGoal.ZoneName ) + if Zone and Zone.ZoneName then + + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. + -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. + -- And this is a problem because it will remove all entries from the SET_ZONE_GOALs. + -- To prevent this from happening, the Zone object has a flag NoDestroy. + -- When true, the SET_ZONE_GOAL won't Remove the Zone object from the set. + -- This flag is switched off after the event handlers have been called in the EVENT class. + self:F( { ZoneNoDestroy=Zone.NoDestroy } ) + if Zone.NoDestroy then + else + self:Remove( Zone.ZoneName ) + end + end + end + end + + --- Validate if a coordinate is in one of the zones in the set. + -- Returns the ZONE object where the coordiante is located. + -- If zones overlap, the first zone that validates the test is returned. + -- @param #SET_ZONE_GOAL self + -- @param Core.Point#COORDINATE Coordinate The coordinate to be searched. + -- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location. + -- @return #nil No zone has been found. + function SET_ZONE_GOAL:IsCoordinateInZone( Coordinate ) + + for _, Zone in pairs( self:GetSet() ) do + local Zone = Zone -- Core.Zone#ZONE_BASE + if Zone:IsCoordinateInZone( Coordinate ) then + return Zone + end + end + + return nil + end + +end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 52c27fd05..37608d7f9 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -120,7 +120,7 @@ ZONE_BASE = { -- @param #string ZoneName Name of the zone. -- @return #ZONE_BASE self function ZONE_BASE:New( ZoneName ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, FSM:New() ) self:F( ZoneName ) self.ZoneName = ZoneName diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 32a860aa8..ce32338cc 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -549,6 +549,9 @@ do -- ZONE_CAPTURE_COALITION -- If it is, then we must move the zone to attack state. self:HandleEvent( EVENTS.Hit, self.OnEventHit ) + -- ZoneGoal objects are added to the _DATABASE.ZONES_GOAL and SET_ZONE_GOAL sets. + _EVENTDISPATCHER:CreateEventNewZoneGoal( self ) + return self end @@ -566,7 +569,7 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:IsGuarded() - local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition ) + local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition ) self:F( { IsGuarded = IsGuarded } ) return IsGuarded end @@ -574,7 +577,7 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:IsEmpty() - local IsEmpty = self.Zone:IsNoneInZone() + local IsEmpty = self:IsNoneInZone() self:F( { IsEmpty = IsEmpty } ) return IsEmpty end @@ -582,7 +585,7 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:IsCaptured() - local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition ) + local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition ) self:F( { IsCaptured = IsCaptured } ) return IsCaptured end @@ -590,7 +593,7 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:IsAttacked() - local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) + local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition ) self:F( { IsAttacked = IsAttacked } ) return IsAttacked end @@ -601,7 +604,7 @@ do -- ZONE_CAPTURE_COALITION -- @param #ZONE_CAPTURE_COALITION self function ZONE_CAPTURE_COALITION:Mark() - local Coord = self.Zone:GetCoordinate() + local Coord = self:GetCoordinate() local ZoneName = self:GetZoneName() local State = self:GetState() @@ -640,7 +643,7 @@ do -- ZONE_CAPTURE_COALITION --self:GetParent( self ):onenterCaptured() - local NewCoalition = self.Zone:GetScannedCoalition() + local NewCoalition = self:GetScannedCoalition() self:F( { NewCoalition = NewCoalition } ) self:SetCoalition( NewCoalition ) @@ -680,7 +683,7 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:IsCaptured() - local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition ) + local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition ) self:F( { IsCaptured = IsCaptured } ) return IsCaptured end @@ -688,7 +691,7 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:IsAttacked() - local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) + local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition ) self:F( { IsAttacked = IsAttacked } ) return IsAttacked end @@ -803,7 +806,7 @@ do -- ZONE_CAPTURE_COALITION local UnitHit = EventData.TgtUnit if UnitHit then - if UnitHit:IsInZone( self.Zone ) then + if UnitHit:IsInZone( self ) then self:Attack() end end diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index 0caabc8a3..67f6a872b 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -44,14 +44,13 @@ do -- Zone --- ZONE_GOAL Constructor. -- @param #ZONE_GOAL self - -- @param Core.Zone#ZONE_BASE Zone A @{Zone} object with the goal to be achieved. + -- @param Core.Zone#ZONE_RADIUS Zone A @{Zone} object with the goal to be achieved. -- @return #ZONE_GOAL function ZONE_GOAL:New( Zone ) - local self = BASE:Inherit( self, FSM:New() ) -- #ZONE_GOAL + local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL self:F( { Zone = Zone } ) - self.Zone = Zone -- Core.Zone#ZONE_BASE self.Goal = GOAL:New() self.SmokeTime = nil @@ -67,6 +66,7 @@ do -- Zone -- @param Wrapper.Unit#UNIT DestroyedUnit The destroyed unit. -- @param #string PlayerName The name of the player. + return self end @@ -74,7 +74,7 @@ do -- Zone -- @param #ZONE_GOAL self -- @return Core.Zone#ZONE_BASE function ZONE_GOAL:GetZone() - return self.Zone + return self end @@ -82,7 +82,7 @@ do -- Zone -- @param #ZONE_GOAL self -- @return #string function ZONE_GOAL:GetZoneName() - return self.Zone:GetName() + return self:GetName() end @@ -101,7 +101,7 @@ do -- Zone -- @param #ZONE_GOAL self -- @param #SMOKECOLOR.Color FlareColor function ZONE_GOAL:Flare( FlareColor ) - self.Zone:FlareZone( FlareColor, math.random( 1, 360 ) ) + self:FlareZone( FlareColor, math.random( 1, 360 ) ) end @@ -130,7 +130,7 @@ do -- Zone if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then if self.SmokeColor then - self.Zone:GetCoordinate():Smoke( self.SmokeColor ) + self:GetCoordinate():Smoke( self.SmokeColor ) --self.SmokeColor = nil self.SmokeTime = CurrentTime end @@ -147,11 +147,9 @@ do -- Zone local Vec3 = EventData.IniDCSUnit:getPosition().p self:F( { Vec3 = Vec3 } ) - local ZoneGoal = self:GetZone() - self:F({ZoneGoal}) if EventData.IniDCSUnit then - if ZoneGoal:IsVec3InZone(Vec3) then + if self:IsVec3InZone(Vec3) then local PlayerHits = _DATABASE.HITS[EventData.IniUnitName] if PlayerHits then for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do diff --git a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua index ba86ffe95..1de5218f3 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua @@ -104,7 +104,7 @@ do -- ZoneGoal local State = self:GetState() self:F( { State = self:GetState() } ) - self.Zone:Scan( { Object.Category.UNIT, Object.Category.STATIC } ) + self:Scan( { Object.Category.UNIT, Object.Category.STATIC } ) end diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index dcef1bb72..2ec90ab27 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -180,7 +180,7 @@ COMMANDCENTER = { COMMANDCENTER.AutoAssignMethods = { ["Random"] = 1, ["Distance"] = 2, - ["Priority"] = 3 + ["Priority"] = 3, } --- The constructor takes an IDENTIFIABLE as the HQ command center. diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 68c8a4803..1a4f8b876 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1111,6 +1111,11 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) end local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + if not self.FlashTaskStatus then + local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, true ):SetTime( MenuTime ):SetTag( "Tasking" ) + else + local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Stop Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, nil ):SetTime( MenuTime ):SetTag( "Tasking" ) + end end end @@ -1234,6 +1239,24 @@ function TASK:MenuTaskStatus( TaskGroup ) end +--- Report the task status. +-- @param #TASK self +function TASK:MenuFlashTaskStatus( TaskGroup, Flash ) + + self.FlashTaskStatus = Flash + + if self.FlashTaskStatus then + self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60 ) + else + if self.FlashTaskScheduler then + self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID ) + self.FlashTaskScheduler = nil + self.FlashTaskScheduleID = nil + end + end + +end + --- Report the task status. -- @param #TASK self function TASK:MenuTaskAbort( TaskGroup ) diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 54c59410d..5ece5f005 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -58,10 +58,10 @@ end -- @param #TASKINFO.Detail Detail The detail Level. -- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. -- @return #TASKINFO self -function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep ) - self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail } ) +function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep, ShowKey, Type ) + self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } ) if Keep == true then - self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail } ) + self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } ) end return self end @@ -124,8 +124,8 @@ end -- @param #TASKINFO.Detail Detail The detail Level. -- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. -- @return #TASKINFO self -function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep ) - self:AddInfo( "Coordinate", Coordinate, Order, Detail, Keep ) +function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep, ShowKey, Name ) + self:AddInfo( Name or "Coordinate", Coordinate, Order, Detail, Keep, ShowKey, "Coordinate" ) return self end @@ -133,8 +133,8 @@ end --- Get the Coordinate. -- @param #TASKINFO self -- @return Core.Point#COORDINATE Coordinate -function TASKINFO:GetCoordinate() - return self:GetData( "Coordinate" ) +function TASKINFO:GetCoordinate( Name ) + return self:GetData( Name or "Coordinate" ) end @@ -308,10 +308,11 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task ) if Data.Detail:find( Detail ) then local Text = "" + local ShowKey = ( Data.ShowKey == nil or Data.ShowKey == true ) if Key == "TaskName" then Key = nil Text = Data.Data - elseif Key == "Coordinate" then + elseif Data.Type and Data.Type == "Coordinate" then local Coordinate = Data.Data -- Core.Point#COORDINATE Text = Coordinate:ToString( ReportGroup:GetUnit(1), nil, Task ) elseif Key == "Threat" then @@ -357,7 +358,7 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task ) end if Text ~= "" then - LineReport:Add( ( Key and ( Key .. ":" ) or "" ) .. Text ) + LineReport:Add( ( ( Key and ShowKey == true ) and ( Key .. ": " ) or "" ) .. Text ) end end diff --git a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua index c7c3227da..7f1fa7d08 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua @@ -214,6 +214,26 @@ do -- TASK_CAPTURE_DISPATCHER end + --- Link an AI A2G dispatcher from the other coalition to understand its plan for defenses. + -- This is used for the tactical overview, so the players also know the zones attacked by the other AI A2G dispatcher! + -- @param #TASK_CAPTURE_DISPATCHER self + -- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER DefenseAIA2GDispatcher + function TASK_CAPTURE_DISPATCHER:SetDefenseAIA2GDispatcher( DefenseAIA2GDispatcher ) + + self.DefenseAIA2GDispatcher = DefenseAIA2GDispatcher + end + + + --- Get the linked AI A2G dispatcher from the other coalition to understand its plan for defenses. + -- This is used for the tactical overview, so the players also know the zones attacked by the AI A2G dispatcher! + -- @param #TASK_CAPTURE_DISPATCHER self + -- @return AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER + function TASK_CAPTURE_DISPATCHER:GetDefenseAIA2GDispatcher() + + return self.DefenseAIA2GDispatcher + end + + --- Add a capture zone task. -- @param #TASK_CAPTURE_DISPATCHER self -- @param #string TaskPrefix (optional) The prefix of the capture zone task. @@ -303,6 +323,7 @@ do -- TASK_CAPTURE_DISPATCHER self.AI_A2G_Dispatcher:Unlock( Task.TaskZoneName ) -- This will unlock the zone to be defended by AI. end CaptureZone.Task:UpdateTaskInfo() + CaptureZone.Task.ZoneGoal.Attacked = true end function CaptureZone.Task.OnEnterSuccess( Task, From, Event, To ) @@ -311,6 +332,7 @@ do -- TASK_CAPTURE_DISPATCHER self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. end CaptureZone.Task:UpdateTaskInfo() + CaptureZone.Task.ZoneGoal.Attacked = false end function CaptureZone.Task.OnEnterCancelled( Task, From, Event, To ) @@ -319,6 +341,7 @@ do -- TASK_CAPTURE_DISPATCHER self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. end CaptureZone.Task:UpdateTaskInfo() + CaptureZone.Task.ZoneGoal.Attacked = false end function CaptureZone.Task.OnEnterFailed( Task, From, Event, To ) @@ -327,6 +350,7 @@ do -- TASK_CAPTURE_DISPATCHER self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. end CaptureZone.Task:UpdateTaskInfo() + CaptureZone.Task.ZoneGoal.Attacked = false end function CaptureZone.Task.OnEnterAborted( Task, From, Event, To ) @@ -335,6 +359,7 @@ do -- TASK_CAPTURE_DISPATCHER self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. end CaptureZone.Task:UpdateTaskInfo() + CaptureZone.Task.ZoneGoal.Attacked = false end -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher. @@ -344,6 +369,7 @@ do -- TASK_CAPTURE_DISPATCHER self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. end CaptureZone.Task:UpdateTaskInfo() + CaptureZone.Task.ZoneGoal.Attacked = false end end diff --git a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua index 73032a60d..59e7d988f 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua @@ -230,9 +230,11 @@ do -- TASK_CAPTURE_ZONE self.TaskInfo:AddCoordinate( ZoneCoordinate, 1, "SOD", Persist ) -- self.TaskInfo:AddText( "Zone Name", self.ZoneGoal:GetZoneName(), 10, "MOD", Persist ) -- self.TaskInfo:AddText( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11, "MOD", Persist ) - local SetUnit = self.ZoneGoal.Zone:GetScannedSetUnit() + local SetUnit = self.ZoneGoal:GetScannedSetUnit() local ThreatLevel, ThreatText = SetUnit:CalculateThreatLevelA2G() + local ThreatCount = SetUnit:Count() self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 20, "MOD", Persist ) + self.TaskInfo:AddInfo( "Remaining Units", ThreatCount, 21, "MOD", Persist, true) if self.Dispatcher then local DefenseTaskCaptureDispatcher = self.Dispatcher:GetDefenseTaskCaptureDispatcher() -- Tasking.Task_Capture_Dispatcher#TASK_CAPTURE_DISPATCHER @@ -242,8 +244,22 @@ do -- TASK_CAPTURE_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:IsStateAssigned() then - self.TaskInfo:AddInfo( "Defense", Task.ZoneGoal:GetName() .. ", " .. Task.ZoneGoal:GetZone():GetCoordinate(), 30, "MOD", Persist ) + 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 + end + end + 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! + for Defender, Task in pairs( DefenseAIA2GDispatcher:GetDefenderTasks() or {} ) do + local DetectedItem = DefenseAIA2GDispatcher:GetDefenderTaskTarget( Defender ) + if DetectedItem then + local DetectedZone = DefenseAIA2GDispatcher.Detection:GetDetectedItemZone( DetectedItem ) + if DetectedZone then + self.TaskInfo:AddInfo( "Defense AI Zone", DetectedZone:GetName(), 40, "MOD", Persist ) + self.TaskInfo:AddCoordinate( DetectedZone:GetCoordinate(), 41, "MOD", Persist, false, "Defense AI Coordinate" ) end end end @@ -291,7 +307,7 @@ do -- TASK_CAPTURE_ZONE -- @param #number AutoAssignMethod The method to be applied to the task. -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center. -- @param Wrapper.Group#GROUP TaskGroup The player group. - function TASK_CAPTURE_ZONE:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup ) + function TASK_CAPTURE_ZONE:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup, AutoAssignReference ) if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then return math.random( 1, 9 )