Last Updates

This commit is contained in:
FlightControl
2016-09-12 12:29:20 +02:00
parent dac29f6356
commit cd4d4af559
241 changed files with 6314 additions and 1827 deletions

View File

@@ -0,0 +1,109 @@
--- This module contains the AIRBASE classes.
--
-- ===
--
-- 1) @{Airbase#AIRBASE} class, extends @{Positionable#POSITIONABLE}
-- =================================================================
-- The @{AIRBASE} class is a wrapper class to handle the DCS Airbase objects:
--
-- * Support all DCS Airbase APIs.
-- * Enhance with Airbase specific APIs not in the DCS Airbase API set.
--
--
-- 1.1) AIRBASE reference methods
-- ------------------------------
-- For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts).
--
-- The AIRBASE class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference
-- using the DCS Airbase or the DCS AirbaseName.
--
-- Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object.
-- The AIRBASE methods will reference the DCS Airbase object by name when it is needed during API execution.
-- If the DCS Airbase object does not exist or is nil, the AIRBASE methods will return nil and log an exception in the DCS.log file.
--
-- The AIRBASE class provides the following functions to retrieve quickly the relevant AIRBASE instance:
--
-- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
-- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
--
-- 1.2) DCS AIRBASE APIs
-- ---------------------
-- The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method.
-- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call,
-- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSAirbase#Airbase.getName}()
-- is implemented in the AIRBASE class as @{#AIRBASE.GetName}().
--
-- More functions will be added
-- ----------------------------
-- During the MOOSE development, more functions will be added.
--
-- @module Airbase
-- @author FlightControl
--- The AIRBASE class
-- @type AIRBASE
-- @extends Positionable#POSITIONABLE
AIRBASE = {
ClassName="AIRBASE",
CategoryName = {
[Airbase.Category.AIRDROME] = "Airdrome",
[Airbase.Category.HELIPAD] = "Helipad",
[Airbase.Category.SHIP] = "Ship",
},
}
-- Registration.
--- Create a new AIRBASE from DCSAirbase.
-- @param #AIRBASE self
-- @param #string AirbaseName The name of the airbase.
-- @return Airbase#AIRBASE
function AIRBASE:Register( AirbaseName )
local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) )
self.AirbaseName = AirbaseName
return self
end
-- Reference methods.
--- Finds a AIRBASE from the _DATABASE using a DCSAirbase object.
-- @param #AIRBASE self
-- @param DCSAirbase#Airbase DCSAirbase An existing DCS Airbase object reference.
-- @return Airbase#AIRBASE self
function AIRBASE:Find( DCSAirbase )
local AirbaseName = DCSAirbase:getName()
local AirbaseFound = _DATABASE:FindAirbase( AirbaseName )
return AirbaseFound
end
--- Find a AIRBASE in the _DATABASE using the name of an existing DCS Airbase.
-- @param #AIRBASE self
-- @param #string AirbaseName The Airbase Name.
-- @return Airbase#AIRBASE self
function AIRBASE:FindByName( AirbaseName )
local AirbaseFound = _DATABASE:FindAirbase( AirbaseName )
return AirbaseFound
end
function AIRBASE:GetDCSObject()
local DCSAirbase = Airbase.getByName( self.AirbaseName )
if DCSAirbase then
return DCSAirbase
end
return nil
end

View File

