diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 88c1e8760..d29ea28b9 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -20,9 +20,9 @@ -- Various types of SET_ classes are available: -- -- * @{#SET_GROUP}: Defines a collection of @{Wrapper.Group}s filtered by filter criteria. --- * @{#SET_UNIT}: Defines a collection of @{Wrapper.Unit}s filtered by filter criteria. +-- * @{#SET_UNIT}: Defines a colleciton of @{Wrapper.Unit}s filtered by filter criteria. -- * @{#SET_STATIC}: Defines a collection of @{Wrapper.Static}s filtered by filter criteria. --- * @{#SET_CLIENT}: Defines a collection of @{Client}s filtered by filter criteria. +-- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. -- * @{#SET_AIRBASE}: Defines a collection of @{Wrapper.Airbase}s filtered by filter criteria. -- * @{#SET_CARGO}: Defines a collection of @{Cargo.Cargo}s filtered by filter criteria. -- * @{#SET_ZONE}: Defines a collection of @{Core.Zone}s filtered by filter criteria. @@ -44,18 +44,20 @@ -- @module Core.Set -- @image Core_Sets.JPG + do -- SET_BASE --- @type SET_BASE -- @field #table Filter Table of filters. -- @field #table Set Table of objects. - -- @field #table Index Table of indices. + -- @field #table Index Table of indicies. -- @field #table List Unused table. -- @field Core.Scheduler#SCHEDULER CallScheduler -- @extends Core.Base#BASE + --- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. - -- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach iterator loop at defined **"intervals"** to the mail simulator loop. + -- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. -- In this way, large loops can be done while not blocking the simulator main processing loop. -- The default **"yield interval"** is after 10 objects processed. -- The default **"time interval"** is after 0.001 seconds. @@ -66,7 +68,7 @@ do -- SET_BASE -- -- ## Define the SET iterator **"yield interval"** and the **"time interval"** -- - -- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetIteratorIntervals} method. + -- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetInteratorIntervals} method. -- You can set the **"yield interval"**, and the **"time interval"**. (See above). -- -- @field #SET_BASE SET_BASE @@ -77,11 +79,12 @@ do -- SET_BASE List = {}, Index = {}, Database = nil, - CallScheduler = nil, - TimeInterval = nil, - YieldInterval = nil, + CallScheduler=nil, + TimeInterval=nil, + YieldInterval=nil, } + --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_BASE self -- @return #SET_BASE @@ -106,7 +109,8 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Object The object. - self:AddTransition( "*", "Added", "*" ) + + self:AddTransition( "*", "Added", "*" ) --- Removed Handler OnAfter for SET_BASE -- @function [parent=#SET_BASE] OnAfterRemoved @@ -117,7 +121,7 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Object The object. - self:AddTransition( "*", "Removed", "*" ) + self:AddTransition( "*", "Removed", "*" ) self.YieldInterval = 10 self.TimeInterval = 0.001 @@ -144,6 +148,8 @@ do -- SET_BASE return self end + + --- Finds an @{Core.Base#BASE} object based on the object Name. -- @param #SET_BASE self -- @param #string ObjectName @@ -154,6 +160,7 @@ do -- SET_BASE return ObjectFound end + --- Gets the Set. -- @param #SET_BASE self -- @return #SET_BASE self @@ -166,7 +173,7 @@ do -- SET_BASE --- Gets a list of the Names of the Objects in the Set. -- @param #SET_BASE self -- @return #SET_BASE self - function SET_BASE:GetSetNames() -- R2.3 + function SET_BASE:GetSetNames() -- R2.3 self:F2() local Names = {} @@ -178,10 +185,11 @@ do -- SET_BASE return Names end + --- Gets a list of the Objects in the Set. -- @param #SET_BASE self -- @return #SET_BASE self - function SET_BASE:GetSetObjects() -- R2.3 + function SET_BASE:GetSetObjects() -- R2.3 self:F2() local Objects = {} @@ -193,18 +201,17 @@ do -- SET_BASE return Objects end + --- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. -- @param #SET_BASE self -- @param #string ObjectName - -- @param NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event. + -- @param NoTriggerEvent (optional) When `true`, the :Remove() method will not trigger a **Removed** event. function SET_BASE:Remove( ObjectName, NoTriggerEvent ) self:F2( { ObjectName = ObjectName } ) - + local TriggerEvent = true - if NoTriggerEvent == false then - TriggerEvent = false - end - + if NoTriggerEvent == false then TriggerEvent = false end + local Object = self.Set[ObjectName] if Object then @@ -222,13 +229,16 @@ do -- SET_BASE end end + --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index. -- @param #SET_BASE self -- @param #string ObjectName The name of the object. -- @param Core.Base#BASE Object The object itself. -- @return Core.Base#BASE The added BASE Object. function SET_BASE:Add( ObjectName, Object ) - self:F2( { ObjectName = ObjectName, Object = Object } ) + + -- Debug info. + self:T( { ObjectName = ObjectName, Object = Object } ) -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set if self.Set[ObjectName] then @@ -258,20 +268,47 @@ do -- SET_BASE end + --- Sort the set by name. + -- @param #SET_BASE self + -- @return Core.Base#BASE The added BASE Object. + function SET_BASE:SortByName() + + local function sort(a, b) + return a= Limit then - break - end - -- if Count % self.YieldInterval == 0 then - -- coroutine.yield( false ) - -- end + Count = Count + 1 + if Count >= Limit then + break + end + -- if Count % self.YieldInterval == 0 then + -- coroutine.yield( false ) + -- end end return true end - -- local co = coroutine.create( CoRoutine ) + -- local co = coroutine.create( CoRoutine ) local co = CoRoutine local function Schedule() - -- local status, res = coroutine.resume( co ) + -- local status, res = coroutine.resume( co ) local status, res = co() self:T3( { status, res } ) @@ -731,48 +780,50 @@ do -- SET_BASE return false end - -- self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) Schedule() return self end - ----- Iterate the SET_BASE and call an iterator function for each **alive** unit, providing the Unit and optional parameters. + + ----- Iterate the SET_BASE and call an interator function for each **alive** unit, providing the Unit and optional parameters. ---- @param #SET_BASE self ---- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) + --function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) -- self:F3( arg ) -- -- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) -- -- return self - -- end + --end -- - ----- Iterate the SET_BASE and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. + ----- Iterate the SET_BASE and call an interator function for each **alive** player, providing the Unit of the player and optional parameters. ---- @param #SET_BASE self ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachPlayer( IteratorFunction, ... ) + --function SET_BASE:ForEachPlayer( IteratorFunction, ... ) -- self:F3( arg ) -- -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) -- -- return self - -- end + --end -- -- - ----- Iterate the SET_BASE and call an iterator function for each client, providing the Client to the function and optional parameters. + ----- Iterate the SET_BASE and call an interator function for each client, providing the Client to the function and optional parameters. ---- @param #SET_BASE self ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachClient( IteratorFunction, ... ) + --function SET_BASE:ForEachClient( IteratorFunction, ... ) -- self:F3( arg ) -- -- self:ForEach( IteratorFunction, arg, self.Clients ) -- -- return self - -- end + --end + --- Decides whether to include the Object. -- @param #SET_BASE self @@ -788,7 +839,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #SET_BASE self - function SET_BASE:IsInSet( ObjectName ) + function SET_BASE:IsInSet(ObjectName) self:F3( Object ) return true @@ -810,7 +861,7 @@ do -- SET_BASE --- Flushes the current SET_BASE contents in the log ... (for debugging reasons). -- @param #SET_BASE self - -- @param Core.Base#BASE MasterObject (Optional) The master object as a reference. + -- @param Core.Base#BASE MasterObject (optional) The master object as a reference. -- @return #string A string with the names of the objects. function SET_BASE:Flush( MasterObject ) self:F3() @@ -826,6 +877,7 @@ do -- SET_BASE end + do -- SET_GROUP --- @type SET_GROUP @@ -867,17 +919,13 @@ do -- SET_GROUP -- * @{#SET_GROUP.FilterCategoryGround}: Builds the SET_GROUP from ground vehicles or infantry. -- * @{#SET_GROUP.FilterCategoryShip}: Builds the SET_GROUP from ships. -- * @{#SET_GROUP.FilterCategoryStructure}: Builds the SET_GROUP from structures. - -- + -- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. -- -- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: -- -- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. -- * @{#SET_GROUP.FilterOnce}: Filters of the groups **once**. -- - -- Planned filter criteria within development are (so these are not yet available): - -- - -- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. - -- -- ## SET_GROUP iterators -- -- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. @@ -897,7 +945,7 @@ do -- SET_GROUP -- ### When a GROUP object crashes or is dead, the SET_GROUP will trigger a **Dead** event. -- -- You can handle the event using the OnBefore and OnAfter event handlers. - -- The event handlers need to have the parameters From, Event, To, GroupObject. + -- The event handlers need to have the paramters From, Event, To, GroupObject. -- The GroupObject is the GROUP object that is dead and within the SET_GROUP, and is passed as a parameter to the event handler. -- See the following example: -- @@ -912,7 +960,7 @@ do -- SET_GROUP -- end -- -- While this is a good example, there is a catch. - -- Imagine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. + -- Imageine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. -- So, the self would need to contain another object. Fortunately, this can be done, but you must use then the **`.`** notation for the method. -- See the modified example: -- @@ -944,6 +992,7 @@ do -- SET_GROUP Categories = nil, Countries = nil, GroupPrefixes = nil, + Zones = nil, }, FilterMeta = { Coalitions = { @@ -961,6 +1010,7 @@ do -- SET_GROUP }, } + --- Creates a new SET_GROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_GROUP self -- @return #SET_GROUP @@ -987,7 +1037,7 @@ do -- SET_GROUP -- Clean the Set before returning with only the alive Groups. for GroupName, GroupObject in pairs( self.Set ) do - local GroupObject = GroupObject -- Wrapper.Group#GROUP + local GroupObject=GroupObject --Wrapper.Group#GROUP if GroupObject then if GroupObject:IsAlive() then AliveSet:Add( GroupName, GroupObject ) @@ -1054,7 +1104,7 @@ do -- SET_GROUP -- @return Core.Set#SET_GROUP self function SET_GROUP:AddGroupsByName( AddGroupNames ) - local AddGroupNamesArray = (type( AddGroupNames ) == "table") and AddGroupNames or { AddGroupNames } + local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) @@ -1069,7 +1119,7 @@ do -- SET_GROUP -- @return Core.Set#SET_GROUP self function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) - local RemoveGroupNamesArray = (type( RemoveGroupNames ) == "table") and RemoveGroupNames or { RemoveGroupNames } + local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do self:Remove( RemoveGroupName ) @@ -1078,6 +1128,9 @@ do -- SET_GROUP return self end + + + --- Finds a Group based on the Group Name. -- @param #SET_GROUP self -- @param #string GroupName @@ -1095,7 +1148,7 @@ do -- SET_GROUP function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) self:F2( PointVec2 ) - local NearestGroup = nil -- Wrapper.Group#GROUP + local NearestGroup = nil --Wrapper.Group#GROUP local ClosestDistance = nil for ObjectID, ObjectData in pairs( self.Set ) do @@ -1114,6 +1167,31 @@ do -- SET_GROUP return NearestGroup end + + --- Builds a set of groups in zones. + -- @param #SET_GROUP self + -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @return #SET_GROUP self + function SET_GROUP:FilterZones( Zones ) + if not self.Filter.Zones then + self.Filter.Zones = {} + end + local zones = {} + if Zones.ClassName and Zones.ClassName == "SET_ZONE" then + zones = Zones.Set + elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") + return self + else + zones = Zones + end + for _,Zone in pairs( zones ) do + local zonename = Zone:GetName() + self.Filter.Zones[zonename] = Zone + end + return self + end + --- Builds a set of groups of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_GROUP self @@ -1132,6 +1210,7 @@ do -- SET_GROUP return self end + --- Builds a set of groups out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_GROUP self @@ -1190,6 +1269,8 @@ do -- SET_GROUP return self end + + --- Builds a set of groups of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_GROUP self @@ -1208,6 +1289,7 @@ do -- SET_GROUP return self end + --- Builds a set of groups that contain the given string in their group name. -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string. -- @param #SET_GROUP self @@ -1229,7 +1311,7 @@ do -- SET_GROUP --- Builds a set of groups that are only active. -- Only the groups that are active will be included within the set. -- @param #SET_GROUP self - -- @param #boolean Active (Optional) Include only active groups to the set. + -- @param #boolean Active (optional) Include only active groups to the set. -- Include inactive groups if you provide false. -- @return #SET_GROUP self -- @usage @@ -1247,11 +1329,12 @@ do -- SET_GROUP -- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() -- function SET_GROUP:FilterActive( Active ) - Active = Active or not (Active == false) + Active = Active or not ( Active == false ) self.Filter.Active = Active return self end + --- Starts the filtering. -- @param #SET_GROUP self -- @return #SET_GROUP self @@ -1265,6 +1348,8 @@ do -- SET_GROUP self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) end + + return self end @@ -1364,6 +1449,22 @@ do -- SET_GROUP return self end + --- Activate late activated groups. + -- @param #SET_GROUP self + -- @param #number Delay Delay in seconds. + -- @return #SET_GROUP self + function SET_GROUP:Activate(Delay) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + local group=GroupData --Wrapper.Group#GROUP + if group and group:IsAlive()==false then + group:Activate(Delay) + end + end + return self + end + + --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. -- @param #SET_GROUP self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. @@ -1372,15 +1473,16 @@ do -- SET_GROUP function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsCompletelyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsCompletelyInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -1393,15 +1495,16 @@ do -- SET_GROUP function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsPartlyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsPartlyInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -1414,15 +1517,16 @@ do -- SET_GROUP function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -1430,7 +1534,7 @@ do -- SET_GROUP --- Iterate the SET_GROUP and return true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE} -- @param #SET_GROUP self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. - -- @return #boolean true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE}, false otherwise + -- @return #boolean true if all the @{Wrapper.Group#GROUP} are completly in the @{Core.Zone#ZONE}, false otherwise -- @usage -- local MyZone = ZONE:New("Zone1") -- local MySetGroup = SET_GROUP:New() @@ -1441,11 +1545,11 @@ do -- SET_GROUP -- else -- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll() -- end - function SET_GROUP:AllCompletelyInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:AllCompletelyInZone(Zone) + self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if not GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsCompletelyInZone(Zone) then return false end end @@ -1460,23 +1564,25 @@ do -- SET_GROUP function SET_GROUP:ForEachGroupAnyInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsAnyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsAnyInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end + --- Iterate the SET_GROUP and return true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE} -- @param #SET_GROUP self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. - -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE}, false otherwise. + -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is completly inside the @{Core.Zone#ZONE}, false otherwise. -- @usage -- local MyZone = ZONE:New("Zone1") -- local MySetGroup = SET_GROUP:New() @@ -1487,11 +1593,11 @@ do -- SET_GROUP -- else -- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() -- end - function SET_GROUP:AnyCompletelyInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:AnyCompletelyInZone(Zone) + self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then return true end end @@ -1501,7 +1607,7 @@ do -- SET_GROUP --- Iterate the SET_GROUP and return true if at least one @{#UNIT} of one @{GROUP} of the @{SET_GROUP} is in @{ZONE} -- @param #SET_GROUP self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. - -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completely inside the @{Core.Zone#ZONE}, false otherwise. + -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. -- @usage -- local MyZone = ZONE:New("Zone1") -- local MySetGroup = SET_GROUP:New() @@ -1512,11 +1618,11 @@ do -- SET_GROUP -- else -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() -- end - function SET_GROUP:AnyInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:AnyInZone(Zone) + self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsPartlyInZone( Zone ) or GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsPartlyInZone(Zone) or GroupData:IsCompletelyInZone(Zone) then return true end end @@ -1527,7 +1633,7 @@ do -- SET_GROUP -- Will return false if a @{GROUP} is fully in the @{ZONE} -- @param #SET_GROUP self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. - -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completely inside the @{Core.Zone#ZONE}, false otherwise. + -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. -- @usage -- local MyZone = ZONE:New("Zone1") -- local MySetGroup = SET_GROUP:New() @@ -1538,14 +1644,14 @@ do -- SET_GROUP -- else -- MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll() -- end - function SET_GROUP:AnyPartlyInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:AnyPartlyInZone(Zone) + self:F2(Zone) local IsPartlyInZone = false local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then return false - elseif GroupData:IsPartlyInZone( Zone ) then + elseif GroupData:IsPartlyInZone(Zone) then IsPartlyInZone = true -- at least one GROUP is partly in zone end end @@ -1573,11 +1679,11 @@ do -- SET_GROUP -- else -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() -- end - function SET_GROUP:NoneInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:NoneInZone(Zone) + self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if not GroupData:IsNotInZone( Zone ) then -- If the GROUP is in Zone in any way + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsNotInZone(Zone) then -- If the GROUP is in Zone in any way return false end end @@ -1596,12 +1702,12 @@ do -- SET_GROUP -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) -- -- MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll() - function SET_GROUP:CountInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:CountInZone(Zone) + self:F2(Zone) local Count = 0 local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then Count = Count + 1 end end @@ -1618,12 +1724,12 @@ do -- SET_GROUP -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) -- -- MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll() - function SET_GROUP:CountUnitInZone( Zone ) - self:F2( Zone ) + function SET_GROUP:CountUnitInZone(Zone) + self:F2(Zone) local Count = 0 local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - Count = Count + GroupData:CountInZone( Zone ) + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + Count = Count + GroupData:CountInZone(Zone) end return Count end @@ -1638,49 +1744,50 @@ do -- SET_GROUP local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP if GroupData and GroupData:IsAlive() then CountG = CountG + 1 - -- Count Units. - for _, _unit in pairs( GroupData:GetUnits() ) do - local unit = _unit -- Wrapper.Unit#UNIT + --Count Units. + for _,_unit in pairs(GroupData:GetUnits()) do + local unit=_unit --Wrapper.Unit#UNIT if unit and unit:IsAlive() then - CountU = CountU + 1 + CountU=CountU+1 end end end end - return CountG, CountU + return CountG,CountU end - ----- Iterate the SET_GROUP and call an iterator function for each **alive** player, providing the Group of the player and optional parameters. + ----- Iterate the SET_GROUP and call an interator function for each **alive** player, providing the Group of the player and optional parameters. ---- @param #SET_GROUP self ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ---- @return #SET_GROUP self - -- function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) + --function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) -- -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) -- -- return self - -- end + --end -- -- - ----- Iterate the SET_GROUP and call an iterator function for each client, providing the Client to the function and optional parameters. + ----- Iterate the SET_GROUP and call an interator function for each client, providing the Client to the function and optional parameters. ---- @param #SET_GROUP self ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ---- @return #SET_GROUP self - -- function SET_GROUP:ForEachClient( IteratorFunction, ... ) + --function SET_GROUP:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) -- -- self:ForEach( IteratorFunction, arg, self.Clients ) -- -- return self - -- end + --end + --- -- @param #SET_GROUP self @@ -1693,7 +1800,7 @@ do -- SET_GROUP if self.Filter.Active ~= nil then local MGroupActive = false self:F( { Active = self.Filter.Active } ) - if self.Filter.Active == false or (self.Filter.Active == true and MGroup:IsActive() == true) then + if self.Filter.Active == false or ( self.Filter.Active == true and MGroup:IsActive() == true ) then MGroupActive = true end MGroupInclude = MGroupInclude and MGroupActive @@ -1736,17 +1843,29 @@ do -- SET_GROUP local MGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) - if string.find( MGroup:GetName(), GroupPrefix:gsub( "-", "%%-" ), 1 ) then + if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then MGroupPrefix = true end end MGroupInclude = MGroupInclude and MGroupPrefix end - + + if self.Filter.Zones then + local MGroupZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + if MGroup:IsInZone(Zone) then + MGroupZone = true + end + end + MGroupInclude = MGroupInclude and MGroupZone + end + self:T2( MGroupInclude ) return MGroupInclude end + --- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit. -- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level. -- @param #SET_GROUP self @@ -1758,7 +1877,7 @@ do -- SET_GROUP local Set = self:GetSet() for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP for UnitName, UnitData in pairs( GroupData:GetUnits() ) do - -- local UnitData = UnitData -- Wrapper.Unit#UNIT + --local UnitData = UnitData -- Wrapper.Unit#UNIT UnitData:SetCargoBayWeightLimit() end end @@ -1766,6 +1885,7 @@ do -- SET_GROUP end + do -- SET_UNIT --- @type SET_UNIT @@ -1801,16 +1921,13 @@ do -- SET_UNIT -- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). -- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units sharing the same string(s) in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. -- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set! - -- + -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. + -- -- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: -- -- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units **dynamically**. -- * @{#SET_UNIT.FilterOnce}: Filters of the units **once**. -- - -- Planned filter criteria within development are (so these are not yet available): - -- - -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. - -- -- ## 4) SET_UNIT iterators -- -- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. @@ -1840,7 +1957,7 @@ do -- SET_UNIT -- ### 6.1) When a UNIT object crashes or is dead, the SET_UNIT will trigger a **Dead** event. -- -- You can handle the event using the OnBefore and OnAfter event handlers. - -- The event handlers need to have the parameters From, Event, To, GroupObject. + -- The event handlers need to have the paramters From, Event, To, GroupObject. -- The GroupObject is the UNIT object that is dead and within the SET_UNIT, and is passed as a parameter to the event handler. -- See the following example: -- @@ -1855,7 +1972,7 @@ do -- SET_UNIT -- end -- -- While this is a good example, there is a catch. - -- Imagine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. + -- Imageine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. -- So, the self would need to contain another object. Fortunately, this can be done, but you must use then the **`.`** notation for the method. -- See the modified example: -- @@ -1887,6 +2004,7 @@ do -- SET_UNIT Types = nil, Countries = nil, UnitPrefixes = nil, + Zones = nil, }, FilterMeta = { Coalitions = { @@ -1904,6 +2022,7 @@ do -- SET_UNIT }, } + --- Get the first unit from the set. -- @function [parent=#SET_UNIT] GetFirst -- @param #SET_UNIT self @@ -1940,13 +2059,14 @@ do -- SET_UNIT return self end + --- Add UNIT(s) to SET_UNIT. -- @param #SET_UNIT self -- @param #string AddUnitNames A single name or an array of UNIT names. -- @return #SET_UNIT self function SET_UNIT:AddUnitsByName( AddUnitNames ) - local AddUnitNamesArray = (type( AddUnitNames ) == "table") and AddUnitNames or { AddUnitNames } + local AddUnitNamesArray = ( type( AddUnitNames ) == "table" ) and AddUnitNames or { AddUnitNames } self:T( AddUnitNamesArray ) for AddUnitID, AddUnitName in pairs( AddUnitNamesArray ) do @@ -1962,7 +2082,7 @@ do -- SET_UNIT -- @return Core.Set#SET_UNIT self function SET_UNIT:RemoveUnitsByName( RemoveUnitNames ) - local RemoveUnitNamesArray = (type( RemoveUnitNames ) == "table") and RemoveUnitNames or { RemoveUnitNames } + local RemoveUnitNamesArray = ( type( RemoveUnitNames ) == "table" ) and RemoveUnitNames or { RemoveUnitNames } for RemoveUnitID, RemoveUnitName in pairs( RemoveUnitNamesArray ) do self:Remove( RemoveUnitName ) @@ -1971,6 +2091,7 @@ do -- SET_UNIT return self end + --- Finds a Unit based on the Unit Name. -- @param #SET_UNIT self -- @param #string UnitName @@ -1981,6 +2102,8 @@ do -- SET_UNIT return UnitFound end + + --- Builds a set of units of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_UNIT self @@ -1998,6 +2121,7 @@ do -- SET_UNIT return self end + --- Builds a set of units out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_UNIT self @@ -2016,6 +2140,7 @@ do -- SET_UNIT return self end + --- Builds a set of units of defined unit types. -- Possible current types are those types known within DCS world. -- @param #SET_UNIT self @@ -2034,6 +2159,7 @@ do -- SET_UNIT return self end + --- Builds a set of units of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_UNIT self @@ -2052,6 +2178,7 @@ do -- SET_UNIT return self end + --- Builds a set of UNITs that contain a given string in their unit name. -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all units that **contain** the string. -- @param #SET_UNIT self @@ -2069,11 +2196,35 @@ do -- SET_UNIT end return self end - + + --- Builds a set of units in zones. + -- @param #SET_UNIT self + -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @return #SET_UNIT self + function SET_UNIT:FilterZones( Zones ) + if not self.Filter.Zones then + self.Filter.Zones = {} + end + local zones = {} + if Zones.ClassName and Zones.ClassName == "SET_ZONE" then + zones = Zones.Set + elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") + return self + else + zones = Zones + end + for _,Zone in pairs( zones ) do + local zonename = Zone:GetName() + self.Filter.Zones[zonename] = Zone + end + return self + end + --- Builds a set of units that are only active. -- Only the units that are active will be included within the set. -- @param #SET_UNIT self - -- @param #boolean Active (Optional) Include only active units to the set. + -- @param #boolean Active (optional) Include only active units to the set. -- Include inactive units if you provide false. -- @return #SET_UNIT self -- @usage @@ -2091,7 +2242,7 @@ do -- SET_UNIT -- UnitSet = SET_UNIT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() -- function SET_UNIT:FilterActive( Active ) - Active = Active or not (Active == false) + Active = Active or not ( Active == false ) self.Filter.Active = Active return self end @@ -2130,7 +2281,7 @@ do -- SET_UNIT local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -2156,6 +2307,8 @@ do -- SET_UNIT 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_UNIT self @@ -2184,9 +2337,11 @@ do -- SET_UNIT function SET_UNIT:FindInDatabase( Event ) self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end + do -- Is Zone methods --- Check if minimal one element of the SET_UNIT is in the Zone. @@ -2199,7 +2354,7 @@ do -- SET_UNIT local function EvaluateZone( ZoneUnit ) - local ZoneUnitName = ZoneUnit:GetName() + local ZoneUnitName = ZoneUnit:GetName() self:F( { ZoneUnitName = ZoneUnitName } ) if self:FindUnit( ZoneUnitName ) then IsPartiallyInZone = true @@ -2215,6 +2370,7 @@ do -- SET_UNIT return IsPartiallyInZone end + --- Check if no element of the SET_UNIT is in the Zone. -- @param #SET_UNIT self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. @@ -2225,7 +2381,7 @@ do -- SET_UNIT local function EvaluateZone( ZoneUnit ) - local ZoneUnitName = ZoneUnit:GetName() + local ZoneUnitName = ZoneUnit:GetName() if self:FindUnit( ZoneUnitName ) then IsNotInZone = false return false @@ -2241,7 +2397,8 @@ do -- SET_UNIT end - --- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. + + --- Iterate the SET_UNIT and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters. -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self @@ -2253,12 +2410,16 @@ do -- SET_UNIT return self end + --- Get the SET of the SET_UNIT **sorted per Threat Level**. -- -- @param #SET_UNIT self -- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10). -- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10). -- @return #SET_UNIT self + -- @usage + -- + -- function SET_UNIT:GetSetPerThreatLevel( FromThreatLevel, ToThreatLevel ) self:F2( arg ) @@ -2275,10 +2436,12 @@ do -- SET_UNIT self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } ) end + local OrderedPerThreatLevelSet = {} local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1 + for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do self:F( { ThreatLevel = ThreatLevel } ) local ThreatLevelItem = ThreatLevelSet[ThreatLevel] @@ -2294,7 +2457,8 @@ do -- SET_UNIT end - --- Iterate the SET_UNIT **sorted *per Threat Level** and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. + + --- Iterate the SET_UNIT **sorted *per Threat Level** and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters. -- -- @param #SET_UNIT self -- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10). @@ -2310,7 +2474,7 @@ do -- SET_UNIT -- end -- ) -- - function SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) -- R2.1 Threat Level implementation + function SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) --R2.1 Threat Level implementation self:F2( arg ) local ThreatLevelSet = {} @@ -2340,6 +2504,8 @@ do -- SET_UNIT return self end + + --- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. -- @param #SET_UNIT self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. @@ -2348,15 +2514,16 @@ do -- SET_UNIT function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Unit#UNIT UnitObject + function( ZoneObject, UnitObject ) + if UnitObject:IsInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -2369,15 +2536,16 @@ do -- SET_UNIT function SET_UNIT:ForEachUnitNotInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Unit#UNIT UnitObject + function( ZoneObject, UnitObject ) + if UnitObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -2405,12 +2573,13 @@ do -- SET_UNIT end for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT + 1] = UnitType .. " of " .. UnitTypeID + MT[#MT+1] = UnitType .. " of " .. UnitTypeID end return UnitTypes end + --- Returns a comma separated string of the unit types with a count in the @{Set}. -- @param #SET_UNIT self -- @return #string The unit types string @@ -2421,7 +2590,7 @@ do -- SET_UNIT local UnitTypes = self:GetUnitTypes() for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT + 1] = UnitType .. " of " .. UnitTypeID + MT[#MT+1] = UnitType .. " of " .. UnitTypeID end return table.concat( MT, ", " ) @@ -2451,7 +2620,7 @@ do -- SET_UNIT return UnitThreatLevels end - --- Calculate the maximum A2G threat level of the SET_UNIT. + --- Calculate the maxium A2G threat level of the SET_UNIT. -- @param #SET_UNIT self -- @return #number The maximum threatlevel function SET_UNIT:CalculateThreatLevelA2G() @@ -2494,27 +2663,27 @@ do -- SET_UNIT local Unit = UnitData -- Wrapper.Unit#UNIT local Coordinate = Unit:GetCoordinate() - x1 = (Coordinate.x < x1) and Coordinate.x or x1 - x2 = (Coordinate.x > x2) and Coordinate.x or x2 - y1 = (Coordinate.y < y1) and Coordinate.y or y1 - y2 = (Coordinate.y > y2) and Coordinate.y or y2 - z1 = (Coordinate.y < z1) and Coordinate.z or z1 - z2 = (Coordinate.y > z2) and Coordinate.z or z2 + x1 = ( Coordinate.x < x1 ) and Coordinate.x or x1 + x2 = ( Coordinate.x > x2 ) and Coordinate.x or x2 + y1 = ( Coordinate.y < y1 ) and Coordinate.y or y1 + y2 = ( Coordinate.y > y2 ) and Coordinate.y or y2 + z1 = ( Coordinate.y < z1 ) and Coordinate.z or z1 + z2 = ( Coordinate.y > z2 ) and Coordinate.z or z2 local Velocity = Coordinate:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity + if Velocity ~= 0 then + MaxVelocity = ( MaxVelocity < Velocity ) and Velocity or MaxVelocity local Heading = Coordinate:GetHeading() - AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading + AvgHeading = AvgHeading and ( AvgHeading + Heading ) or Heading MovingCount = MovingCount + 1 end end - AvgHeading = AvgHeading and (AvgHeading / MovingCount) + AvgHeading = AvgHeading and ( AvgHeading / MovingCount ) - Coordinate.x = (x2 - x1) / 2 + x1 - Coordinate.y = (y2 - y1) / 2 + y1 - Coordinate.z = (z2 - z1) / 2 + z1 + Coordinate.x = ( x2 - x1 ) / 2 + x1 + Coordinate.y = ( y2 - y1 ) / 2 + y1 + Coordinate.z = ( z2 - z1 ) / 2 + z1 Coordinate:SetHeading( AvgHeading ) Coordinate:SetVelocity( MaxVelocity ) @@ -2538,8 +2707,8 @@ do -- SET_UNIT local Coordinate = Unit:GetCoordinate() local Velocity = Coordinate:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity + if Velocity ~= 0 then + MaxVelocity = ( MaxVelocity < Velocity ) and Velocity or MaxVelocity end end @@ -2562,12 +2731,12 @@ do -- SET_UNIT local Coordinate = Unit:GetCoordinate() local Velocity = Coordinate:GetVelocity() - if Velocity ~= 0 then + if Velocity ~= 0 then local Heading = Coordinate:GetHeading() if HeadingSet == nil then HeadingSet = Heading else - local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180 + local HeadingDiff = ( HeadingSet - Heading + 180 + 360 ) % 360 - 180 HeadingDiff = math.abs( HeadingDiff ) if HeadingDiff > 5 then HeadingSet = nil @@ -2581,6 +2750,8 @@ do -- SET_UNIT end + + --- Returns if the @{Set} has targets having a radar (of a given type). -- @param #SET_UNIT self -- @param DCS#Unit.RadarType RadarType @@ -2589,7 +2760,7 @@ do -- SET_UNIT self:F2( RadarType ) local RadarCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs( self:GetSet()) do local UnitSensorTest = UnitData -- Wrapper.Unit#UNIT local HasSensors if RadarType then @@ -2597,7 +2768,7 @@ do -- SET_UNIT else HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR ) end - self:T3( HasSensors ) + self:T3(HasSensors) if HasSensors then RadarCount = RadarCount + 1 end @@ -2613,14 +2784,14 @@ do -- SET_UNIT self:F2() local SEADCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs( self:GetSet()) do local UnitSEAD = UnitData -- Wrapper.Unit#UNIT if UnitSEAD:IsAlive() then local UnitSEADAttributes = UnitSEAD:GetDesc().attributes local HasSEAD = UnitSEAD:HasSEAD() - self:T3( HasSEAD ) + self:T3(HasSEAD) if HasSEAD then SEADCount = SEADCount + 1 end @@ -2637,7 +2808,7 @@ do -- SET_UNIT self:F2() local GroundUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs( self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT if UnitTest:IsGround() then GroundUnitCount = GroundUnitCount + 1 @@ -2671,7 +2842,7 @@ do -- SET_UNIT self:F2() local FriendlyUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs( self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT if UnitTest:IsFriendly( FriendlyCoalition ) then FriendlyUnitCount = FriendlyUnitCount + 1 @@ -2681,30 +2852,33 @@ do -- SET_UNIT return FriendlyUnitCount end - ----- Iterate the SET_UNIT and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. + + + ----- Iterate the SET_UNIT and call an interator function for each **alive** player, providing the Unit of the player and optional parameters. ---- @param #SET_UNIT self ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ---- @return #SET_UNIT self - -- function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) + --function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) -- -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) -- -- return self - -- end + --end -- -- - ----- Iterate the SET_UNIT and call an iterator function for each client, providing the Client to the function and optional parameters. + ----- Iterate the SET_UNIT and call an interator function for each client, providing the Client to the function and optional parameters. ---- @param #SET_UNIT self ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ---- @return #SET_UNIT self - -- function SET_UNIT:ForEachClient( IteratorFunction, ... ) + --function SET_UNIT:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) -- -- self:ForEach( IteratorFunction, arg, self.Clients ) -- -- return self - -- end + --end + --- -- @param #SET_UNIT self @@ -2721,7 +2895,7 @@ do -- SET_UNIT if self.Filter.Active ~= nil then local MUnitActive = false - if self.Filter.Active == false or (self.Filter.Active == true and MUnit:IsActive() == true) then + if self.Filter.Active == false or ( self.Filter.Active == true and MUnit:IsActive() == true ) then MUnitActive = true end MUnitInclude = MUnitInclude and MUnitActive @@ -2805,14 +2979,26 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitSEAD end end - + + if self.Filter.Zones then + local MGroupZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + if MUnit:IsInZone(Zone) then + MGroupZone = true + end + end + MUnitInclude = MUnitInclude and MGroupZone + end + self:T2( MUnitInclude ) return MUnitInclude end + --- Retrieve the type names of the @{Wrapper.Unit}s in the SET, delimited by an optional delimiter. -- @param #SET_UNIT self - -- @param #string Delimiter (Optional) The delimiter, which is default a comma. + -- @param #string Delimiter (optional) The delimiter, which is default a comma. -- @return #string The types of the @{Wrapper.Unit}s delimited. function SET_UNIT:GetTypeNames( Delimiter ) @@ -2843,13 +3029,16 @@ do -- SET_UNIT function SET_UNIT:SetCargoBayWeightLimit() local Set = self:GetSet() for UnitID, UnitData in pairs( Set ) do -- For each UNIT in SET_UNIT - -- local UnitData = UnitData -- Wrapper.Unit#UNIT + --local UnitData = UnitData -- Wrapper.Unit#UNIT UnitData:SetCargoBayWeightLimit() end end + + end + do -- SET_STATIC --- @type SET_STATIC @@ -2884,15 +3073,12 @@ do -- SET_STATIC -- * @{#SET_STATIC.FilterTypes}: Builds the SET_STATIC with the units belonging to the unit type(s). -- * @{#SET_STATIC.FilterCountries}: Builds the SET_STATIC with the units belonging to the country(ies). -- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **ATTENTION** bad naming convention as this *does not** only filter *prefixes*. - -- + -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}. + -- -- Once the filter criteria have been set for the SET_STATIC, you can start filtering using: -- -- * @{#SET_STATIC.FilterStart}: Starts the filtering of the units within the SET_STATIC. -- - -- Planned filter criteria within development are (so these are not yet available): - -- - -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}. - -- -- ## SET_STATIC iterators -- -- Once the filters have been defined and the SET_STATIC has been built, you can iterate the SET_STATIC with the available iterator methods. @@ -2900,14 +3086,9 @@ do -- SET_STATIC -- The following iterator methods are currently available within the SET_STATIC: -- -- * @{#SET_STATIC.ForEachStatic}: Calls a function for each alive unit it finds within the SET_STATIC. - -- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. - -- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. - -- - -- Planned iterators methods in development are (so these are not yet available): - -- - -- * @{#SET_STATIC.ForEachStaticInZone}: Calls a function for each unit contained within the SET_STATIC. - -- * @{#SET_STATIC.ForEachStaticCompletelyInZone}: Iterate and call an iterator function for each **alive** STATIC presence completely in a @{Zone}, providing the STATIC and optional parameters to the called function. - -- * @{#SET_STATIC.ForEachStaticNotInZone}: Iterate and call an iterator function for each **alive** STATIC presence not in a @{Zone}, providing the STATIC and optional parameters to the called function. + -- * @{#SET_STATIC.ForEachStaticCompletelyInZone}: Iterate the SET_STATIC and call an iterator function for each **alive** STATIC presence completely in a @{Zone}, providing the STATIC and optional parameters to the called function. + -- * @{#SET_STATIC.ForEachStaticInZone}: Iterate the SET_STATIC and call an iterator function for each **alive** STATIC presence completely in a @{Zone}, providing the STATIC and optional parameters to the called function. + -- * @{#SET_STATIC.ForEachStaticNotInZone}: Iterate the SET_STATIC and call an iterator function for each **alive** STATIC presence not in a @{Zone}, providing the STATIC and optional parameters to the called function. -- -- ## SET_STATIC atomic methods -- @@ -2926,6 +3107,7 @@ do -- SET_STATIC Types = nil, Countries = nil, StaticPrefixes = nil, + Zones = nil, }, FilterMeta = { Coalitions = { @@ -2943,6 +3125,7 @@ do -- SET_STATIC }, } + --- Get the first unit from the set. -- @function [parent=#SET_STATIC] GetFirst -- @param #SET_STATIC self @@ -2974,13 +3157,14 @@ do -- SET_STATIC return self end + --- Add STATIC(s) to SET_STATIC. -- @param #SET_STATIC self -- @param #string AddStaticNames A single name or an array of STATIC names. -- @return #SET_STATIC self function SET_STATIC:AddStaticsByName( AddStaticNames ) - local AddStaticNamesArray = (type( AddStaticNames ) == "table") and AddStaticNames or { AddStaticNames } + local AddStaticNamesArray = ( type( AddStaticNames ) == "table" ) and AddStaticNames or { AddStaticNames } self:T( AddStaticNamesArray ) for AddStaticID, AddStaticName in pairs( AddStaticNamesArray ) do @@ -2996,7 +3180,7 @@ do -- SET_STATIC -- @return self function SET_STATIC:RemoveStaticsByName( RemoveStaticNames ) - local RemoveStaticNamesArray = (type( RemoveStaticNames ) == "table") and RemoveStaticNames or { RemoveStaticNames } + local RemoveStaticNamesArray = ( type( RemoveStaticNames ) == "table" ) and RemoveStaticNames or { RemoveStaticNames } for RemoveStaticID, RemoveStaticName in pairs( RemoveStaticNamesArray ) do self:Remove( RemoveStaticName ) @@ -3005,6 +3189,7 @@ do -- SET_STATIC return self end + --- Finds a Static based on the Static Name. -- @param #SET_STATIC self -- @param #string StaticName @@ -3015,6 +3200,8 @@ do -- SET_STATIC return StaticFound end + + --- Builds a set of units of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_STATIC self @@ -3032,6 +3219,31 @@ do -- SET_STATIC end return self end + + + --- Builds a set of statics in zones. + -- @param #SET_STATIC self + -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @return #SET_STATIC self + function SET_STATIC:FilterZones( Zones ) + if not self.Filter.Zones then + self.Filter.Zones = {} + end + local zones = {} + if Zones.ClassName and Zones.ClassName == "SET_ZONE" then + zones = Zones.Set + elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") + return self + else + zones = Zones + end + for _,Zone in pairs( zones ) do + local zonename = Zone:GetName() + self.Filter.Zones[zonename] = Zone + end + return self + end --- Builds a set of units out of categories. -- Possible current categories are plane, helicopter, ground, ship. @@ -3051,6 +3263,7 @@ do -- SET_STATIC return self end + --- Builds a set of units of defined unit types. -- Possible current types are those types known within DCS world. -- @param #SET_STATIC self @@ -3069,6 +3282,7 @@ do -- SET_STATIC return self end + --- Builds a set of units of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_STATIC self @@ -3087,6 +3301,7 @@ do -- SET_STATIC return self end + --- Builds a set of STATICs that contain the given string in their name. -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all statics that **contain** the string. -- @param #SET_STATIC self @@ -3105,6 +3320,7 @@ do -- SET_STATIC return self end + --- Starts the filtering. -- @param #SET_STATIC self -- @return #SET_STATIC self @@ -3128,7 +3344,7 @@ do -- SET_STATIC local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do + for UnitID, UnitData in pairs(Set) do if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -3166,9 +3382,11 @@ do -- SET_STATIC function SET_STATIC:FindInDatabase( Event ) self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end + do -- Is Zone methods --- Check if minimal one element of the SET_STATIC is in the Zone. @@ -3181,7 +3399,7 @@ do -- SET_STATIC local function EvaluateZone( ZoneStatic ) - local ZoneStaticName = ZoneStatic:GetName() + local ZoneStaticName = ZoneStatic:GetName() if self:FindStatic( ZoneStaticName ) then IsPartiallyInZone = true return false @@ -3193,6 +3411,7 @@ do -- SET_STATIC return IsPartiallyInZone end + --- Check if no element of the SET_STATIC is in the Zone. -- @param #SET_STATIC self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. @@ -3203,7 +3422,7 @@ do -- SET_STATIC local function EvaluateZone( ZoneStatic ) - local ZoneStaticName = ZoneStatic:GetName() + local ZoneStaticName = ZoneStatic:GetName() if self:FindStatic( ZoneStaticName ) then IsNotInZone = false return false @@ -3217,6 +3436,7 @@ do -- SET_STATIC return IsNotInZone end + --- Check if minimal one element of the SET_STATIC is in the Zone. -- @param #SET_STATIC self -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. @@ -3229,9 +3449,11 @@ do -- SET_STATIC return self end + end - --- Iterate the SET_STATIC and call an iterator function for each **alive** STATIC, providing the STATIC and optional parameters. + + --- Iterate the SET_STATIC and call an interator function for each **alive** STATIC, providing the STATIC and optional parameters. -- @param #SET_STATIC self -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self @@ -3243,6 +3465,7 @@ do -- SET_STATIC return self end + --- Iterate the SET_STATIC and call an iterator function for each **alive** STATIC presence completely in a @{Zone}, providing the STATIC and optional parameters to the called function. -- @param #SET_STATIC self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. @@ -3251,15 +3474,16 @@ do -- SET_STATIC function SET_STATIC:ForEachStaticCompletelyInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Static#STATIC StaticObject - function( ZoneObject, StaticObject ) - if StaticObject:IsInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Static#STATIC StaticObject + function( ZoneObject, StaticObject ) + if StaticObject:IsInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -3272,15 +3496,16 @@ do -- SET_STATIC function SET_STATIC:ForEachStaticNotInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Static#STATIC StaticObject - function( ZoneObject, StaticObject ) - if StaticObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Static#STATIC StaticObject + function( ZoneObject, StaticObject ) + if StaticObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -3308,12 +3533,13 @@ do -- SET_STATIC end for StaticTypeID, StaticType in pairs( StaticTypes ) do - MT[#MT + 1] = StaticType .. " of " .. StaticTypeID + MT[#MT+1] = StaticType .. " of " .. StaticTypeID end return StaticTypes end + --- Returns a comma separated string of the unit types with a count in the @{Set}. -- @param #SET_STATIC self -- @return #string The unit types string @@ -3324,7 +3550,7 @@ do -- SET_STATIC local StaticTypes = self:GetStaticTypes() for StaticTypeID, StaticType in pairs( StaticTypes ) do - MT[#MT + 1] = StaticType .. " of " .. StaticTypeID + MT[#MT+1] = StaticType .. " of " .. StaticTypeID end return table.concat( MT, ", " ) @@ -3352,27 +3578,27 @@ do -- SET_STATIC local Static = StaticData -- Wrapper.Static#STATIC local Coordinate = Static:GetCoordinate() - x1 = (Coordinate.x < x1) and Coordinate.x or x1 - x2 = (Coordinate.x > x2) and Coordinate.x or x2 - y1 = (Coordinate.y < y1) and Coordinate.y or y1 - y2 = (Coordinate.y > y2) and Coordinate.y or y2 - z1 = (Coordinate.y < z1) and Coordinate.z or z1 - z2 = (Coordinate.y > z2) and Coordinate.z or z2 + x1 = ( Coordinate.x < x1 ) and Coordinate.x or x1 + x2 = ( Coordinate.x > x2 ) and Coordinate.x or x2 + y1 = ( Coordinate.y < y1 ) and Coordinate.y or y1 + y2 = ( Coordinate.y > y2 ) and Coordinate.y or y2 + z1 = ( Coordinate.y < z1 ) and Coordinate.z or z1 + z2 = ( Coordinate.y > z2 ) and Coordinate.z or z2 local Velocity = Coordinate:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity + if Velocity ~= 0 then + MaxVelocity = ( MaxVelocity < Velocity ) and Velocity or MaxVelocity local Heading = Coordinate:GetHeading() - AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading + AvgHeading = AvgHeading and ( AvgHeading + Heading ) or Heading MovingCount = MovingCount + 1 end end - AvgHeading = AvgHeading and (AvgHeading / MovingCount) + AvgHeading = AvgHeading and ( AvgHeading / MovingCount ) - Coordinate.x = (x2 - x1) / 2 + x1 - Coordinate.y = (y2 - y1) / 2 + y1 - Coordinate.z = (z2 - z1) / 2 + z1 + Coordinate.x = ( x2 - x1 ) / 2 + x1 + Coordinate.y = ( y2 - y1 ) / 2 + y1 + Coordinate.z = ( z2 - z1 ) / 2 + z1 Coordinate:SetHeading( AvgHeading ) Coordinate:SetVelocity( MaxVelocity ) @@ -3404,12 +3630,12 @@ do -- SET_STATIC local Coordinate = Static:GetCoordinate() local Velocity = Coordinate:GetVelocity() - if Velocity ~= 0 then + if Velocity ~= 0 then local Heading = Coordinate:GetHeading() if HeadingSet == nil then HeadingSet = Heading else - local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180 + local HeadingDiff = ( HeadingSet - Heading + 180 + 360 ) % 360 - 180 HeadingDiff = math.abs( HeadingDiff ) if HeadingDiff > 5 then HeadingSet = nil @@ -3428,19 +3654,19 @@ do -- SET_STATIC -- @return #number The maximum threatlevel function SET_STATIC:CalculateThreatLevelA2G() - local MaxThreatLevelA2G = 0 - local MaxThreatText = "" - for StaticName, StaticData in pairs( self:GetSet() ) do - local ThreatStatic = StaticData -- Wrapper.Static#STATIC - local ThreatLevelA2G, ThreatText = ThreatStatic:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - MaxThreatText = ThreatText - end + local MaxThreatLevelA2G = 0 + local MaxThreatText = "" + for StaticName, StaticData in pairs( self:GetSet() ) do + local ThreatStatic = StaticData -- Wrapper.Static#STATIC + local ThreatLevelA2G, ThreatText = ThreatStatic:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + MaxThreatText = ThreatText end + end - self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) - return MaxThreatLevelA2G, MaxThreatText + self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) + return MaxThreatLevelA2G, MaxThreatText end @@ -3506,14 +3732,26 @@ do -- SET_STATIC end MStaticInclude = MStaticInclude and MStaticPrefix end - + + if self.Filter.Zones then + local MStaticZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + if MStatic and MStatic:IsInZone(Zone) then + MStaticZone = true + end + end + MStaticInclude = MStaticInclude and MStaticZone + end + self:T2( MStaticInclude ) return MStaticInclude end + --- Retrieve the type names of the @{Static}s in the SET, delimited by an optional delimiter. -- @param #SET_STATIC self - -- @param #string Delimiter (Optional) The delimiter, which is default a comma. + -- @param #string Delimiter (optional) The delimiter, which is default a comma. -- @return #string The types of the @{Static}s delimited. function SET_STATIC:GetTypeNames( Delimiter ) @@ -3537,11 +3775,15 @@ do -- SET_STATIC end + do -- SET_CLIENT + --- @type SET_CLIENT -- @extends Core.Set#SET_BASE + + --- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: -- -- * Coalitions @@ -3572,16 +3814,13 @@ do -- SET_CLIENT -- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). -- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients containing the same string(s) in their unit/pilot name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. -- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set! - -- + -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. + -- -- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: -- -- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients **dynamically**. -- * @{#SET_CLIENT.FilterOnce}: Filters the clients **once**. -- - -- Planned filter criteria within development are (so these are not yet available): - -- - -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. - -- -- ## 4) SET_CLIENT iterators -- -- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. @@ -3601,6 +3840,7 @@ do -- SET_CLIENT Types = nil, Countries = nil, ClientPrefixes = nil, + Zones = nil, }, FilterMeta = { Coalitions = { @@ -3640,7 +3880,7 @@ do -- SET_CLIENT -- @return self function SET_CLIENT:AddClientsByName( AddClientNames ) - local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames } + local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } for AddClientID, AddClientName in pairs( AddClientNamesArray ) do self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) @@ -3655,7 +3895,7 @@ do -- SET_CLIENT -- @return self function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) - local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames } + local RemoveClientNamesArray = ( type( RemoveClientNames ) == "table" ) and RemoveClientNames or { RemoveClientNames } for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do self:Remove( RemoveClientName.ClientName ) @@ -3664,6 +3904,7 @@ do -- SET_CLIENT return self end + --- Finds a Client based on the Client Name. -- @param #SET_CLIENT self -- @param #string ClientName @@ -3674,6 +3915,8 @@ do -- SET_CLIENT return ClientFound end + + --- Builds a set of clients of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CLIENT self @@ -3692,6 +3935,7 @@ do -- SET_CLIENT return self end + --- Builds a set of clients out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_CLIENT self @@ -3710,6 +3954,7 @@ do -- SET_CLIENT return self end + --- Builds a set of clients of defined client types. -- Possible current types are those types known within DCS world. -- @param #SET_CLIENT self @@ -3728,6 +3973,7 @@ do -- SET_CLIENT return self end + --- Builds a set of clients of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_CLIENT self @@ -3746,6 +3992,7 @@ do -- SET_CLIENT return self end + --- Builds a set of CLIENTs that contain the given string in their unit/pilot name. -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all clients that **contain** the string. -- @param #SET_CLIENT self @@ -3767,7 +4014,7 @@ do -- SET_CLIENT --- Builds a set of clients that are only active. -- Only the clients that are active will be included within the set. -- @param #SET_CLIENT self - -- @param #boolean Active (Optional) Include only active clients to the set. + -- @param #boolean Active (optional) Include only active clients to the set. -- Include inactive clients if you provide false. -- @return #SET_CLIENT self -- @usage @@ -3785,11 +4032,35 @@ do -- SET_CLIENT -- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() -- function SET_CLIENT:FilterActive( Active ) - Active = Active or not (Active == false) + Active = Active or not ( Active == false ) self.Filter.Active = Active return self end + --- Builds a set of clients in zones. + -- @param #SET_CLIENT self + -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @return #SET_TABLE self + function SET_CLIENT:FilterZones( Zones ) + if not self.Filter.Zones then + self.Filter.Zones = {} + end + local zones = {} + if Zones.ClassName and Zones.ClassName == "SET_ZONE" then + zones = Zones.Set + elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") + return self + else + zones = Zones + end + for _,Zone in pairs( zones ) do + local zonename = Zone:GetName() + self.Filter.Zones[zonename] = Zone + end + return self + end + --- Starts the filtering. -- @param #SET_CLIENT self -- @return #SET_CLIENT self @@ -3829,7 +4100,7 @@ do -- SET_CLIENT return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT, providing the CLIENT and optional parameters. + --- Iterate the SET_CLIENT and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. -- @param #SET_CLIENT self -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self @@ -3849,15 +4120,16 @@ do -- SET_CLIENT function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Client#CLIENT ClientObject + function( ZoneObject, ClientObject ) + if ClientObject:IsInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -3870,19 +4142,20 @@ do -- SET_CLIENT function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Client#CLIENT ClientObject + function( ZoneObject, ClientObject ) + if ClientObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end - + --- Iterate the SET_CLIENT and count alive units. -- @param #SET_CLIENT self -- @return #number count @@ -3891,7 +4164,7 @@ do -- SET_CLIENT local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -3900,7 +4173,7 @@ do -- SET_CLIENT return CountU end - + --- -- @param #SET_CLIENT self -- @param Wrapper.Client#CLIENT MClient @@ -3915,7 +4188,7 @@ do -- SET_CLIENT if self.Filter.Active ~= nil then local MClientActive = false - if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true) then + if self.Filter.Active == false or ( self.Filter.Active == true and MClient:IsActive() == true ) then MClientActive = true end MClientInclude = MClientInclude and MClientActive @@ -3962,7 +4235,7 @@ do -- SET_CLIENT if self.Filter.Countries then local MClientCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) self:T3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) if country.id[CountryName] and country.id[CountryName] == ClientCountryID then MClientCountry = true @@ -3985,17 +4258,32 @@ do -- SET_CLIENT end end + if self.Filter.Zones then + local MClientZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + local unit = MClient:GetClientGroupUnit() + if unit and unit:IsInZone(Zone) then + MClientZone = true + end + end + MClientInclude = MClientInclude and MClientZone + end + self:T2( MClientInclude ) return MClientInclude end end + do -- SET_PLAYER --- @type SET_PLAYER -- @extends Core.Set#SET_BASE + + --- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: -- -- ## SET_PLAYER constructor @@ -4042,6 +4330,7 @@ do -- SET_PLAYER Types = nil, Countries = nil, ClientPrefixes = nil, + Zones = nil, }, FilterMeta = { Coalitions = { @@ -4059,6 +4348,7 @@ do -- SET_PLAYER }, } + --- Creates a new SET_PLAYER object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_PLAYER self -- @return #SET_PLAYER @@ -4078,7 +4368,7 @@ do -- SET_PLAYER -- @return self function SET_PLAYER:AddClientsByName( AddClientNames ) - local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames } + local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } for AddClientID, AddClientName in pairs( AddClientNamesArray ) do self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) @@ -4093,7 +4383,7 @@ do -- SET_PLAYER -- @return self function SET_PLAYER:RemoveClientsByName( RemoveClientNames ) - local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames } + local RemoveClientNamesArray = ( type( RemoveClientNames ) == "table" ) and RemoveClientNames or { RemoveClientNames } for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do self:Remove( RemoveClientName.ClientName ) @@ -4102,6 +4392,7 @@ do -- SET_PLAYER return self end + --- Finds a Client based on the Player Name. -- @param #SET_PLAYER self -- @param #string PlayerName @@ -4112,6 +4403,8 @@ do -- SET_PLAYER return ClientFound end + + --- Builds a set of clients of coalitions joined by specific players. -- Possible current coalitions are red, blue and neutral. -- @param #SET_PLAYER self @@ -4129,6 +4422,31 @@ do -- SET_PLAYER end return self end + + --- Builds a set of players in zones. + -- @param #SET_PLAYER self + -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @return #SET_PLAYER self + function SET_PLAYER:FilterZones( Zones ) + if not self.Filter.Zones then + self.Filter.Zones = {} + end + local zones = {} + if Zones.ClassName and Zones.ClassName == "SET_ZONE" then + zones = Zones.Set + elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") + return self + else + zones = Zones + end + for _,Zone in pairs( zones ) do + local zonename = Zone:GetName() + self.Filter.Zones[zonename] = Zone + end + return self + end + --- Builds a set of clients out of categories joined by players. -- Possible current categories are plane, helicopter, ground, ship. @@ -4148,6 +4466,7 @@ do -- SET_PLAYER return self end + --- Builds a set of clients of defined client types joined by players. -- Possible current types are those types known within DCS world. -- @param #SET_PLAYER self @@ -4166,6 +4485,7 @@ do -- SET_PLAYER return self end + --- Builds a set of clients of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_PLAYER self @@ -4184,6 +4504,7 @@ do -- SET_PLAYER return self end + --- Builds a set of PLAYERs that contain the given string in their unit/pilot name. -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all player clients that **contain** the string. -- @param #SET_PLAYER self @@ -4202,6 +4523,9 @@ do -- SET_PLAYER return self end + + + --- Starts the filtering. -- @param #SET_PLAYER self -- @return #SET_PLAYER self @@ -4241,7 +4565,7 @@ do -- SET_PLAYER return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- Iterate the SET_PLAYER and call an iterator function for each **alive** CLIENT, providing the CLIENT and optional parameters. + --- Iterate the SET_PLAYER and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. -- @param #SET_PLAYER self -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self @@ -4261,15 +4585,16 @@ do -- SET_PLAYER function SET_PLAYER:ForEachPlayerInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Client#CLIENT ClientObject + function( ZoneObject, ClientObject ) + if ClientObject:IsInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -4282,15 +4607,16 @@ do -- SET_PLAYER function SET_PLAYER:ForEachPlayerNotInZone( ZoneObject, IteratorFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet(), --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Client#CLIENT ClientObject + function( ZoneObject, ClientObject ) + if ClientObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) return self end @@ -4348,7 +4674,7 @@ do -- SET_PLAYER if self.Filter.Countries then local MClientCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) self:T3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) if country.id[CountryName] and country.id[CountryName] == ClientCountryID then MClientCountry = true @@ -4370,13 +4696,26 @@ do -- SET_PLAYER MClientInclude = MClientInclude and MClientPrefix end end - + + if self.Filter.Zones then + local MClientZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + local unit = MClient:GetClientGroupUnit() + if unit and unit:IsInZone(Zone) then + MClientZone = true + end + end + MClientInclude = MClientInclude and MClientZone + end + self:T2( MClientInclude ) return MClientInclude end end + do -- SET_AIRBASE --- @type SET_AIRBASE @@ -4438,6 +4777,7 @@ do -- SET_AIRBASE }, } + --- Creates a new SET_AIRBASE object, building a set of airbases belonging to a coalitions and categories. -- @param #SET_AIRBASE self -- @return #SET_AIRBASE self @@ -4468,7 +4808,7 @@ do -- SET_AIRBASE -- @return self function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) - local AddAirbaseNamesArray = (type( AddAirbaseNames ) == "table") and AddAirbaseNames or { AddAirbaseNames } + local AddAirbaseNamesArray = ( type( AddAirbaseNames ) == "table" ) and AddAirbaseNames or { AddAirbaseNames } for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) @@ -4483,7 +4823,7 @@ do -- SET_AIRBASE -- @return self function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) - local RemoveAirbaseNamesArray = (type( RemoveAirbaseNames ) == "table") and RemoveAirbaseNames or { RemoveAirbaseNames } + local RemoveAirbaseNamesArray = ( type( RemoveAirbaseNames ) == "table" ) and RemoveAirbaseNames or { RemoveAirbaseNames } for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do self:Remove( RemoveAirbaseName ) @@ -4492,6 +4832,7 @@ do -- SET_AIRBASE return self end + --- Finds a Airbase based on the Airbase Name. -- @param #SET_AIRBASE self -- @param #string AirbaseName @@ -4502,6 +4843,7 @@ do -- SET_AIRBASE return AirbaseFound end + --- Finds an Airbase in range of a coordinate. -- @param #SET_AIRBASE self -- @param Core.Point#COORDINATE Coordinate @@ -4516,7 +4858,7 @@ do -- SET_AIRBASE local AirbaseCoordinate = AirbaseObject:GetCoordinate() local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) - self:F( { Distance = Distance } ) + self:F({Distance=Distance}) if Distance <= Range then AirbaseFound = AirbaseObject @@ -4528,6 +4870,7 @@ do -- SET_AIRBASE return AirbaseFound end + --- Finds a random Airbase in the set. -- @param #SET_AIRBASE self -- @return Wrapper.Airbase#AIRBASE The found Airbase. @@ -4539,6 +4882,8 @@ do -- SET_AIRBASE return RandomAirbase end + + --- Builds a set of airbases of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_AIRBASE self @@ -4557,6 +4902,7 @@ do -- SET_AIRBASE return self end + --- Builds a set of airbases out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_AIRBASE self @@ -4583,8 +4929,8 @@ do -- SET_AIRBASE if _DATABASE then -- We use the BaseCaptured event, which is generated by DCS when a base got captured. - self:HandleEvent( EVENTS.BaseCaptured ) - self:HandleEvent( EVENTS.Dead ) + self:HandleEvent(EVENTS.BaseCaptured) + self:HandleEvent(EVENTS.Dead) -- We initialize the first set. for ObjectName, Object in pairs( self.Database ) do @@ -4602,7 +4948,7 @@ do -- SET_AIRBASE --- Base capturing event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - function SET_AIRBASE:OnEventBaseCaptured( EventData ) + function SET_AIRBASE:OnEventBaseCaptured(EventData) -- When a base got captured, we reevaluate the set. for ObjectName, Object in pairs( self.Database ) do @@ -4620,16 +4966,17 @@ do -- SET_AIRBASE --- Dead event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - function SET_AIRBASE:OnEventDead( EventData ) + function SET_AIRBASE:OnEventDead(EventData) - local airbaseName, airbase = self:FindInDatabase( EventData ) + local airbaseName, airbase=self:FindInDatabase(EventData) if airbase and (airbase:IsShip() or airbase:IsHelipad()) then - self:RemoveAirbasesByName( airbaseName ) + self:RemoveAirbasesByName(airbaseName) end 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_AIRBASE self @@ -4652,7 +4999,7 @@ do -- SET_AIRBASE return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- Iterate the SET_AIRBASE and call an iterator function for each AIRBASE, providing the AIRBASE and optional parameters. + --- Iterate the SET_AIRBASE and call an interator function for each AIRBASE, providing the AIRBASE and optional parameters. -- @param #SET_AIRBASE self -- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. -- @return #SET_AIRBASE self @@ -4675,6 +5022,8 @@ do -- SET_AIRBASE return NearestAirbase end + + --- -- @param #SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE MAirbase @@ -4720,6 +5069,7 @@ do -- SET_AIRBASE end + do -- SET_CARGO --- @type SET_CARGO @@ -4784,37 +5134,40 @@ do -- SET_CARGO }, } + --- Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. -- @param #SET_CARGO self -- @return #SET_CARGO -- @usage -- -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos. -- DatabaseSet = SET_CARGO:New() - function SET_CARGO:New() -- R2.1 + function SET_CARGO:New() --R2.1 -- Inherits from BASE local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) -- #SET_CARGO return self end + --- (R2.1) Add CARGO to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO Cargo A single cargo. -- @return Core.Set#SET_CARGO self - function SET_CARGO:AddCargo( Cargo ) -- R2.4 + function SET_CARGO:AddCargo( Cargo ) --R2.4 self:Add( Cargo:GetName(), Cargo ) return self end + --- (R2.1) Add CARGOs to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param #string AddCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self - function SET_CARGO:AddCargosByName( AddCargoNames ) -- R2.1 + function SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1 - local AddCargoNamesArray = (type( AddCargoNames ) == "table") and AddCargoNames or { AddCargoNames } + local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames } for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) @@ -4827,9 +5180,9 @@ do -- SET_CARGO -- @param Core.Set#SET_CARGO self -- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self - function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) -- R2.1 + function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1 - local RemoveCargoNamesArray = (type( RemoveCargoNames ) == "table") and RemoveCargoNames or { RemoveCargoNames } + local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames } for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do self:Remove( RemoveCargoName.CargoName ) @@ -4838,22 +5191,25 @@ do -- SET_CARGO return self end + --- (R2.1) Finds a Cargo based on the Cargo Name. -- @param #SET_CARGO self -- @param #string CargoName -- @return Wrapper.Cargo#CARGO The found Cargo. - function SET_CARGO:FindCargo( CargoName ) -- R2.1 + function SET_CARGO:FindCargo( CargoName ) --R2.1 local CargoFound = self.Set[CargoName] return CargoFound end + + --- (R2.1) Builds a set of cargos of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CARGO self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_CARGO self - function SET_CARGO:FilterCoalitions( Coalitions ) -- R2.1 + function SET_CARGO:FilterCoalitions( Coalitions ) --R2.1 if not self.Filter.Coalitions then self.Filter.Coalitions = {} end @@ -4871,7 +5227,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_CARGO self - function SET_CARGO:FilterTypes( Types ) -- R2.1 + function SET_CARGO:FilterTypes( Types ) --R2.1 if not self.Filter.Types then self.Filter.Types = {} end @@ -4884,12 +5240,13 @@ do -- SET_CARGO return self end + --- (R2.1) Builds a set of cargos of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_CARGO self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_CARGO self - function SET_CARGO:FilterCountries( Countries ) -- R2.1 + function SET_CARGO:FilterCountries( Countries ) --R2.1 if not self.Filter.Countries then self.Filter.Countries = {} end @@ -4902,12 +5259,13 @@ do -- SET_CARGO return self end + --- Builds a set of CARGOs that contain a given string in their name. -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all cargos that **contain** the string. -- @param #SET_CARGO self -- @param #string Prefixes The string pattern(s) that need to be in the cargo name. Can also be passed as a `#table` of strings. -- @return #SET_CARGO self - function SET_CARGO:FilterPrefixes( Prefixes ) -- R2.1 + function SET_CARGO:FilterPrefixes( Prefixes ) --R2.1 if not self.Filter.CargoPrefixes then self.Filter.CargoPrefixes = {} end @@ -4920,10 +5278,12 @@ do -- SET_CARGO return self end + + --- (R2.1) Starts the filtering. -- @param #SET_CARGO self -- @return #SET_CARGO self - function SET_CARGO:FilterStart() -- R2.1 + function SET_CARGO:FilterStart() --R2.1 if _DATABASE then self:_FilterStart() @@ -4945,13 +5305,14 @@ do -- SET_CARGO return self end + --- (R2.1) 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_CARGO self -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CARGO -- @return #table The CARGO - function SET_CARGO:AddInDatabase( Event ) -- R2.1 + function SET_CARGO:AddInDatabase( Event ) --R2.1 self:F3( { Event } ) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] @@ -4963,17 +5324,17 @@ do -- SET_CARGO -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CARGO -- @return #table The CARGO - function SET_CARGO:FindInDatabase( Event ) -- R2.1 + function SET_CARGO:FindInDatabase( Event ) --R2.1 self:F3( { Event } ) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- (R2.1) Iterate the SET_CARGO and call an iterator function for each CARGO, providing the CARGO and optional parameters. + --- (R2.1) Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters. -- @param #SET_CARGO self -- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. -- @return #SET_CARGO self - function SET_CARGO:ForEachCargo( IteratorFunction, ... ) -- R2.1 + function SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet() ) @@ -4985,7 +5346,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. -- @return Wrapper.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. - function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) -- R2.1 + function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 self:F2( PointVec2 ) local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 ) @@ -5020,6 +5381,7 @@ do -- SET_CARGO return FirstCargo end + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded. -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. @@ -5028,6 +5390,7 @@ do -- SET_CARGO return FirstCargo end + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded and not Deployed. -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. @@ -5036,6 +5399,7 @@ do -- SET_CARGO return FirstCargo end + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Loaded. -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. @@ -5044,6 +5408,7 @@ do -- SET_CARGO return FirstCargo end + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Deployed. -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. @@ -5052,11 +5417,14 @@ do -- SET_CARGO return FirstCargo end + + + --- (R2.1) -- @param #SET_CARGO self -- @param AI.AI_Cargo#AI_CARGO MCargo -- @return #SET_CARGO self - function SET_CARGO:IsIncludeObject( MCargo ) -- R2.1 + function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 self:F2( MCargo ) local MCargoInclude = true @@ -5109,13 +5477,13 @@ do -- SET_CARGO --- (R2.1) Handles the OnEventNewCargo event for the Set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData - function SET_CARGO:OnEventNewCargo( EventData ) -- R2.1 + function SET_CARGO:OnEventNewCargo( EventData ) --R2.1 self:F( { "New Cargo", EventData } ) if EventData.Cargo then if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then - self:Add( EventData.Cargo.Name, EventData.Cargo ) + self:Add( EventData.Cargo.Name , EventData.Cargo ) end end end @@ -5123,20 +5491,20 @@ do -- SET_CARGO --- (R2.1) Handles the OnDead or OnCrash event for alive units set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData - function SET_CARGO:OnEventDeleteCargo( EventData ) -- R2.1 + function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 self:F3( { EventData } ) if EventData.Cargo then local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) if Cargo and Cargo.Name 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_CARGOs. - -- To prevent this from happening, the Cargo object has a flag NoDestroy. - -- When true, the SET_CARGO won't Remove the Cargo object from the set. - -- This flag is switched off after the event handlers have been called in the EVENT class. - self:F( { CargoNoDestroy = Cargo.NoDestroy } ) + -- 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_CARGOs. + -- To prevent this from happening, the Cargo object has a flag NoDestroy. + -- When true, the SET_CARGO won't Remove the Cargo object from the set. + -- This flag is switched off after the event handlers have been called in the EVENT class. + self:F( { CargoNoDestroy=Cargo.NoDestroy } ) if Cargo.NoDestroy then else self:Remove( Cargo.Name ) @@ -5147,6 +5515,7 @@ do -- SET_CARGO end + do -- SET_ZONE --- @type SET_ZONE @@ -5186,15 +5555,17 @@ do -- SET_ZONE -- -- === -- @field #SET_ZONE SET_ZONE - SET_ZONE = { - lassName = "SET_ZONE", + SET_ZONE = { + ClassName = "SET_ZONE", Zones = {}, Filter = { Prefixes = nil, }, - FilterMeta = {}, + FilterMeta = { + }, } + --- Creates a new SET_ZONE object, building a set of zones. -- @param #SET_ZONE self -- @return #SET_ZONE self @@ -5214,7 +5585,7 @@ do -- SET_ZONE -- @return self function SET_ZONE:AddZonesByName( AddZoneNames ) - local AddZoneNamesArray = (type( AddZoneNames ) == "table") and AddZoneNames or { AddZoneNames } + local AddZoneNamesArray = ( type( AddZoneNames ) == "table" ) and AddZoneNames or { AddZoneNames } for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) ) @@ -5234,13 +5605,14 @@ do -- SET_ZONE return self end + --- Remove ZONEs from SET_ZONE. -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. -- @return self function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) - local RemoveZoneNamesArray = (type( RemoveZoneNames ) == "table") and RemoveZoneNames or { RemoveZoneNames } + local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do self:Remove( RemoveZoneName ) @@ -5249,6 +5621,7 @@ do -- SET_ZONE return self end + --- Finds a Zone based on the Zone Name. -- @param #SET_ZONE self -- @param #string ZoneName @@ -5259,13 +5632,14 @@ do -- SET_ZONE return ZoneFound end + --- Get a random zone from the set. -- @param #SET_ZONE self -- @param #number margin Number of tries to find a zone -- @return Core.Zone#ZONE_BASE The random Zone. -- @return #nil if no zone in the collection. - function SET_ZONE:GetRandomZone( margin ) - + function SET_ZONE:GetRandomZone(margin) + local margin = margin or 100 if self:Count() ~= 0 then @@ -5288,6 +5662,7 @@ do -- SET_ZONE return nil end + --- Set a zone probability. -- @param #SET_ZONE self -- @param #string ZoneName The name of the zone. @@ -5296,6 +5671,9 @@ do -- SET_ZONE Zone:SetZoneProbability( ZoneProbability ) end + + + --- Builds a set of ZONEs that contain the given string in their name. -- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string. -- @param #SET_ZONE self @@ -5314,6 +5692,7 @@ do -- SET_ZONE return self end + --- Starts the filtering. -- @param #SET_ZONE self -- @return #SET_ZONE self @@ -5372,7 +5751,7 @@ do -- SET_ZONE return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- Iterate the SET_ZONE and call an iterator function for each ZONE, providing the ZONE and optional parameters. + --- Iterate the SET_ZONE and call an interator function for each ZONE, providing the ZONE and optional parameters. -- @param #SET_ZONE self -- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter. -- @return #SET_ZONE self @@ -5384,6 +5763,7 @@ do -- SET_ZONE return self end + --- -- @param #SET_ZONE self -- @param Core.Zone#ZONE_BASE MZone @@ -5416,13 +5796,13 @@ do -- SET_ZONE --- Handles the OnEventNewZone event for the Set. -- @param #SET_ZONE self -- @param Core.Event#EVENTDATA EventData - function SET_ZONE:OnEventNewZone( EventData ) -- R2.1 + function SET_ZONE:OnEventNewZone( EventData ) --R2.1 self:F( { "New Zone", EventData } ) if EventData.Zone then if EventData.Zone and self:IsIncludeObject( EventData.Zone ) then - self:Add( EventData.Zone.ZoneName, EventData.Zone ) + self:Add( EventData.Zone.ZoneName , EventData.Zone ) end end end @@ -5430,20 +5810,20 @@ do -- SET_ZONE --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_ZONE self -- @param Core.Event#EVENTDATA EventData - function SET_ZONE:OnEventDeleteZone( EventData ) -- R2.1 + function SET_ZONE:OnEventDeleteZone( EventData ) --R2.1 self:F3( { EventData } ) if EventData.Zone then local Zone = _DATABASE:FindZone( EventData.Zone.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_ZONEs. - -- To prevent this from happening, the Zone object has a flag NoDestroy. - -- When true, the SET_ZONE 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 } ) + -- 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_ZONEs. + -- To prevent this from happening, the Zone object has a flag NoDestroy. + -- When true, the SET_ZONE 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 ) @@ -5518,9 +5898,11 @@ do -- SET_ZONE_GOAL Filter = { Prefixes = nil, }, - FilterMeta = {}, + FilterMeta = { + }, } + --- Creates a new SET_ZONE_GOAL object, building a set of zones. -- @param #SET_ZONE_GOAL self -- @return #SET_ZONE_GOAL self @@ -5545,13 +5927,14 @@ do -- SET_ZONE_GOAL 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 } + local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do self:Remove( RemoveZoneName ) @@ -5560,6 +5943,7 @@ do -- SET_ZONE_GOAL return self end + --- Finds a Zone based on the Zone Name. -- @param #SET_ZONE_GOAL self -- @param #string ZoneName @@ -5570,6 +5954,7 @@ do -- SET_ZONE_GOAL return ZoneFound end + --- Get a random zone from the set. -- @param #SET_ZONE_GOAL self -- @return Core.Zone#ZONE_BASE The random Zone. @@ -5595,6 +5980,7 @@ do -- SET_ZONE_GOAL return nil end + --- Set a zone probability. -- @param #SET_ZONE_GOAL self -- @param #string ZoneName The name of the zone. @@ -5603,6 +5989,9 @@ do -- SET_ZONE_GOAL Zone:SetZoneProbability( ZoneProbability ) end + + + --- Builds a set of ZONE_GOALs that contain the given string in their name. -- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string. -- @param #SET_ZONE_GOAL self @@ -5621,6 +6010,7 @@ do -- SET_ZONE_GOAL return self end + --- Starts the filtering. -- @param #SET_ZONE_GOAL self -- @return #SET_ZONE_GOAL self @@ -5679,7 +6069,7 @@ do -- SET_ZONE_GOAL return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- Iterate the SET_ZONE_GOAL and call an iterator function for each ZONE, providing the ZONE and optional parameters. + --- 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 @@ -5691,6 +6081,7 @@ do -- SET_ZONE_GOAL return self end + --- -- @param #SET_ZONE_GOAL self -- @param Core.Zone#ZONE_BASE MZone @@ -5725,13 +6116,14 @@ do -- SET_ZONE_GOAL -- @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 } ) + -- Debug info. + self:T( { "New Zone Capture Coalition", EventData } ) + self:T( { "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 ) + self:T( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } ) + self:Add( EventData.ZoneGoal.ZoneName , EventData.ZoneGoal ) end end end @@ -5739,20 +6131,20 @@ do -- SET_ZONE_GOAL --- 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 + 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 } ) + -- 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 ) @@ -5781,3 +6173,618 @@ do -- SET_ZONE_GOAL end end + + + +do -- SET_OPSGROUP + + --- @type SET_OPSGROUP + -- @extends Core.Set#SET_BASE + + --- Mission designers can use the @{Core.Set#SET_OPSGROUP} class to build sets of OPS groups belonging to certain: + -- + -- * Coalitions + -- * Categories + -- * Countries + -- * Contain a certain string pattern + -- + -- ## SET_OPSGROUP constructor + -- + -- Create a new SET_OPSGROUP object with the @{#SET_OPSGROUP.New} method: + -- + -- * @{#SET_OPSGROUP.New}: Creates a new SET_OPSGROUP object. + -- + -- ## Add or Remove GROUP(s) from SET_OPSGROUP + -- + -- GROUPS can be added and removed using the @{Core.Set#SET_OPSGROUP.AddGroupsByName} and @{Core.Set#SET_OPSGROUP.RemoveGroupsByName} respectively. + -- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_OPSGROUP. + -- + -- ## SET_OPSGROUP filter criteria + -- + -- You can set filter criteria to define the set of groups within the SET_OPSGROUP. + -- Filter criteria are defined by: + -- + -- * @{#SET_OPSGROUP.FilterCoalitions}: Builds the SET_OPSGROUP with the groups belonging to the coalition(s). + -- * @{#SET_OPSGROUP.FilterCategories}: Builds the SET_OPSGROUP with the groups belonging to the category(ies). + -- * @{#SET_OPSGROUP.FilterCountries}: Builds the SET_OPSGROUP with the groups belonging to the country(ies). + -- * @{#SET_OPSGROUP.FilterPrefixes}: Builds the SET_OPSGROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*. + -- * @{#SET_OPSGROUP.FilterActive}: Builds the SET_OPSGROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set! + -- + -- For the Category Filter, extra methods have been added: + -- + -- * @{#SET_OPSGROUP.FilterCategoryAirplane}: Builds the SET_OPSGROUP from airplanes. + -- * @{#SET_OPSGROUP.FilterCategoryHelicopter}: Builds the SET_OPSGROUP from helicopters. + -- * @{#SET_OPSGROUP.FilterCategoryGround}: Builds the SET_OPSGROUP from ground vehicles or infantry. + -- * @{#SET_OPSGROUP.FilterCategoryShip}: Builds the SET_OPSGROUP from ships. + -- + -- + -- Once the filter criteria have been set for the SET_OPSGROUP, you can start filtering using: + -- + -- * @{#SET_OPSGROUP.FilterStart}: Starts the filtering of the groups within the SET_OPSGROUP and add or remove GROUP objects **dynamically**. + -- * @{#SET_OPSGROUP.FilterOnce}: Filters of the groups **once**. + -- + -- + -- ## SET_OPSGROUP iterators + -- + -- Once the filters have been defined and the SET_OPSGROUP has been built, you can iterate the SET_OPSGROUP with the available iterator methods. + -- The iterator methods will walk the SET_OPSGROUP set, and call for each element within the set a function that you provide. + -- The following iterator methods are currently available within the SET_OPSGROUP: + -- + -- * @{#SET_OPSGROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_OPSGROUP. + -- + -- ## SET_OPSGROUP trigger events on the GROUP objects. + -- + -- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the GROUP objects in the SET_OPSGROUP. + -- + -- ### When a GROUP object crashes or is dead, the SET_OPSGROUP will trigger a **Dead** event. + -- + -- You can handle the event using the OnBefore and OnAfter event handlers. + -- The event handlers need to have the paramters From, Event, To, GroupObject. + -- The GroupObject is the GROUP object that is dead and within the SET_OPSGROUP, and is passed as a parameter to the event handler. + -- See the following example: + -- + -- -- Create the SetCarrier SET_OPSGROUP collection. + -- + -- local SetHelicopter = SET_OPSGROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- + -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. + -- + -- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject ) + -- self:F( { GroupObject = GroupObject:GetName() } ) + -- end + -- + -- + -- === + -- + -- @field #SET_OPSGROUP SET_OPSGROUP + -- + SET_OPSGROUP = { + ClassName = "SET_OPSGROUP", + Filter = { + Coalitions = nil, + Categories = nil, + Countries = nil, + GroupPrefixes = nil, + }, + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + Categories = { + plane = Group.Category.AIRPLANE, + helicopter = Group.Category.HELICOPTER, + ground = Group.Category.GROUND, + ship = Group.Category.SHIP, + }, + }, -- FilterMeta + } + + + --- Creates a new SET_OPSGROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP + function SET_OPSGROUP:New() + + -- Inherit SET_BASE. + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.GROUPS)) -- #SET_OPSGROUP + + -- Include non activated + self:FilterActive( false ) + + return self + end + + --- Gets the Set. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:GetAliveSet() + + local AliveSet = SET_OPSGROUP:New() + + -- Clean the Set before returning with only the alive Groups. + for GroupName, GroupObject in pairs(self.Set) do + local GroupObject=GroupObject --Wrapper.Group#GROUP + + if GroupObject and GroupObject:IsAlive() then + AliveSet:Add(GroupName, GroupObject) + end + end + + return AliveSet.Set or {} + end + + --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index. + -- @param #SET_BASE self + -- @param #string ObjectName The name of the object. + -- @param Core.Base#BASE Object The object itself. + -- @return Core.Base#BASE The added BASE Object. + function SET_OPSGROUP:Add(ObjectName, Object) + self:T( { ObjectName = ObjectName, Object = Object } ) + + -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set + if self.Set[ObjectName] then + self:Remove(ObjectName, true) + end + + local object=nil --Ops.OpsGroup#OPSGROUP + if Object:IsInstanceOf("GROUP") then + + --- + -- GROUP Object + --- + + -- Fist, look up in the DATABASE if an OPSGROUP already exists. + object=_DATABASE:FindOpsGroup(ObjectName) + + if not object then + + if Object:IsShip() then + object=NAVYGROUP:New(Object) + elseif Object:IsGround() then + object=ARMYGROUP:New(Object) + elseif Object:IsAir() then + object=FLIGHTGROUP:New(Object) + else + env.error("ERROR: Unknown category of group object!") + end + end + + elseif Object:IsInstanceOf("OPSGROUP") then + -- We already have an OPSGROUP. + object=Object + else + env.error("ERROR: Object must be a GROUP or OPSGROUP!") + end + + -- Add object to set. + self.Set[ObjectName]=object + + -- Add Object name to Index. + table.insert(self.Index, ObjectName) + + -- Trigger Added event. + self:Added(ObjectName, object) + end + + --- Add a GROUP or OPSGROUP object to the set. + -- **NOTE** that an OPSGROUP is automatically created from the GROUP if it does not exist already. + -- @param Core.Set#SET_OPSGROUP self + -- @param Wrapper.Group#GROUP group The GROUP which should be added to the set. Can also be given as an #OPSGROUP object. + -- @return Core.Set#SET_OPSGROUP self + function SET_OPSGROUP:AddGroup(group) + + local groupname=group:GetName() + + self:Add(groupname, group ) + + return self + end + + --- Add GROUP(s) or OPSGROUP(s) to the set. + -- @param Core.Set#SET_OPSGROUP self + -- @param #string AddGroupNames A single name or an array of GROUP names. + -- @return Core.Set#SET_OPSGROUP self + function SET_OPSGROUP:AddGroupsByName( AddGroupNames ) + + local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } + + for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do + self:Add(AddGroupName, GROUP:FindByName(AddGroupName)) + end + + return self + end + + --- Remove GROUP(s) or OPSGROUP(s) from the set. + -- @param Core.Set#SET_OPSGROUP self + -- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. + -- @return Core.Set#SET_OPSGROUP self + function SET_OPSGROUP:RemoveGroupsByName( RemoveGroupNames ) + + local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } + + for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do + self:Remove( RemoveGroupName ) + end + + return self + end + + --- Finds an OPSGROUP based on the group name. + -- @param #SET_OPSGROUP self + -- @param #string GroupName Name of the group. + -- @return Ops.OpsGroup#OPSGROUP The found OPSGROUP (FLIGHTGROUP, ARMYGROUP or NAVYGROUP) or `#nil` if the group is not in the set. + function SET_OPSGROUP:FindGroup(GroupName) + local GroupFound = self.Set[GroupName] + return GroupFound + end + + --- Finds a FLIGHTGROUP based on the group name. + -- @param #SET_OPSGROUP self + -- @param #string GroupName Name of the group. + -- @return Ops.FlightGroup#FLIGHTGROUP The found FLIGHTGROUP or `#nil` if the group is not in the set. + function SET_OPSGROUP:FindFlightGroup(GroupName) + local GroupFound = self:FindGroup(GroupName) + return GroupFound + end + + --- Finds a ARMYGROUP based on the group name. + -- @param #SET_OPSGROUP self + -- @param #string GroupName Name of the group. + -- @return Ops.ArmyGroup#ARMYGROUP The found ARMYGROUP or `#nil` if the group is not in the set. + function SET_OPSGROUP:FindArmyGroup(GroupName) + local GroupFound = self:FindGroup(GroupName) + return GroupFound + end + + + --- Finds a NAVYGROUP based on the group name. + -- @param #SET_OPSGROUP self + -- @param #string GroupName Name of the group. + -- @return Ops.NavyGroup#NAVYGROUP The found NAVYGROUP or `#nil` if the group is not in the set. + function SET_OPSGROUP:FindNavyGroup(GroupName) + local GroupFound = self:FindGroup(GroupName) + return GroupFound + end + + --- Builds a set of groups of coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_OPSGROUP self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCoalitions(Coalitions) + + -- Create an empty set. + if not self.Filter.Coalitions then + self.Filter.Coalitions={} + end + + -- Ensure we got a table. + if type(Coalitions)~="table" then + Coalitions = {Coalitions} + end + + -- Set filter. + for CoalitionID, Coalition in pairs( Coalitions ) do + self.Filter.Coalitions[Coalition] = Coalition + end + + return self + end + + + --- Builds a set of groups out of categories. + -- + -- Possible current categories are: + -- + -- * "plane" for fixed wing groups + -- * "helicopter" for rotary wing groups + -- * "ground" for ground groups + -- * "ship" for naval groups + -- + -- @param #SET_OPSGROUP self + -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship" or combinations as a table, for example `{"plane", "helicopter"}`. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCategories( Categories ) + + if not self.Filter.Categories then + self.Filter.Categories={} + end + + if type(Categories)~="table" then + Categories={Categories} + end + + for CategoryID, Category in pairs( Categories ) do + self.Filter.Categories[Category] = Category + end + + return self + end + + --- Builds a set of groups out of ground category. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCategoryGround() + self:FilterCategories("ground") + return self + end + + --- Builds a set of groups out of airplane category. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCategoryAirplane() + self:FilterCategories("plane") + return self + end + + --- Builds a set of groups out of aicraft category (planes and helicopters). + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCategoryAircraft() + self:FilterCategories({"plane", "helicopter"}) + return self + end + + --- Builds a set of groups out of helicopter category. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCategoryHelicopter() + self:FilterCategories("helicopter") + return self + end + + --- Builds a set of groups out of ship category. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCategoryShip() + self:FilterCategories("ship") + return self + end + + --- Builds a set of groups of defined countries. + -- @param #SET_OPSGROUP self + -- @param #string Countries Can take those country strings known within DCS world. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterCountries(Countries) + + -- Create empty table if necessary. + if not self.Filter.Countries then + self.Filter.Countries = {} + end + + -- Ensure input is a table. + if type(Countries)~="table" then + Countries={Countries} + end + + -- Set filter. + for CountryID, Country in pairs( Countries ) do + self.Filter.Countries[Country] = Country + end + + return self + end + + + --- Builds a set of groups that contain the given string in their group name. + -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string. + -- @param #SET_OPSGROUP self + -- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterPrefixes(Prefixes) + + -- Create emtpy table if necessary. + if not self.Filter.GroupPrefixes then + self.Filter.GroupPrefixes={} + end + + -- Ensure we have a table. + if type(Prefixes)~="table" then + Prefixes={Prefixes} + end + + -- Set group prefixes. + for PrefixID, Prefix in pairs(Prefixes) do + self.Filter.GroupPrefixes[Prefix]=Prefix + end + + return self + end + + --- Builds a set of groups that are only active. + -- Only the groups that are active will be included within the set. + -- @param #SET_OPSGROUP self + -- @param #boolean Active (optional) Include only active groups to the set. + -- Include inactive groups if you provide false. + -- @return #SET_OPSGROUP self + -- @usage + -- + -- -- Include only active groups to the set. + -- GroupSet = SET_OPSGROUP:New():FilterActive():FilterStart() + -- + -- -- Include only active groups to the set of the blue coalition, and filter one time. + -- GroupSet = SET_OPSGROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- + -- -- Include only active groups to the set of the blue coalition, and filter one time. + -- -- Later, reset to include back inactive groups to the set. + -- GroupSet = SET_OPSGROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ... logic ... + -- GroupSet = SET_OPSGROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- + function SET_OPSGROUP:FilterActive( Active ) + Active = Active or not ( Active == false ) + self.Filter.Active = Active + return self + end + + + --- Starts the filtering. + -- @param #SET_OPSGROUP self + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:FilterStart() + + if _DATABASE then + self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + end + + return self + end + + --- Activate late activated groups in the set. + -- @param #SET_OPSGROUP self + -- @param #number Delay Delay in seconds. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:Activate(Delay) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do + local group=GroupData --Ops.OpsGroup#OPSGROUP + if group and group:IsAlive()==false then + group:Activate(Delay) + end + end + return self + end + + --- Handles the OnDead or OnCrash event for alive groups set. + -- Note: The GROUP object in the SET_OPSGROUP collection will only be removed if the last unit is destroyed of the GROUP. + -- @param #SET_OPSGROUP self + -- @param Core.Event#EVENTDATA Event + function SET_OPSGROUP:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) + + if Event.IniDCSUnit then + local ObjectName, Object = self:FindInDatabase( Event ) + if ObjectName then + if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. + self:Remove( ObjectName ) + end + end + end + 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_OPSGROUP self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the GROUP + -- @return #table The GROUP + function SET_OPSGROUP:AddInDatabase( Event ) + + if Event.IniObjectCategory==1 then + + if not self.Database[Event.IniDCSGroupName] then + self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) + end + + end + + return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] + 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_OPSGROUP self + -- @param Core.Event#EVENTDATA Event Event data table. + -- @return #string The name of the GROUP + -- @return #table The GROUP + function SET_OPSGROUP:FindInDatabase(Event) + return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] + end + + --- Iterate the set and call an iterator function for each OPSGROUP object. + -- @param #SET_OPSGROUP self + -- @param #function IteratorFunction The function that will be called for all OPSGROUPs in the set. **NOTE** that the function must have the OPSGROUP as first parameter! + -- @param ... (Optional) arguments passed to the `IteratorFunction`. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:ForEachGroup( IteratorFunction, ... ) + + self:ForEach(IteratorFunction, arg, self:GetSet()) + + return self + end + + --- Check include object. + -- @param #SET_OPSGROUP self + -- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion. + -- @return #SET_OPSGROUP self + function SET_OPSGROUP:IsIncludeObject(MGroup) + + -- Assume it is and check later if not. + local MGroupInclude=true + + -- Filter active. + if self.Filter.Active~=nil then + + local MGroupActive = false + + if self.Filter.Active==false or (self.Filter.Active==true and MGroup:IsActive()==true) then + MGroupActive = true + end + + MGroupInclude = MGroupInclude and MGroupActive + end + + -- Filter coalitions. + if self.Filter.Coalitions then + + local MGroupCoalition = false + + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName]==MGroup:GetCoalition() then + MGroupCoalition = true + end + end + + MGroupInclude = MGroupInclude and MGroupCoalition + end + + -- Filter categories. + if self.Filter.Categories then + + local MGroupCategory = false + + for CategoryID, CategoryName in pairs( self.Filter.Categories ) do + if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName]==MGroup:GetCategory() then + MGroupCategory = true + end + end + + MGroupInclude = MGroupInclude and MGroupCategory + end + + -- Filter countries. + if self.Filter.Countries then + local MGroupCountry = false + for CountryID, CountryName in pairs( self.Filter.Countries ) do + if country.id[CountryName] == MGroup:GetCountry() then + MGroupCountry = true + end + end + MGroupInclude = MGroupInclude and MGroupCountry + end + + -- Filter "prefixes". + if self.Filter.GroupPrefixes then + + local MGroupPrefix = false + + for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do + if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?! + MGroupPrefix = true + end + end + + MGroupInclude = MGroupInclude and MGroupPrefix + end + + return MGroupInclude + end + +end