FlightControl c1191e286a Added the new event S_EVENT_REMOVE_UNIT to trigger the removal of units (from groups also) when a :Destroy(false) or :Destroy() is called for a UNIT object.
The units are then removed from each SET that is subscribed to a set of UNIT objects or GROUP objects! Also the DATABASE is correctly managing this new removal method.
This to prevent the DATABASE getting corrupted with dead units, which were removed with :Destroy(), but which weren't cleaned from the database.
2018-09-11 09:00:30 +02:00

1843 lines
54 KiB
Lua

--- **Wrapper** -- GROUP wraps the DCS Class Group objects.
--
-- ===
--
-- The @{#GROUP} class is a wrapper class to handle the DCS Group objects:
--
-- * Support all DCS Group APIs.
-- * Enhance with Group specific APIs not in the DCS Group API set.
-- * Handle local Group Controller.
-- * Manage the "state" of the DCS Group.
--
-- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).**
--
-- See the detailed documentation on the GROUP class.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ### Contributions:
--
-- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff().
--
-- ===
--
-- @module Wrapper.Group
-- @image Wrapper_Group.JPG
--- @type GROUP
-- @extends Wrapper.Controllable#CONTROLLABLE
-- @field #string GroupName The name of the group.
--- Wrapper class of the DCS world Group object.
--
-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class).
--
-- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the DCS Group or the DCS GroupName.
--
-- Another thing to know is that GROUP objects do not "contain" the DCS Group object.
-- The GROUP methods will reference the DCS Group object by name when it is needed during API execution.
-- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file.
--
-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance:
--
-- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object.
-- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name.
--
-- ## GROUP task methods
--
-- A GROUP is a @{Wrapper.Controllable}. See the @{Wrapper.Controllable} task methods section for a description of the task methods.
--
-- ### Obtain the mission from group templates
--
-- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another:
--
-- * @{Wrapper.Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template.
--
-- ## GROUP Command methods
--
-- A GROUP is a @{Wrapper.Controllable}. See the @{Wrapper.Controllable} command methods section for a description of the command methods.
--
-- ## GROUP option methods
--
-- A GROUP is a @{Wrapper.Controllable}. See the @{Wrapper.Controllable} option methods section for a description of the option methods.
--
-- ## GROUP Zone validation methods
--
-- The group can be validated whether it is completely, partly or not within a @{Zone}.
-- Use the following Zone validation methods on the group:
--
-- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}.
-- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}.
-- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}.
--
-- The zone can be of any @{Zone} class derived from @{Core.Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on.
--
-- ## GROUP AI methods
--
-- A GROUP has AI methods to control the AI activation.
--
-- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off.
-- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On.
-- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off.
--
-- @field #GROUP GROUP
GROUP = {
ClassName = "GROUP",
}
--- Enumerator for location at airbases
-- @type GROUP.Takeoff
GROUP.Takeoff = {
Air = 1,
Runway = 2,
Hot = 3,
Cold = 4,
}
GROUPTEMPLATE = {}
GROUPTEMPLATE.Takeoff = {
[GROUP.Takeoff.Air] = { "Turning Point", "Turning Point" },
[GROUP.Takeoff.Runway] = { "TakeOff", "From Runway" },
[GROUP.Takeoff.Hot] = { "TakeOffParkingHot", "From Parking Area Hot" },
[GROUP.Takeoff.Cold] = { "TakeOffParking", "From Parking Area" }
}
--- Create a new GROUP from a given GroupTemplate as a parameter.
-- Note that the GroupTemplate is NOT spawned into the mission.
-- It is merely added to the @{Core.Database}.
-- @param #GROUP self
-- @param #table GroupTemplate The GroupTemplate Structure exactly as defined within the mission editor.
-- @param DCS#coalition.side CoalitionSide The coalition.side of the group.
-- @param DCS#Group.Category CategoryID The Group.Category of the group.
-- @param DCS#country.id CountryID the country.id of the group.
-- @return #GROUP self
function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID )
local GroupName = GroupTemplate.name
_DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self.GroupName = GroupName
if not _DATABASE.GROUPS[GroupName] then
_DATABASE.GROUPS[GroupName] = self
end
self:SetEventPriority( 4 )
return self
end
--- Create a new GROUP from an existing Group in the Mission.
-- @param #GROUP self
-- @param #string GroupName The Group name
-- @return #GROUP self
function GROUP:Register( GroupName )
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) -- #GROUP
self.GroupName = GroupName
self:SetEventPriority( 4 )
return self
end
-- Reference methods.
--- Find the GROUP wrapper class instance using the DCS Group.
-- @param #GROUP self
-- @param DCS#Group DCSGroup The DCS Group.
-- @return #GROUP The GROUP.
function GROUP:Find( DCSGroup )
local GroupName = DCSGroup:getName() -- Wrapper.Group#GROUP
local GroupFound = _DATABASE:FindGroup( GroupName )
return GroupFound
end
--- Find the created GROUP using the DCS Group Name.
-- @param #GROUP self
-- @param #string GroupName The DCS Group Name.
-- @return #GROUP The GROUP.
function GROUP:FindByName( GroupName )
local GroupFound = _DATABASE:FindGroup( GroupName )
return GroupFound
end
-- DCS Group methods support.
--- Returns the DCS Group.
-- @param #GROUP self
-- @return DCS#Group The DCS Group.
function GROUP:GetDCSObject()
local DCSGroup = Group.getByName( self.GroupName )
if DCSGroup then
return DCSGroup
end
return nil
end
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionablePosition = DCSPositionable:getUnits()[1]:getPosition().p
self:T3( PositionablePosition )
return PositionablePosition
end
return nil
end
--- Returns if the Group is alive.
-- The Group must:
--
-- * Exist at run-time.
-- * Has at least one unit.
--
-- When the first @{Wrapper.Unit} of the Group is active, it will return true.
-- If the first @{Wrapper.Unit} of the Group is inactive, it will return false.
--
-- @param #GROUP self
-- @return #boolean true if the Group is alive and active.
-- @return #boolean false if the Group is alive but inactive.
-- @return #nil if the group does not exist anymore.
function GROUP:IsAlive()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject() -- DCS#Group
if DCSGroup then
if DCSGroup:isExist() then
local DCSUnit = DCSGroup:getUnit(1) -- DCS#Unit
if DCSUnit then
local GroupIsAlive = DCSUnit:isActive()
self:T3( GroupIsAlive )
return GroupIsAlive
end
end
end
return nil
end
--- Destroys the DCS Group and all of its DCS Units.
-- Note that this destroy method also can raise a destroy event at run-time.
-- So all event listeners will catch the destroy event of this group for each unit in the group.
-- To raise these events, provide the `GenerateEvent` parameter.
-- @param #GROUP self
-- @param #boolean GenerateEvent true if you want to generate a crash or dead event for each unit.
-- @usage
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
-- Helicopter = GROUP:FindByName( "Helicopter" )
-- Helicopter:Destroy( true )
-- @usage
-- -- Ground unit example: destroy the Tanks and generate a S_EVENT_DEAD for each unit in the Tanks group.
-- Tanks = GROUP:FindByName( "Tanks" )
-- Tanks:Destroy( true )
-- @usage
-- -- Ship unit example: destroy the Ship silently.
-- Ship = GROUP:FindByName( "Ship" )
-- Ship:Destroy( true )
function GROUP:Destroy( GenerateEvent )
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
if GenerateEvent and GenerateEvent == true then
if self:IsAir() then
self:CreateEventCrash( timer.getTime(), UnitData )
else
self:CreateEventDead( timer.getTime(), UnitData )
end
else
self:CreateEventRemove( timer.getTime(), UnitData )
end
end
USERFLAG:New( self:GetName() ):Set( 100 )
DCSGroup:destroy()
DCSGroup = nil
end
return nil
end
--- Returns category of the DCS Group.
-- @param #GROUP self
-- @return DCS#Group.Category The category ID
function GROUP:GetCategory()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCategory = DCSGroup:getCategory()
self:T3( GroupCategory )
return GroupCategory
end
return nil
end
--- Returns the category name of the #GROUP.
-- @param #GROUP self
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
function GROUP:GetCategoryName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local CategoryNames = {
[Group.Category.AIRPLANE] = "Airplane",
[Group.Category.HELICOPTER] = "Helicopter",
[Group.Category.GROUND] = "Ground Unit",
[Group.Category.SHIP] = "Ship",
}
local GroupCategory = DCSGroup:getCategory()
self:T3( GroupCategory )
return CategoryNames[GroupCategory]
end
return nil
end
--- Returns the coalition of the DCS Group.
-- @param #GROUP self
-- @return DCS#coalition.side The coalition side of the DCS Group.
function GROUP:GetCoalition()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCoalition = DCSGroup:getCoalition()
self:T3( GroupCoalition )
return GroupCoalition
end
return nil
end
--- Returns the country of the DCS Group.
-- @param #GROUP self
-- @return DCS#country.id The country identifier or nil if the DCS Group is not existing or alive.
function GROUP:GetCountry()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCountry = DCSGroup:getUnit(1):getCountry()
self:T3( GroupCountry )
return GroupCountry
end
return nil
end
--- Check if at least one (or all) unit(s) has (have) a certain attribute.
-- See [hoggit documentation](https://wiki.hoggitworld.com/view/DCS_func_hasAttribute).
-- @param #GROUP self
-- @param #string attribute The name of the attribute the group is supposed to have. Valid attributes can be found in the "db_attributes.lua" file which is located at in "C:\Program Files\Eagle Dynamics\DCS World\Scripts\Database".
-- @param #boolean all If true, all units of the group must have the attribute in order to return true. Default is only one unit of a heterogenious group needs to have the attribute.
-- @return #boolean Group has this attribute.
function GROUP:HasAttribute(attribute, all)
-- Get all units of the group.
local _units=self:GetUnits()
local _allhave=true
local _onehas=false
for _,_unit in pairs(_units) do
local _unit=_unit --Wrapper.Unit#UNIT
if _unit then
local _hastit=_unit:HasAttribute(attribute)
if _hastit==true then
_onehas=true
else
_allhave=false
end
end
end
if all==true then
return _allhave
else
return _onehas
end
end
--- Returns the maximum speed of the group.
-- If the group is heterogenious and consists of different units, the max speed of the slowest unit is returned.
-- @param #GROUP self
-- @return #number Speed in km/h.
function GROUP:GetSpeedMax()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local Units=self:GetUnits()
local speedmax=nil
for _,unit in pairs(Units) do
local unit=unit --Wrapper.Unit#UNIT
local speed=unit:GetSpeedMax()
if speedmax==nil then
speedmax=speed
elseif speed<speedmax then
speedmax=speed
end
end
return speedmax
end
return nil
end
--- Returns a list of @{Wrapper.Unit} objects of the @{Wrapper.Group}.
-- @param #GROUP self
-- @return #list<Wrapper.Unit#UNIT> The list of @{Wrapper.Unit} objects of the @{Wrapper.Group}.
function GROUP:GetUnits()
self:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnits = DCSGroup:getUnits()
local Units = {}
for Index, UnitData in pairs( DCSUnits ) do
Units[#Units+1] = UNIT:Find( UnitData )
end
self:T3( Units )
return Units
end
return nil
end
--- Returns a list of @{Wrapper.Unit} objects of the @{Wrapper.Group} that are occupied by a player.
-- @param #GROUP self
-- @return #list<Wrapper.Unit#UNIT> The list of player occupied @{Wrapper.Unit} objects of the @{Wrapper.Group}.
function GROUP:GetPlayerUnits()
self:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnits = DCSGroup:getUnits()
local Units = {}
for Index, UnitData in pairs( DCSUnits ) do
local PlayerUnit = UNIT:Find( UnitData )
if PlayerUnit:GetPlayerName() then
Units[#Units+1] = PlayerUnit
end
end
self:T3( Units )
return Units
end
return nil
end
--- Returns the UNIT wrapper class with number UnitNumber.
-- If the underlying DCS Unit does not exist, the method will return nil. .
-- @param #GROUP self
-- @param #number UnitNumber The number of the UNIT wrapper class to be returned.
-- @return Wrapper.Unit#UNIT The UNIT wrapper class.
function GROUP:GetUnit( UnitNumber )
self:F3( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnit = DCSGroup:getUnit( UnitNumber )
local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) )
self:T2( UnitFound )
return UnitFound
end
return nil
end
--- Returns the DCS Unit with number UnitNumber.
-- If the underlying DCS Unit does not exist, the method will return nil. .
-- @param #GROUP self
-- @param #number UnitNumber The number of the DCS Unit to be returned.
-- @return DCS#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber )
self:F3( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnitFound = DCSGroup:getUnit( UnitNumber )
self:T3( DCSUnitFound )
return DCSUnitFound
end
return nil
end
--- Returns current size of the DCS Group.
-- If some of the DCS Units of the DCS Group are destroyed the size of the DCS Group is changed.
-- @param #GROUP self
-- @return #number The DCS Group size.
function GROUP:GetSize()
self:F3( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupSize = DCSGroup:getSize()
if GroupSize then
self:T3( GroupSize )
return GroupSize
else
return 0
end
end
return nil
end
--- Returns the average velocity Vec3 vector.
-- @param Wrapper.Group#GROUP self
-- @return DCS#Vec3 The velocity Vec3 vector
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetVelocityVec3()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup and DCSGroup:isExist() then
local GroupUnits = DCSGroup:getUnits()
local GroupCount = #GroupUnits
local VelocityVec3 = { x = 0, y = 0, z = 0 }
for _, DCSUnit in pairs( GroupUnits ) do
local UnitVelocityVec3 = DCSUnit:getVelocity()
VelocityVec3.x = VelocityVec3.x + UnitVelocityVec3.x
VelocityVec3.y = VelocityVec3.y + UnitVelocityVec3.y
VelocityVec3.z = VelocityVec3.z + UnitVelocityVec3.z
end
VelocityVec3.x = VelocityVec3.x / GroupCount
VelocityVec3.y = VelocityVec3.y / GroupCount
VelocityVec3.z = VelocityVec3.z / GroupCount
return VelocityVec3
end
BASE:E( { "Cannot GetVelocityVec3", Group = self, Alive = self:IsAlive() } )
return nil
end
--- Returns the average group height in meters.
-- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level.
-- @return DCS#Vec3 The height of the group or nil if is not existing or alive.
function GROUP:GetHeight( FromGround )
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupUnits = DCSGroup:getUnits()
local GroupCount = #GroupUnits
local GroupHeight = 0
for _, DCSUnit in pairs( GroupUnits ) do
local GroupPosition = DCSUnit:getPosition()
if FromGround == true then
local LandHeight = land.getHeight( { x = GroupPosition.p.x, y = GroupPosition.p.z } )
GroupHeight = GroupHeight + ( GroupPosition.p.y - LandHeight )
else
GroupHeight = GroupHeight + GroupPosition.p.y
end
end
return GroupHeight / GroupCount
end
return nil
end
---
--- Returns the initial size of the DCS Group.
-- If some of the DCS Units of the DCS Group are destroyed, the initial size of the DCS Group is unchanged.
-- @param #GROUP self
-- @return #number The DCS Group initial size.
function GROUP:GetInitialSize()
self:F3( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupInitialSize = DCSGroup:getInitialSize()
self:T3( GroupInitialSize )
return GroupInitialSize
end
return nil
end
--- Returns the DCS Units of the DCS Group.
-- @param #GROUP self
-- @return #table The DCS Units.
function GROUP:GetDCSUnits()
self:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnits = DCSGroup:getUnits()
self:T3( DCSUnits )
return DCSUnits
end
return nil
end
--- Activates a GROUP.
-- @param #GROUP self
function GROUP:Activate()
self:F2( { self.GroupName } )
trigger.action.activateGroup( self:GetDCSObject() )
return self:GetDCSObject()
end
--- Gets the type name of the group.
-- @param #GROUP self
-- @return #string The type name of the group.
function GROUP:GetTypeName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
self:T3( GroupTypeName )
return( GroupTypeName )
end
return nil
end
--- Gets the player name of the group.
-- @param #GROUP self
-- @return #string The player name of the group.
function GROUP:GetPlayerName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local PlayerName = DCSGroup:getUnit(1):getPlayerName()
self:T3( PlayerName )
return( PlayerName )
end
return nil
end
--- Gets the CallSign of the first DCS Unit of the DCS Group.
-- @param #GROUP self
-- @return #string The CallSign of the first DCS Unit of the DCS Group.
function GROUP:GetCallsign()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCallSign = DCSGroup:getUnit(1):getCallsign()
self:T3( GroupCallSign )
return GroupCallSign
end
BASE:E( { "Cannot GetCallsign", Positionable = self, Alive = self:IsAlive() } )
return nil
end
--- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group.
-- @param #GROUP self
-- @return DCS#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group.
function GROUP:GetVec2()
self:F2( self.GroupName )
local UnitPoint = self:GetUnit(1)
UnitPoint:GetVec2()
local GroupPointVec2 = UnitPoint:GetVec2()
self:T3( GroupPointVec2 )
return GroupPointVec2
end
--- Returns the current Vec3 vector of the first DCS Unit in the GROUP.
-- @param #GROUP self
-- @return DCS#Vec3 Current Vec3 of the first DCS Unit of the GROUP.
function GROUP:GetVec3()
self:F2( self.GroupName )
local GroupVec3 = self:GetUnit(1):GetVec3()
self:T3( GroupVec3 )
return GroupVec3
end
--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.
-- @param #GROUP self
-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP.
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetPointVec2()
self:F2(self.GroupName)
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitPointVec2 = FirstUnit:GetPointVec2()
self:T3(FirstUnitPointVec2)
return FirstUnitPointVec2
end
BASE:E( { "Cannot GetPointVec2", Group = self, Alive = self:IsAlive() } )
return nil
end
--- Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission.
-- @param Wrapper.Group#GROUP self
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
function GROUP:GetCoordinate()
self:F2( self.PositionableName )
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
self:T3(FirstUnitCoordinate)
return FirstUnitCoordinate
end
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
return nil
end
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
-- @param #GROUP self
-- @param #number Radius
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP.
-- @return #nil The GROUP is invalid or empty
-- @usage
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
function GROUP:GetRandomVec3(Radius)
self:F2(self.GroupName)
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius)
self:T3(FirstUnitRandomPointVec3)
return FirstUnitRandomPointVec3
end
BASE:E( { "Cannot GetRandomVec3", Group = self, Alive = self:IsAlive() } )
return nil
end
--- Returns the mean heading of every UNIT in the GROUP in degrees
-- @param #GROUP self
-- @return #number mean heading of the GROUP
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetHeading()
self:F2(self.GroupName)
local GroupSize = self:GetSize()
local HeadingAccumulator = 0
if GroupSize then
for i = 1, GroupSize do
HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading()
end
return math.floor(HeadingAccumulator / GroupSize)
end
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
return nil
end
--- Return the fuel state and unit reference for the unit with the least
-- amount of fuel in the group.
-- @param #GROUP self
-- @return #number The fuel state of the unit with the least amount of fuel
-- @return #Unit reference to #Unit object for further processing
function GROUP:GetFuelMin()
self:F(self.ControllableName)
if not self:GetDCSObject() then
BASE:E( { "Cannot GetFuel", Group = self, Alive = self:IsAlive() } )
return 0
end
local min = 65535 -- some sufficiently large number to init with
local unit = nil
local tmp = nil
for UnitID, UnitData in pairs( self:GetUnits() ) do
tmp = UnitData:GetFuel()
if tmp < min then
min = tmp
unit = UnitData
end
end
return min, unit
end
--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its
-- internal tanks. If there are additional fuel tanks the value may be
-- greater than 1.0.
-- @param #GROUP self
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetFuelAvg()
self:F( self.ControllableName )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local GroupSize = self:GetSize()
local TotalFuel = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local UnitFuel = Unit:GetFuel()
self:F( { Fuel = UnitFuel } )
TotalFuel = TotalFuel + UnitFuel
end
local GroupFuel = TotalFuel / GroupSize
return GroupFuel
end
BASE:E( { "Cannot GetFuel", Group = self, Alive = self:IsAlive() } )
return 0
end
--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
-- @param #GROUP self
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetFuel()
return self:GetFuelAvg()
end
do -- Is Zone methods
--- Returns true if all units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is completely within the @{Core.Zone#ZONE_BASE}
function GROUP:IsCompletelyInZone( Zone )
self:F2( { self.GroupName, Zone } )
if not self:IsAlive() then return false end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
else
return false
end
end
return true
end
--- Returns true if some but NOT ALL units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is partially within the @{Core.Zone#ZONE_BASE}
function GROUP:IsPartlyInZone( Zone )
self:F2( { self.GroupName, Zone } )
local IsOneUnitInZone = false
local IsOneUnitOutsideZone = false
if not self:IsAlive() then return false end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
IsOneUnitInZone = true
else
IsOneUnitOutsideZone = true
end
end
if IsOneUnitInZone and IsOneUnitOutsideZone then
return true
else
return false
end
end
--- Returns true if part or all units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is partially or completely within the @{Core.Zone#ZONE_BASE}.
function GROUP:IsPartlyOrCompletelyInZone( Zone )
return self:IsPartlyInZone(Zone) or self:IsCompletelyInZone(Zone)
end
--- Returns true if none of the group units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is not within the @{Core.Zone#ZONE_BASE}
function GROUP:IsNotInZone( Zone )
self:F2( { self.GroupName, Zone } )
if not self:IsAlive() then return true end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
return false
end
end
return true
end
--- Returns the number of UNITs that are in the @{Zone}
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #number The number of UNITs that are in the @{Zone}
function GROUP:CountInZone( Zone )
self:F2( {self.GroupName, Zone} )
local Count = 0
if not self:IsAlive() then return Count end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
Count = Count + 1
end
end
return Count
end
--- Returns if the group is of an air category.
-- If the group is a helicopter or a plane, then this method will return true, otherwise false.
-- @param #GROUP self
-- @return #boolean Air category evaluation result.
function GROUP:IsAir()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local IsAirResult = DCSGroup:getCategory() == Group.Category.AIRPLANE or DCSGroup:getCategory() == Group.Category.HELICOPTER
self:T3( IsAirResult )
return IsAirResult
end
return nil
end
--- Returns if the DCS Group contains Helicopters.
-- @param #GROUP self
-- @return #boolean true if DCS Group contains Helicopters.
function GROUP:IsHelicopter()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCategory = DCSGroup:getCategory()
self:T2( GroupCategory )
return GroupCategory == Group.Category.HELICOPTER
end
return nil
end
--- Returns if the DCS Group contains AirPlanes.
-- @param #GROUP self
-- @return #boolean true if DCS Group contains AirPlanes.
function GROUP:IsAirPlane()
self:F2()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCategory = DCSGroup:getCategory()
self:T2( GroupCategory )
return GroupCategory == Group.Category.AIRPLANE
end
return nil
end
--- Returns if the DCS Group contains Ground troops.
-- @param #GROUP self
-- @return #boolean true if DCS Group contains Ground troops.
function GROUP:IsGround()
self:F2()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCategory = DCSGroup:getCategory()
self:T2( GroupCategory )
return GroupCategory == Group.Category.GROUND
end
return nil
end
--- Returns if the DCS Group contains Ships.
-- @param #GROUP self
-- @return #boolean true if DCS Group contains Ships.
function GROUP:IsShip()
self:F2()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCategory = DCSGroup:getCategory()
self:T2( GroupCategory )
return GroupCategory == Group.Category.SHIP
end
return nil
end
--- Returns if all units of the group are on the ground or landed.
-- If all units of this group are on the ground, this function will return true, otherwise false.
-- @param #GROUP self
-- @return #boolean All units on the ground result.
function GROUP:AllOnGround()
self:F2()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local AllOnGroundResult = true
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
if UnitData:inAir() then
AllOnGroundResult = false
end
end
self:T3( AllOnGroundResult )
return AllOnGroundResult
end
return nil
end
end
do -- AI methods
--- Turns the AI On or Off for the GROUP.
-- @param #GROUP self
-- @param #boolean AIOnOff The value true turns the AI On, the value false turns the AI Off.
-- @return #GROUP The GROUP.
function GROUP:SetAIOnOff( AIOnOff )
local DCSGroup = self:GetDCSObject() -- DCS#Group
if DCSGroup then
local DCSController = DCSGroup:getController() -- DCS#Controller
if DCSController then
DCSController:setOnOff( AIOnOff )
return self
end
end
return nil
end
--- Turns the AI On for the GROUP.
-- @param #GROUP self
-- @return #GROUP The GROUP.
function GROUP:SetAIOn()
return self:SetAIOnOff( true )
end
--- Turns the AI Off for the GROUP.
-- @param #GROUP self
-- @return #GROUP The GROUP.
function GROUP:SetAIOff()
return self:SetAIOnOff( false )
end
end
--- Returns the current maximum velocity of the group.
-- Each unit within the group gets evaluated, and the maximum velocity (= the unit which is going the fastest) is returned.
-- @param #GROUP self
-- @return #number Maximum velocity found.
function GROUP:GetMaxVelocity()
self:F2()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupVelocityMax = 0
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
local UnitVelocityVec3 = UnitData:getVelocity()
local UnitVelocity = math.abs( UnitVelocityVec3.x ) + math.abs( UnitVelocityVec3.y ) + math.abs( UnitVelocityVec3.z )
if UnitVelocity > GroupVelocityMax then
GroupVelocityMax = UnitVelocity
end
end
return GroupVelocityMax
end
return nil
end
--- Returns the current minimum height of the group.
-- Each unit within the group gets evaluated, and the minimum height (= the unit which is the lowest elevated) is returned.
-- @param #GROUP self
-- @return #number Minimum height found.
function GROUP:GetMinHeight()
self:F2()
end
--- Returns the current maximum height of the group.
-- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned.
-- @param #GROUP self
-- @return #number Maximum height found.
function GROUP:GetMaxHeight()
self:F2()
end
-- RESPAWNING
--- Returns the group template from the @{DATABASE} (_DATABASE object).
-- @param #GROUP self
-- @return #table
function GROUP:GetTemplate()
local GroupName = self:GetName()
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
end
--- Returns the group template route.points[] (the waypoints) from the @{DATABASE} (_DATABASE object).
-- @param #GROUP self
-- @return #table
function GROUP:GetTemplateRoutePoints()
local GroupName = self:GetName()
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ).route.points )
end
--- Sets the controlled status in a Template.
-- @param #GROUP self
-- @param #boolean Controlled true is controlled, false is uncontrolled.
-- @return #table
function GROUP:SetTemplateControlled( Template, Controlled )
Template.uncontrolled = not Controlled
return Template
end
--- Sets the CountryID of the group in a Template.
-- @param #GROUP self
-- @param DCS#country.id CountryID The country ID.
-- @return #table
function GROUP:SetTemplateCountry( Template, CountryID )
Template.CountryID = CountryID
return Template
end
--- Sets the CoalitionID of the group in a Template.
-- @param #GROUP self
-- @param DCS#coalition.side CoalitionID The coalition ID.
-- @return #table
function GROUP:SetTemplateCoalition( Template, CoalitionID )
Template.CoalitionID = CoalitionID
return Template
end
--- Set the heading for the units in degrees within the respawned group.
-- @param #GROUP self
-- @param #number Heading The heading in meters.
-- @return #GROUP self
function GROUP:InitHeading( Heading )
self.InitRespawnHeading = Heading
return self
end
--- Set the height for the units in meters for the respawned group. (This is applicable for air units).
-- @param #GROUP self
-- @param #number Height The height in meters.
-- @return #GROUP self
function GROUP:InitHeight( Height )
self.InitRespawnHeight = Height
return self
end
--- Set the respawn @{Zone} for the respawned group.
-- @param #GROUP self
-- @param Core.Zone#ZONE Zone The zone in meters.
-- @return #GROUP self
function GROUP:InitZone( Zone )
self.InitRespawnZone = Zone
return self
end
--- Randomize the positions of the units of the respawned group within the @{Zone}.
-- When a Respawn happens, the units of the group will be placed at random positions within the Zone (selected).
-- @param #GROUP self
-- @param #boolean PositionZone true will randomize the positions within the Zone.
-- @return #GROUP self
function GROUP:InitRandomizePositionZone( PositionZone )
self.InitRespawnRandomizePositionZone = PositionZone
self.InitRespawnRandomizePositionInner = nil
self.InitRespawnRandomizePositionOuter = nil
return self
end
--- Randomize the positions of the units of the respawned group in a circle band.
-- When a Respawn happens, the units of the group will be positioned at random places within the Outer and Inner radius.
-- Thus, a band is created around the respawn location where the units will be placed at random positions.
-- @param #GROUP self
-- @param #boolean OuterRadius Outer band in meters from the center.
-- @param #boolean InnerRadius Inner band in meters from the center.
-- @return #GROUP self
function GROUP:InitRandomizePositionRadius( OuterRadius, InnerRadius )
self.InitRespawnRandomizePositionZone = nil
self.InitRespawnRandomizePositionOuter = OuterRadius
self.InitRespawnRandomizePositionInner = InnerRadius
return self
end
--- Respawn the @{Wrapper.Group} at a @{Point}.
-- The method will setup the new group template according the Init(Respawn) settings provided for the group.
-- These settings can be provided by calling the relevant Init...() methods of the Group.
--
-- - @{#GROUP.InitHeading}: Set the heading for the units in degrees within the respawned group.
-- - @{#GROUP.InitHeight}: Set the height for the units in meters for the respawned group. (This is applicable for air units).
-- - @{#GROUP.InitRandomizeHeading}: Randomize the headings for the units within the respawned group.
-- - @{#GROUP.InitZone}: Set the respawn @{Zone} for the respawned group.
-- - @{#GROUP.InitRandomizeZones}: Randomize the respawn @{Zone} between one of the @{Zone}s given for the respawned group.
-- - @{#GROUP.InitRandomizePositionZone}: Randomize the positions of the units of the respawned group within the @{Zone}.
-- - @{#GROUP.InitRandomizePositionRadius}: Randomize the positions of the units of the respawned group in a circle band.
-- - @{#GROUP.InitRandomizeTemplates}: Randomize the Template for the respawned group.
--
--
-- Notes:
--
-- - When InitZone or InitRandomizeZones is not used, the position of the respawned group will be its current position.
-- - The current alive group will always be destroyed and respawned using the template definition.
--
-- @param Wrapper.Group#GROUP self
-- @param #table Template (optional) The template of the Group retrieved with GROUP:GetTemplate(). If the template is not provided, the template will be retrieved of the group itself.
function GROUP:Respawn( Template, Reset )
if not Template then
Template = self:GetTemplate()
end
if self:IsAlive() then
local Zone = self.InitRespawnZone -- Core.Zone#ZONE
local Vec3 = Zone and Zone:GetVec3() or self:GetVec3()
local From = { x = Template.x, y = Template.y }
Template.x = Vec3.x
Template.y = Vec3.z
--Template.x = nil
--Template.y = nil
self:F( #Template.units )
if Reset == true then
for UnitID, UnitData in pairs( self:GetUnits() ) do
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
self:F( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
self:F( "Alive" )
local GroupUnitVec3 = GroupUnit:GetVec3()
if Zone then
if self.InitRespawnRandomizePositionZone then
GroupUnitVec3 = Zone:GetRandomVec3()
else
if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then
GroupUnitVec3 = POINT_VEC3:NewFromVec2( From ):GetRandomPointVec3InRadius( self.InitRespawnRandomizePositionsOuter, self.InitRespawnRandomizePositionsInner )
else
GroupUnitVec3 = Zone:GetVec3()
end
end
end
Template.units[UnitID].alt = self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y
Template.units[UnitID].x = ( Template.units[UnitID].x - From.x ) + GroupUnitVec3.x -- Keep the original x position of the template and translate to the new position.
Template.units[UnitID].y = ( Template.units[UnitID].y - From.y ) + GroupUnitVec3.z -- Keep the original z position of the template and translate to the new position.
Template.units[UnitID].heading = self.InitRespawnHeading and self.InitRespawnHeading or GroupUnit:GetHeading()
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
end
end
else
for UnitID, TemplateUnitData in pairs( Template.units ) do
self:F( "Reset" )
local GroupUnitVec3 = { x = TemplateUnitData.x, y = TemplateUnitData.alt, z = TemplateUnitData.y }
if Zone then
if self.InitRespawnRandomizePositionZone then
GroupUnitVec3 = Zone:GetRandomVec3()
else
if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then
GroupUnitVec3 = POINT_VEC3:NewFromVec2( From ):GetRandomPointVec3InRadius( self.InitRespawnRandomizePositionsOuter, self.InitRespawnRandomizePositionsInner )
else
GroupUnitVec3 = Zone:GetVec3()
end
end
end
Template.units[UnitID].alt = self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y
Template.units[UnitID].x = ( Template.units[UnitID].x - From.x ) + GroupUnitVec3.x -- Keep the original x position of the template and translate to the new position.
Template.units[UnitID].y = ( Template.units[UnitID].y - From.y ) + GroupUnitVec3.z -- Keep the original z position of the template and translate to the new position.
Template.units[UnitID].heading = self.InitRespawnHeading and self.InitRespawnHeading or TemplateUnitData.heading
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
end
end
end
self:Destroy()
_DATABASE:Spawn( Template )
self:ResetEvents()
return self
end
--- Respawn a group at an airbase.
-- Note that the group has to be on parking spots at the airbase already in order for this to work.
-- So each unit of the group is respawned at exactly the same parking spot as it currently occupies.
-- @param Wrapper.Group#GROUP self
-- @param #table SpawnTemplate (Optional) The spawn template for the group. If no template is given it is exacted from the group.
-- @param Core.Spawn#SPAWN.Takeoff Takeoff (Optional) Takeoff type. Sould be either SPAWN.Takeoff.Cold or SPAWN.Takeoff.Hot. Default is SPAWN.Takeoff.Hot.
-- @param #boolean Uncontrolled (Optional) If true, spawn in uncontrolled state.
-- @return Wrapper.Group#GROUP Group spawned at airbase or nil if group could not be spawned.
function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- R2.4
self:F2( { SpawnTemplate, Takeoff, Uncontrolled} )
-- Get closest airbase. Should be the one we are currently on.
local airbase=self:GetCoordinate():GetClosestAirbase()
if airbase then
self:F2("Closest airbase = "..airbase:GetName())
else
self:E("ERROR: could not find closest airbase!")
return nil
end
-- Takeoff type. Default hot.
Takeoff = Takeoff or SPAWN.Takeoff.Hot
-- Coordinate of the airbase.
local AirbaseCoord=airbase:GetCoordinate()
-- Spawn template.
SpawnTemplate = SpawnTemplate or self:GetTemplate()
if SpawnTemplate then
local SpawnPoint = SpawnTemplate.route.points[1]
-- These are only for ships.
SpawnPoint.linkUnit = nil
SpawnPoint.helipadId = nil
SpawnPoint.airdromeId = nil
-- Aibase id and category.
local AirbaseID = airbase:GetID()
local AirbaseCategory = airbase:GetDesc().category
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
SpawnPoint.linkUnit = AirbaseID
SpawnPoint.helipadId = AirbaseID
elseif AirbaseCategory == Airbase.Category.AIRDROME then
SpawnPoint.airdromeId = AirbaseID
end
SpawnPoint.alt = AirbaseCoord:GetLandHeight()
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
-- Get the units of the group.
local units=self:GetUnits()
for UnitID,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
-- Get closest parking spot of current unit. Note that we look for occupied spots since the unit is currently sitting on it!
local Parkingspot, TermialID, Distance=unit:GetCoordinate():GetClosestParkingSpot(airbase)
--Parkingspot:MarkToAll("parking spot")
self:T2(string.format("Closest parking spot distance = %s, terminal ID=%s", tostring(Distance), tostring(TermialID)))
-- Get unit coordinates for respawning position.
local uc=unit:GetCoordinate()
SpawnTemplate.units[UnitID].x = Parkingspot.x
SpawnTemplate.units[UnitID].y = Parkingspot.z
SpawnTemplate.units[UnitID].alt = Parkingspot.y
SpawnTemplate.units[UnitID].parking = TermialID
SpawnTemplate.units[UnitID].parking_id = nil
end
SpawnPoint.x = AirbaseCoord.x
SpawnPoint.y = AirbaseCoord.z
SpawnTemplate.x = AirbaseCoord.x
SpawnTemplate.y = AirbaseCoord.z
-- Set uncontrolled state.
SpawnTemplate.uncontrolled=Uncontrolled
-- Destroy and respawn.
self:Destroy()
_DATABASE:Spawn( SpawnTemplate )
-- Reset events.
self:ResetEvents()
return self
end
return nil
end
--- Return the mission template of the group.
-- @param #GROUP self
-- @return #table The MissionTemplate
function GROUP:GetTaskMission()
self:F2( self.GroupName )
return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template )
end
--- Return the mission route of the group.
-- @param #GROUP self
-- @return #table The mission route defined by points.
function GROUP:GetTaskRoute()
self:F2( self.GroupName )
return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points )
end
--- Return the route of a group by using the @{Core.Database#DATABASE} class.
-- @param #GROUP self
-- @param #number Begin The route point from where the copy will start. The base route point is 0.
-- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0.
-- @param #boolean Randomize Randomization of the route, when true.
-- @param #number Radius When randomization is on, the randomization is within the radius.
function GROUP:CopyRoute( Begin, End, Randomize, Radius )
self:F2( { Begin, End } )
local Points = {}
-- Could be a Spawned Group
local GroupName = string.match( self:GetName(), ".*#" )
if GroupName then
GroupName = GroupName:sub( 1, -2 )
else
GroupName = self:GetName()
end
self:T3( { GroupName } )
local Template = _DATABASE.Templates.Groups[GroupName].Template
if Template then
if not Begin then
Begin = 0
end
if not End then
End = 0
end
for TPointID = Begin + 1, #Template.route.points - End do
if Template.route.points[TPointID] then
Points[#Points+1] = routines.utils.deepCopy( Template.route.points[TPointID] )
if Randomize then
if not Radius then
Radius = 500
end
Points[#Points].x = Points[#Points].x + math.random( Radius * -1, Radius )
Points[#Points].y = Points[#Points].y + math.random( Radius * -1, Radius )
end
end
end
return Points
else
error( "Template not found for Group : " .. GroupName )
end
return nil
end
--- Calculate the maxium A2G threat level of the Group.
-- @param #GROUP self
function GROUP:CalculateThreatLevelA2G()
local MaxThreatLevelA2G = 0
for UnitName, UnitData in pairs( self:GetUnits() ) do
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
local ThreatLevelA2G = ThreatUnit:GetThreatLevel()
if ThreatLevelA2G > MaxThreatLevelA2G then
MaxThreatLevelA2G = ThreatLevelA2G
end
end
self:T3( MaxThreatLevelA2G )
return MaxThreatLevelA2G
end
--- Returns true if the first unit of the GROUP is in the air.
-- @param Wrapper.Group#GROUP self
-- @return #boolean true if in the first unit of the group is in the air.
-- @return #nil The GROUP is not existing or not alive.
function GROUP:InAir()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnit = DCSGroup:getUnit(1)
if DCSUnit then
local GroupInAir = DCSGroup:getUnit(1):inAir()
self:T3( GroupInAir )
return GroupInAir
end
end
return nil
end
do -- Route methods
--- (AIR) Return the Group to an @{Wrapper.Airbase#AIRBASE}.
-- The following things are to be taken into account:
--
-- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back).
-- * A group consisting out of more than one unit, may rejoin formation when respawned.
-- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base.
-- * When there is no @{Wrapper.Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base.
-- * When there is no @{Wrapper.Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase.
--
-- @param #GROUP self
-- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Wrapper.Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase.
-- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected.
-- @return #GROUP
function GROUP:RouteRTB( RTBAirbase, Speed )
self:F( { RTBAirbase:GetName(), Speed } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
if RTBAirbase then
local GroupPoint = self:GetVec2()
local GroupVelocity = self:GetUnit(1):GetDesc().speedMax
local PointFrom = {}
PointFrom.x = GroupPoint.x
PointFrom.y = GroupPoint.y
PointFrom.type = "Turning Point"
PointFrom.action = "Turning Point"
PointFrom.speed = GroupVelocity
local PointTo = {}
local AirbasePointVec2 = RTBAirbase:GetPointVec2()
local AirbaseAirPoint = AirbasePointVec2:WaypointAir(
POINT_VEC3.RoutePointAltType.BARO,
"Land",
"Landing",
Speed or self:GetUnit(1):GetDesc().speedMax
)
AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID()
AirbaseAirPoint["speed_locked"] = true,
self:F(AirbaseAirPoint )
local Points = { PointFrom, AirbaseAirPoint }
self:T3( Points )
local Template = self:GetTemplate()
Template.route.points = Points
self:Respawn( Template )
--self:Route( Points )
else
self:ClearTasks()
end
end
return self
end
end
function GROUP:OnReSpawn( ReSpawnFunction )
self.ReSpawnFunction = ReSpawnFunction
end
do -- Event Handling
--- Subscribe to a DCS Event.
-- @param #GROUP self
-- @param Core.Event#EVENTS Event
-- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP.
-- @return #GROUP
function GROUP:HandleEvent( Event, EventFunction, ... )
self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event, ... )
return self
end
--- UnSubscribe to a DCS event.
-- @param #GROUP self
-- @param Core.Event#EVENTS Event
-- @return #GROUP
function GROUP:UnHandleEvent( Event )
self:EventDispatcher():RemoveEvent( self, Event )
return self
end
--- Reset the subscriptions.
-- @param #GROUP self
-- @return #GROUP
function GROUP:ResetEvents()
self:EventDispatcher():Reset( self )
for UnitID, UnitData in pairs( self:GetUnits() ) do
UnitData:ResetEvents()
end
return self
end
end
do -- Players
--- Get player names
-- @param #GROUP self
-- @return #table The group has players, an array of player names is returned.
-- @return #nil The group has no players
function GROUP:GetPlayerNames()
local HasPlayers = false
local PlayerNames = {}
local Units = self:GetUnits()
for UnitID, UnitData in pairs( Units ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = Unit:GetPlayerName()
if PlayerName and PlayerName ~= "" then
PlayerNames = PlayerNames or {}
table.insert( PlayerNames, PlayerName )
HasPlayers = true
end
end
if HasPlayers == true then
self:F2( PlayerNames )
return PlayerNames
end
return nil
end
--- Get the active player count in the group.
-- @param #GROUP self
-- @return #number The amount of players.
function GROUP:GetPlayerCount()
local PlayerCount = 0
local Units = self:GetUnits()
for UnitID, UnitData in pairs( Units or {} ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = Unit:GetPlayerName()
if PlayerName and PlayerName ~= "" then
PlayerCount = PlayerCount + 1
end
end
return PlayerCount
end
end
--do -- Smoke
--
----- Signal a flare at the position of the GROUP.
---- @param #GROUP self
---- @param Utilities.Utils#FLARECOLOR FlareColor
--function GROUP:Flare( FlareColor )
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
--end
--
----- Signal a white flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareWhite()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
--end
--
----- Signal a yellow flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareYellow()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
--end
--
----- Signal a green flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareGreen()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
--end
--
----- Signal a red flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareRed()
-- self:F2()
-- local Vec3 = self:GetVec3()
-- if Vec3 then
-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
-- end
--end
--
----- Smoke the GROUP.
---- @param #GROUP self
--function GROUP:Smoke( SmokeColor, Range )
-- self:F2()
-- if Range then
-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
-- else
-- trigger.action.smoke( self:GetVec3(), SmokeColor )
-- end
--
--end
--
----- Smoke the GROUP Green.
---- @param #GROUP self
--function GROUP:SmokeGreen()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
--end
--
----- Smoke the GROUP Red.
---- @param #GROUP self
--function GROUP:SmokeRed()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
--end
--
----- Smoke the GROUP White.
---- @param #GROUP self
--function GROUP:SmokeWhite()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
--end
--
----- Smoke the GROUP Orange.
---- @param #GROUP self
--function GROUP:SmokeOrange()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
--end
--
----- Smoke the GROUP Blue.
---- @param #GROUP self
--function GROUP:SmokeBlue()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
--end
--
--
--
--end