@@ -0,0 +1,471 @@
--- This module contains the CLIENT class.
--
-- 1) @{Client#CLIENT} class, extends @{Unit#UNIT}
-- ===============================================
-- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__.
-- Note that clients are NOT the same as Units, they are NOT necessarily alive.
-- The @{Client#CLIENT} class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__:
--
-- * Wraps the DCS Unit objects with skill level set to Player or Client.
-- * Support all DCS Unit APIs.
-- * Enhance with Unit specific APIs not in the DCS Group API set.
-- * When player joins Unit, execute alive init logic.
-- * Handles messages to players.
-- * Manage the "state" of the DCS Unit.
--
-- Clients are being used by the @{MISSION} class to follow players and register their successes.
--
-- 1.1) CLIENT reference methods
-- -----------------------------
-- For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts).
--
-- The CLIENT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the DCS Unit or the DCS UnitName.
--
-- Another thing to know is that CLIENT objects do not "contain" the DCS Unit object.
-- The CLIENT methods will reference the DCS Unit object by name when it is needed during API execution.
-- If the DCS Unit object does not exist or is nil, the CLIENT methods will return nil and log an exception in the DCS.log file.
--
-- The CLIENT class provides the following functions to retrieve quickly the relevant CLIENT instance:
--
-- * @{#CLIENT.Find}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object.
-- * @{#CLIENT.FindByName}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name.
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).
--
-- @module Client
-- @author FlightControl
--- The CLIENT class
-- @type CLIENT
-- @extends Unit#UNIT
CLIENT = {
ONBOARDSIDE = {
NONE = 0,
LEFT = 1,
RIGHT = 2,
BACK = 3,
FRONT = 4
},
ClassName = "CLIENT",
ClientName = nil,
ClientAlive = false,
ClientTransport = false,
ClientBriefingShown = false,
_Menus = {},
_Tasks = {},
Messages = {
}
}
--- Finds a CLIENT from the _DATABASE using the relevant DCS Unit.
-- @param #CLIENT self
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
-- @return #CLIENT
-- @usage
-- -- Create new Clients.
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
-- Mission:AddGoal( DeploySA6TroopsGoal )
--
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
function CLIENT:Find( DCSUnit )
local ClientName = DCSUnit:getName()
local ClientFound = _DATABASE:FindClient( ClientName )
if ClientFound then
ClientFound:F( ClientName )
return ClientFound
end
error( "CLIENT not found for: " .. ClientName )
end
--- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.
-- As an optional parameter, a briefing text can be given also.
-- @param #CLIENT self
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
-- @return #CLIENT
-- @usage
-- -- Create new Clients.
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
-- Mission:AddGoal( DeploySA6TroopsGoal )
--
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
function CLIENT:FindByName( ClientName, ClientBriefing )
local ClientFound = _DATABASE:FindClient( ClientName )
if ClientFound then
ClientFound:F( { ClientName, ClientBriefing } )
ClientFound:AddBriefing( ClientBriefing )
ClientFound.MessageSwitch = true
return ClientFound
end
error( "CLIENT not found for: " .. ClientName )
end
function CLIENT:Register( ClientName )
local self = BASE:Inherit( self, UNIT:Register( ClientName ) )
self:F( ClientName )
self.ClientName = ClientName
self.MessageSwitch = true
self.ClientAlive2 = false
--self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 )
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5 )
self:E( self )
return self
end
--- Transport defines that the Client is a Transport. Transports show cargo.
-- @param #CLIENT self
-- @return #CLIENT
function CLIENT:Transport()
self:F()
self.ClientTransport = true
return self
end
--- AddBriefing adds a briefing to a CLIENT when a player joins a mission.
-- @param #CLIENT self
-- @param #string ClientBriefing is the text defining the Mission briefing.
-- @return #CLIENT self
function CLIENT:AddBriefing( ClientBriefing )
self:F( ClientBriefing )
self.ClientBriefing = ClientBriefing
self.ClientBriefingShown = false
return self
end
--- Show the briefing of a CLIENT.
-- @param #CLIENT self
-- @return #CLIENT self
function CLIENT:ShowBriefing()
self:F( { self.ClientName, self.ClientBriefingShown } )
if not self.ClientBriefingShown then
self.ClientBriefingShown = true
local Briefing = ""
if self.ClientBriefing then
Briefing = Briefing .. self.ClientBriefing
end
Briefing = Briefing .. " Press [LEFT ALT]+[B] to view the complete mission briefing."
self:Message( Briefing, 60, "Briefing" )
end
return self
end
--- Show the mission briefing of a MISSION to the CLIENT.
-- @param #CLIENT self
-- @param #string MissionBriefing
-- @return #CLIENT self
function CLIENT:ShowMissionBriefing( MissionBriefing )
self:F( { self.ClientName } )
if MissionBriefing then
self:Message( MissionBriefing, 60, "Mission Briefing" )
end
return self
end
--- Resets a CLIENT.
-- @param #CLIENT self
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
function CLIENT:Reset( ClientName )
self:F()
self._Menus = {}
end
-- Is Functions
--- Checks if the CLIENT is a multi-seated UNIT.
-- @param #CLIENT self
-- @return #boolean true if multi-seated.
function CLIENT:IsMultiSeated()
self:F( self.ClientName )
local ClientMultiSeatedTypes = {
["Mi-8MT"] = "Mi-8MT",
["UH-1H"] = "UH-1H",
["P-51B"] = "P-51B"
}
if self:IsAlive() then
local ClientTypeName = self:GetClientGroupUnit():GetTypeName()
if ClientMultiSeatedTypes[ClientTypeName] then
return true
end
end
return false
end
--- Checks for a client alive event and calls a function on a continuous basis.
-- @param #CLIENT self
-- @param #function CallBack Function.
-- @return #CLIENT
function CLIENT:Alive( CallBackFunction, ... )
self:F()
self.ClientCallBack = CallBackFunction
self.ClientParameters = arg
return self
end
--- @param #CLIENT self
function CLIENT:_AliveCheckScheduler( SchedulerName )
self:F( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } )
if self:IsAlive() then
if self.ClientAlive2 == false then
self:ShowBriefing()
if self.ClientCallBack then
self:T("Calling Callback function")
self.ClientCallBack( self, unpack( self.ClientParameters ) )
end
self.ClientAlive2 = true
end
else
if self.ClientAlive2 == true then
self.ClientAlive2 = false
end
end
return true
end
--- Return the DCSGroup of a Client.
-- This function is modified to deal with a couple of bugs in DCS 1.5.3
-- @param #CLIENT self
-- @return DCSGroup#Group
function CLIENT:GetDCSGroup()
self:F3()
-- local ClientData = Group.getByName( self.ClientName )
-- if ClientData and ClientData:isExist() then
-- self:T( self.ClientName .. " : group found!" )
-- return ClientData
-- else
-- return nil
-- end
local ClientUnit = Unit.getByName( self.ClientName )
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
self:T3( { "CoalitionData:", CoalitionData } )
for UnitId, UnitData in pairs( CoalitionData ) do
self:T3( { "UnitData:", UnitData } )
if UnitData and UnitData:isExist() then
--self:E(self.ClientName)
if ClientUnit then
local ClientGroup = ClientUnit:getGroup()
if ClientGroup then
self:T3( "ClientGroup = " .. self.ClientName )
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
if ClientGroup:getID() == UnitData:getGroup():getID() then
self:T3( "Normal logic" )
self:T3( self.ClientName .. " : group found!" )
self.ClientGroupID = ClientGroup:getID()
self.ClientGroupName = ClientGroup:getName()
return ClientGroup
end
else
-- Now we need to resolve the bugs in DCS 1.5 ...
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
self:T3( "Bug 1.5 logic" )
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
self.ClientGroupID = ClientGroupTemplate.groupId
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
return ClientGroup
end
-- else
-- error( "Client " .. self.ClientName .. " not found!" )
end
else
--self:E( { "Client not found!", self.ClientName } )
end
end
end
end
-- For non player clients
if ClientUnit then
local ClientGroup = ClientUnit:getGroup()
if ClientGroup then
self:T3( "ClientGroup = " .. self.ClientName )
if ClientGroup:isExist() then
self:T3( "Normal logic" )
self:T3( self.ClientName .. " : group found!" )
return ClientGroup
end
end
end
self.ClientGroupID = nil
self.ClientGroupUnit = nil
return nil
end
-- TODO: Check DCSTypes#Group.ID
--- Get the group ID of the client.
-- @param #CLIENT self
-- @return DCSTypes#Group.ID
function CLIENT:GetClientGroupID()
local ClientGroup = self:GetDCSGroup()
--self:E( self.ClientGroupID ) -- Determined in GetDCSGroup()
return self.ClientGroupID
end
--- Get the name of the group of the client.
-- @param #CLIENT self
-- @return #string
function CLIENT:GetClientGroupName()
local ClientGroup = self:GetDCSGroup()
self:T( self.ClientGroupName ) -- Determined in GetDCSGroup()
return self.ClientGroupName
end
--- Returns the UNIT of the CLIENT.
-- @param #CLIENT self
-- @return Unit#UNIT
function CLIENT:GetClientGroupUnit()
self:F2()
local ClientDCSUnit = Unit.getByName( self.ClientName )
self:T( self.ClientDCSUnit )
if ClientDCSUnit and ClientDCSUnit:isExist() then
local ClientUnit = _DATABASE:FindUnit( self.ClientName )
self:T2( ClientUnit )
return ClientUnit
end
end
--- Returns the DCSUnit of the CLIENT.
-- @param #CLIENT self
-- @return DCSTypes#Unit
function CLIENT:GetClientGroupDCSUnit()
self:F2()
local ClientDCSUnit = Unit.getByName( self.ClientName )
if ClientDCSUnit and ClientDCSUnit:isExist() then
self:T2( ClientDCSUnit )
return ClientDCSUnit
end
end
--- Evaluates if the CLIENT is a transport.
-- @param #CLIENT self
-- @return #boolean true is a transport.
function CLIENT:IsTransport()
self:F()
return self.ClientTransport
end
--- Shows the @{Cargo#CARGO} contained within the CLIENT to the player as a message.
-- The @{Cargo#CARGO} is shown using the @{Message#MESSAGE} distribution system.
-- @param #CLIENT self
function CLIENT:ShowCargo()
self:F()
local CargoMsg = ""
for CargoName, Cargo in pairs( CARGOS ) do
if self == Cargo:IsLoadedInClient() then
CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n"
end
end
if CargoMsg == "" then
CargoMsg = "empty"
end
self:Message( CargoMsg, 15, "Co-Pilot: Cargo Status", 30 )
end
-- TODO (1) I urgently need to revise this.
--- A local function called by the DCS World Menu system to switch off messages.
function CLIENT.SwitchMessages( PrmTable )
PrmTable[1].MessageSwitch = PrmTable[2]
end
--- The main message driver for the CLIENT.
-- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system.
-- @param #CLIENT self
-- @param #string Message is the text describing the message.
-- @param #number MessageDuration is the duration in seconds that the Message should be displayed.
-- @param #string MessageCategory is the category of the message (the title).
-- @param #number MessageInterval is the interval in seconds between the display of the @{Message#MESSAGE} when the CLIENT is in the air.
-- @param #string MessageID is the identifier of the message when displayed with intervals.
function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInterval, MessageID )
self:F( { Message, MessageDuration, MessageCategory, MessageInterval } )
if self.MessageSwitch == true then
if MessageCategory == nil then
MessageCategory = "Messages"
end
if MessageID ~= nil then
if self.Messages[MessageID] == nil then
self.Messages[MessageID] = {}
self.Messages[MessageID].MessageId = MessageID
self.Messages[MessageID].MessageTime = timer.getTime()
self.Messages[MessageID].MessageDuration = MessageDuration
if MessageInterval == nil then
self.Messages[MessageID].MessageInterval = 600
else
self.Messages[MessageID].MessageInterval = MessageInterval
end
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
else
if self:GetClientGroupDCSUnit() and not self:GetClientGroupDCSUnit():inAir() then
if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + 10 then
MESSAGE:New( Message, MessageDuration , MessageCategory):ToClient( self )
self.Messages[MessageID].MessageTime = timer.getTime()
end
else
if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + self.Messages[MessageID].MessageInterval then
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
self.Messages[MessageID].MessageTime = timer.getTime()
end
end
end
else
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,868 @@
--- This module contains the GROUP class.
--
-- 1) @{Group#GROUP} class, extends @{Controllable#CONTROLLABLE}
-- =============================================================
-- The @{Group#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).**
--
-- 1.1) GROUP reference methods
-- -----------------------
-- 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.
--
-- 1.2) GROUP task methods
-- -----------------------
-- Several group task methods are available that help you to prepare tasks.
-- These methods return a string consisting of the task description, which can then be given to either a
-- @{Controllable#CONTROLLABLE.PushTask} or @{Controllable#CONTROLLABLE.SetTask} method to assign the task to the GROUP.
-- Tasks are specific for the category of the GROUP, more specific, for AIR, GROUND or AIR and GROUND.
-- Each task description where applicable indicates for which group category the task is valid.
-- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks.
--
-- ### 1.2.1) Assigned task methods
--
-- Assigned task methods make the group execute the task where the location of the (possible) targets of the task are known before being detected.
-- This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed.
--
-- Find below a list of the **assigned task** methods:
--
-- * @{Controllable#CONTROLLABLE.TaskAttackGroup}: (AIR) Attack a Group.
-- * @{Controllable#CONTROLLABLE.TaskAttackMapObject}: (AIR) Attacking the map object (building, structure, e.t.c).
-- * @{Controllable#CONTROLLABLE.TaskAttackUnit}: (AIR) Attack the Unit.
-- * @{Controllable#CONTROLLABLE.TaskBombing}: (Controllable#CONTROLLABLEDelivering weapon at the point on the ground.
-- * @{Controllable#CONTROLLABLE.TaskBombingRunway}: (AIR) Delivering weapon on the runway.
-- * @{Controllable#CONTROLLABLE.TaskEmbarking}: (AIR) Move the group to a Vec2 Point, wait for a defined duration and embark a group.
-- * @{Controllable#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location.
-- * @{Controllable#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne group.
-- * @{Controllable#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the group/unit a FAC and orders the FAC to control the target (enemy ground group) destruction.
-- * @{Controllable#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire at a VEC2 point until ammunition is finished.
-- * @{Controllable#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne group.
-- * @{Controllable#CONTROLLABLE.TaskHold}: (GROUND) Hold ground group from moving.
-- * @{Controllable#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the group.
-- * @{Controllable#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only.
-- * @{Controllable#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the group at a @{Zone#ZONE_RADIUS).
-- * @{Controllable#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the group at a specified alititude.
-- * @{Controllable#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed.
-- * @{Controllable#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters.
-- * @{Controllable#CONTROLLABLE.TaskRoute}: (AIR + GROUND) Return a Misson task to follow a given route defined by Points.
-- * @{Controllable#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Group move to a given point.
-- * @{Controllable#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Group move to a given point.
-- * @{Controllable#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the group to a given zone.
-- * @{Controllable#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the group to an airbase.
--
-- ### 1.2.2) EnRoute task methods
--
-- EnRoute tasks require the targets of the task need to be detected by the group (using its sensors) before the task can be executed:
--
-- * @{Controllable#CONTROLLABLE.EnRouteTaskAWACS}: (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters.
-- * @{Controllable#CONTROLLABLE.EnRouteTaskEngageGroup}: (AIR) Engaging a group. The task does not assign the target group to the unit/group to attack now; it just allows the unit/group to engage the target group as well as other assigned targets.
-- * @{Controllable#CONTROLLABLE.EnRouteTaskEngageTargets}: (AIR) Engaging targets of defined types.
-- * @{Controllable#CONTROLLABLE.EnRouteTaskEWR}: (AIR) Attack the Unit.
-- * @{Controllable#CONTROLLABLE.EnRouteTaskFAC}: (AIR + GROUND) The task makes the group/unit a FAC and lets the FAC to choose a targets (enemy ground group) around as well as other assigned targets.
-- * @{Controllable#CONTROLLABLE.EnRouteTaskFAC_EngageGroup}: (AIR + GROUND) The task makes the group/unit a FAC and lets the FAC to choose the target (enemy ground group) as well as other assigned targets.
-- * @{Controllable#CONTROLLABLE.EnRouteTaskTanker}: (AIR) Aircraft will act as a tanker for friendly units. No parameters.
--
-- ### 1.2.3) Preparation task methods
--
-- There are certain task methods that allow to tailor the task behaviour:
--
-- * @{Controllable#CONTROLLABLE.TaskWrappedAction}: Return a WrappedAction Task taking a Command.
-- * @{Controllable#CONTROLLABLE.TaskCombo}: Return a Combo Task taking an array of Tasks.
-- * @{Controllable#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task.
-- * @{Controllable#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition.
--
-- ### 1.2.4) 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:
--
-- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template.
--
-- 1.3) GROUP Command methods
-- --------------------------
-- Group **command methods** prepare the execution of commands using the @{Controllable#CONTROLLABLE.SetCommand} method:
--
-- * @{Controllable#CONTROLLABLE.CommandDoScript}: Do Script command.
-- * @{Controllable#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command.
--
-- 1.4) GROUP Option methods
-- -------------------------
-- Group **Option methods** change the behaviour of the Group while being alive.
--
-- ### 1.4.1) Rule of Engagement:
--
-- * @{Controllable#CONTROLLABLE.OptionROEWeaponFree}
-- * @{Controllable#CONTROLLABLE.OptionROEOpenFire}
-- * @{Controllable#CONTROLLABLE.OptionROEReturnFire}
-- * @{Controllable#CONTROLLABLE.OptionROEEvadeFire}
--
-- To check whether an ROE option is valid for a specific group, use:
--
-- * @{Controllable#CONTROLLABLE.OptionROEWeaponFreePossible}
-- * @{Controllable#CONTROLLABLE.OptionROEOpenFirePossible}
-- * @{Controllable#CONTROLLABLE.OptionROEReturnFirePossible}
-- * @{Controllable#CONTROLLABLE.OptionROEEvadeFirePossible}
--
-- ### 1.4.2) Rule on thread:
--
-- * @{Controllable#CONTROLLABLE.OptionROTNoReaction}
-- * @{Controllable#CONTROLLABLE.OptionROTPassiveDefense}
-- * @{Controllable#CONTROLLABLE.OptionROTEvadeFire}
-- * @{Controllable#CONTROLLABLE.OptionROTVertical}
--
-- To test whether an ROT option is valid for a specific group, use:
--
-- * @{Controllable#CONTROLLABLE.OptionROTNoReactionPossible}
-- * @{Controllable#CONTROLLABLE.OptionROTPassiveDefensePossible}
-- * @{Controllable#CONTROLLABLE.OptionROTEvadeFirePossible}
-- * @{Controllable#CONTROLLABLE.OptionROTVerticalPossible}
--
-- 1.5) 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 @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on.
--
-- @module Group
-- @author FlightControl
--- The GROUP class
-- @type GROUP
-- @extends Controllable#CONTROLLABLE
-- @field #string GroupName The name of the group.
GROUP = {
ClassName = "GROUP",
}
--- Create a new GROUP from a DCSGroup
-- @param #GROUP self
-- @param DCSGroup#Group GroupName The DCS Group name
-- @return #GROUP self
function GROUP:Register( GroupName )
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self:F2( GroupName )
self.GroupName = GroupName
return self
end
-- Reference methods.
--- Find the GROUP wrapper class instance using the DCS Group.
-- @param #GROUP self
-- @param DCSGroup#Group DCSGroup The DCS Group.
-- @return #GROUP The GROUP.
function GROUP:Find( DCSGroup )
local GroupName = DCSGroup:getName() -- 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 DCSGroup#Group The DCS Group.
function GROUP:GetDCSObject()
local DCSGroup = Group.getByName( self.GroupName )
if DCSGroup then
return DCSGroup
end
return nil
end
--- Returns if the DCS Group is alive.
-- When the group exists at run-time, this method will return true, otherwise false.
-- @param #GROUP self
-- @return #boolean true if the DCS Group is alive.
function GROUP:IsAlive()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupIsAlive = DCSGroup:isExist()
self:T3( GroupIsAlive )
return GroupIsAlive
end
return nil
end
--- Destroys the DCS Group and all of its DCS Units.
-- Note that this destroy method also raises a destroy event at run-time.
-- So all event listeners will catch the destroy event of this DCS Group.
-- @param #GROUP self
function GROUP:Destroy()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
self:CreateEventCrash( timer.getTime(), UnitData )
end
DCSGroup:destroy()
DCSGroup = nil
end
return nil
end
--- Returns category of the DCS Group.
-- @param #GROUP self
-- @return DCSGroup#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 DCS 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 DCSCoalitionObject#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 DCScountry#country.id The country identifier.
-- @return #nil 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
--- 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 Unit#UNIT The UNIT wrapper class.
function GROUP:GetUnit( UnitNumber )
self:F2( { self.GroupName, UnitNumber } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) )
self:T3( UnitFound.UnitName )
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 DCSUnit#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber )
self:F2( { 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:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupSize = DCSGroup:getSize()
self:T3( GroupSize )
return GroupSize
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:F2( { self.GroupName } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupInitialSize = DCSGroup:getInitialSize()
self:T3( GroupInitialSize )
return GroupInitialSize
end
return nil
end
--- Returns the UNITs wrappers of the DCS Units of the DCS Group.
-- @param #GROUP self
-- @return #table The UNITs wrappers.
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 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 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
return nil
end
--- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group.
-- @param #GROUP self
-- @return DCSTypes#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.
-- @return DCSTypes#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
-- Is Zone Functions
--- Returns true if all units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE}
function GROUP:IsCompletelyInZone( Zone )
self:F2( { self.GroupName, Zone } )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Unit#UNIT
-- TODO: Rename IsPointVec3InZone to IsVec3InZone
if Zone:IsPointVec3InZone( Unit:GetVec3() ) then
else
return false
end
end
return true
end
--- Returns true if some units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE}
function GROUP:IsPartlyInZone( Zone )
self:F2( { self.GroupName, Zone } )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Unit#UNIT
if Zone:IsPointVec3InZone( Unit:GetVec3() ) then
return true
end
end
return false
end
--- Returns true if none of the group units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE}
function GROUP:IsNotInZone( Zone )
self:F2( { self.GroupName, Zone } )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Unit#UNIT
if Zone:IsPointVec3InZone( Unit:GetVec3() ) then
return false
end
end
return true
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
--- 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
-- SPAWNING
--- Respawn the @{GROUP} using a (tweaked) template of the Group.
-- The template must be retrieved with the @{Group#GROUP.GetTemplate}() function.
-- The template contains all the definitions as declared within the mission file.
-- To understand templates, do the following:
--
-- * unpack your .miz file into a directory using 7-zip.
-- * browse in the directory created to the file **mission**.
-- * open the file and search for the country group definitions.
--
-- Your group template will contain the fields as described within the mission file.
--
-- This function will:
--
-- * Get the current position and heading of the group.
-- * When the group is alive, it will tweak the template x, y and heading coordinates of the group and the embedded units to the current units positions.
-- * Then it will destroy the current alive group.
-- * And it will respawn the group using your new template definition.
-- @param Group#GROUP self
-- @param #table Template The template of the Group retrieved with GROUP:GetTemplate()
function GROUP:Respawn( Template )
local Vec3 = self:GetVec3()
Template.x = Vec3.x
Template.y = Vec3.z
--Template.x = nil
--Template.y = nil
self:E( #Template.units )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local GroupUnit = UnitData -- Unit#UNIT
self:E( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
Template.units[UnitID].alt = GroupUnitVec3.y
Template.units[UnitID].x = GroupUnitVec3.x
Template.units[UnitID].y = GroupUnitVec3.z
Template.units[UnitID].heading = GroupUnitHeading
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
end
end
self:Destroy()
_DATABASE:Spawn( Template )
end
--- Returns the group template from the @{DATABASE} (_DATABASE object).
-- @param #GROUP self
-- @return #table
function GROUP:GetTemplate()
local GroupName = self:GetName()
self:E( GroupName )
return _DATABASE:GetGroupTemplate( GroupName )
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 DCScountry#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 DCSCoalitionObject#coalition.side CoalitionID The coalition ID.
-- @return #table
function GROUP:SetTemplateCoalition( Template, CoalitionID )
Template.CoalitionID = CoalitionID
return Template
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 @{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

View File

@@ -0,0 +1,220 @@
--- This module contains the IDENTIFIABLE class.
--
-- 1) @{Identifiable#IDENTIFIABLE} class, extends @{Object#OBJECT}
-- ===============================================================
-- The @{Identifiable#IDENTIFIABLE} class is a wrapper class to handle the DCS Identifiable objects:
--
-- * Support all DCS Identifiable APIs.
-- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set.
-- * Manage the "state" of the DCS Identifiable.
--
-- 1.1) IDENTIFIABLE constructor:
-- ------------------------------
-- The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance:
--
-- * @{Identifiable#IDENTIFIABLE.New}(): Create a IDENTIFIABLE instance.
--
-- 1.2) IDENTIFIABLE methods:
-- --------------------------
-- The following methods can be used to identify an identifiable object:
--
-- * @{Identifiable#IDENTIFIABLE.GetName}(): Returns the name of the Identifiable.
-- * @{Identifiable#IDENTIFIABLE.IsAlive}(): Returns if the Identifiable is alive.
-- * @{Identifiable#IDENTIFIABLE.GetTypeName}(): Returns the type name of the Identifiable.
-- * @{Identifiable#IDENTIFIABLE.GetCoalition}(): Returns the coalition of the Identifiable.
-- * @{Identifiable#IDENTIFIABLE.GetCountry}(): Returns the country of the Identifiable.
-- * @{Identifiable#IDENTIFIABLE.GetDesc}(): Returns the descriptor structure of the Identifiable.
--
--
-- ===
--
-- @module Identifiable
-- @author FlightControl
--- The IDENTIFIABLE class
-- @type IDENTIFIABLE
-- @extends Object#OBJECT
-- @field #string IdentifiableName The name of the identifiable.
IDENTIFIABLE = {
ClassName = "IDENTIFIABLE",
IdentifiableName = "",
}
local _CategoryName = {
[Unit.Category.AIRPLANE] = "Airplane",
[Unit.Category.HELICOPTER] = "Helicoper",
[Unit.Category.GROUND_UNIT] = "Ground Identifiable",
[Unit.Category.SHIP] = "Ship",
[Unit.Category.STRUCTURE] = "Structure",
}
--- Create a new IDENTIFIABLE from a DCSIdentifiable
-- @param #IDENTIFIABLE self
-- @param DCSIdentifiable#Identifiable IdentifiableName The DCS Identifiable name
-- @return #IDENTIFIABLE self
function IDENTIFIABLE:New( IdentifiableName )
local self = BASE:Inherit( self, OBJECT:New( IdentifiableName ) )
self:F2( IdentifiableName )
self.IdentifiableName = IdentifiableName
return self
end
--- Returns if the Identifiable is alive.
-- @param Identifiable#IDENTIFIABLE self
-- @return #boolean true if Identifiable is alive.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:IsAlive()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableIsAlive = DCSIdentifiable:isExist()
return IdentifiableIsAlive
end
return false
end
--- Returns DCS Identifiable object name.
-- The function provides access to non-activated objects too.
-- @param Identifiable#IDENTIFIABLE self
-- @return #string The name of the DCS Identifiable.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetName()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableName = self.IdentifiableName
return IdentifiableName
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Returns the type name of the DCS Identifiable.
-- @param Identifiable#IDENTIFIABLE self
-- @return #string The type name of the DCS Identifiable.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetTypeName()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableTypeName = DCSIdentifiable:getTypeName()
self:T3( IdentifiableTypeName )
return IdentifiableTypeName
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Returns category of the DCS Identifiable.
-- @param #IDENTIFIABLE self
-- @return DCSObject#Object.Category The category ID
function IDENTIFIABLE:GetCategory()
self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject()
if DCSObject then
local ObjectCategory = DCSObject:getCategory()
self:T3( ObjectCategory )
return ObjectCategory
end
return nil
end
--- Returns the DCS Identifiable category name as defined within the DCS Identifiable Descriptor.
-- @param Identifiable#IDENTIFIABLE self
-- @return #string The DCS Identifiable Category Name
function IDENTIFIABLE:GetCategoryName()
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableCategoryName = _CategoryName[ self:GetDesc().category ]
return IdentifiableCategoryName
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Returns coalition of the Identifiable.
-- @param Identifiable#IDENTIFIABLE self
-- @return DCSCoalitionObject#coalition.side The side of the coalition.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetCoalition()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
self:T3( IdentifiableCoalition )
return IdentifiableCoalition
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Returns country of the Identifiable.
-- @param Identifiable#IDENTIFIABLE self
-- @return DCScountry#country.id The country identifier.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetCountry()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableCountry = DCSIdentifiable:getCountry()
self:T3( IdentifiableCountry )
return IdentifiableCountry
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Returns Identifiable descriptor. Descriptor type depends on Identifiable category.
-- @param Identifiable#IDENTIFIABLE self
-- @return DCSIdentifiable#Identifiable.Desc The Identifiable descriptor.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetDesc()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableDesc = DCSIdentifiable:getDesc()
self:T2( IdentifiableDesc )
return IdentifiableDesc
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end

View File

@@ -0,0 +1,89 @@
--- This module contains the OBJECT class.
--
-- 1) @{Object#OBJECT} class, extends @{Base#BASE}
-- ===========================================================
-- The @{Object#OBJECT} class is a wrapper class to handle the DCS Object objects:
--
-- * Support all DCS Object APIs.
-- * Enhance with Object specific APIs not in the DCS Object API set.
-- * Manage the "state" of the DCS Object.
--
-- 1.1) OBJECT constructor:
-- ------------------------------
-- The OBJECT class provides the following functions to construct a OBJECT instance:
--
-- * @{Object#OBJECT.New}(): Create a OBJECT instance.
--
-- 1.2) OBJECT methods:
-- --------------------------
-- The following methods can be used to identify an Object object:
--
-- * @{Object#OBJECT.GetID}(): Returns the ID of the Object object.
--
-- ===
--
-- @module Object
-- @author FlightControl
--- The OBJECT class
-- @type OBJECT
-- @extends Base#BASE
-- @field #string ObjectName The name of the Object.
OBJECT = {
ClassName = "OBJECT",
ObjectName = "",
}
--- A DCSObject
-- @type DCSObject
-- @field id_ The ID of the controllable in DCS
--- Create a new OBJECT from a DCSObject
-- @param #OBJECT self
-- @param DCSObject#Object ObjectName The Object name
-- @return #OBJECT self
function OBJECT:New( ObjectName )
local self = BASE:Inherit( self, BASE:New() )
self:F2( ObjectName )
self.ObjectName = ObjectName
return self
end
--- Returns the unit's unique identifier.
-- @param Object#OBJECT self
-- @return DCSObject#Object.ID ObjectID
-- @return #nil The DCS Object is not existing or alive.
function OBJECT:GetID()
self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject()
if DCSObject then
local ObjectID = DCSObject:getID()
return ObjectID
end
return nil
end
--- Destroys the OBJECT.
-- @param #OBJECT self
-- @return #nil The DCS Unit is not existing or alive.
function OBJECT:Destroy()
self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject()
if DCSObject then
DCSObject:destroy()
end
return nil
end

View File

@@ -0,0 +1,281 @@
--- This module contains the POSITIONABLE class.
--
-- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE}
-- ===========================================================
-- The @{Positionable#POSITIONABLE} class is a wrapper class to handle the POSITIONABLE objects:
--
-- * Support all DCS APIs.
-- * Enhance with POSITIONABLE specific APIs not in the DCS API set.
-- * Manage the "state" of the POSITIONABLE.
--
-- 1.1) POSITIONABLE constructor:
-- ------------------------------
-- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance:
--
-- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance.
--
-- 1.2) POSITIONABLE methods:
-- --------------------------
-- The following methods can be used to identify an measurable object:
--
-- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object.
-- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object.
--
-- ===
--
-- @module Positionable
-- @author FlightControl
--- The POSITIONABLE class
-- @type POSITIONABLE
-- @extends Identifiable#IDENTIFIABLE
-- @field #string PositionableName The name of the measurable.
POSITIONABLE = {
ClassName = "POSITIONABLE",
PositionableName = "",
}
--- A DCSPositionable
-- @type DCSPositionable
-- @field id_ The ID of the controllable in DCS
--- Create a new POSITIONABLE from a DCSPositionable
-- @param #POSITIONABLE self
-- @param DCSPositionable#Positionable PositionableName The POSITIONABLE name
-- @return #POSITIONABLE self
function POSITIONABLE:New( PositionableName )
local self = BASE:Inherit( self, IDENTIFIABLE:New( PositionableName ) )
return self
end
--- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
-- @param Positionable#POSITIONABLE self
-- @return DCSTypes#Position The 3D position vectors of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetPositionVec3()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionablePosition = DCSPositionable:getPosition()
self:T3( PositionablePosition )
return PositionablePosition
end
return nil
end
--- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission.
-- @param Positionable#POSITIONABLE self
-- @return DCSTypes#Vec2 The 2D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVec2()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableVec3 = DCSPositionable:getPosition().p
local PositionableVec2 = {}
PositionableVec2.x = PositionableVec3.x
PositionableVec2.y = PositionableVec3.z
self:T2( PositionableVec2 )
return PositionableVec2
end
return nil
end
--- Returns a POINT_VEC2 object indicating the point in 2D of the POSITIONABLE within the mission.
-- @param Positionable#POSITIONABLE self
-- @return Point#POINT_VEC2 The 2D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetPointVec2()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableVec3 = DCSPositionable:getPosition().p
local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 )
self:T2( PositionablePointVec2 )
return PositionablePointVec2
end
return nil
end
--- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission.
-- @param Positionable#POSITIONABLE self
-- @return DCSTypes#Vec3 The 3D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetRandomVec3( Radius )
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionablePointVec3 = DCSPositionable:getPosition().p
local PositionableRandomVec3 = {}
local angle = math.random() * math.pi*2;
PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius;
PositionableRandomVec3.y = PositionablePointVec3.y
PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius;
self:T3( PositionableRandomVec3 )
return PositionableRandomVec3
end
return nil
end
--- Returns the @{DCSTypes#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission.
-- @param Positionable#POSITIONABLE self
-- @return DCSTypes#Vec3 The 3D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVec3()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableVec3 = DCSPositionable:getPosition().p
self:T3( PositionableVec3 )
return PositionableVec3
end
return nil
end
--- Returns the altitude of the POSITIONABLE.
-- @param Positionable#POSITIONABLE self
-- @return DCSTypes#Distance The altitude of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetAltitude()
self:F2()
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionablePointVec3 = DCSPositionable:getPoint() --DCSTypes#Vec3
return PositionablePointVec3.y
end
return nil
end
--- Returns if the Positionable is located above a runway.
-- @param Positionable#POSITIONABLE self
-- @return #boolean true if Positionable is above a runway.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:IsAboveRunway()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local Vec2 = self:GetVec2()
local SurfaceType = land.getSurfaceType( Vec2 )
local IsAboveRunway = SurfaceType == land.SurfaceType.RUNWAY
self:T2( IsAboveRunway )
return IsAboveRunway
end
return nil
end
--- Returns the POSITIONABLE heading in degrees.
-- @param Positionable#POSITIONABLE self
-- @return #number The POSTIONABLE heading
function POSITIONABLE:GetHeading()
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionablePosition = DCSPositionable:getPosition()
if PositionablePosition then
local PositionableHeading = math.atan2( PositionablePosition.x.z, PositionablePosition.x.x )
if PositionableHeading < 0 then
PositionableHeading = PositionableHeading + 2 * math.pi
end
PositionableHeading = PositionableHeading * 180 / math.pi
self:T2( PositionableHeading )
return PositionableHeading
end
end
return nil
end
--- Returns true if the POSITIONABLE is in the air.
-- @param Positionable#POSITIONABLE self
-- @return #boolean true if in the air.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:InAir()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableInAir = DCSPositionable:inAir()
self:T3( PositionableInAir )
return PositionableInAir
end
return nil
end
--- Returns the POSITIONABLE velocity vector.
-- @param Positionable#POSITIONABLE self
-- @return DCSTypes#Vec3 The velocity vector
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocity()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableVelocityVec3 = DCSPositionable:getVelocity()
self:T3( PositionableVelocityVec3 )
return PositionableVelocityVec3
end
return nil
end
--- Returns the POSITIONABLE velocity in km/h.
-- @param Positionable#POSITIONABLE self
-- @return #number The velocity in km/h
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocityKMH()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local VelocityVec3 = self:GetVelocity()
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
local Velocity = Velocity * 3.6 -- now it is in km/h.
self:T3( Velocity )
return Velocity
end
return nil
end

View File

@@ -0,0 +1,81 @@
--- This module contains the STATIC class.
--
-- 1) @{Static#STATIC} class, extends @{Positionable#POSITIONABLE}
-- ===============================================================
-- Statics are **Static Units** defined within the Mission Editor.
-- Note that Statics are almost the same as Units, but they don't have a controller.
-- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects:
--
-- * Wraps the DCS Static objects.
-- * Support all DCS Static APIs.
-- * Enhance with Static specific APIs not in the DCS API set.
--
-- 1.1) STATIC reference methods
-- -----------------------------
-- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts).
--
-- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the Static Name.
--
-- Another thing to know is that STATIC objects do not "contain" the DCS Static object.
-- The STATIc methods will reference the DCS Static object by name when it is needed during API execution.
-- If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file.
--
-- The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance:
--
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
--
-- @module Static
-- @author FlightControl
--- The STATIC class
-- @type STATIC
-- @extends Positionable#POSITIONABLE
STATIC = {
ClassName = "STATIC",
}
--- Finds a STATIC from the _DATABASE using the relevant Static Name.
-- As an optional parameter, a briefing text can be given also.
-- @param #STATIC self
-- @param #string StaticName Name of the DCS **Static** as defined within the Mission Editor.
-- @return #STATIC
function STATIC:FindByName( StaticName )
local StaticFound = _DATABASE:FindStatic( StaticName )
self.StaticName = StaticName
if StaticFound then
StaticFound:F( { StaticName } )
return StaticFound
end
error( "STATIC not found for: " .. StaticName )
end
function STATIC:Register( StaticName )
local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) )
self.StaticName = StaticName
return self
end
function STATIC:GetDCSObject()
local DCSStatic = StaticObject.getByName( self.StaticName )
if DCSStatic then
return DCSStatic
end
return nil
end

View File

@@ -0,0 +1,829 @@
--- This module contains the UNIT class.
--
-- 1) @{Unit#UNIT} class, extends @{Controllable#CONTROLLABLE}
-- ===========================================================
-- The @{Unit#UNIT} class is a wrapper class to handle the DCS Unit objects:
--
-- * Support all DCS Unit APIs.
-- * Enhance with Unit specific APIs not in the DCS Unit API set.
-- * Handle local Unit Controller.
-- * Manage the "state" of the DCS Unit.
--
--
-- 1.1) UNIT reference methods
-- ----------------------
-- For each DCS Unit object alive within a running mission, a UNIT 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 Unit objects are spawned (using the @{SPAWN} class).
--
-- The UNIT class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference
-- using the DCS Unit or the DCS UnitName.
--
-- Another thing to know is that UNIT objects do not "contain" the DCS Unit object.
-- The UNIT methods will reference the DCS Unit object by name when it is needed during API execution.
-- If the DCS Unit object does not exist or is nil, the UNIT methods will return nil and log an exception in the DCS.log file.
--
-- The UNIT class provides the following functions to retrieve quickly the relevant UNIT instance:
--
-- * @{#UNIT.Find}(): Find a UNIT instance from the _DATABASE object using a DCS Unit object.
-- * @{#UNIT.FindByName}(): Find a UNIT instance from the _DATABASE object using a DCS Unit name.
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).
--
-- 1.2) DCS UNIT APIs
-- ------------------
-- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method.
-- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call,
-- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSUnit#Unit.getName}()
-- is implemented in the UNIT class as @{#UNIT.GetName}().
--
-- 1.3) Smoke, Flare Units
-- -----------------------
-- The UNIT class provides methods to smoke or flare units easily.
-- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods
-- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit.
-- When the DCS Unit moves for whatever reason, the smoking will still continue!
-- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}()
-- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration.
--
-- 1.4) Location Position, Point
-- -----------------------------
-- The UNIT class provides methods to obtain the current point or position of the DCS Unit.
-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively.
-- If you want to obtain the complete **3D position** including ori<72>ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively.
--
-- 1.5) Test if alive
-- ------------------
-- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active.
--
-- 1.6) Test for proximity
-- -----------------------
-- The UNIT class contains methods to test the location or proximity against zones or other objects.
--
-- ### 1.6.1) Zones
-- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}.
--
-- ### 1.6.2) Units
-- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method.
--
-- @module Unit
-- @author FlightControl
--- The UNIT class
-- @type UNIT
-- @extends Controllable#CONTROLLABLE
-- @field #UNIT.FlareColor FlareColor
-- @field #UNIT.SmokeColor SmokeColor
UNIT = {
ClassName="UNIT",
FlareColor = {
Green = trigger.flareColor.Green,
Red = trigger.flareColor.Red,
White = trigger.flareColor.White,
Yellow = trigger.flareColor.Yellow
},
SmokeColor = {
Green = trigger.smokeColor.Green,
Red = trigger.smokeColor.Red,
White = trigger.smokeColor.White,
Orange = trigger.smokeColor.Orange,
Blue = trigger.smokeColor.Blue
},
}
--- FlareColor
-- @type UNIT.FlareColor
-- @field Green
-- @field Red
-- @field White
-- @field Yellow
--- SmokeColor
-- @type UNIT.SmokeColor
-- @field Green
-- @field Red
-- @field White
-- @field Orange
-- @field Blue
--- Unit.SensorType
-- @type Unit.SensorType
-- @field OPTIC
-- @field RADAR
-- @field IRST
-- @field RWR
-- Registration.
--- Create a new UNIT from DCSUnit.
-- @param #UNIT self
-- @param #string UnitName The name of the DCS unit.
-- @return Unit#UNIT
function UNIT:Register( UnitName )
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) )
self.UnitName = UnitName
return self
end
-- Reference methods.
--- Finds a UNIT from the _DATABASE using a DCSUnit object.
-- @param #UNIT self
-- @param DCSUnit#Unit DCSUnit An existing DCS Unit object reference.
-- @return Unit#UNIT self
function UNIT:Find( DCSUnit )
local UnitName = DCSUnit:getName()
local UnitFound = _DATABASE:FindUnit( UnitName )
return UnitFound
end
--- Find a UNIT in the _DATABASE using the name of an existing DCS Unit.
-- @param #UNIT self
-- @param #string UnitName The Unit Name.
-- @return Unit#UNIT self
function UNIT:FindByName( UnitName )
local UnitFound = _DATABASE:FindUnit( UnitName )
return UnitFound
end
--- Return the name of the UNIT.
-- @param #UNIT self
-- @return #string The UNIT name.
function UNIT:Name()
return self.UnitName
end
--- @param #UNIT self
-- @return DCSUnit#Unit
function UNIT:GetDCSObject()
local DCSUnit = Unit.getByName( self.UnitName )
if DCSUnit then
return DCSUnit
end
return nil
end
--- Respawn the @{Unit} using a (tweaked) template of the parent Group.
--
-- This function will:
--
-- * Get the current position and heading of the group.
-- * When the unit is alive, it will tweak the template x, y and heading coordinates of the group and the embedded units to the current units positions.
-- * Then it will respawn the re-modelled group.
--
-- @param Unit#UNIT self
-- @param DCSTypes#Vec3 SpawnVec3 The position where to Spawn the new Unit at.
-- @param #number Heading The heading of the unit respawn.
function UNIT:ReSpawn( SpawnVec3, Heading )
local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) )
self:T( SpawnGroupTemplate )
local SpawnGroup = self:GetGroup()
if SpawnGroup then
local Vec3 = SpawnGroup:GetVec3()
SpawnGroupTemplate.x = SpawnVec3.x
SpawnGroupTemplate.y = SpawnVec3.z
self:E( #SpawnGroupTemplate.units )
for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do
local GroupUnit = UnitData -- Unit#UNIT
self:E( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
SpawnGroupTemplate.units[UnitID].alt = GroupUnitVec3.y
SpawnGroupTemplate.units[UnitID].x = GroupUnitVec3.x
SpawnGroupTemplate.units[UnitID].y = GroupUnitVec3.z
SpawnGroupTemplate.units[UnitID].heading = GroupUnitHeading
self:E( { UnitID, SpawnGroupTemplate.units[UnitID], SpawnGroupTemplate.units[UnitID] } )
end
end
end
for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do
self:T( UnitTemplateData.name )
if UnitTemplateData.name == self:Name() then
self:T("Adjusting")
SpawnGroupTemplate.units[UnitTemplateID].alt = SpawnVec3.y
SpawnGroupTemplate.units[UnitTemplateID].x = SpawnVec3.x
SpawnGroupTemplate.units[UnitTemplateID].y = SpawnVec3.z
SpawnGroupTemplate.units[UnitTemplateID].heading = Heading
self:E( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } )
else
self:E( SpawnGroupTemplate.units[UnitTemplateID].name )
local GroupUnit = UNIT:FindByName( SpawnGroupTemplate.units[UnitTemplateID].name ) -- Unit#UNIT
if GroupUnit and GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
UnitTemplateData.alt = GroupUnitVec3.y
UnitTemplateData.x = GroupUnitVec3.x
UnitTemplateData.y = GroupUnitVec3.z
UnitTemplateData.heading = GroupUnitHeading
else
if SpawnGroupTemplate.units[UnitTemplateID].name ~= self:Name() then
self:T("nilling")
SpawnGroupTemplate.units[UnitTemplateID].delete = true
end
end
end
end
-- Remove obscolete units from the group structure
i = 1
while i <= #SpawnGroupTemplate.units do
local UnitTemplateData = SpawnGroupTemplate.units[i]
self:T( UnitTemplateData.name )
if UnitTemplateData.delete then
table.remove( SpawnGroupTemplate.units, i )
else
i = i + 1
end
end
_DATABASE:Spawn( SpawnGroupTemplate )
end
--- Returns if the unit is activated.
-- @param Unit#UNIT self
-- @return #boolean true if Unit is activated.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:IsActive()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitIsActive = DCSUnit:isActive()
return UnitIsActive
end
return nil
end
--- Returns the Unit's callsign - the localized string.
-- @param Unit#UNIT self
-- @return #string The Callsign of the Unit.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetCallsign()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitCallSign = DCSUnit:getCallsign()
return UnitCallSign
end
self:E( self.ClassName .. " " .. self.UnitName .. " not found!" )
return nil
end
--- Returns name of the player that control the unit or nil if the unit is controlled by A.I.
-- @param Unit#UNIT self
-- @return #string Player Name
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetPlayerName()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local PlayerName = DCSUnit:getPlayerName()
if PlayerName == nil then
PlayerName = ""
end
return PlayerName
end
return nil
end
--- Returns the unit's number in the group.
-- The number is the same number the unit has in ME.
-- It may not be changed during the mission.
-- If any unit in the group is destroyed, the numbers of another units will not be changed.
-- @param Unit#UNIT self
-- @return #number The Unit number.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetNumber()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitNumber = DCSUnit:getNumber()
return UnitNumber
end
return nil
end
--- Returns the unit's group if it exist and nil otherwise.
-- @param Unit#UNIT self
-- @return Group#GROUP The Group of the Unit.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetGroup()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitGroup = GROUP:Find( DCSUnit:getGroup() )
return UnitGroup
end
return nil
end
-- Need to add here functions to check if radar is on and which object etc.
--- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign.
-- DCS Units spawned with the @{SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name.
-- The spawn sequence number and unit number are contained within the name after the '#' sign.
-- @param Unit#UNIT self
-- @return #string The name of the DCS Unit.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetPrefix()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
self:T3( UnitPrefix )
return UnitPrefix
end
return nil
end
--- Returns the Unit's ammunition.
-- @param Unit#UNIT self
-- @return DCSUnit#Unit.Ammo
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetAmmo()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitAmmo = DCSUnit:getAmmo()
return UnitAmmo
end
return nil
end
--- Returns the unit sensors.
-- @param Unit#UNIT self
-- @return DCSUnit#Unit.Sensors
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetSensors()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitSensors = DCSUnit:getSensors()
return UnitSensors
end
return nil
end
-- Need to add here a function per sensortype
-- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS)
--- Returns if the unit has sensors of a certain type.
-- @param Unit#UNIT self
-- @return #boolean returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:HasSensors( ... )
self:F2( arg )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local HasSensors = DCSUnit:hasSensors( unpack( arg ) )
return HasSensors
end
return nil
end
--- Returns if the unit is SEADable.
-- @param Unit#UNIT self
-- @return #boolean returns true if the unit is SEADable.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:HasSEAD()
self:F2()
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitSEADAttributes = DCSUnit:getDesc().attributes
local HasSEAD = false
if UnitSEADAttributes["RADAR_BAND1_FOR_ARM"] and UnitSEADAttributes["RADAR_BAND1_FOR_ARM"] == true or
UnitSEADAttributes["RADAR_BAND2_FOR_ARM"] and UnitSEADAttributes["RADAR_BAND2_FOR_ARM"] == true then
HasSEAD = true
end
return HasSEAD
end
return nil
end
--- Returns two values:
--
-- * First value indicates if at least one of the unit's radar(s) is on.
-- * Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
-- @param Unit#UNIT self
-- @return #boolean Indicates if at least one of the unit's radar(s) is on.
-- @return DCSObject#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetRadar()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitRadarOn, UnitRadarObject = DCSUnit:getRadar()
return UnitRadarOn, UnitRadarObject
end
return nil, nil
end
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
-- @param Unit#UNIT self
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetFuel()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitFuel = DCSUnit:getFuel()
return UnitFuel
end
return nil
end
--- Returns the unit's health. Dead units has health <= 1.0.
-- @param Unit#UNIT self
-- @return #number The Unit's health value.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetLife()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitLife = DCSUnit:getLife()
return UnitLife
end
return nil
end
--- Returns the Unit's initial health.
-- @param Unit#UNIT self
-- @return #number The Unit's initial health value.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetLife0()
self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitLife0 = DCSUnit:getLife0()
return UnitLife0
end
return nil
end
--- Returns the Unit's A2G threat level on a scale from 1 to 10 ...
-- The following threat levels are foreseen:
--
-- * Threat level 0: Unit is unarmed.
-- * Threat level 1: Unit is infantry.
-- * Threat level 2: Unit is an infantry vehicle.
-- * Threat level 3: Unit is ground artillery.
-- * Threat level 4: Unit is a tank.
-- * Threat level 5: Unit is a modern tank or ifv with ATGM.
-- * Threat level 6: Unit is a AAA.
-- * Threat level 7: Unit is a SAM or manpad, IR guided.
-- * Threat level 8: Unit is a Short Range SAM, radar guided.
-- * Threat level 9: Unit is a Medium Range SAM, radar guided.
-- * Threat level 10: Unit is a Long Range SAM, radar guided.
function UNIT:GetThreatLevel()
local Attributes = self:GetDesc().attributes
local ThreatLevel = 0
local ThreatLevels = {
"Unarmed",
"Infantry",
"Old Tanks & APCs",
"Tanks & IFVs without ATGM",
"Tanks & IFV with ATGM",
"Modern Tanks",
"AAA",
"IR Guided SAMs",
"SR SAMs",
"MR SAMs",
"LR SAMs"
}
self:T2( Attributes )
if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and
not Attributes["IR Guided SAM"] then ThreatLevel = 8
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
Attributes["IR Guided SAM"] then ThreatLevel = 7
elseif Attributes["AAA"] then ThreatLevel = 6
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
Attributes["ATGM"] then ThreatLevel = 4
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1
end
self:T2( ThreatLevel )
return ThreatLevel, ThreatLevels[ThreatLevel+1]
end
-- Is functions
--- Returns true if the unit is within a @{Zone}.
-- @param #UNIT self
-- @param Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the unit is within the @{Zone#ZONE_BASE}
function UNIT:IsInZone( Zone )
self:F2( { self.UnitName, Zone } )
if self:IsAlive() then
local IsInZone = Zone:IsPointVec3InZone( self:GetVec3() )
self:T( { IsInZone } )
return IsInZone
end
return false
end
--- Returns true if the unit is not within a @{Zone}.
-- @param #UNIT self
-- @param Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the unit is not within the @{Zone#ZONE_BASE}
function UNIT:IsNotInZone( Zone )
self:F2( { self.UnitName, Zone } )
if self:IsAlive() then
local IsInZone = not Zone:IsPointVec3InZone( self:GetVec3() )
self:T( { IsInZone } )
return IsInZone
else
return false
end
end
--- Returns true if there is an **other** DCS Unit within a radius of the current 2D point of the DCS Unit.
-- @param Unit#UNIT self
-- @param Unit#UNIT AwaitUnit The other UNIT wrapper object.
-- @param Radius The radius in meters with the DCS Unit in the centre.
-- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit.
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitVec3 = self:GetVec3()
local AwaitUnitVec3 = AwaitUnit:GetVec3()
if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then
self:T3( "true" )
return true
else
self:T3( "false" )
return false
end
end
return nil
end
--- Signal a flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:Flare( FlareColor )
self:F2()
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
end
--- Signal a white flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareWhite()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
end
--- Signal a yellow flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareYellow()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
end
--- Signal a green flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareGreen()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
end
--- Signal a red flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareRed()
self:F2()
local Vec3 = self:GetVec3()
if Vec3 then
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
end
end
--- Smoke the UNIT.
-- @param #UNIT self
function UNIT: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 UNIT Green.
-- @param #UNIT self
function UNIT:SmokeGreen()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
end
--- Smoke the UNIT Red.
-- @param #UNIT self
function UNIT:SmokeRed()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
end
--- Smoke the UNIT White.
-- @param #UNIT self
function UNIT:SmokeWhite()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
end
--- Smoke the UNIT Orange.
-- @param #UNIT self
function UNIT:SmokeOrange()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
end
--- Smoke the UNIT Blue.
-- @param #UNIT self
function UNIT:SmokeBlue()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end
-- Is methods
--- Returns if the unit is of an air category.
-- If the unit is a helicopter or a plane, then this method will return true, otherwise false.
-- @param #UNIT self
-- @return #boolean Air category evaluation result.
function UNIT:IsAir()
self:F2()
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitDescriptor = DCSUnit:getDesc()
self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER )
self:T3( IsAirResult )
return IsAirResult
end
return nil
end
--- Returns if the unit is of an ground category.
-- If the unit is a ground vehicle or infantry, this method will return true, otherwise false.
-- @param #UNIT self
-- @return #boolean Ground category evaluation result.
function UNIT:IsGround()
self:F2()
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitDescriptor = DCSUnit:getDesc()
self:T3( { UnitDescriptor.category, Unit.Category.GROUND_UNIT } )
local IsGroundResult = ( UnitDescriptor.category == Unit.Category.GROUND_UNIT )
self:T3( IsGroundResult )
return IsGroundResult
end
return nil
end
--- Returns if the unit is a friendly unit.
-- @param #UNIT self
-- @return #boolean IsFriendly evaluation result.
function UNIT:IsFriendly( FriendlyCoalition )
self:F2()
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitCoalition = DCSUnit:getCoalition()
self:T3( { UnitCoalition, FriendlyCoalition } )
local IsFriendlyResult = ( UnitCoalition == FriendlyCoalition )
self:E( IsFriendlyResult )
return IsFriendlyResult
end
return nil
end
--- Returns if the unit is of a ship category.
-- If the unit is a ship, this method will return true, otherwise false.
-- @param #UNIT self
-- @return #boolean Ship category evaluation result.
function UNIT:IsShip()
self:F2()
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local UnitDescriptor = DCSUnit:getDesc()
self:T3( { UnitDescriptor.category, Unit.Category.SHIP } )
local IsShipResult = ( UnitDescriptor.category == Unit.Category.SHIP )
self:T3( IsShipResult )
return IsShipResult
end
return nil
end