From 070c893940b9a80406a8f930ba14b5a78b14e6ac Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 24 May 2016 11:57:01 +0200 Subject: [PATCH] Documentation, and test mission video + optimizations --- Dcs/DCSGroup.lua | 5 +- Dcs/DCSTime.lua | 8 +- Dcs/Moose_Test_WRAPPER.lua | 8 + Documentation/Base.html | 14 + Documentation/CARGO.html | 14 + Documentation/CLEANUP.html | 14 + Documentation/Client.html | 271 +- Documentation/DCSAirbase.html | 3 + Documentation/DCSCoalitionObject.html | 3 + Documentation/DCSCommand.html | 3 + Documentation/DCSController.html | 3 + Documentation/DCSGroup.html | 27 + Documentation/DCSObject.html | 3 + Documentation/DCSTask.html | 3 + Documentation/DCSTypes.html | 3 + Documentation/DCSUnit.html | 3 + Documentation/DCSWorld.html | 3 + Documentation/DCStimer.html | 3 + Documentation/DEPLOYTASK.html | 14 + Documentation/DESTROYBASETASK.html | 14 + Documentation/DESTROYGROUPSTASK.html | 14 + Documentation/DESTROYRADARSTASK.html | 14 + Documentation/DESTROYUNITTYPESTASK.html | 14 + Documentation/Database.html | 136 +- Documentation/Escort.html | 15 + Documentation/Event.html | 14 + Documentation/GOHOMETASK.html | 14 + Documentation/Group.html | 615 ++-- Documentation/MISSION.html | 57 +- Documentation/MOVEMENT.html | 14 + Documentation/Menu.html | 14 + Documentation/Message.html | 14 + Documentation/MissileTrainer.html | 14 + Documentation/NOTASK.html | 14 + Documentation/PICKUPTASK.html | 14 + Documentation/ROUTETASK.html | 14 + Documentation/STAGE.html | 14 + Documentation/Scheduler.html | 14 + Documentation/Scoring.html | 14 + Documentation/Sead.html | 14 + Documentation/Set.html | 14 + Documentation/Spawn.html | 14 + Documentation/StaticObject.html | 3 + Documentation/TASK.html | 14 + Documentation/Unit.html | 54 +- Documentation/Zone.html | 14 + Documentation/env.html | 3 + Documentation/index.html | 100 +- Documentation/land.html | 3 + Documentation/routines.html | 14 + Embedded/Moose_Embedded.lua | 2714 +++++++++++------ Moose/Client.lua | 162 +- Moose/Database.lua | 52 +- Moose/Group.lua | 1414 +++++---- Moose/Mission.lua | 19 - Moose/Set.lua | 6 +- Moose/Stage.lua | 2 +- Moose/Unit.lua | 161 +- ...BASE - UNIT - CLIENT - GROUP - ZONE, .pptx | Bin 807332 -> 807682 bytes .../Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz | Bin 29167 -> 35094 bytes .../Moose_Test_DESTROY/Moose_Test_DESTROY.lua | 4 +- .../Moose_Test_ESCORT/MOOSE_Test_ESCORT.lua | 4 +- .../Moose_Test_ESCORT/MOOSE_Test_ESCORT.miz | Bin 144754 -> 144763 bytes .../Moose_Test_SPAWN/MOOSE_Test_SPAWN.lua | 2 +- .../MOOSE_Test_TASK_Pickup_and_Deploy.lua | 10 +- .../MOOSE_Test_TASK_Pickup_and_Deploy.miz | Bin 32808 -> 33259 bytes .../Moose_Test_WRAPPER/Moose_Test_WRAPPER.lua | 39 + .../Moose_Test_WRAPPER/Moose_Test_WRAPPER.miz | Bin 0 -> 35017 bytes 68 files changed, 4190 insertions(+), 2098 deletions(-) create mode 100644 Dcs/Moose_Test_WRAPPER.lua create mode 100644 Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.lua create mode 100644 Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.miz diff --git a/Dcs/DCSGroup.lua b/Dcs/DCSGroup.lua index a3d0edf5d..46e212f2f 100644 --- a/Dcs/DCSGroup.lua +++ b/Dcs/DCSGroup.lua @@ -37,10 +37,11 @@ -- @return #Group.Category --TODO check coalition.side ---- Returns coalition of the group. + +--- Returns the coalition of the group. -- @function [parent=#Group] getCoalition -- @param #Group self --- @return #coalition.side +-- @return DCSCoalitionObject#coalition.side --- Returns the group's name. This is the same name assigned to the group in Mission Editor. -- @function [parent=#Group] getName diff --git a/Dcs/DCSTime.lua b/Dcs/DCSTime.lua index 95b1efcc1..d70d337ec 100644 --- a/Dcs/DCSTime.lua +++ b/Dcs/DCSTime.lua @@ -1,2 +1,8 @@ --- @type ModelTime +------------------------------------------------------------------------------- +-- @module DCSTime + +--- @type ModelTime -- @extends #number + +--- @type Time +-- @extends #number \ No newline at end of file diff --git a/Dcs/Moose_Test_WRAPPER.lua b/Dcs/Moose_Test_WRAPPER.lua new file mode 100644 index 000000000..bed5f303a --- /dev/null +++ b/Dcs/Moose_Test_WRAPPER.lua @@ -0,0 +1,8 @@ + +Include.File( "Group" ) +Include.File( "Unit" ) + +local UnitAirPlaneAI = _DATABASE:FindUnit( "Airplane AI" ) + +UnitAirPlaneAI:FlareRed() + diff --git a/Documentation/Base.html b/Documentation/Base.html index cec5967a3..f7177aa41 100644 --- a/Documentation/Base.html +++ b/Documentation/Base.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/CARGO.html b/Documentation/CARGO.html index 3927ac107..fdacc75e3 100644 --- a/Documentation/CARGO.html +++ b/Documentation/CARGO.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/CLEANUP.html b/Documentation/CLEANUP.html index 4e3e55cb0..1b0702bb3 100644 --- a/Documentation/CLEANUP.html +++ b/Documentation/CLEANUP.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Client.html b/Documentation/Client.html index b45d4be53..67e324e6f 100644 --- a/Documentation/Client.html +++ b/Documentation/Client.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • @@ -60,18 +74,41 @@

    #CLIENT class

    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.

    +Note that clients are NOT the same as Units, they are NOT necessarily alive. +The CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as Client or Player:

    + +

    Clients are being used by the MISSION class to follow players and register their successes.

    -

    CLIENT construction methods:

    -

    Create a new CLIENT object with the CLIENT.New method:

    +

    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:

    +

    IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).

    + +

    Global(s)

    @@ -93,12 +130,6 @@ Note that clients are NOT the same as Units, they are NOT necessarily alive.

    - - - - @@ -171,6 +202,18 @@ Note that clients are NOT the same as Units, they are NOT necessarily alive.

    + + + + + + + + @@ -237,24 +280,12 @@ Note that clients are NOT the same as Units, they are NOT necessarily alive.

    - - - - - - - - @@ -273,6 +304,12 @@ Note that clients are NOT the same as Units, they are NOT necessarily alive.

    + + + + @@ -357,8 +394,8 @@ is the text defining the Mission briefing.

    Return value

    -

    #CLIENT:

    - +

    #CLIENT: +self

    @@ -392,20 +429,6 @@ Function.

    #CLIENT:

    - - -
    -
    - - - -CLIENT.AliveCheckScheduler - -
    -
    - - -
    @@ -536,7 +559,6 @@ Function.

    - CLIENT.ClientName @@ -573,6 +595,100 @@ Function.

    + +
    +
    +
    + + +CLIENT:Find(ClientName, ClientBriefing, DCSUnit) + +
    +
    + +

    Finds a CLIENT from the _DATABASE using the relevant DCS Unit.

    + +

    Parameters

    +
      +
    • + +

      #string ClientName : +Name of the DCS Unit as defined within the Mission Editor.

      + +
    • +
    • + +

      #string ClientBriefing : +Text that describes the briefing of the mission when a Player logs into the Client.

      + +
    • +
    • + +

      DCSUnit :

      + +
    • +
    +

    Return value

    + +

    #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() )
    + +
    +
    +
    +
    + + +CLIENT:FindByName(ClientName, ClientBriefing) + +
    +
    + +

    Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.

    + + +

    As an optional parameter, a briefing text can be given also.

    + +

    Parameters

    +
      +
    • + +

      #string ClientName : +Name of the DCS Unit as defined within the Mission Editor.

      + +
    • +
    • + +

      #string ClientBriefing : +Text that describes the briefing of the mission when a Player logs into the Client.

      + +
    • +
    +

    Return value

    + +

    #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() )
    +
    @@ -794,20 +910,6 @@ is the interval in seconds between the display of the -
    - - #boolean - -CLIENT.MessageSwitch - -
    -
    - - -
    @@ -822,49 +924,6 @@ is the interval in seconds between the display of the -
    - - -CLIENT:New(ClientName, ClientBriefing) - -
    -
    - -

    Use this method to register new Clients within a mission.

    - -

    Parameters

    -
      -
    • - -

      #string ClientName : -Name of the DCS Unit as defined within the Mission Editor.

      - -
    • -
    • - -

      #string ClientBriefing : -Text that describes the briefing of the mission when a Player logs into the Client.

      - -
    • -
    -

    Return value

    - -

    #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:New( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
    -	Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
    -	Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
    -	Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
    -
    @@ -927,6 +986,24 @@ Name of the Group as defined within the Mission Editor. The Group must have a Un
    + +CLIENT:ShowBriefing() + +
    +
    + +

    Show the briefing of the MISSION to the CLIENT.

    + +

    Return value

    + +

    #CLIENT: +self

    + +
    +
    +
    +
    + CLIENT:ShowCargo() diff --git a/Documentation/DCSAirbase.html b/Documentation/DCSAirbase.html index 5ba906d1e..907c576e5 100644 --- a/Documentation/DCSAirbase.html +++ b/Documentation/DCSAirbase.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSCoalitionObject.html b/Documentation/DCSCoalitionObject.html index 8f4146e80..ee0e7b8dc 100644 --- a/Documentation/DCSCoalitionObject.html +++ b/Documentation/DCSCoalitionObject.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSCommand.html b/Documentation/DCSCommand.html index e5cb77601..4c9762082 100644 --- a/Documentation/DCSCommand.html +++ b/Documentation/DCSCommand.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSController.html b/Documentation/DCSController.html index 05bd95b0e..c4e7c7428 100644 --- a/Documentation/DCSController.html +++ b/Documentation/DCSController.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSGroup.html b/Documentation/DCSGroup.html index 9f2258624..a7a84c8d1 100644 --- a/Documentation/DCSGroup.html +++ b/Documentation/DCSGroup.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • @@ -106,6 +109,12 @@
    + + + + @@ -291,6 +300,24 @@

    #Group.Category:

    + + +
    +
    + + +Group:getCoalition() + +
    +
    + +

    Returns the coalition of the group.

    + +

    Return value

    + +

    DCSCoalitionObject#coalition.side:

    + +
    diff --git a/Documentation/DCSObject.html b/Documentation/DCSObject.html index 017a27b88..44157433f 100644 --- a/Documentation/DCSObject.html +++ b/Documentation/DCSObject.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSTask.html b/Documentation/DCSTask.html index 4f42d8760..93585f188 100644 --- a/Documentation/DCSTask.html +++ b/Documentation/DCSTask.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSTypes.html b/Documentation/DCSTypes.html index 7eb5381e2..2cf9f22b0 100644 --- a/Documentation/DCSTypes.html +++ b/Documentation/DCSTypes.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSUnit.html b/Documentation/DCSUnit.html index 7b78a1fd6..e09265c44 100644 --- a/Documentation/DCSUnit.html +++ b/Documentation/DCSUnit.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCSWorld.html b/Documentation/DCSWorld.html index e1dc25088..e91ba3f3f 100644 --- a/Documentation/DCSWorld.html +++ b/Documentation/DCSWorld.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DCStimer.html b/Documentation/DCStimer.html index 1ee0f7a2c..b4af45ac2 100644 --- a/Documentation/DCStimer.html +++ b/Documentation/DCStimer.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/DEPLOYTASK.html b/Documentation/DEPLOYTASK.html index 8f8bebbc4..b57bf41c4 100644 --- a/Documentation/DEPLOYTASK.html +++ b/Documentation/DEPLOYTASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/DESTROYBASETASK.html b/Documentation/DESTROYBASETASK.html index 2fdf173df..263a03453 100644 --- a/Documentation/DESTROYBASETASK.html +++ b/Documentation/DESTROYBASETASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/DESTROYGROUPSTASK.html b/Documentation/DESTROYGROUPSTASK.html index 88cb8aba6..2235e0590 100644 --- a/Documentation/DESTROYGROUPSTASK.html +++ b/Documentation/DESTROYGROUPSTASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/DESTROYRADARSTASK.html b/Documentation/DESTROYRADARSTASK.html index e7de3a904..f246dfc10 100644 --- a/Documentation/DESTROYRADARSTASK.html +++ b/Documentation/DESTROYRADARSTASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/DESTROYUNITTYPESTASK.html b/Documentation/DESTROYUNITTYPESTASK.html index cb581bed7..62898b2ee 100644 --- a/Documentation/DESTROYUNITTYPESTASK.html +++ b/Documentation/DESTROYUNITTYPESTASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Database.html b/Documentation/Database.html index 07554c5b7..baef7069e 100644 --- a/Documentation/Database.html +++ b/Documentation/Database.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • @@ -143,6 +157,12 @@ The following iterator methods are currently available within the DATABASE:

    + + + + @@ -152,13 +172,13 @@ The following iterator methods are currently available within the DATABASE:

    - + - + @@ -203,6 +223,12 @@ The following iterator methods are currently available within the DATABASE:

    + + + + @@ -236,15 +262,15 @@ The following iterator methods are currently available within the DATABASE:

    - + - + @@ -308,7 +334,7 @@ The following iterator methods are currently available within the DATABASE:

    - + @@ -421,6 +447,32 @@ The following iterator methods are currently available within the DATABASE:

    + +DATABASE:AddGroup(DCSGroup, GroupName) + +
    +
    + +

    Adds a GROUP based on the GroupName in the DATABASE.

    + +

    Parameters

    +
      +
    • + +

      DCSGroup :

      + +
    • +
    • + +

      GroupName :

      + +
    • +
    +
    +
    +
    +
    + DATABASE:AddUnit(DCSUnit, DCSUnitName) @@ -447,9 +499,9 @@ The following iterator methods are currently available within the DATABASE:

    - #string - -DATABASE.ClassName + + +DATABASE.CLIENTS
    @@ -461,9 +513,9 @@ The following iterator methods are currently available within the DATABASE:

    - - -DATABASE.Clients + #string + +DATABASE.ClassName
    @@ -592,6 +644,32 @@ The found CLIENT.

    + +DATABASE:FindGroup(GroupName) + +
    +
    + +

    Finds a GROUP based on the GroupName.

    + +

    Parameter

    +
      +
    • + +

      #string GroupName :

      + +
    • +
    +

    Return value

    + +

    Group#GROUP: +The found GROUP.

    + +
    +
    +
    +
    + DATABASE:FindUnit(UnitName) @@ -746,6 +824,20 @@ The function that will be called when there is an alive player in the database.

    #DATABASE: self

    +
    +
    +
    +
    + + + +DATABASE.GROUPS + +
    +
    + + +
    @@ -767,20 +859,6 @@ self

    -
    -
    -
    -
    - - - -DATABASE.Groups - -
    -
    - - -
    @@ -970,8 +1048,8 @@ self

    - -DATABASE.Units + +DATABASE.UNITS
    diff --git a/Documentation/Escort.html b/Documentation/Escort.html index 5c70cc46c..6da80cd1e 100644 --- a/Documentation/Escort.html +++ b/Documentation/Escort.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • @@ -1836,6 +1850,7 @@ self

    + ESCORT.ReportTargetsScheduler diff --git a/Documentation/Event.html b/Documentation/Event.html index ce93521fd..153c8cb07 100644 --- a/Documentation/Event.html +++ b/Documentation/Event.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/GOHOMETASK.html b/Documentation/GOHOMETASK.html index a2aedee46..3a3adccd4 100644 --- a/Documentation/GOHOMETASK.html +++ b/Documentation/GOHOMETASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Group.html b/Documentation/Group.html index eccd41bc8..cfbc60f20 100644 --- a/Documentation/Group.html +++ b/Documentation/Group.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,19 +56,52 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • Module Group

    -

    A GROUP class abstraction of a DCSGroup class.

    +

    GROUP class.

    -

    The GROUP class will take an abstraction of the DCSGroup class, providing more methods that can be done with a GROUP.

    + +

    GROUP class

    +

    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.
    • +
    + + +

    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.
    • +
    + +

    IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).

    Global(s)

    CLIENT:Alive(CallBack, ...)

    Checks for a client alive event and calls a function on a continuous basis.

    -
    CLIENT.AliveCheckScheduler -
    CLIENT.ClientTransport +
    CLIENT:Find(ClientName, ClientBriefing, DCSUnit) +

    Finds a CLIENT from the _DATABASE using the relevant DCS Unit.

    +
    CLIENT:FindByName(ClientName, ClientBriefing) +

    Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.

    CLIENT:Message(Message, MessageDuration, MessageId, MessageCategory, MessageInterval)

    The main message driver for the CLIENT.

    -
    CLIENT.MessageSwitch -
    CLIENT.Messages -
    CLIENT:New(ClientName, ClientBriefing) -

    Use this method to register new Clients within a mission.

    CLIENT:Reset(ClientName)

    Resets a CLIENT.

    +
    CLIENT:ShowBriefing() +

    Show the briefing of the MISSION to the CLIENT.

    Group:getCategory()

    Returns category of the group.

    +
    Group:getCoalition() +

    Returns the coalition of the group.

    DATABASE:AddClient(ClientName)

    Adds a CLIENT based on the ClientName in the DATABASE.

    +
    DATABASE:AddGroup(DCSGroup, GroupName) +

    Adds a GROUP based on the GroupName in the DATABASE.

    DATABASE.ClassNameDATABASE.CLIENTS
    DATABASE.ClientsDATABASE.ClassName DATABASE:FindClient(ClientName)

    Finds a CLIENT based on the ClientName.

    +
    DATABASE:FindGroup(GroupName) +

    Finds a GROUP based on the GroupName.

    DATABASE:GetStatusGroup(GroupName)DATABASE.GROUPS -

    Get a status to a Group within the Database, this to check crossing events for example.

    +
    DATABASE.GroupsDATABASE:GetStatusGroup(GroupName) - +

    Get a status to a Group within the Database, this to check crossing events for example.

    DATABASE.UnitsDATABASE.UNITS
    @@ -113,7 +157,7 @@ @@ -131,44 +175,61 @@ - + + + + + + + + + + + + + @@ -180,7 +241,15 @@ Note that this destroy method also raises a destroy event at run-time.

    + + + + @@ -204,19 +273,25 @@ Note that this destroy method also raises a destroy event at run-time.

    + + + + @@ -240,13 +315,19 @@ Note that this destroy method also raises a destroy event at run-time.

    + + + + @@ -264,31 +345,31 @@ Note that this destroy method also raises a destroy event at run-time.

    @@ -325,24 +406,6 @@ Note that this destroy method also raises a destroy event at run-time.

    - - - - - - - - - - - - @@ -451,6 +514,12 @@ Note that this destroy method also raises a destroy event at run-time.

    + + + + @@ -565,12 +634,18 @@ Note that this destroy method also raises a destroy event at run-time.

    + + + + @@ -604,7 +679,7 @@ Note that this destroy method also raises a destroy event at run-time.

    - + @@ -619,22 +694,6 @@ Note that this destroy method also raises a destroy event at run-time.

    - -
    GROUP.Controller -

    The controller of the group.

    +
    GROUP:Destroy() -

    Destroy a GROUP -Note that this destroy method also raises a destroy event at run-time.

    +

    Destroys the DCS Group and all of its DCS Units.

    GROUP.FindGroup(Group, DCSGroup)GROUP:Find(DCSGroup) -

    Find the created GROUP using the DCSGroup ID.

    +

    Find the GROUP wrapper class instance using the DCS Group.

    +
    GROUP:FindByName(GroupName) +

    Find the created GROUP using the DCS Group Name.

    GROUP:GetCallsign() -

    Gets the callsign of the fist unit of the group.

    +

    Gets the CallSign of the first DCS Unit of the DCS Group.

    +
    GROUP:GetCategory() +

    Returns category of the DCS Group.

    GROUP:GetCategoryName() -

    Returns the category name of the group.

    +

    Returns the category name of the DCS Group.

    +
    GROUP:GetCoalition() +

    Returns the coalition of the DCS Group.

    GROUP:GetDCSGroup() -

    Gets the DCSGroup of the GROUP.

    +

    Returns the DCS Group.

    GROUP:GetDCSUnit(UnitNumber) -

    Gets the DCS Unit of the GROUP.

    +

    Returns the DCS Unit with number UnitNumber.

    GROUP:GetDCSUnits() -

    Gets the DCSUnits of the GROUP.

    +

    Returns the DCS Units of the DCS Group.

    GROUP:GetID() -

    Gets the ID of the GROUP.

    +

    Returns the DCS Group identifier.

    +
    GROUP:GetInitialSize() +
      +
    • Returns the initial size of the DCS Group.
    • +
    GROUP:GetName() -

    Returns the name of the Group.

    +

    Returns the name of the DCS Group.

    GROUP:GetPointVec2() -

    Gets the current Point of the GROUP in VEC3 format.

    +

    Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group.

    GROUP:GetPointVec3() -

    Gets the current Point of the GROUP in VEC3 format.

    +

    Returns the current point (Vec3 vector) of the first DCS Unit in the DCS Group.

    +
    GROUP:GetSize() +

    Returns current size of the DCS Group.

    GROUP:GetUnit(UnitNumber) -

    Gets the DCS Unit.

    +

    Returns the UNIT wrapper class with number UnitNumber.

    +
    GROUP:GetUnits() +

    Returns the UNITs wrappers of the DCS Units of the DCS Group.

    GROUP.GroupID -

    the ID of the group.

    +
    GROUP:IsAirPlane() -

    Returns if the GROUP are AirPlanes.

    +

    Returns if the DCS Group contains AirPlanes.

    GROUP:IsAlive() -

    Returns if the group is alive.

    +

    Returns if the DCS Group is alive.

    GROUP:IsGround() -

    Returns if the GROUP are Ground troops.

    +

    Returns if the DCS Group contains Ground troops.

    GROUP:IsHelicopter() -

    Returns if the GROUP is a Helicopter.

    +

    Returns if the DCS Group contains Helicopters.

    GROUP:IsShip() -

    Returns if the GROUP are Ships.

    +

    Returns if the DCS Group contains Ships.

    GROUP:MessageToRed(Message, Duration)

    Send a message to the red coalition.

    -
    GROUP:New(DCSGroup) -

    Create a new GROUP from a DCSGroup

    -
    GROUP:NewFromDCSUnit(DCSUnit) -

    Create a new GROUP from an existing DCSUnit in the mission.

    -
    GROUP:NewFromName(GroupName) -

    Create a new GROUP from an existing group name.

    GROUP:PushTask(DCSTask, WaitTime)

    Pushing Task on the queue from the group.

    +
    GROUP:Register(GroupName) +

    Create a new GROUP from a DCSGroup

    GROUP:TaskRoute(Points)

    Return a Misson task to follow a given route defined by Points.

    +
    GROUP:TaskRouteToVec2(Point, Speed) +

    Make the DCS Group to fly to a given point and hover.

    GROUP:TaskRouteToVec3(Point, Speed) -

    Make the group to fly to a given point and hover.

    +

    Make the DCS Group to fly to a given point and hover.

    GROUP:WayPointInitialize(WayPoint)GROUP:WayPointInitialize()

    Retrieve the group mission and allow to place function hooks within the mission waypoint plan.

    GROUP:_GetController()

    Get the controller for the GROUP.

    -
    - -

    Type Vec3

    - - - - - - - -
    Vec3.x - -
    Vec3.y -
    @@ -799,14 +858,13 @@ All units on the ground result.

    - #table GROUP.Controller
    -

    The controller of the group.

    +
    @@ -873,46 +931,65 @@ When randomization is on, the randomization is within the radius.

    -

    Destroy a GROUP -Note that this destroy method also raises a destroy event at run-time.

    +

    Destroys the DCS Group and all of its DCS Units.

    -

    So all event listeners will catch the destroy event of this GROUP.

    +

    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.

    - -GROUP.FindGroup(Group, DCSGroup) + +GROUP:Find(DCSGroup)
    -

    Find the created GROUP using the DCSGroup ID.

    +

    Find the GROUP wrapper class instance using the DCS Group.

    - -

    If a GROUP was created with the DCSGroupID, the the GROUP instance will be returned. -Otherwise nil will be returned.

    - -

    Parameters

    +

    Parameter

    Return value

    -

    #GROUP:

    +

    #GROUP: +The GROUP.

    +
    +
    +
    +
    + + +GROUP:FindByName(GroupName) + +
    +
    + +

    Find the created GROUP using the DCS Group Name.

    + +

    Parameter

    +
      +
    • + +

      #string GroupName : +The DCS Group Name.

      + +
    • +
    +

    Return value

    + +

    #GROUP: +The GROUP.

    @@ -925,12 +1002,30 @@ Otherwise nil will be returned.

    -

    Gets the callsign of the fist unit of the group.

    +

    Gets the CallSign of the first DCS Unit of the DCS Group.

    Return value

    #string: -The callsign of the first unit of the group.

    +The CallSign of the first DCS Unit of the DCS Group.

    + +
    + +
    +
    + + +GROUP:GetCategory() + +
    +
    + +

    Returns category of the DCS Group.

    + +

    Return value

    + +

    DCSGroup#Group.Category: +The category ID

    @@ -943,7 +1038,7 @@ The callsign of the first unit of the group.

    -

    Returns the category name of the group.

    +

    Returns the category name of the DCS Group.

    Return value

    @@ -955,18 +1050,36 @@ Category name = Helicopter, Airplane, Ground Unit, Ship

    + +GROUP:GetCoalition() + +
    +
    + +

    Returns the coalition of the DCS Group.

    + +

    Return value

    + +

    DCSCoalitionObject#coalition.side: +The coalition side of the DCS Group.

    + +
    +
    +
    +
    + GROUP:GetDCSGroup()
    -

    Gets the DCSGroup of the GROUP.

    +

    Returns the DCS Group.

    Return value

    DCSGroup#Group: -The DCSGroup.

    +The DCS Group.

    @@ -979,20 +1092,23 @@ The DCSGroup.

    -

    Gets the DCS Unit of the GROUP.

    +

    Returns the DCS Unit with number UnitNumber.

    + + +

    If the underlying DCS Unit does not exist, the method will return nil. .

    Parameter

    Return value

    -

    #Unit: +

    DCSUnit#Unit: The DCS Unit.

    @@ -1006,12 +1122,12 @@ The DCS Unit.

    -

    Gets the DCSUnits of the GROUP.

    +

    Returns the DCS Units of the DCS Group.

    Return value

    #table: -The DCSUnits.

    +The DCS Units.

    @@ -1037,12 +1153,35 @@ The DCSUnits.

    -

    Gets the ID of the GROUP.

    +

    Returns the DCS Group identifier.

    Return value

    #number: -The ID of the GROUP.

    +The identifier of the DCS Group.

    + +
    + +
    +
    + + +GROUP:GetInitialSize() + +
    +
    + +
      +
    • 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.

    + +

    Return value

    + +

    #number: +The DCS Group initial size.

    @@ -1118,12 +1257,12 @@ Minimum height found.

    -

    Returns the name of the Group.

    +

    Returns the name of the DCS Group.

    Return value

    #string: -GroupName

    +The DCS Group name.

    @@ -1136,12 +1275,12 @@ GroupName

    -

    Gets the current Point of the GROUP in VEC3 format.

    +

    Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group.

    Return value

    -

    #Vec3: -Current x,y and z position of the group.

    +

    DCSTypes#Vec2: +Current Vec2 point of the first DCS Unit of the DCS Group.

    @@ -1154,12 +1293,33 @@ Current x,y and z position of the group.

    -

    Gets the current Point of the GROUP in VEC3 format.

    +

    Returns the current point (Vec3 vector) of the first DCS Unit in the DCS Group.

    Return value

    -

    #Vec3: -Current Vec3 position of the group.

    +

    DCSTypes#Vec3: +Current Vec3 point of the first DCS Unit of the DCS Group.

    + +
    + +
    +
    + + +GROUP:GetSize() + +
    +
    + +

    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.

    + +

    Return value

    + +

    #number: +The DCS Group size.

    @@ -1226,21 +1386,42 @@ The type name of the group.

    -

    Gets the DCS Unit.

    +

    Returns the UNIT wrapper class with number UnitNumber.

    + + +

    If the underlying DCS Unit does not exist, the method will return nil. .

    Parameter

    Return value

    Unit#UNIT: -The DCS Unit.

    +The UNIT wrapper class.

    + +
    + +
    +
    + + +GROUP:GetUnits() + +
    +
    + +

    Returns the UNITs wrappers of the DCS Units of the DCS Group.

    + +

    Return value

    + +

    #table: +The UNITs wrappers.

    @@ -1254,7 +1435,7 @@ The DCS Unit.

    -

    the ID of the group.

    +
    @@ -1302,12 +1483,12 @@ Air category evaluation result.

    -

    Returns if the GROUP are AirPlanes.

    +

    Returns if the DCS Group contains AirPlanes.

    Return value

    #boolean: -true if GROUP are AirPlanes.

    +true if DCS Group contains AirPlanes.

    @@ -1320,7 +1501,7 @@ true if GROUP are AirPlanes.

    -

    Returns if the group is alive.

    +

    Returns if the DCS Group is alive.

    When the group exists at run-time, this method will return true, otherwise false.

    @@ -1328,7 +1509,7 @@ true if GROUP are AirPlanes.

    Return value

    #boolean: -Alive result.

    +true if the DCS Group is alive.

    @@ -1341,12 +1522,12 @@ Alive result.

    -

    Returns if the GROUP are Ground troops.

    +

    Returns if the DCS Group contains Ground troops.

    Return value

    #boolean: -true if GROUP are Ground troops.

    +true if DCS Group contains Ground troops.

    @@ -1359,12 +1540,12 @@ true if GROUP are Ground troops.

    -

    Returns if the GROUP is a Helicopter.

    +

    Returns if the DCS Group contains Helicopters.

    Return value

    #boolean: -true if GROUP are Helicopters.

    +true if DCS Group contains Helicopters.

    @@ -1377,12 +1558,12 @@ true if GROUP are Helicopters.

    -

    Returns if the GROUP are Ships.

    +

    Returns if the DCS Group contains Ships.

    Return value

    #boolean: -true if GROUP are Ships.

    +true if DCS Group contains Ships.

    @@ -1573,87 +1754,6 @@ The duration of the message.

    - -GROUP:New(DCSGroup) - -
    -
    - -

    Create a new GROUP from a DCSGroup

    - -

    Parameter

    - -

    Return value

    - -

    #GROUP: -self

    - -
    -
    -
    -
    - - -GROUP:NewFromDCSUnit(DCSUnit) - -
    -
    - -

    Create a new GROUP from an existing DCSUnit in the mission.

    - -

    Parameter

    -
      -
    • - -

      DCSUnit : -The DCSUnit.

      - -
    • -
    -

    Return value

    - -

    #GROUP: -self

    - -
    -
    -
    -
    - - -GROUP:NewFromName(GroupName) - -
    -
    - -

    Create a new GROUP from an existing group name.

    - -

    Parameter

    -
      -
    • - -

      GroupName : -The name of the DCS Group.

      - -
    • -
    -

    Return value

    - -

    #GROUP: -self

    - -
    -
    -
    -
    - GROUP:OptionROEHoldFire() @@ -1991,6 +2091,33 @@ self

    + +GROUP:Register(GroupName) + +
    +
    + +

    Create a new GROUP from a DCSGroup

    + +

    Parameter

    + +

    Return value

    + +

    #GROUP: +self

    + +
    +
    +
    +
    + GROUP:Route(GoPoints) @@ -2178,7 +2305,7 @@ CSTask#Task> DCSTasks

    • -

      #Time time :

      +

      DCSTime#Time time :

    • @@ -2198,7 +2325,7 @@ CSTask#Task> DCSTasks

    • -

      #Time duration :

      +

      DCSTime#Time duration :

    • @@ -2257,7 +2384,7 @@ CSTask#Task> DCSTasks

      • -

        #Vec2 Point : +

        DCSTypes#Vec2 Point : The point where to wait.

      • @@ -2290,7 +2417,7 @@ The DCS task structure.

    +
    +
    + + +GROUP:TaskRouteToVec2(Point, Speed) + +
    +
    + +

    Make the DCS Group to fly to a given point and hover.

    + +

    Parameters

    +
      +
    • + +

      DCSTypes#Vec3 Point : +The destination point in Vec3 format.

      + +
    • +
    • + +

      #number Speed : +The speed to travel.

      + +
    • +
    +

    Return value

    + +

    #GROUP: +self

    +
    @@ -2621,14 +2781,14 @@ A table of route points.

    -

    Make the group to fly to a given point and hover.

    +

    Make the DCS Group to fly to a given point and hover.

    Parameters

    • -

      #Vec3 Point : -The destination point.

      +

      DCSTypes#Vec3 Point : +The destination point in Vec3 format.

    • @@ -2825,7 +2985,7 @@ The waypoint function to be called when the group moves over the waypoint. The w
      -GROUP:WayPointInitialize(WayPoint) +GROUP:WayPointInitialize()
      @@ -2837,14 +2997,6 @@ The waypoint function to be called when the group moves over the waypoint. The w Use the method @{Group@GROUP:WayPointExecute) to start the execution of the new mission plan. Note that when WayPointInitialize is called, the Mission of the group is RESTARTED!

      -

      Parameter

      -
        -
      • - -

        #number WayPoint :

        - -
      • -

      Return value

      #GROUP:

      @@ -2879,44 +3031,7 @@ The waypoint function to be called when the group moves over the waypoint. The w

      Return value

      -

      Controller#Controller:

      - - -
      -
    - -

    Type Time

    - -

    Type Unit

    - -

    Type Vec2

    - -

    Type Vec3

    -

    Field(s)

    -
    -
    - - - -Vec3.x - -
    -
    - - - -
    -
    -
    -
    - - - -Vec3.y - -
    -
    - +

    DCSController#Controller:

    diff --git a/Documentation/MISSION.html b/Documentation/MISSION.html index d20297316..0e909bbc5 100644 --- a/Documentation/MISSION.html +++ b/Documentation/MISSION.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • @@ -273,12 +287,6 @@ A CLIENT needs to be registered within the MISSION.SUCCESS - - - - MISSION.ShowBriefing(CLIENT, self, Client) - -

    Show the briefing of the MISSION to the CLIENT.

    @@ -1157,43 +1165,6 @@ local Mission = MISSIONSCHEDULER.AddMission( 'Rescue secret agent', 'Tactical', - -
    -
    -
    - - -MISSION.ShowBriefing(CLIENT, self, Client) - -
    -
    - -

    Show the briefing of the MISSION to the CLIENT.

    - -

    Parameters

    -
      -
    • - -

      CLIENT : -Client to show briefing to.

      - -
    • -
    • - -

      self :

      - -
    • -
    • - -

      Client :

      - -
    • -
    -

    Return value

    - - -

    CLIENT

    -
    diff --git a/Documentation/MOVEMENT.html b/Documentation/MOVEMENT.html index 2e1825e50..5d35d954c 100644 --- a/Documentation/MOVEMENT.html +++ b/Documentation/MOVEMENT.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Menu.html b/Documentation/Menu.html index c2e778b76..286cd3f1f 100644 --- a/Documentation/Menu.html +++ b/Documentation/Menu.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Message.html b/Documentation/Message.html index ecaa029d9..e7fb13b06 100644 --- a/Documentation/Message.html +++ b/Documentation/Message.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/MissileTrainer.html b/Documentation/MissileTrainer.html index 54a295ba7..8e21f7b5b 100644 --- a/Documentation/MissileTrainer.html +++ b/Documentation/MissileTrainer.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/NOTASK.html b/Documentation/NOTASK.html index d56be9578..8aeb474b4 100644 --- a/Documentation/NOTASK.html +++ b/Documentation/NOTASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/PICKUPTASK.html b/Documentation/PICKUPTASK.html index 3b5c01584..079600693 100644 --- a/Documentation/PICKUPTASK.html +++ b/Documentation/PICKUPTASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/ROUTETASK.html b/Documentation/ROUTETASK.html index 9ab3852b2..b53f143b3 100644 --- a/Documentation/ROUTETASK.html +++ b/Documentation/ROUTETASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/STAGE.html b/Documentation/STAGE.html index 985997808..c45a68326 100644 --- a/Documentation/STAGE.html +++ b/Documentation/STAGE.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Scheduler.html b/Documentation/Scheduler.html index cb5845838..41d757d5d 100644 --- a/Documentation/Scheduler.html +++ b/Documentation/Scheduler.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Scoring.html b/Documentation/Scoring.html index ad5556374..bcecc4626 100644 --- a/Documentation/Scoring.html +++ b/Documentation/Scoring.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Sead.html b/Documentation/Sead.html index 55b652c23..e8bf12d86 100644 --- a/Documentation/Sead.html +++ b/Documentation/Sead.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Set.html b/Documentation/Set.html index 078f9d754..e43329a31 100644 --- a/Documentation/Set.html +++ b/Documentation/Set.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Spawn.html b/Documentation/Spawn.html index b0c92b062..9d0eb5271 100644 --- a/Documentation/Spawn.html +++ b/Documentation/Spawn.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/StaticObject.html b/Documentation/StaticObject.html index 9f6847026..a49ce3be6 100644 --- a/Documentation/StaticObject.html +++ b/Documentation/StaticObject.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/TASK.html b/Documentation/TASK.html index fee28dd8f..301d33c96 100644 --- a/Documentation/TASK.html +++ b/Documentation/TASK.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/Unit.html b/Documentation/Unit.html index c55741f7e..b1d81bcd6 100644 --- a/Documentation/Unit.html +++ b/Documentation/Unit.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • @@ -67,7 +81,7 @@ @@ -75,7 +89,7 @@

    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 +

    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. @@ -86,11 +100,45 @@ If the DCS Unit object does not exist or is nil, the UNIT methods will return ni

    IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).

    +

    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().

    + +

    Additional UNIT APIs

    +

    The UNIT class comes with additional methods. Find below a summary.

    + +

    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.

    + +

    Position, Point

    +

    The UNIT class provides methods to obtain the current point or position of the DCS Unit. +The UNIT.GetPointVec2(), UNIT.GetPointVec3() will obtain the current location of the DCS Unit in a Vec2 (2D) or a Vec3 (3D) vector respectively. +If you want to obtain the complete 3D position including oriëntation and direction vectors, consult the UNIT.GetPositionVec3() method respectively.

    + +

    Alive

    +

    The UNIT.IsAlive(), UNIT.IsActive() methods determines if the DCS Unit is alive, meaning, it is existing and active.

    + +

    Test for other units in radius

    +

    One can test if another DCS Unit is within a given radius of the current DCS Unit, by using the UNIT.OtherUnitInRadius() method.

    + +

    More functions will be added

    +

    During the MOOSE development, more functions will be added. A complete list of the current functions is below.

    + + + +

    Global(s)

    diff --git a/Documentation/Zone.html b/Documentation/Zone.html index f3c5ffc36..447c617e5 100644 --- a/Documentation/Zone.html +++ b/Documentation/Zone.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Documentation/env.html b/Documentation/env.html index 959ef7350..5a02f5de9 100644 --- a/Documentation/env.html +++ b/Documentation/env.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/index.html b/Documentation/index.html index 7ac67ba31..e260bcbe9 100644 --- a/Documentation/index.html +++ b/Documentation/index.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • @@ -76,6 +90,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,7 +215,7 @@ @@ -220,6 +300,12 @@ + + + + @@ -245,6 +331,18 @@ + + + + + + + + diff --git a/Documentation/land.html b/Documentation/land.html index 5399503ee..82d512824 100644 --- a/Documentation/land.html +++ b/Documentation/land.html @@ -46,12 +46,15 @@
  • MOVEMENT
  • Menu
  • Message
  • +
  • MissileTrainer
  • NOTASK
  • PICKUPTASK
  • ROUTETASK
  • STAGE
  • +
  • Scheduler
  • Scoring
  • Sead
  • +
  • Set
  • Spawn
  • StaticObject
  • TASK
  • diff --git a/Documentation/routines.html b/Documentation/routines.html index 6dd92ef95..198b37402 100644 --- a/Documentation/routines.html +++ b/Documentation/routines.html @@ -21,6 +21,17 @@
  • CARGO
  • CleanUp
  • Client
  • +
  • DCSAirbase
  • +
  • DCSCoalitionObject
  • +
  • DCSCommand
  • +
  • DCSController
  • +
  • DCSGroup
  • +
  • DCSObject
  • +
  • DCSTask
  • +
  • DCSTypes
  • +
  • DCSUnit
  • +
  • DCSWorld
  • +
  • DCStimer
  • DEPLOYTASK
  • DESTROYBASETASK
  • DESTROYGROUPSTASK
  • @@ -45,9 +56,12 @@
  • Sead
  • Set
  • Spawn
  • +
  • StaticObject
  • TASK
  • Unit
  • Zone
  • +
  • env
  • +
  • land
  • routines
  • diff --git a/Embedded/Moose_Embedded.lua b/Embedded/Moose_Embedded.lua index 302dd40da..05b36b49a 100644 --- a/Embedded/Moose_Embedded.lua +++ b/Embedded/Moose_Embedded.lua @@ -3998,9 +3998,38 @@ function MENU_COALITION_COMMAND:Remove() self.ParentMenu.Menus[self.MenuPath] = nil return nil end ---- A GROUP class abstraction of a DCSGroup class. --- The GROUP class will take an abstraction of the DCSGroup class, providing more methods that can be done with a GROUP. +--- GROUP class. +-- +-- @{GROUP} class +-- ============== +-- 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. +-- +-- +-- 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. +-- +-- IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil). -- @module Group +-- @author FlightControl Include.File( "Routines" ) Include.File( "Base" ) @@ -4012,8 +4041,6 @@ Include.File( "Unit" ) -- @extends Base#BASE -- @field DCSGroup#Group DCSGroup The DCS group class. -- @field #string GroupName The name of the group. --- @field #number GroupID the ID of the group. --- @field #table Controller The controller of the group. GROUP = { ClassName = "GROUP", GroupName = "", @@ -4027,81 +4054,310 @@ GROUP = { -- @type DCSGroup -- @field id_ The ID of the group in DCS ---- The GROUPS structure contains references to all the created GROUP instances. -local GROUPS = {} - --- Create a new GROUP from a DCSGroup -- @param #GROUP self --- @param DCSGroup#Group DCSGroup The DCS Group +-- @param DCSGroup#Group GroupName The DCS Group name -- @return #GROUP self -function GROUP:New( DCSGroup ) +function GROUP:Register( GroupName ) local self = BASE:Inherit( self, BASE:New() ) - self:F( DCSGroup ) + self:F2( GroupName ) + self.GroupName = GroupName + return self +end - self.DCSGroup = DCSGroup - if self.DCSGroup and self.DCSGroup:isExist() then - self.GroupName = DCSGroup:getName() - self.GroupID = DCSGroup:getID() - self.Controller = DCSGroup:getController() - else - self:E( { "DCSGroup is nil or does not exist, cannot initialize GROUP!", self.DCSGroup } ) +-- 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:GetDCSGroup() + 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:GetDCSGroup() + + if DCSGroup then + local GroupIsAlive = DCSGroup:isExist() + self:T3( GroupIsAlive ) + return GroupIsAlive end - GROUPS[self.GroupID] = self - - return self + return nil end ---- Create a new GROUP from an existing group name. +--- 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 --- @param GroupName The name of the DCS Group. --- @return #GROUP self -function GROUP:NewFromName( GroupName ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( GroupName ) - - self.DCSGroup = Group.getByName( GroupName ) - if self.DCSGroup then - self.GroupName = self.DCSGroup:getName() - self.GroupID = self.DCSGroup:getID() - self.Controller = self.DCSGroup:getController() - end - - GROUPS[self.GroupID] = self - - return self +function GROUP:Destroy() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + + 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 ---- Create a new GROUP from an existing DCSUnit in the mission. +--- Returns category of the DCS Group. -- @param #GROUP self --- @param DCSUnit The DCSUnit. --- @return #GROUP self -function GROUP:NewFromDCSUnit( DCSUnit ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( DCSUnit ) +-- @return DCSGroup#Group.Category The category ID +function GROUP:GetCategory() + self:F2( self.GroupName ) - self.DCSGroup = DCSUnit:getGroup() - if self.DCSGroup then - self.GroupName = self.DCSGroup:getName() - self.GroupID = self.DCSGroup:getID() - self.Controller = self.DCSGroup:getController() + local DCSGroup = self:GetDCSGroup() + 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:GetDCSGroup() + 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:GetDCSGroup() + if DCSGroup then + local GroupCoalition = DCSGroup:getCoalition() + self:T3( GroupCoalition ) + return GroupCoalition + end + + return nil +end + +--- Returns the name of the DCS Group. +-- @param #GROUP self +-- @return #string The DCS Group name. +function GROUP:GetName() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupName = DCSGroup:getName() + self:T3( GroupName ) + return GroupName + end + + return nil +end + +--- Returns the DCS Group identifier. +-- @param #GROUP self +-- @return #number The identifier of the DCS Group. +function GROUP:GetID() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupID = DCSGroup:getID() + self:T3( GroupID ) + return GroupID + 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:GetDCSGroup() + + if DCSGroup then + local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) ) + self:T3( UnitFound.UnitName ) + self:T2( UnitFound ) + return UnitFound end - GROUPS[self.GroupID] = self - - return self + return nil end ---- Returns the name of the Group. +--- Returns the DCS Unit with number UnitNumber. +-- If the underlying DCS Unit does not exist, the method will return nil. . -- @param #GROUP self --- @return #string GroupName -function GROUP:GetName() +-- @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 GroupName = self.DCSGroup:getName() + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local DCSUnitFound = DCSGroup:getUnit( UnitNumber ) + self:T3( DCSUnitFound ) + return DCSUnitFound + end - return GroupName + 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:GetDCSGroup() + + 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:GetDCSGroup() + + 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:GetDCSGroup() + + 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:GetDCSGroup() + + if DCSGroup then + local DCSUnits = DCSGroup:getUnits() + self:T3( DCSUnits ) + return DCSUnits + end + + return nil +end + +--- Get the controller for the GROUP. +-- @param #GROUP self +-- @return DCSController#Controller +function GROUP:_GetController() + self:F2( { self.GroupName } ) + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupController = DCSGroup:getController() + self:T3( GroupController ) + return GroupController + end + + return nil +end --- Retrieve the group mission and allow to place function hooks within the mission waypoint plan. @@ -4109,7 +4365,6 @@ end -- Use the method @{Group@GROUP:WayPointExecute) to start the execution of the new mission plan. -- Note that when WayPointInitialize is called, the Mission of the group is RESTARTED! -- @param #GROUP self --- @param #number WayPoint -- @return #GROUP function GROUP:WayPointInitialize() @@ -4126,7 +4381,7 @@ end -- @param #function WayPointFunction The waypoint function to be called when the group moves over the waypoint. The waypoint function takes variable parameters. -- @return #GROUP function GROUP:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) - self:F( { WayPoint, WayPointIndex, WayPointFunction } ) + self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex ) self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg ) @@ -4139,7 +4394,7 @@ function GROUP:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionAr local DCSTask local DCSScript = {} - DCSScript[#DCSScript+1] = "local MissionGroup = GROUP.FindGroup( ... ) " + DCSScript[#DCSScript+1] = "local MissionGroup = GROUP:Find( ... ) " if FunctionArguments.n > 0 then DCSScript[#DCSScript+1] = FunctionString .. "( MissionGroup, " .. table.concat( FunctionArguments, "," ) .. ")" @@ -4153,7 +4408,7 @@ function GROUP:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionAr ), WayPointIndex ) - self:T( DCSTask ) + self:T3( DCSTask ) return DCSTask @@ -4179,7 +4434,7 @@ function GROUP:WayPointExecute( WayPoint, WaitTime ) table.remove( self.WayPoints, 1 ) end - self:T( self.WayPoints ) + self:T3( self.WayPoints ) self:SetTask( self:TaskRoute( self.WayPoints ), WaitTime ) @@ -4187,148 +4442,70 @@ function GROUP:WayPointExecute( WayPoint, WaitTime ) end - ---- Gets the DCSGroup of the GROUP. --- @param #GROUP self --- @return DCSGroup#Group The DCSGroup. -function GROUP:GetDCSGroup() - self:F( { self.GroupName } ) - self.DCSGroup = Group.getByName( self.GroupName ) - return self.DCSGroup -end - ---- Gets the DCS Unit of the GROUP. --- @param #GROUP self --- @param #number UnitNumber The unit index to be returned from the GROUP. --- @return #Unit The DCS Unit. -function GROUP:GetDCSUnit( UnitNumber ) - self:F( { self.GroupName, UnitNumber } ) - return self.DCSGroup:getUnit( UnitNumber ) - -end - ---- Gets the DCSUnits of the GROUP. --- @param #GROUP self --- @return #table The DCSUnits. -function GROUP:GetDCSUnits() - self:F( { self.GroupName } ) - return self.DCSGroup:getUnits() - -end - --- Activates a GROUP. -- @param #GROUP self function GROUP:Activate() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) trigger.action.activateGroup( self:GetDCSGroup() ) return self:GetDCSGroup() end ---- Gets the ID of the GROUP. --- @param #GROUP self --- @return #number The ID of the GROUP. -function GROUP:GetID() - self:F( self.GroupName ) - - return self.GroupID -end - ---- Gets the name of the GROUP. --- @param #GROUP self --- @return #string The name of the GROUP. -function GROUP:GetName() - self:F( self.GroupName ) - - return self.GroupName -end --- Gets the type name of the group. -- @param #GROUP self -- @return #string The type name of the group. function GROUP:GetTypeName() - self:F( self.GroupName ) + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() - return self.DCSGroup:getUnit(1):getTypeName() + if DCSGroup then + local GroupTypeName = DCSGroup:getUnit(1):getTypeName() + self:T3( GroupTypeName ) + return( GroupTypeName ) + end + + return nil end ---- Gets the callsign of the fist unit of the group. +--- Gets the CallSign of the first DCS Unit of the DCS Group. -- @param #GROUP self --- @return #string The callsign of the first unit of the group. +-- @return #string The CallSign of the first DCS Unit of the DCS Group. function GROUP:GetCallsign() - self:F( self.GroupName ) + self:F2( self.GroupName ) - return self.DCSGroup:getUnit(1):getCallsign() + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupCallSign = DCSGroup:getUnit(1):getCallsign() + self:T3( GroupCallSign ) + return GroupCallSign + end + + return nil end ---- Gets the current Point of the GROUP in VEC3 format. --- @return #Vec3 Current x,y and z position of the group. +--- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group. +-- @return DCSTypes#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. function GROUP:GetPointVec2() - self:F( self.GroupName ) + self:F2( self.GroupName ) - local GroupPoint = self:GetUnit(1):GetPointVec2() - self:T( GroupPoint ) - return GroupPoint + local GroupPointVec2 = self:GetUnit(1):GetPointVec2() + self:T3( GroupPointVec2 ) + return GroupPointVec2 end ---- Gets the current Point of the GROUP in VEC2 format. --- @return #Vec2 Current x and y position of the group in the 2D plane. -function GROUP:GetPointVec2() - self:F( self.GroupName ) - - local GroupPoint = self:GetUnit(1):GetPointVec2() - self:T( GroupPoint ) - return GroupPoint -end - ---- Gets the current Point of the GROUP in VEC3 format. --- @return #Vec3 Current Vec3 position of the group. +--- Returns the current point (Vec3 vector) of the first DCS Unit in the DCS Group. +-- @return DCSTypes#Vec3 Current Vec3 point of the first DCS Unit of the DCS Group. function GROUP:GetPointVec3() - self:F( self.GroupName ) + self:F2( self.GroupName ) - local GroupPoint = self:GetUnit(1):GetPointVec3() - self:T( GroupPoint ) - return GroupPoint + local GroupPointVec3 = self:GetUnit(1):GetPointVec3() + self:T3( GroupPointVec3 ) + return GroupPointVec3 end ---- Destroy a GROUP --- Note that this destroy method also raises a destroy event at run-time. --- So all event listeners will catch the destroy event of this GROUP. --- @param #GROUP self -function GROUP:Destroy() - self:F( self.GroupName ) - - for Index, UnitData in pairs( self.DCSGroup:getUnits() ) do - self:CreateEventCrash( timer.getTime(), UnitData ) - end - - self.DCSGroup:destroy() - self.DCSGroup = nil -end ---- Gets the DCS Unit. --- @param #GROUP self --- @param #number UnitNumber The number of the Unit to be returned. --- @return Unit#UNIT The DCS Unit. -function GROUP:GetUnit( UnitNumber ) - self:F( { self.GroupName, UnitNumber } ) - return UNIT:New( self.DCSGroup:getUnit( UnitNumber ) ) -end - ---- Returns the category name of the group. --- @param #GROUP self --- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -function GROUP:GetCategoryName() - self:F( self.GroupName ) - - local CategoryNames = { - [Group.Category.AIRPLANE] = "Airplane", - [Group.Category.HELICOPTER] = "Helicopter", - [Group.Category.GROUND] = "Ground Unit", - [Group.Category.SHIP] = "Ship", - } - - return CategoryNames[self.DCSGroup:getCategory()] -end -- Is Functions @@ -4337,73 +4514,85 @@ end -- @param #GROUP self -- @return #boolean Air category evaluation result. function GROUP:IsAir() - self:F() - - local IsAirResult = self.DCSGroup:getCategory() == Group.Category.AIRPLANE or self.DCSGroup:getCategory() == Group.Category.HELICOPTER + self:F2( self.GroupName ) - self:T( IsAirResult ) - return IsAirResult + local DCSGroup = self:GetDCSGroup() + + 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 group is alive. --- When the group exists at run-time, this method will return true, otherwise false. +--- Returns if the DCS Group contains Helicopters. -- @param #GROUP self --- @return #boolean Alive result. -function GROUP:IsAlive() - self:F() - - local IsAliveResult = self.DCSGroup and self.DCSGroup:isExist() - - self:T( IsAliveResult ) - return IsAliveResult -end - ---- Returns if the GROUP is a Helicopter. --- @param #GROUP self --- @return #boolean true if GROUP are Helicopters. +-- @return #boolean true if DCS Group contains Helicopters. function GROUP:IsHelicopter() - self:F2() + self:F2( self.GroupName ) - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.HELICOPTER + if DCSGroup then + local GroupCategory = DCSGroup:getCategory() + self:T2( GroupCategory ) + return GroupCategory == Group.Category.HELICOPTER + end + + return nil end ---- Returns if the GROUP are AirPlanes. +--- Returns if the DCS Group contains AirPlanes. -- @param #GROUP self --- @return #boolean true if GROUP are AirPlanes. +-- @return #boolean true if DCS Group contains AirPlanes. function GROUP:IsAirPlane() self:F2() - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.AIRPLANE + if DCSGroup then + local GroupCategory = DCSGroup:getCategory() + self:T2( GroupCategory ) + return GroupCategory == Group.Category.AIRPLANE + end + + return nil end ---- Returns if the GROUP are Ground troops. +--- Returns if the DCS Group contains Ground troops. -- @param #GROUP self --- @return #boolean true if GROUP are Ground troops. +-- @return #boolean true if DCS Group contains Ground troops. function GROUP:IsGround() self:F2() - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.GROUND + if DCSGroup then + local GroupCategory = DCSGroup:getCategory() + self:T2( GroupCategory ) + return GroupCategory == Group.Category.GROUND + end + + return nil end ---- Returns if the GROUP are Ships. +--- Returns if the DCS Group contains Ships. -- @param #GROUP self --- @return #boolean true if GROUP are Ships. +-- @return #boolean true if DCS Group contains Ships. function GROUP:IsShip() self:F2() - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.SHIP + 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. @@ -4411,18 +4600,24 @@ end -- @param #GROUP self -- @return #boolean All units on the ground result. function GROUP:AllOnGround() - self:F() + self:F2() - local AllOnGroundResult = true - - for Index, UnitData in pairs( self.DCSGroup:getUnits() ) do - if UnitData:inAir() then - AllOnGroundResult = false - end - end - - self:T( AllOnGroundResult ) - return AllOnGroundResult + local DCSGroup = self:GetDCSGroup() + + 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. @@ -4430,21 +4625,27 @@ end -- @param #GROUP self -- @return #number Maximum velocity found. function GROUP:GetMaxVelocity() - self:F() + self:F2() - local MaxVelocity = 0 - - for Index, UnitData in pairs( self.DCSGroup:getUnits() ) do - - local Velocity = UnitData:getVelocity() - local VelocityTotal = math.abs( Velocity.x ) + math.abs( Velocity.y ) + math.abs( Velocity.z ) - - if VelocityTotal < MaxVelocity then - MaxVelocity = VelocityTotal - end - end - - return MaxVelocity + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local MaxVelocity = 0 + + for Index, UnitData in pairs( DCSGroup:getUnits() ) do + + local Velocity = UnitData:getVelocity() + local VelocityTotal = math.abs( Velocity.x ) + math.abs( Velocity.y ) + math.abs( Velocity.z ) + + if VelocityTotal < MaxVelocity then + MaxVelocity = VelocityTotal + end + end + + return MaxVelocity + end + + return nil end --- Returns the current minimum height of the group. @@ -4452,7 +4653,7 @@ end -- @param #GROUP self -- @return #number Minimum height found. function GROUP:GetMinHeight() - self:F() + self:F2() end @@ -4461,7 +4662,7 @@ end -- @param #GROUP self -- @return #number Maximum height found. function GROUP:GetMaxHeight() - self:F() + self:F2() end @@ -4471,68 +4672,85 @@ end -- @param #GROUP self -- @return Group#GROUP self function GROUP:PopCurrentTask() - self:F() + self:F2() - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - Controller:popTask() - - return self + if DCSGroup then + local Controller = self:_GetController() + Controller:popTask() + return self + end + + return nil end --- Pushing Task on the queue from the group. -- @param #GROUP self -- @return Group#GROUP self function GROUP:PushTask( DCSTask, WaitTime ) - self:F() + self:F2() - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Group. - -- Controller:pushTask( DCSTask ) - - if not WaitTime then - Controller:pushTask( DCSTask ) - else - routines.scheduleFunction( Controller.pushTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) + if DCSGroup then + local Controller = self:_GetController() + + -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. + -- Therefore we schedule the functions to set the mission and options for the Group. + -- Controller:pushTask( DCSTask ) + + if WaitTime then + routines.scheduleFunction( Controller.pushTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) + else + Controller:pushTask( DCSTask ) + end + + return self end - - return self + + return nil end --- Clearing the Task Queue and Setting the Task on the queue from the group. -- @param #GROUP self -- @return Group#GROUP self function GROUP:SetTask( DCSTask, WaitTime ) - self:F( { DCSTask } ) + self:F2( { DCSTask } ) - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Group. - -- Controller.setTask( Controller, DCSTask ) - - if not WaitTime then - WaitTime = 1 + if DCSGroup then + + local Controller = self:_GetController() + + -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. + -- Therefore we schedule the functions to set the mission and options for the Group. + -- Controller.setTask( Controller, DCSTask ) + + if not WaitTime then + WaitTime = 1 + end + routines.scheduleFunction( Controller.setTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) + + return self end - routines.scheduleFunction( Controller.setTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) - return self + return nil end --- Return a condition section for a controlled task -- @param #GROUP self --- @param #Time time +-- @param DCSTime#Time time -- @param #string userFlag -- @param #boolean userFlagValue -- @param #string condition --- @param #Time duration +-- @param DCSTime#Time duration -- @param #number lastWayPoint -- return DCSTask#Task function GROUP:TaskCondition( time, userFlag, userFlagValue, condition, duration, lastWayPoint ) - self:F( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) + self:F2( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) local DCSStopCondition = {} DCSStopCondition.time = time @@ -4542,7 +4760,7 @@ function GROUP:TaskCondition( time, userFlag, userFlagValue, condition, duration DCSStopCondition.duration = duration DCSStopCondition.lastWayPoint = lastWayPoint - self:T( { DCSStopCondition } ) + self:T3( { DCSStopCondition } ) return DCSStopCondition end @@ -4552,7 +4770,7 @@ end -- @param #DCSStopCondition DCSStopCondition -- @return DCSTask#Task function GROUP:TaskControlled( DCSTask, DCSStopCondition ) - self:F( { DCSTask, DCSStopCondition } ) + self:F2( { DCSTask, DCSStopCondition } ) local DCSTaskControlled @@ -4564,7 +4782,7 @@ function GROUP:TaskControlled( DCSTask, DCSStopCondition ) } } - self:T( { DCSTaskControlled } ) + self:T3( { DCSTaskControlled } ) return DCSTaskControlled end @@ -4573,7 +4791,7 @@ end -- @param #list DCSTasks -- @return DCSTask#Task function GROUP:TaskCombo( DCSTasks ) - self:F( { DCSTasks } ) + self:F2( { DCSTasks } ) local DCSTaskCombo @@ -4584,7 +4802,7 @@ function GROUP:TaskCombo( DCSTasks ) } } - self:T( { DCSTaskCombo } ) + self:T3( { DCSTaskCombo } ) return DCSTaskCombo end @@ -4593,7 +4811,7 @@ end -- @param DCSCommand#Command DCSCommand -- @return DCSTask#Task function GROUP:TaskWrappedAction( DCSCommand, Index ) - self:F( { DCSCommand } ) + self:F2( { DCSCommand } ) local DCSTaskWrappedAction @@ -4607,7 +4825,7 @@ function GROUP:TaskWrappedAction( DCSCommand, Index ) }, } - self:T( { DCSTaskWrappedAction } ) + self:T3( { DCSTaskWrappedAction } ) return DCSTaskWrappedAction end @@ -4616,13 +4834,17 @@ end -- @param DCSCommand#Command DCSCommand -- @return #GROUP self function GROUP:SetCommand( DCSCommand ) - self:F( DCSCommand ) + self:F2( DCSCommand ) - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - Controller:setCommand( DCSCommand ) - - return self + if DCSGroup then + local Controller = self:_GetController() + Controller:setCommand( DCSCommand ) + return self + end + + return nil end --- Perform a switch waypoint command @@ -4631,7 +4853,7 @@ end -- @param #number ToWayPoint -- @return DCSTask#Task function GROUP:CommandSwitchWayPoint( FromWayPoint, ToWayPoint, Index ) - self:F( { FromWayPoint, ToWayPoint, Index } ) + self:F2( { FromWayPoint, ToWayPoint, Index } ) local CommandSwitchWayPoint = { id = 'SwitchWaypoint', @@ -4641,19 +4863,19 @@ function GROUP:CommandSwitchWayPoint( FromWayPoint, ToWayPoint, Index ) }, } - self:T( { CommandSwitchWayPoint } ) + self:T3( { CommandSwitchWayPoint } ) return CommandSwitchWayPoint end --- Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- @param #GROUP self --- @param #Vec2 Point The point to hold the position. +-- @param DCSTypes#Vec2 Point The point to hold the position. -- @param #number Altitude The altitude to hold the position. -- @param #number Speed The speed flying when holding the position. -- @return #GROUP self function GROUP:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) - self:F( { self.GroupName, Point, Altitude, Speed } ) + self:F2( { self.GroupName, Point, Altitude, Speed } ) -- pattern = enum AI.Task.OribtPattern, -- point = Vec2, @@ -4663,7 +4885,7 @@ function GROUP:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) local LandHeight = land.getHeight( Point ) - self:T( { LandHeight } ) + self:T3( { LandHeight } ) local DCSTask = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.CIRCLE, @@ -4697,11 +4919,16 @@ end -- @param #number Speed The speed flying when holding the position. -- @return #GROUP self function GROUP:TaskOrbitCircle( Altitude, Speed ) - self:F( { self.GroupName, Altitude, Speed } ) + self:F2( { self.GroupName, Altitude, Speed } ) - local GroupPoint = self:GetPointVec2() + local DCSGroup = self:GetDCSGroup() - return self:TaskOrbitCircleAtVec2( GroupPoint, Altitude, Speed ) + if DCSGroup then + local GroupPoint = self:GetPointVec2() + return self:TaskOrbitCircleAtVec2( GroupPoint, Altitude, Speed ) + end + + return nil end @@ -4711,7 +4938,7 @@ end -- @param #number Duration The maximum duration in seconds to hold the position. -- @return #GROUP self function GROUP:TaskHoldPosition() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) return self:TaskOrbitCircle( 30, 10 ) end @@ -4719,11 +4946,11 @@ end --- Land the group at a Vec2Point. -- @param #GROUP self --- @param #Vec2 Point The point where to land. +-- @param DCSTypes#Vec2 Point The point where to land. -- @param #number Duration The duration in seconds to stay on the ground. -- @return #GROUP self function GROUP:TaskLandAtVec2( Point, Duration ) - self:F( { self.GroupName, Point, Duration } ) + self:F2( { self.GroupName, Point, Duration } ) local DCSTask @@ -4733,7 +4960,7 @@ function GROUP:TaskLandAtVec2( Point, Duration ) DCSTask = { id = 'Land', params = { point = Point, durationFlag = false } } end - self:T( DCSTask ) + self:T3( DCSTask ) return DCSTask end @@ -4743,7 +4970,7 @@ end -- @param #number Duration The duration in seconds to stay on the ground. -- @return #GROUP self function GROUP:TaskLandAtZone( Zone, Duration, RandomPoint ) - self:F( { self.GroupName, Zone, Duration, RandomPoint } ) + self:F2( { self.GroupName, Zone, Duration, RandomPoint } ) local Point if RandomPoint then @@ -4754,7 +4981,7 @@ function GROUP:TaskLandAtZone( Zone, Duration, RandomPoint ) local DCSTask = self:TaskLandAtVec2( Point, Duration ) - self:T( DCSTask ) + self:T3( DCSTask ) return DCSTask end @@ -4764,7 +4991,7 @@ end -- @param Unit#UNIT The unit. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskAttackUnit( AttackUnit ) - self:F( { self.GroupName, AttackUnit } ) + self:F2( { self.GroupName, AttackUnit } ) -- AttackUnit = { -- id = 'AttackUnit', @@ -4787,7 +5014,7 @@ function GROUP:TaskAttackUnit( AttackUnit ) }, }, - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -4796,7 +5023,7 @@ end -- @param Group#GROUP AttackGroup The Group to be attacked. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskAttackGroup( AttackGroup ) - self:F( { self.GroupName, AttackGroup } ) + self:F2( { self.GroupName, AttackGroup } ) -- AttackGroup = { -- id = 'AttackGroup', @@ -4820,7 +5047,7 @@ function GROUP:TaskAttackGroup( AttackGroup ) }, }, - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -4830,7 +5057,7 @@ end -- @param DCSTypes#Distance Radius The radius of the zone to deploy the fire at. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskFireAtPoint( PointVec2, Radius ) - self:F( { self.GroupName, PointVec2, Radius } ) + self:F2( { self.GroupName, PointVec2, Radius } ) -- FireAtPoint = { -- id = 'FireAtPoint', @@ -4847,7 +5074,7 @@ function GROUP:TaskFireAtPoint( PointVec2, Radius ) } } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -4855,12 +5082,12 @@ end --- Move the group to a Vec2 Point, wait for a defined duration and embark a group. -- @param #GROUP self --- @param #Vec2 Point The point where to wait. +-- @param DCSTypes#Vec2 Point The point where to wait. -- @param #number Duration The duration in seconds to wait. -- @param #GROUP EmbarkingGroup The group to be embarked. -- @return DCSTask#Task The DCS task structure function GROUP:TaskEmbarkingAtVec2( Point, Duration, EmbarkingGroup ) - self:F( { self.GroupName, Point, Duration, EmbarkingGroup.DCSGroup } ) + self:F2( { self.GroupName, Point, Duration, EmbarkingGroup.DCSGroup } ) local DCSTask DCSTask = { id = 'Embarking', @@ -4874,17 +5101,17 @@ function GROUP:TaskEmbarkingAtVec2( Point, Duration, EmbarkingGroup ) } } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end --- Move to a defined Vec2 Point, and embark to a group when arrived within a defined Radius. -- @param #GROUP self --- @param #Vec2 Point The point where to wait. +-- @param DCSTypes#Vec2 Point The point where to wait. -- @param #number Radius The radius of the embarking zone around the Point. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskEmbarkToTransportAtVec2( Point, Radius ) - self:F( { self.GroupName, Point, Radius } ) + self:F2( { self.GroupName, Point, Radius } ) local DCSTask --DCSTask#Task DCSTask = { id = 'EmbarkToTransport', @@ -4894,7 +5121,7 @@ function GROUP:TaskEmbarkToTransportAtVec2( Point, Radius ) } } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -4903,12 +5130,12 @@ end -- @param #table TaskMission A table containing the mission task. -- @return DCSTask#Task function GROUP:TaskMission( TaskMission ) - self:F( Points ) + self:F2( Points ) local DCSTask DCSTask = { id = 'Mission', params = { TaskMission, }, } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -4917,22 +5144,73 @@ end -- @param #table Points A table of route points. -- @return DCSTask#Task function GROUP:TaskRoute( Points ) - self:F( Points ) + self:F2( Points ) local DCSTask DCSTask = { id = 'Mission', params = { route = { points = Points, }, }, } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end ---- Make the group to fly to a given point and hover. +--- Make the DCS Group to fly to a given point and hover. -- @param #GROUP self --- @param #Vec3 Point The destination point. +-- @param DCSTypes#Vec3 Point The destination point in Vec3 format. +-- @param #number Speed The speed to travel. +-- @return #GROUP self +function GROUP:TaskRouteToVec2( Point, Speed ) + self:F2( { Point, Speed } ) + + local GroupPoint = self:GetUnit( 1 ):GetPointVec2() + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Turning Point" + PointFrom.speed = Speed + PointFrom.speed_locked = true + PointFrom.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + + local PointTo = {} + PointTo.x = Point.x + PointTo.y = Point.y + PointTo.type = "Turning Point" + PointTo.action = "Fly Over Point" + PointTo.speed = Speed + PointTo.speed_locked = true + PointTo.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self +end + +--- Make the DCS Group to fly to a given point and hover. +-- @param #GROUP self +-- @param DCSTypes#Vec3 Point The destination point in Vec3 format. -- @param #number Speed The speed to travel. -- @return #GROUP self function GROUP:TaskRouteToVec3( Point, Speed ) - self:F( { Point, Speed } ) + self:F2( { Point, Speed } ) local GroupPoint = self:GetUnit( 1 ):GetPointVec3() @@ -4974,7 +5252,7 @@ function GROUP:TaskRouteToVec3( Point, Speed ) local Points = { PointFrom, PointTo } - self:T( Points ) + self:T3( Points ) self:Route( Points ) @@ -4988,16 +5266,20 @@ end -- @param #table GoPoints A table of Route Points. -- @return #GROUP self function GROUP:Route( GoPoints ) - self:F( GoPoints ) + self:F2( GoPoints ) - local Points = routines.utils.deepCopy( GoPoints ) - local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } - - --self.Controller.setTask( self.Controller, MissionTask ) - - routines.scheduleFunction( self.Controller.setTask, { self.Controller, MissionTask}, timer.getTime() + 1 ) - - return self + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local Points = routines.utils.deepCopy( GoPoints ) + local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } + local Controller = self:_GetController() + --Controller.setTask( Controller, MissionTask ) + routines.scheduleFunction( Controller.setTask, { Controller, MissionTask}, timer.getTime() + 1 ) + return self + end + + return nil end @@ -5012,50 +5294,57 @@ end -- @param #number Speed The speed. -- @param Base#FORMATION Formation The formation string. function GROUP:TaskRouteToZone( Zone, Randomize, Speed, Formation ) - self:F( Zone ) - - local GroupPoint = self:GetPointVec2() - - local PointFrom = {} - PointFrom.x = GroupPoint.x - PointFrom.y = GroupPoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Cone" - PointFrom.speed = 20 / 1.6 - + self:F2( Zone ) - local PointTo = {} - local ZonePoint - - if Randomize then - ZonePoint = Zone:GetRandomPointVec2() - else - ZonePoint = Zone:GetPointVec2() - end - - PointTo.x = ZonePoint.x - PointTo.y = ZonePoint.y - PointTo.type = "Turning Point" - - if Formation then - PointTo.action = Formation - else - PointTo.action = "Cone" - end - - if Speed then - PointTo.speed = Speed - else - PointTo.speed = 20 / 1.6 - end - - local Points = { PointFrom, PointTo } - - self:T( Points ) - - self:Route( Points ) - - return self + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + + local GroupPoint = self:GetPointVec2() + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Cone" + PointFrom.speed = 20 / 1.6 + + + local PointTo = {} + local ZonePoint + + if Randomize then + ZonePoint = Zone:GetRandomPointVec2() + else + ZonePoint = Zone:GetPointVec2() + end + + PointTo.x = ZonePoint.x + PointTo.y = ZonePoint.y + PointTo.type = "Turning Point" + + if Formation then + PointTo.action = Formation + else + PointTo.action = "Cone" + end + + if Speed then + PointTo.speed = Speed + else + PointTo.speed = 20 / 1.6 + end + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self + end + + return nil end -- Commands @@ -5073,7 +5362,7 @@ function GROUP:CommandDoScript( DoScript ) }, } - self:T( DCSDoScript ) + self:T3( DCSDoScript ) return DCSDoScript end @@ -5082,7 +5371,7 @@ end -- @param #GROUP self -- @return #table The MissionTemplate function GROUP:GetTaskMission() - self:F( self.GroupName ) + self:F2( self.GroupName ) return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template ) end @@ -5091,7 +5380,7 @@ end -- @param #GROUP self -- @return #table The mission route defined by points. function GROUP:GetTaskRoute() - self:F( self.GroupName ) + self:F2( self.GroupName ) return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) end @@ -5103,7 +5392,7 @@ end -- @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:F( { Begin, End } ) + self:F2( { Begin, End } ) local Points = {} @@ -5115,7 +5404,7 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius ) GroupName = self:GetName() end - self:T( { GroupName } ) + self:T3( { GroupName } ) local Template = _DATABASE.Templates.Groups[GroupName].Template @@ -5145,36 +5434,37 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius ) return nil end ---- Get the controller for the GROUP. --- @function _GetController --- @param #GROUP self --- @return Controller#Controller -function GROUP:_GetController() - - return self.DCSGroup:getController() - -end function GROUP:GetDetectedTargets() + self:F2( self.GroupName ) - return self:_GetController():getDetectedTargets() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + return self:_GetController():getDetectedTargets() + end + return nil end function GROUP:IsTargetDetected( DCSObject ) - - local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, - Controller.Detection.VISUAL, - Controller.Detection.OPTIC, - Controller.Detection.RADAR, - Controller.Detection.IRST, - Controller.Detection.RWR, - Controller.Detection.DLINK - ) - - return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity + = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, + Controller.Detection.VISUAL, + Controller.Detection.OPTIC, + Controller.Detection.RADAR, + Controller.Detection.IRST, + Controller.Detection.RWR, + Controller.Detection.DLINK + ) + return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity + end + + return nil end -- Options @@ -5183,137 +5473,182 @@ end -- @param #GROUP self -- @return #boolean function GROUP:OptionROEHoldFirePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() or self:IsGround() or self:IsShip() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() or self:IsGround() or self:IsShip() then + return true + end + + return false end - return false + return nil end --- Holding weapons. -- @param Group#GROUP self -- @return Group#GROUP self function GROUP:OptionROEHoldFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) + elseif self:IsGround() then + Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) + elseif self:IsShip() then + Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) + end + + return self end - return self + return nil end --- Can the GROUP attack returning on enemy fire? -- @param #GROUP self -- @return #boolean function GROUP:OptionROEReturnFirePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() or self:IsGround() or self:IsShip() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() or self:IsGround() or self:IsShip() then + return true + end + + return false end - return false + return nil end --- Return fire. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROEReturnFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) + elseif self:IsGround() then + Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) + elseif self:IsShip() then + Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) + end + + return self end - - return self + + return nil end --- Can the GROUP attack designated targets? -- @param #GROUP self -- @return #boolean function GROUP:OptionROEOpenFirePossible() - self:F( { self.GroupName } ) - - if self:IsAir() or self:IsGround() or self:IsShip() then - return true + self:F2( { self.GroupName } ) + + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() or self:IsGround() or self:IsShip() then + return true + end + + return false end - return false + return nil end --- Openfire. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROEOpenFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) + elseif self:IsGround() then + Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) + elseif self:IsShip() then + Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) + end - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) + return self end - - return self + + return nil end --- Can the GROUP attack targets of opportunity? -- @param #GROUP self -- @return #boolean function GROUP:OptionROEWeaponFreePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end --- Weapon free. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROEWeaponFree() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) + end + + return self end - return self + return nil end --- Can the GROUP ignore enemy fire? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTNoReactionPossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end @@ -5321,56 +5656,76 @@ end -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTNoReaction() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) + end + + return self end - return self + return nil end --- Can the GROUP evade using passive defenses? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTPassiveDefensePossible() - self:F( { self.GroupName } ) - - if self:IsAir() then - return true + self:F2( { self.GroupName } ) + + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end --- Evasion passive defense. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTPassiveDefense() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) + end + + return self end - return self + return nil end --- Can the GROUP evade on enemy fire? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTEvadeFirePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end @@ -5378,28 +5733,38 @@ end -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTEvadeFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + end + + return self end - return self + return nil end --- Can the GROUP evade on fire using vertical manoeuvres? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTVerticalPossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end @@ -5407,15 +5772,20 @@ end -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTVertical() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) + end + + return self end - return self + return nil end -- Message APIs @@ -5426,9 +5796,14 @@ end -- @param #Duration Duration The duration of the message. -- @return Message#MESSAGE function GROUP:Message( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - return MESSAGE:New( Message, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")", Duration, self:GetClassNameAndID() ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + return MESSAGE:New( Message, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")", Duration, self:GetClassNameAndID() ) + end + + return nil end --- Send a message to all coalitions. @@ -5437,9 +5812,14 @@ end -- @param #string Message The message text -- @param #Duration Duration The duration of the message. function GROUP:MessageToAll( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToAll() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToAll() + end + + return nil end --- Send a message to the red coalition. @@ -5448,9 +5828,14 @@ end -- @param #string Message The message text -- @param #Duration Duration The duration of the message. function GROUP:MessageToRed( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToRed() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToRed() + end + + return nil end --- Send a message to the blue coalition. @@ -5459,9 +5844,14 @@ end -- @param #string Message The message text -- @param #Duration Duration The duration of the message. function GROUP:MessageToBlue( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToBlue() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToBlue() + end + + return nil end --- Send a message to a client. @@ -5471,29 +5861,89 @@ end -- @param #Duration Duration The duration of the message. -- @param Client#CLIENT Client The client object receiving the message. function GROUP:MessageToClient( Message, Duration, Client ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToClient( Client ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToClient( Client ) + end + + return nil end - - - - ---- Find the created GROUP using the DCSGroup ID. If a GROUP was created with the DCSGroupID, the the GROUP instance will be returned. --- Otherwise nil will be returned. --- @param DCSGroup#Group Group --- @return #GROUP -function GROUP.FindGroup( DCSGroup ) - - local self = GROUPS[DCSGroup:getID()] -- Group#GROUP - self:T( self:GetClassNameAndID() ) - return self - -end - - ---- UNIT Classes +--- UNIT Class +-- +-- @{UNIT} class +-- ============== +-- The @{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. +-- +-- +-- 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). +-- +-- 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}(). +-- +-- Additional UNIT APIs +-- ==================== +-- The UNIT class comes with additional methods. Find below a summary. +-- +-- 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. +-- +-- Position, Point +-- --------------- +-- The UNIT class provides methods to obtain the current point or position of the DCS Unit. +-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetPointVec3}() will obtain the current location of the DCS Unit in a Vec2 (2D) or a Vec3 (3D) vector respectively. +-- If you want to obtain the complete 3D position including oriëntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. +-- +-- Alive +-- ----- +-- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. +-- +-- Test for other units in radius +-- ------------------------------ +-- One can test if another DCS Unit is within a given radius of the current DCS Unit, by using the @{#UNIT.OtherUnitInRadius}() method. +-- +-- More functions will be added +-- ---------------------------- +-- During the MOOSE development, more functions will be added. A complete list of the current functions is below. +-- +-- +-- +-- -- @module Unit +-- @author FlightControl Include.File( "Routines" ) Include.File( "Base" ) @@ -5501,7 +5951,7 @@ Include.File( "Message" ) --- The UNIT class -- @type UNIT --- @Extends Base#BASE +-- @extends Base#BASE -- @field #UNIT.FlareColor FlareColor -- @field #UNIT.SmokeColor SmokeColor UNIT = { @@ -5542,205 +5992,638 @@ UNIT = { -- @field White -- @field Orange -- @field Blue - +-- Registration. + --- Create a new UNIT from DCSUnit. -- @param #UNIT self -- @param DCSUnit#Unit DCSUnit +-- @param Database#DATABASE Database -- @return Unit#UNIT -function UNIT:New( DCSUnit ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( DCSUnit ) +function UNIT:Register( UnitName ) - self.DCSUnit = DCSUnit - if DCSUnit then - self.UnitName = DCSUnit:getName() - self.UnitID = DCSUnit:getID() - end - - return self + local self = BASE:Inherit( self, BASE:New() ) + self:F2( UnitName ) + self.UnitName = UnitName + return self end -function UNIT:IsAlive() - self:F( self.UnitName ) - - return ( self.DCSUnit and self.DCSUnit:isExist() ) +-- 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 function UNIT:GetDCSUnit() - self:F( self.DCSUnit ) - - return self.DCSUnit -end - -function UNIT:GetID() - self:F( self.UnitID ) - - return self.UnitID -end - - -function UNIT:GetName() - self:F( self.UnitName ) - - return self.UnitName -end - -function UNIT:GetPlayerName() - self:F( self.UnitName ) - local DCSUnit = Unit.getByName( self.UnitName ) - local PlayerName = DCSUnit:getPlayerName() - if PlayerName == nil then - PlayerName = "" + if DCSUnit then + return DCSUnit end + + return nil +end + +--- Returns coalition of the Unit. +-- @param Unit#UNIT self +-- @return DCSCoalitionObject#coalition.side The side of the coalition. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetCoalition() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() - return PlayerName -end -function UNIT:GetTypeName() - self:F( self.UnitName ) - - return self.DCSUnit:getTypeName() + if DCSUnit then + local UnitCoalition = DCSUnit:getCoalition() + self:T3( UnitCoalition ) + return UnitCoalition + end + + return nil end -function UNIT:GetPrefix() - self:F( self.UnitName ) - - local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) - self:T( UnitPrefix ) +--- Returns country of the Unit. +-- @param Unit#UNIT self +-- @return DCScountry#country.id The country identifier. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetCountry() + self:F2( self.UnitName ) - return UnitPrefix + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitCountry = DCSUnit:getCountry() + self:T3( UnitCountry ) + return UnitCountry + end + + return nil +end + + +--- Returns DCS Unit object name. +-- The function provides access to non-activated units too. +-- @param Unit#UNIT self +-- @return #string The name of the DCS Unit. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetName() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitName = self.UnitName + return UnitName + end + + return nil end -function UNIT:GetCallSign() - self:F( self.UnitName ) +--- Returns if the unit is alive. +-- @param Unit#UNIT self +-- @return #boolean true if Unit is alive. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:IsAlive() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitIsAlive = DCSUnit:isExist() + return UnitIsAlive + end - return self.DCSUnit:getCallsign() -end - - -function UNIT:GetPointVec2() - self:F( self.UnitName ) - - local UnitPos = self.DCSUnit:getPosition().p - - local UnitPoint = {} - UnitPoint.x = UnitPos.x - UnitPoint.y = UnitPos.z - - self:T( UnitPoint ) - return UnitPoint -end - - -function UNIT:GetPointVec3() - self:F( self.UnitName ) - - local UnitPos = self.DCSUnit:getPosition().p - - self:T( UnitPos ) - return UnitPos -end - -function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) - self:F( { self.UnitName, AwaitUnit.UnitName, Radius } ) - - local UnitPos = self:GetPointVec3() - local AwaitUnitPos = AwaitUnit:GetPointVec3() - - if (((UnitPos.x - AwaitUnitPos.x)^2 + (UnitPos.z - AwaitUnitPos.z)^2)^0.5 <= Radius) then - self:T( "true" ) - return true - else - self:T( "false" ) - return false - end - - self:T( "false" ) return false 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:GetDCSUnit() + + if DCSUnit then + + local UnitIsActive = DCSUnit:isActive() + return UnitIsActive + end + + 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:GetDCSUnit() + + if DCSUnit then + + local PlayerName = DCSUnit:getPlayerName() + if PlayerName == nil then + PlayerName = "" + end + return PlayerName + end + + return nil +end + +--- Returns the unit's unique identifier. +-- @param Unit#UNIT self +-- @return DCSUnit#Unit.ID Unit ID +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetID() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitID = DCSUnit:getID() + return UnitID + 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:GetDCSUnit() + + 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:GetDCSUnit() + + if DCSUnit then + local UnitGroup = DCSUnit:getGroup() + return UnitGroup + 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:GetDCSUnit() + + if DCSUnit then + local UnitCallSign = DCSUnit:getCallsign() + return UnitCallSign + 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:GetDCSUnit() + + 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:GetDCSUnit() + + if DCSUnit then + local UnitLife0 = DCSUnit:getLife0() + return UnitLife0 + end + + return 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:GetDCSUnit() + + if DCSUnit then + local UnitFuel = DCSUnit:getFuel() + return UnitFuel + 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:GetDCSUnit() + + 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:GetDCSUnit() + + 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 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:GetDCSUnit() + + if DCSUnit then + local UnitRadarOn, UnitRadarObject = DCSUnit:getRadar() + return UnitRadarOn, UnitRadarObject + end + + return nil, nil +end + +-- Need to add here functions to check if radar is on and which object etc. + +--- Returns unit descriptor. Descriptor type depends on unit category. +-- @param Unit#UNIT self +-- @return DCSUnit#Unit.Desc The Unit descriptor. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetDesc() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitDesc = DCSUnit:getDesc() + return UnitDesc + end + + return nil +end + + +--- Returns the type name of the DCS Unit. +-- @param Unit#UNIT self +-- @return #string The type name of the DCS Unit. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetTypeName() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitTypeName = DCSUnit:getTypeName() + self:T3( UnitTypeName ) + return UnitTypeName + end + + return nil +end + + + +--- 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:GetDCSUnit() + + if DCSUnit then + local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) + self:T3( UnitPrefix ) + return UnitPrefix + end + + return nil +end + + + +--- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the DCS Unit within the mission. +-- @param Unit#UNIT self +-- @return DCSTypes#Vec2 The 2D point vector of the DCS Unit. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetPointVec2() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitPointVec3 = DCSUnit:getPosition().p + + local UnitPointVec2 = {} + UnitPointVec2.x = UnitPointVec3.x + UnitPointVec2.y = UnitPointVec3.z + + self:T3( UnitPointVec2 ) + return UnitPointVec2 + end + + return nil +end + + +--- Returns the @{DCSTypes#Vec3} vector indicating the point in 3D of the DCS Unit within the mission. +-- @param Unit#UNIT self +-- @return DCSTypes#Vec3 The 3D point vector of the DCS Unit. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetPointVec3() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitPointVec3 = DCSUnit:getPosition().p + self:T3( UnitPointVec3 ) + return UnitPointVec3 + end + + return nil +end + +--- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the DCS Unit within the mission. +-- @param Unit#UNIT self +-- @return DCSTypes#Position The 3D position vectors of the DCS Unit. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetPositionVec3() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitPosition = DCSUnit:getPosition() + self:T3( UnitPosition ) + return UnitPosition + end + + return nil +end + +--- Returns the DCS Unit velocity vector. +-- @param Unit#UNIT self +-- @return DCSTypes#Vec3 The velocity vector +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetVelocity() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitVelocityVec3 = DCSUnit:getVelocity() + self:T3( UnitVelocityVec3 ) + return UnitVelocityVec3 + end + + return nil +end + +--- Returns true if the DCS Unit is in the air. +-- @param Unit#UNIT self +-- @return #boolean true if in the air. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:InAir() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitInAir = DCSUnit:inAir() + self:T3( UnitInAir ) + return UnitInAir + end + + return nil +end + +--- Returns the altitude of the DCS Unit. +-- @param Unit#UNIT self +-- @return DCSTypes#Distance The altitude of the DCS Unit. +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetAltitude() + self:F2() + + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitPointVec3 = DCSUnit:getPoint() --DCSTypes#Vec3 + return UnitPointVec3.y + end + + return nil +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:GetDCSUnit() + + if DCSUnit then + local UnitPos = self:GetPointVec3() + local AwaitUnitPos = AwaitUnit:GetPointVec3() + + if (((UnitPos.x - AwaitUnitPos.x)^2 + (UnitPos.z - AwaitUnitPos.z)^2)^0.5 <= Radius) then + self:T3( "true" ) + return true + else + self:T3( "false" ) + return false + end + end + + return nil +end + +--- Returns the DCS Unit category name as defined within the DCS Unit Descriptor. +-- @param Unit#UNIT self +-- @return #string The DCS Unit Category Name function UNIT:GetCategoryName() - return self.CategoryName[ self.DCSUnit:getDesc().category ] + local DCSUnit = self:GetDCSUnit() + + if DCSUnit then + local UnitCategoryName = self.CategoryName[ self:GetDesc().category ] + return UnitCategoryName + end + + return nil end --- Signal a flare at the position of the UNIT. -- @param #UNIT self function UNIT:Flare( FlareColor ) - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), FlareColor , 0 ) end --- Signal a white flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareWhite() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.White , 0 ) end --- Signal a yellow flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareYellow() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Yellow , 0 ) end --- Signal a green flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareGreen() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Green , 0 ) end --- Signal a red flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareRed() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Red, 0 ) end --- Smoke the UNIT. -- @param #UNIT self function UNIT:Smoke( SmokeColor ) - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), SmokeColor ) end --- Smoke the UNIT Green. -- @param #UNIT self function UNIT:SmokeGreen() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Green ) end --- Smoke the UNIT Red. -- @param #UNIT self function UNIT:SmokeRed() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Red ) end --- Smoke the UNIT White. -- @param #UNIT self function UNIT:SmokeWhite() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.White ) end --- Smoke the UNIT Orange. -- @param #UNIT self function UNIT:SmokeOrange() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Orange ) end --- Smoke the UNIT Blue. -- @param #UNIT self function UNIT:SmokeBlue() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Blue ) end @@ -5751,14 +6634,14 @@ end -- @param #UNIT self -- @return #boolean Air category evaluation result. function UNIT:IsAir() - self:F() + self:F2() local UnitDescriptor = self.DCSUnit:getDesc() - self:T( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) + self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER ) - self:T( IsAirResult ) + self:T3( IsAirResult ) return IsAirResult end @@ -5847,15 +6730,36 @@ end -- ================ -- 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} 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. --- --- CLIENT construction methods: --- ============================ --- Create a new CLIENT object with the @{#CLIENT.New} method: --- --- * @{#CLIENT.New}: Creates a new CLIENT object taking the name of the **DCSUnit** that is a client as defined within the mission editor. -- +-- 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 @@ -5867,7 +6771,7 @@ Include.File( "Message" ) --- The CLIENT class -- @type CLIENT --- @extends Base#BASE +-- @extends Unit#UNIT CLIENT = { ONBOARDSIDE = { NONE = 0, @@ -5888,7 +6792,35 @@ CLIENT = { } ---- Use this method to register new Clients within a mission. +--- 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. @@ -5898,21 +6830,38 @@ CLIENT = { -- 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:New( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:New( ClientName, ClientBriefing ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( ClientName, ClientBriefing ) +-- 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 ) - self.ClientName = ClientName - self:AddBriefing( ClientBriefing ) - self.MessageSwitch = true - - return self + 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 ) + + return self +end + + --- Transport defines that the Client is a Transport. Transports show cargo. -- @param #CLIENT self -- @return #CLIENT @@ -5926,13 +6875,38 @@ 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 +-- @return #CLIENT self function CLIENT:AddBriefing( ClientBriefing ) - self:F() + self:F( ClientBriefing ) self.ClientBriefing = ClientBriefing + self.ClientBriefingShown = false + return self end +--- Show the briefing of the MISSION to the 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.MissionBriefing then + Briefing = Briefing .. self.MissionBriefing + end + if self.ClientBriefing then + Briefing = Briefing .. "\n" .. self.ClientBriefing + end + Briefing = Briefing .. "\nPress [LEFT ALT]+[B] to view the complete mission briefing." + self:Message( Briefing, 30, self.ClientName .. '/MissionBriefing', "Briefing" ) + end + + return self +end + + --- Resets a CLIENT. -- @param #CLIENT self @@ -5942,21 +6916,6 @@ function CLIENT:Reset( ClientName ) self._Menus = {} 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( CallBack, ... ) - self:F() - - self.ClientAlive2 = false - self.ClientCallBack = CallBack - self.ClientParameters = arg - self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 ) - - return self -end - -- Is Functions --- Checks if the CLIENT is a multi-seated UNIT. @@ -5981,32 +6940,30 @@ function CLIENT:IsMultiSeated() return false end ---- Checks if client is alive and returns true or false. +--- Checks for a client alive event and calls a function on a continuous basis. -- @param #CLIENT self --- @returns #boolean Returns true if client is alive. -function CLIENT:IsAlive() - self:F( self.ClientName ) +-- @param #function CallBack Function. +-- @return #CLIENT +function CLIENT:Alive( CallBack, ... ) + self:F() - local ClientUnit = Unit.getByName( self.ClientName ) - - if ClientUnit and ClientUnit:isExist() then - self:T("true") - return true - end - - self:T( "false" ) - return false -end + self.ClientCallBack = CallBack + self.ClientParameters = arg + return self +end --- @param #CLIENT self function CLIENT:_AliveCheckScheduler() - self:F( { self.ClientName, self.ClientAlive2 } ) + self:F( { self.ClientName, self.ClientAlive2, self.ClientBriefingShown } ) - if self:IsAlive() then + if self:IsAlive() then -- Polymorphic call of UNIT if self.ClientAlive2 == false then - self:T("Calling Callback function") - self.ClientCallBack( self, unpack( self.ClientParameters ) ) + self:ShowBriefing() + if self.ClientCallBack then + self:T("Calling Callback function") + self.ClientCallBack( self, unpack( self.ClientParameters ) ) + end self.ClientAlive2 = true end else @@ -6127,7 +7084,7 @@ function CLIENT:GetClientGroupUnit() self:T( self.ClientDCSUnit ) if ClientDCSUnit and ClientDCSUnit:isExist() then - local ClientUnit = _DATABASE.Units[ self.ClientName ] + local ClientUnit = _DATABASE:FindUnit( self.ClientName ) self:T2( ClientUnit ) return ClientUnit end @@ -6147,109 +7104,6 @@ function CLIENT:GetClientGroupDCSUnit() end end --- TODO what is this??? check. possible double function. -function CLIENT:GetUnit() - self:F() - - return UNIT:New( self:GetClientGroupDCSUnit() ) -end - ---- Returns the position of the CLIENT in @{DCSTypes#Vec2} format.. --- @param #CLIENT self --- @return DCSTypes#Vec2 -function CLIENT:GetPointVec2() - self:F() - - local ClientGroupUnit = self:GetClientGroupDCSUnit() - - if ClientGroupUnit then - if ClientGroupUnit:isExist() then - local PointVec3 = ClientGroupUnit:getPoint() --DCSTypes#Vec3 - local PointVec2 = {} --DCSTypes#Vec2 - PointVec2.x = PointVec3.x - PointVec2.y = PointVec3.z - self:T( { PointVec2 } ) - return PointVec2 - end - end - - return nil -end - -function CLIENT:GetPointVec3() - self:F( self.ClientName ) - - local DCSUnit = Unit.getByName( self.ClientName ) - local UnitPos = DCSUnit:getPosition().p - - self:T( UnitPos ) - return UnitPos -end - -function CLIENT:GetID() - self:F( self.ClientName ) - - local DCSUnit = Unit.getByName( self.ClientName ) - local UnitID = DCSUnit:getID() - - self:T( UnitID ) - return UnitID -end - -function CLIENT:GetName() - self:F( self.ClientName ) - - self:T( self.ClientName ) - return self.ClientName -end - -function CLIENT:GetTypeName() - self:F( self.ClientName ) - - local DCSUnit = Unit.getByName( self.ClientName ) - local TypeName = DCSUnit:getTypeName() - - self:T( TypeName ) - return TypeName -end - - - ---- Returns the position of the CLIENT in @{DCSTypes#Vec3} format. --- @param #CLIENT self --- @return DCSTypes#Vec3 -function CLIENT:GetPositionVec3() - self:F() - - local ClientGroupUnit = self:GetClientGroupDCSUnit() - - if ClientGroupUnit then - if ClientGroupUnit:isExist() then - return ClientGroupUnit:getPosition() - end - end - - return nil -end - ---- Returns the altitude of the CLIENT. --- @param #CLIENT self --- @return DCSTypes#Distance -function CLIENT:GetAltitude() - self:F() - - local ClientGroupUnit = self:GetClientGroupDCSUnit() - - if ClientGroupUnit then - if ClientGroupUnit:isExist() then - local PointVec3 = ClientGroupUnit:getPoint() --DCSTypes#Vec3 - return PointVec3.y - end - end - - return nil -end - --- Evaluates if the CLIENT is a transport. -- @param #CLIENT self @@ -6296,7 +7150,7 @@ end -- @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. function CLIENT:Message( Message, MessageDuration, MessageId, MessageCategory, MessageInterval ) - self:F() + self:F( { Message, MessageDuration, MessageId, MessageCategory, MessageInterval } ) if not self.MenuMessages then if self:GetClientGroupID() then @@ -6420,18 +7274,14 @@ DATABASE = { ClientsByID = {}, }, DCSUnits = {}, - DCSUnitsAlive = {}, DCSGroups = {}, - DCSGroupsAlive = {}, - Units = {}, - UnitsAlive = {}, - Groups = {}, - GroupsAlive = {}, + UNITS = {}, + GROUPS = {}, NavPoints = {}, Statics = {}, Players = {}, PlayersAlive = {}, - Clients = {}, + CLIENTS = {}, ClientsAlive = {}, Filter = { Coalitions = nil, @@ -6499,168 +7349,67 @@ function DATABASE:New() return self end ---- Builds a set of units of coalitons. --- Possible current coalitions are red, blue and neutral. +--- Finds a Unit based on the Unit Name. -- @param #DATABASE self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #DATABASE self -function DATABASE:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self +-- @param #string UnitName +-- @return Unit#UNIT The found Unit. +function DATABASE:FindUnit( UnitName ) + + local UnitFound = self.UNITS[UnitName] + return UnitFound end ---- Builds a set of units out of categories. --- Possible current categories are plane, helicopter, ground, ship. +--- Adds a Unit based on the Unit Name in the DATABASE. -- @param #DATABASE self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #DATABASE self -function DATABASE:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self +function DATABASE:AddUnit( DCSUnit, DCSUnitName ) + + self.DCSUnits[DCSUnitName] = DCSUnit + self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) end ---- Builds a set of units of defined unit types. --- Possible current types are those types known within DCS world. +--- Deletes a Unit from the DATABASE based on the Unit Name. -- @param #DATABASE self --- @param #string Types Can take those type strings known within DCS world. --- @return #DATABASE self -function DATABASE:FilterTypes( Types ) - if not self.Filter.Types then - self.Filter.Types = {} - end - if type( Types ) ~= "table" then - Types = { Types } - end - for TypeID, Type in pairs( Types ) do - self.Filter.Types[Type] = Type - end - return self +function DATABASE:DeleteUnit( DCSUnitName ) + + self.DCSUnits[DCSUnitName] = nil end ---- Builds a set of units of defined countries. --- Possible current countries are those known within DCS world. +--- Finds a CLIENT based on the ClientName. -- @param #DATABASE self --- @param #string Countries Can take those country strings known within DCS world. --- @return #DATABASE self -function DATABASE:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self +-- @param #string ClientName +-- @return Client#CLIENT The found CLIENT. +function DATABASE:FindClient( ClientName ) + + local ClientFound = self.CLIENTS[ClientName] + return ClientFound end ---- Builds a set of units of defined unit prefixes. --- All the units starting with the given prefixes will be included within the set. +--- Adds a CLIENT based on the ClientName in the DATABASE. -- @param #DATABASE self --- @param #string Prefixes The prefix of which the unit name starts with. --- @return #DATABASE self -function DATABASE:FilterUnitPrefixes( Prefixes ) - if not self.Filter.UnitPrefixes then - self.Filter.UnitPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.UnitPrefixes[Prefix] = Prefix - end - return self +function DATABASE:AddClient( ClientName ) + + self.CLIENTS[ClientName] = CLIENT:Register( ClientName ) + self:E( self.CLIENTS[ClientName]:GetClassNameAndID() ) end ---- Builds a set of units of defined group prefixes. --- All the units starting with the given group prefixes will be included within the set. +--- Finds a GROUP based on the GroupName. -- @param #DATABASE self --- @param #string Prefixes The prefix of which the group name where the unit belongs to starts with. --- @return #DATABASE self -function DATABASE:FilterGroupPrefixes( Prefixes ) - if not self.Filter.GroupPrefixes then - self.Filter.GroupPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.GroupPrefixes[Prefix] = Prefix - end - return self +-- @param #string GroupName +-- @return Group#GROUP The found GROUP. +function DATABASE:FindGroup( GroupName ) + + local GroupFound = self.GROUPS[GroupName] + return GroupFound end ---- Starts the filtering. +--- Adds a GROUP based on the GroupName in the DATABASE. -- @param #DATABASE self --- @return #DATABASE self -function DATABASE:FilterStart() +function DATABASE:AddGroup( DCSGroup, GroupName ) - if _DATABASE then - -- OK, we have a _DATABASE - -- Now use the different filters to build the set. - -- We first take ALL of the Units of the _DATABASE. - - self:E( { "Adding Database Datapoints with filters" } ) - for DCSUnitName, DCSUnit in pairs( _DATABASE.DCSUnits ) do - - if self:_IsIncludeDCSUnit( DCSUnit ) then - - self:E( { "Adding Unit:", DCSUnitName } ) - self.DCSUnits[DCSUnitName] = _DATABASE.DCSUnits[DCSUnitName] - self.Units[DCSUnitName] = _DATABASE.Units[DCSUnitName] - - if _DATABASE.DCSUnitsAlive[DCSUnitName] then - self.DCSUnitsAlive[DCSUnitName] = _DATABASE.DCSUnitsAlive[DCSUnitName] - self.UnitsAlive[DCSUnitName] = _DATABASE.UnitsAlive[DCSUnitName] - end - - end - end - - for DCSGroupName, DCSGroup in pairs( _DATABASE.DCSGroups ) do - - --if self:_IsIncludeDCSGroup( DCSGroup ) then - self:E( { "Adding Group:", DCSGroupName } ) - self.DCSGroups[DCSGroupName] = _DATABASE.DCSGroups[DCSGroupName] - self.Groups[DCSGroupName] = _DATABASE.Groups[DCSGroupName] - --end - - if _DATABASE.DCSGroupsAlive[DCSGroupName] then - self.DCSGroupsAlive[DCSGroupName] = _DATABASE.DCSGroupsAlive[DCSGroupName] - self.GroupsAlive[DCSGroupName] = _DATABASE.GroupsAlive[DCSGroupName] - end - end - - for DCSUnitName, Client in pairs( _DATABASE.Clients ) do - self:E( { "Adding Client for Unit:", DCSUnitName } ) - self.Clients[DCSUnitName] = _DATABASE.Clients[DCSUnitName] - end - - else - self:E( "There is a structural error in MOOSE. No _DATABASE has been defined! Cannot build this custom DATABASE." ) - end - - return self + self.DCSGroups[GroupName] = DCSGroup + self.GROUPS[GroupName] = GROUP:Register( GroupName ) end - --- Instantiate new Groups within the DCSRTE. -- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined: -- SpawnCountryID, SpawnCategoryID @@ -6692,7 +7441,7 @@ function DATABASE:Spawn( SpawnTemplate ) SpawnTemplate.SpawnCategoryID = SpawnCategoryID - local SpawnGroup = GROUP:New( Group.getByName( SpawnTemplate.name ) ) + local SpawnGroup = GROUP:Register( SpawnTemplate.name ) return SpawnGroup end @@ -6787,7 +7536,7 @@ end -- @return #DATABASE self function DATABASE:_RegisterDatabase() - local CoalitionsData = { AlivePlayersRed = coalition.getGroups( coalition.side.RED ), AlivePlayersBlue = coalition.getGroups( coalition.side.BLUE ) } + local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ) } for CoalitionId, CoalitionData in pairs( CoalitionsData ) do for DCSGroupId, DCSGroup in pairs( CoalitionData ) do @@ -6795,43 +7544,29 @@ function DATABASE:_RegisterDatabase() local DCSGroupName = DCSGroup:getName() self:E( { "Register Group:", DCSGroup, DCSGroupName } ) - self.DCSGroups[DCSGroupName] = DCSGroup - self.Groups[DCSGroupName] = GROUP:New( DCSGroup ) - - if self:_IsAliveDCSGroup(DCSGroup) then - self:E( { "Register Alive Group:", DCSGroup, DCSGroupName } ) - self.DCSGroupsAlive[DCSGroupName] = DCSGroup - self.GroupsAlive[DCSGroupName] = self.Groups[DCSGroupName] - end - + self:AddGroup( DCSGroup, DCSGroupName ) + for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do local DCSUnitName = DCSUnit:getName() self:E( { "Register Unit:", DCSUnit, DCSUnitName } ) - - self.DCSUnits[DCSUnitName] = DCSUnit - self.Units[DCSUnitName] = UNIT:New( DCSUnit ) - - if self:_IsAliveDCSUnit(DCSUnit) then - self:E( { "Register Alive Unit:", DCSUnit, DCSUnitName } ) - self.DCSUnitsAlive[DCSUnitName] = DCSUnit - self.UnitsAlive[DCSUnitName] = self.Units[DCSUnitName] - end + self:AddUnit( DCSUnit, DCSUnitName ) end else - self:E( "Group does not exist: " .. DCSGroup ) + self:E( { "Group does not exist: ", DCSGroup } ) end - for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do - self.Clients[ClientName] = CLIENT:New( ClientName ) - end end end + + for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do + self:E( { "Adding Client:", ClientName } ) + self:AddClient( ClientName ) + end return self end - --- Events --- Handles the OnBirth event for the alive units set. @@ -6842,15 +7577,8 @@ function DATABASE:_EventOnBirth( Event ) if Event.IniDCSUnit then if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then - self.DCSUnits[Event.IniDCSUnitName] = Event.IniDCSUnit - self.DCSUnitsAlive[Event.IniDCSUnitName] = Event.IniDCSUnit - self.Units[Event.IniDCSUnitName] = UNIT:New( Event.IniDCSUnit ) - - --if not self.DCSGroups[Event.IniDCSGroupName] then - -- self.DCSGroups[Event.IniDCSGroupName] = Event.IniDCSGroupName - -- self.DCSGroupsAlive[Event.IniDCSGroupName] = Event.IniDCSGroupName - -- self.Groups[Event.IniDCSGroupName] = GROUP:New( Event.IniDCSGroup ) - --end + self:AddUnit( Event.IniDCSUnit, Event.IniDCSUnitName ) + self:AddGroup( Event.IniDCSGroup, Event.IniDCSGroupName ) self:_EventOnPlayerEnterUnit( Event ) end end @@ -6863,9 +7591,9 @@ function DATABASE:_EventOnDeadOrCrash( Event ) self:F( { Event } ) if Event.IniDCSUnit then - if self.DCSUnitsAlive[Event.IniDCSUnitName] then - self.DCSUnits[Event.IniDCSUnitName] = nil - self.DCSUnitsAlive[Event.IniDCSUnitName] = nil + if self.DCSUnits[Event.IniDCSUnitName] then + self:DeleteUnit( Event.IniDCSUnitName ) + -- add logic to correctly remove a group once all units are destroyed... end end end @@ -6881,7 +7609,7 @@ function DATABASE:_EventOnPlayerEnterUnit( Event ) if not self.PlayersAlive[Event.IniDCSUnitName] then self:E( { "Add player for unit:", Event.IniDCSUnitName, Event.IniDCSUnit:getPlayerName() } ) self.PlayersAlive[Event.IniDCSUnitName] = Event.IniDCSUnit:getPlayerName() - self.ClientsAlive[Event.IniDCSUnitName] = _DATABASE.Clients[ Event.IniDCSUnitName ] + self.ClientsAlive[Event.IniDCSUnitName] = self.CLIENTS[ Event.IniDCSUnitName ] end end end @@ -6953,10 +7681,10 @@ end -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called when there is an alive unit in the database. The function needs to accept a UNIT parameter. -- @return #DATABASE self -function DATABASE:ForEachDCSUnitAlive( IteratorFunction, ... ) +function DATABASE:ForEachDCSUnit( IteratorFunction, ... ) self:F( arg ) - self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) + self:ForEach( IteratorFunction, arg, self.DCSUnits ) return self end @@ -6981,7 +7709,7 @@ end function DATABASE:ForEachClient( IteratorFunction, ... ) self:F( arg ) - self:ForEach( IteratorFunction, arg, self.Clients ) + self:ForEach( IteratorFunction, arg, self.CLIENTS ) return self end @@ -6991,7 +7719,7 @@ function DATABASE:ScanEnvironment() self:F() self.Navpoints = {} - self.Units = {} + self.UNITS = {} --Build routines.db.units and self.Navpoints for coa_name, coa_data in pairs(env.mission.coalition) do @@ -7164,7 +7892,6 @@ function DATABASE:TraceDatabase() self:F() self:T( { "DCSUnits:", self.DCSUnits } ) - self:T( { "DCSUnitsAlive:", self.DCSUnitsAlive } ) end @@ -9506,7 +10233,7 @@ end function STAGEBRIEF:Execute( Mission, Client, Task ) local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) self:F() - Mission:ShowBriefing( Client ) + Client:ShowBriefing() self.StageBriefingTime = timer.getTime() return Valid end @@ -11742,25 +12469,6 @@ function MISSION:AddGoalFunction( GoalFunction ) self.GoalFunction = GoalFunction end ---- Show the briefing of the MISSION to the CLIENT. --- @param CLIENT Client to show briefing to. --- @return CLIENT -function MISSION:ShowBriefing( Client ) - self:F( { Client.ClientName } ) - - if not Client.ClientBriefingShown then - Client.ClientBriefingShown = true - local Briefing = self.MissionBriefing - if Client.ClientBriefing then - Briefing = Briefing .. "\n" .. Client.ClientBriefing - end - Briefing = Briefing .. "\n (Press [LEFT ALT]+[B] to view the graphical documentation.)" - Client:Message( Briefing, 30, self.Name .. '/MissionBriefing', "Command: Mission Briefing" ) - end - - return Client -end - --- Register a new @{CLIENT} to participate within the mission. -- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}. -- @return CLIENT @@ -15117,7 +15825,7 @@ function ESCORT:_ReportTargetsScheduler() self:T( EscortObject ) if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then - local EscortTargetUnit = UNIT:New( EscortObject ) + local EscortTargetUnit = UNIT:Find( EscortObject ) local EscortTargetUnitName = EscortTargetUnit:GetName() @@ -15734,8 +16442,8 @@ function MISSILETRAINER:_EventShot( Event ) local Client = self.DBClients[TrainerTargetDCSUnitName] if Client then - local TrainerSourceUnit = UNIT:New(TrainerSourceDCSUnit) - local TrainerTargetUnit = UNIT:New(TrainerTargetDCSUnit) + local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit ) + local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit ) if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then diff --git a/Moose/Client.lua b/Moose/Client.lua index b1d4d2e24..ab5bd0a6f 100644 --- a/Moose/Client.lua +++ b/Moose/Client.lua @@ -4,15 +4,36 @@ -- ================ -- 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} 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. --- --- CLIENT construction methods: --- ============================ --- Create a new CLIENT object with the @{#CLIENT.New} method: --- --- * @{#CLIENT.New}: Creates a new CLIENT object taking the name of the **DCSUnit** that is a client as defined within the mission editor. -- +-- 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 @@ -45,7 +66,35 @@ CLIENT = { } ---- Use this method to register new Clients within a mission. +--- 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. @@ -55,26 +104,34 @@ CLIENT = { -- 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:New( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:New( ClientName, ClientBriefing ) - self = _DATABASE:FindClient( ClientName ) +-- 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 ) - self:F( ClientName, ClientBriefing ) - self.ClientName = ClientName - self:AddBriefing( ClientBriefing ) - self.MessageSwitch = true + if ClientFound then + ClientFound:F( { ClientName, ClientBriefing } ) + ClientFound:AddBriefing( ClientBriefing ) + ClientFound.MessageSwitch = true - return self + 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 ) + return self end @@ -92,13 +149,38 @@ 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 +-- @return #CLIENT self function CLIENT:AddBriefing( ClientBriefing ) - self:F() + self:F( ClientBriefing ) self.ClientBriefing = ClientBriefing + self.ClientBriefingShown = false + return self end +--- Show the briefing of the MISSION to the 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.MissionBriefing then + Briefing = Briefing .. self.MissionBriefing + end + if self.ClientBriefing then + Briefing = Briefing .. "\n" .. self.ClientBriefing + end + Briefing = Briefing .. "\nPress [LEFT ALT]+[B] to view the complete mission briefing." + self:Message( Briefing, 30, self.ClientName .. '/MissionBriefing', "Briefing" ) + end + + return self +end + + --- Resets a CLIENT. -- @param #CLIENT self @@ -108,21 +190,6 @@ function CLIENT:Reset( ClientName ) self._Menus = {} 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( CallBack, ... ) - self:F() - - self.ClientAlive2 = false - self.ClientCallBack = CallBack - self.ClientParameters = arg - self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 ) - - return self -end - -- Is Functions --- Checks if the CLIENT is a multi-seated UNIT. @@ -147,15 +214,30 @@ function CLIENT:IsMultiSeated() 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( CallBack, ... ) + self:F() + + self.ClientCallBack = CallBack + self.ClientParameters = arg + + return self +end --- @param #CLIENT self function CLIENT:_AliveCheckScheduler() - self:F( { self.ClientName, self.ClientAlive2 } ) + self:F( { self.ClientName, self.ClientAlive2, self.ClientBriefingShown } ) if self:IsAlive() then -- Polymorphic call of UNIT if self.ClientAlive2 == false then - self:T("Calling Callback function") - self.ClientCallBack( self, unpack( self.ClientParameters ) ) + self:ShowBriefing() + if self.ClientCallBack then + self:T("Calling Callback function") + self.ClientCallBack( self, unpack( self.ClientParameters ) ) + end self.ClientAlive2 = true end else @@ -276,7 +358,7 @@ function CLIENT:GetClientGroupUnit() self:T( self.ClientDCSUnit ) if ClientDCSUnit and ClientDCSUnit:isExist() then - local ClientUnit = _DATABASE.Units[ self.ClientName ] + local ClientUnit = _DATABASE:FindUnit( self.ClientName ) self:T2( ClientUnit ) return ClientUnit end @@ -342,7 +424,7 @@ end -- @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. function CLIENT:Message( Message, MessageDuration, MessageId, MessageCategory, MessageInterval ) - self:F() + self:F( { Message, MessageDuration, MessageId, MessageCategory, MessageInterval } ) if not self.MenuMessages then if self:GetClientGroupID() then diff --git a/Moose/Database.lua b/Moose/Database.lua index b725d9728..1814af91f 100644 --- a/Moose/Database.lua +++ b/Moose/Database.lua @@ -83,13 +83,13 @@ DATABASE = { }, DCSUnits = {}, DCSGroups = {}, - Units = {}, - Groups = {}, + UNITS = {}, + GROUPS = {}, NavPoints = {}, Statics = {}, Players = {}, PlayersAlive = {}, - Clients = {}, + CLIENTS = {}, ClientsAlive = {}, Filter = { Coalitions = nil, @@ -163,7 +163,7 @@ end -- @return Unit#UNIT The found Unit. function DATABASE:FindUnit( UnitName ) - local UnitFound = self.Units[UnitName] + local UnitFound = self.UNITS[UnitName] return UnitFound end @@ -172,7 +172,7 @@ end function DATABASE:AddUnit( DCSUnit, DCSUnitName ) self.DCSUnits[DCSUnitName] = DCSUnit - self.Units[DCSUnitName] = UNIT:Register( DCSUnitName ) + self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) end --- Deletes a Unit from the DATABASE based on the Unit Name. @@ -188,7 +188,7 @@ end -- @return Client#CLIENT The found CLIENT. function DATABASE:FindClient( ClientName ) - local ClientFound = self.Clients[ClientName] + local ClientFound = self.CLIENTS[ClientName] return ClientFound end @@ -196,7 +196,26 @@ end -- @param #DATABASE self function DATABASE:AddClient( ClientName ) - self.Clients[ClientName] = CLIENT:Register( ClientName ) + self.CLIENTS[ClientName] = CLIENT:Register( ClientName ) + self:E( self.CLIENTS[ClientName]:GetClassNameAndID() ) +end + +--- Finds a GROUP based on the GroupName. +-- @param #DATABASE self +-- @param #string GroupName +-- @return Group#GROUP The found GROUP. +function DATABASE:FindGroup( GroupName ) + + local GroupFound = self.GROUPS[GroupName] + return GroupFound +end + +--- Adds a GROUP based on the GroupName in the DATABASE. +-- @param #DATABASE self +function DATABASE:AddGroup( DCSGroup, GroupName ) + + self.DCSGroups[GroupName] = DCSGroup + self.GROUPS[GroupName] = GROUP:Register( GroupName ) end --- Instantiate new Groups within the DCSRTE. @@ -230,7 +249,7 @@ function DATABASE:Spawn( SpawnTemplate ) SpawnTemplate.SpawnCategoryID = SpawnCategoryID - local SpawnGroup = GROUP:New( Group.getByName( SpawnTemplate.name ) ) + local SpawnGroup = GROUP:Register( SpawnTemplate.name ) return SpawnGroup end @@ -333,8 +352,7 @@ function DATABASE:_RegisterDatabase() local DCSGroupName = DCSGroup:getName() self:E( { "Register Group:", DCSGroup, DCSGroupName } ) - self.DCSGroups[DCSGroupName] = DCSGroup - self.Groups[DCSGroupName] = GROUP:New( DCSGroup ) + self:AddGroup( DCSGroup, DCSGroupName ) for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do @@ -368,12 +386,7 @@ function DATABASE:_EventOnBirth( Event ) if Event.IniDCSUnit then if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then self:AddUnit( Event.IniDCSUnit, Event.IniDCSUnitName ) - - --if not self.DCSGroups[Event.IniDCSGroupName] then - -- self.DCSGroups[Event.IniDCSGroupName] = Event.IniDCSGroupName - -- self.DCSGroupsAlive[Event.IniDCSGroupName] = Event.IniDCSGroupName - -- self.Groups[Event.IniDCSGroupName] = GROUP:New( Event.IniDCSGroup ) - --end + self:AddGroup( Event.IniDCSGroup, Event.IniDCSGroupName ) self:_EventOnPlayerEnterUnit( Event ) end end @@ -388,6 +401,7 @@ function DATABASE:_EventOnDeadOrCrash( Event ) if Event.IniDCSUnit then if self.DCSUnits[Event.IniDCSUnitName] then self:DeleteUnit( Event.IniDCSUnitName ) + -- add logic to correctly remove a group once all units are destroyed... end end end @@ -403,7 +417,7 @@ function DATABASE:_EventOnPlayerEnterUnit( Event ) if not self.PlayersAlive[Event.IniDCSUnitName] then self:E( { "Add player for unit:", Event.IniDCSUnitName, Event.IniDCSUnit:getPlayerName() } ) self.PlayersAlive[Event.IniDCSUnitName] = Event.IniDCSUnit:getPlayerName() - self.ClientsAlive[Event.IniDCSUnitName] = _DATABASE.Clients[ Event.IniDCSUnitName ] + self.ClientsAlive[Event.IniDCSUnitName] = self.CLIENTS[ Event.IniDCSUnitName ] end end end @@ -503,7 +517,7 @@ end function DATABASE:ForEachClient( IteratorFunction, ... ) self:F( arg ) - self:ForEach( IteratorFunction, arg, self.Clients ) + self:ForEach( IteratorFunction, arg, self.CLIENTS ) return self end @@ -513,7 +527,7 @@ function DATABASE:ScanEnvironment() self:F() self.Navpoints = {} - self.Units = {} + self.UNITS = {} --Build routines.db.units and self.Navpoints for coa_name, coa_data in pairs(env.mission.coalition) do diff --git a/Moose/Group.lua b/Moose/Group.lua index d532c23df..86bdf5d71 100644 --- a/Moose/Group.lua +++ b/Moose/Group.lua @@ -1,6 +1,35 @@ ---- A GROUP class abstraction of a DCSGroup class. --- The GROUP class will take an abstraction of the DCSGroup class, providing more methods that can be done with a GROUP. +--- GROUP class. +-- +-- @{GROUP} class +-- ============== +-- 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. +-- +-- +-- 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. +-- +-- IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil). -- @module Group +-- @author FlightControl Include.File( "Routines" ) Include.File( "Base" ) @@ -12,8 +41,6 @@ Include.File( "Unit" ) -- @extends Base#BASE -- @field DCSGroup#Group DCSGroup The DCS group class. -- @field #string GroupName The name of the group. --- @field #number GroupID the ID of the group. --- @field #table Controller The controller of the group. GROUP = { ClassName = "GROUP", GroupName = "", @@ -27,81 +54,310 @@ GROUP = { -- @type DCSGroup -- @field id_ The ID of the group in DCS ---- The GROUPS structure contains references to all the created GROUP instances. -local GROUPS = {} - --- Create a new GROUP from a DCSGroup -- @param #GROUP self --- @param DCSGroup#Group DCSGroup The DCS Group +-- @param DCSGroup#Group GroupName The DCS Group name -- @return #GROUP self -function GROUP:New( DCSGroup ) +function GROUP:Register( GroupName ) local self = BASE:Inherit( self, BASE:New() ) - self:F( DCSGroup ) + self:F2( GroupName ) + self.GroupName = GroupName + return self +end - self.DCSGroup = DCSGroup - if self.DCSGroup and self.DCSGroup:isExist() then - self.GroupName = DCSGroup:getName() - self.GroupID = DCSGroup:getID() - self.Controller = DCSGroup:getController() - else - self:E( { "DCSGroup is nil or does not exist, cannot initialize GROUP!", self.DCSGroup } ) +-- 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:GetDCSGroup() + 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:GetDCSGroup() + + if DCSGroup then + local GroupIsAlive = DCSGroup:isExist() + self:T3( GroupIsAlive ) + return GroupIsAlive end - GROUPS[self.GroupID] = self - - return self + return nil end ---- Create a new GROUP from an existing group name. +--- 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 --- @param GroupName The name of the DCS Group. --- @return #GROUP self -function GROUP:NewFromName( GroupName ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( GroupName ) - - self.DCSGroup = Group.getByName( GroupName ) - if self.DCSGroup then - self.GroupName = self.DCSGroup:getName() - self.GroupID = self.DCSGroup:getID() - self.Controller = self.DCSGroup:getController() - end - - GROUPS[self.GroupID] = self - - return self +function GROUP:Destroy() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + + 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 ---- Create a new GROUP from an existing DCSUnit in the mission. +--- Returns category of the DCS Group. -- @param #GROUP self --- @param DCSUnit The DCSUnit. --- @return #GROUP self -function GROUP:NewFromDCSUnit( DCSUnit ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( DCSUnit ) +-- @return DCSGroup#Group.Category The category ID +function GROUP:GetCategory() + self:F2( self.GroupName ) - self.DCSGroup = DCSUnit:getGroup() - if self.DCSGroup then - self.GroupName = self.DCSGroup:getName() - self.GroupID = self.DCSGroup:getID() - self.Controller = self.DCSGroup:getController() + local DCSGroup = self:GetDCSGroup() + 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:GetDCSGroup() + 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:GetDCSGroup() + if DCSGroup then + local GroupCoalition = DCSGroup:getCoalition() + self:T3( GroupCoalition ) + return GroupCoalition + end + + return nil +end + +--- Returns the name of the DCS Group. +-- @param #GROUP self +-- @return #string The DCS Group name. +function GROUP:GetName() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupName = DCSGroup:getName() + self:T3( GroupName ) + return GroupName + end + + return nil +end + +--- Returns the DCS Group identifier. +-- @param #GROUP self +-- @return #number The identifier of the DCS Group. +function GROUP:GetID() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupID = DCSGroup:getID() + self:T3( GroupID ) + return GroupID + 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:GetDCSGroup() + + if DCSGroup then + local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) ) + self:T3( UnitFound.UnitName ) + self:T2( UnitFound ) + return UnitFound end - GROUPS[self.GroupID] = self - - return self + return nil end ---- Returns the name of the Group. +--- Returns the DCS Unit with number UnitNumber. +-- If the underlying DCS Unit does not exist, the method will return nil. . -- @param #GROUP self --- @return #string GroupName -function GROUP:GetName() +-- @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 GroupName = self.DCSGroup:getName() + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local DCSUnitFound = DCSGroup:getUnit( UnitNumber ) + self:T3( DCSUnitFound ) + return DCSUnitFound + end - return GroupName + 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:GetDCSGroup() + + 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:GetDCSGroup() + + 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:GetDCSGroup() + + 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:GetDCSGroup() + + if DCSGroup then + local DCSUnits = DCSGroup:getUnits() + self:T3( DCSUnits ) + return DCSUnits + end + + return nil +end + +--- Get the controller for the GROUP. +-- @param #GROUP self +-- @return DCSController#Controller +function GROUP:_GetController() + self:F2( { self.GroupName } ) + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupController = DCSGroup:getController() + self:T3( GroupController ) + return GroupController + end + + return nil +end --- Retrieve the group mission and allow to place function hooks within the mission waypoint plan. @@ -109,7 +365,6 @@ end -- Use the method @{Group@GROUP:WayPointExecute) to start the execution of the new mission plan. -- Note that when WayPointInitialize is called, the Mission of the group is RESTARTED! -- @param #GROUP self --- @param #number WayPoint -- @return #GROUP function GROUP:WayPointInitialize() @@ -126,7 +381,7 @@ end -- @param #function WayPointFunction The waypoint function to be called when the group moves over the waypoint. The waypoint function takes variable parameters. -- @return #GROUP function GROUP:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) - self:F( { WayPoint, WayPointIndex, WayPointFunction } ) + self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex ) self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg ) @@ -139,7 +394,7 @@ function GROUP:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionAr local DCSTask local DCSScript = {} - DCSScript[#DCSScript+1] = "local MissionGroup = GROUP.FindGroup( ... ) " + DCSScript[#DCSScript+1] = "local MissionGroup = GROUP:Find( ... ) " if FunctionArguments.n > 0 then DCSScript[#DCSScript+1] = FunctionString .. "( MissionGroup, " .. table.concat( FunctionArguments, "," ) .. ")" @@ -153,7 +408,7 @@ function GROUP:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionAr ), WayPointIndex ) - self:T( DCSTask ) + self:T3( DCSTask ) return DCSTask @@ -179,7 +434,7 @@ function GROUP:WayPointExecute( WayPoint, WaitTime ) table.remove( self.WayPoints, 1 ) end - self:T( self.WayPoints ) + self:T3( self.WayPoints ) self:SetTask( self:TaskRoute( self.WayPoints ), WaitTime ) @@ -187,148 +442,70 @@ function GROUP:WayPointExecute( WayPoint, WaitTime ) end - ---- Gets the DCSGroup of the GROUP. --- @param #GROUP self --- @return DCSGroup#Group The DCSGroup. -function GROUP:GetDCSGroup() - self:F( { self.GroupName } ) - self.DCSGroup = Group.getByName( self.GroupName ) - return self.DCSGroup -end - ---- Gets the DCS Unit of the GROUP. --- @param #GROUP self --- @param #number UnitNumber The unit index to be returned from the GROUP. --- @return #Unit The DCS Unit. -function GROUP:GetDCSUnit( UnitNumber ) - self:F( { self.GroupName, UnitNumber } ) - return self.DCSGroup:getUnit( UnitNumber ) - -end - ---- Gets the DCSUnits of the GROUP. --- @param #GROUP self --- @return #table The DCSUnits. -function GROUP:GetDCSUnits() - self:F( { self.GroupName } ) - return self.DCSGroup:getUnits() - -end - --- Activates a GROUP. -- @param #GROUP self function GROUP:Activate() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) trigger.action.activateGroup( self:GetDCSGroup() ) return self:GetDCSGroup() end ---- Gets the ID of the GROUP. --- @param #GROUP self --- @return #number The ID of the GROUP. -function GROUP:GetID() - self:F( self.GroupName ) - - return self.GroupID -end - ---- Gets the name of the GROUP. --- @param #GROUP self --- @return #string The name of the GROUP. -function GROUP:GetName() - self:F( self.GroupName ) - - return self.GroupName -end --- Gets the type name of the group. -- @param #GROUP self -- @return #string The type name of the group. function GROUP:GetTypeName() - self:F( self.GroupName ) + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() - return self.DCSGroup:getUnit(1):getTypeName() + if DCSGroup then + local GroupTypeName = DCSGroup:getUnit(1):getTypeName() + self:T3( GroupTypeName ) + return( GroupTypeName ) + end + + return nil end ---- Gets the callsign of the fist unit of the group. +--- Gets the CallSign of the first DCS Unit of the DCS Group. -- @param #GROUP self --- @return #string The callsign of the first unit of the group. +-- @return #string The CallSign of the first DCS Unit of the DCS Group. function GROUP:GetCallsign() - self:F( self.GroupName ) + self:F2( self.GroupName ) - return self.DCSGroup:getUnit(1):getCallsign() + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local GroupCallSign = DCSGroup:getUnit(1):getCallsign() + self:T3( GroupCallSign ) + return GroupCallSign + end + + return nil end ---- Gets the current Point of the GROUP in VEC3 format. --- @return #Vec3 Current x,y and z position of the group. +--- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group. +-- @return DCSTypes#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. function GROUP:GetPointVec2() - self:F( self.GroupName ) + self:F2( self.GroupName ) - local GroupPoint = self:GetUnit(1):GetPointVec2() - self:T( GroupPoint ) - return GroupPoint + local GroupPointVec2 = self:GetUnit(1):GetPointVec2() + self:T3( GroupPointVec2 ) + return GroupPointVec2 end ---- Gets the current Point of the GROUP in VEC2 format. --- @return #Vec2 Current x and y position of the group in the 2D plane. -function GROUP:GetPointVec2() - self:F( self.GroupName ) - - local GroupPoint = self:GetUnit(1):GetPointVec2() - self:T( GroupPoint ) - return GroupPoint -end - ---- Gets the current Point of the GROUP in VEC3 format. --- @return #Vec3 Current Vec3 position of the group. +--- Returns the current point (Vec3 vector) of the first DCS Unit in the DCS Group. +-- @return DCSTypes#Vec3 Current Vec3 point of the first DCS Unit of the DCS Group. function GROUP:GetPointVec3() - self:F( self.GroupName ) + self:F2( self.GroupName ) - local GroupPoint = self:GetUnit(1):GetPointVec3() - self:T( GroupPoint ) - return GroupPoint + local GroupPointVec3 = self:GetUnit(1):GetPointVec3() + self:T3( GroupPointVec3 ) + return GroupPointVec3 end ---- Destroy a GROUP --- Note that this destroy method also raises a destroy event at run-time. --- So all event listeners will catch the destroy event of this GROUP. --- @param #GROUP self -function GROUP:Destroy() - self:F( self.GroupName ) - - for Index, UnitData in pairs( self.DCSGroup:getUnits() ) do - self:CreateEventCrash( timer.getTime(), UnitData ) - end - - self.DCSGroup:destroy() - self.DCSGroup = nil -end ---- Gets the DCS Unit. --- @param #GROUP self --- @param #number UnitNumber The number of the Unit to be returned. --- @return Unit#UNIT The DCS Unit. -function GROUP:GetUnit( UnitNumber ) - self:F( { self.GroupName, UnitNumber } ) - return UNIT:Find( self.DCSGroup:getUnit( UnitNumber ) ) -end - ---- Returns the category name of the group. --- @param #GROUP self --- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -function GROUP:GetCategoryName() - self:F( self.GroupName ) - - local CategoryNames = { - [Group.Category.AIRPLANE] = "Airplane", - [Group.Category.HELICOPTER] = "Helicopter", - [Group.Category.GROUND] = "Ground Unit", - [Group.Category.SHIP] = "Ship", - } - - return CategoryNames[self.DCSGroup:getCategory()] -end -- Is Functions @@ -337,73 +514,85 @@ end -- @param #GROUP self -- @return #boolean Air category evaluation result. function GROUP:IsAir() - self:F() - - local IsAirResult = self.DCSGroup:getCategory() == Group.Category.AIRPLANE or self.DCSGroup:getCategory() == Group.Category.HELICOPTER + self:F2( self.GroupName ) - self:T( IsAirResult ) - return IsAirResult + local DCSGroup = self:GetDCSGroup() + + 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 group is alive. --- When the group exists at run-time, this method will return true, otherwise false. +--- Returns if the DCS Group contains Helicopters. -- @param #GROUP self --- @return #boolean Alive result. -function GROUP:IsAlive() - self:F() - - local IsAliveResult = self.DCSGroup and self.DCSGroup:isExist() - - self:T( IsAliveResult ) - return IsAliveResult -end - ---- Returns if the GROUP is a Helicopter. --- @param #GROUP self --- @return #boolean true if GROUP are Helicopters. +-- @return #boolean true if DCS Group contains Helicopters. function GROUP:IsHelicopter() - self:F2() + self:F2( self.GroupName ) - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.HELICOPTER + if DCSGroup then + local GroupCategory = DCSGroup:getCategory() + self:T2( GroupCategory ) + return GroupCategory == Group.Category.HELICOPTER + end + + return nil end ---- Returns if the GROUP are AirPlanes. +--- Returns if the DCS Group contains AirPlanes. -- @param #GROUP self --- @return #boolean true if GROUP are AirPlanes. +-- @return #boolean true if DCS Group contains AirPlanes. function GROUP:IsAirPlane() self:F2() - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.AIRPLANE + if DCSGroup then + local GroupCategory = DCSGroup:getCategory() + self:T2( GroupCategory ) + return GroupCategory == Group.Category.AIRPLANE + end + + return nil end ---- Returns if the GROUP are Ground troops. +--- Returns if the DCS Group contains Ground troops. -- @param #GROUP self --- @return #boolean true if GROUP are Ground troops. +-- @return #boolean true if DCS Group contains Ground troops. function GROUP:IsGround() self:F2() - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.GROUND + if DCSGroup then + local GroupCategory = DCSGroup:getCategory() + self:T2( GroupCategory ) + return GroupCategory == Group.Category.GROUND + end + + return nil end ---- Returns if the GROUP are Ships. +--- Returns if the DCS Group contains Ships. -- @param #GROUP self --- @return #boolean true if GROUP are Ships. +-- @return #boolean true if DCS Group contains Ships. function GROUP:IsShip() self:F2() - local GroupCategory = self.DCSGroup:getCategory() - self:T2( GroupCategory ) + local DCSGroup = self:GetDCSGroup() - return GroupCategory == Group.Category.SHIP + 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. @@ -411,18 +600,24 @@ end -- @param #GROUP self -- @return #boolean All units on the ground result. function GROUP:AllOnGround() - self:F() + self:F2() - local AllOnGroundResult = true - - for Index, UnitData in pairs( self.DCSGroup:getUnits() ) do - if UnitData:inAir() then - AllOnGroundResult = false - end - end - - self:T( AllOnGroundResult ) - return AllOnGroundResult + local DCSGroup = self:GetDCSGroup() + + 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. @@ -430,21 +625,27 @@ end -- @param #GROUP self -- @return #number Maximum velocity found. function GROUP:GetMaxVelocity() - self:F() + self:F2() - local MaxVelocity = 0 - - for Index, UnitData in pairs( self.DCSGroup:getUnits() ) do - - local Velocity = UnitData:getVelocity() - local VelocityTotal = math.abs( Velocity.x ) + math.abs( Velocity.y ) + math.abs( Velocity.z ) - - if VelocityTotal < MaxVelocity then - MaxVelocity = VelocityTotal - end - end - - return MaxVelocity + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local MaxVelocity = 0 + + for Index, UnitData in pairs( DCSGroup:getUnits() ) do + + local Velocity = UnitData:getVelocity() + local VelocityTotal = math.abs( Velocity.x ) + math.abs( Velocity.y ) + math.abs( Velocity.z ) + + if VelocityTotal < MaxVelocity then + MaxVelocity = VelocityTotal + end + end + + return MaxVelocity + end + + return nil end --- Returns the current minimum height of the group. @@ -452,7 +653,7 @@ end -- @param #GROUP self -- @return #number Minimum height found. function GROUP:GetMinHeight() - self:F() + self:F2() end @@ -461,7 +662,7 @@ end -- @param #GROUP self -- @return #number Maximum height found. function GROUP:GetMaxHeight() - self:F() + self:F2() end @@ -471,68 +672,85 @@ end -- @param #GROUP self -- @return Group#GROUP self function GROUP:PopCurrentTask() - self:F() + self:F2() - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - Controller:popTask() - - return self + if DCSGroup then + local Controller = self:_GetController() + Controller:popTask() + return self + end + + return nil end --- Pushing Task on the queue from the group. -- @param #GROUP self -- @return Group#GROUP self function GROUP:PushTask( DCSTask, WaitTime ) - self:F() + self:F2() - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Group. - -- Controller:pushTask( DCSTask ) - - if not WaitTime then - Controller:pushTask( DCSTask ) - else - routines.scheduleFunction( Controller.pushTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) + if DCSGroup then + local Controller = self:_GetController() + + -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. + -- Therefore we schedule the functions to set the mission and options for the Group. + -- Controller:pushTask( DCSTask ) + + if WaitTime then + routines.scheduleFunction( Controller.pushTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) + else + Controller:pushTask( DCSTask ) + end + + return self end - - return self + + return nil end --- Clearing the Task Queue and Setting the Task on the queue from the group. -- @param #GROUP self -- @return Group#GROUP self function GROUP:SetTask( DCSTask, WaitTime ) - self:F( { DCSTask } ) + self:F2( { DCSTask } ) - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Group. - -- Controller.setTask( Controller, DCSTask ) - - if not WaitTime then - WaitTime = 1 + if DCSGroup then + + local Controller = self:_GetController() + + -- When a group SPAWNs, it takes about a second to get the group in the simulator. Setting tasks to unspawned groups provides unexpected results. + -- Therefore we schedule the functions to set the mission and options for the Group. + -- Controller.setTask( Controller, DCSTask ) + + if not WaitTime then + WaitTime = 1 + end + routines.scheduleFunction( Controller.setTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) + + return self end - routines.scheduleFunction( Controller.setTask, { Controller, DCSTask }, timer.getTime() + WaitTime ) - return self + return nil end --- Return a condition section for a controlled task -- @param #GROUP self --- @param #Time time +-- @param DCSTime#Time time -- @param #string userFlag -- @param #boolean userFlagValue -- @param #string condition --- @param #Time duration +-- @param DCSTime#Time duration -- @param #number lastWayPoint -- return DCSTask#Task function GROUP:TaskCondition( time, userFlag, userFlagValue, condition, duration, lastWayPoint ) - self:F( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) + self:F2( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) local DCSStopCondition = {} DCSStopCondition.time = time @@ -542,7 +760,7 @@ function GROUP:TaskCondition( time, userFlag, userFlagValue, condition, duration DCSStopCondition.duration = duration DCSStopCondition.lastWayPoint = lastWayPoint - self:T( { DCSStopCondition } ) + self:T3( { DCSStopCondition } ) return DCSStopCondition end @@ -552,7 +770,7 @@ end -- @param #DCSStopCondition DCSStopCondition -- @return DCSTask#Task function GROUP:TaskControlled( DCSTask, DCSStopCondition ) - self:F( { DCSTask, DCSStopCondition } ) + self:F2( { DCSTask, DCSStopCondition } ) local DCSTaskControlled @@ -564,7 +782,7 @@ function GROUP:TaskControlled( DCSTask, DCSStopCondition ) } } - self:T( { DCSTaskControlled } ) + self:T3( { DCSTaskControlled } ) return DCSTaskControlled end @@ -573,7 +791,7 @@ end -- @param #list DCSTasks -- @return DCSTask#Task function GROUP:TaskCombo( DCSTasks ) - self:F( { DCSTasks } ) + self:F2( { DCSTasks } ) local DCSTaskCombo @@ -584,7 +802,7 @@ function GROUP:TaskCombo( DCSTasks ) } } - self:T( { DCSTaskCombo } ) + self:T3( { DCSTaskCombo } ) return DCSTaskCombo end @@ -593,7 +811,7 @@ end -- @param DCSCommand#Command DCSCommand -- @return DCSTask#Task function GROUP:TaskWrappedAction( DCSCommand, Index ) - self:F( { DCSCommand } ) + self:F2( { DCSCommand } ) local DCSTaskWrappedAction @@ -607,7 +825,7 @@ function GROUP:TaskWrappedAction( DCSCommand, Index ) }, } - self:T( { DCSTaskWrappedAction } ) + self:T3( { DCSTaskWrappedAction } ) return DCSTaskWrappedAction end @@ -616,13 +834,17 @@ end -- @param DCSCommand#Command DCSCommand -- @return #GROUP self function GROUP:SetCommand( DCSCommand ) - self:F( DCSCommand ) + self:F2( DCSCommand ) - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() - Controller:setCommand( DCSCommand ) - - return self + if DCSGroup then + local Controller = self:_GetController() + Controller:setCommand( DCSCommand ) + return self + end + + return nil end --- Perform a switch waypoint command @@ -631,7 +853,7 @@ end -- @param #number ToWayPoint -- @return DCSTask#Task function GROUP:CommandSwitchWayPoint( FromWayPoint, ToWayPoint, Index ) - self:F( { FromWayPoint, ToWayPoint, Index } ) + self:F2( { FromWayPoint, ToWayPoint, Index } ) local CommandSwitchWayPoint = { id = 'SwitchWaypoint', @@ -641,19 +863,19 @@ function GROUP:CommandSwitchWayPoint( FromWayPoint, ToWayPoint, Index ) }, } - self:T( { CommandSwitchWayPoint } ) + self:T3( { CommandSwitchWayPoint } ) return CommandSwitchWayPoint end --- Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- @param #GROUP self --- @param #Vec2 Point The point to hold the position. +-- @param DCSTypes#Vec2 Point The point to hold the position. -- @param #number Altitude The altitude to hold the position. -- @param #number Speed The speed flying when holding the position. -- @return #GROUP self function GROUP:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) - self:F( { self.GroupName, Point, Altitude, Speed } ) + self:F2( { self.GroupName, Point, Altitude, Speed } ) -- pattern = enum AI.Task.OribtPattern, -- point = Vec2, @@ -663,7 +885,7 @@ function GROUP:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) local LandHeight = land.getHeight( Point ) - self:T( { LandHeight } ) + self:T3( { LandHeight } ) local DCSTask = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.CIRCLE, @@ -697,11 +919,16 @@ end -- @param #number Speed The speed flying when holding the position. -- @return #GROUP self function GROUP:TaskOrbitCircle( Altitude, Speed ) - self:F( { self.GroupName, Altitude, Speed } ) + self:F2( { self.GroupName, Altitude, Speed } ) - local GroupPoint = self:GetPointVec2() + local DCSGroup = self:GetDCSGroup() - return self:TaskOrbitCircleAtVec2( GroupPoint, Altitude, Speed ) + if DCSGroup then + local GroupPoint = self:GetPointVec2() + return self:TaskOrbitCircleAtVec2( GroupPoint, Altitude, Speed ) + end + + return nil end @@ -711,7 +938,7 @@ end -- @param #number Duration The maximum duration in seconds to hold the position. -- @return #GROUP self function GROUP:TaskHoldPosition() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) return self:TaskOrbitCircle( 30, 10 ) end @@ -719,11 +946,11 @@ end --- Land the group at a Vec2Point. -- @param #GROUP self --- @param #Vec2 Point The point where to land. +-- @param DCSTypes#Vec2 Point The point where to land. -- @param #number Duration The duration in seconds to stay on the ground. -- @return #GROUP self function GROUP:TaskLandAtVec2( Point, Duration ) - self:F( { self.GroupName, Point, Duration } ) + self:F2( { self.GroupName, Point, Duration } ) local DCSTask @@ -733,7 +960,7 @@ function GROUP:TaskLandAtVec2( Point, Duration ) DCSTask = { id = 'Land', params = { point = Point, durationFlag = false } } end - self:T( DCSTask ) + self:T3( DCSTask ) return DCSTask end @@ -743,7 +970,7 @@ end -- @param #number Duration The duration in seconds to stay on the ground. -- @return #GROUP self function GROUP:TaskLandAtZone( Zone, Duration, RandomPoint ) - self:F( { self.GroupName, Zone, Duration, RandomPoint } ) + self:F2( { self.GroupName, Zone, Duration, RandomPoint } ) local Point if RandomPoint then @@ -754,7 +981,7 @@ function GROUP:TaskLandAtZone( Zone, Duration, RandomPoint ) local DCSTask = self:TaskLandAtVec2( Point, Duration ) - self:T( DCSTask ) + self:T3( DCSTask ) return DCSTask end @@ -764,7 +991,7 @@ end -- @param Unit#UNIT The unit. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskAttackUnit( AttackUnit ) - self:F( { self.GroupName, AttackUnit } ) + self:F2( { self.GroupName, AttackUnit } ) -- AttackUnit = { -- id = 'AttackUnit', @@ -787,7 +1014,7 @@ function GROUP:TaskAttackUnit( AttackUnit ) }, }, - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -796,7 +1023,7 @@ end -- @param Group#GROUP AttackGroup The Group to be attacked. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskAttackGroup( AttackGroup ) - self:F( { self.GroupName, AttackGroup } ) + self:F2( { self.GroupName, AttackGroup } ) -- AttackGroup = { -- id = 'AttackGroup', @@ -820,7 +1047,7 @@ function GROUP:TaskAttackGroup( AttackGroup ) }, }, - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -830,7 +1057,7 @@ end -- @param DCSTypes#Distance Radius The radius of the zone to deploy the fire at. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskFireAtPoint( PointVec2, Radius ) - self:F( { self.GroupName, PointVec2, Radius } ) + self:F2( { self.GroupName, PointVec2, Radius } ) -- FireAtPoint = { -- id = 'FireAtPoint', @@ -847,7 +1074,7 @@ function GROUP:TaskFireAtPoint( PointVec2, Radius ) } } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -855,12 +1082,12 @@ end --- Move the group to a Vec2 Point, wait for a defined duration and embark a group. -- @param #GROUP self --- @param #Vec2 Point The point where to wait. +-- @param DCSTypes#Vec2 Point The point where to wait. -- @param #number Duration The duration in seconds to wait. -- @param #GROUP EmbarkingGroup The group to be embarked. -- @return DCSTask#Task The DCS task structure function GROUP:TaskEmbarkingAtVec2( Point, Duration, EmbarkingGroup ) - self:F( { self.GroupName, Point, Duration, EmbarkingGroup.DCSGroup } ) + self:F2( { self.GroupName, Point, Duration, EmbarkingGroup.DCSGroup } ) local DCSTask DCSTask = { id = 'Embarking', @@ -874,17 +1101,17 @@ function GROUP:TaskEmbarkingAtVec2( Point, Duration, EmbarkingGroup ) } } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end --- Move to a defined Vec2 Point, and embark to a group when arrived within a defined Radius. -- @param #GROUP self --- @param #Vec2 Point The point where to wait. +-- @param DCSTypes#Vec2 Point The point where to wait. -- @param #number Radius The radius of the embarking zone around the Point. -- @return DCSTask#Task The DCS task structure. function GROUP:TaskEmbarkToTransportAtVec2( Point, Radius ) - self:F( { self.GroupName, Point, Radius } ) + self:F2( { self.GroupName, Point, Radius } ) local DCSTask --DCSTask#Task DCSTask = { id = 'EmbarkToTransport', @@ -894,7 +1121,7 @@ function GROUP:TaskEmbarkToTransportAtVec2( Point, Radius ) } } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -903,12 +1130,12 @@ end -- @param #table TaskMission A table containing the mission task. -- @return DCSTask#Task function GROUP:TaskMission( TaskMission ) - self:F( Points ) + self:F2( Points ) local DCSTask DCSTask = { id = 'Mission', params = { TaskMission, }, } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end @@ -917,22 +1144,73 @@ end -- @param #table Points A table of route points. -- @return DCSTask#Task function GROUP:TaskRoute( Points ) - self:F( Points ) + self:F2( Points ) local DCSTask DCSTask = { id = 'Mission', params = { route = { points = Points, }, }, } - self:T( { DCSTask } ) + self:T3( { DCSTask } ) return DCSTask end ---- Make the group to fly to a given point and hover. +--- Make the DCS Group to fly to a given point and hover. -- @param #GROUP self --- @param #Vec3 Point The destination point. +-- @param DCSTypes#Vec3 Point The destination point in Vec3 format. +-- @param #number Speed The speed to travel. +-- @return #GROUP self +function GROUP:TaskRouteToVec2( Point, Speed ) + self:F2( { Point, Speed } ) + + local GroupPoint = self:GetUnit( 1 ):GetPointVec2() + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Turning Point" + PointFrom.speed = Speed + PointFrom.speed_locked = true + PointFrom.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + + local PointTo = {} + PointTo.x = Point.x + PointTo.y = Point.y + PointTo.type = "Turning Point" + PointTo.action = "Fly Over Point" + PointTo.speed = Speed + PointTo.speed_locked = true + PointTo.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self +end + +--- Make the DCS Group to fly to a given point and hover. +-- @param #GROUP self +-- @param DCSTypes#Vec3 Point The destination point in Vec3 format. -- @param #number Speed The speed to travel. -- @return #GROUP self function GROUP:TaskRouteToVec3( Point, Speed ) - self:F( { Point, Speed } ) + self:F2( { Point, Speed } ) local GroupPoint = self:GetUnit( 1 ):GetPointVec3() @@ -974,7 +1252,7 @@ function GROUP:TaskRouteToVec3( Point, Speed ) local Points = { PointFrom, PointTo } - self:T( Points ) + self:T3( Points ) self:Route( Points ) @@ -988,16 +1266,20 @@ end -- @param #table GoPoints A table of Route Points. -- @return #GROUP self function GROUP:Route( GoPoints ) - self:F( GoPoints ) + self:F2( GoPoints ) - local Points = routines.utils.deepCopy( GoPoints ) - local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } - - --self.Controller.setTask( self.Controller, MissionTask ) - - routines.scheduleFunction( self.Controller.setTask, { self.Controller, MissionTask}, timer.getTime() + 1 ) - - return self + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + local Points = routines.utils.deepCopy( GoPoints ) + local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } + local Controller = self:_GetController() + --Controller.setTask( Controller, MissionTask ) + routines.scheduleFunction( Controller.setTask, { Controller, MissionTask}, timer.getTime() + 1 ) + return self + end + + return nil end @@ -1012,50 +1294,57 @@ end -- @param #number Speed The speed. -- @param Base#FORMATION Formation The formation string. function GROUP:TaskRouteToZone( Zone, Randomize, Speed, Formation ) - self:F( Zone ) - - local GroupPoint = self:GetPointVec2() - - local PointFrom = {} - PointFrom.x = GroupPoint.x - PointFrom.y = GroupPoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Cone" - PointFrom.speed = 20 / 1.6 - + self:F2( Zone ) - local PointTo = {} - local ZonePoint - - if Randomize then - ZonePoint = Zone:GetRandomPointVec2() - else - ZonePoint = Zone:GetPointVec2() - end - - PointTo.x = ZonePoint.x - PointTo.y = ZonePoint.y - PointTo.type = "Turning Point" - - if Formation then - PointTo.action = Formation - else - PointTo.action = "Cone" - end - - if Speed then - PointTo.speed = Speed - else - PointTo.speed = 20 / 1.6 - end - - local Points = { PointFrom, PointTo } - - self:T( Points ) - - self:Route( Points ) - - return self + local DCSGroup = self:GetDCSGroup() + + if DCSGroup then + + local GroupPoint = self:GetPointVec2() + + local PointFrom = {} + PointFrom.x = GroupPoint.x + PointFrom.y = GroupPoint.y + PointFrom.type = "Turning Point" + PointFrom.action = "Cone" + PointFrom.speed = 20 / 1.6 + + + local PointTo = {} + local ZonePoint + + if Randomize then + ZonePoint = Zone:GetRandomPointVec2() + else + ZonePoint = Zone:GetPointVec2() + end + + PointTo.x = ZonePoint.x + PointTo.y = ZonePoint.y + PointTo.type = "Turning Point" + + if Formation then + PointTo.action = Formation + else + PointTo.action = "Cone" + end + + if Speed then + PointTo.speed = Speed + else + PointTo.speed = 20 / 1.6 + end + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self + end + + return nil end -- Commands @@ -1073,7 +1362,7 @@ function GROUP:CommandDoScript( DoScript ) }, } - self:T( DCSDoScript ) + self:T3( DCSDoScript ) return DCSDoScript end @@ -1082,7 +1371,7 @@ end -- @param #GROUP self -- @return #table The MissionTemplate function GROUP:GetTaskMission() - self:F( self.GroupName ) + self:F2( self.GroupName ) return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template ) end @@ -1091,7 +1380,7 @@ end -- @param #GROUP self -- @return #table The mission route defined by points. function GROUP:GetTaskRoute() - self:F( self.GroupName ) + self:F2( self.GroupName ) return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) end @@ -1103,7 +1392,7 @@ end -- @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:F( { Begin, End } ) + self:F2( { Begin, End } ) local Points = {} @@ -1115,7 +1404,7 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius ) GroupName = self:GetName() end - self:T( { GroupName } ) + self:T3( { GroupName } ) local Template = _DATABASE.Templates.Groups[GroupName].Template @@ -1145,36 +1434,37 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius ) return nil end ---- Get the controller for the GROUP. --- @function _GetController --- @param #GROUP self --- @return Controller#Controller -function GROUP:_GetController() - - return self.DCSGroup:getController() - -end function GROUP:GetDetectedTargets() + self:F2( self.GroupName ) - return self:_GetController():getDetectedTargets() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + return self:_GetController():getDetectedTargets() + end + return nil end function GROUP:IsTargetDetected( DCSObject ) - - local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, - Controller.Detection.VISUAL, - Controller.Detection.OPTIC, - Controller.Detection.RADAR, - Controller.Detection.IRST, - Controller.Detection.RWR, - Controller.Detection.DLINK - ) - - return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity + = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, + Controller.Detection.VISUAL, + Controller.Detection.OPTIC, + Controller.Detection.RADAR, + Controller.Detection.IRST, + Controller.Detection.RWR, + Controller.Detection.DLINK + ) + return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity + end + + return nil end -- Options @@ -1183,137 +1473,182 @@ end -- @param #GROUP self -- @return #boolean function GROUP:OptionROEHoldFirePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() or self:IsGround() or self:IsShip() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() or self:IsGround() or self:IsShip() then + return true + end + + return false end - return false + return nil end --- Holding weapons. -- @param Group#GROUP self -- @return Group#GROUP self function GROUP:OptionROEHoldFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) + elseif self:IsGround() then + Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) + elseif self:IsShip() then + Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) + end + + return self end - return self + return nil end --- Can the GROUP attack returning on enemy fire? -- @param #GROUP self -- @return #boolean function GROUP:OptionROEReturnFirePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() or self:IsGround() or self:IsShip() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() or self:IsGround() or self:IsShip() then + return true + end + + return false end - return false + return nil end --- Return fire. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROEReturnFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) + elseif self:IsGround() then + Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) + elseif self:IsShip() then + Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) + end + + return self end - - return self + + return nil end --- Can the GROUP attack designated targets? -- @param #GROUP self -- @return #boolean function GROUP:OptionROEOpenFirePossible() - self:F( { self.GroupName } ) - - if self:IsAir() or self:IsGround() or self:IsShip() then - return true + self:F2( { self.GroupName } ) + + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() or self:IsGround() or self:IsShip() then + return true + end + + return false end - return false + return nil end --- Openfire. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROEOpenFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) + elseif self:IsGround() then + Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) + elseif self:IsShip() then + Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) + end - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) + return self end - - return self + + return nil end --- Can the GROUP attack targets of opportunity? -- @param #GROUP self -- @return #boolean function GROUP:OptionROEWeaponFreePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end --- Weapon free. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROEWeaponFree() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) + end + + return self end - return self + return nil end --- Can the GROUP ignore enemy fire? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTNoReactionPossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end @@ -1321,56 +1656,76 @@ end -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTNoReaction() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) + end + + return self end - return self + return nil end --- Can the GROUP evade using passive defenses? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTPassiveDefensePossible() - self:F( { self.GroupName } ) - - if self:IsAir() then - return true + self:F2( { self.GroupName } ) + + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end --- Evasion passive defense. -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTPassiveDefense() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) + end + + return self end - return self + return nil end --- Can the GROUP evade on enemy fire? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTEvadeFirePossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end @@ -1378,28 +1733,38 @@ end -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTEvadeFire() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) + end + + return self end - return self + return nil end --- Can the GROUP evade on fire using vertical manoeuvres? -- @param #GROUP self -- @return #boolean function GROUP:OptionROTVerticalPossible() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - if self:IsAir() then - return true + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + if self:IsAir() then + return true + end + + return false end - return false + return nil end @@ -1407,15 +1772,20 @@ end -- @param #GROUP self -- @return #GROUP self function GROUP:OptionROTVertical() - self:F( { self.GroupName } ) + self:F2( { self.GroupName } ) - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) + end + + return self end - return self + return nil end -- Message APIs @@ -1426,9 +1796,14 @@ end -- @param #Duration Duration The duration of the message. -- @return Message#MESSAGE function GROUP:Message( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - return MESSAGE:New( Message, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")", Duration, self:GetClassNameAndID() ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + return MESSAGE:New( Message, self:GetCallsign() .. " (" .. self:GetTypeName() .. ")", Duration, self:GetClassNameAndID() ) + end + + return nil end --- Send a message to all coalitions. @@ -1437,9 +1812,14 @@ end -- @param #string Message The message text -- @param #Duration Duration The duration of the message. function GROUP:MessageToAll( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToAll() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToAll() + end + + return nil end --- Send a message to the red coalition. @@ -1448,9 +1828,14 @@ end -- @param #string Message The message text -- @param #Duration Duration The duration of the message. function GROUP:MessageToRed( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToRed() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToRed() + end + + return nil end --- Send a message to the blue coalition. @@ -1459,9 +1844,14 @@ end -- @param #string Message The message text -- @param #Duration Duration The duration of the message. function GROUP:MessageToBlue( Message, Duration ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToBlue() + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToBlue() + end + + return nil end --- Send a message to a client. @@ -1471,24 +1861,12 @@ end -- @param #Duration Duration The duration of the message. -- @param Client#CLIENT Client The client object receiving the message. function GROUP:MessageToClient( Message, Duration, Client ) - self:F( { Message, Duration } ) + self:F2( { Message, Duration } ) - self:Message( Message, Duration ):ToClient( Client ) + local DCSGroup = self:GetDCSGroup() + if DCSGroup then + self:Message( Message, Duration ):ToClient( Client ) + end + + return nil end - - - - ---- Find the created GROUP using the DCSGroup ID. If a GROUP was created with the DCSGroupID, the the GROUP instance will be returned. --- Otherwise nil will be returned. --- @param DCSGroup#Group Group --- @return #GROUP -function GROUP.FindGroup( DCSGroup ) - - local self = GROUPS[DCSGroup:getID()] -- Group#GROUP - self:T( self:GetClassNameAndID() ) - return self - -end - - diff --git a/Moose/Mission.lua b/Moose/Mission.lua index 65e4222f4..7d6959bea 100644 --- a/Moose/Mission.lua +++ b/Moose/Mission.lua @@ -231,25 +231,6 @@ function MISSION:AddGoalFunction( GoalFunction ) self.GoalFunction = GoalFunction end ---- Show the briefing of the MISSION to the CLIENT. --- @param CLIENT Client to show briefing to. --- @return CLIENT -function MISSION:ShowBriefing( Client ) - self:F( { Client.ClientName } ) - - if not Client.ClientBriefingShown then - Client.ClientBriefingShown = true - local Briefing = self.MissionBriefing - if Client.ClientBriefing then - Briefing = Briefing .. "\n" .. Client.ClientBriefing - end - Briefing = Briefing .. "\n (Press [LEFT ALT]+[B] to view the graphical documentation.)" - Client:Message( Briefing, 30, self.Name .. '/MissionBriefing', "Command: Mission Briefing" ) - end - - return Client -end - --- Register a new @{CLIENT} to participate within the mission. -- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}. -- @return CLIENT diff --git a/Moose/Set.lua b/Moose/Set.lua index e10978d3a..063ec2fe0 100644 --- a/Moose/Set.lua +++ b/Moose/Set.lua @@ -308,7 +308,7 @@ function SET:FilterStart() self:E( { "Adding Unit:", DCSUnitName } ) self.DCSUnits[DCSUnitName] = _DATABASE.DCSUnits[DCSUnitName] - self.Units[DCSUnitName] = _DATABASE.Units[DCSUnitName] + self.Units[DCSUnitName] = _DATABASE:FindUnit( DCSUnitName ) if _DATABASE.DCSUnitsAlive[DCSUnitName] then self.DCSUnitsAlive[DCSUnitName] = _DATABASE.DCSUnitsAlive[DCSUnitName] @@ -323,7 +323,7 @@ function SET:FilterStart() --if self:_IsIncludeDCSGroup( DCSGroup ) then self:E( { "Adding Group:", DCSGroupName } ) self.DCSGroups[DCSGroupName] = _DATABASE.DCSGroups[DCSGroupName] - self.Groups[DCSGroupName] = _DATABASE.Groups[DCSGroupName] + self.Groups[DCSGroupName] = _DATABASE:FindGroups( DCSGroupName ) --end if _DATABASE.DCSGroupsAlive[DCSGroupName] then @@ -332,7 +332,7 @@ function SET:FilterStart() end end - for DCSUnitName, Client in pairs( _DATABASE.Clients ) do + for DCSUnitName, Client in pairs( _DATABASE.CLIENTS ) do self:E( { "Adding Client for Unit:", DCSUnitName } ) self.Clients[DCSUnitName] = _DATABASE.Clients[DCSUnitName] end diff --git a/Moose/Stage.lua b/Moose/Stage.lua index 893a3b08d..5dae9ae6a 100644 --- a/Moose/Stage.lua +++ b/Moose/Stage.lua @@ -69,7 +69,7 @@ end function STAGEBRIEF:Execute( Mission, Client, Task ) local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) self:F() - Mission:ShowBriefing( Client ) + Client:ShowBriefing() self.StageBriefingTime = timer.getTime() return Valid end diff --git a/Moose/Unit.lua b/Moose/Unit.lua index 99d13daf2..5df82eeec 100644 --- a/Moose/Unit.lua +++ b/Moose/Unit.lua @@ -7,7 +7,7 @@ -- * 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 objects. +-- * Manage the "state" of the DCS Unit. -- -- -- UNIT reference methods @@ -15,7 +15,7 @@ -- 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 +-- 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. @@ -25,10 +25,51 @@ -- 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 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). -- +-- 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}(). +-- +-- Additional UNIT APIs +-- ==================== +-- The UNIT class comes with additional methods. Find below a summary. +-- +-- 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. +-- +-- Position, Point +-- --------------- +-- The UNIT class provides methods to obtain the current point or position of the DCS Unit. +-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetPointVec3}() will obtain the current location of the DCS Unit in a Vec2 (2D) or a Vec3 (3D) vector respectively. +-- If you want to obtain the complete 3D position including oriëntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. +-- +-- Alive +-- ----- +-- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. +-- +-- Test for other units in radius +-- ------------------------------ +-- One can test if another DCS Unit is within a given radius of the current DCS Unit, by using the @{#UNIT.OtherUnitInRadius}() method. +-- +-- More functions will be added +-- ---------------------------- +-- During the MOOSE development, more functions will be added. A complete list of the current functions is below. +-- +-- +-- +-- -- @module Unit -- @author FlightControl @@ -38,7 +79,7 @@ Include.File( "Message" ) --- The UNIT class -- @type UNIT --- @Extends Base#BASE +-- @extends Base#BASE -- @field #UNIT.FlareColor FlareColor -- @field #UNIT.SmokeColor SmokeColor UNIT = { @@ -79,6 +120,8 @@ UNIT = { -- @field White -- @field Orange -- @field Blue + +-- Registration. --- Create a new UNIT from DCSUnit. -- @param #UNIT self @@ -88,11 +131,12 @@ UNIT = { function UNIT:Register( UnitName ) local self = BASE:Inherit( self, BASE:New() ) - self:F( UnitName ) + self:F2( UnitName ) self.UnitName = UnitName return self end +-- Reference methods. --- Finds a UNIT from the _DATABASE using a DCSUnit object. -- @param #UNIT self @@ -110,10 +154,9 @@ end -- @param #string UnitName The Unit Name. -- @return Unit#UNIT self function UNIT:FindByName( UnitName ) --- self:F( UnitName ) - local FoundUnit = _DATABASE:FindUnit( UnitName ) - return FoundUnit + local UnitFound = _DATABASE:FindUnit( UnitName ) + return UnitFound end function UNIT:GetDCSUnit() @@ -131,13 +174,13 @@ end -- @return DCSCoalitionObject#coalition.side The side of the coalition. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetCoalition() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitCoalition = DCSUnit:getCoalition() - self:T( UnitCoalition ) + self:T3( UnitCoalition ) return UnitCoalition end @@ -149,13 +192,13 @@ end -- @return DCScountry#country.id The country identifier. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetCountry() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitCountry = DCSUnit:getCountry() - self:T( UnitCountry ) + self:T3( UnitCountry ) return UnitCountry end @@ -169,7 +212,7 @@ end -- @return #string The name of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetName() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -187,7 +230,7 @@ end -- @return #boolean true if Unit is alive. -- @return #nil The DCS Unit is not existing or alive. function UNIT:IsAlive() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -204,7 +247,7 @@ end -- @return #boolean true if Unit is activated. -- @return #nil The DCS Unit is not existing or alive. function UNIT:IsActive() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -222,7 +265,7 @@ end -- @return #string Player Name -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetPlayerName() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -243,7 +286,7 @@ end -- @return DCSUnit#Unit.ID Unit ID -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetID() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -263,7 +306,7 @@ end -- @return #number The Unit number. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetNumber() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -280,7 +323,7 @@ end -- @return Group#GROUP The Group of the Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetGroup() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -298,7 +341,7 @@ end -- @return #string The Callsign of the Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetCallSign() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -315,7 +358,7 @@ end -- @return #number The Unit's health value. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetLife() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -332,7 +375,7 @@ end -- @return #number The Unit's initial health value. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetLife0() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -349,7 +392,7 @@ end -- @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:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -366,7 +409,7 @@ end -- @return DCSUnit#Unit.Ammo -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetAmmo() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -383,7 +426,7 @@ end -- @return DCSUnit#Unit.Sensors -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetSensors() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -407,7 +450,7 @@ end -- @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:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -426,7 +469,7 @@ end -- @return DCSUnit#Unit.Desc The Unit descriptor. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetDesc() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -444,13 +487,13 @@ end -- @return #string The type name of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetTypeName() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitTypeName = DCSUnit:getTypeName() - self:T( UnitTypeName ) + self:T3( UnitTypeName ) return UnitTypeName end @@ -466,13 +509,13 @@ end -- @return #string The name of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetPrefix() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) - self:T( UnitPrefix ) + self:T3( UnitPrefix ) return UnitPrefix end @@ -486,7 +529,7 @@ end -- @return DCSTypes#Vec2 The 2D point vector of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetPointVec2() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() @@ -497,7 +540,7 @@ function UNIT:GetPointVec2() UnitPointVec2.x = UnitPointVec3.x UnitPointVec2.y = UnitPointVec3.z - self:T( UnitPointVec2 ) + self:T3( UnitPointVec2 ) return UnitPointVec2 end @@ -510,13 +553,13 @@ end -- @return DCSTypes#Vec3 The 3D point vector of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetPointVec3() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitPointVec3 = DCSUnit:getPosition().p - self:T( UnitPointVec3 ) + self:T3( UnitPointVec3 ) return UnitPointVec3 end @@ -528,13 +571,13 @@ end -- @return DCSTypes#Position The 3D position vectors of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetPositionVec3() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitPosition = DCSUnit:getPosition() - self:T( UnitPosition ) + self:T3( UnitPosition ) return UnitPosition end @@ -546,13 +589,13 @@ end -- @return DCSTypes#Vec3 The velocity vector -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetVelocity() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitVelocityVec3 = DCSUnit:getVelocity() - self:T( UnitVelocityVec3 ) + self:T3( UnitVelocityVec3 ) return UnitVelocityVec3 end @@ -564,13 +607,13 @@ end -- @return #boolean true if in the air. -- @return #nil The DCS Unit is not existing or alive. function UNIT:InAir() - self:F( self.UnitName ) + self:F2( self.UnitName ) local DCSUnit = self:GetDCSUnit() if DCSUnit then local UnitInAir = DCSUnit:inAir() - self:T( UnitInAir ) + self:T3( UnitInAir ) return UnitInAir end @@ -582,7 +625,7 @@ end -- @return DCSTypes#Distance The altitude of the DCS Unit. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetAltitude() - self:F() + self:F2() local DCSUnit = self:GetDCSUnit() @@ -601,7 +644,7 @@ end -- @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:F( { self.UnitName, AwaitUnit.UnitName, Radius } ) + self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } ) local DCSUnit = self:GetDCSUnit() @@ -610,10 +653,10 @@ function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) local AwaitUnitPos = AwaitUnit:GetPointVec3() if (((UnitPos.x - AwaitUnitPos.x)^2 + (UnitPos.z - AwaitUnitPos.z)^2)^0.5 <= Radius) then - self:T( "true" ) + self:T3( "true" ) return true else - self:T( "false" ) + self:T3( "false" ) return false end end @@ -638,77 +681,77 @@ end --- Signal a flare at the position of the UNIT. -- @param #UNIT self function UNIT:Flare( FlareColor ) - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), FlareColor , 0 ) end --- Signal a white flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareWhite() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.White , 0 ) end --- Signal a yellow flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareYellow() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Yellow , 0 ) end --- Signal a green flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareGreen() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Green , 0 ) end --- Signal a red flare at the position of the UNIT. -- @param #UNIT self function UNIT:FlareRed() - self:F() + self:F2() trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Red, 0 ) end --- Smoke the UNIT. -- @param #UNIT self function UNIT:Smoke( SmokeColor ) - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), SmokeColor ) end --- Smoke the UNIT Green. -- @param #UNIT self function UNIT:SmokeGreen() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Green ) end --- Smoke the UNIT Red. -- @param #UNIT self function UNIT:SmokeRed() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Red ) end --- Smoke the UNIT White. -- @param #UNIT self function UNIT:SmokeWhite() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.White ) end --- Smoke the UNIT Orange. -- @param #UNIT self function UNIT:SmokeOrange() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Orange ) end --- Smoke the UNIT Blue. -- @param #UNIT self function UNIT:SmokeBlue() - self:F() + self:F2() trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Blue ) end @@ -719,14 +762,14 @@ end -- @param #UNIT self -- @return #boolean Air category evaluation result. function UNIT:IsAir() - self:F() + self:F2() local UnitDescriptor = self.DCSUnit:getDesc() - self:T( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) + self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER ) - self:T( IsAirResult ) + self:T3( IsAirResult ) return IsAirResult end diff --git a/Presentations/DCS World - MOOSE - Development - Part 3 - The DATABASE - UNIT - CLIENT - GROUP - ZONE, .pptx b/Presentations/DCS World - MOOSE - Development - Part 3 - The DATABASE - UNIT - CLIENT - GROUP - ZONE, .pptx index fbb0df74138120c098de897fe730b293b2b0f3be..62b627f6260d8c43bd4f9019d6fb128e81fc27c2 100644 GIT binary patch delta 42858 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�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 delta 42486 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%0AHFgCM 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 diff --git a/Test Missions/Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz b/Test Missions/Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz index af1132ad39a04542af7ebde70d78516cc7dca145..d64a61ca629e19644101ad1c2df0422d808abd80 100644 GIT binary patch delta 13722 zcmZ9zb981+@Gcr#6Wi9rb|$v%iEZbNZQFJx*2K2n7!%v($#;M2-h0m3|Lv|`-M#j% zs^@uXbzSX&rY3^bYy5(3Ene1X0R;h}O)~sN59pHyXGY<-FZg1bq)Esu91%e#t$|6! zye}nP-#q7tr%e3a9sXsPsK;*Gs(Ea>N^Za9&6+XhKG;%rTQ;e z9WWRKnvc;k{aln-m&L;*{_pI^4wQ8WEU}#0F{&@lI{e4ZCMU( zFk30#-CRdF>CtV78OLrE;49o$yTGO!?rYwK-!=}XI3BS_6`s~G9wLh#8Z`UyR8f(g zb6TqV?=p1~i|gBBvxCEX33P9vu>Pa~Uzd}4Piw%B*VE6upuyq^m+f^n;|OCpZN z+ln&a5a=KvATS``e-Z?m(H@ov3s1xlcnmQ@b-`Zt%*_eY-Ql7R0SC%jd$%lg^5ZxK{3yIdU}RER;I|XmRF5sZE#F0 z-mC5QC7{AN#OGG`vr65Caod~6fi|s;F%6^wjj~}Gl!%#7Y|etmQsHg0& zZs$NUvaVBgR=Hg5bMwT-j4-m9i{5(MNk9C!$XW1IZr9C#+OIXIULtIW&MIB1bd;gO zXFPg{6%A9?@i?U5KEiN0*h1gB#n=H_+{mj7)(Fhlf7w(q z4hWC zzTSh1@!F&b)J7!IzRFKCTw%(Bvu!X0DtCmC?n0@X2=hA0Tkd*JCN>jGeD+TMmJ%7} z0K>Uldpm$RO`9zAt4IXJ&zd`1cCa?<2R zy_kREB%()baRJ*6x3aE#w8toxCAdI$H6o%81Jz*zq+nA&z506CHaujOatq3D}tNmDhQIs8B?O3Tgp^qs;}O~q@1rjI(0Uxo=rWKl3qHm;WnM0a!mG!`&K z2RY2-lc|P^W#={7DD6WXY=uShFoxhpe`(A`N=2s1BEi!D4I3S#8*tZUEt@2HR+4dK zm4*YyyhO8+LgB5IBnw$s8SvS)m +xjp4revX){mG(bc+t@CON2z|FU#Ecw6Jfq+ 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 Ne+%&+TC)Ey`d><80M!5h diff --git a/Test Missions/Moose_Test_DESTROY/Moose_Test_DESTROY.lua b/Test Missions/Moose_Test_DESTROY/Moose_Test_DESTROY.lua index 4eecb01d7..2caa20805 100644 --- a/Test Missions/Moose_Test_DESTROY/Moose_Test_DESTROY.lua +++ b/Test Missions/Moose_Test_DESTROY/Moose_Test_DESTROY.lua @@ -12,7 +12,7 @@ Include.File( "Event" ) do local Mission = MISSION:New( 'Destroy Gound', 'Ground', 'Briefing', 'CCCP' ) - Mission:AddClient( CLIENT:New( 'Client Plane', "Just wait and observe the SU-25T destoying targets. Your mission goal should increase..." ) ) + Mission:AddClient( CLIENT:FindByName( 'Client Plane', "Just wait and observe the SU-25T destoying targets. Your mission goal should increase..." ) ) local DESTROYGROUPSTASK = DESTROYGROUPSTASK:New( 'Ground Vehicle', 'Ground Vehicles', { 'DESTROY Test 1' }, 100 ) -- 75% of a patriot battery needs to be destroyed to achieve mission success... DESTROYGROUPSTASK:SetGoalTotal( 1 ) @@ -25,7 +25,7 @@ end do local Mission = MISSION:New( 'Destroy Helicopters', 'Helicopters', 'Briefing', 'CCCP' ) - Mission:AddClient( CLIENT:New( 'Client Plane', "Just wait and observe the SU-25T destoying the helicopters. The helicopter mission goal should increase once all are destroyed ..." ) ) + Mission:AddClient( CLIENT:FindByName( 'Client Plane', "Just wait and observe the SU-25T destoying the helicopters. The helicopter mission goal should increase once all are destroyed ..." ) ) local DESTROYGROUPSTASK = DESTROYGROUPSTASK:New( 'Helicopter', 'Helicopters', { 'DESTROY Test 2' }, 50 ) DESTROYGROUPSTASK:SetGoalTotal( 2 ) diff --git a/Test Missions/Moose_Test_ESCORT/MOOSE_Test_ESCORT.lua b/Test Missions/Moose_Test_ESCORT/MOOSE_Test_ESCORT.lua index 6e5e36e0b..e6803dad4 100644 --- a/Test Missions/Moose_Test_ESCORT/MOOSE_Test_ESCORT.lua +++ b/Test Missions/Moose_Test_ESCORT/MOOSE_Test_ESCORT.lua @@ -53,8 +53,8 @@ do SpawnEscortGround = SPAWN:New( "Escort Ground" ) SpawnEscortShip = SPAWN:New( "Escort Ship" ) - EscortClientHeli = CLIENT:New( "Lead Helicopter", "Fly around and observe the behaviour of the escort helicopter" ):Alive( EventAliveHelicopter ) - EscortClientPlane = CLIENT:New( "Lead Plane", "Fly around and observe the behaviour of the escort airplane. Select Navigate->Joun-Up and airplane should follow you. Change speed and directions." ) + EscortClientHeli = CLIENT:FindByName( "Lead Helicopter", "Fly around and observe the behaviour of the escort helicopter" ):Alive( EventAliveHelicopter ) + EscortClientPlane = CLIENT:FindByName( "Lead Plane", "Fly around and observe the behaviour of the escort airplane. Select Navigate->Joun-Up and airplane should follow you. Change speed and directions." ) :Alive( EventAlivePlane ) end diff --git a/Test Missions/Moose_Test_ESCORT/MOOSE_Test_ESCORT.miz b/Test Missions/Moose_Test_ESCORT/MOOSE_Test_ESCORT.miz index 2b8e5b65841a574d27fac7b5330385d1b73754f6..b0ae3d33e0ce2fa45fae29f5597dd650be05c143 100644 GIT binary patch delta 32608 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 delta 32516 zcmV)7K*zuP>Im}c2oq3C0|XQR000O80RR91=+2S~^N|xQe;h}W<@5Ac$n@Ni7Wd1; zJj5ka>Lsbxh-_)6*Vft!K#_zILdgJ7yP$0TeUDcaC;%0K3KEqfGz(d)MX~?_UY_pf z_~rQd;@8tRi>uSs`M>|=x4-<`|N8o5akc#VfB*Ym{tdsvFRz!23;a@YA^y7ljg!SS ze!cz0&#UvRf4BIh#V`Nsuk`85|NG17#ro_0>ycmSV)+JN1;6$6*Xv*ZpT+ertJCwV z%UfUbH(S&w{FGS9Trhod;+XeJF=cqxp*U%k|F88YtUm*vaetJ6f7f4cUvs=TJG(r6v;G#J{ZKys z_Sxc+zQ?a)`r&Uc|Mk=M+x}NtE{^f@Y+v=u>hknz^YhVftiL(@_Sx$E&FR(q6Z*?l z7zDn3Z$r@DgumXr1lHZYLYsk0?v>=$NbYW4alCqeaY=v9Z+_?P^7PHyEBcyx(A)>f zrT5(1fAyb}xi|lULC5GqFnMCGaL@FSSg@e1U}92{I~Bc=Q`Ml#i@ny2Z&p?7#c$>? z9#nnbklNTF@i22yGpQWB ze--rX;x%g5XBoZ%_eKRTj1t1_SXhW5ILU+2J{aC~VRPGsHBQcwsU~e`iE-)09iqph51>Sl?fq{=B-le79op-P@}L z{;Ou(Z+<#_K=6ay##4jPTMr1_tdlN&PN#YTLYF;N(kUCKnU{#HT1d;hb%r?~Vb@W{ zsf<;78a!tE?3q?)mv5K9o>tBH-Qlx7d~{<*DG>i$&5UMno4~3mfz20Je^ZVte;Z-b zG&;vLLOn}X3YIh1QALTM5UYlDo42{^*=NtbU7TOOTUlg@b|gK>Vx)Y=VRa}$JCcqeqr)a3NHZCwC@ix~|YO#y8P91$3Q1rwvw z&P{q$W5d>7?RV?b^X0|j?EHH9)sL%_D&T!_c(+yq*H#4JQfo~WJLo}bf8_wyoE2`@ z)^gxND&%-$@n)$VQ!8Ny33TDCw%T`_TEr5)N}h!v0e~06G9Zt{LM$RCkqYu%J=t{x z=a-9%pBG?dRfm50!yHyd2qj2sr2jpG-L|qC+y)oJ9fRGm@@{Yz_!y)V)&=WbGg{mU z%+5q-Mm@3&1cq2~d<_4!f0j)m8R30$GB;>%536{w`f#$U=DQgzieRf85)Ir#$|{Y3 zMS}pwbjG*}$pz-M0iO?45uC7Q*TBZ(J+aw|6@cqc|EpNgIwD`Q5Ca8j}$ z#UQffQP`@DwcZ~iI^uDhCm;u_R4_^jUIoAi$Y2keP_j^F*Vy|4f3nnrM(z^=4}i9v zw!Q%ZZ*PD=2Y>q%@UTPQF+>H$(-_VpO^$o`HyE-FL%&-|er7%~BGB>jHKGQiK+w;MOAq zJ8*ysI37Ss@c=mMf2_X=w|CRrpbZ6*hr{vxXc0O1xX7HTNo1x$Z~)xG#5h7oOo-K% z*DWr@I$Z2C`Zr5>@5zU^bsqA~cR$X-=Ku)sp|$bW*WKAT30j~;FutpTcFUeOmP?4H z0ArjP%_pXhm+;leBJA361O$ie#>bG1XBXE$*E#SU;WcRde=q{cyu~bn0#J2GWwsJs zIl@DrA4>obo67U=;J#W)Z-lY3msoHfqsT=w5i(qp+anN*s2M<+%r+#Yrh2)d&%m#a zPR~v+PucU;@!Qk$(?5p9!H~`o-o-z>YHFYYLj(_bTbfF!w`!Zsa~{u8pdDv+97m&levS7fZz3)v{*h z=ZCMDA>b;t_5;nOt|A6C1VKiYN+>IKeR{{R)(_H}b&*K8;Q9b>&J)EL~e^;;qqNfdtL(4-dA_=+hUQm)KYVbu01i|_$MdRv6$itF>ngjpfDb*u;WI0k zKQ2C;E|8Nt_{7aiKY>PgE%w;%b65nG#GK@77YeP^Ch;%>F9EIZS`PsM8O^F7EdJT; z0Ri5E4e+8W_;~5~k$P9#7FPI78L{BNWqh>Y( z#-*y6(ZU)jxO1(lnWB!7BV%{9*ySyb_Iv)~dP*h3YBn;h7lOr+TQ+H#i`U}>Y*24e zbQE`gSY2MNer4wiR#xZlzqANA zS@F(zyRG)_Pf`WRUr$c6;3M348NhN?R?HTfQ^{c7st&^t29$Y>iJ#5Tu!NBVpc9}i za(&fxVPxy5tmuO>6>PiL$jx$%x^35IfA6mrpin(&>gJ`NlBUcY#V$Dj`$#GyhQO;h zN)Ey~%e~j!h22Vw7-RrUKwz}i|K5+dm8M#@yEC1TEtgjNk*Vwh^(Y-&QfcPB$YM+} zw+5B~?)Lj*+uF~*JA5{hNbF|Q9#^gT4{0fw3U6=K2OsJ#Nb1^HFC7B3X@b!^e{mFy zcM7tsCp;9>ifUQL1y+m=V1ZJEP#^d(2kMT^&{apG7tU)Se=B$+ph{ycc&0Qu>^|%J z+hcw}Fu)%oVYON9{kq`@^bTm1nRiaY*2Xf=wW11*Ed{BS>w3;I((i};r|;gbE|ymx zsv-3s-_FupU*~!a@Lxv*X7Jl7e-UM651#t$;e|Yf(uoh(;Vd0ud^u3p{m6gB=vRkF(4oHjokGitKjsc z$H+z{Ku&q^EDFV0NCp8nWCWwfA*(M}4e*X;6c7|#y<|((02c(<7$V1Rf5}!127g9S zOnPJFj(sH`0=R?0)2(jk+yf4X^kw80Mmv(%iW25!~H)E<)-fXTsK>jO9B(8z&S*BX#8 zF-$Ktz6x)5$L`m*kgAlYI$}Vk(?`H)Vazaiw#D%1a*^WCI7fy!xbBs^)+{OD9IzKi zLY<&-A{WT{;nwi}?OyLdfdjz>?5SC%xohg}=tfWfygB62?3%0Xe{jWNiG&`&AS@Vy z#{a4<-Sq&7Q$S@_E5Bn&G0>Plyc%Ce$;vpMA*Y82g66q41ioGGy47v%Fy+I&*_Ju= zHX9t&n(L;mK$L}BB5&2*(m1dI8qsjR+Jg)rb_NhBPziE3%J%WHI2xr(^lX|Y;$QM|H4l`YAZ)qe+RZ<{~}4x6Fi@N|qiQJVxRM5CZ!2fvvmS3y+Zf9>JuSWr&8oE)>#1JWKX zBsgwyEEmV4;?(P?k1Q$~_S*Hk(|4=0#qwA7?ETqMb0GA2o?{DY)863PN#C$=*w9^R z=AG8JNT5lj!~_=vwW!D7U~4H2ACWnc6dH3LdOyaB7e6n4J-zsfeYK%zHT(WF!&tM} z4%~WzkS*4*fAL_Wr0$tN_-y;qCZAR?XrT6jT7oBVDuatCK*viSQ{98T>tG41qxWaa zt9q&N)f{&9oQ4iGNNcqmCI*mRtCZ$K`5Newh|}~HxV^Q@bDJm_1)u?>0WQ%!EzZ5= z*}KA~P!yts^(-YGS>iesmyFkAeU;})axvkwbO2&Ef19?up6kJ;EOHuRe=5?6iTz(nr&C=_!dQypyank57smJo7N1UZSN-o{0e-M`H6{_KkIY<;-vWo8U)}f>MDGwK^o^EdRP4JBcS~CLI@gHVdYJAfu@;f zWxrs9*+iEaDFQIPfPsU{z-$3gA{U+~{upNVNpdU)EiV`g@@44BuKKMUyr#6Lf3>?c zOj}c22~JeTn1rVpGC}kEG;`h=8b-W1#<-D92))FEsR~&fi(@ok#@V%M4Sm+`fDyBx zh$pdpde77*P1;S`GY(+Jv_3gz!Sj(>oo!53PNS(SG=bjqsIk0TT>f(1*eCh^*&H!L zZf9_m6%Ab`Dc3vd-t>vwd~VDTe{Qk`!Hc=60G{b%$vmPkO9uE1j}w?ZL(eQ(K&I2gPZZ5AwO6NaTXk|F zPbE3VWu~@J0E0_8G3!m?A%WhRdYjnt{n7W?doZ}G537bM-m}B^gEZfEBOFJNteVvg zdgBgY%W}@>8&ya)=geeEf12r~GAt>t*`!J|+ABoidM8tvjv=x-2u?G!DnQ=|I!Pgo zk=1x02M%f`Xok<}o+hSiH-qn(u;>zsO^NYBFf0g5bL9@yViY+i?ht?fgsamW&nN78 zJNw^#@M)QpG_q`{tWHZ3C`0}xEg2(s>t@DUz%T?}nlQ7yBacRlf1}jcBv&0iDY|Lo=IL0jNE%oNYJW2!QgIY97s5&N7CwZy%mH@6@U$vjihj^0S0V#YziR(54yyCea!rE zeBtmU?8!8L@AHMpIgy2z6lEy?fjP6XP4O-R3;re;i)q z8IjiB1X^A1vLRk<$S|nFoEpTxAv2sxRUB-{ynHcpPEsf@HO6-0Q9%*QwHPZsa%U5Gh?fpI9dQ56Yb;8;azuMd|&4HZ9&P zmMx8%G>dMw+OS$eR@ETj2DK5!BsuFj5v|W-9HW&iI+|cOSuWtQq?5v>DWi5?sCplQ zUeLFRR#&r|g(#>^9X2>;tMTCXeLgp9(=1C-l9>{7o=~jj@K>u1;4OF@Ag)ibq0+k1 zB;N~Zf0yTrcc<*x>I|HrI{o}|hElj3Xg(3&pl^tTNm5?|Kr{Ly@_*RAg67nLBsi6K zLc2_R+qOJptwTzTgol_uIyqsdQ>R&!d#{y0M0@)U6>zqM#EK>ijU2{U(H9yiXj~g4+Kg>{12!pfh0ILBC zf2y+~ZjkH5!SN7_IT=KbQ8`~6f^JkyzgEa>Tn8o$(L9c)A=Ya(u7mx)yjuKvvHE3o z#$JB=%VMo?SD!PTW7u2{!ms_5WCW|o%nT%&Nd+-drkazc1@E)qiDp6yfZ1!w62)da zS%MB)*v5K~6#M5NpOJ;H-kqLT4Liepe+U5U_VHJ`bt^(|Pi`pDRSo-G!%8)Y=(u8{ zD2FV!M1b%hZb{h)>Wgo1s`q-8+;7L$DgkdxI>_G+UlOg6t*Gl+X z%2yAV*7L!0+GI6eh4#qqy#lUIVWN>nScz$+03NcPm{l>1K08pAx)Ol3`}<=re}7(` zE!!KEFAv{#RDN(_T`T}q^Nmqb>f)?nu@F6Gb$-weNQ8shN#4X{cZ&-~!&$Xw5k{ms z82ALJ9~+esla7FmvF@#Xm@F+X&lc4vFv}b?$**!_6_-_%t7uT1&K?O~Ymbo_qX6kO zgp@1#`DU$>CnZUftL~kDgv(Kue=L}dA<;HkU_Rr&XuDS%A9F5^(Oj|Laeug8e0X5> z_kSKfhanzpLal|{s@4R^wi>BxBhWi`U9S_v8j93#edk!N<;rJknU9XT4YXBFD}Q>* zuB1^)k?PV+om_&-VJx1UEvpND^F&!Z<(?!fM8kPCiRzOCdDVA`js?1heQXlhCAg=ugsmAoWQ%S8C;*NECsk5u&?8#jDTcZm-?23?AWC2)f#?2hqVE+1QUM+t)TU{)Vf8Q=!oyu1;U^AL2U#ng!+T&@kH?wKIX<5^|*|Dy)YOsRN zpl|@HrgiVv%Zl`%Yf-K{m5XCWsDEr3(os?Dj(3udo@GROd{K_*LoBl z{l>^HNxjkD4#{TsMB=_#B{Dml5y4Xs}95MvOrAR^d|Z%l?Mzn@UVGZ?wvn20k3UZkP0;R+;B zRgVRBlcEKso(`X7bPAN4bwS=BC@&ejvnwJ*foSSYWMddDBEs=&mP+=F-k=g=cB8Lj zh(jKSc|Oa%!x@%6+v8YzHe&{ikKDhVJ@6$VE~gQsl^JD)0Cnvs$OS% z`p!*rzQlfcvqa1#U9@*3uh$M9&4X#gu@Te5_aZxO;Pl2MAm(0{>IU`$(6{2RkS=| zC_w1Mro_i0=(vjdGhX%zfev~SN^8wXk){J)VuRitis-oPWx}9^hf{}e6Jk1wLV(SD zDvn7TES*3>`jbgB$%Tkz@=3<}NRRHQ#DCK`&-3?xf6ifHp`^qkuVJQ!azEcReh?~}*i)mnpa2{hMp;#g+S{!%6Vw=nZp;Xr6T7&&tqYI@-^ozeCjF+98XWGmKV0 zm4vo)e*skGqV1HK$Oysuc=4JAJVMObF>!<%J2pnE{{#r)Y1~}BzcDn9WAqvpUMosk z;aGBtipDMllX;p%V|KMekMG6e;`Dt(sq@7QcNX2CH)q3%NjJ7I__l;Z;=Ub0`LmTC zG6=Z=vGZ$D9jzVn5g?-|og)5ar&#ZP^jxA#f6I%L>qSFTC(Te4vV+@pTTixNXA1_k0^*LY7FhAwD=RDxO z4YdI`Y{sYPf+(L=Z6FI*Jk{f4rjwP&CoGK!3eJW}@Tex!>gjh}0*aSc7mIgq>rMSQ zR)f5m+b*t%AnQAXb~`)_0-@cUhg2r3yv`=DBtxWG2_&|Z4T}>sxJu&A)`8j~tCxSj zUd@r&khh!#6;RSNuGMfl=5B`lPPvoTSP79A0&eN!n;e>|4xn^mK zo*6`33wA$+MyJVj+zNbt`g2DZ^=yL|MwIp!(?LM~lm){Vmev?Bvj%t@EJatUwmj*Osxu~%p4#s)4_ zt$chv0Ii!_#T_=Kp-gErXC(@{ZzVYkWt>=cCgHm)c5-!Ts6zsaS%$U0SbW$Sp@4;p zbD9|;1=GiWQZa8w%N$i)t)51_2ak!t51e0^!*OXAN-8?ZQAH969ujCx*r0SpA7=0I z1wTju%q^wyLGzs;eIv`&>^}cd`WJVz~vyN`$T*<}M5jx|p;Rk_qxr?U#Za6?x3(L>W`q zM}e|0)%*Oy`VjYfp4Q0`PYlbJ5}N{IfYCKzS<;z8u5Q3G-?M%6{@wqdGDl+=ISe?l zxYqrD=8|FnaG0F%EsBh9vms#sYO`0A>&Ml_>YOwWFW+5%^_u?sEB4h!Kc;5tpANsb ztY}1sR=h$84gE7a=(S)UEq$n-26JCb2*r^)L}Gq4l4<7*OJ0IhL@P%=j!COW5w-TD z`z&MbA`m{Ho=`*e5cq)P10?uEZL|_$#H_D>DS|HV#sY<5FbQrNHQi*9EahUE9E*!tSt}cTOwIQzM?Ii=c6fn* zqbGBJc!!`7x5ab7ToLO5>?Ors!^)%*$#0pF*Oeh`=Xo8na~{%w_yW@6^<# zd(A+_T6`!x;XS*EihzX_(vlbr?^NPZq2QeuSxS<$r`<90E?K|?n^Ku#%DFC%`Z&M6 zem{@Cw+^7OfQ|Jzc7kRI0!S&wR;1~F_sV-aKESIb9DZ#}*&Eqoq%=`2)|joxlm$YqsCrkN7SsX|lsjNL z6PjI#yQTAFoz_`1g)joA6@kUVJrF~&QH&muw>~ndeMd8M0Yuvpn?5|IRf=GL)Oz7q zdfp&kemylJ5lzFKvkg4i>~$QOjI>a1sXimP0{$}D+9^y{HiRX+j*nh_IQ>wa7&-i= zw+w^Ybl#0L^|GQF!WJ+JCuSUJ*p^c}=A+w%*VbtC&5h)Pjz(`(1(2kAPLikvNIhjFi6wpld;BZYaz@YD{64(cLdNr*O}% zw^bNAp8ZbIhTUbgIgYiGqJtJ1nNsx4eacNet_!nvTThU6w7Jp(U)aZwo-ZJP1U zLvPbG+Q}dnts4f&e=MevM9joUMA)O=5XWJmF zbShLE0QNdUvcmg@Bh3sXQS#e0?Zo#$QM1-^QM7U9Z%$Y3ovTBXM*N+?Lc_@PYpYlQGGGB; zz)KhW6k46w{b{MAemz4J1xs>{?%v{`y5c5vwZUF#0YO%7OLQ#nIQTh9Z~~;=>>PKv zaF8DO9vGmEBh_5FWg$_05V$mu7Ln8S(xB-WVtnB4?cup9J?D~t1al;8q#Eb2U69(T zb&Igp%i4GNmKIq+(gS!yyE3>FC5o6dNf#Ey6HI)f5Ns3TMRCPwoBU|%En4aK*8^TG zuHUVGVb8vOK=!E|USXcnnYYRk6L7W~EA(J#ca$mvp*Dv*&?cah0Z(m^e0QY>X%KV* z94-Jfe}X(l5*ZPHM6+lWP0SpV4svqvZ$~GkNfn}%-#P|4UYx#Loh?oqLHWBG)_Md& z2OajXbfmQkz-R$tU8pB*rWsFN-rtMvfMB1nO1%TaK!j$1VQx@(^tG^(>((G0*R=j< z=xA%jZEx1Bt-hTlJ2Z3dz_2O$5GEm+0(KxNEzVNmhz!|(Br}y50n`(a*6#x#e(e!k#gzr{abP_awF$FySdTA|ff;f@^0oYc6 zTn+tg8k>FeZ0ro9u}?)l{=T7Ap70%wBQrMBu{3>u%z}kCkKPn&XaZB!4ITJ?j`02B z;{Db2d39Ui`xzEWfu}Y-Ab4O{!&}l}Ao%O)3$U4< zde){0BNe5+(4OAD+r*~(vocIHSM2DtuiumjHTTJ@JR-o@Y8SovuxDP3g z&QR2PDw9|{^%gDsG?ol%<*43rv|uUI3433J84-Pxe~cbmyQnosNy{n+L;%;7ve7hf z>!Daj>bF%9Qql}Uik74!vD-Be#t%*-*dXA9Hg~GHNhM}H=@P$Ijzt#(D?Uem zn~R&^u74hxcv4VQo~v5}Eca#N4Gm2S#Vh@T9+`N0P`IUnLa)Z1im~`n1?EIiOb0{5 zZ#FVJMG}MtM!*ry* z(+_9Yi)xSe*$gsa_U4K;jbqf~#iog-wC_?c|7<-PQF69ZVR586!`C!bl!9h|#KJ+w zg_=w~L_FF1X-{Q0^q%)7T?)8hWJ+2@iscEguuYlqN!XOcG)=i7{L`_Q@M?K}vbt!> zR=t`*`bP|4q7EOHuK(8YFbP{xuS@!HI0{^3nh@J13yPM!meR0P#KeM2A<+7{F!eBC zIwaaMiq%Pea9ZXGmfe64=|NYVt|dQMilF1aM? z)^ZSK^{~AD-DZL=Q4|vx7s}8{KhQI>Z17PPJ{4tsqi*{YsROx_CDw0$o74dTN=T(# zW#X`dI?#zF;SJt3*S#l`5q^{QjE{vzX^+XtMinAw zVv?W(+|qn3iLrX=U}Z^%q-LFn`C*2m%A6MExd(sZp`@WW%7BQG9vl94v zGhDF{HQf(aWlS94qA?QMa|`L&>E+q#*VV}%wzx>BYkMXY_`DH%gWm9A+VJYYWq5)I z;f$eOhoJhllK^U^5T3K*#=x{YR?KrDnbR5Xp)6(6pFb`0w=*KK7nz)9)Yb%4VQOGl6ThHYIDX_P#$q!ei?+SjxLGO{V^V$2xg zG&YLm9mBTI7H5n1m(~5G|8aO+SsnUzn@w7`lw0N1HC%*i|&IjH^*@d(l#O#Pq%PveUXO5jc8~M=*szB``w5e z8Odp-b~gkxL+S0o!)g;e_UR%=ZmA$#_53X4&MQzV~>U~KLCrW>lguwZN$wC zqDqf{AA3%h>QPF;k5x7 zw86N>6(UcCaxI0@6>Z7AcW0%dCa(}Q2+DhJfB!C0n3v~QeG9msD&YHv42hHEq2>|^ z-_w|QgME2RGLh>sg=((5QB!uNHqYEw1xpfta3NZ9*oh838O_4v4Vy;Ji%=17V4W!R ztlw{Jf4025u1?lIpJfGPd(Z+Op)uRB?TjnmH4_RfHjsvi1>|^OF#K){C-LNE4ouj| zz^dS2k(QjbbK1p1gJ|__yRhM3dv)X%A5Itb#nB(*3}?ORXp>WT%MIPcYd~Mc9sZGj zx+m0vogvmD3DWVQ2aYe?HmU($NC-GAn@CjxC0K%$nU`yAWCy=}%&5U1+ZTHpTcY>s zhQM8;&xW(f5=MCKUo-HF$g(BnIN?=pj4{i{wZ&#JFfB17@hdJHg#sz)Mj>#LtF~+Q zcg4VhrCtlBbfd6Pq~j$C79t0iaD>NyQi!Z@?zKvhD&e1fzNura{HO2dur){-9(+ls z-x|D_j_(pICOolY>%GFL)(%<;BWTXRc5jsfUs#t@VUwa0WDBiXvQe?oN1ttx#`4Dq zPJWS=!_!%Lz8gC3yIuz{`vei+%3AIm)iF3S-DxqxDP5-&f=gudIrjO! z;4#j7SuainFfAxHc}b@V-3jv(z*;GG42IO%8kgUMP_Dzk9ghAg%>dfq&WM&8Bus=R zXmhl~BYo%s7g}nAnL0n zu0v_=#q#X*YSp;=_}vU-!q6mhSf4zWvRW&%WRCR&9=PzV=LxCEFj~?~9zE0iwPPNj zf#o#Owr)|yHK3W;Un}d;qnTme?&(~U-nqjv%IK_`NZJ$DKH-W1xkW~QdxVG-IT}%y zq@~4&!^GSdGc?j^77=imX6kMI9sr`Q^rT;Fy{-&6(sHm=$bBScRZ*vVAn4xRdqBX6 zNrAzsK*|&D2#yW3!gTaBHbw`X>NAd}L#g`3`*&|I8uYkKvyctC#%)BZZ=hk zM|EvW3Ntvr9!_YJi+hECNRK?0BI7=;1%DMwg1YIGD|o`-mae6p+Odec} z?uh>yy1at8AVB0nm4#-=Y>vv6BG0xJs*d78gUe)It z?O@iGGwG~XbMBNBe1=tCuu&C|WTj!-q7^GbX6r?Co0*I+HRm4M7^kS>kR=JaIdwI`(~%H8m>& zq6G!C#vrr{2ot0v8f>lPv#+Km2PM5$cqfhP-8xGpWH*>SM@^y2z|{qfuD#aGW(^%ElhIfKTXyV-)U+SjX>=4L&OOFiqcO&>vp3t+Jr z$3m(gui?Mb#OUf+HqzuV)q&cR-k(&C`j51-qpK*5%JJ(85G_g8?=(<8qXKg@5WM7IQq`H0J#`4Kz#=6AI}h#Ys9gdQiMdP0%|sFn%K$SItwt2F(h7|L zU56JPH!&WXM?1V^`%^inw^NFrQkWtDh7%el<{F-?jAyQRP?5<^Ce@dKp6Jm+P8QNc zSX&IlgG_6GI;!&-Hs*s-)KYN-PkJMU${`k#ZXvN2rp2*^7jwr*{V>tmYHkeu<4NnG z(GO{jyqck+Mw=%VKO?+V*9#V;GNyp=jIz%6H9~*4<;Q}rn3RrIFr-2n-aQd4P0lke z5obx_%;Z)<(&)$f=Cd?|d>hT3ieLj>S*b5dz&{m#cX&lWM(Zg>J`hW9*>c-->}W^q zm;2b&Xcu$n2Wp@zWV=(xV$Tg(T}} zT)+NPB!|uJG*_ZR77hks?{wRtf(!k->)o z001A8v56~xO^f0{5WNfjheBA0pfY;cgD?x?=<1@57-i2rNvX}W>5%Ry^Y6F2EAb90SFl|0oybRw5bK7qscoE{D>12Jdm-*!~-gSXQ2Q&g!27NWE$JhEVxmKw}D?bHX$Tf0S20X_o9ZMnPX`nsmK0V#_^-8{p z6yZQq`lVFdcEiTCOdeC$x`Ao<7Qz8dSp{2}*e8X7j6zkULFs15LQF2{y1f!5!4f;2 z7p`J|lNG@kO6mNAd`Iurw!+wOU9)6|T6yr@&@822p-K;!38{b|7^Bi?XlfR`lM|!R zemoS$Qm_a=)r5=c-HO859;)Jux|YRVQBd?n6>C%)obkUZ{-r8N5EVnVZ&6s7G8rF^ z`GWh>oU?G#a5rIkJU7G9R-W5yl`Q=|j|&`st?41d9P=f9E;wgrA1b(Icgub$tVzpk zIrzPx^mjL0@g7yfuGL?fNj2oAorNA%BhE*(2(nREvf~VA)6rs`VXWG(PhX##TV3`iyS*X`)LJ>*sXSV5E2dQB_uSD6>J>+dKrp2QY^ z-T@y41{xM7Y{xXVfBZ6u+MC#=%ELhIWop*N)~y@(kZA{odlv1CCq|^YAy3flTh+O` z?2rD+y`a-JKZn{z)oYn7+ijRneSiA80Dm|cy=-Qq75MAHYQ0(3Ynkip>G+x@+UmOG zp#@}h|Hw_dM@@K$baOE}c6hXvUUrQRvIui2&9WqQ{sB-+0|b|#GXffyGuib4>|JIcz?;0RR9kmn_-=EPqW)12GUiZ=wHTm{TG(MURCZ#4iMkg5I=9 zx|`hw(nON17xBNFWLHhw6$@ULP4ecwHQrCvyt>Kl_ z4J_{N9xefav4mq@ZiHox@MkOc82$+bIv$NjH@Z}96?xu2vNgNsejPUJR+nBHO@9Iy z06JmkZ8)mLs%;`;58y#r z`236$oFa{hRJn^QsYLDq`h&C|;DxL~8xJ|&J#C?u;MYP2Xt52-Fv}p3EhF~3pn(0! zEY&h+4oigEnhoSf?B1Hj@=YpWw0~H1MXQ4dcTQMOx*Jj9rs}{$1|tWwm})r!?MOr7 zfpHF6s_z8<<9Z>T3l;uxeYovB^e@}Pz6)Wku|V+Lw5m!s8fE$>j%qNNM^D}5uAJz} z4y{7e5Y<7&DEeluNv{_BG!y;5=3d+ENOp?pyE7ztnoocii3HQ*@QO4KKLou>%pd-KHl`d^OA0dv>WCsRhzc zP)h>@6aWAK2mk>9006}B$ucmPAKU>XPD^jvKoGw3D=c~;QmMRNKO^-(2uZ63f`BN8 z5Frb;jTM5C?Wm2a{`dNUy=$A9ft(VU$L`EG-^^?_!8!@TogaN@U$xHPj+XvBb+6Nx zk=y|ke@}KBZIC4X`c+$qIMF`oy3_sK(0tq{xgKMMUQ6I%7Zeh%RY+tXWVU!qRGxwz zWbcH((WYB}Ct%0ir93;>otO@qHlRCI0tV%pd-Y2*_#TE$cw-E5S4y}X5Ktn^4l-(yn)zI78?ODWe>RHOVU*-X(+_WF_Yc#{iTHgJg>RXY zp0;e8Uv{;NWoOwbEG+!^HUBMn*w7pAVaE<-Se4q2@N zWTQZ{Vdy}}s4SZvX4!xv0kO5j933m~aJ<~z!FB4@gpHaMbvhK+GZL>lgjabA8U;oT z1mDCqScm(RnlK&j1X7UVWxqU#7YT$Df6dBFPlIg;@KXgFC`Qt|%Np(HXzP(2Km!pY zAf$Rc3K0w}0wtEL1w@!_>EJlVd4r0gi80p^8df+DqzGgXfkqXjMETJYwUAU(3GpCe zCIcRy=_Fij)J|M(5vwCG9TPQg5D!@l47Z1q-WLriTtqi30ZIm=zY&G)^*tidu0qFF$rFXkd$G@I(?h6Nq#F^Q2vC2%(2R#Ni(9tcURkYf05;XMMj*H zYm#f!2?ma|3uRAUgP%IVSD2w5emHqWgUY{4&e7#ecfymKMTZ<}L<$X~b9BL5qYI?(9x>hVI&US`qhwr?759%X=7GClhojiJwcK?F8w4*j#JER zJOsCMT^Xa#LuJ?^nb9OSQ-6BiHt6yym;Y1dJx)Fab27{hHs&B5jGge%LG4*4{m|i^ z)*a-jpe!jZVJ0?tN1%>pktA+mi#YNCLO)qD^H|~j)B1f)q@Kc`+9oLfQ`7kmmu}zz zyMGs|F`&Of004!k1ONvB003=ib8~5LZtZ=|a_cy<=Dwdo;Z=7;NzH#sapNv%N%m16 z{kSFBr>oDs!JsAD<{Vk_r6gCmD|#{ed4q|VcbJKoh}q0)UZCz94gVy9BtSAj*#>Q3 z(S6DyfiDvvkoh5zcyzW~XR~hnYvT{U{ePk1mG5Ue*SSCYcmFT`e}{h!f3X%j@7Znq zWAcIEA<_Tw%rdw3&b(V$_kZko3s#nghS0>b}6xFNb*Q*ffRg3CX^XpZ!^`?3~ z>|(Ufy?Lj&&v)+L#`PKI?=s=<3gPc+_U~!T0Yr419~Dw!w7T|g@2>yox9}(YxQm`x z1aq+IY)${!a4v3SUXk&y+*zwF@g1cQh?@6A`ol$}&wTu|o#pQKPoO#A_7S~y*MyI<|_OXue9Zsn})+`c7_f$Xzo)ECx?U9$@-%+BPo3V~InzRuLI zN4+4r+HH2$DriOc3HkG_w{mg)rG5>Sf@sOLmt0AyUjv=sKHtL^8-2i!)~~+xH3&5* zDxIIAAMV}Fe!c8&);o8z!he6@&i2Y#ZFbQ!H}-?GSlOXQk_&;?>gMnZv6H+h$62jx z_r+Xn9v`js(sqk9625r+NQ9p-za9oh>c@1y;2KW>bkti^5v#U;#L4+d^_erFNdh`M8xAt>qf% z&Sg^)&z)_g%%N?07Jst4wiiEqr}4F^loY~f zib84ie6fD{(U`YHBa-8$Z%hgWxcJr-J3_D1=xto9YzKP^6V+Q!VWo5%K3aO9`Z9ML z-@J+OVN-U>c)r)}Ol@ z*Y-Z!?Vl~jUAWfW?$4uLy>(PwJ4Evcc^qR?hW@Oh}|I_y^;cHDPJ?FGe`lo}C?gh$D4tjiHq0M2m z+49RzXB`E=Di`#Z-psR>CI)~&>KrQXW0NTDSW+ul4*fA?)os~;n>=lk1u?GU@)xlx@AmLnl#8uaW;A@0`Q6p zEl8d8lD#jl{F5n@{@~;LWJH6Jnf+Q#>iOelI`;khZQDhx^m0eT`*pJSbZn|0_nO1U_UC3Z{@~KbS_8hP?}n1r*@;&#<5gpd zJi0rd4vg8+ih&)0xQ+$cZPD$)dQ6#7^LVmI=oi&ZTQ|a}2&$|8R|(Ej^P;0Z_3Cp7 z8m5&tiqB}HV~jX}6c}+0jo%3B*T-yR&v8&( zZ%j`ubkSx8I{NCmGB7bLedxRT_SIThH}Z`84*|zMR;r*=A!EEhjnth(U%*$d>%cAZ z@XW!Ln9DK}F7>Zg?tYcR0F=zXMHKPeuP9&H<7d)B0+#0i0OaD7g~%aMK`JEW)WLYg zPd-#twRr$Cr6#))G`W1H{N39#^#w!m7lex4`rCOI$oZ&~Ug&`qXMdWKPkdSG5-J2p zjD>E@(X@;B`m=c~NF(5!&aA6hsf5Jbd=dTM_Q`SFd&8rX`eSx?m>Wyzu<%@@!FUA) zrk@&iPERWUQSqqNF%xY8)!>YaysLdh9^!zUlNqWv-i4WP66=>WP*rQ`K4P8fTeCLA z^-!!?2(k8jO_&fPdYY6)>zoZs797w67X?)cI~3!SY>)K5h>ln%o#qm5X1&8n`(f1} zybOxi@ZsA2_ShI_rsf=lJaq6&sb6T&8-EM1-4P(un~g3P?wWyk49BxbRoh@z+n?lv z@~PY5n?%K3J7#^Htc`PeXWLE_LIq|69nLiSDiQ#@4pWw2z~1v2(#3-U-qKn`gw2V1LL28f?;<+Fuyz zTNnVF-kU(mTc;bb>+M@;Nso=#Cy&V5b*I>Sr}cQjY31wsJL|^e_dG;<(w;*Op;fX= zoXC08ucne|OLrW1rCs&!n)XI(J#r(t*DM@9qT)S@&-;K(@TST2vNb?g3veQqm+O<8 z>%&3&job2ILvw31vT7*tRyx!1Yj$UBZz3R4l^U6H{c-fmBSylSs`BI}w`a|-a@pl# z(hviGM-(c=n|Iw?$LyJ2tmCg~ZA53(yhlaO!DTKnA(D=d3^R=04GMAbO^Ginl16B0 z)jxak-|^Y!)nzc}QHuF$%sc5s?atQx)_F5E(Dh_(=rO(7G>%O*s8nTNzUVy}oLvAW z82m_;M;A&ev_8Nyaj8=U^pv9_7-#(o*UW+lhv(jrL9yXc=lTc_V9i#`yo%y!- zgmlEnP#O)19a?L<7098_qahDn5>w<(ANhKoM_?L1+tSXR(;>#}1Jzqfmzd*w`2~1N zF`TeG2_D&=T$Dr*ZQMEk_G1aPXVYx_=HvCOD(~}Qv%g&9z*!(uI^5o3uEx3!6u&-O ze(4!Mo59-NzW7Ull&e{8`BXO(@m#? zU^XGCIo(>8+bduj&F}R#l9Xj8V?*a0qX`8w8>sHFo&*EXdR$)#dnazOjr@N*Y5&?auNFV(OK;@Oj4T zBM?7>)zii0RM_=kc(9^34oV@+OCiFmn1cv9TUGbEBSq-?n<4!SQ@;oG6;OZma!Ob5 zMcNN~_^235nddvC!vRYGNZ3eu^EYf-&T5s~F$L@>hoswPZyh}c{B+5CcSDQ7o9M86 zI_^s+W>w|dAepwsQu1LKfnNJg<4mjO$|<;pBEFN*d{e|1aA|WEYNb$B!$4$|#_wWq z0m{Hq%&w%GOWw!y@)2~HNJ2W~y<_LoXsk_u;CjGOC)%A(Q-@>-NbIxOfLjlCZoWf; zipzD6XcA-j8@b*5(}LFFx{%IoW~sE${h*YrwZuvV)kkOuRja1#Gi9K@z(Xp5)s*?< zM~*m6cmbV*oJQ8Tx6U{E_kd2D^61UKQD(>D0z8;tf=ROZy4^_p6YNoaR6#W!dVwTc8~*4OFs$lo!k8L4*!YjA4);*ryv}26;b#aKd&aFZ>=TIL4eV~@Z^180hb$#Zi>G4 z`!x5-S20)(iry}-`iqNiUwgJACPQGaXE^-S57tBp{WkRZ&#pF=o1zoi>b0wCTzdnw zdgY5vW{Xmu`hXT$KvWj@hXWtmZr8s{>X_jBtp1ZP^8Ljoz)#RV9ZSp0Q zcqVn7jJAB5JJ#W-#b>zv405CU?VZt_qY|pDpu%SAXK^$qz7S=Cj}_{c9{tA+gxC>E zAK{g7l5Ny6t@X(t-0R($9I5CLg-0@wDHjT8gELtR(E;Ti%aytsZD|7I&*0ivrnS;y zaqp9jRI0mXds3AJPj1oZgq2lsJoC^sdoC@*-xHA~|(-%87(#<>Ofg{i2{!Fph;ca1FKD2vx*kKg)U8x|75+1Dg z4C2qf+Z>zYqC7$hYm)YoAAo}z9ZFb-+1yy)_mczkX44ken)?Tv{fp{BSs$jy(@8s< zcJCZ(d`Rgg3uJxj(=Uhwb))^*TWxI)0}SA?qTI za0j=-T7NlM$McUMEJSVlo)sG4?Thj5tg`Ci;sc7U(8lc;A+Fvvxbl0K0Vm774P1J# zCxf!8DOGBU<`X*~qDDB$DsMku46me&Lxn83-J#pumSA2}n-cNNn69Apn141;!f_~C zIl!?mEPTrboI%FLA%9SzOZbQ9#QfaJc?wk2JcEvdPe!PNA9ho`0zVmt((Y>@;4@rE0W=QJo4Q zPRZZr;oBWH5#XwB3e^Sq#s|oT%7z~x_#Ly=-n4iU%M=!j#<3L5wGw=e>z<)?kN}1s zNfqMHo`ft==DmIzo#mw_;C%W>$<%jXWb|Uu1%+M!0PF2`(@mw5(1+dSd(2 zVA-$W0X+5ITCG1oY-a#v|#|3HOYx$*tS+3BdWLG^NN&T?>|-)_lwQa=a)o z?H#>!(p)It_bzTlvysS}S6L^y-XeI?f2hk8((>FK-z~! zcl_nQcJqZV;M!uEG3~eNZ>v#vs}0LS(fusp1P+8##uw#rPf$fNe@_vIL1Ijq*zWY-DjovPDG7(jG{Chg0W3DL zt%QLsuI-mq`6&j$#qfyrUxyqwNrKCzf;R=@IteX7HJCXGj3SBqI4VOj^X&mkzk3XY z8^5#$_%?yW*f`AFodV{emC8FLbfnYa!pf=Ur8oP?8a zlboMp{VXbE$);t7wR-qT)-VZ%9aum381&###^r+xF;#xatMdmg<@Wk%qr1cn9h*`G z1+45KkJRU}z zb4vpSEgC6_Q*$60QOXc9i*LVFy#_wZv~A?%NcO3D{97%AyUZyCyF_DhlQpSP+E+N5 zg<&Zv%}dZ)y#RU)SkG=7*`z@l8iYp0F{dyrA`!74OwkcpfoOhdv0LaTkMkv21DNMd z;9i}D1DK;)d*8rI+(>(2XE{|TViUZNIuh^M>U44mQWqwX7A1(T%%Uh@zM1-!+Yyb| zpVpHXBZL(>KocU~kc5%2R5~dsP>ks5_0}Xf~t`UOt6wT)v2`gdJ;h z!VK*n3d!>2MY>FE^@=phtlzY7s95^aHiNm`4@BwlOQrRDCY1=hGN?N}uPZn4n% z^DVfbDj851WNp9=L@5vP$2X<`ECmv_z}f9rN$WmUUY}0gXoVpDpme})eyi*xN36jw zTtyH4Nm`kF>@m92L}6cxzo>X=qr3HXSmY8<+~WtPzc&F>esu_HbO62oI;;+ar#xs_ zbWVGNX5=0$6Wg7jfzL19Jge-PNh1+{uU+9&05BQZ*#*xhNhzTz%)>j9C?fske_>{T zEwy@cv92M_&_C{96wKi712%DhZ;PwNc6VVOJ(s{4&XG!78{JTX8iU ziW%GFgeYq+KBFnxAX#T|L>(`6jJ!ok=6u!)x%T28Igtrf!2AbrT0jT&Xx940s6$J! z_Et_pga?s7@bLB;e?Jp=47JjTF!l6Eeyvri3}l`sHP-ZqtFSP0ep14*-0%bi zO67qLNu%OOuY?D{S^u-H;}WPaQgxYS)J5zj*nIYU4|QXNLJ-rF;RpNzW^QAtr)=L_Q1Tr6YFKrGcMw-t zw})_t6k-BfBc0~q8R zeVM4;`;_O^%qp}WRNAc)tc0$J+zJqkJI^3c z9K1cfHOWGE2v|1B(vVJHBuzY8Y>*JlpeDgUDFw|z6e$?7;$Vmk{EtQQ4C-&s4&SAN zzDl{=P!3V5E2vRvrSj*wmIEReAFx(u7g&F^%SDHcKZvNPBYuk#04o>(+B|`?f^!42 zp1?@~+|M`tt2h0M^{^5X5!60g5#>?(GT*AkM@Z}b3}(@XaURy;ak`0 zX*(Nc(0%=E8qlEa4LXSpDOl#Q8u#_^rwMD5OEeYIQ85M z+?(a8y~2Qb`z2;5Z2B^duEvw&FH;4~c?M_wvNGohoOuSv0)P~ntG;k;Ck4tavZ@pGPehqIG_d=?mP427Ap&T{gs$AX5XQ zCSHF`Z#kfZ@I|q2&B`9T-1DqN8<^8;{6nhhe zx#G7Ee|*TtLIPA7odE77^D>d>JikQCop`O=hE(zqk?8z7d2+dQq4{|&Z^wfxp_Fejd6j=F50k4Qxgr)(c&8u>rm zxQaN*!^jdCa^jEO{0xQ3h*)kc1_6gjFj_s_B!H%`OpK%CGwn||5+LXz+4`pT60r2% zW5{lNj2AwpO4k|RGjTKKm3xmk2=$l)z=>huIkpgV04>;@*aU_>3%~Y9KD>Mb-RGBh zi>_i<<{o|=mY-bUj3cH-Is&!^4q+J4$ZwiAv`^eBtdn$4=ngLMc^X%k0C#72VG@k8 z9Tgo8&KCE~0Y0zk{*glrmn-xcw6Db8s#Gdk7+gcFL|?5mM_BU0<6$}gOGdqZ*m*Ru zG!kL=xc(|wiI>$hu{trMo^dIcIes&ap(&U317nXf(ml7oIB86`u}YYTR;)e-bCVZs)!03aZD>@&HQRVlx=1MsL0DEOO+bgl@B1pF!<`W>}NR>$^eww!~1k(x?X}*4i#So zsjk3aE3Ta6iIwqyGqOX#`cYo21np<&-PA@G#4^q!AcgWIa4-b@7EESe^Os-^iSBQh zNtz>50)hPGjQ{Kl7V52~x>^?v+cq=#kw8wy@h`ssikyrHEagc6x0&&mh%YY2#30rT z@22V0kM%3n4SRfc5`#-1Vj2ioT+$$wZFWl+}zC8 z7F{+5Pr_JU*!ig#_h|f@IQu6)egPU3p#HI@Cji{Wc`%dt;TbxVE&ObCKe?m|YpEhK zSX{RqK_fN^6NYj7--yv1MWs2L5eI{wn@a+I2|AIHfm*v7|FeHU7Ab0HW8qq0!v{Da z;6HH^yRl@K>ZQS}!%4hOR+V7iD)aaAJYC~|LA3k|mA7$FMWcUMb!j`e$=Kr&JVQd0 zKK-AE*C>W~WeTk=sTJ#KB}%hdn5mmeDfQ*>ZAOpd&~s|_t9o>+F;%}mazSpT( zR)sen52W+Uj&kG}Dly(rB-`s7%v#TTKK1Sd`H6v9KKZA=X;tJ%2NmQnCb1!H)Dwg^ zP^N=r=OAMT_(!6sX7Xe(y)oQB4h#ka=?8bUO|;fX<&5l3?5!S<5rx(PaviK4C9pB? zJ`H-8n^UJIn9J;MT?56a+N!?t5}2f(@!^+w(Tk$F%h6nHzgz_WCl1FQwkx-8);d3T!X|66}s$NRl~?{y=W$opGb;T?wmgZ8B1uW-5G%9w~IXm%z7YP$}_ zuLzmgDLefuY-LpA=K$?j-M-9KmA*P>>8duR>2GY4-_kfnZq8o31hF5l`c-XrcVU6H?1r;Ygx68hGil*)Y9(5#fs?$&Oqz(zE7#L8M&F!e^jqE_ zQ8M^4ap2xaym6mvTRxfb^~S-r%2(W=WP)ROFiA7 zdNw1tLl{TTxu>QkOsSHl3gjK0WOB{vL>pzp(wr574B)A;hGeIfheP^Eb#;*; zvY8Bz;cQnDt1S88?Hj!PnwJ&m&!RNFsuZHGbYu(MsyS(wFBr+nHo%4MYW{_%G%ShW-xnp@+p6rQF3A+cGJg$coEA z(I1Y^(TU4w8pz8mlz0%hq}k~rWY(nY0t1|X#y7bSg>B$kz{(>vj;Cc@2z!X*cQC1_ z(4==Fc8t5#NZtJwTZqf~6(EYKjmiW;@D49Q|oB)(+5))*Tr-ht6BaVL3mA_NXbx~^Q`834Me%c4o;E5)v43y|jSn7Z(=A6s6qAL3lFr+G(hSwcA4TN_c2 z!Y$~yWR8m$WbJ^*h_JEK3muBN~yA8Gw&c(+W7L6*ov-veOjNxLe20R-1zfe{`18F;Yeft;>;*>fZW05C>I-Np`A9LQ!vn5F z2VF?cEzMtd1ak{tViiK}*5HQwF!0&MOd}3NEh#MdW_V=6`?d~K=m}P2uRCc%28`HO zUlH3a)H@gE1~V;wlr!1NaUB57qiX!L2-JTR9)^A$!*RqdSf4&=A06gjW`sRjc5T& zRW|^qA5NsRPyK}luWd~AXV<{JZ!mK!q@IqZUivPim<2m+qhwkbZ#5-19;TRWn3W8H zqSrwNVxsGjspY1{4z+~yF7+2Yh#gIyD^8w&ee@}XHy++mOBldkHXrp1UIXG0Jjo*Nb-TWM5*4S1Xa-W=z3z1*`mB6) zk9c?qctpi#4Q%U7$*~t#T1(om$u?hS^w9L~ElDRIJ&#|WyNfRjaatf!E)vd2&u>6Z z9=qMAsxA`XagQ!GsAL{fm$~HJI3AGF(Axr>PI=Usb82b*{o^lo`%$T_(jnzsx=Vzy zY~-d@<2i?&mFxOrK{K^>{f9xKrnT;%0S1Xg8dUW{|`BPScGEU)E7U2 zK%&CG6KNRBxlxs1kE?sB?&O=E)xPr`@Cq*E*15ES%qo-A`Wj!)p%?v zq&51PXK})w`YiH zs*Jcu%vXSA-C|@;BurXu3DBSw0w7VsyiW&YC@x#-UCa)rPxu({=Lyjqu+!+c+^upR zKnk>_=d8NKWlEL^s#X^)j$49O1)lhr(upS$CPTxWU%V(A>i(2j@v2+zZ+>m_pH0gO zD29VhBwxRM8R=kv@j?0l>yiB77AY|#Yc^NQaQ)u5mb{2PtIyv<651vDg(2RtRYE29=6t%ptI+RjSa=)t z7Is*{vogZBTVjU9L6-EJ^BvwaM9$CmkDqszrk?^oLLf-~pT!^UdVV7T*Gr&7qR8myoF{h?SX)c|@D?i5XBsSQPJU z%h`pz$yFKD;yTm{a*YS+8LGvE{g9g%im|n4ONr(8Oj*?#$W9}b zBMQS;GQNXfcT^lvC?J_^kd;A*vIRzHfGahWcyfvXrNl5GN5A_Wo&(-a4W6H2piHFM zMQBAq_DGj}b#0c}r*J+cTVDk`OTM*+d&6Nc!3+ETHsEV&=m#7`-?JEa?^OuIn)-1F zgyZdb*P|FXl%$Dd9bhj60&DrtA|LqdUNj$#>d)=1j~uXB*dIK?H!8^WUL?LRs9?K3 z2NHDMrL)`B@mn`B{+h&EE~D@0cRrg7uH4&{pVrp+Ta!==T%kF9 zEm=GY-t%<$I6)_XNW&f5Z8eJMZHp)iUr5Zs9Dn4~i-+@r<&*?wIB{!2%GCkG5g_PA znFWRSLVn4l9?)#@Bq>K>g{Bpylkh2i(L+C8up*E)yfB0!=uGS9cnGQ%iKxq-Hb5Y&JbVQ9>NLq>I7w$d|_ z+QVw+TTgf`Tir6AfbJQ~;~vs|4hPdUm=EsT(iRtSmSYsMw)5eHHI;zR-R;T2vP%H! z#lsGM)ZT90qp@Y=&<#Ot|DVi1aYI|~=PldP-{x&9xA+#*v&1FE92f?IMvx6p!n$(= zpLb&Lm4tlD`&|IqzAMvRgdhBPh*K4F1h}_ji#hirO=Fxk`INrf2GYb%8b(gYd)O)i zB-72wlZlts2?@f3L3oM{tr3HnHPlE()%MZbA967tkrpH zE#%%FWf_2BJybj(tJ^P64j)H5yv{s{L%oDb{sn;uF7%;$SE*Ryw$V{>Dvt-~(aTQ9 z0!`gD!d6(Q5&38>r2=68&>t+8cG(%Ai6xvvLpm2s`R#_XJ-OHKZKlqFn_&{SBPqq? zQgo)&2;)CR<7z%yOz*_Io6e)Gn_S18HF>sE83tgj^0rMXkEFI*(RsIdjA84(BTRJ+ zq09d1g-xZ4f-8o`k4AO> zkxxtz+%k;vV)NJ*57ureGzJ|SW3F>N7Qx&UaLn!Macoua5&P)c;-Y5s_BnCtG6D>% z8V8U(`b<$Ofaj|`)kdPvu%*e_W1+4<^&{Vm(^M;2zt(rF=r*X#!3w8>QND7BpNf_r z4doTUVSV(x!r=NXwsz9^vv4~X<@>583?7r8J~cvKy%S7Ql^fcdo!JBR_ORkT`&Qh ztSrQW2J^3z<+au7R0`gZ0&4{hqiVhK)gJi^u3$Gu44GG*LPzj>IG3PS8W&=Vmd<-uOKxNWCHfVhZ~Jhyo)8! ze&pXRrP|}aEK+jiT11^iqmkOvG(`Yc74zZIW0#1K>y@2Pz27)gMNntdGES00a#^pZ z>m2T@EpAaJyPW81e`6nCzwt4?_J9k@&+uR(<+{dQ;N1GH^Ypx+*>3&f_=8`45t&@C z&T0+$L&Me1wZn*`S1O<(C2}Tl|9U=J_ z1wy#g21V->Q-C1eo%m~r`vhi0`aJs(e0;gJox>E)8IG&vc7r!AIXG%{Xt7u1rp)m) z;eF@V*lUyFBRo7p<$CP;0rGJ`8@`u9%yWt3 z6;nHA`RMfCW&Js!^>@f}wrsQ2tIUzrDql@HX#RE#&KU!PvLnuXGGkE+nsUf30~_Q0 z^musD(^BY*a1KkRrZ_g$Th$DF){W|#pg1OZ(T!?;eP?o%R+3!Owe0RbF^iL}xS%5P z@Hfm!oN9e=%IZ5#Cw;)TMU7jfP#Xa6pRXA_!?U-4tbW_{ig0%hjvZR|jueN(iw$?1G9aBYAnJKFRL0k|9jlo0Er_?#-NjFSO>BE!G;7>3$u_gZ@# zOWgY1@K9@o48kqLY3tr0+;|>mYy~NhhXUB-z|rPVcEv-SJ=t>~I&XJ4yYekY-}d6j zsngU#@v8I0sYu}!2%|o@Ga7v$P$Dp@asQcxc<}7~D3RZWMooOZW+nr2ma!mJr%p$; zilj6I5ymm33(7LJAHbS3IaL?3Tfc4YV+E=vxYl(>^A8os7S3y`Lldedtt0Pkp0oO8NiV?aXN( ztjPKj0IoH-*6#a*YYK95{PXiLYqUjBx@|J&oSG43>zNzP$oXLhC2+R~M*|_xNExh# zsq&0xG7?PV+0^;FA`{E?M2NB-);N7|=6(oJYb8p-@$5APQxox0Lp?gr@jB`r`wOxO zZ6DUHfPxj+R4q;Muvw}SxYTIGSpvOQT*90PfD~~wJvFr|AO3cB78;)SlZ-j@WVukM z4SqUO@|RIu6lUtdJib?U6Sp1}kn+sOMssIIw%&%$Qxl4tqy!YyD;{D7hj-$J*^0Vh zf4yb(Z8Yqp)-j-L2I3ESdSr z_ggw8yMwZEP!#IyKNN5mNYt5xte0wHWCA%9z$=WUo6kZM_mdn({>9_s;bDBU$9`&O zc~4-2EB_%u#ol;aY5-mvN$uMrR4icToV}|BG;p3!O~Gb=(*}DCQ=zusd;$EX>J;sP z6%JTkPvsk%7=wOIe`%u0M8i7gAd}E)p4d2Wib(mL)kB78VVHFVybCGt;jRD7NdeEn z1Q8W^D+1B5TJEWC{n+zWr0p;HOiczH{Qs@}cpVGY01^~r8eJS+Q#4CLK>SsNV$Gv# z!jvowp)xO190s@LB(d_}rM*5xY{oRY7#xB*rB4^t5Gv{z^lGHof=K+@Zuc_gGbT8O z98U_kICsb**sd2zKNBz=Cm83wG98jj8)1_X? z()nk0QOZZahMZk5rOy31$CPYq$wEg<5{9KtwlxVh2@2*4!ohL>KKOb*jB#swGc_aR z>hI3=mMIs9S}*`gV+=BNf042!n-UNh9Z3<%H-Y)S3sJpAHZR1vfX}c=-IgD)s{Qn& zQL*2sHrsAgU>h6+cAbLngRrpOkXX@qnDsK;?OQYSBXfL4V`_=3g+FK+~1BC zhN^_?iK*Sv;eE$yBjZBgpD+o})|8SL#l+rp?Jw3Bw=mhI2tq}hiSy1;$UbjR24Nn^ zWDq}zol0lGWR5QYVzkMo_?LwX9CM!&>uXEyH85yuT_j$$Ip+B9`)&B_UG!F>127WO zijpv%BY)Iaj&Uc$y7e<#v!^_8B!YhGa%ci9;nqg$+U5oVBZRp(gzr5aflA z)F~CM;@fUTp?Qk(`QUKcqS}gXC0ss(_@R_0CIc&m|AfQLcB;3m(dv#I+Xc!4tu8p! zStN6+n<`vMf>K(>^d||F1SqmQWc5B-T#d$AUjUPYO82_2FjL6>X;$xz!~lW9$V87% zejOx~mVwDD4UkbnP^ef7u#L8ou~c8D;Em3FC)G9!A295m~z&2sjwhLw8fJ|d_Qc%$!W6$B}tQQGfG6mnCm|*w|miU zKB0lN^zqwI8sJm!>iYxXd;r6I-g2(r8Fw~v$yR}huKlzpijLnxFncBR^IVouy$ve} zhu03E$Vg1c-OhnYQqzwL9|22r7-J=}jf73Bi{#~uaR%$s{xWLfwk;BqY(_N(8(qHZ zBp}5UY#wRSFHaT|hnytm1!bg!E_K)!hLS}%H#ZkTSYai27Y~Ld>;g+pp)Ym`lqR-T zXQHe&=|&Bc#%yZ*`E#QAI8PE*TU=gL@~$5+B*l{#uGbC20Mc9#u?6K=domM>Ey{$9 z`!pDNic`x|U*>JZ$U;&eaYB|(?h{NW4TIvpy`V<%-uEP!Fd;3lBvuY;BwcJivg z1Fyb{Onxg!rm=4Ig7p~BTvUK3*Tu3yoAdS%g)+T*zs!U|mMlwvbpXoH>~n+rt+N z=^=3FGn%h&p)E0}o?k+=FnFLE@kIc5k(nIYr-_iMp{+NvWF=r+f{t=?3*Rj=8nfh> z4k5w?=G*sD5Q*HFsvOCS!x-`gtv+q&U%tmA#vDT29(( z-|agMMnW8f4XUiU`6m5-nY?4tg5^d;Ck#AKBB#hR#N@^wF%)6CO`fkI?py<$`$uL1 z^x`3q{j@!b3MO`hiQr=CDL4q&6#kN3(H>_a{=k8rRj|)TOr+02N}MnI62D>uMU#u} zY0Op;EZAj|djOw63nw?N9m08J(QE>B2phld2pnPM3olz_lyAFF$HG*T*kO3L51%9c zoAnHGc%?uNFK5zKqpwIp+#H}_qL{f}v?>W+#(C~}@UbehcLcN)guhxqNi7C$#&kEm z5i}$O6)T=nfJ`WxccD3#+xPRXGGknwNpMRjf5LxcTkEG~&qR0&b+M(4Ag`Yx6m*6t z)6&Bo;j7|P;32AUkA~ssm zhxmC8yj)!2=ze{?wVjJ{^OqAK6zHZkGmV%$-!%?!QqH|js$(gJbuq8vC1_)tFp&&T+z0;7g&7`Qou;F`L zF}UU>X*G3__xrGyBq#OM)<+IAP!Pl6LqsK@eBcQK-!WZNgfB4KEItLG~-aED7pZL7OKaUUOt

    -R-;5wVoOVe2Cq_+itjMDmfVr@p z97y3%*r3UAsK(9HC!*?Pd%C1BN)kwHaeUwl6Bsti5u2C(HLPaR*LUjw;V3qsp<wadY z+=xfXbe;4_;Y};HE6)+f*j)?I_O8a?&onur!#=1gS{1iN-g}CQvdLqWk-pIELR>X8 zr6)P-jOkK|dNQ7KWl-!R2M%xv0 zkrI8RcvZmRZkS4-_W*^w{85CdC-~4VBm)$K{rGoetmCP)mdLlhqLRj3xL}IYYezj! zD~=|S+?Swgu@A3771Ehf>!dHhU(N33VJDp6zDW7tGcW6R z26KF;^544$H;pr;${-9YPxcSPdhGj0*E6pH(vo! z*|5!da}%=edgrY9;ZufLs-t-oU#(%WI9cfyoAYGG5se2a@M&IwTnd*WMS8{4ec1Tm zoyH~_z0QyecO>LSkRvUE^bao~s@<&sFLX+UvKi%hkJJ9CV@LMP>B;A~XB=}*fZ1Y) ztT|*ADFtOE*ohNn_+b~SUaAunNd0!}0l?uLf17ffv4cSMP-&Rtafo6dUFQM?9E0C? zQaBJ$x#8cmivMk>&0;V_QQ~sK_All|)7V9b?DM`h>-q_@zsQF5E;o`gFjM6QAqYK9 zw7Vfx477#eSQDj((d*5Iy8y)r)@R`f-!J6vTY(vC1R2WIIPRTTO|TrgTw(H}MhUdE zs6zJxrQK07^8J2hP|8d-fBY?q##$cVKtbRJzJQx6b*3iCT{Noqp{tp;Na#Tiy&u(- zc&KbqGTOQ{w~>i-&-jh-tSMt`1H6cbChAI*)r%PN(%&T6)YNy*x461nr#e~d@KW^q zA)u-tS(v{^->X=yP#3Zwr3bi7MZUvKE+p-}-4cFE2K-N6gO9;anAT4U2sSvV|1MYu zaxp=efHMQrnINRWU4g%uAcVkUfdtGD65tiUugnli;M>3&W(YCxW8ft-gfKW0keLNS z1e^u%RRuCOFa0OJrk~o2ZX^2TG z0`plRh=2lY5cm**PAQpAgurkX2vlImzlx{e6toaLU>h5#^*7)?8;Fq=AQ?NTumOH$ zhme3Ui%9W}AOQBVg9rqHr|ckxF@S6w5Of6po9GG-42&FUT{Kif diff --git a/Test Missions/Moose_Test_SPAWN/MOOSE_Test_SPAWN.lua b/Test Missions/Moose_Test_SPAWN/MOOSE_Test_SPAWN.lua index 71bb56e2f..293d8faf8 100644 --- a/Test Missions/Moose_Test_SPAWN/MOOSE_Test_SPAWN.lua +++ b/Test Missions/Moose_Test_SPAWN/MOOSE_Test_SPAWN.lua @@ -26,7 +26,7 @@ Group_Vehicle6 = Spawn_Vehicle:Spawn() Group_Vehicle1:TaskRouteToZone( ZONE:New( "Landing Zone" ), true, 40, "Cone" ) -- Now land the spawned plane on to the Vinson, by copying the route of another object. -Route_Plane = GROUP:NewFromName( "Spawn Helicopter Route Copy" ):CopyRoute( 1, 0 ) +Route_Plane = GROUP:FindByName( "Spawn Helicopter Route Copy" ):CopyRoute( 1, 0 ) Group_Plane:Route( Route_Plane ) diff --git a/Test Missions/Moose_Test_TASK_Pickup_and_Deploy/MOOSE_Test_TASK_Pickup_and_Deploy.lua b/Test Missions/Moose_Test_TASK_Pickup_and_Deploy/MOOSE_Test_TASK_Pickup_and_Deploy.lua index 516bf390a..53728aaa4 100644 --- a/Test Missions/Moose_Test_TASK_Pickup_and_Deploy/MOOSE_Test_TASK_Pickup_and_Deploy.lua +++ b/Test Missions/Moose_Test_TASK_Pickup_and_Deploy/MOOSE_Test_TASK_Pickup_and_Deploy.lua @@ -14,8 +14,8 @@ Include.File( "CleanUp" ) do local Mission = MISSION:New( 'Pickup', 'Operational', 'Pickup Troops', 'NATO' ) - Mission:AddClient( CLIENT:New( 'DE Pickup Test 1' ):Transport() ) - Mission:AddClient( CLIENT:New( 'DE Pickup Test 2' ):Transport() ) + Mission:AddClient( CLIENT:FindByName( 'DE Pickup Test 1' ):Transport() ) + Mission:AddClient( CLIENT:FindByName( 'DE Pickup Test 2' ):Transport() ) local CargoTable = {} @@ -62,7 +62,7 @@ end do local Mission = MISSION:New( 'Deliver secret letter', 'Operational', 'Pickup letter to the commander.', 'NATO' ) - Client_Package_1 = CLIENT:New( 'BE Package Test 1' ):Transport() + Client_Package_1 = CLIENT:FindByName( 'BE Package Test 1' ):Transport() Mission:AddClient( Client_Package_1 ) @@ -95,8 +95,8 @@ end do local Mission = MISSION:New( 'Sling load Cargo', 'Operational', 'Sling Load Cargo to Deploy Zone.', 'NATO' ) - Mission:AddClient( CLIENT:New( 'Sling Load Test Client 1' ):Transport() ) - Mission:AddClient( CLIENT:New( 'Sling Load Test Client 2' ):Transport() ) + Mission:AddClient( CLIENT:FindByName( 'Sling Load Test Client 1' ):Transport() ) + Mission:AddClient( CLIENT:FindByName( 'Sling Load Test Client 2' ):Transport() ) Sling_Load_Pickup_Zone = CARGO_ZONE:New( 'Sling Load Pickup Zone', 'Sling Load Guard' ):RedSmoke() diff --git a/Test Missions/Moose_Test_TASK_Pickup_and_Deploy/MOOSE_Test_TASK_Pickup_and_Deploy.miz b/Test Missions/Moose_Test_TASK_Pickup_and_Deploy/MOOSE_Test_TASK_Pickup_and_Deploy.miz index 2c514f75255e257d2930306d9f9486dde1bbaeb7..f92fcbb5226f2d696fd296c3c2697f30588ce182 100644 GIT binary patch delta 18043 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

    *xRjf!Ic(XX{-(>AZZZbyi0En9b=5x?p5p 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%0DD- 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 ITnpj>0GyDk_5c6? delta 17570 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 DaPT6c diff --git a/Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.lua b/Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.lua new file mode 100644 index 000000000..dc09e4e7d --- /dev/null +++ b/Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.lua @@ -0,0 +1,39 @@ + +Include.File( "Group" ) +Include.File( "Unit" ) +Include.File( "Client" ) + +BASE:TraceClass( "UNIT" ) +BASE:TraceClass( "GROUP" ) +BASE:TraceClass( "CLIENT" ) + +UnitTankAI1 = _DATABASE:FindUnit( "Smoke Test 1" ) +UnitTankAI2 = _DATABASE:FindUnit( "Smoke Test 2" ) +UnitTankAI3 = UNIT:FindByName( "Smoke Test 3" ) +UnitTankAI4 = _DATABASE:FindUnit( "Smoke Test 4" ) + +UnitTankAI1:SmokeBlue() + +UnitTankAI3:SmokeOrange() + +UnitTankAI2:T( UnitTankAI2:GetAmmo() ) + +GroupTanks = GROUP:FindByName( "Smoke Test" ) + +GroupTanks:T( GroupTanks:OptionROEOpenFirePossible() ) + +GroupTanks:OptionROEOpenFire() + + local function ClientAlive( Client, ClientNumber ) + GroupTanks:MessageToClient( "Hello Client " .. ClientNumber .. "! We are reporting to you on our way...", 5, Client ) + end + + +ClientHeli = CLIENT:FindByName( "Client Test 1", "Fly slowly to waypoint 3 of the Command Post!" ):Alive( ClientAlive, 1 ) +ClientHeli2 = CLIENT:FindByName( "Client Test 2", "Fly slowly to waypoint 3 of the Command Post!" ):Alive( ClientAlive, 2 ) + + + + + + diff --git a/Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.miz b/Test Missions/Moose_Test_WRAPPER/Moose_Test_WRAPPER.miz new file mode 100644 index 0000000000000000000000000000000000000000..3ba158dad7bd7d52018909bd6f38c5b67d17b7f6 GIT binary patch literal 35017 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 L%nS@STR=PjI1u&D literal 0 HcmV?d00001

    Client

    The CLIENT models client units in multi player missions.

    +
    DCSAirbase + +
    DCSCoalitionObject + +
    DCSCommand + +
    DCSController + +
    DCSGroup + +
    DCSObject + +
    DCSTask + +
    DCSTypes + +
    DCSUnit + +
    DCSWorld + +
    DCStimer +
    Group -

    A GROUP class abstraction of a DCSGroup class.

    +

    GROUP class.

    Spawn

    Dynamic spawning of groups (and units).

    +
    StaticObject +
    Zone

    ZONE Classes

    +
    env + +
    land +