mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Correct module links. Replace references to missing images with MOOSE.JPG. Add default MOOSE.JPG where no image was previously set. Fix minor spelling error.
6994 lines
242 KiB
Lua
6994 lines
242 KiB
Lua
--- **Core** - Define collections of objects to perform bulk actions and logically group objects.
|
|
--
|
|
-- ===
|
|
--
|
|
-- ## Features:
|
|
--
|
|
-- * Dynamically maintain collections of objects.
|
|
-- * Manually modify the collection, by adding or removing objects.
|
|
-- * Collections of different types.
|
|
-- * Validate the presence of objects in the collection.
|
|
-- * Perform bulk actions on collection.
|
|
--
|
|
-- ===
|
|
--
|
|
-- Group objects or data of the same type into a collection, which is either:
|
|
--
|
|
-- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method.
|
|
-- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method.
|
|
--
|
|
-- 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_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_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.
|
|
-- * @{#SET_SCENERY}: Defines a collection of @{Warpper.Scenery}s added via a filtered @{#SET_ZONE}.
|
|
--
|
|
-- These classes are derived from @{#SET_BASE}, which contains the main methods to manage the collections.
|
|
--
|
|
-- A multitude of other methods are available in the individual set classes that allow to:
|
|
--
|
|
-- * Validate the presence of objects in the SET.
|
|
-- * Trigger events when objects in the SET change a zone presence.
|
|
--
|
|
-- ===
|
|
--
|
|
-- ### Author: **FlightControl**
|
|
-- ### Contributions: **funkyfranky**, **applevangelist**
|
|
--
|
|
-- ===
|
|
--
|
|
-- @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 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.
|
|
-- 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.
|
|
--
|
|
-- ## Add or remove objects from the SET
|
|
--
|
|
-- Some key core functions are @{Core.Set#SET_BASE.Add} and @{Core.Set#SET_BASE.Remove} to add or remove objects from the SET in your logic.
|
|
--
|
|
-- ## Define the SET iterator **"yield interval"** and the **"time interval"**
|
|
--
|
|
-- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetIteratorIntervals} method.
|
|
-- You can set the **"yield interval"**, and the **"time interval"**. (See above).
|
|
--
|
|
-- @field #SET_BASE SET_BASE
|
|
SET_BASE = {
|
|
ClassName = "SET_BASE",
|
|
Filter = {},
|
|
Set = {},
|
|
List = {},
|
|
Index = {},
|
|
Database = 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
|
|
-- @usage
|
|
-- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE.
|
|
-- DBObject = SET_BASE:New()
|
|
function SET_BASE:New( Database )
|
|
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, FSM:New() ) -- Core.Set#SET_BASE
|
|
|
|
self.Database = Database
|
|
|
|
self:SetStartState( "Started" )
|
|
|
|
--- Added Handler OnAfter for SET_BASE
|
|
-- @function [parent=#SET_BASE] OnAfterAdded
|
|
-- @param #SET_BASE self
|
|
-- @param #string From
|
|
-- @param #string Event
|
|
-- @param #string To
|
|
-- @param #string ObjectName The name of the object.
|
|
-- @param Object The object.
|
|
|
|
self:AddTransition( "*", "Added", "*" )
|
|
|
|
--- Removed Handler OnAfter for SET_BASE
|
|
-- @function [parent=#SET_BASE] OnAfterRemoved
|
|
-- @param #SET_BASE self
|
|
-- @param #string From
|
|
-- @param #string Event
|
|
-- @param #string To
|
|
-- @param #string ObjectName The name of the object.
|
|
-- @param Object The object.
|
|
|
|
self:AddTransition( "*", "Removed", "*" )
|
|
|
|
self.YieldInterval = 10
|
|
self.TimeInterval = 0.001
|
|
|
|
self.Set = {}
|
|
self.Index = {}
|
|
|
|
self.CallScheduler = SCHEDULER:New( self )
|
|
|
|
self:SetEventPriority( 2 )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Clear the Objects in the Set.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:Clear()
|
|
|
|
for Name, Object in pairs( self.Set ) do
|
|
self:Remove( Name )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds an @{Core.Base#BASE} object based on the object Name.
|
|
-- @param #SET_BASE self
|
|
-- @param #string ObjectName
|
|
-- @return Core.Base#BASE The Object found.
|
|
function SET_BASE:_Find( ObjectName )
|
|
|
|
local ObjectFound = self.Set[ObjectName]
|
|
return ObjectFound
|
|
end
|
|
|
|
--- Gets the Set.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:GetSet()
|
|
self:F2()
|
|
|
|
return self.Set or {}
|
|
end
|
|
|
|
--- 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
|
|
self:F2()
|
|
|
|
local Names = {}
|
|
|
|
for Name, Object in pairs( self.Set ) do
|
|
table.insert( Names, Name )
|
|
end
|
|
|
|
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
|
|
self:F2()
|
|
|
|
local Objects = {}
|
|
|
|
for Name, Object in pairs( self.Set ) do
|
|
table.insert( Objects, Object )
|
|
end
|
|
|
|
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.
|
|
function SET_BASE:Remove( ObjectName, NoTriggerEvent )
|
|
self:F2( { ObjectName = ObjectName } )
|
|
|
|
local TriggerEvent = true
|
|
if NoTriggerEvent then TriggerEvent = false end
|
|
|
|
local Object = self.Set[ObjectName]
|
|
|
|
if Object then
|
|
for Index, Key in ipairs( self.Index ) do
|
|
if Key == ObjectName then
|
|
table.remove( self.Index, Index )
|
|
self.Set[ObjectName] = nil
|
|
break
|
|
end
|
|
end
|
|
-- When NoTriggerEvent is true, then no Removed event will be triggered.
|
|
if TriggerEvent then
|
|
self:Removed( ObjectName, Object )
|
|
end
|
|
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 )
|
|
|
|
-- 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
|
|
self:Remove( ObjectName, true )
|
|
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
|
|
|
|
--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index.
|
|
-- @param #SET_BASE self
|
|
-- @param Wrapper.Object#OBJECT Object
|
|
-- @return Core.Base#BASE The added BASE Object.
|
|
function SET_BASE:AddObject( Object )
|
|
self:F2( Object.ObjectName )
|
|
|
|
self:T( Object.UnitName )
|
|
self:T( Object.ObjectName )
|
|
self:Add( Object.ObjectName, Object )
|
|
|
|
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<b
|
|
end
|
|
|
|
table.sort(self.Index)
|
|
|
|
end
|
|
|
|
--- Add a SET to this set.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Set#SET_BASE SetToAdd Set to add.
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:AddSet(SetToAdd)
|
|
|
|
if not SetToAdd then return self end
|
|
|
|
for _,ObjectB in pairs(SetToAdd.Set) do
|
|
self:AddObject(ObjectB)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
--- Get the *union* of two sets.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Set#SET_BASE SetB Set *B*.
|
|
-- @return Core.Set#SET_BASE The union set, i.e. contains objects that are in set *A* **or** in set *B*.
|
|
function SET_BASE:GetSetUnion( SetB )
|
|
|
|
local union = SET_BASE:New()
|
|
|
|
for _, ObjectA in pairs( self.Set ) do
|
|
union:AddObject( ObjectA )
|
|
end
|
|
|
|
for _, ObjectB in pairs( SetB.Set ) do
|
|
union:AddObject( ObjectB )
|
|
end
|
|
|
|
return union
|
|
end
|
|
|
|
--- Get the *intersection* of this set, called *A*, and another set.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Set#SET_BASE SetB Set other set, called *B*.
|
|
-- @return Core.Set#SET_BASE A set of objects that is included in set *A* **and** in set *B*.
|
|
|
|
function SET_BASE:GetSetIntersection(SetB)
|
|
|
|
local intersection=SET_BASE:New()
|
|
|
|
local union=self:GetSetUnion(SetB)
|
|
|
|
for _,Object in pairs(union.Set) do
|
|
if self:IsIncludeObject(Object) and SetB:IsIncludeObject(Object) then
|
|
intersection:AddObject(Object)
|
|
end
|
|
end
|
|
|
|
return intersection
|
|
end
|
|
|
|
--- Get the *complement* of two sets.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Set#SET_BASE SetB Set other set, called *B*.
|
|
-- @return Core.Set#SET_BASE The set of objects that are in set *B* but **not** in this set *A*.
|
|
function SET_BASE:GetSetComplement( SetB )
|
|
|
|
local complement = self:GetSetUnion(SetB)
|
|
local intersection = self:GetSetIntersection(SetB)
|
|
|
|
for _,Object in pairs(intersection.Set) do
|
|
complement:Remove(Object.ObjectName,true)
|
|
end
|
|
|
|
return complement
|
|
end
|
|
|
|
--- Compare two sets.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Set#SET_BASE SetA First set.
|
|
-- @param Core.Set#SET_BASE SetB Set to be merged into first set.
|
|
-- @return Core.Set#SET_BASE The set of objects that are included in SetA and SetB.
|
|
function SET_BASE:CompareSets( SetA, SetB )
|
|
|
|
for _, ObjectB in pairs( SetB.Set ) do
|
|
if SetA:IsIncludeObject( ObjectB ) then
|
|
SetA:Add( ObjectB )
|
|
end
|
|
end
|
|
|
|
return SetA
|
|
end
|
|
|
|
--- Gets 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
|
|
-- @return Core.Base#BASE
|
|
function SET_BASE:Get( ObjectName )
|
|
self:F( ObjectName )
|
|
|
|
local Object = self.Set[ObjectName]
|
|
|
|
self:T3( { ObjectName, Object } )
|
|
return Object
|
|
end
|
|
|
|
--- Gets the first object from the @{Core.Set#SET_BASE} and derived classes.
|
|
-- @param #SET_BASE self
|
|
-- @return Core.Base#BASE
|
|
function SET_BASE:GetFirst()
|
|
|
|
local ObjectName = self.Index[1]
|
|
local FirstObject = self.Set[ObjectName]
|
|
self:T3( { FirstObject } )
|
|
return FirstObject
|
|
end
|
|
|
|
--- Gets the last object from the @{Core.Set#SET_BASE} and derived classes.
|
|
-- @param #SET_BASE self
|
|
-- @return Core.Base#BASE
|
|
function SET_BASE:GetLast()
|
|
|
|
local ObjectName = self.Index[#self.Index]
|
|
local LastObject = self.Set[ObjectName]
|
|
self:T3( { LastObject } )
|
|
return LastObject
|
|
end
|
|
|
|
--- Gets a random object from the @{Core.Set#SET_BASE} and derived classes.
|
|
-- @param #SET_BASE self
|
|
-- @return Core.Base#BASE
|
|
function SET_BASE:GetRandom()
|
|
|
|
local RandomItem = self.Set[self.Index[math.random( #self.Index )]]
|
|
self:T3( { RandomItem } )
|
|
return RandomItem
|
|
end
|
|
|
|
--- Retrieves the amount of objects in the @{Core.Set#SET_BASE} and derived classes.
|
|
-- @param #SET_BASE self
|
|
-- @return #number Count
|
|
function SET_BASE:Count()
|
|
|
|
return self.Index and #self.Index or 0
|
|
end
|
|
|
|
--- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set).
|
|
-- @param #SET_BASE self
|
|
-- @param #SET_BASE BaseSet
|
|
-- @return #SET_BASE
|
|
function SET_BASE:SetDatabase( BaseSet )
|
|
|
|
-- Copy the filter criteria of the BaseSet
|
|
local OtherFilter = routines.utils.deepCopy( BaseSet.Filter )
|
|
self.Filter = OtherFilter
|
|
|
|
-- Now base the new Set on the BaseSet
|
|
self.Database = BaseSet:GetSet()
|
|
return self
|
|
end
|
|
|
|
--- Define the SET iterator **"yield interval"** and the **"time interval"**.
|
|
-- @param #SET_BASE self
|
|
-- @param #number YieldInterval Sets the frequency when the iterator loop will yield after the number of objects processed. The default frequency is 10 objects processed.
|
|
-- @param #number TimeInterval Sets the time in seconds when the main logic will resume the iterator loop. The default time is 0.001 seconds.
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:SetIteratorIntervals( YieldInterval, TimeInterval )
|
|
|
|
self.YieldInterval = YieldInterval
|
|
self.TimeInterval = TimeInterval
|
|
|
|
return self
|
|
end
|
|
|
|
--- Define the SET iterator **"limit"**.
|
|
-- @param #SET_BASE self
|
|
-- @param #number Limit Defines how many objects are evaluated of the set as part of the Some iterators. The default is 1.
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:SetSomeIteratorLimit( Limit )
|
|
|
|
self.SomeIteratorLimit = Limit or 1
|
|
|
|
return self
|
|
end
|
|
|
|
--- Get the SET iterator **"limit"**.
|
|
-- @param #SET_BASE self
|
|
-- @return #number Defines how many objects are evaluated of the set as part of the Some iterators.
|
|
function SET_BASE:GetSomeIteratorLimit()
|
|
|
|
return self.SomeIteratorLimit or self:Count()
|
|
end
|
|
|
|
--- Filters for the defined collection.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:FilterOnce()
|
|
|
|
for ObjectName, Object in pairs( self.Database ) do
|
|
|
|
if self:IsIncludeObject( Object ) then
|
|
self:Add( ObjectName, Object )
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering for the defined collection.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:_FilterStart()
|
|
|
|
for ObjectName, Object in pairs( self.Database ) do
|
|
|
|
if self:IsIncludeObject( Object ) then
|
|
self:Add( ObjectName, Object )
|
|
end
|
|
end
|
|
|
|
-- Follow alive players and clients
|
|
-- self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
|
|
-- self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering of the Dead events for the collection.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:FilterDeads() -- R2.1 allow deads to be filtered to automatically handle deads in the collection.
|
|
|
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering of the Crash events for the collection.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:FilterCrashes() -- R2.1 allow crashes to be filtered to automatically handle crashes in the collection.
|
|
|
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Stops the filtering for the defined collection.
|
|
-- @param #SET_BASE self
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:FilterStop()
|
|
|
|
self:UnHandleEvent( EVENTS.Birth )
|
|
self:UnHandleEvent( EVENTS.Dead )
|
|
self:UnHandleEvent( EVENTS.Crash )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set.
|
|
-- @return Core.Base#BASE The closest object.
|
|
function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 )
|
|
self:F2( PointVec2 )
|
|
|
|
local NearestObject = nil
|
|
local ClosestDistance = nil
|
|
|
|
for ObjectID, ObjectData in pairs( self.Set ) do
|
|
if NearestObject == nil then
|
|
NearestObject = ObjectData
|
|
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
|
else
|
|
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
|
if Distance < ClosestDistance then
|
|
NearestObject = ObjectData
|
|
ClosestDistance = Distance
|
|
end
|
|
end
|
|
end
|
|
|
|
return NearestObject
|
|
end
|
|
|
|
----- Private method that registers all alive players in the mission.
|
|
---- @param #SET_BASE self
|
|
---- @return #SET_BASE self
|
|
-- function SET_BASE:_RegisterPlayers()
|
|
--
|
|
-- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
|
-- for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
|
-- for UnitId, UnitData in pairs( CoalitionData ) do
|
|
-- self:T3( { "UnitData:", UnitData } )
|
|
-- if UnitData and UnitData:isExist() then
|
|
-- local UnitName = UnitData:getName()
|
|
-- if not self.PlayersAlive[UnitName] then
|
|
-- self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } )
|
|
-- self.PlayersAlive[UnitName] = UnitData:getPlayerName()
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
--
|
|
-- return self
|
|
-- end
|
|
|
|
--- Events
|
|
|
|
--- Handles the OnBirth event for the Set.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
function SET_BASE:_EventOnBirth( Event )
|
|
self:F3( { Event } )
|
|
|
|
if Event.IniDCSUnit then
|
|
local ObjectName, Object = self:AddInDatabase( Event )
|
|
self:T3( ObjectName, Object )
|
|
if Object and self:IsIncludeObject( Object ) then
|
|
self:Add( ObjectName, Object )
|
|
-- self:_EventOnPlayerEnterUnit( Event )
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Handles the OnDead or OnCrash event for alive units set.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
function SET_BASE:_EventOnDeadOrCrash( Event )
|
|
self:F( { Event } )
|
|
|
|
if Event.IniDCSUnit then
|
|
local ObjectName, Object = self:FindInDatabase( Event )
|
|
if ObjectName then
|
|
self:Remove( ObjectName )
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied).
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- function SET_BASE:_EventOnPlayerEnterUnit( Event )
|
|
-- self:F3( { Event } )
|
|
--
|
|
-- if Event.IniDCSUnit then
|
|
-- local ObjectName, Object = self:AddInDatabase( Event )
|
|
-- self:T3( ObjectName, Object )
|
|
-- if self:IsIncludeObject( Object ) then
|
|
-- self:Add( ObjectName, Object )
|
|
-- --self:_EventOnPlayerEnterUnit( Event )
|
|
-- end
|
|
-- end
|
|
-- end
|
|
|
|
--- Handles the OnPlayerLeaveUnit event to clean the active players table.
|
|
-- @param #SET_BASE self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- function SET_BASE:_EventOnPlayerLeaveUnit( Event )
|
|
-- self:F3( { Event } )
|
|
--
|
|
-- local ObjectName = Event.IniDCSUnit
|
|
-- if Event.IniDCSUnit then
|
|
-- if Event.IniDCSGroup then
|
|
-- local GroupUnits = Event.IniDCSGroup:getUnits()
|
|
-- local PlayerCount = 0
|
|
-- for _, DCSUnit in pairs( GroupUnits ) do
|
|
-- if DCSUnit ~= Event.IniDCSUnit then
|
|
-- if DCSUnit:getPlayerName() ~= nil then
|
|
-- PlayerCount = PlayerCount + 1
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- self:E(PlayerCount)
|
|
-- if PlayerCount == 0 then
|
|
-- self:Remove( Event.IniDCSGroupName )
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
|
|
-- Iterators
|
|
|
|
--- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters.
|
|
-- @param #SET_BASE self
|
|
-- @param #function IteratorFunction The function that will be called.
|
|
-- @param #table arg Arguments of the IteratorFunction.
|
|
-- @param #SET_BASE Set (Optional) The set to use. Default self:GetSet().
|
|
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
|
|
-- @param #table FunctionArguments (Optional) Function arguments.
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments )
|
|
self:F3( arg )
|
|
|
|
Set = Set or self:GetSet()
|
|
arg = arg or {}
|
|
|
|
local function CoRoutine()
|
|
local Count = 0
|
|
for ObjectID, ObjectData in pairs( Set ) do
|
|
local Object = ObjectData
|
|
self:T3( Object )
|
|
if Function then
|
|
if Function( unpack( FunctionArguments or {} ), Object ) == true then
|
|
IteratorFunction( Object, unpack( arg ) )
|
|
end
|
|
else
|
|
IteratorFunction( Object, unpack( arg ) )
|
|
end
|
|
Count = Count + 1
|
|
-- if Count % self.YieldInterval == 0 then
|
|
-- coroutine.yield( false )
|
|
-- end
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- local co = coroutine.create( CoRoutine )
|
|
local co = CoRoutine
|
|
|
|
local function Schedule()
|
|
|
|
-- local status, res = coroutine.resume( co )
|
|
local status, res = co()
|
|
self:T3( { status, res } )
|
|
|
|
if status == false then
|
|
error( res )
|
|
end
|
|
if res == false then
|
|
return true -- resume next time the loop
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 )
|
|
Schedule()
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters.
|
|
-- @param #SET_BASE self
|
|
-- @param #function IteratorFunction The function that will be called.
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:ForSome( IteratorFunction, arg, Set, Function, FunctionArguments )
|
|
self:F3( arg )
|
|
|
|
Set = Set or self:GetSet()
|
|
arg = arg or {}
|
|
|
|
local Limit = self:GetSomeIteratorLimit()
|
|
|
|
local function CoRoutine()
|
|
local Count = 0
|
|
for ObjectID, ObjectData in pairs( Set ) do
|
|
local Object = ObjectData
|
|
self:T3( Object )
|
|
if Function then
|
|
if Function( unpack( FunctionArguments ), Object ) == true then
|
|
IteratorFunction( Object, unpack( arg ) )
|
|
end
|
|
else
|
|
IteratorFunction( Object, unpack( arg ) )
|
|
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
|
|
|
|
local function Schedule()
|
|
|
|
-- local status, res = coroutine.resume( co )
|
|
local status, res = co()
|
|
self:T3( { status, res } )
|
|
|
|
if status == false then
|
|
error( res )
|
|
end
|
|
if res == false then
|
|
return true -- resume next time the loop
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- 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.
|
|
---- @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, ... )
|
|
-- self:F3( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive )
|
|
--
|
|
-- return self
|
|
-- end
|
|
--
|
|
----- Iterate the SET_BASE and call an iterator 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, ... )
|
|
-- self:F3( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.PlayersAlive )
|
|
--
|
|
-- return self
|
|
-- end
|
|
--
|
|
--
|
|
----- Iterate the SET_BASE and call an iterator 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, ... )
|
|
-- self:F3( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.Clients )
|
|
--
|
|
-- return self
|
|
-- end
|
|
|
|
--- Decides whether to include the Object.
|
|
-- @param #SET_BASE self
|
|
-- @param #table Object
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:IsIncludeObject( Object )
|
|
self:F3( Object )
|
|
|
|
return true
|
|
end
|
|
|
|
--- Decides whether an object is in the SET
|
|
-- @param #SET_BASE self
|
|
-- @param #table Object
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:IsInSet( Object )
|
|
self:F3( Object )
|
|
local outcome = false
|
|
local name = Object:GetName()
|
|
self:ForEach(
|
|
function(object)
|
|
if object:GetName() == name then
|
|
outcome = true
|
|
end
|
|
end
|
|
)
|
|
return outcome
|
|
end
|
|
|
|
--- Decides whether an object is **not** in the SET
|
|
-- @param #SET_BASE self
|
|
-- @param #table Object
|
|
-- @return #SET_BASE self
|
|
function SET_BASE:IsNotInSet( Object )
|
|
self:F3( Object )
|
|
return not self:IsInSet(Object)
|
|
end
|
|
|
|
--- Gets a string with all the object names.
|
|
-- @param #SET_BASE self
|
|
-- @return #string A string with the names of the objects.
|
|
function SET_BASE:GetObjectNames()
|
|
self:F3()
|
|
|
|
local ObjectNames = ""
|
|
for ObjectName, Object in pairs( self.Set ) do
|
|
ObjectNames = ObjectNames .. ObjectName .. ", "
|
|
end
|
|
|
|
return ObjectNames
|
|
end
|
|
|
|
--- 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.
|
|
-- @return #string A string with the names of the objects.
|
|
function SET_BASE:Flush( MasterObject )
|
|
self:F3()
|
|
|
|
local ObjectNames = ""
|
|
for ObjectName, Object in pairs( self.Set ) do
|
|
ObjectNames = ObjectNames .. ObjectName .. ", "
|
|
end
|
|
self:F( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } )
|
|
|
|
return ObjectNames
|
|
end
|
|
|
|
end
|
|
|
|
do -- SET_GROUP
|
|
|
|
--- @type SET_GROUP
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain:
|
|
--
|
|
-- * Coalitions
|
|
-- * Categories
|
|
-- * Countries
|
|
-- * Starting with certain prefix strings.
|
|
--
|
|
-- ## SET_GROUP constructor
|
|
--
|
|
-- Create a new SET_GROUP object with the @{#SET_GROUP.New} method:
|
|
--
|
|
-- * @{#SET_GROUP.New}: Creates a new SET_GROUP object.
|
|
--
|
|
-- ## Add or Remove GROUP(s) from SET_GROUP
|
|
--
|
|
-- GROUPS can be added and removed using the @{Core.Set#SET_GROUP.AddGroupsByName} and @{Core.Set#SET_GROUP.RemoveGroupsByName} respectively.
|
|
-- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP.
|
|
--
|
|
-- ## SET_GROUP filter criteria
|
|
--
|
|
-- You can set filter criteria to define the set of groups within the SET_GROUP.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s).
|
|
-- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies).
|
|
-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the groups belonging to the country(ies).
|
|
-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*.
|
|
-- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP 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_GROUP.FilterCategoryAirplane}: Builds the SET_GROUP from airplanes.
|
|
-- * @{#SET_GROUP.FilterCategoryHelicopter}: Builds the SET_GROUP from helicopters.
|
|
-- * @{#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**.
|
|
--
|
|
-- ## 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.
|
|
-- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_GROUP:
|
|
--
|
|
-- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP.
|
|
-- * @{#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.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly 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.
|
|
--
|
|
--
|
|
-- ## SET_GROUP 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_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 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:
|
|
--
|
|
-- -- Create the SetCarrier SET_GROUP collection.
|
|
--
|
|
-- local SetHelicopter = SET_GROUP: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
|
|
--
|
|
-- 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.
|
|
-- 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:
|
|
--
|
|
-- -- Now we have a constructor of the class AI_CARGO_DISPATCHER, that receives the SetHelicopter as a parameter.
|
|
-- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter.
|
|
-- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER.
|
|
--
|
|
-- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones )
|
|
--
|
|
-- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER
|
|
--
|
|
-- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
|
-- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration.
|
|
--
|
|
-- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, GroupObject )
|
|
-- SetHelicopter:F( { GroupObject = GroupObject:GetName() } )
|
|
-- self.PickupCargo[GroupObject] = nil -- So here I clear the PickupCargo table entry of the self object AI_CARGO_DISPATCHER.
|
|
-- self.CarrierHome[GroupObject] = nil
|
|
-- end
|
|
--
|
|
-- end
|
|
--
|
|
-- ===
|
|
-- @field #SET_GROUP SET_GROUP
|
|
SET_GROUP = {
|
|
ClassName = "SET_GROUP",
|
|
Filter = {
|
|
Coalitions = nil,
|
|
Categories = nil,
|
|
Countries = nil,
|
|
GroupPrefixes = nil,
|
|
Zones = 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, -- R2.2
|
|
ship = Group.Category.SHIP,
|
|
structure = Group.Category.STRUCTURE,
|
|
},
|
|
},
|
|
}
|
|
|
|
--- 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
|
|
-- @usage
|
|
-- -- Define a new SET_GROUP Object. This DBObject will contain a reference to all alive GROUPS.
|
|
-- DBObject = SET_GROUP:New()
|
|
function SET_GROUP:New()
|
|
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) -- #SET_GROUP
|
|
|
|
self:FilterActive( false )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Gets the Set.
|
|
-- @param #SET_GROUP self
|
|
-- @return #table Table of objects
|
|
function SET_GROUP:GetAliveSet()
|
|
self:F2()
|
|
|
|
local AliveSet = SET_GROUP: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 then
|
|
if GroupObject:IsAlive() then
|
|
AliveSet:Add( GroupName, GroupObject )
|
|
end
|
|
end
|
|
end
|
|
|
|
return AliveSet.Set or {}
|
|
end
|
|
|
|
--- Returns a report of of unit types.
|
|
-- @param #SET_GROUP self
|
|
-- @return Core.Report#REPORT A report of the unit types found. The key is the UnitTypeName and the value is the amount of unit types found.
|
|
function SET_GROUP:GetUnitTypeNames()
|
|
self:F2()
|
|
|
|
local MT = {} -- Message Text
|
|
local UnitTypes = {}
|
|
|
|
local ReportUnitTypes = REPORT:New()
|
|
|
|
for GroupID, GroupData in pairs( self:GetSet() ) do
|
|
local Units = GroupData:GetUnits()
|
|
for UnitID, UnitData in pairs( Units ) do
|
|
if UnitData:IsAlive() then
|
|
local UnitType = UnitData:GetTypeName()
|
|
|
|
if not UnitTypes[UnitType] then
|
|
UnitTypes[UnitType] = 1
|
|
else
|
|
UnitTypes[UnitType] = UnitTypes[UnitType] + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for UnitTypeID, UnitType in pairs( UnitTypes ) do
|
|
ReportUnitTypes:Add( UnitType .. " of " .. UnitTypeID )
|
|
end
|
|
|
|
return ReportUnitTypes
|
|
end
|
|
|
|
--- Add a GROUP to SET_GROUP.
|
|
-- Note that for each unit in the group that is set, a default cargo bay limit is initialized.
|
|
-- @param Core.Set#SET_GROUP self
|
|
-- @param Wrapper.Group#GROUP group The group which should be added to the set.
|
|
-- @param #boolean DontSetCargoBayLimit If true, do not attempt to auto-add the cargo bay limit per unit in this group.
|
|
-- @return Core.Set#SET_GROUP self
|
|
function SET_GROUP:AddGroup( group, DontSetCargoBayLimit )
|
|
|
|
self:Add( group:GetName(), group )
|
|
|
|
if not DontSetCargoBayLimit then
|
|
-- I set the default cargo bay weight limit each time a new group is added to the set.
|
|
-- TODO Why is this here in the first place?
|
|
for UnitID, UnitData in pairs( group:GetUnits() ) do
|
|
if UnitData and UnitData:IsAlive() then
|
|
UnitData:SetCargoBayWeightLimit()
|
|
end
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add GROUP(s) to SET_GROUP.
|
|
-- @param Core.Set#SET_GROUP self
|
|
-- @param #string AddGroupNames A single name or an array of GROUP names.
|
|
-- @return Core.Set#SET_GROUP self
|
|
function SET_GROUP: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) from SET_GROUP.
|
|
-- @param Core.Set#SET_GROUP self
|
|
-- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names.
|
|
-- @return Core.Set#SET_GROUP self
|
|
function SET_GROUP: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 a Group based on the Group Name.
|
|
-- @param #SET_GROUP self
|
|
-- @param #string GroupName
|
|
-- @return Wrapper.Group#GROUP The found Group.
|
|
function SET_GROUP:FindGroup( GroupName )
|
|
|
|
local GroupFound = self.Set[GroupName]
|
|
return GroupFound
|
|
end
|
|
|
|
--- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#POINT_VEC2}.
|
|
-- @param #SET_GROUP self
|
|
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set.
|
|
-- @return Wrapper.Group#GROUP The closest group.
|
|
function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 )
|
|
self:F2( PointVec2 )
|
|
|
|
local NearestGroup = nil -- Wrapper.Group#GROUP
|
|
local ClosestDistance = nil
|
|
|
|
local Set = self:GetAliveSet()
|
|
|
|
for ObjectID, ObjectData in pairs( Set ) do
|
|
if NearestGroup == nil then
|
|
NearestGroup = ObjectData
|
|
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
|
else
|
|
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
|
if Distance < ClosestDistance then
|
|
NearestGroup = ObjectData
|
|
ClosestDistance = Distance
|
|
end
|
|
end
|
|
end
|
|
|
|
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 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
|
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCoalitions( Coalitions )
|
|
if not self.Filter.Coalitions then
|
|
self.Filter.Coalitions = {}
|
|
end
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
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, helicopter, ground, ship.
|
|
-- @param #SET_GROUP self
|
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP: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_GROUP self
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCategoryGround()
|
|
self:FilterCategories( "ground" )
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of groups out of airplane category.
|
|
-- @param #SET_GROUP self
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCategoryAirplane()
|
|
self:FilterCategories( "plane" )
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of groups out of helicopter category.
|
|
-- @param #SET_GROUP self
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCategoryHelicopter()
|
|
self:FilterCategories( "helicopter" )
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of groups out of ship category.
|
|
-- @param #SET_GROUP self
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCategoryShip()
|
|
self:FilterCategories( "ship" )
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of groups out of structure category.
|
|
-- @param #SET_GROUP self
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCategoryStructure()
|
|
self:FilterCategories( "structure" )
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of groups of defined countries.
|
|
-- Possible current countries are those known within DCS world.
|
|
-- @param #SET_GROUP self
|
|
-- @param #string Countries Can take those country strings known within DCS world.
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:FilterCountries( Countries )
|
|
if not self.Filter.Countries then
|
|
self.Filter.Countries = {}
|
|
end
|
|
if type( Countries ) ~= "table" then
|
|
Countries = { Countries }
|
|
end
|
|
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_GROUP 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_GROUP self
|
|
function SET_GROUP:FilterPrefixes( Prefixes )
|
|
if not self.Filter.GroupPrefixes then
|
|
self.Filter.GroupPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
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_GROUP self
|
|
-- @param #boolean Active (Optional) Include only active groups to the set.
|
|
-- Include inactive groups if you provide false.
|
|
-- @return #SET_GROUP self
|
|
-- @usage
|
|
--
|
|
-- -- Include only active groups to the set.
|
|
-- GroupSet = SET_GROUP:New():FilterActive():FilterStart()
|
|
--
|
|
-- -- Include only active groups to the set of the blue coalition, and filter one time.
|
|
-- GroupSet = SET_GROUP: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_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
|
|
-- ... logic ...
|
|
-- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce()
|
|
--
|
|
function SET_GROUP:FilterActive( Active )
|
|
Active = Active or not (Active == false)
|
|
self.Filter.Active = Active
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_GROUP self
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP: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
|
|
|
|
--- Handles the OnDead or OnCrash event for alive groups set.
|
|
-- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP.
|
|
-- @param #SET_GROUP self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
function SET_GROUP:_EventOnDeadOrCrash( Event )
|
|
self:F( { Event } )
|
|
|
|
if Event.IniDCSUnit then
|
|
local ObjectName, Object = self:FindInDatabase( Event )
|
|
if ObjectName then
|
|
local size = 1
|
|
if Event.IniDCSGroup then
|
|
size = Event.IniDCSGroup:getSize()
|
|
end
|
|
if size == 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_GROUP self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the GROUP
|
|
-- @return #table The GROUP
|
|
function SET_GROUP:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
if Event.IniObjectCategory == 1 then
|
|
if not self.Database[Event.IniDCSGroupName] then
|
|
self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName )
|
|
self:T3( self.Database[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_GROUP self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the GROUP
|
|
-- @return #table The GROUP
|
|
function SET_GROUP:FindInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName]
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and call an iterator function for each GROUP object, providing the GROUP and optional parameters.
|
|
-- @param #SET_GROUP self
|
|
-- @param #function IteratorFunction The function that will be called for all GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:ForEachGroup( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and call an iterator function for some GROUP objects, providing the GROUP and optional parameters.
|
|
-- @param #SET_GROUP self
|
|
-- @param #function IteratorFunction The function that will be called for some GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:ForSomeGroup( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForSome( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP object, providing the GROUP and optional parameters.
|
|
-- @param #SET_GROUP self
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:ForEachGroupAlive( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetAliveSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and call an iterator function for some **alive** GROUP objects, providing the GROUP and optional parameters.
|
|
-- @param #SET_GROUP self
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:ForSomeGroupAlive( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForSome( IteratorFunction, arg, self:GetAliveSet() )
|
|
|
|
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.
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly 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.
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- 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.
|
|
-- @param #SET_GROUP self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- 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
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- MySetGroup:AddGroupsByName({"Group1", "Group2"})
|
|
--
|
|
-- if MySetGroup:AllCompletelyInZone(MyZone) then
|
|
-- MESSAGE:New("All the SET's GROUP are in zone !", 10):ToAll()
|
|
-- else
|
|
-- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll()
|
|
-- end
|
|
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
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and call an iterator function for each alive GROUP that has any unit in the @{Core.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.
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
|
-- @return #SET_GROUP self
|
|
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 } )
|
|
|
|
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.
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- MySetGroup:AddGroupsByName({"Group1", "Group2"})
|
|
--
|
|
-- if MySetGroup:AnyCompletelyInZone(MyZone) then
|
|
-- MESSAGE:New("At least one GROUP is completely in zone !", 10):ToAll()
|
|
-- else
|
|
-- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll()
|
|
-- end
|
|
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
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- 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.
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- MySetGroup:AddGroupsByName({"Group1", "Group2"})
|
|
--
|
|
-- if MySetGroup:AnyPartlyInZone(MyZone) then
|
|
-- MESSAGE:New("At least one GROUP has at least one UNIT in zone !", 10):ToAll()
|
|
-- else
|
|
-- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll()
|
|
-- end
|
|
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
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and return true if at least one @{GROUP} of the @{SET_GROUP} is partly in @{ZONE}.
|
|
-- 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.
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- MySetGroup:AddGroupsByName({"Group1", "Group2"})
|
|
--
|
|
-- if MySetGroup:AnyPartlyInZone(MyZone) then
|
|
-- MESSAGE:New("At least one GROUP is partially in the zone, but none are fully in it !", 10):ToAll()
|
|
-- 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 )
|
|
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
|
|
return false
|
|
elseif GroupData:IsPartlyInZone( Zone ) then
|
|
IsPartlyInZone = true -- at least one GROUP is partly in zone
|
|
end
|
|
end
|
|
|
|
if IsPartlyInZone then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and return true if no @{GROUP} of the @{SET_GROUP} is in @{ZONE}
|
|
-- This could also be achieved with `not SET_GROUP:AnyPartlyInZone(Zone)`, but it's easier for the
|
|
-- mission designer to add a dedicated method
|
|
-- @param #SET_GROUP self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @return #boolean true if no @{Wrapper.Group#GROUP} is inside the @{Core.Zone#ZONE} in any way, false otherwise.
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- MySetGroup:AddGroupsByName({"Group1", "Group2"})
|
|
--
|
|
-- if MySetGroup:NoneInZone(MyZone) then
|
|
-- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll()
|
|
-- else
|
|
-- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll()
|
|
-- end
|
|
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
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and count how many GROUPs are completely in the Zone
|
|
-- That could easily be done with SET_GROUP:ForEachGroupCompletelyInZone(), but this function
|
|
-- provides an easy to use shortcut...
|
|
-- @param #SET_GROUP self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @return #number the number of GROUPs completely in the Zone
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- 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 )
|
|
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
|
|
Count = Count + 1
|
|
end
|
|
end
|
|
return Count
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and count how many UNITs are completely in the Zone
|
|
-- @param #SET_GROUP self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @return #number the number of GROUPs completely in the Zone
|
|
-- @usage
|
|
-- local MyZone = ZONE:New("Zone1")
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- 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 )
|
|
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 )
|
|
end
|
|
return Count
|
|
end
|
|
|
|
--- Iterate the SET_GROUP and count how many GROUPs and UNITs are alive.
|
|
-- @param #SET_GROUP self
|
|
-- @return #number The number of GROUPs alive.
|
|
-- @return #number The number of UNITs alive.
|
|
function SET_GROUP:CountAlive()
|
|
local CountG = 0
|
|
local CountU = 0
|
|
|
|
local Set = self:GetSet()
|
|
|
|
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
|
|
if unit and unit:IsAlive() then
|
|
CountU = CountU + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
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.
|
|
---- @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, ... )
|
|
-- self:F2( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.PlayersAlive )
|
|
--
|
|
-- return self
|
|
-- end
|
|
--
|
|
--
|
|
----- Iterate the SET_GROUP and call an iterator 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, ... )
|
|
-- self:F2( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.Clients )
|
|
--
|
|
-- return self
|
|
-- end
|
|
|
|
---
|
|
-- @param #SET_GROUP self
|
|
-- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion.
|
|
-- @return #SET_GROUP self
|
|
function SET_GROUP:IsIncludeObject( MGroup )
|
|
self:F2( MGroup )
|
|
local MGroupInclude = true
|
|
|
|
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
|
|
MGroupActive = true
|
|
end
|
|
MGroupInclude = MGroupInclude and MGroupActive
|
|
end
|
|
|
|
if self.Filter.Coalitions then
|
|
local MGroupCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MGroup:GetCoalition() then
|
|
MGroupCoalition = true
|
|
end
|
|
end
|
|
MGroupInclude = MGroupInclude and MGroupCoalition
|
|
end
|
|
|
|
if self.Filter.Categories then
|
|
local MGroupCategory = false
|
|
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
|
self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } )
|
|
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MGroup:GetCategory() then
|
|
MGroupCategory = true
|
|
end
|
|
end
|
|
MGroupInclude = MGroupInclude and MGroupCategory
|
|
end
|
|
|
|
if self.Filter.Countries then
|
|
local MGroupCountry = false
|
|
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
|
self:T3( { "Country:", MGroup:GetCountry(), CountryName } )
|
|
if country.id[CountryName] == MGroup:GetCountry() then
|
|
MGroupCountry = true
|
|
end
|
|
end
|
|
MGroupInclude = MGroupInclude and MGroupCountry
|
|
end
|
|
|
|
if self.Filter.GroupPrefixes then
|
|
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
|
|
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
|
|
-- @usage
|
|
-- -- Set the default cargo bay weight limits of the carrier units.
|
|
-- local MySetGroup = SET_GROUP:New()
|
|
-- MySetGroup:SetCargoBayWeightLimit()
|
|
function SET_GROUP:SetCargoBayWeightLimit()
|
|
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
|
|
UnitData:SetCargoBayWeightLimit()
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
do -- SET_UNIT
|
|
|
|
--- @type SET_UNIT
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the SET_UNIT class to build sets of units belonging to certain:
|
|
--
|
|
-- * Coalitions
|
|
-- * Categories
|
|
-- * Countries
|
|
-- * Unit types
|
|
-- * Starting with certain prefix strings.
|
|
--
|
|
-- ## 1) SET_UNIT constructor
|
|
--
|
|
-- Create a new SET_UNIT object with the @{#SET_UNIT.New} method:
|
|
--
|
|
-- * @{#SET_UNIT.New}: Creates a new SET_UNIT object.
|
|
--
|
|
-- ## 2) Add or Remove UNIT(s) from SET_UNIT
|
|
--
|
|
-- UNITs can be added and removed using the @{Core.Set#SET_UNIT.AddUnitsByName} and @{Core.Set#SET_UNIT.RemoveUnitsByName} respectively.
|
|
-- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT.
|
|
--
|
|
-- ## 3) SET_UNIT filter criteria
|
|
--
|
|
-- You can set filter criteria to define the set of units within the SET_UNIT.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s).
|
|
-- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies).
|
|
-- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s).
|
|
-- * @{#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**.
|
|
--
|
|
-- ## 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.
|
|
-- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_UNIT:
|
|
--
|
|
-- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT.
|
|
-- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Zone}, providing the UNIT object and optional parameters to the called function.
|
|
-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Zone}, providing the UNIT object and optional parameters to the called function.
|
|
--
|
|
-- Planned iterators methods in development are (so these are not yet available):
|
|
--
|
|
-- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT.
|
|
-- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function.
|
|
-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function.
|
|
--
|
|
-- ## 5) SET_UNIT atomic methods
|
|
--
|
|
-- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT:
|
|
--
|
|
-- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Wrapper.Unit}s in the SET, delimited by a comma.
|
|
--
|
|
-- ## 6) SET_UNIT trigger events on the UNIT objects.
|
|
--
|
|
-- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the UNIT objects in the 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 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:
|
|
--
|
|
-- -- Create the SetCarrier SET_UNIT collection.
|
|
--
|
|
-- local SetHelicopter = SET_UNIT:New():FilterPrefixes( "Helicopter" ):FilterStart()
|
|
--
|
|
-- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier unit is destroyed, that all internal parameters are reset.
|
|
--
|
|
-- function SetHelicopter:OnAfterDead( From, Event, To, UnitObject )
|
|
-- self:F( { UnitObject = UnitObject:GetName() } )
|
|
-- 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.
|
|
-- 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:
|
|
--
|
|
-- -- Now we have a constructor of the class AI_CARGO_DISPATCHER, that receives the SetHelicopter as a parameter.
|
|
-- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter.
|
|
-- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER.
|
|
--
|
|
-- function ACLASS:New( SetCarrier, SetCargo, SetDeployZones )
|
|
--
|
|
-- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER
|
|
--
|
|
-- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
|
-- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration.
|
|
--
|
|
-- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, UnitObject )
|
|
-- SetHelicopter:F( { UnitObject = UnitObject:GetName() } )
|
|
-- self.array[UnitObject] = nil -- So here I clear the array table entry of the self object ACLASS.
|
|
-- end
|
|
--
|
|
-- end
|
|
-- ===
|
|
-- @field #SET_UNIT SET_UNIT
|
|
SET_UNIT = {
|
|
ClassName = "SET_UNIT",
|
|
Units = {},
|
|
Filter = {
|
|
Coalitions = nil,
|
|
Categories = nil,
|
|
Types = nil,
|
|
Countries = nil,
|
|
UnitPrefixes = nil,
|
|
Zones = nil,
|
|
},
|
|
FilterMeta = {
|
|
Coalitions = {
|
|
red = coalition.side.RED,
|
|
blue = coalition.side.BLUE,
|
|
neutral = coalition.side.NEUTRAL,
|
|
},
|
|
Categories = {
|
|
plane = Unit.Category.AIRPLANE,
|
|
helicopter = Unit.Category.HELICOPTER,
|
|
ground = Unit.Category.GROUND_UNIT,
|
|
ship = Unit.Category.SHIP,
|
|
structure = Unit.Category.STRUCTURE,
|
|
},
|
|
},
|
|
}
|
|
|
|
--- Get the first unit from the set.
|
|
-- @function [parent=#SET_UNIT] GetFirst
|
|
-- @param #SET_UNIT self
|
|
-- @return Wrapper.Unit#UNIT The UNIT object.
|
|
|
|
--- Creates a new SET_UNIT object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
|
|
-- @param #SET_UNIT self
|
|
-- @return #SET_UNIT
|
|
-- @usage
|
|
-- -- Define a new SET_UNIT Object. This DBObject will contain a reference to all alive Units.
|
|
-- DBObject = SET_UNIT:New()
|
|
function SET_UNIT:New()
|
|
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- #SET_UNIT
|
|
|
|
self:FilterActive( false )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add UNIT(s) to SET_UNIT.
|
|
-- @param #SET_UNIT self
|
|
-- @param Wrapper.Unit#UNIT Unit A single UNIT.
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:AddUnit( Unit )
|
|
self:F2( Unit:GetName() )
|
|
|
|
self:Add( Unit:GetName(), Unit )
|
|
|
|
-- Set the default cargo bay limit each time a new unit is added to the set.
|
|
Unit:SetCargoBayWeightLimit()
|
|
|
|
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 }
|
|
|
|
self:T( AddUnitNamesArray )
|
|
for AddUnitID, AddUnitName in pairs( AddUnitNamesArray ) do
|
|
self:Add( AddUnitName, UNIT:FindByName( AddUnitName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove UNIT(s) from SET_UNIT.
|
|
-- @param Core.Set#SET_UNIT self
|
|
-- @param #table RemoveUnitNames A single name or an array of UNIT names.
|
|
-- @return Core.Set#SET_UNIT self
|
|
function SET_UNIT:RemoveUnitsByName( RemoveUnitNames )
|
|
|
|
local RemoveUnitNamesArray = (type( RemoveUnitNames ) == "table") and RemoveUnitNames or { RemoveUnitNames }
|
|
|
|
for RemoveUnitID, RemoveUnitName in pairs( RemoveUnitNamesArray ) do
|
|
self:Remove( RemoveUnitName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Unit based on the Unit Name.
|
|
-- @param #SET_UNIT self
|
|
-- @param #string UnitName
|
|
-- @return Wrapper.Unit#UNIT The found Unit.
|
|
function SET_UNIT:FindUnit( UnitName )
|
|
|
|
local UnitFound = self.Set[UnitName]
|
|
return UnitFound
|
|
end
|
|
|
|
--- Builds a set of units of coalitions.
|
|
-- Possible current coalitions are red, blue and neutral.
|
|
-- @param #SET_UNIT self
|
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:FilterCoalitions( Coalitions )
|
|
|
|
self.Filter.Coalitions = {}
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
|
self.Filter.Coalitions[Coalition] = Coalition
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of units out of categories.
|
|
-- Possible current categories are plane, helicopter, ground, ship.
|
|
-- @param #SET_UNIT self
|
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT: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 units of defined unit types.
|
|
-- Possible current types are those types known within DCS world.
|
|
-- @param #SET_UNIT self
|
|
-- @param #string Types Can take those type strings known within DCS world.
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:FilterTypes( Types )
|
|
if not self.Filter.Types then
|
|
self.Filter.Types = {}
|
|
end
|
|
if type( Types ) ~= "table" then
|
|
Types = { Types }
|
|
end
|
|
for TypeID, Type in pairs( Types ) do
|
|
self.Filter.Types[Type] = Type
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of units of defined countries.
|
|
-- Possible current countries are those known within DCS world.
|
|
-- @param #SET_UNIT self
|
|
-- @param #string Countries Can take those country strings known within DCS world.
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:FilterCountries( Countries )
|
|
if not self.Filter.Countries then
|
|
self.Filter.Countries = {}
|
|
end
|
|
if type( Countries ) ~= "table" then
|
|
Countries = { Countries }
|
|
end
|
|
for CountryID, Country in pairs( Countries ) do
|
|
self.Filter.Countries[Country] = Country
|
|
end
|
|
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
|
|
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:FilterPrefixes( Prefixes )
|
|
if not self.Filter.UnitPrefixes then
|
|
self.Filter.UnitPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.UnitPrefixes[Prefix] = Prefix
|
|
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.
|
|
-- Include inactive units if you provide false.
|
|
-- @return #SET_UNIT self
|
|
-- @usage
|
|
--
|
|
-- -- Include only active units to the set.
|
|
-- UnitSet = SET_UNIT:New():FilterActive():FilterStart()
|
|
--
|
|
-- -- Include only active units to the set of the blue coalition, and filter one time.
|
|
-- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
|
|
--
|
|
-- -- Include only active units to the set of the blue coalition, and filter one time.
|
|
-- -- Later, reset to include back inactive units to the set.
|
|
-- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
|
|
-- ... logic ...
|
|
-- UnitSet = SET_UNIT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce()
|
|
--
|
|
function SET_UNIT:FilterActive( Active )
|
|
Active = Active or not (Active == false)
|
|
self.Filter.Active = Active
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of units having a radar of give types.
|
|
-- All the units having a radar of a given type will be included within the set.
|
|
-- @param #SET_UNIT self
|
|
-- @param #table RadarTypes The radar types.
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:FilterHasRadar( RadarTypes )
|
|
|
|
self.Filter.RadarTypes = self.Filter.RadarTypes or {}
|
|
if type( RadarTypes ) ~= "table" then
|
|
RadarTypes = { RadarTypes }
|
|
end
|
|
for RadarTypeID, RadarType in pairs( RadarTypes ) do
|
|
self.Filter.RadarTypes[RadarType] = RadarType
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of SEADable units.
|
|
-- @param #SET_UNIT self
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:FilterHasSEAD()
|
|
|
|
self.Filter.SEAD = true
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_UNIT and count how many UNITs are alive.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The number of UNITs alive.
|
|
function SET_UNIT:CountAlive()
|
|
|
|
local Set = self:GetSet()
|
|
|
|
local CountU = 0
|
|
for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
|
if UnitData and UnitData:IsAlive() then
|
|
CountU = CountU + 1
|
|
end
|
|
|
|
end
|
|
|
|
return CountU
|
|
end
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_UNIT self
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT: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
|
|
|
|
--- 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
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the UNIT
|
|
-- @return #table The UNIT
|
|
function SET_UNIT:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
if Event.IniObjectCategory == 1 then
|
|
if not self.Database[Event.IniDCSUnitName] then
|
|
self.Database[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnitName )
|
|
self:T3( self.Database[Event.IniDCSUnitName] )
|
|
end
|
|
end
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_UNIT self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the UNIT
|
|
-- @return #table The 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.
|
|
-- @param #SET_UNIT self
|
|
-- @param Core.Zone#ZONE ZoneTest The Zone to be tested for.
|
|
-- @return #boolean
|
|
function SET_UNIT:IsPartiallyInZone( ZoneTest )
|
|
|
|
local IsPartiallyInZone = false
|
|
|
|
local function EvaluateZone( ZoneUnit )
|
|
|
|
local ZoneUnitName = ZoneUnit:GetName()
|
|
self:F( { ZoneUnitName = ZoneUnitName } )
|
|
if self:FindUnit( ZoneUnitName ) then
|
|
IsPartiallyInZone = true
|
|
self:F( { Found = true } )
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
ZoneTest:SearchZone( EvaluateZone )
|
|
|
|
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.
|
|
-- @return #boolean
|
|
function SET_UNIT:IsNotInZone( Zone )
|
|
|
|
local IsNotInZone = true
|
|
|
|
local function EvaluateZone( ZoneUnit )
|
|
|
|
local ZoneUnitName = ZoneUnit:GetName()
|
|
if self:FindUnit( ZoneUnitName ) then
|
|
IsNotInZone = false
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
Zone:SearchZone( EvaluateZone )
|
|
|
|
return IsNotInZone
|
|
end
|
|
|
|
end
|
|
|
|
|
|
--- Iterate the SET_UNIT and call an iterator 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
|
|
function SET_UNIT:ForEachUnit( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
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
|
|
function SET_UNIT:GetSetPerThreatLevel( FromThreatLevel, ToThreatLevel )
|
|
self:F2( arg )
|
|
|
|
local ThreatLevelSet = {}
|
|
|
|
if self:Count() ~= 0 then
|
|
for UnitName, UnitObject in pairs( self.Set ) do
|
|
local Unit = UnitObject -- Wrapper.Unit#UNIT
|
|
|
|
local ThreatLevel = Unit:GetThreatLevel()
|
|
ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {}
|
|
ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {}
|
|
ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject
|
|
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]
|
|
if ThreatLevelItem then
|
|
for UnitName, UnitObject in pairs( ThreatLevelItem.Set ) do
|
|
table.insert( OrderedPerThreatLevelSet, UnitObject )
|
|
end
|
|
end
|
|
end
|
|
|
|
return OrderedPerThreatLevelSet
|
|
end
|
|
|
|
end
|
|
|
|
|
|
--- Iterate the SET_UNIT **sorted *per Threat Level** and call an iterator 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).
|
|
-- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10).
|
|
-- @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
|
|
-- @usage
|
|
--
|
|
-- UnitSet:ForEachUnitPerThreatLevel( 10, 0,
|
|
-- -- @param Wrapper.Unit#UNIT UnitObject The UNIT object in the UnitSet, that will be passed to the local function for evaluation.
|
|
-- function( UnitObject )
|
|
-- .. logic ..
|
|
-- end
|
|
-- )
|
|
--
|
|
function SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) -- R2.1 Threat Level implementation
|
|
self:F2( arg )
|
|
|
|
local ThreatLevelSet = {}
|
|
|
|
if self:Count() ~= 0 then
|
|
for UnitName, UnitObject in pairs( self.Set ) do
|
|
local Unit = UnitObject -- Wrapper.Unit#UNIT
|
|
|
|
local ThreatLevel = Unit:GetThreatLevel()
|
|
ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {}
|
|
ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {}
|
|
ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject
|
|
self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } )
|
|
end
|
|
|
|
local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1
|
|
|
|
for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do
|
|
self:F( { ThreatLevel = ThreatLevel } )
|
|
local ThreatLevelItem = ThreatLevelSet[ThreatLevel]
|
|
if ThreatLevelItem then
|
|
self:ForEach( IteratorFunction, arg, ThreatLevelItem.Set )
|
|
end
|
|
end
|
|
end
|
|
|
|
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.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence not 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.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Returns map of unit types.
|
|
-- @param #SET_UNIT self
|
|
-- @return #map<#string,#number> A map of the unit types found. The key is the UnitTypeName and the value is the amount of unit types found.
|
|
function SET_UNIT:GetUnitTypes()
|
|
self:F2()
|
|
|
|
local MT = {} -- Message Text
|
|
local UnitTypes = {}
|
|
|
|
for UnitID, UnitData in pairs( self:GetSet() ) do
|
|
local TextUnit = UnitData -- Wrapper.Unit#UNIT
|
|
if TextUnit:IsAlive() then
|
|
local UnitType = TextUnit:GetTypeName()
|
|
|
|
if not UnitTypes[UnitType] then
|
|
UnitTypes[UnitType] = 1
|
|
else
|
|
UnitTypes[UnitType] = UnitTypes[UnitType] + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
for UnitTypeID, UnitType in pairs( UnitTypes ) do
|
|
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
|
|
function SET_UNIT:GetUnitTypesText()
|
|
self:F2()
|
|
|
|
local MT = {} -- Message Text
|
|
local UnitTypes = self:GetUnitTypes()
|
|
|
|
for UnitTypeID, UnitType in pairs( UnitTypes ) do
|
|
MT[#MT + 1] = UnitType .. " of " .. UnitTypeID
|
|
end
|
|
|
|
return table.concat( MT, ", " )
|
|
end
|
|
|
|
--- Returns map of unit threat levels.
|
|
-- @param #SET_UNIT self
|
|
-- @return #table.
|
|
function SET_UNIT:GetUnitThreatLevels()
|
|
self:F2()
|
|
|
|
local UnitThreatLevels = {}
|
|
|
|
for UnitID, UnitData in pairs( self:GetSet() ) do
|
|
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
|
|
if ThreatUnit:IsAlive() then
|
|
local UnitThreatLevel, UnitThreatLevelText = ThreatUnit:GetThreatLevel()
|
|
local ThreatUnitName = ThreatUnit:GetName()
|
|
|
|
UnitThreatLevels[UnitThreatLevel] = UnitThreatLevels[UnitThreatLevel] or {}
|
|
UnitThreatLevels[UnitThreatLevel].UnitThreatLevelText = UnitThreatLevelText
|
|
UnitThreatLevels[UnitThreatLevel].Units = UnitThreatLevels[UnitThreatLevel].Units or {}
|
|
UnitThreatLevels[UnitThreatLevel].Units[ThreatUnitName] = ThreatUnit
|
|
end
|
|
end
|
|
|
|
return UnitThreatLevels
|
|
end
|
|
|
|
--- Calculate the maximum A2G threat level of the SET_UNIT.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The maximum threat level
|
|
function SET_UNIT:CalculateThreatLevelA2G()
|
|
|
|
local MaxThreatLevelA2G = 0
|
|
local MaxThreatText = ""
|
|
for UnitName, UnitData in pairs( self:GetSet() ) do
|
|
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
|
|
local ThreatLevelA2G, ThreatText = ThreatUnit:GetThreatLevel()
|
|
if ThreatLevelA2G > MaxThreatLevelA2G then
|
|
MaxThreatLevelA2G = ThreatLevelA2G
|
|
MaxThreatText = ThreatText
|
|
end
|
|
end
|
|
|
|
self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } )
|
|
return MaxThreatLevelA2G, MaxThreatText
|
|
|
|
end
|
|
|
|
--- Get the center coordinate of the SET_UNIT.
|
|
-- @param #SET_UNIT self
|
|
-- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units.
|
|
function SET_UNIT:GetCoordinate()
|
|
|
|
local Coordinate = self:GetRandom():GetCoordinate()
|
|
--self:F({Coordinate:GetVec3()})
|
|
|
|
|
|
local x1 = Coordinate.x
|
|
local x2 = Coordinate.x
|
|
local y1 = Coordinate.y
|
|
local y2 = Coordinate.y
|
|
local z1 = Coordinate.z
|
|
local z2 = Coordinate.z
|
|
local MaxVelocity = 0
|
|
local AvgHeading = nil
|
|
local MovingCount = 0
|
|
|
|
for UnitName, UnitData in pairs( self:GetSet() ) do
|
|
|
|
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
|
|
|
|
local Velocity = Coordinate:GetVelocity()
|
|
if Velocity ~= 0 then
|
|
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
|
|
local Heading = Coordinate:GetHeading()
|
|
AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading
|
|
MovingCount = MovingCount + 1
|
|
end
|
|
end
|
|
|
|
AvgHeading = AvgHeading and (AvgHeading / MovingCount)
|
|
|
|
Coordinate.x = (x2 - x1) / 2 + x1
|
|
Coordinate.y = (y2 - y1) / 2 + y1
|
|
Coordinate.z = (z2 - z1) / 2 + z1
|
|
Coordinate:SetHeading( AvgHeading )
|
|
Coordinate:SetVelocity( MaxVelocity )
|
|
|
|
self:F( { Coordinate = Coordinate } )
|
|
return Coordinate
|
|
|
|
end
|
|
|
|
--- Get the maximum velocity of the SET_UNIT.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The speed in mps in case of moving units.
|
|
function SET_UNIT:GetVelocity()
|
|
|
|
local Coordinate = self:GetFirst():GetCoordinate()
|
|
|
|
local MaxVelocity = 0
|
|
|
|
for UnitName, UnitData in pairs( self:GetSet() ) do
|
|
|
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
|
local Coordinate = Unit:GetCoordinate()
|
|
|
|
local Velocity = Coordinate:GetVelocity()
|
|
if Velocity ~= 0 then
|
|
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
|
|
end
|
|
end
|
|
|
|
self:F( { MaxVelocity = MaxVelocity } )
|
|
return MaxVelocity
|
|
|
|
end
|
|
|
|
--- Get the average heading of the SET_UNIT.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number Heading Heading in degrees and speed in mps in case of moving units.
|
|
function SET_UNIT:GetHeading()
|
|
|
|
local HeadingSet = nil
|
|
local MovingCount = 0
|
|
|
|
for UnitName, UnitData in pairs( self:GetSet() ) do
|
|
|
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
|
local Coordinate = Unit:GetCoordinate()
|
|
|
|
local Velocity = Coordinate:GetVelocity()
|
|
if Velocity ~= 0 then
|
|
local Heading = Coordinate:GetHeading()
|
|
if HeadingSet == nil then
|
|
HeadingSet = Heading
|
|
else
|
|
local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180
|
|
HeadingDiff = math.abs( HeadingDiff )
|
|
if HeadingDiff > 5 then
|
|
HeadingSet = nil
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return HeadingSet
|
|
|
|
end
|
|
|
|
--- Returns if the @{Set} has targets having a radar (of a given type).
|
|
-- @param #SET_UNIT self
|
|
-- @param DCS#Unit.RadarType RadarType
|
|
-- @return #number The amount of radars in the Set with the given type
|
|
function SET_UNIT:HasRadar( RadarType )
|
|
self:F2( RadarType )
|
|
|
|
local RadarCount = 0
|
|
for UnitID, UnitData in pairs( self:GetSet() ) do
|
|
local UnitSensorTest = UnitData -- Wrapper.Unit#UNIT
|
|
local HasSensors
|
|
if RadarType then
|
|
HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR, RadarType )
|
|
else
|
|
HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR )
|
|
end
|
|
self:T3( HasSensors )
|
|
if HasSensors then
|
|
RadarCount = RadarCount + 1
|
|
end
|
|
end
|
|
|
|
return RadarCount
|
|
end
|
|
|
|
--- Returns if the @{Set} has targets that can be SEADed.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The amount of SEADable units in the Set
|
|
function SET_UNIT:HasSEAD()
|
|
self:F2()
|
|
|
|
local SEADCount = 0
|
|
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 )
|
|
if HasSEAD then
|
|
SEADCount = SEADCount + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
return SEADCount
|
|
end
|
|
|
|
--- Returns if the @{Set} has ground targets.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The amount of ground targets in the Set.
|
|
function SET_UNIT:HasGroundUnits()
|
|
self:F2()
|
|
|
|
local GroundUnitCount = 0
|
|
for UnitID, UnitData in pairs( self:GetSet() ) do
|
|
local UnitTest = UnitData -- Wrapper.Unit#UNIT
|
|
if UnitTest:IsGround() then
|
|
GroundUnitCount = GroundUnitCount + 1
|
|
end
|
|
end
|
|
|
|
return GroundUnitCount
|
|
end
|
|
|
|
--- Returns if the @{Set} has air targets.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The amount of air targets in the Set.
|
|
function SET_UNIT:HasAirUnits()
|
|
self:F2()
|
|
|
|
local AirUnitCount = 0
|
|
for UnitID, UnitData in pairs( self:GetSet() ) do
|
|
local UnitTest = UnitData -- Wrapper.Unit#UNIT
|
|
if UnitTest:IsAir() then
|
|
AirUnitCount = AirUnitCount + 1
|
|
end
|
|
end
|
|
|
|
return AirUnitCount
|
|
end
|
|
|
|
--- Returns if the @{Set} has friendly ground units.
|
|
-- @param #SET_UNIT self
|
|
-- @return #number The amount of ground targets in the Set.
|
|
function SET_UNIT:HasFriendlyUnits( FriendlyCoalition )
|
|
self:F2()
|
|
|
|
local FriendlyUnitCount = 0
|
|
for UnitID, UnitData in pairs( self:GetSet() ) do
|
|
local UnitTest = UnitData -- Wrapper.Unit#UNIT
|
|
if UnitTest:IsFriendly( FriendlyCoalition ) then
|
|
FriendlyUnitCount = FriendlyUnitCount + 1
|
|
end
|
|
end
|
|
|
|
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.
|
|
---- @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, ... )
|
|
-- self:F2( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.PlayersAlive )
|
|
--
|
|
-- return self
|
|
-- end
|
|
--
|
|
--
|
|
----- Iterate the SET_UNIT and call an iterator 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, ... )
|
|
-- self:F2( arg )
|
|
--
|
|
-- self:ForEach( IteratorFunction, arg, self.Clients )
|
|
--
|
|
-- return self
|
|
-- end
|
|
|
|
---
|
|
-- @param #SET_UNIT self
|
|
-- @param Wrapper.Unit#UNIT MUnit
|
|
-- @return #SET_UNIT self
|
|
function SET_UNIT:IsIncludeObject( MUnit )
|
|
self:F2( MUnit )
|
|
|
|
local MUnitInclude = false
|
|
|
|
if MUnit:IsAlive() ~= nil then
|
|
|
|
MUnitInclude = true
|
|
|
|
if self.Filter.Active ~= nil then
|
|
local MUnitActive = false
|
|
if self.Filter.Active == false or (self.Filter.Active == true and MUnit:IsActive() == true) then
|
|
MUnitActive = true
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitActive
|
|
end
|
|
|
|
if self.Filter.Coalitions then
|
|
local MUnitCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then
|
|
MUnitCoalition = true
|
|
end
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitCoalition
|
|
end
|
|
|
|
if self.Filter.Categories then
|
|
local MUnitCategory = false
|
|
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
|
self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
|
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then
|
|
MUnitCategory = true
|
|
end
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitCategory
|
|
end
|
|
|
|
if self.Filter.Types then
|
|
local MUnitType = false
|
|
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
|
self:T3( { "Type:", MUnit:GetTypeName(), TypeName } )
|
|
if TypeName == MUnit:GetTypeName() then
|
|
MUnitType = true
|
|
end
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitType
|
|
end
|
|
|
|
if self.Filter.Countries then
|
|
local MUnitCountry = false
|
|
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
|
self:T3( { "Country:", MUnit:GetCountry(), CountryName } )
|
|
if country.id[CountryName] == MUnit:GetCountry() then
|
|
MUnitCountry = true
|
|
end
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitCountry
|
|
end
|
|
|
|
if self.Filter.UnitPrefixes then
|
|
local MUnitPrefix = false
|
|
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
|
|
self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } )
|
|
if string.find( MUnit:GetName(), UnitPrefix, 1 ) then
|
|
MUnitPrefix = true
|
|
end
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitPrefix
|
|
end
|
|
|
|
if self.Filter.RadarTypes then
|
|
local MUnitRadar = false
|
|
for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do
|
|
self:T3( { "Radar:", RadarType } )
|
|
if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then
|
|
if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability.
|
|
self:T3( "RADAR Found" )
|
|
end
|
|
MUnitRadar = true
|
|
end
|
|
end
|
|
MUnitInclude = MUnitInclude and MUnitRadar
|
|
end
|
|
|
|
if self.Filter.SEAD then
|
|
local MUnitSEAD = false
|
|
if MUnit:HasSEAD() == true then
|
|
self:T3( "SEAD Found" )
|
|
MUnitSEAD = true
|
|
end
|
|
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.
|
|
-- @return #string The types of the @{Wrapper.Unit}s delimited.
|
|
function SET_UNIT:GetTypeNames( Delimiter )
|
|
|
|
Delimiter = Delimiter or ", "
|
|
local TypeReport = REPORT:New()
|
|
local Types = {}
|
|
|
|
for UnitName, UnitData in pairs( self:GetSet() ) do
|
|
|
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
|
local UnitTypeName = Unit:GetTypeName()
|
|
|
|
if not Types[UnitTypeName] then
|
|
Types[UnitTypeName] = UnitTypeName
|
|
TypeReport:Add( UnitTypeName )
|
|
end
|
|
end
|
|
|
|
return TypeReport:Text( Delimiter )
|
|
end
|
|
|
|
--- Iterate the SET_UNIT and set for each unit the default cargo bay weight limit.
|
|
-- @param #SET_UNIT self
|
|
-- @usage
|
|
-- -- Set the default cargo bay weight limits of the carrier units.
|
|
-- local MySetUnit = SET_UNIT:New()
|
|
-- MySetUnit:SetCargoBayWeightLimit()
|
|
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
|
|
UnitData:SetCargoBayWeightLimit()
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
do -- SET_STATIC
|
|
|
|
--- @type SET_STATIC
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain:
|
|
--
|
|
-- * Coalitions
|
|
-- * Categories
|
|
-- * Countries
|
|
-- * Static types
|
|
-- * Starting with certain prefix strings.
|
|
--
|
|
-- ## SET_STATIC constructor
|
|
--
|
|
-- Create a new SET_STATIC object with the @{#SET_STATIC.New} method:
|
|
--
|
|
-- * @{#SET_STATIC.New}: Creates a new SET_STATIC object.
|
|
--
|
|
-- ## Add or Remove STATIC(s) from SET_STATIC
|
|
--
|
|
-- STATICs can be added and removed using the @{Core.Set#SET_STATIC.AddStaticsByName} and @{Core.Set#SET_STATIC.RemoveStaticsByName} respectively.
|
|
-- These methods take a single STATIC name or an array of STATIC names to be added or removed from SET_STATIC.
|
|
--
|
|
-- ## SET_STATIC filter criteria
|
|
--
|
|
-- You can set filter criteria to define the set of units within the SET_STATIC.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_STATIC.FilterCoalitions}: Builds the SET_STATIC with the units belonging to the coalition(s).
|
|
-- * @{#SET_STATIC.FilterCategories}: Builds the SET_STATIC with the units belonging to the category(ies).
|
|
-- * @{#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.
|
|
--
|
|
-- ## 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.
|
|
-- The iterator methods will walk the SET_STATIC set, and call for each element within the set a function that you provide.
|
|
-- 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_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
|
|
--
|
|
-- Various methods exist for a SET_STATIC to perform actions or calculations and retrieve results from the SET_STATIC:
|
|
--
|
|
-- * @{#SET_STATIC.GetTypeNames}(): Retrieve the type names of the @{Wrapper.Static}s in the SET, delimited by a comma.
|
|
--
|
|
-- ===
|
|
-- @field #SET_STATIC SET_STATIC
|
|
SET_STATIC = {
|
|
ClassName = "SET_STATIC",
|
|
Statics = {},
|
|
Filter = {
|
|
Coalitions = nil,
|
|
Categories = nil,
|
|
Types = nil,
|
|
Countries = nil,
|
|
StaticPrefixes = nil,
|
|
Zones = nil,
|
|
},
|
|
FilterMeta = {
|
|
Coalitions = {
|
|
red = coalition.side.RED,
|
|
blue = coalition.side.BLUE,
|
|
neutral = coalition.side.NEUTRAL,
|
|
},
|
|
Categories = {
|
|
plane = Unit.Category.AIRPLANE,
|
|
helicopter = Unit.Category.HELICOPTER,
|
|
ground = Unit.Category.GROUND_STATIC,
|
|
ship = Unit.Category.SHIP,
|
|
structure = Unit.Category.STRUCTURE,
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
--- Get the first unit from the set.
|
|
-- @function [parent=#SET_STATIC] GetFirst
|
|
-- @param #SET_STATIC self
|
|
-- @return Wrapper.Static#STATIC The STATIC object.
|
|
|
|
--- Creates a new SET_STATIC object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
|
|
-- @param #SET_STATIC self
|
|
-- @return #SET_STATIC
|
|
-- @usage
|
|
-- -- Define a new SET_STATIC Object. This DBObject will contain a reference to all alive Statics.
|
|
-- DBObject = SET_STATIC:New()
|
|
function SET_STATIC:New()
|
|
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.STATICS ) ) -- Core.Set#SET_STATIC
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add STATIC(s) to SET_STATIC.
|
|
-- @param #SET_STATIC self
|
|
-- @param #string AddStatic A single STATIC.
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:AddStatic( AddStatic )
|
|
self:F2( AddStatic:GetName() )
|
|
|
|
self:Add( AddStatic:GetName(), AddStatic )
|
|
|
|
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 }
|
|
|
|
self:T( AddStaticNamesArray )
|
|
for AddStaticID, AddStaticName in pairs( AddStaticNamesArray ) do
|
|
self:Add( AddStaticName, STATIC:FindByName( AddStaticName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove STATIC(s) from SET_STATIC.
|
|
-- @param Core.Set#SET_STATIC self
|
|
-- @param Wrapper.Static#STATIC RemoveStaticNames A single name or an array of STATIC names.
|
|
-- @return self
|
|
function SET_STATIC:RemoveStaticsByName( RemoveStaticNames )
|
|
|
|
local RemoveStaticNamesArray = (type( RemoveStaticNames ) == "table") and RemoveStaticNames or { RemoveStaticNames }
|
|
|
|
for RemoveStaticID, RemoveStaticName in pairs( RemoveStaticNamesArray ) do
|
|
self:Remove( RemoveStaticName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Static based on the Static Name.
|
|
-- @param #SET_STATIC self
|
|
-- @param #string StaticName
|
|
-- @return Wrapper.Static#STATIC The found Static.
|
|
function SET_STATIC:FindStatic( StaticName )
|
|
|
|
local StaticFound = self.Set[StaticName]
|
|
return StaticFound
|
|
end
|
|
|
|
--- Builds a set of units of coalitions.
|
|
-- Possible current coalitions are red, blue and neutral.
|
|
-- @param #SET_STATIC self
|
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:FilterCoalitions( Coalitions )
|
|
if not self.Filter.Coalitions then
|
|
self.Filter.Coalitions = {}
|
|
end
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
|
self.Filter.Coalitions[Coalition] = Coalition
|
|
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.
|
|
-- @param #SET_STATIC self
|
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC: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 units of defined unit types.
|
|
-- Possible current types are those types known within DCS world.
|
|
-- @param #SET_STATIC self
|
|
-- @param #string Types Can take those type strings known within DCS world.
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:FilterTypes( Types )
|
|
if not self.Filter.Types then
|
|
self.Filter.Types = {}
|
|
end
|
|
if type( Types ) ~= "table" then
|
|
Types = { Types }
|
|
end
|
|
for TypeID, Type in pairs( Types ) do
|
|
self.Filter.Types[Type] = Type
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of units of defined countries.
|
|
-- Possible current countries are those known within DCS world.
|
|
-- @param #SET_STATIC self
|
|
-- @param #string Countries Can take those country strings known within DCS world.
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:FilterCountries( Countries )
|
|
if not self.Filter.Countries then
|
|
self.Filter.Countries = {}
|
|
end
|
|
if type( Countries ) ~= "table" then
|
|
Countries = { Countries }
|
|
end
|
|
for CountryID, Country in pairs( Countries ) do
|
|
self.Filter.Countries[Country] = Country
|
|
end
|
|
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
|
|
-- @param #string Prefixes The string pattern(s) that need to be contained in the static name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:FilterPrefixes( Prefixes )
|
|
if not self.Filter.StaticPrefixes then
|
|
self.Filter.StaticPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.StaticPrefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_STATIC self
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:FilterStart()
|
|
|
|
if _DATABASE then
|
|
self:_FilterStart()
|
|
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_STATIC and count how many STATICSs are alive.
|
|
-- @param #SET_STATIC self
|
|
-- @return #number The number of UNITs alive.
|
|
function SET_STATIC:CountAlive()
|
|
|
|
local Set = self:GetSet()
|
|
|
|
local CountU = 0
|
|
for UnitID, UnitData in pairs( Set ) do
|
|
if UnitData and UnitData:IsAlive() then
|
|
CountU = CountU + 1
|
|
end
|
|
|
|
end
|
|
|
|
return CountU
|
|
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_STATIC self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the STATIC
|
|
-- @return #table The STATIC
|
|
function SET_STATIC:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
if Event.IniObjectCategory == Object.Category.STATIC then
|
|
if not self.Database[Event.IniDCSUnitName] then
|
|
self.Database[Event.IniDCSUnitName] = STATIC:Register( Event.IniDCSUnitName )
|
|
self:T3( self.Database[Event.IniDCSUnitName] )
|
|
end
|
|
end
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_STATIC self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the STATIC
|
|
-- @return #table The 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.
|
|
-- @param #SET_STATIC self
|
|
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
|
|
-- @return #boolean
|
|
function SET_STATIC:IsPartiallyInZone( Zone )
|
|
|
|
local IsPartiallyInZone = false
|
|
|
|
local function EvaluateZone( ZoneStatic )
|
|
|
|
local ZoneStaticName = ZoneStatic:GetName()
|
|
if self:FindStatic( ZoneStaticName ) then
|
|
IsPartiallyInZone = true
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
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.
|
|
-- @return #boolean
|
|
function SET_STATIC:IsNotInZone( Zone )
|
|
|
|
local IsNotInZone = true
|
|
|
|
local function EvaluateZone( ZoneStatic )
|
|
|
|
local ZoneStaticName = ZoneStatic:GetName()
|
|
if self:FindStatic( ZoneStaticName ) then
|
|
IsNotInZone = false
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
Zone:Search( EvaluateZone )
|
|
|
|
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.
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:ForEachStaticInZone( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
end
|
|
|
|
|
|
--- Iterate the SET_STATIC and call an iterator 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
|
|
function SET_STATIC:ForEachStatic( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
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.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- 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.
|
|
-- @param #SET_STATIC self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Returns map of unit types.
|
|
-- @param #SET_STATIC self
|
|
-- @return #map<#string,#number> A map of the unit types found. The key is the StaticTypeName and the value is the amount of unit types found.
|
|
function SET_STATIC:GetStaticTypes()
|
|
self:F2()
|
|
|
|
local MT = {} -- Message Text
|
|
local StaticTypes = {}
|
|
|
|
for StaticID, StaticData in pairs( self:GetSet() ) do
|
|
local TextStatic = StaticData -- Wrapper.Static#STATIC
|
|
if TextStatic:IsAlive() then
|
|
local StaticType = TextStatic:GetTypeName()
|
|
|
|
if not StaticTypes[StaticType] then
|
|
StaticTypes[StaticType] = 1
|
|
else
|
|
StaticTypes[StaticType] = StaticTypes[StaticType] + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
for StaticTypeID, StaticType in pairs( StaticTypes ) do
|
|
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
|
|
function SET_STATIC:GetStaticTypesText()
|
|
self:F2()
|
|
|
|
local MT = {} -- Message Text
|
|
local StaticTypes = self:GetStaticTypes()
|
|
|
|
for StaticTypeID, StaticType in pairs( StaticTypes ) do
|
|
MT[#MT + 1] = StaticType .. " of " .. StaticTypeID
|
|
end
|
|
|
|
return table.concat( MT, ", " )
|
|
end
|
|
|
|
--- Get the center coordinate of the SET_STATIC.
|
|
-- @param #SET_STATIC self
|
|
-- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units.
|
|
function SET_STATIC:GetCoordinate()
|
|
|
|
local Coordinate = self:GetFirst():GetCoordinate()
|
|
|
|
local x1 = Coordinate.x
|
|
local x2 = Coordinate.x
|
|
local y1 = Coordinate.y
|
|
local y2 = Coordinate.y
|
|
local z1 = Coordinate.z
|
|
local z2 = Coordinate.z
|
|
local MaxVelocity = 0
|
|
local AvgHeading = nil
|
|
local MovingCount = 0
|
|
|
|
for StaticName, StaticData in pairs( self:GetSet() ) do
|
|
|
|
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
|
|
|
|
local Velocity = Coordinate:GetVelocity()
|
|
if Velocity ~= 0 then
|
|
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
|
|
local Heading = Coordinate:GetHeading()
|
|
AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading
|
|
MovingCount = MovingCount + 1
|
|
end
|
|
end
|
|
|
|
AvgHeading = AvgHeading and (AvgHeading / MovingCount)
|
|
|
|
Coordinate.x = (x2 - x1) / 2 + x1
|
|
Coordinate.y = (y2 - y1) / 2 + y1
|
|
Coordinate.z = (z2 - z1) / 2 + z1
|
|
Coordinate:SetHeading( AvgHeading )
|
|
Coordinate:SetVelocity( MaxVelocity )
|
|
|
|
self:F( { Coordinate = Coordinate } )
|
|
return Coordinate
|
|
|
|
end
|
|
|
|
--- Get the maximum velocity of the SET_STATIC.
|
|
-- @param #SET_STATIC self
|
|
-- @return #number The speed in mps in case of moving units.
|
|
function SET_STATIC:GetVelocity()
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
--- Get the average heading of the SET_STATIC.
|
|
-- @param #SET_STATIC self
|
|
-- @return #number Heading Heading in degrees and speed in mps in case of moving units.
|
|
function SET_STATIC:GetHeading()
|
|
|
|
local HeadingSet = nil
|
|
local MovingCount = 0
|
|
|
|
for StaticName, StaticData in pairs( self:GetSet() ) do
|
|
|
|
local Static = StaticData -- Wrapper.Static#STATIC
|
|
local Coordinate = Static:GetCoordinate()
|
|
|
|
local Velocity = Coordinate:GetVelocity()
|
|
if Velocity ~= 0 then
|
|
local Heading = Coordinate:GetHeading()
|
|
if HeadingSet == nil then
|
|
HeadingSet = Heading
|
|
else
|
|
local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180
|
|
HeadingDiff = math.abs( HeadingDiff )
|
|
if HeadingDiff > 5 then
|
|
HeadingSet = nil
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return HeadingSet
|
|
|
|
end
|
|
|
|
--- Calculate the maximum A2G threat level of the SET_STATIC.
|
|
-- @param #SET_STATIC self
|
|
-- @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
|
|
end
|
|
|
|
self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } )
|
|
return MaxThreatLevelA2G, MaxThreatText
|
|
|
|
end
|
|
|
|
---
|
|
-- @param #SET_STATIC self
|
|
-- @param Wrapper.Static#STATIC MStatic
|
|
-- @return #SET_STATIC self
|
|
function SET_STATIC:IsIncludeObject( MStatic )
|
|
self:F2( MStatic )
|
|
local MStaticInclude = true
|
|
|
|
if self.Filter.Coalitions then
|
|
local MStaticCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
self:T3( { "Coalition:", MStatic:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MStatic:GetCoalition() then
|
|
MStaticCoalition = true
|
|
end
|
|
end
|
|
MStaticInclude = MStaticInclude and MStaticCoalition
|
|
end
|
|
|
|
if self.Filter.Categories then
|
|
local MStaticCategory = false
|
|
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
|
self:T3( { "Category:", MStatic:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
|
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MStatic:GetDesc().category then
|
|
MStaticCategory = true
|
|
end
|
|
end
|
|
MStaticInclude = MStaticInclude and MStaticCategory
|
|
end
|
|
|
|
if self.Filter.Types then
|
|
local MStaticType = false
|
|
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
|
self:T3( { "Type:", MStatic:GetTypeName(), TypeName } )
|
|
if TypeName == MStatic:GetTypeName() then
|
|
MStaticType = true
|
|
end
|
|
end
|
|
MStaticInclude = MStaticInclude and MStaticType
|
|
end
|
|
|
|
if self.Filter.Countries then
|
|
local MStaticCountry = false
|
|
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
|
self:T3( { "Country:", MStatic:GetCountry(), CountryName } )
|
|
if country.id[CountryName] == MStatic:GetCountry() then
|
|
MStaticCountry = true
|
|
end
|
|
end
|
|
MStaticInclude = MStaticInclude and MStaticCountry
|
|
end
|
|
|
|
if self.Filter.StaticPrefixes then
|
|
local MStaticPrefix = false
|
|
for StaticPrefixId, StaticPrefix in pairs( self.Filter.StaticPrefixes ) do
|
|
self:T3( { "Prefix:", string.find( MStatic:GetName(), StaticPrefix, 1 ), StaticPrefix } )
|
|
if string.find( MStatic:GetName(), StaticPrefix, 1 ) then
|
|
MStaticPrefix = true
|
|
end
|
|
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 @{Wrapper.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.
|
|
-- @return #string The types of the @{Wrapper.Static}s delimited.
|
|
function SET_STATIC:GetTypeNames( Delimiter )
|
|
|
|
Delimiter = Delimiter or ", "
|
|
local TypeReport = REPORT:New()
|
|
local Types = {}
|
|
|
|
for StaticName, StaticData in pairs( self:GetSet() ) do
|
|
|
|
local Static = StaticData -- Wrapper.Static#STATIC
|
|
local StaticTypeName = Static:GetTypeName()
|
|
|
|
if not Types[StaticTypeName] then
|
|
Types[StaticTypeName] = StaticTypeName
|
|
TypeReport:Add( StaticTypeName )
|
|
end
|
|
end
|
|
|
|
return TypeReport:Text( Delimiter )
|
|
end
|
|
|
|
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
|
|
-- * Categories
|
|
-- * Countries
|
|
-- * Client types
|
|
-- * Starting with certain prefix strings.
|
|
--
|
|
-- ## 1) SET_CLIENT constructor
|
|
--
|
|
-- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method:
|
|
--
|
|
-- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object.
|
|
--
|
|
-- ## 2) Add or Remove CLIENT(s) from SET_CLIENT
|
|
--
|
|
-- CLIENTs can be added and removed using the @{Core.Set#SET_CLIENT.AddClientsByName} and @{Core.Set#SET_CLIENT.RemoveClientsByName} respectively.
|
|
-- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT.
|
|
--
|
|
-- ## 3) SET_CLIENT filter criteria
|
|
--
|
|
-- You can set filter criteria to define the set of clients within the SET_CLIENT.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s).
|
|
-- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies).
|
|
-- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s).
|
|
-- * @{#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**.
|
|
--
|
|
-- ## 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.
|
|
-- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_CLIENT:
|
|
--
|
|
-- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT.
|
|
--
|
|
-- ===
|
|
-- @field #SET_CLIENT SET_CLIENT
|
|
SET_CLIENT = {
|
|
ClassName = "SET_CLIENT",
|
|
Clients = {},
|
|
Filter = {
|
|
Coalitions = nil,
|
|
Categories = nil,
|
|
Types = nil,
|
|
Countries = nil,
|
|
ClientPrefixes = nil,
|
|
Zones = nil,
|
|
},
|
|
FilterMeta = {
|
|
Coalitions = {
|
|
red = coalition.side.RED,
|
|
blue = coalition.side.BLUE,
|
|
neutral = coalition.side.NEUTRAL,
|
|
},
|
|
Categories = {
|
|
plane = Unit.Category.AIRPLANE,
|
|
helicopter = Unit.Category.HELICOPTER,
|
|
ground = Unit.Category.GROUND_UNIT,
|
|
ship = Unit.Category.SHIP,
|
|
structure = Unit.Category.STRUCTURE,
|
|
},
|
|
},
|
|
}
|
|
|
|
--- Creates a new SET_CLIENT object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names.
|
|
-- @param #SET_CLIENT self
|
|
-- @return #SET_CLIENT
|
|
-- @usage
|
|
-- -- Define a new SET_CLIENT Object. This DBObject will contain a reference to all Clients.
|
|
-- DBObject = SET_CLIENT:New()
|
|
function SET_CLIENT:New()
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) -- #SET_CLIENT
|
|
|
|
self:FilterActive( false )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add CLIENT(s) to SET_CLIENT.
|
|
-- @param Core.Set#SET_CLIENT self
|
|
-- @param #string AddClientNames A single name or an array of CLIENT names.
|
|
-- @return self
|
|
function SET_CLIENT:AddClientsByName( AddClientNames )
|
|
|
|
local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames }
|
|
|
|
for AddClientID, AddClientName in pairs( AddClientNamesArray ) do
|
|
self:Add( AddClientName, CLIENT:FindByName( AddClientName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove CLIENT(s) from SET_CLIENT.
|
|
-- @param Core.Set#SET_CLIENT self
|
|
-- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names.
|
|
-- @return self
|
|
function SET_CLIENT:RemoveClientsByName( RemoveClientNames )
|
|
|
|
local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames }
|
|
|
|
for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do
|
|
self:Remove( RemoveClientName.ClientName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Client based on the Client Name.
|
|
-- @param #SET_CLIENT self
|
|
-- @param #string ClientName
|
|
-- @return Wrapper.Client#CLIENT The found Client.
|
|
function SET_CLIENT:FindClient( ClientName )
|
|
|
|
local ClientFound = self.Set[ClientName]
|
|
return ClientFound
|
|
end
|
|
|
|
--- Builds a set of clients of coalitions.
|
|
-- Possible current coalitions are red, blue and neutral.
|
|
-- @param #SET_CLIENT self
|
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
|
-- @return #SET_CLIENT self
|
|
function SET_CLIENT:FilterCoalitions( Coalitions )
|
|
if not self.Filter.Coalitions then
|
|
self.Filter.Coalitions = {}
|
|
end
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
|
self.Filter.Coalitions[Coalition] = Coalition
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of clients out of categories.
|
|
-- Possible current categories are plane, helicopter, ground, ship.
|
|
-- @param #SET_CLIENT self
|
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
|
-- @return #SET_CLIENT self
|
|
function SET_CLIENT: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 clients of defined client types.
|
|
-- Possible current types are those types known within DCS world.
|
|
-- @param #SET_CLIENT self
|
|
-- @param #string Types Can take those type strings known within DCS world.
|
|
-- @return #SET_CLIENT self
|
|
function SET_CLIENT:FilterTypes( Types )
|
|
if not self.Filter.Types then
|
|
self.Filter.Types = {}
|
|
end
|
|
if type( Types ) ~= "table" then
|
|
Types = { Types }
|
|
end
|
|
for TypeID, Type in pairs( Types ) do
|
|
self.Filter.Types[Type] = Type
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of clients of defined countries.
|
|
-- Possible current countries are those known within DCS world.
|
|
-- @param #SET_CLIENT self
|
|
-- @param #string Countries Can take those country strings known within DCS world.
|
|
-- @return #SET_CLIENT self
|
|
function SET_CLIENT:FilterCountries( Countries )
|
|
if not self.Filter.Countries then
|
|
self.Filter.Countries = {}
|
|
end
|
|
if type( Countries ) ~= "table" then
|
|
Countries = { Countries }
|
|
end
|
|
for CountryID, Country in pairs( Countries ) do
|
|
self.Filter.Countries[Country] = Country
|
|
end
|
|
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
|
|
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_CLIENT self
|
|
function SET_CLIENT:FilterPrefixes( Prefixes )
|
|
if not self.Filter.ClientPrefixes then
|
|
self.Filter.ClientPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.ClientPrefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- 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.
|
|
-- Include inactive clients if you provide false.
|
|
-- @return #SET_CLIENT self
|
|
-- @usage
|
|
--
|
|
-- -- Include only active clients to the set.
|
|
-- ClientSet = SET_CLIENT:New():FilterActive():FilterStart()
|
|
--
|
|
-- -- Include only active clients to the set of the blue coalition, and filter one time.
|
|
-- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
|
|
--
|
|
-- -- Include only active clients to the set of the blue coalition, and filter one time.
|
|
-- -- Later, reset to include back inactive clients to the set.
|
|
-- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
|
|
-- ... logic ...
|
|
-- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce()
|
|
--
|
|
function SET_CLIENT:FilterActive( Active )
|
|
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
|
|
function SET_CLIENT:FilterStart()
|
|
|
|
if _DATABASE then
|
|
self:_FilterStart()
|
|
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
|
end
|
|
|
|
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_CLIENT self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the CLIENT
|
|
-- @return #table The CLIENT
|
|
function SET_CLIENT:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_CLIENT self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the CLIENT
|
|
-- @return #table The CLIENT
|
|
function SET_CLIENT:FindInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
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.
|
|
-- @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
|
|
function SET_CLIENT:ForEachClient( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT presence completely in a @{Zone}, providing the CLIENT and optional parameters to the called function.
|
|
-- @param #SET_CLIENT self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT presence not in a @{Zone}, providing the CLIENT and optional parameters to the called function.
|
|
-- @param #SET_CLIENT self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_CLIENT and count alive units.
|
|
-- @param #SET_CLIENT self
|
|
-- @return #number count
|
|
function SET_CLIENT:CountAlive()
|
|
|
|
local Set = self:GetSet()
|
|
|
|
local CountU = 0
|
|
for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
|
if UnitData and UnitData:IsAlive() then
|
|
CountU = CountU + 1
|
|
end
|
|
|
|
end
|
|
|
|
return CountU
|
|
end
|
|
|
|
|
|
--- Gets the alive set.
|
|
-- @param #SET_CLIENT self
|
|
-- @return #table Table of SET objects
|
|
function SET_CLIENT:GetAliveSet()
|
|
|
|
local AliveSet = SET_CLIENT:New()
|
|
|
|
-- Clean the Set before returning with only the alive Groups.
|
|
for GroupName, GroupObject in pairs(self.Set) do
|
|
local GroupObject=GroupObject --Wrapper.Client#CLIENT
|
|
|
|
if GroupObject and GroupObject:IsAlive() then
|
|
AliveSet:Add(GroupName, GroupObject)
|
|
end
|
|
end
|
|
|
|
return AliveSet.Set or {}
|
|
end
|
|
|
|
---
|
|
-- @param #SET_CLIENT self
|
|
-- @param Wrapper.Client#CLIENT MClient
|
|
-- @return #SET_CLIENT self
|
|
function SET_CLIENT:IsIncludeObject( MClient )
|
|
self:F2( MClient )
|
|
|
|
local MClientInclude = true
|
|
|
|
if MClient then
|
|
local MClientName = MClient.UnitName
|
|
|
|
if self.Filter.Active ~= nil then
|
|
local MClientActive = false
|
|
if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true) then
|
|
MClientActive = true
|
|
end
|
|
MClientInclude = MClientInclude and MClientActive
|
|
end
|
|
|
|
if self.Filter.Coalitions then
|
|
local MClientCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName )
|
|
self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then
|
|
MClientCoalition = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Coalition", MClientCoalition } )
|
|
MClientInclude = MClientInclude and MClientCoalition
|
|
end
|
|
|
|
if self.Filter.Categories then
|
|
local MClientCategory = false
|
|
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
|
local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName )
|
|
self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
|
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then
|
|
MClientCategory = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Category", MClientCategory } )
|
|
MClientInclude = MClientInclude and MClientCategory
|
|
end
|
|
|
|
if self.Filter.Types then
|
|
local MClientType = false
|
|
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
|
self:T3( { "Type:", MClient:GetTypeName(), TypeName } )
|
|
if TypeName == MClient:GetTypeName() then
|
|
MClientType = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Type", MClientType } )
|
|
MClientInclude = MClientInclude and MClientType
|
|
end
|
|
|
|
if self.Filter.Countries then
|
|
local MClientCountry = false
|
|
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
|
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
|
|
end
|
|
end
|
|
self:T( { "Evaluated Country", MClientCountry } )
|
|
MClientInclude = MClientInclude and MClientCountry
|
|
end
|
|
|
|
if self.Filter.ClientPrefixes then
|
|
local MClientPrefix = false
|
|
for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do
|
|
self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } )
|
|
if string.find( MClient.UnitName, ClientPrefix, 1 ) then
|
|
MClientPrefix = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Prefix", MClientPrefix } )
|
|
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_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
|
|
--
|
|
-- Create a new SET_PLAYER object with the @{#SET_PLAYER.New} method:
|
|
--
|
|
-- * @{#SET_PLAYER.New}: Creates a new SET_PLAYER object.
|
|
--
|
|
-- ## SET_PLAYER filter criteria
|
|
--
|
|
-- You can set filter criteria to define the set of clients within the SET_PLAYER.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_PLAYER.FilterCoalitions}: Builds the SET_PLAYER with the clients belonging to the coalition(s).
|
|
-- * @{#SET_PLAYER.FilterCategories}: Builds the SET_PLAYER with the clients belonging to the category(ies).
|
|
-- * @{#SET_PLAYER.FilterTypes}: Builds the SET_PLAYER with the clients belonging to the client type(s).
|
|
-- * @{#SET_PLAYER.FilterCountries}: Builds the SET_PLAYER with the clients belonging to the country(ies).
|
|
-- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients sharing the same string(s) in their unit/pilot name. **ATTENTION** Bad naming convention as this *does not* only filter prefixes.
|
|
--
|
|
-- Once the filter criteria have been set for the SET_PLAYER, you can start filtering using:
|
|
--
|
|
-- * @{#SET_PLAYER.FilterStart}: Starts the filtering of the clients within the SET_PLAYER.
|
|
--
|
|
-- Planned filter criteria within development are (so these are not yet available):
|
|
--
|
|
-- * @{#SET_PLAYER.FilterZones}: Builds the SET_PLAYER with the clients within a @{Core.Zone#ZONE}.
|
|
--
|
|
-- ## SET_PLAYER iterators
|
|
--
|
|
-- Once the filters have been defined and the SET_PLAYER has been built, you can iterate the SET_PLAYER with the available iterator methods.
|
|
-- The iterator methods will walk the SET_PLAYER set, and call for each element within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_PLAYER:
|
|
--
|
|
-- * @{#SET_PLAYER.ForEachClient}: Calls a function for each alive client it finds within the SET_PLAYER.
|
|
--
|
|
-- ===
|
|
-- @field #SET_PLAYER SET_PLAYER
|
|
SET_PLAYER = {
|
|
ClassName = "SET_PLAYER",
|
|
Clients = {},
|
|
Filter = {
|
|
Coalitions = nil,
|
|
Categories = nil,
|
|
Types = nil,
|
|
Countries = nil,
|
|
ClientPrefixes = nil,
|
|
Zones = nil,
|
|
},
|
|
FilterMeta = {
|
|
Coalitions = {
|
|
red = coalition.side.RED,
|
|
blue = coalition.side.BLUE,
|
|
neutral = coalition.side.NEUTRAL,
|
|
},
|
|
Categories = {
|
|
plane = Unit.Category.AIRPLANE,
|
|
helicopter = Unit.Category.HELICOPTER,
|
|
ground = Unit.Category.GROUND_UNIT,
|
|
ship = Unit.Category.SHIP,
|
|
structure = Unit.Category.STRUCTURE,
|
|
},
|
|
},
|
|
}
|
|
|
|
--- 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
|
|
-- @usage
|
|
-- -- Define a new SET_PLAYER Object. This DBObject will contain a reference to all Clients.
|
|
-- DBObject = SET_PLAYER:New()
|
|
function SET_PLAYER:New()
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.PLAYERS ) )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add CLIENT(s) to SET_PLAYER.
|
|
-- @param Core.Set#SET_PLAYER self
|
|
-- @param #string AddClientNames A single name or an array of CLIENT names.
|
|
-- @return self
|
|
function SET_PLAYER:AddClientsByName( AddClientNames )
|
|
|
|
local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames }
|
|
|
|
for AddClientID, AddClientName in pairs( AddClientNamesArray ) do
|
|
self:Add( AddClientName, CLIENT:FindByName( AddClientName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove CLIENT(s) from SET_PLAYER.
|
|
-- @param Core.Set#SET_PLAYER self
|
|
-- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names.
|
|
-- @return self
|
|
function SET_PLAYER:RemoveClientsByName( RemoveClientNames )
|
|
|
|
local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames }
|
|
|
|
for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do
|
|
self:Remove( RemoveClientName.ClientName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Client based on the Player Name.
|
|
-- @param #SET_PLAYER self
|
|
-- @param #string PlayerName
|
|
-- @return Wrapper.Client#CLIENT The found Client.
|
|
function SET_PLAYER:FindClient( PlayerName )
|
|
|
|
local ClientFound = self.Set[PlayerName]
|
|
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
|
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER:FilterCoalitions( Coalitions )
|
|
if not self.Filter.Coalitions then
|
|
self.Filter.Coalitions = {}
|
|
end
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
|
self.Filter.Coalitions[Coalition] = Coalition
|
|
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.
|
|
-- @param #SET_PLAYER self
|
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER: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 clients of defined client types joined by players.
|
|
-- Possible current types are those types known within DCS world.
|
|
-- @param #SET_PLAYER self
|
|
-- @param #string Types Can take those type strings known within DCS world.
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER:FilterTypes( Types )
|
|
if not self.Filter.Types then
|
|
self.Filter.Types = {}
|
|
end
|
|
if type( Types ) ~= "table" then
|
|
Types = { Types }
|
|
end
|
|
for TypeID, Type in pairs( Types ) do
|
|
self.Filter.Types[Type] = Type
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of clients of defined countries.
|
|
-- Possible current countries are those known within DCS world.
|
|
-- @param #SET_PLAYER self
|
|
-- @param #string Countries Can take those country strings known within DCS world.
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER:FilterCountries( Countries )
|
|
if not self.Filter.Countries then
|
|
self.Filter.Countries = {}
|
|
end
|
|
if type( Countries ) ~= "table" then
|
|
Countries = { Countries }
|
|
end
|
|
for CountryID, Country in pairs( Countries ) do
|
|
self.Filter.Countries[Country] = Country
|
|
end
|
|
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
|
|
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER:FilterPrefixes( Prefixes )
|
|
if not self.Filter.ClientPrefixes then
|
|
self.Filter.ClientPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.ClientPrefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_PLAYER self
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER:FilterStart()
|
|
|
|
if _DATABASE then
|
|
self:_FilterStart()
|
|
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
|
end
|
|
|
|
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_PLAYER self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the CLIENT
|
|
-- @return #table The CLIENT
|
|
function SET_PLAYER:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_PLAYER self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the CLIENT
|
|
-- @return #table The CLIENT
|
|
function SET_PLAYER:FindInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
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.
|
|
-- @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
|
|
function SET_PLAYER:ForEachPlayer( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_PLAYER and call an iterator function for each **alive** CLIENT presence completely in a @{Zone}, providing the CLIENT and optional parameters to the called function.
|
|
-- @param #SET_PLAYER self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_PLAYER and call an iterator function for each **alive** CLIENT presence not in a @{Zone}, providing the CLIENT and optional parameters to the called function.
|
|
-- @param #SET_PLAYER self
|
|
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
|
-- @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
|
|
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 } )
|
|
|
|
return self
|
|
end
|
|
|
|
---
|
|
-- @param #SET_PLAYER self
|
|
-- @param Wrapper.Client#CLIENT MClient
|
|
-- @return #SET_PLAYER self
|
|
function SET_PLAYER:IsIncludeObject( MClient )
|
|
self:F2( MClient )
|
|
|
|
local MClientInclude = true
|
|
|
|
if MClient then
|
|
local MClientName = MClient.UnitName
|
|
|
|
if self.Filter.Coalitions then
|
|
local MClientCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName )
|
|
self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then
|
|
MClientCoalition = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Coalition", MClientCoalition } )
|
|
MClientInclude = MClientInclude and MClientCoalition
|
|
end
|
|
|
|
if self.Filter.Categories then
|
|
local MClientCategory = false
|
|
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
|
local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName )
|
|
self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
|
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then
|
|
MClientCategory = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Category", MClientCategory } )
|
|
MClientInclude = MClientInclude and MClientCategory
|
|
end
|
|
|
|
if self.Filter.Types then
|
|
local MClientType = false
|
|
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
|
self:T3( { "Type:", MClient:GetTypeName(), TypeName } )
|
|
if TypeName == MClient:GetTypeName() then
|
|
MClientType = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Type", MClientType } )
|
|
MClientInclude = MClientInclude and MClientType
|
|
end
|
|
|
|
if self.Filter.Countries then
|
|
local MClientCountry = false
|
|
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
|
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
|
|
end
|
|
end
|
|
self:T( { "Evaluated Country", MClientCountry } )
|
|
MClientInclude = MClientInclude and MClientCountry
|
|
end
|
|
|
|
if self.Filter.ClientPrefixes then
|
|
local MClientPrefix = false
|
|
for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do
|
|
self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } )
|
|
if string.find( MClient.UnitName, ClientPrefix, 1 ) then
|
|
MClientPrefix = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Prefix", MClientPrefix } )
|
|
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
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain:
|
|
--
|
|
-- * Coalitions
|
|
--
|
|
-- ## SET_AIRBASE constructor
|
|
--
|
|
-- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method:
|
|
--
|
|
-- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object.
|
|
--
|
|
-- ## Add or Remove AIRBASEs from SET_AIRBASE
|
|
--
|
|
-- AIRBASEs can be added and removed using the @{Core.Set#SET_AIRBASE.AddAirbasesByName} and @{Core.Set#SET_AIRBASE.RemoveAirbasesByName} respectively.
|
|
-- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE.
|
|
--
|
|
-- ## SET_AIRBASE filter criteria
|
|
--
|
|
-- You can set filter criteria to define the set of clients within the SET_AIRBASE.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s).
|
|
--
|
|
-- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using:
|
|
--
|
|
-- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE.
|
|
--
|
|
-- ## SET_AIRBASE iterators
|
|
--
|
|
-- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods.
|
|
-- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_AIRBASE:
|
|
--
|
|
-- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE.
|
|
--
|
|
-- ===
|
|
-- @field #SET_AIRBASE SET_AIRBASE
|
|
SET_AIRBASE = {
|
|
ClassName = "SET_AIRBASE",
|
|
Airbases = {},
|
|
Filter = {
|
|
Coalitions = nil,
|
|
},
|
|
FilterMeta = {
|
|
Coalitions = {
|
|
red = coalition.side.RED,
|
|
blue = coalition.side.BLUE,
|
|
neutral = coalition.side.NEUTRAL,
|
|
},
|
|
Categories = {
|
|
airdrome = Airbase.Category.AIRDROME,
|
|
helipad = Airbase.Category.HELIPAD,
|
|
ship = Airbase.Category.SHIP,
|
|
},
|
|
},
|
|
}
|
|
|
|
--- 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
|
|
-- @usage
|
|
-- -- Define a new SET_AIRBASE Object. The DatabaseSet will contain a reference to all Airbases.
|
|
-- DatabaseSet = SET_AIRBASE:New()
|
|
function SET_AIRBASE:New()
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add an AIRBASE object to SET_AIRBASE.
|
|
-- @param Core.Set#SET_AIRBASE self
|
|
-- @param Wrapper.Airbase#AIRBASE airbase Airbase that should be added to the set.
|
|
-- @return self
|
|
function SET_AIRBASE:AddAirbase( airbase )
|
|
|
|
self:Add( airbase:GetName(), airbase )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add AIRBASEs to SET_AIRBASE.
|
|
-- @param Core.Set#SET_AIRBASE self
|
|
-- @param #string AddAirbaseNames A single name or an array of AIRBASE names.
|
|
-- @return self
|
|
function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames )
|
|
|
|
local AddAirbaseNamesArray = (type( AddAirbaseNames ) == "table") and AddAirbaseNames or { AddAirbaseNames }
|
|
|
|
for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do
|
|
self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove AIRBASEs from SET_AIRBASE.
|
|
-- @param Core.Set#SET_AIRBASE self
|
|
-- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names.
|
|
-- @return self
|
|
function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames )
|
|
|
|
local RemoveAirbaseNamesArray = (type( RemoveAirbaseNames ) == "table") and RemoveAirbaseNames or { RemoveAirbaseNames }
|
|
|
|
for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do
|
|
self:Remove( RemoveAirbaseName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Airbase based on the Airbase Name.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param #string AirbaseName
|
|
-- @return Wrapper.Airbase#AIRBASE The found Airbase.
|
|
function SET_AIRBASE:FindAirbase( AirbaseName )
|
|
|
|
local AirbaseFound = self.Set[AirbaseName]
|
|
return AirbaseFound
|
|
end
|
|
|
|
--- Finds an Airbase in range of a coordinate.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param Core.Point#COORDINATE Coordinate
|
|
-- @param #number Range
|
|
-- @return Wrapper.Airbase#AIRBASE The found Airbase.
|
|
function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range )
|
|
|
|
local AirbaseFound = nil
|
|
|
|
for AirbaseName, AirbaseObject in pairs( self.Set ) do
|
|
|
|
local AirbaseCoordinate = AirbaseObject:GetCoordinate()
|
|
local Distance = Coordinate:Get2DDistance( AirbaseCoordinate )
|
|
|
|
self:F( { Distance = Distance } )
|
|
|
|
if Distance <= Range then
|
|
AirbaseFound = AirbaseObject
|
|
break
|
|
end
|
|
|
|
end
|
|
|
|
return AirbaseFound
|
|
end
|
|
|
|
--- Finds a random Airbase in the set.
|
|
-- @param #SET_AIRBASE self
|
|
-- @return Wrapper.Airbase#AIRBASE The found Airbase.
|
|
function SET_AIRBASE:GetRandomAirbase()
|
|
|
|
local RandomAirbase = self:GetRandom()
|
|
self:F( { RandomAirbase = RandomAirbase:GetName() } )
|
|
|
|
return RandomAirbase
|
|
end
|
|
|
|
--- Builds a set of airbases of coalitions.
|
|
-- Possible current coalitions are red, blue and neutral.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
|
-- @return #SET_AIRBASE self
|
|
function SET_AIRBASE:FilterCoalitions( Coalitions )
|
|
if not self.Filter.Coalitions then
|
|
self.Filter.Coalitions = {}
|
|
end
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
|
self.Filter.Coalitions[Coalition] = Coalition
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Builds a set of airbases out of categories.
|
|
-- Possible current categories are plane, helicopter, ground, ship.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param #string Categories Can take the following values: "airdrome", "helipad", "ship".
|
|
-- @return #SET_AIRBASE self
|
|
function SET_AIRBASE: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
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_AIRBASE self
|
|
-- @return #SET_AIRBASE self
|
|
function SET_AIRBASE:FilterStart()
|
|
|
|
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 )
|
|
|
|
-- We initialize the first set.
|
|
for ObjectName, Object in pairs( self.Database ) do
|
|
if self:IsIncludeObject( Object ) then
|
|
self:Add( ObjectName, Object )
|
|
else
|
|
self:RemoveAirbasesByName( ObjectName )
|
|
end
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Base capturing event.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param Core.Event#EVENT EventData
|
|
function SET_AIRBASE:OnEventBaseCaptured( EventData )
|
|
|
|
-- When a base got captured, we reevaluate the set.
|
|
for ObjectName, Object in pairs( self.Database ) do
|
|
if self:IsIncludeObject( Object ) then
|
|
-- We add captured bases on yet in the set.
|
|
self:Add( ObjectName, Object )
|
|
else
|
|
-- We remove captured bases that are not anymore part of the set.
|
|
self:RemoveAirbasesByName( ObjectName )
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
--- Dead event.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param Core.Event#EVENT EventData
|
|
function SET_AIRBASE:OnEventDead( EventData )
|
|
|
|
local airbaseName, airbase = self:FindInDatabase( EventData )
|
|
|
|
if airbase and (airbase:IsShip() or airbase:IsHelipad()) then
|
|
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
|
|
-- @param Core.Event#EVENTDATA Event Event data.
|
|
-- @return #string The name of the AIRBASE.
|
|
-- @return Wrapper.Airbase#AIRBASE The AIRBASE object.
|
|
function SET_AIRBASE:AddInDatabase( Event )
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_AIRBASE self
|
|
-- @param Core.Event#EVENTDATA Event Event data.
|
|
-- @return #string The name of the AIRBASE.
|
|
-- @return Wrapper.Airbase#AIRBASE The AIRBASE object.
|
|
function SET_AIRBASE:FindInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
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.
|
|
-- @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
|
|
function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#POINT_VEC2}.
|
|
-- @param #SET_AIRBASE self
|
|
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}.
|
|
-- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}.
|
|
function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 )
|
|
self:F2( PointVec2 )
|
|
|
|
local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 )
|
|
return NearestAirbase
|
|
end
|
|
|
|
---
|
|
-- @param #SET_AIRBASE self
|
|
-- @param Wrapper.Airbase#AIRBASE MAirbase
|
|
-- @return #SET_AIRBASE self
|
|
function SET_AIRBASE:IsIncludeObject( MAirbase )
|
|
self:F2( MAirbase )
|
|
|
|
local MAirbaseInclude = true
|
|
|
|
if MAirbase then
|
|
local MAirbaseName = MAirbase:GetName()
|
|
|
|
if self.Filter.Coalitions then
|
|
local MAirbaseCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName )
|
|
self:T3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then
|
|
MAirbaseCoalition = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Coalition", MAirbaseCoalition } )
|
|
MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition
|
|
end
|
|
|
|
if self.Filter.Categories then
|
|
local MAirbaseCategory = false
|
|
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
|
local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName )
|
|
self:T3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
|
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then
|
|
MAirbaseCategory = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Category", MAirbaseCategory } )
|
|
MAirbaseInclude = MAirbaseInclude and MAirbaseCategory
|
|
end
|
|
end
|
|
|
|
self:T2( MAirbaseInclude )
|
|
return MAirbaseInclude
|
|
end
|
|
|
|
end
|
|
|
|
do -- SET_CARGO
|
|
|
|
--- @type SET_CARGO
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain:
|
|
--
|
|
-- * Coalitions
|
|
-- * Types
|
|
-- * Name or Prefix
|
|
--
|
|
-- ## SET_CARGO constructor
|
|
--
|
|
-- Create a new SET_CARGO object with the @{#SET_CARGO.New} method:
|
|
--
|
|
-- * @{#SET_CARGO.New}: Creates a new SET_CARGO object.
|
|
--
|
|
-- ## Add or Remove CARGOs from SET_CARGO
|
|
--
|
|
-- CARGOs can be added and removed using the @{Core.Set#SET_CARGO.AddCargosByName} and @{Core.Set#SET_CARGO.RemoveCargosByName} respectively.
|
|
-- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO.
|
|
--
|
|
-- ## SET_CARGO filter criteria
|
|
--
|
|
-- You can set filter criteria to automatically maintain the SET_CARGO contents.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s).
|
|
-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the same string(s). **ATTENTION** Bad naming convention as this *does not* only filter *prefixes*.
|
|
-- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s).
|
|
-- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies).
|
|
--
|
|
-- Once the filter criteria have been set for the SET_CARGO, you can start filtering using:
|
|
--
|
|
-- * @{#SET_CARGO.FilterStart}: Starts the filtering of the cargos within the SET_CARGO.
|
|
--
|
|
-- ## SET_CARGO iterators
|
|
--
|
|
-- Once the filters have been defined and the SET_CARGO has been built, you can iterate the SET_CARGO with the available iterator methods.
|
|
-- The iterator methods will walk the SET_CARGO set, and call for each cargo within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_CARGO:
|
|
--
|
|
-- * @{#SET_CARGO.ForEachCargo}: Calls a function for each cargo it finds within the SET_CARGO.
|
|
--
|
|
-- @field #SET_CARGO SET_CARGO
|
|
SET_CARGO = {
|
|
ClassName = "SET_CARGO",
|
|
Cargos = {},
|
|
Filter = {
|
|
Coalitions = nil,
|
|
Types = nil,
|
|
Countries = nil,
|
|
ClientPrefixes = nil,
|
|
},
|
|
FilterMeta = {
|
|
Coalitions = {
|
|
red = coalition.side.RED,
|
|
blue = coalition.side.BLUE,
|
|
neutral = coalition.side.NEUTRAL,
|
|
},
|
|
},
|
|
}
|
|
|
|
--- 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
|
|
-- 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
|
|
|
|
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
|
|
|
|
local AddCargoNamesArray = (type( AddCargoNames ) == "table") and AddCargoNames or { AddCargoNames }
|
|
|
|
for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do
|
|
self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- (R2.1) Remove CARGOs from 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
|
|
|
|
local RemoveCargoNamesArray = (type( RemoveCargoNames ) == "table") and RemoveCargoNames or { RemoveCargoNames }
|
|
|
|
for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do
|
|
self:Remove( RemoveCargoName.CargoName )
|
|
end
|
|
|
|
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
|
|
|
|
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
|
|
if not self.Filter.Coalitions then
|
|
self.Filter.Coalitions = {}
|
|
end
|
|
if type( Coalitions ) ~= "table" then
|
|
Coalitions = { Coalitions }
|
|
end
|
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
|
self.Filter.Coalitions[Coalition] = Coalition
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- (R2.1) Builds a set of cargos of defined cargo types.
|
|
-- Possible current types are those types known within DCS world.
|
|
-- @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
|
|
if not self.Filter.Types then
|
|
self.Filter.Types = {}
|
|
end
|
|
if type( Types ) ~= "table" then
|
|
Types = { Types }
|
|
end
|
|
for TypeID, Type in pairs( Types ) do
|
|
self.Filter.Types[Type] = Type
|
|
end
|
|
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
|
|
if not self.Filter.Countries then
|
|
self.Filter.Countries = {}
|
|
end
|
|
if type( Countries ) ~= "table" then
|
|
Countries = { Countries }
|
|
end
|
|
for CountryID, Country in pairs( Countries ) do
|
|
self.Filter.Countries[Country] = Country
|
|
end
|
|
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
|
|
if not self.Filter.CargoPrefixes then
|
|
self.Filter.CargoPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.CargoPrefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- (R2.1) Starts the filtering.
|
|
-- @param #SET_CARGO self
|
|
-- @return #SET_CARGO self
|
|
function SET_CARGO:FilterStart() -- R2.1
|
|
|
|
if _DATABASE then
|
|
self:_FilterStart()
|
|
self:HandleEvent( EVENTS.NewCargo )
|
|
self:HandleEvent( EVENTS.DeleteCargo )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Stops the filtering for the defined collection.
|
|
-- @param #SET_CARGO self
|
|
-- @return #SET_CARGO self
|
|
function SET_CARGO:FilterStop()
|
|
|
|
self:UnHandleEvent( EVENTS.NewCargo )
|
|
self:UnHandleEvent( EVENTS.DeleteCargo )
|
|
|
|
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
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- (R2.1) 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_CARGO self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the CARGO
|
|
-- @return #table The CARGO
|
|
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.
|
|
-- @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
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#POINT_VEC2}.
|
|
-- @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
|
|
self:F2( PointVec2 )
|
|
|
|
local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 )
|
|
return NearestCargo
|
|
end
|
|
|
|
function SET_CARGO:FirstCargoWithState( State )
|
|
|
|
local FirstCargo = nil
|
|
|
|
for CargoName, Cargo in pairs( self.Set ) do
|
|
if Cargo:Is( State ) then
|
|
FirstCargo = Cargo
|
|
break
|
|
end
|
|
end
|
|
|
|
return FirstCargo
|
|
end
|
|
|
|
function SET_CARGO:FirstCargoWithStateAndNotDeployed( State )
|
|
|
|
local FirstCargo = nil
|
|
|
|
for CargoName, Cargo in pairs( self.Set ) do
|
|
if Cargo:Is( State ) and not Cargo:IsDeployed() then
|
|
FirstCargo = Cargo
|
|
break
|
|
end
|
|
end
|
|
|
|
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}.
|
|
function SET_CARGO:FirstCargoUnLoaded()
|
|
local FirstCargo = self:FirstCargoWithState( "UnLoaded" )
|
|
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}.
|
|
function SET_CARGO:FirstCargoUnLoadedAndNotDeployed()
|
|
local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" )
|
|
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}.
|
|
function SET_CARGO:FirstCargoLoaded()
|
|
local FirstCargo = self:FirstCargoWithState( "Loaded" )
|
|
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}.
|
|
function SET_CARGO:FirstCargoDeployed()
|
|
local FirstCargo = self:FirstCargoWithState( "Deployed" )
|
|
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
|
|
self:F2( MCargo )
|
|
|
|
local MCargoInclude = true
|
|
|
|
if MCargo then
|
|
local MCargoName = MCargo:GetName()
|
|
|
|
if self.Filter.Coalitions then
|
|
local MCargoCoalition = false
|
|
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
|
local CargoCoalitionID = MCargo:GetCoalition()
|
|
self:T3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then
|
|
MCargoCoalition = true
|
|
end
|
|
end
|
|
self:F( { "Evaluated Coalition", MCargoCoalition } )
|
|
MCargoInclude = MCargoInclude and MCargoCoalition
|
|
end
|
|
|
|
if self.Filter.Types then
|
|
local MCargoType = false
|
|
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
|
self:T3( { "Type:", MCargo:GetType(), TypeName } )
|
|
if TypeName == MCargo:GetType() then
|
|
MCargoType = true
|
|
end
|
|
end
|
|
self:F( { "Evaluated Type", MCargoType } )
|
|
MCargoInclude = MCargoInclude and MCargoType
|
|
end
|
|
|
|
if self.Filter.CargoPrefixes then
|
|
local MCargoPrefix = false
|
|
for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do
|
|
self:T3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } )
|
|
if string.find( MCargo.Name, CargoPrefix, 1 ) then
|
|
MCargoPrefix = true
|
|
end
|
|
end
|
|
self:F( { "Evaluated Prefix", MCargoPrefix } )
|
|
MCargoInclude = MCargoInclude and MCargoPrefix
|
|
end
|
|
end
|
|
|
|
self:T2( MCargoInclude )
|
|
return MCargoInclude
|
|
end
|
|
|
|
--- (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
|
|
|
|
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 )
|
|
end
|
|
end
|
|
end
|
|
|
|
--- (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
|
|
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 } )
|
|
if Cargo.NoDestroy then
|
|
else
|
|
self:Remove( Cargo.Name )
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
do -- SET_ZONE
|
|
|
|
--- @type SET_ZONE
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types.
|
|
--
|
|
-- ## SET_ZONE constructor
|
|
--
|
|
-- Create a new SET_ZONE object with the @{#SET_ZONE.New} method:
|
|
--
|
|
-- * @{#SET_ZONE.New}: Creates a new SET_ZONE object.
|
|
--
|
|
-- ## Add or Remove ZONEs from SET_ZONE
|
|
--
|
|
-- ZONEs can be added and removed using the @{Core.Set#SET_ZONE.AddZonesByName} and @{Core.Set#SET_ZONE.RemoveZonesByName} respectively.
|
|
-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE.
|
|
--
|
|
-- ## SET_ZONE filter criteria
|
|
--
|
|
-- You can set filter criteria to build the collection of zones in SET_ZONE.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
|
--
|
|
-- Once the filter criteria have been set for the SET_ZONE, you can start filtering using:
|
|
--
|
|
-- * @{#SET_ZONE.FilterStart}: Starts the filtering of the zones within the SET_ZONE.
|
|
--
|
|
-- ## SET_ZONE iterators
|
|
--
|
|
-- Once the filters have been defined and the SET_ZONE has been built, you can iterate the SET_ZONE with the available iterator methods.
|
|
-- The iterator methods will walk the SET_ZONE set, and call for each airbase within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_ZONE:
|
|
--
|
|
-- * @{#SET_ZONE.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE.
|
|
--
|
|
-- ===
|
|
-- @field #SET_ZONE SET_ZONE
|
|
SET_ZONE = {
|
|
ClassName = "SET_ZONE",
|
|
Zones = {},
|
|
Filter = {
|
|
Prefixes = nil,
|
|
},
|
|
FilterMeta = {
|
|
},
|
|
}
|
|
|
|
--- Creates a new SET_ZONE object, building a set of zones.
|
|
-- @param #SET_ZONE self
|
|
-- @return #SET_ZONE self
|
|
-- @usage
|
|
-- -- Define a new SET_ZONE Object. The DatabaseSet will contain a reference to all Zones.
|
|
-- DatabaseSet = SET_ZONE:New()
|
|
function SET_ZONE:New()
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add ZONEs by a search name to SET_ZONE.
|
|
-- @param Core.Set#SET_ZONE self
|
|
-- @param #string AddZoneNames A single name or an array of ZONE_BASE names.
|
|
-- @return self
|
|
function SET_ZONE:AddZonesByName( AddZoneNames )
|
|
|
|
local AddZoneNamesArray = (type( AddZoneNames ) == "table") and AddZoneNames or { AddZoneNames }
|
|
|
|
for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do
|
|
self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add ZONEs to SET_ZONE.
|
|
-- @param Core.Set#SET_ZONE self
|
|
-- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object.
|
|
-- @return self
|
|
function SET_ZONE:AddZone( Zone )
|
|
|
|
self:Add( Zone:GetName(), 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 }
|
|
|
|
for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do
|
|
self:Remove( RemoveZoneName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Zone based on the Zone Name.
|
|
-- @param #SET_ZONE self
|
|
-- @param #string ZoneName
|
|
-- @return Core.Zone#ZONE_BASE The found Zone.
|
|
function SET_ZONE:FindZone( ZoneName )
|
|
|
|
local ZoneFound = self.Set[ZoneName]
|
|
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 )
|
|
|
|
local margin = margin or 100
|
|
if self:Count() ~= 0 then
|
|
|
|
local Index = self.Index
|
|
local ZoneFound = nil -- Core.Zone#ZONE_BASE
|
|
|
|
-- Loop until a zone has been found.
|
|
-- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected.
|
|
-- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues!
|
|
local counter = 0
|
|
while (not ZoneFound) or (counter < margin) do
|
|
local ZoneRandom = math.random( 1, #Index )
|
|
ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe()
|
|
counter = counter + 1
|
|
end
|
|
|
|
return ZoneFound
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
--- Set a zone probability.
|
|
-- @param #SET_ZONE self
|
|
-- @param #string ZoneName The name of the zone.
|
|
function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability )
|
|
local Zone = self:FindZone( ZoneName )
|
|
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
|
|
-- @param #string Prefixes The string pattern(s) that need to be contained in the zone name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_ZONE self
|
|
function SET_ZONE:FilterPrefixes( Prefixes )
|
|
if not self.Filter.Prefixes then
|
|
self.Filter.Prefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.Prefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_ZONE self
|
|
-- @return #SET_ZONE self
|
|
function SET_ZONE:FilterStart()
|
|
|
|
if _DATABASE then
|
|
|
|
-- We initialize the first set.
|
|
for ObjectName, Object in pairs( self.Database ) do
|
|
if self:IsIncludeObject( Object ) then
|
|
self:Add( ObjectName, Object )
|
|
else
|
|
self:RemoveZonesByName( ObjectName )
|
|
end
|
|
end
|
|
end
|
|
|
|
self:HandleEvent( EVENTS.NewZone )
|
|
self:HandleEvent( EVENTS.DeleteZone )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Stops the filtering for the defined collection.
|
|
-- @param #SET_ZONE self
|
|
-- @return #SET_ZONE self
|
|
function SET_ZONE:FilterStop()
|
|
|
|
self:UnHandleEvent( EVENTS.NewZone )
|
|
self:UnHandleEvent( EVENTS.DeleteZone )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
|
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
|
-- @param #SET_ZONE self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the AIRBASE
|
|
-- @return #table The AIRBASE
|
|
function SET_ZONE:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_ZONE self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the AIRBASE
|
|
-- @return #table The AIRBASE
|
|
function SET_ZONE:FindInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
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.
|
|
-- @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
|
|
function SET_ZONE:ForEachZone( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Draw all zones in the set on the F10 map.
|
|
-- @param #SET_ZONE self
|
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
|
-- @return #SET_ZONE self
|
|
function SET_ZONE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
|
|
|
for _,_zone in pairs(self.Set) do
|
|
local zone=_zone --Core.Zone#ZONE
|
|
zone:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
--- Private function.
|
|
-- @param #SET_ZONE self
|
|
-- @param Core.Zone#ZONE_BASE MZone
|
|
-- @return #SET_ZONE self
|
|
function SET_ZONE:IsIncludeObject( MZone )
|
|
self:F2( MZone )
|
|
|
|
local MZoneInclude = true
|
|
|
|
if MZone then
|
|
local MZoneName = MZone:GetName()
|
|
|
|
if self.Filter.Prefixes then
|
|
local MZonePrefix = false
|
|
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
|
|
self:T2( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
|
if string.find( MZoneName, ZonePrefix, 1 ) then
|
|
MZonePrefix = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Prefix", MZonePrefix } )
|
|
MZoneInclude = MZoneInclude and MZonePrefix
|
|
end
|
|
end
|
|
|
|
self:T2( MZoneInclude )
|
|
return MZoneInclude
|
|
end
|
|
|
|
--- Handles the OnEventNewZone event for the Set.
|
|
-- @param #SET_ZONE self
|
|
-- @param Core.Event#EVENTDATA EventData
|
|
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 )
|
|
end
|
|
end
|
|
end
|
|
|
|
--- 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
|
|
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 } )
|
|
if Zone.NoDestroy then
|
|
else
|
|
self:Remove( Zone.ZoneName )
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Validate if a coordinate is in one of the zones in the set.
|
|
-- Returns the ZONE object where the coordinate is located.
|
|
-- If zones overlap, the first zone that validates the test is returned.
|
|
-- @param #SET_ZONE self
|
|
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
|
-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location.
|
|
-- @return #nil No zone has been found.
|
|
function SET_ZONE:IsCoordinateInZone( Coordinate )
|
|
|
|
for _, Zone in pairs( self:GetSet() ) do
|
|
local Zone = Zone -- Core.Zone#ZONE_BASE
|
|
if Zone:IsCoordinateInZone( Coordinate ) then
|
|
return Zone
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
end
|
|
|
|
do -- SET_ZONE_GOAL
|
|
|
|
--- @type SET_ZONE_GOAL
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types.
|
|
--
|
|
-- ## SET_ZONE_GOAL constructor
|
|
--
|
|
-- Create a new SET_ZONE_GOAL object with the @{#SET_ZONE_GOAL.New} method:
|
|
--
|
|
-- * @{#SET_ZONE_GOAL.New}: Creates a new SET_ZONE_GOAL object.
|
|
--
|
|
-- ## Add or Remove ZONEs from SET_ZONE_GOAL
|
|
--
|
|
-- ZONEs can be added and removed using the @{Core.Set#SET_ZONE_GOAL.AddZonesByName} and @{Core.Set#SET_ZONE_GOAL.RemoveZonesByName} respectively.
|
|
-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE_GOAL.
|
|
--
|
|
-- ## SET_ZONE_GOAL filter criteria
|
|
--
|
|
-- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL.
|
|
-- Filter criteria are defined by:
|
|
--
|
|
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
|
--
|
|
-- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using:
|
|
--
|
|
-- * @{#SET_ZONE_GOAL.FilterStart}: Starts the filtering of the zones within the SET_ZONE_GOAL.
|
|
--
|
|
-- ## SET_ZONE_GOAL iterators
|
|
--
|
|
-- Once the filters have been defined and the SET_ZONE_GOAL has been built, you can iterate the SET_ZONE_GOAL with the available iterator methods.
|
|
-- The iterator methods will walk the SET_ZONE_GOAL set, and call for each airbase within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_ZONE_GOAL:
|
|
--
|
|
-- * @{#SET_ZONE_GOAL.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE_GOAL.
|
|
--
|
|
-- ===
|
|
-- @field #SET_ZONE_GOAL SET_ZONE_GOAL
|
|
SET_ZONE_GOAL = {
|
|
ClassName = "SET_ZONE_GOAL",
|
|
Zones = {},
|
|
Filter = {
|
|
Prefixes = nil,
|
|
},
|
|
FilterMeta = {
|
|
},
|
|
}
|
|
|
|
--- Creates a new SET_ZONE_GOAL object, building a set of zones.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @return #SET_ZONE_GOAL self
|
|
-- @usage
|
|
-- -- Define a new SET_ZONE_GOAL Object. The DatabaseSet will contain a reference to all Zones.
|
|
-- DatabaseSet = SET_ZONE_GOAL:New()
|
|
function SET_ZONE_GOAL:New()
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES_GOAL ) )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Add ZONEs to SET_ZONE_GOAL.
|
|
-- @param Core.Set#SET_ZONE_GOAL self
|
|
-- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object.
|
|
-- @return self
|
|
function SET_ZONE_GOAL:AddZone( Zone )
|
|
|
|
self:Add( Zone:GetName(), Zone )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove ZONEs from SET_ZONE_GOAL.
|
|
-- @param Core.Set#SET_ZONE_GOAL self
|
|
-- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names.
|
|
-- @return self
|
|
function SET_ZONE_GOAL:RemoveZonesByName( RemoveZoneNames )
|
|
|
|
local RemoveZoneNamesArray = (type( RemoveZoneNames ) == "table") and RemoveZoneNames or { RemoveZoneNames }
|
|
|
|
for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do
|
|
self:Remove( RemoveZoneName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Zone based on the Zone Name.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param #string ZoneName
|
|
-- @return Core.Zone#ZONE_BASE The found Zone.
|
|
function SET_ZONE_GOAL:FindZone( ZoneName )
|
|
|
|
local ZoneFound = self.Set[ZoneName]
|
|
return ZoneFound
|
|
end
|
|
|
|
--- Get a random zone from the set.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @return Core.Zone#ZONE_BASE The random Zone.
|
|
-- @return #nil if no zone in the collection.
|
|
function SET_ZONE_GOAL:GetRandomZone()
|
|
|
|
if self:Count() ~= 0 then
|
|
|
|
local Index = self.Index
|
|
local ZoneFound = nil -- Core.Zone#ZONE_BASE
|
|
|
|
-- Loop until a zone has been found.
|
|
-- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected.
|
|
-- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues!
|
|
while not ZoneFound do
|
|
local ZoneRandom = math.random( 1, #Index )
|
|
ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe()
|
|
end
|
|
|
|
return ZoneFound
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
--- Set a zone probability.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param #string ZoneName The name of the zone.
|
|
function SET_ZONE_GOAL:SetZoneProbability( ZoneName, ZoneProbability )
|
|
local Zone = self:FindZone( ZoneName )
|
|
Zone:SetZoneProbability( ZoneProbability )
|
|
end
|
|
|
|
--- Builds a set of 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
|
|
-- @param #string Prefixes The string pattern(s) that needs to be contained in the zone name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_ZONE_GOAL self
|
|
function SET_ZONE_GOAL:FilterPrefixes( Prefixes )
|
|
if not self.Filter.Prefixes then
|
|
self.Filter.Prefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.Prefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Starts the filtering.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @return #SET_ZONE_GOAL self
|
|
function SET_ZONE_GOAL:FilterStart()
|
|
|
|
if _DATABASE then
|
|
|
|
-- We initialize the first set.
|
|
for ObjectName, Object in pairs( self.Database ) do
|
|
if self:IsIncludeObject( Object ) then
|
|
self:Add( ObjectName, Object )
|
|
else
|
|
self:RemoveZonesByName( ObjectName )
|
|
end
|
|
end
|
|
end
|
|
|
|
self:HandleEvent( EVENTS.NewZoneGoal )
|
|
self:HandleEvent( EVENTS.DeleteZoneGoal )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Stops the filtering for the defined collection.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @return #SET_ZONE_GOAL self
|
|
function SET_ZONE_GOAL:FilterStop()
|
|
|
|
self:UnHandleEvent( EVENTS.NewZoneGoal )
|
|
self:UnHandleEvent( EVENTS.DeleteZoneGoal )
|
|
|
|
return self
|
|
end
|
|
|
|
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
|
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the AIRBASE
|
|
-- @return #table The AIRBASE
|
|
function SET_ZONE_GOAL:AddInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Handles the Database to check on any event that Object exists in the Database.
|
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param Core.Event#EVENTDATA Event
|
|
-- @return #string The name of the AIRBASE
|
|
-- @return #table The AIRBASE
|
|
function SET_ZONE_GOAL:FindInDatabase( Event )
|
|
self:F3( { Event } )
|
|
|
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
|
end
|
|
|
|
--- Iterate the SET_ZONE_GOAL and call an iterator function for each ZONE, providing the ZONE and optional parameters.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE_GOAL. The function needs to accept a AIRBASE parameter.
|
|
-- @return #SET_ZONE_GOAL self
|
|
function SET_ZONE_GOAL:ForEachZone( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
|
|
return self
|
|
end
|
|
|
|
---
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param Core.Zone#ZONE_BASE MZone
|
|
-- @return #SET_ZONE_GOAL self
|
|
function SET_ZONE_GOAL:IsIncludeObject( MZone )
|
|
self:F2( MZone )
|
|
|
|
local MZoneInclude = true
|
|
|
|
if MZone then
|
|
local MZoneName = MZone:GetName()
|
|
|
|
if self.Filter.Prefixes then
|
|
local MZonePrefix = false
|
|
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
|
|
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
|
if string.find( MZoneName, ZonePrefix, 1 ) then
|
|
MZonePrefix = true
|
|
end
|
|
end
|
|
self:T( { "Evaluated Prefix", MZonePrefix } )
|
|
MZoneInclude = MZoneInclude and MZonePrefix
|
|
end
|
|
end
|
|
|
|
self:T2( MZoneInclude )
|
|
return MZoneInclude
|
|
end
|
|
|
|
--- Handles the OnEventNewZone event for the Set.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param Core.Event#EVENTDATA EventData
|
|
function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData )
|
|
|
|
-- 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:T( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } )
|
|
self:Add( EventData.ZoneGoal.ZoneName, EventData.ZoneGoal )
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Handles the OnDead or OnCrash event for alive units set.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param Core.Event#EVENTDATA EventData
|
|
function SET_ZONE_GOAL:OnEventDeleteZoneGoal( EventData ) -- R2.1
|
|
self:F3( { EventData } )
|
|
|
|
if EventData.ZoneGoal then
|
|
local Zone = _DATABASE:FindZone( EventData.ZoneGoal.ZoneName )
|
|
if Zone and Zone.ZoneName then
|
|
|
|
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
|
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
|
-- And this is a problem because it will remove all entries from the SET_ZONE_GOALs.
|
|
-- To prevent this from happening, the Zone object has a flag NoDestroy.
|
|
-- When true, the SET_ZONE_GOAL won't Remove the Zone object from the set.
|
|
-- This flag is switched off after the event handlers have been called in the EVENT class.
|
|
self:F( { ZoneNoDestroy = Zone.NoDestroy } )
|
|
if Zone.NoDestroy then
|
|
else
|
|
self:Remove( Zone.ZoneName )
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Validate if a coordinate is in one of the zones in the set.
|
|
-- Returns the ZONE object where the coordiante is located.
|
|
-- If zones overlap, the first zone that validates the test is returned.
|
|
-- @param #SET_ZONE_GOAL self
|
|
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
|
-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location.
|
|
-- @return #nil No zone has been found.
|
|
function SET_ZONE_GOAL:IsCoordinateInZone( Coordinate )
|
|
|
|
for _, Zone in pairs( self:GetSet() ) do
|
|
local Zone = Zone -- Core.Zone#ZONE_BASE
|
|
if Zone:IsCoordinateInZone( Coordinate ) then
|
|
return Zone
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
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 parameters 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 self
|
|
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
|
|
|
|
--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index.
|
|
-- @param #SET_BASE self
|
|
-- @param Ops.OpsGroup#OPSGROUP Object Ops group
|
|
-- @return Core.Base#BASE The added BASE Object.
|
|
function SET_OPSGROUP:AddObject(Object)
|
|
self:Add(Object.groupname, 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
|
|
|
|
do -- SET_SCENERY
|
|
|
|
---
|
|
-- @type SET_SCENERY
|
|
-- @extends Core.Set#SET_BASE
|
|
|
|
--- Mission designers can use the SET_SCENERY class to build sets of scenery belonging to certain:
|
|
--
|
|
-- * Zone Sets
|
|
--
|
|
-- ## SET_SCENERY constructor
|
|
--
|
|
-- Create a new SET_SCENERY object with the @{#SET_SCENERY.New} method:
|
|
--
|
|
-- * @{#SET_SCENERY.New}: Creates a new SET_SCENERY object.
|
|
--
|
|
-- ## Add or Remove SCENERY(s) from SET_SCENERY
|
|
--
|
|
-- SCENERYs can be added and removed using the @{Core.Set#SET_SCENERY.AddSceneryByName} and @{Core.Set#SET_SCENERY.RemoveSceneryByName} respectively.
|
|
-- These methods take a single SCENERY name or an array of SCENERY names to be added or removed from SET_SCENERY.
|
|
--
|
|
-- ## SET_SCENERY filter criteria
|
|
--
|
|
-- N/A at the moment
|
|
--
|
|
-- ## SET_SCENERY iterators
|
|
--
|
|
-- Once the filters have been defined and the SET_SCENERY has been built, you can iterate the SET_SCENERY with the available iterator methods.
|
|
-- The iterator methods will walk the SET_SCENERY set, and call for each element within the set a function that you provide.
|
|
-- The following iterator methods are currently available within the SET_SCENERY:
|
|
--
|
|
-- * @{#SET_SCENERY.ForEachScenery}: Calls a function for each alive object it finds within the SET_SCENERY.
|
|
--
|
|
-- ## SET_SCENERY atomic methods
|
|
--
|
|
-- N/A at the moment
|
|
--
|
|
-- ===
|
|
-- @field #SET_SCENERY SET_SCENERY
|
|
SET_SCENERY = {
|
|
ClassName = "SET_SCENERY",
|
|
Scenerys = {},
|
|
Filter = {
|
|
SceneryPrefixes = nil,
|
|
Zones = nil,
|
|
},
|
|
}
|
|
|
|
--- Creates a new SET_SCENERY object. Scenery is **not** auto-registered in the Moose database, there are too many objects on each map. Hence we need to find them first. For this we are using a SET_ZONE.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #SET_ZONE ZoneSet SET_ZONE of ZONE objects as created by right-clicks on the map in the mission editor, choosing "assign as...". Rename the zones for grouping purposes, e.g. all sections of a bridge as "Bridge-1" to "Bridge-3".
|
|
-- @return #SET_SCENERY
|
|
-- @usage
|
|
-- -- Define a new SET_SCENERY Object. This Object will contain a reference to all added Scenery Objects.
|
|
-- ZoneSet = SET_ZONE:New():FilterPrefixes("Bridge"):FilterOnce()
|
|
-- mysceneryset = SET_SCENERY:New(ZoneSet)
|
|
function SET_SCENERY:New(ZoneSet)
|
|
|
|
local zoneset = {}
|
|
-- Inherits from BASE
|
|
local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY
|
|
|
|
local zonenames = {}
|
|
|
|
if ZoneSet then
|
|
for _,_zone in pairs(ZoneSet.Set) do
|
|
--self:I("Zone type handed: "..tostring(_zone.ClassName))
|
|
table.insert(zonenames,_zone:GetName())
|
|
end
|
|
self:AddSceneryByName(zonenames)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Creates a new SET_SCENERY object. Scenery is **not** auto-registered in the Moose database, there are too many objects on each map. Hence we need to find them first. For this we scan the zone.
|
|
-- @param #SET_SCENERY self
|
|
-- @param Core.Zone#ZONE Zone The zone to be scanned. Can be a ZONE_RADIUS (round) or a ZONE_POLYGON (e.g. Quad-Point)
|
|
-- @return #SET_SCENERY
|
|
function SET_SCENERY:NewFromZone(Zone)
|
|
local zone = Zone -- Core.Zone#ZONE_POLYGON
|
|
if type(Zone) == "string" then
|
|
zone = ZONE:FindByName(Zone)
|
|
end
|
|
zone:Scan({Object.Category.SCENERY})
|
|
return zone:GetScannedSetScenery()
|
|
end
|
|
|
|
--- Add SCENERY(s) to SET_SCENERY.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #string AddScenery A single SCENERY.
|
|
-- @return #SET_SCENERY self
|
|
function SET_SCENERY:AddScenery( AddScenery )
|
|
self:F2( AddScenery:GetName() )
|
|
|
|
self:Add( AddScenery:GetName(), AddScenery )
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
--- Add SCENERY(s) to SET_SCENERY.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #string AddSceneryNames A single name or an array of SCENERY zone names.
|
|
-- @return #SET_SCENERY self
|
|
function SET_SCENERY:AddSceneryByName( AddSceneryNames )
|
|
|
|
local AddSceneryNamesArray = ( type( AddSceneryNames ) == "table" ) and AddSceneryNames or { AddSceneryNames }
|
|
|
|
self:T( AddSceneryNamesArray )
|
|
for AddSceneryID, AddSceneryName in pairs( AddSceneryNamesArray ) do
|
|
self:Add( AddSceneryName, SCENERY:FindByZoneName( AddSceneryName ) )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Remove SCENERY(s) from SET_SCENERY.
|
|
-- @param Core.Set#SET_SCENERY self
|
|
-- @param Wrapper.Scenery#SCENERY RemoveSceneryNames A single name or an array of SCENERY zone names.
|
|
-- @return self
|
|
function SET_SCENERY:RemoveSceneryByName( RemoveSceneryNames )
|
|
|
|
local RemoveSceneryNamesArray = ( type( RemoveSceneryNames ) == "table" ) and RemoveSceneryNames or { RemoveSceneryNames }
|
|
|
|
for RemoveSceneryID, RemoveSceneryName in pairs( RemoveSceneryNamesArray ) do
|
|
self:Remove( RemoveSceneryName )
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Finds a Scenery in the SET, based on the Scenery Name.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #string SceneryName
|
|
-- @return Wrapper.Scenery#SCENERY The found Scenery.
|
|
function SET_SCENERY:FindScenery( SceneryName )
|
|
local SceneryFound = self.Set[SceneryName]
|
|
return SceneryFound
|
|
end
|
|
|
|
--- Builds a set of scenery objects in zones.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
|
-- @return #SET_SCENERY self
|
|
function SET_SCENERY: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 SCENERYs that contain the given string in their name.
|
|
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all scenery that **contain** the string.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #string Prefixes The string pattern(s) that need to be contained in the scenery name. Can also be passed as a `#table` of strings.
|
|
-- @return #SET_SCENERY self
|
|
function SET_SCENERY:FilterPrefixes( Prefixes )
|
|
if not self.Filter.SceneryPrefixes then
|
|
self.Filter.SceneryPrefixes = {}
|
|
end
|
|
if type( Prefixes ) ~= "table" then
|
|
Prefixes = { Prefixes }
|
|
end
|
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
|
self.Filter.SceneryPrefixes[Prefix] = Prefix
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Iterate the SET_SCENERY and count how many SCENERYSs are alive.
|
|
-- @param #SET_SCENERY self
|
|
-- @return #number The number of SCENERYSs alive.
|
|
function SET_SCENERY:CountAlive()
|
|
|
|
local Set = self:GetSet()
|
|
|
|
local CountU = 0
|
|
for UnitID, UnitData in pairs(Set) do
|
|
if UnitData and UnitData:IsAlive() then
|
|
CountU = CountU + 1
|
|
end
|
|
|
|
end
|
|
|
|
return CountU
|
|
end
|
|
|
|
--- Iterate the SET_SCENERY and call an iterator function for each **alive** SCENERY, providing the SCENERY and optional parameters.
|
|
-- @param #SET_SCENERY self
|
|
-- @param #function IteratorFunction The function that will be called when there is an alive SCENERY in the SET_SCENERY. The function needs to accept a SCENERY parameter.
|
|
-- @return #SET_SCENERY self
|
|
function SET_SCENERY:ForEachScenery( IteratorFunction, ... )
|
|
self:F2( arg )
|
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
|
return self
|
|
end
|
|
|
|
--- Get the center coordinate of the SET_SCENERY.
|
|
-- @param #SET_SCENERY self
|
|
-- @return Core.Point#COORDINATE The center coordinate of all the objects in the set.
|
|
function SET_SCENERY:GetCoordinate()
|
|
|
|
local Coordinate = self:GetRandom():GetCoordinate()
|
|
|
|
local x1 = Coordinate.x
|
|
local x2 = Coordinate.x
|
|
local y1 = Coordinate.y
|
|
local y2 = Coordinate.y
|
|
local z1 = Coordinate.z
|
|
local z2 = Coordinate.z
|
|
|
|
for SceneryName, SceneryData in pairs( self:GetSet() ) do
|
|
|
|
local Scenery = SceneryData -- Wrapper.Scenery#SCENERY
|
|
local Coordinate = Scenery: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
|
|
|
|
end
|
|
|
|
Coordinate.x = ( x2 - x1 ) / 2 + x1
|
|
Coordinate.y = ( y2 - y1 ) / 2 + y1
|
|
Coordinate.z = ( z2 - z1 ) / 2 + z1
|
|
|
|
self:F( { Coordinate = Coordinate } )
|
|
return Coordinate
|
|
|
|
end
|
|
|
|
---
|
|
-- @param #SET_SCENERY self
|
|
-- @param Wrapper.Scenery#SCENERY MScenery
|
|
-- @return #SET_SCENERY self
|
|
function SET_SCENERY:IsIncludeObject( MScenery )
|
|
self:F2( MScenery )
|
|
return true
|
|
end
|
|
end
|