From 0a68e7e2f151d6abfffeca2996edc494f003d7c4 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 30 Jul 2018 19:48:15 +0200 Subject: [PATCH 1/5] Pictures changed --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 688bbd759..0b1700e04 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -7,7 +7,7 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher --- @image AI_Cargo_Dispatching_For_Helicopters.JPG +-- @image AI_Cargo_Dispatcher.JPG --- @type AI_CARGO_DISPATCHER -- @extends Core.Fsm#FSM diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 5da5638cc..45013e153 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -9,7 +9,7 @@ -- === -- -- @module Tasking.Task_Cargo_Dispatcher --- @image MOOSE.JPG +-- @image Task_Cargo_Dispatcher.JPG do -- TASK_CARGO_DISPATCHER From 12dc173aea664cc0c62f9b750f2aa6aff07a6d03 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 30 Jul 2018 19:56:01 +0200 Subject: [PATCH 2/5] added features --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 45013e153..d732b148f 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -1,5 +1,47 @@ --- **Tasking** - Creates and manages player TASK_CARGO tasks. -- +-- **Specific features:** +-- +-- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones. +-- * Derived from the TASK_CARGO class, which is derived from the TASK class. +-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. +-- * Co-operation tasking, so a player joins a group of players executing the same task. +-- +-- +-- **A complete task menu system to allow players to:** +-- +-- * Join the task, abort the task. +-- * Mark the task location on the map. +-- * Provide details of the target. +-- * Route to the cargo. +-- * Route to the deploy zones. +-- * Load/Unload cargo. +-- * Board/Unboard cargo. +-- * Slingload cargo. +-- * Display the task briefing. +-- +-- +-- **A complete mission menu system to allow players to:** +-- +-- * Join a task, abort the task. +-- * Display task reports. +-- * Display mission statistics. +-- * Mark the task locations on the map. +-- * Provide details of the targets. +-- * Display the mission briefing. +-- * Provide status updates as retrieved from the command center. +-- * Automatically assign a random task as part of a mission. +-- * Manually assign a specific task as part of a mission. +-- +-- +-- **A settings system, using the settings menu:** +-- +-- * Tweak the duration of the display of messages. +-- * Switch between metric and imperial measurement system. +-- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. +-- * Different settings modes for A2G and A2A operations. +-- * Various other options. +-- -- === -- -- ### Author: **FlightControl** From 9a6be14fab380291c35152b7d9e1697d7783b7e4 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 31 Jul 2018 07:26:33 +0200 Subject: [PATCH 3/5] Revised the documentation. --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 269 ++++++++++-------- 1 file changed, 145 insertions(+), 124 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index d732b148f..f5072264b 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -1,5 +1,16 @@ --- **Tasking** - Creates and manages player TASK_CARGO tasks. -- +-- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human +-- players transport cargo as part of a task. +-- +-- The cargo dispatcher will implement for you mechanisms to create cargo transportation tasks: +-- +-- * As setup by the mission designer. +-- * Dynamically create CSAR missions (when a pilot is downed as part of a downed plane). +-- * Dynamically spawn new cargo and create cargo taskings! +-- +-- +-- -- **Specific features:** -- -- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones. @@ -67,148 +78,158 @@ do -- TASK_CARGO_DISPATCHER --- Implements the dynamic dispatching of cargo tasks. -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia3.JPG) + -- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human + -- players transport cargo as part of a task. -- - -- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. - -- Find a summary below describing for which situation a task type is created: + -- There are currently two types of tasks that can be constructed: -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia9.JPG) + -- * A normal cargo transport task, which tasks humans to transport cargo from a location towards a deploy zone. + -- * A CSAR cargo transport task. CSAR tasks are automatically generated when a pilot is downed... + -- It is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). -- - -- * **CSAR Task**: Is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). + -- Let's explore step by step how to setup the task dispatcher. -- - -- ## 1. TASK\_A2A\_DISPATCHER constructor: + -- # 1. Setup a mission environment. -- - -- The @{#TASK_CARGO_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. + -- It is easy, as it works just like any other task setup, so setup a command center and a mission. -- - -- ### 1.1. Define or set the **Mission**: + -- ## 1.1. Create a command center. -- - -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. + -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. -- - -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) - -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) - -- - -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. - -- Create the MISSION object, and hook it under the command center. - -- - -- ### 1.2. Build a set of the groups seated by human players: - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia6.JPG) - -- - -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. + -- local CommandCenter = COMMANDCENTER + -- :New( HQ, "Lima" ) -- Create the CommandCenter. -- - -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() - -- - -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. - -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. + -- ## 1.2. Create a mission. -- - -- ### 1.3. Define the **EWR network**: + -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. + -- A command center can govern multiple missions. + -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. -- - -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. - -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- -- Declare the Mission for the Command Center. + -- local Mission = MISSION + -- :New( CommandCenter, + -- "Overlord", + -- "High", + -- "Transport the cargo.", + -- coalition.side.RED + -- ) -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia5.JPG) -- - -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. - -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). - -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. - -- The position of these units is very important as they need to provide enough coverage - -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- # 2. Dispatch a **transport cargo** task. -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia7.JPG) + -- So, now that we have a command center and a mission, we now create the transport task. + -- We create the transport task using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() constructor. + -- + -- ## 2.1. Create the cargo in the mission. + -- + -- Because a transport task will not generate the cargo itself, you'll need to create it first. + -- + -- -- Here we define the "cargo set", which is a collection of cargo objects. + -- -- The cargo set will be the input for the cargo transportation task. + -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. + -- local WorkmaterialsCargoSet = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() + -- + -- -- Now we add cargo into the battle scene. + -- local PilotGroup = GROUP:FindByName( "Engineers" ) + -- + -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. + -- -- We name this group Engineers. + -- -- Note that the name of the cargo is "Engineers". + -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. + -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Workmaterials", "Engineer Team 1", 500 ) + -- + -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. + -- + -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- local PilotGroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() + -- + -- ## 2.2. Setup the cargo transport task. + -- + -- First, we need to create a TASK_CARGO_DISPATCHER object. + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, PilotGroupSet ) + -- + -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo transport tasks: -- - -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. - -- For example if they are a long way forward and can detect enemy planes on the ground and taking off - -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. - -- Having the radars further back will mean a slower escalation because fewer targets will be detected and - -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. - -- It all depends on what the desired effect is. + -- * for mission `Mission` + -- * for the pilots `PilotGroupSet`. -- - -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. - -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, - -- increasing or decreasing the radar coverage of the Early Warning System. + -- Now that we have `TaskDispatcher` object, we can now create the TransportTask manually, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! -- - -- See the following example to setup an EWR network containing EWR stations and AWACS. + -- local TransportTask = TaskDispatcher:AddTransportTask( + -- "Transport workmaterials", + -- WorkmaterialsCargoSet, + -- "Transport the workers, engineers and the equipment near the Workplace." ) -- - -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() - -- - -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) - -- EWRDetection:SetFriendliesRange( 10000 ) - -- EWRDetection:SetRefreshTimeInterval(30) - -- - -- -- Setup the A2A dispatcher, and initialize it. - -- A2ADispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) + -- And you're done! As you can see, it is a bit of work, but the reward is great. + -- And, because all this is done using program interfaces, you can build a mission with a **dynamic cargo transport task mechanism** yourself! + -- Based on events happening within your mission, you can use the above methods to create new cargo, and setup a new task for cargo transportation to a group of players! -- - -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. - -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. - -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. - -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. - -- The **EWRDetection** object is then passed to the @{#TASK_CARGO_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. - -- - -- ### 2. Define the detected **target grouping radius**: - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia8.JPG) - -- - -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. - -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. - -- Fast planes like in the 80s, need a larger radius than WWII planes. - -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. - -- - -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate - -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! - -- - -- ## 3. Set the **Engage radius**: - -- - -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia11.JPG) - -- - -- So, if there is a target area detected and reported, - -- then any friendlies that are airborne near this target area, - -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). - -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, - -- will be considered to receive the command to engage that target area. - -- You need to evaluate the value of this parameter carefully. - -- If too small, more intercept missions may be triggered upon detected target areas. - -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. - -- - -- ## 4. Set **Scoring** and **Messages**: - -- - -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER. - -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, - -- when the player reaches certain achievements in the task. - -- - -- The prototype to handle the **Assign** event needs to be developed as follows: - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( ... ) - -- - -- --- @param #TaskDispatcher self - -- -- @param #string From Contains the name of the state from where the Event was triggered. - -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. - -- -- @param #string To Contains the name of the state that will be transitioned to. - -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. - -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. - -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. - -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) - -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) - -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) - -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) - -- end - -- - -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. - -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. - -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. - -- - -- The TASK class implements various methods to additional **set scoring** for player achievements: - -- - -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. - -- Examples of **task progress** can be destroying units, arriving at zones etc. - -- - -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. - -- This means the **task has been successfully completed**. -- - -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. - -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! + -- # 3. Dispatch CSAR tasks. + -- + -- CSAR tasks can be dynamically created when a friendly pilot ejects, or can be created manually. + -- We'll explore both options. + -- + -- ## 3.1. CSAR task dynamic creation. + -- + -- Because there is an "event" in a running simulation that creates CSAR tasks, the method @{#TASK_CARGO_DISPATCHER.StartCSARTasks}() will create automatically: + -- + -- 1. a new downed pilot at the location where the plane was shot + -- 2. declare that pilot as cargo + -- 3. creates a CSAR task automatically to retrieve that pilot + -- 4. requires deploy zones to be specified where to transport the downed pilot to, in order to complete that task. + -- + -- You create a CSAR task dynamically in a very easy way: + -- + -- TaskDispatcher:StartCSARTasks( + -- "CSAR", + -- { ZONE_UNIT:New( "Hospital", STATIC:FindByName( "Hospital" ), 100 ) }, + -- "One of our pilots has ejected. Go out to Search and Rescue our pilot!\n" .. + -- "Use the radio menu to let the command center assist you with the CSAR tasking." + -- ) + -- + -- The method @{#TASK_CARGO_DISPATCHER.StopCSARTasks}() will automatically stop with the creation of CSAR tasks when friendly pilots eject. + -- + -- **Remarks:** + -- + -- * the ZONE_UNIT can also be a ZONE, or a ZONE_POLYGON object, or any other ZONE_ object! + -- * you can declare the array of zones in another variable, or course! + -- + -- + -- ## 3.2. CSAR task manual creation. + -- + -- We create the CSAR task using the @{#TASK_CARGO_DISPATCHER.AddCSARTask}() constructor. + -- + -- The method will create a new CSAR task, and will generate the pilots cargo itself, at the specified coordinate. + -- + -- What is first needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. + -- + -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() + -- + -- We need to create a TASK_CARGO_DISPATCHER object. + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, GroupSet ) + -- + -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo CSAR tasks: + -- + -- * for mission `Mission`. + -- * for the group of players (pilots) captured within the `GroupSet` (those groups with a name starting with `"Transport"`). + -- + -- Now that we have a PilotsCargoSet and a GroupSet, we can now create the CSAR task manually. + -- + -- -- Declare the CSAR task. + -- local CSARTask = TaskDispatcher:AddCSARTask( + -- "CSAR Task", + -- Coordinate, + -- 270, + -- "Bring the pilot back!" + -- ) + -- + -- Note that when you declare a CSAR task manually, you'll still need to specify a deployment zone! + -- -- -- @field #TASK_CARGO_DISPATCHER TASK_CARGO_DISPATCHER = { @@ -363,11 +384,11 @@ do -- TASK_CARGO_DISPATCHER -- -- -- Add a CSAR task to rescue a downed pilot from within a coordinate. -- local Coordinate = PlaneUnit:GetPointVec2() - -- TaskA2ADispatcher:AddCSARTask( Coordinate ) + -- TaskA2ADispatcher:AddCSARTask( "CSAR Task", Coordinate ) -- -- -- Add a CSAR task to rescue a downed pilot from within a coordinate of country RUSSIA, which is pointing to the west (270°). -- local Coordinate = PlaneUnit:GetPointVec2() - -- TaskA2ADispatcher:AddCSARTask( Coordinate, 270, Country.RUSSIA ) + -- TaskA2ADispatcher:AddCSARTask( "CSAR Task", Coordinate, 270, Country.RUSSIA ) -- function TASK_CARGO_DISPATCHER:AddCSARTask( CSARTaskPrefix, CSARCoordinate, CSARHeading, CSARCountry, CSARBriefing ) From 12be42ee2fb435759361278912ef53fd319a927b Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 31 Jul 2018 21:47:25 +0200 Subject: [PATCH 4/5] Documentation updates --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index f5072264b..d1473dd4d 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -81,13 +81,13 @@ do -- TASK_CARGO_DISPATCHER -- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human -- players transport cargo as part of a task. -- - -- There are currently two types of tasks that can be constructed: + -- There are currently **two types of tasks** that can be constructed: -- - -- * A normal cargo transport task, which tasks humans to transport cargo from a location towards a deploy zone. - -- * A CSAR cargo transport task. CSAR tasks are automatically generated when a pilot is downed... - -- It is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). + -- * A **normal cargo transport** task, which tasks humans to transport cargo from a location towards a deploy zone. + -- * A **CSAR** cargo transport task. CSAR tasks are **automatically generated** when a friendly (AI) plane is downed and the friendly pilot ejects... + -- You as a player (the helo pilot) can go out in the battlefield, fly behind enemy lines, and rescue the pilot (back to a deploy zone). -- - -- Let's explore step by step how to setup the task dispatcher. + -- Let's explore **step by step** how to setup the task cargo dispatcher. -- -- # 1. Setup a mission environment. -- @@ -127,21 +127,21 @@ do -- TASK_CARGO_DISPATCHER -- -- -- Here we define the "cargo set", which is a collection of cargo objects. -- -- The cargo set will be the input for the cargo transportation task. - -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. + -- -- So a transportation object is handling a cargo set, which is automatically updated when new cargo is added/deleted. -- local WorkmaterialsCargoSet = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() -- -- -- Now we add cargo into the battle scene. -- local PilotGroup = GROUP:FindByName( "Engineers" ) -- -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. - -- -- We name this group Engineers. - -- -- Note that the name of the cargo is "Engineers". - -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. + -- -- We name the type of this group "Workmaterials", so that this cargo group will be included within the WorkmaterialsCargoSet. + -- -- Note that the name of the cargo is "Engineer Team 1". -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Workmaterials", "Engineer Team 1", 500 ) -- -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. -- - -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- -- Allocate the Transport, which are the helicopters to retrieve the pilot, that can be manned by players. + -- -- The name of these helicopter groups containing one client begins with "Transport", as modelled within the mission editor. -- local PilotGroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() -- -- ## 2.2. Setup the cargo transport task. @@ -152,16 +152,20 @@ do -- TASK_CARGO_DISPATCHER -- -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo transport tasks: -- - -- * for mission `Mission` - -- * for the pilots `PilotGroupSet`. + -- * for mission `Mission`. + -- * for the group set `PilotGroupSet`. -- - -- Now that we have `TaskDispatcher` object, we can now create the TransportTask manually, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! + -- Now that we have `TaskDispatcher` object, we can now **create the TransportTask**, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! -- -- local TransportTask = TaskDispatcher:AddTransportTask( -- "Transport workmaterials", -- WorkmaterialsCargoSet, -- "Transport the workers, engineers and the equipment near the Workplace." ) -- + -- As a result of this code, the `TransportTask` (returned) variable will contain an object of @{#TASK_CARGO_TRANSPORT}! + -- We pass to the method the title of the task, and the `WorkmaterialsCargoSet`, which is the set of cargo groups to be transported! + -- This object can also be used to setup additional things, or to control this specific task with special actions. + -- -- And you're done! As you can see, it is a bit of work, but the reward is great. -- And, because all this is done using program interfaces, you can build a mission with a **dynamic cargo transport task mechanism** yourself! -- Based on events happening within your mission, you can use the above methods to create new cargo, and setup a new task for cargo transportation to a group of players! @@ -228,8 +232,35 @@ do -- TASK_CARGO_DISPATCHER -- "Bring the pilot back!" -- ) -- + -- As a result of this code, the `CSARTask` (returned) variable will contain an object of @{#TASK_CARGO_CSAR}! + -- We pass to the method the title of the task, and the `WorkmaterialsCargoSet`, which is the set of cargo groups to be transported! + -- This object can also be used to setup additional things, or to control this specific task with special actions. -- Note that when you declare a CSAR task manually, you'll still need to specify a deployment zone! -- + -- # 4. Setup the deploy zone(s). + -- + -- The task cargo dispatcher also foresees methods to setup the deployment zones to where the cargo needs to be transported! + -- + -- There are two levels on which deployment zones can be configured: + -- + -- * Default deploy zones: The TASK_CARGO_DISPATCHER object can have default deployment zones, which will apply over all tasks active in the task dispatcher. + -- * Task specific deploy zones: The TASK_CARGO_DISPATCHER object can have specific deployment zones which apply to a specific task only! + -- + -- Note that for Task specific deployment zones, there are separate deployment zone creation methods per task type! + -- + -- ## 4.1. Setup default deploy zones. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetDefaultDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetDefaultDeployZones}() to setup multiple default deployment zones in one call. + -- + -- ## 4.2. Setup task specific deploy zones for a **transport task**. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetTransportDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetTransportDeployZones}() to setup multiple default deployment zones in one call. + -- + -- ## 4.3. Setup task specific deploy zones for a **CSAR task**. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetCSARDeployZones}() to setup multiple default deployment zones in one call. + -- + -- -- -- @field #TASK_CARGO_DISPATCHER TASK_CARGO_DISPATCHER = { From e93f2c54b283ed47e7c99a1d581a88f175b6a650 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 4 Aug 2018 16:47:18 +0200 Subject: [PATCH 5/5] Working version, but needs to be upgraded. --- .../Moose/AI/AI_Cargo_Airplane.lua | 32 ++++++- .../Moose/AI/AI_Cargo_Dispatcher.lua | 93 ++++++++++++++++--- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 32 ++++--- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- Moose Development/Moose/Core/Set.lua | 39 ++++++++ 6 files changed, 171 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index bbbe19ed0..dc3076e95 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -111,6 +111,18 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) end +function AI_CARGO_AIRPLANE:IsTransporting() + + return self.Transporting == true +end + +function AI_CARGO_AIRPLANE:IsRelocating() + + return self.Relocating == true +end + + + --- Set the Carrier. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane @@ -155,7 +167,8 @@ function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) function Airplane:OnEventEngineShutdown( EventData ) - AICargo:Landed() + self:F("Calling") + AICargo:Landed( self.Airplane ) end self.Coalition = self.Airplane:GetCoalition() @@ -199,6 +212,10 @@ end -- @param #number Speed function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) + self:F({Airplane, From, Event, To}) + self:F({IsAlive=Airplane:IsAlive()}) + self:F({RoutePickup=self.RoutePickup}) + if Airplane and Airplane:IsAlive() then if self.RoutePickup == true then @@ -230,6 +247,8 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp self:Route( Airplane, Airbase, Speed ) self.RoutePickup = true self.Airbase = Airbase + self.Transporting = true + self.Relocating = false end end @@ -248,6 +267,8 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp self:Route( Airplane, Airbase, Speed ) self.RouteDeploy = true self.Airbase = Airbase + self.Transporting = false + self.Relocating = false end end @@ -260,7 +281,9 @@ function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) if Airplane and Airplane:IsAlive() then for _, Cargo in pairs( self.CargoSet:GetSet() ) do - if Cargo:IsInLoadRadius( Coordinate ) then + self:F({Cargo:GetName()}) + local InRadius = Cargo:IsInLoadRadius( Coordinate ) + if InRadius then self:__Board( 5 ) Cargo:Board( Airplane, 25 ) self.Cargo = Cargo @@ -359,6 +382,7 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) FromWaypoint.helipadId = nil FromWaypoint.airdromeId = nil + local ParkingSpots = self.Airbase:FindFreeParkingSpotForAircraft( Airplane, AIRBASE.TerminalType.OpenBig ) local AirbaseID = self.Airbase:GetID() local AirbaseCategory = self.Airbase:GetDesc().category @@ -377,8 +401,8 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) -- These cause a lot of confusion. local UnitTemplate = Template.units[UnitID] - UnitTemplate.parking = 15 - UnitTemplate.parking_id = "1" + UnitTemplate.parking = nil + UnitTemplate.parking_id = nil UnitTemplate.alt = 0 local SX = UnitTemplate.x diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 0b1700e04..6b3732121 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -74,7 +74,7 @@ AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", SetCarrier = nil, - SetDeployZones = nil, + DeployZonesSet = nil, AI_Cargo = {}, PickupCargo = {} } @@ -90,23 +90,21 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- @param #AI_CARGO_DISPATCHER self -- @param Core.Set#SET_GROUP SetCarrier -- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZones -- @return #AI_CARGO_DISPATCHER -- @usage -- -- -- Create a new cargo dispatcher --- SetCarrier = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) -- -function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER self.SetCarrier = SetCarrier -- Core.Set#SET_GROUP self.SetCargo = SetCargo -- Core.Set#SET_CARGO - self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE self:SetStartState( "Idle" ) @@ -144,6 +142,58 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) end +--- Creates a new AI_CARGO_DISPATCHER object. +-- @param #AI_CARGO_DISPATCHER self +-- @param Core.Set#SET_GROUP SetCarrier +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE DeployZonesSet +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- DeployZonesSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- +function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, DeployZonesSet ) + + local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER + + self.DeployZonesSet = DeployZonesSet + + return self +end + + +--- Creates a new AI_CARGO_DISPATCHER object. +-- @param #AI_CARGO_DISPATCHER self +-- @param Core.Set#SET_GROUP SetCarrier +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_AIRBASE PickupAirbasesSet +-- @param Core.Set#SET_AIRBASE DeployAirbasesSet +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- PickupAirbasesSet = SET_AIRBASES:New() +-- DeployAirbasesSet = SET_AIRBASES:New() +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, PickupAirbasesSet, DeployAirbasesSet ) +-- +function AI_CARGO_DISPATCHER:NewWithAirbases( SetCarriers, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) + + local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER + + self.DeployAirbasesSet = DeployAirbasesSet + self.PickupAirbasesSet = PickupAirbasesSet + + return self +end + + + --- Set the home zone. -- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone. -- They will await here new orders. @@ -361,7 +411,16 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) - AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) + + if self.PickupAirbasesSet then + -- Find airbase within 2km from the cargo with the set. + local PickupAirbase = self.PickupAirbasesSet:FindAirbaseInRange( PickupCoordinate, 4000 ) + if PickupAirbase then + AI_Cargo:Pickup( PickupAirbase, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) + end + else + AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) + end break else if self.HomeZone then @@ -464,12 +523,22 @@ end -- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) - local DeployZone = self.SetDeployZones:GetRandomZone() + if self.DeployZonesSet then - local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + local DeployZone = self.DeployZonesSet:GetRandomZone() + + local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) + self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) - self.PickupCargo[Carrier] = nil + end + + if self.DeployAirbasesSet then + + local DeployAirbase = self.DeployAirbasesSet:GetRandomAirbase() + self.AI_Cargo[Carrier]:Deploy( DeployAirbase, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + end + + self.PickupCargo[Carrier] = nil end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 561125786..413ca7c26 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -101,7 +101,7 @@ AI_CARGO_DISPATCHER_APC = { -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) -- -function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRadius ) +function AI_CARGO_DISPATCHER_APC:NewWithZones( SetAPC, SetCargo, SetDeployZone, CombatRadius ) local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index c208bab9a..52dd0a712 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -16,8 +16,8 @@ --- Brings a dynamic cargo handling capability for AI groups. -- -- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER\_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_AIRPLANE object recognize the cargo. +-- The AI_CARGO_DISPATCHER_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_AIRPLANE object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- -- @@ -29,23 +29,33 @@ AI_CARGO_DISPATCHER_AIRPLANE = { --- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. -- @param #AI_CARGO_DISPATCHER_AIRPLANE self --- @param Core.Set#SET_GROUP SetAirplane --- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZone +-- @param Core.Set#SET_GROUP SetAirplanes +-- @param Core.Set#SET_CARGO SetCargos +-- @param Core.Set#SET_AIRBASE PickupAirbasesSet +-- @param Core.Set#SET_AIRBASE DeployAirbasesSet -- @return #AI_CARGO_DISPATCHER_AIRPLANE -- @usage -- -- -- Create a new cargo dispatcher --- SetAirplane = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo ) +-- SetAirplanes = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- PickupAirbasesSet = SET_AIRBASE:New() +-- DeployAirbasesSet = SET_AIRBASE:New() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) -- -function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAirplane, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithAirbases( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + + self:SetDeploySpeed( 200, 150 ) + self:SetPickupSpeed( 200, 150 ) + self:SetPickupRadius( 0, 0 ) + self:SetDeployRadius( 0, 0 ) return self end +function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, SetCargo ) + return AI_CARGO_AIRPLANE:New( Airplane, SetCargo ) +end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index eaf9666d2..26b5d9aab 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -102,7 +102,7 @@ AI_CARGO_DISPATCHER_HELICOPTER = { -- function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER self:SetDeploySpeed( 200, 150 ) self:SetPickupSpeed( 200, 150 ) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e81beb007..7183623e0 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4010,6 +4010,45 @@ function SET_AIRBASE:FindAirbase( AirbaseName ) end +--- Finds an Airbase in range of a coordinate. +-- @param #SET_AIRBASE self +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Range +-- @return Wrapper.Airbase#AIRBASE The found Airbase. +function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range ) + + local AirbaseFound = nil + + for AirbaseName, AirbaseObject in pairs( self.Set ) do + + local AirbaseCoordinate = AirbaseObject:GetCoordinate() + local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) + + self:F({Distance=Distance}) + + if Distance <= Range then + AirbaseFound = AirbaseObject + break + end + + end + + return AirbaseFound +end + + +--- Finds a random Airbase in the set. +-- @param #SET_AIRBASE self +-- @return Wrapper.Airbase#AIRBASE The found Airbase. +function SET_AIRBASE:GetRandomAirbase() + + local RandomAirbase = self:GetRandom() + self:F( { RandomAirbase = RandomAirbase:GetName() } ) + + return RandomAirbase +end + + --- Builds a set of airbases of coalitions. -- Possible current coalitions are red, blue and neutral.