From 1baeba251ed8660550c5a93fbf93365dbe8b490b Mon Sep 17 00:00:00 2001
From: funkyfranky
Date: Mon, 11 Sep 2017 00:11:50 +0200
Subject: [PATCH] Added RAT documentation.
Caught some user errors for unknown airports etc.
Improved despawn on inactive planes.
---
Moose Development/Moose/Functional/RAT.lua | 653 ++++++++++++------
docs/Documentation/AI_A2A_Dispatcher.html | 27 +
docs/Documentation/AI_Patrol.html | 3 -
docs/Documentation/Movement.html | 4 -
docs/Documentation/Point.html | 74 ++
docs/Documentation/RAT.html | 493 +++++++++----
docs/Documentation/Spawn.html | 28 +-
docs/Documentation/Task.html | 60 ++
docs/Documentation/index.html | 2 +-
.../RAT/RAT_Airport_Selection.png | Bin 0 -> 574126 bytes
.../RAT/RAT_Basic_Lua_Script.png | Bin 0 -> 1371450 bytes
docs/Presentations/RAT/RAT_Examples_Misc.png | Bin 0 -> 452108 bytes
.../RAT/RAT_Examples_Spawn_at_Zones.png | Bin 0 -> 2733389 bytes
.../RAT/RAT_Examples_Spawn_in_Air.png | Bin 0 -> 312513 bytes
...ples_Specify_Departure_and_Destination.png | Bin 0 -> 441902 bytes
docs/Presentations/RAT/RAT_Flight_Plan.png | Bin 0 -> 128246 bytes
docs/Presentations/RAT/RAT_Mission_Setup.png | Bin 0 -> 1387600 bytes
17 files changed, 963 insertions(+), 381 deletions(-)
create mode 100644 docs/Presentations/RAT/RAT_Airport_Selection.png
create mode 100644 docs/Presentations/RAT/RAT_Basic_Lua_Script.png
create mode 100644 docs/Presentations/RAT/RAT_Examples_Misc.png
create mode 100644 docs/Presentations/RAT/RAT_Examples_Spawn_at_Zones.png
create mode 100644 docs/Presentations/RAT/RAT_Examples_Spawn_in_Air.png
create mode 100644 docs/Presentations/RAT/RAT_Examples_Specify_Departure_and_Destination.png
create mode 100644 docs/Presentations/RAT/RAT_Flight_Plan.png
create mode 100644 docs/Presentations/RAT/RAT_Mission_Setup.png
diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua
index 2194704de..8ecade729 100644
--- a/Moose Development/Moose/Functional/RAT.lua
+++ b/Moose Development/Moose/Functional/RAT.lua
@@ -1,19 +1,44 @@
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- **Functional** -- Create random airtraffic in your missions.
+--- **Functional** - Create random airtraffic in your missions.
--
-- 
--
-- ====
--
--- The documentation of the RAT class can be found further in this document.
+-- The aim of the RAT class is to fill the empty DCS world with randomized air traffic and bring more life to your airports.
+--
+-- In particular, it is designed to spawn AI air units at random airports. These units will be assigned a random flight path to another random airport on the map.
+--
+-- Even the mission designer will not know where aircraft will be spawned and which route they follow.
+--
+-- ## Features
+--
+-- * Very simple interface. Just one unit and two lines of Lua code needed to fill your map.
+-- * High degree of randomization. Aircraft will spawn at random airports, have random routes and random destinations.
+-- * Specific departure and/or destination airports can be chosen.
+-- * Departure and destination airports can be restricted by coalition.
+-- * Planes and helicopters supported. Helicopters can also be send to FARPs and ships.
+-- * Units can also be spawned in air within pre-defined zones of the map.
+-- * Aircraft will be removed when they arrive at their destination (or get stuck on the ground).
+-- * When a unit is removed a new unit with a different flight plan is respawned.
+-- * Aircraft can report their status during the route.
+-- * All of the above can be customized by the user if necessary.
+-- * All current (Caucasus, Nevada, Normandy) and future maps are supported.
+--
+-- The RAT class creates an entry in the F10 menu which allows to
+--
+-- * Create new groups on-the-fly, i.e. at run time within the mission,
+-- * Destroy specific groups (e.g. if they get stuck or damaged and block a runway),
+-- * Request the status of all RAT aircraft or individual groups,
+-- * Place markers at waypoints on the F10 map for each group.
+--
+-- Note that by its very nature, this class is suited best for civil or transport aircraft. However, it also works perfectly fine for military aircraft of any kind.
+--
+-- More of the documentation include some simple examples can be found further down this page.
--
-- ====
--
-- # Demo Missions
---
--- ### [RAT Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
---
--- ### [RAT Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
@@ -24,74 +49,192 @@
-- ### [RAT YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL)
--
-- ===
--- ====
--
-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)**
--
--- ### Contributions: **Sven Van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))**
+-- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))**
--
-- ====
-- @module Rat
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
--- TODO: Add description.
-
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
---- RAT class
+--- RAT class
-- @type RAT
--- @field #string ClassName Name of the Class
--- @field #boolean debug
--- @field #number spawndelay
--- @field #number spawninterval
--- @field #number coalition
--- @field #string category
--- @field #string friendly
--- @field #table ctable
--- @field #table aircraft
--- @field #number Vcruisemax
--- @field #number Vclimb
--- @field #number AlphaDescent
--- @field #string roe
--- @field #string rot
--- @field #string takeoff
--- @field #number mindist
--- @field #number maxdist
--- @field #table airports_map
--- @field #table airports
--- @field #boolean random_departure
--- @field #boolean random_destination
--- @field #table departure_zones
--- @field #table departure_ports
--- @field #table destination_ports
--- @field #table ratcraft
--- @field #boolean reportstatus
--- @field #number statusinterval
--- @field #boolean placemarkers
--- @field #number FLuser
--- @field #number FLminuser
--- @field #number FLmaxuser
--- @field #boolean commute
--- @field #boolean continuejourney
--- @field #number alive
--- @field #boolean f10menu
--- @field #table Menu
--- @field #string SubMenuName
--- @field #boolean respawn_after_landing
--- @field #number respawn_delay
--- @field #table markerids
--- @field #table RAT
+-- @field #string ClassName Name of the Class.
+-- @field #boolean debug Turn debug messages on or off.
+-- @field #number spawndelay Delay time in seconds before first spawning happens.
+-- @field #number spawninterval Interval between spawning units/groups. Note that we add a randomization of 50%.
+-- @field #number coalition Coalition of spawn group template.
+-- @field #string category Category of aircarft: "plane" or "heli".
+-- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
+-- @field #table ctable Table with the valid coalitons from choice self.friendly.
+-- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...).
+-- @field #number Vcruisemax Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user.
+-- @field #number Vclimb Default climb rate in ft/min.
+-- @field #number AlphaDescent Default angle of descenti in degrees. A value of 3.6 follows the 3:1 rule of 3 miles of travel and 1000 ft descent.
+-- @field #string roe ROE of spawned groups, default is weapon hold (this is a peaceful class for civil aircraft or ferry missions). Possible: "hold", "return", "free".
+-- @field #string rot ROT of spawned groups, default is no reaction. Possible: "noreaction", "passive", "evade".
+-- @field #string takeoff Takeoff type. 0=coldorhot.
+-- @field #number mindist Min distance from departure to destination in meters. Default 5 km.
+-- @field #number maxdist Max distance from departure to destination in meters. Default 5000 km.
+-- @field #table airports_map All airports available on current map (Caucasus, Nevada, Normandy, ...).
+-- @field #table airports All airports of friedly coalitions.
+-- @field #boolean random_departure By default a random friendly airport is chosen as departure.
+-- @field #boolean random_destination By default a random friendly airport is chosen as destination.
+-- @field #table departure_zones Array containing the names of the departure zones.
+-- @field #table departure_ports Array containing the names of the destination airports.
+-- @field #table destination_ports Array containing the names of the destination airports.
+-- @field #table ratcraft Array with the spawned RAT aircraft.
+-- @field #number Tinactive Time in seconds after which inactive units will be destroyed. Default is 180 seconds.
+-- @field #boolean reportstatus Aircraft report status.
+-- @field #number statusinterval Intervall between status checks (and reports if enabled).
+-- @field #boolean placemarkers Place markers of waypoints on F10 map.
+-- @field #number FLuser Flight level set by users explicitly.
+-- @field #number FLminuser Minimum flight level set by user.
+-- @field #number FLmaxuser Maximum flight level set by user.
+-- @field #boolean commute Aircraft commute between departure and destination, i.e. when respawned the departure airport becomes the new destiation.
+-- @field #boolean continuejourney Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination.
+-- @field #number ngroups Number of groups to be spawned in total.
+-- @field #number alive Number of groups which are alive.
+-- @field #boolean f10menu Add an F10 menu for RAT.
+-- @field #table Menu F10 menu items for this RAT object.
+-- @field #string SubMenuName Submenu name for RAT object.
+-- @field #boolean respawn_at_landing Respawn aircraft the moment they land rather than at engine shutdown.
+-- @field #number respawn_delay Delay in seconds until repawn happens after landing.
+-- @field #table markerids Array with marker IDs.
-- @extends Functional.Spawn#SPAWN
---- # RAT class, extends @{Spawn#SPAWN}
+---# RAT class, extends @{Spawn#SPAWN}
+-- The RAT class implements an easy to use way to randomly fill your map with AI aircraft.
--
--- The RAT class allows to easily create random air traffic in your missions.
--
-
-
---- RAT class
--- @field #RAT RAT
+-- ## Airport Selection
+--
+-- 
+--
+-- ### Default settings:
+--
+-- * By default, aircraft are spawned at airports of their own coalition (blue or red) or neutral airports.
+-- * Destination airports are by default also of neutral or of the same coalition as the template group of the spawned aircraft.
+-- * Possible destinations are restricted by their distance to the departure airport. The maximal distance depends on the max range of spawned aircraft type and its initial fuel amount.
+--
+-- ### The default behavior can be changed:
+--
+-- * A specific departure and/or destination airport can be chosen.
+-- * Valid coalitions can be set, e.g. only red, blue or neutral, all three „colours“.
+-- * It is possible to start in air within a zone defined in the mission editor or within a zone above an airport of the map.
+--
+-- ## Flight Plan
+--
+-- 
+--
+-- * A general flight plan has five main airborn segments: Climb, cruise, descent, holding and final approach.
+-- * Events monitored during the flight are: birth, engine-start, take-off, landing and engine-shutdown.
+-- * The default flight level (FL) is set to ~FL200, i.e. 20000 feet ASL but randomized for each aircraft.
+-- Service ceiling of aircraft type is into account for max FL as well as the distance between departure and destination.
+-- * Maximal distance between destination and departure airports depends on range and initial fuel of aircraft.
+-- * Climb rate is set to a moderate value of ~1500 ft/min.
+-- * The standard descent rate follows the 3:1 rule, i.e. 1000 ft decent per 3 miles of travel. Hence, angle of descent is ~3.6 degrees.
+-- * A holding point is randomly selected at a distance between 5 and 10 km away from destination airport.
+-- * The altitude of theholding point is ~1200 m AGL. Holding patterns might or might not happen with variable duration.
+-- * If an aircraft is spawned in air, the procedure omitts taxi and take-off and starts with the climb/cruising part.
+-- * All values are randomized for each spawned aircraft.
+--
+-- ## Mission Editor Setup
+--
+-- 
+--
+-- Basic mission setup is very simple and essentially a three step process:
+--
+-- * Place your aircraft **anywhere** on the map. It really does not matter where you put it.
+-- * Give the group a good name. In the example above the group is named "RAT_YAK".
+-- * Activate the "LATE ACTIVATION" tick box. Note that this aircraft will not be spawned itself but serves a template for each RAT aircraft spawned when the mission starts.
+--
+-- Voilà, your already done!
+--
+-- Optionally, you can set a specific livery for the aircraft or give it some weapons.
+-- However, the aircraft will by default not engage any enemies. Think of them as beeing on a peaceful or ferry mission.
+--
+-- ## Basic Lua Script
+--
+-- 
+--
+-- The basic Lua script for one template group consits of two simple lines as shown in the picture above.
+--
+-- * **Line 2** creates a new RAT object "yak". The only required parameter for the constructor @{#RAT.New}() is the name of the group as defined in the mission editor. In this example it is "RAT_YAK".
+-- * **Line 5** trigger the command to spawn the aircraft. The (optional) parameter for the @{#RAT.Spawn}() function is the number of aircraft to be spawned of this object.
+-- By default each of these aircraft gets a random departure airport anywhere on the map and a random destination airport, which lies within range of the of the selected aircraft type.
+--
+-- In this simple example aircraft are respawned with a completely new flightplan when they have reached their destination airport.
+-- The "old" aircraft is despawned (destroyed) after it has shut-down its engines and a new aircraft of the same type is spawned at a random departure airport anywhere on the map.
+-- Hence, the default flight plan for a RAT aircraft will be: Fly from airport A to B, get respawned at C and fly to D, get respawned at E and fly to F, ...
+-- This ensures that you always have a constant number of AI aircraft on your map.
+--
+-- ## Examples
+--
+-- Here are a few examples, how you can modify the default settings of RAT class objects.
+--
+-- ### Specify Departure and Destinations
+--
+-- 
+--
+-- In the picture above you find a few possibilities how to modify the default behaviour to spawn at random airports and fly to random destinations.
+--
+-- In particular, you can specify fixed departure and/or destination airports. This is done via the @{#RAT.SetDeparture}() or @{#RAT.SetDestination}() functions, respectively.
+--
+-- * If you only fix a specific departure airport via @{#RAT.SetDeparture}() all aircraft will be spawned at that airport and get random destination airports.
+-- * If you only fix the destination airport via @{#RAT.SetDestination}(), aircraft a spawned at random departure airports but will all fly to the destination airport.
+-- * If you fix departure and destination airports, aircraft will only travel from between those airports.
+-- When the aircraft reaches its destination, it will be respawned at its departure and fly again to its destination.
+--
+-- There is also an option that allows aircraft to "continue their journey" from their destination. This is achieved by the @{#RAT.ContinueJourney}() function.
+-- In that case, when the aircraft arrives at its first destination it will be respawned at that very airport and get a new random destination.
+-- So the flight plan in this case would be: Fly from airport A to B, then from B to C, then from C to D, ...
+--
+-- It is also possible to make aircraft "commute" between two airports, i.e. flying from airport A to B and then back from B to A, etc.
+-- This can be done by the @{#RAT.Commute}() function. Note that if no departure or destination airports are specified, the first departure and destination are chosen randomly.
+-- Then the aircraft will fly back and forth between those two airports indefinetly.
+--
+--
+-- ### Spawn in Air
+--
+-- 
+--
+-- Aircraft can also be spawned in air rather than at airports on the ground. This is done by setting @{#RAT.SetTakeoff}() to "air".
+--
+-- By default, aircraft are spawned randomly above airports of the map.
+--
+-- The @{#RAT.SetDeparture}() option can be used to specify zones, which have been defined in the mission editor as departure zones.
+-- Aircraft will then be spawned at a random point within the zone or zones.
+--
+-- Note that @{#RAT.SetDeparture}() also accepts airport names. For an air takeoff these are treated like zones with a radius of XX kilometers.
+-- Again, aircraft are spawned at random points within these zones around the airport.
+--
+-- ### Misc Options
+--
+-- 
+--
+-- The default "takeoff" type of RAT aircraft is that they are spawned with hot or cold engines.
+-- The choice is random, so 50% of aircraft will be spawned with hot engines while the other 50% will be spawned with cold engines.
+-- This setting can be changed using the @{#RAT.SetTakeoff}() function. The possible parameters for starting on ground are:
+--
+-- * @{#RAT.SetTakeoff}("cold"), which means that all aircraft are spawned with their engines off,
+-- * @{#RAT.SetTakeoff}("hot"), which means that all aircraft are spawned with their engines on,
+-- * @{#RAT.SetTakeoff}("runway"), which means that all aircraft are spawned already at the runway ready to takeoff.
+-- Note that in this case the default spawn intervall is set to 180 seconds in order to avoid aircraft jamms on the runway. Generally, this takeoff at runways should be used with care and problems are to be expected.
+--
+--
+-- The options @{#RAT.SetMinDistance}() and @{#RAT.SetMaxDistance}() can be used to restrict the range from departure to destination. For example
+--
+-- * @{#RAT.SetMinDistance}(100) will cause only random destination airports to be selected which are **at least** 100 km away from the departure airport.
+-- * @{#RAT.SetMaxDistance}(150) will allow only destination airports which are **less than** 150 km away from the departure airport.
+--
+--
+-- Certain other options like the flight level can also be specified. However, note that this might not be a good idea for random departures and/or destinations.
+-- For example the random route might be too short to reach that altitude, which would result in very high climb and descent rates or strange flight plans.
+--
+--
+-- @field #RAT
RAT={
ClassName = "RAT", -- Name of class: RAT = Random Air Traffic.
debug=false, -- Turn debug messages on or off.
@@ -102,7 +245,7 @@ RAT={
friendly = "same", -- Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
ctable = {}, -- Table with the valid coalitons from choice self.friendly.
aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...).
- Vcruisemax=250, -- Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt).
+ Vcruisemax=nil, -- Max cruise speed in set by user.
Vclimb=1500, -- Default climb rate in ft/min.
AlphaDescent=3.6, -- Default angle of descenti in degrees. A value of 3.6 follows the 3:1 rule of 3 miles of travel and 1000 ft descent.
roe = "hold", -- ROE of spawned groups, default is weapon hold (this is a peaceful class for civil aircraft or ferry missions). Possible: "hold", "return", "free".
@@ -118,15 +261,17 @@ RAT={
departure_ports={}, -- Array containing the names of the departure airports.
destination_ports={}, -- Array containing the names of the destination airports.
ratcraft={}, -- Array with the spawned RAT aircraft.
+ Tinactive=180, -- Time in seconds after which inactive units will be destroyed. Default is 180 seconds.
reportstatus=false, -- Aircraft report status.
statusinterval=30, -- Intervall between status checks (and reports if enabled).
placemarkers=false, -- Place markers of waypoints on F10 map.
FLminuser=nil, -- Minimum flight level set by user.
- FLmaxuser=nil, -- Minimum flight level set by user.
+ FLmaxuser=nil, -- Maximum flight level set by user.
FLuser=nil, -- Flight level set by users explicitly.
commute=false, -- Aircraft commute between departure and destination, i.e. when respawned the departure airport becomes the new destiation.
continuejourney=false, -- Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination.
alive=0, -- Number of groups which are alive.
+ ngroups=nil, -- Number of groups to be spawned in total.
f10menu=true, -- Add an F10 menu for RAT.
Menu={}, -- F10 menu items for this RAT object.
SubMenuName=nil, -- Submenu name for RAT object.
@@ -138,14 +283,16 @@ RAT={
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Categories of the RAT class.
--- @field #RAT cat
+-- @list cat
+-- @field #string plane Plane.
+-- @field #string heli Heli.
RAT.cat={
- plane="plane",
- heli="heli",
+ plane="plane", --plane
+ heli="heli", --hallo
}
--- RAT waypoint type.
--- @field #RAT wp
+-- @list wp
RAT.wp={
coldorhot=0,
air=1,
@@ -160,7 +307,7 @@ RAT.wp={
}
--- RAT friendly coalitions.
--- @field #RAT coal
+-- @list coal
RAT.coal={
same="same",
sameonly="sameonly",
@@ -173,7 +320,7 @@ RAT.coal={
}
--- RAT unit conversions.
--- @field #RAT unit
+-- @list unit
RAT.unit={
ft2meter=0.305,
kmh2ms=0.278,
@@ -183,7 +330,7 @@ RAT.unit={
}
--- RAT rules of engagement.
--- @field #RAT ROE
+-- @list ROE
RAT.ROE={
weaponhold="hold",
weaponfree="free",
@@ -191,7 +338,7 @@ RAT.ROE={
}
--- RAT reaction to threat.
--- @field #RAT ROT
+-- @list ROT
RAT.ROT={
evade="evade",
passive="passive",
@@ -199,16 +346,16 @@ RAT.ROT={
}
--- Running number of placed markers on the F10 map.
--- @field #RAT markerid
+-- @field #number markerid
RAT.markerid=0
--- Main F10 menu.
--- @field #RAT MenuF10
+-- @field #string MenuF10
RAT.MenuF10=nil
---- Some ID to identify where we are
--- #string myid
-myid="RAT | "
+--- Some ID to identify who we are in output of the DCS.log file.
+-- @field #string id
+RAT.id="RAT | "
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -237,7 +384,7 @@ myid="RAT | "
--TODO: Check out uncontrolled spawning.
--TODO: Check aircraft spawning in air at Sochi after third aircraft was spawned.
--TODO: Improve despawn after stationary. Might lead to despawning if many aircraft spawn at the same time.
---TODO: Check why birth event is not handled.
+--TODO: Check why birth event is not handled. ==> Seems to be okay if it is called _OnBirth rather than _OnBirthday. Dont know why actually!?
--TODO: Improve behaviour when no destination or departure airports were found. Leads to crash, e.g. 1184: attempt to get length of local 'destinations' (a nil value)
--TODO: Check cases where aircraft get shot down. Respawn?
--TODO: Handle the case where more than 10 RAT objects are spawned. Likewise, more than 10 groups of one object. Causes problems with the number of menu items!
@@ -253,7 +400,7 @@ myid="RAT | "
function RAT:New(groupname)
-- Welcome message.
- env.info(myid.."Creating new RAT object from template: "..groupname)
+ env.info(RAT.id.."Creating new RAT object from template: "..groupname)
-- Inherit SPAWN clase.
local self=BASE:Inherit(self, SPAWN:New(groupname)) -- #RAT
@@ -293,54 +440,64 @@ end
function RAT:Spawn(naircraft)
-- Number of aircraft to spawn. Default is one.
- naircraft=naircraft or 1
+ self.ngroups=naircraft or 1
-- Set the coalition table based on choice of self.coalition and self.friendly.
self:_SetCoalitionTable()
-- Get all airports of this map beloning to friendly coalition(s).
self:_GetAirportsOfCoalition()
+
+ -- Set submenuname if it has not been set by user.
+ if not self.SubMenuName then
+ self.SubMenuName=self.SpawnTemplatePrefix
+ end
+ -- Check if submenu already exsits.
+ if self.f10menu then
+ if self.Menu[self.SubMenuName] then
+ self.SubMenuName=self.SubMenuName.." 01"
+ end
+ end
-- debug message
local text=string.format("\n******************************************************\n")
- text=text..string.format("Spawning %i aircraft from template %s of type %s.\n", naircraft, self.SpawnTemplatePrefix, self.aircraft.type)
- text=text..string.format("Takeoff type: %i\n", self.takeoff)
+ text=text..string.format("Spawning %i aircraft from template %s of type %s.\n", self.ngroups, self.SpawnTemplatePrefix, self.aircraft.type)
+ text=text..string.format("Category: %s\n", self.category)
text=text..string.format("Friendly coalitions: %s\n", self.friendly)
text=text..string.format("Number of airports on map : %i\n", #self.airports_map)
text=text..string.format("Number of friendly airports: %i\n", #self.airports)
+ text=text..string.format("Min dist to destination: %4.1f\n", self.mindist)
+ text=text..string.format("Max dist to destination: %4.1f\n", self.maxdist)
+ text=text..string.format("Takeoff type: %i\n", self.takeoff)
text=text..string.format("Commute: %s\n", tostring(self.commute))
text=text..string.format("Journey: %s\n", tostring(self.continuejourney))
text=text..string.format("Spawn delay: %4.1f\n", self.spawndelay)
text=text..string.format("Spawn interval: %4.1f\n", self.spawninterval)
- text=text..string.format("Category: %s\n", self.category)
- text=text..string.format("Vcruisemax: %4.1f\n", self.Vcruisemax)
- text=text..string.format("Vclimb: %4.1f\n", self.Vclimb)
- text=text..string.format("Vcruisemax: %4.1f\n", self.Vcruisemax)
- text=text..string.format("AlphaDescent: %4.2f\n", self.AlphaDescent)
+ text=text..string.format("Respawn after landing: %s\n", tostring(self.respawn_at_landing))
+ text=text..string.format("Respawn delay: %s\n", tostring(self.respawn_delay))
text=text..string.format("ROE: %s\n", tostring(self.roe))
text=text..string.format("ROT: %s\n", tostring(self.rot))
- text=text..string.format("Min dist: %4.1f\n", self.mindist)
- text=text..string.format("Max dist: %4.1f\n", self.maxdist)
- text=text..string.format("Report status: %s\n", tostring(self.reportstatus))
- text=text..string.format("Status interval: %4.1f\n", self.statusinterval)
- text=text..string.format("Place markers: %s\n", tostring(self.placemarkers))
+ text=text..string.format("Vclimb: %4.1f\n", self.Vclimb)
+ text=text..string.format("AlphaDescent: %4.2f\n", self.AlphaDescent)
+ text=text..string.format("Vcruisemax: %s\n", tostring(self.Vcruisemax))
text=text..string.format("FLuser: %s\n", tostring(self.Fluser))
text=text..string.format("FLminuser: %s\n", tostring(self.Flminuser))
text=text..string.format("FLmaxuser: %s\n", tostring(self.Flmaxuser))
- text=text..string.format("Respawn after landing: %s\n", tostring(self.respawn_after_landing))
- text=text..string.format("Respawn delay: %s\n", tostring(self.respawn_delay))
- -- @field #number FLminuser
+ text=text..string.format("Place markers: %s\n", tostring(self.placemarkers))
+ text=text..string.format("Report status: %s\n", tostring(self.reportstatus))
+ text=text..string.format("Status interval: %4.1f\n", self.statusinterval)
+ text=text..string.format("Time inactive: %4.1f\n", self.Tinactive)
+ text=text..string.format("Create F10 menu : %s\n", tostring(self.f10menu))
+ text=text..string.format("F10 submenu name: %s\n", self.SubMenuName)
text=text..string.format("******************************************************\n")
- env.info(myid..text)
-
+ env.info(RAT.id..text)
-- Create submenus.
if self.f10menu then
- self.SubMenuName=self.SpawnTemplatePrefix
self.Menu[self.SubMenuName]=MENU_MISSION:New(self.SubMenuName, RAT.MenuF10)
self.Menu[self.SubMenuName]["groups"]=MENU_MISSION:New("Groups", self.Menu[self.SubMenuName])
MENU_MISSION_COMMAND:New("Spawn new group", self.Menu[self.SubMenuName], self._SpawnWithRoute, self)
- MENU_MISSION_COMMAND:New("Delete markers", self.Menu[self.SubMenuName], self._DeleteMarkers, self, self.markerids)
+ MENU_MISSION_COMMAND:New("Delete markers", self.Menu[self.SubMenuName], self._DeleteMarkers, self)
MENU_MISSION_COMMAND:New("Status report", self.Menu[self.SubMenuName], self.Status, self, true)
end
@@ -351,8 +508,8 @@ function RAT:Spawn(naircraft)
if self.takeoff==RAT.wp.runway then
dt=math.max(dt, 180)
end
- local Tstop=Tstart+dt*(naircraft-1)
- SCHEDULER:New(nil, self._SpawnWithRoute, {self}, Tstart, dt, 0.1, Tstop)
+ local Tstop=Tstart+dt*(self.ngroups-1)
+ SCHEDULER:New(nil, self._SpawnWithRoute, {self}, Tstart, dt, 0.0, Tstop)
-- Status check and report scheduler.
SCHEDULER:New(nil, self.Status, {self}, Tstart+1, self.statusinterval)
@@ -529,14 +686,22 @@ end
-- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 180 seconds.
function RAT:RespawnAfterLanding(delay)
delay = delay or 180
- self.respawn_after_landing=true
+ self.respawn_at_landing=true
self.respawn_delay=delay
end
+--- Set the time after which inactive groups will be destroyed. Default is 180 seconds.
+-- @param #RAT self
+-- @param #number time Time in seconds.
+function RAT:TimeDestroyInactive(time)
+ self.Tinactive=time
+end
+
--- Set the maximum cruise speed of the aircraft.
-- @param #RAT self
-- @param #number speed Speed in km/h.
function RAT:SetMaxCruiseSpeed(speed)
+ -- Convert to m/s.
self.Vcruisemax=speed/3.6
end
@@ -569,7 +734,7 @@ end
--- Set reaction to threat (ROT). Default is no reaction, i.e. aircraft will simply ignore all enemies.
-- @param #RAT self
--- @param #string rot "noreaction = no reactino, "passive" = passive defence, "evade" = weapons free.
+-- @param #string rot "noreaction" = no reaction to threats, "passive" = passive defence, "evade" = evade enemy attacks.
function RAT:SetROT(rot)
if rot=="passive" then
self.rot=RAT.ROT.passive
@@ -581,7 +746,7 @@ function RAT:SetROT(rot)
end
--- Set minimum distance between departure and destination. Default is 5 km.
--- Minimum distance should not be smaller than ~500(?) meters to ensure that departure and destination are different.
+-- Minimum distance should not be smaller than maybe ~500 meters to ensure that departure and destination are different.
-- @param #RAT self
-- @param #number dist Distance in km.
function RAT:SetMinDistance(dist)
@@ -662,7 +827,7 @@ function RAT:_InitAircraft(DCSgroup)
local DCScategory=DCSgroup:getCategory()
local DCStype=DCSunit:getTypeName()
- -- Ddescriptors table of unit.
+ -- Descriptors table of unit.
if self.debug then
self:E({"DCSdesc", DCSdesc})
end
@@ -674,7 +839,7 @@ function RAT:_InitAircraft(DCSgroup)
self.category=RAT.cat.heli
else
self.category="other"
- env.error(myid.."Group of RAT is neither airplane nor helicopter!")
+ env.error(RAT.id.."Group of RAT is neither airplane nor helicopter!")
end
-- Get type of aircraft.
@@ -721,7 +886,7 @@ function RAT:_InitAircraft(DCSgroup)
text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n", self.aircraft.ceiling/1000, self.aircraft.ceiling/RAT.unit.FL2m)
text=text..string.format("FL cruise = %6.1f km = FL%3.0f\n", self.aircraft.FLcruise/1000, self.aircraft.FLcruise/RAT.unit.FL2m)
text=text..string.format("******************************************************\n")
- env.info(myid..text)
+ env.info(RAT.id..text)
end
@@ -737,18 +902,27 @@ end
-- @param #string _destination (Optional) Name of destination airbase.
function RAT:_SpawnWithRoute(_departure, _destination)
+ -- Check that we don't already have more groups than we initally wanted.
+ if self.alive > self.ngroups then
+ return
+ end
+
-- Set takeoff type.
local _takeoff=self.takeoff
if self.takeoff==RAT.wp.coldorhot then
local temp={RAT.wp.cold, RAT.wp.hot}
_takeoff=temp[math.random(2)]
- env.info(myid.."Random takeoff type: ".._takeoff)
end
- env.info(myid.."Takeoff type: ".._takeoff)
+ env.info(RAT.id.."Takeoff type: ".._takeoff)
-- Set flight plan.
local departure, destination, waypoints = self:_SetRoute(_takeoff, _departure, _destination)
+ -- Return nil if we could not find a departure destination or waypoints
+ if not (departure and destination and waypoints) then
+ return nil
+ end
+
-- Modify the spawn template to follow the flight plan.
self:_ModifySpawnTemplate(waypoints)
@@ -774,14 +948,17 @@ function RAT:_SpawnWithRoute(_departure, _destination)
if group:InAir() then
self.ratcraft[self.SpawnIndex]["Tground"]=nil
self.ratcraft[self.SpawnIndex]["Pground"]=nil
+ self.ratcraft[self.SpawnIndex]["Tlastcheck"]=nil
else
self.ratcraft[self.SpawnIndex]["Tground"]=timer.getTime()
self.ratcraft[self.SpawnIndex]["Pground"]=group:GetCoordinate()
+ self.ratcraft[self.SpawnIndex]["Tlastcheck"]=timer.getTime()
end
-- Initial and current position. For calculating the travelled distance.
self.ratcraft[self.SpawnIndex]["P0"]=group:GetCoordinate()
self.ratcraft[self.SpawnIndex]["Pnow"]=group:GetCoordinate()
self.ratcraft[self.SpawnIndex]["Distance"]=0
+
-- Each aircraft gets its own takeoff type and randomized route.
self.ratcraft[self.SpawnIndex]["takeoff"]=_takeoff
self.ratcraft[self.SpawnIndex]["random_departure"]=self.random_departure
@@ -805,10 +982,11 @@ function RAT:_SpawnWithRoute(_departure, _destination)
-- F10/RAT//Group X/
MENU_MISSION_COMMAND:New("Despawn group", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._Despawn, self, group)
MENU_MISSION_COMMAND:New("Place markers", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._PlaceMarkers, self, waypoints)
- --MENU_MISSION_COMMAND:New("Delete markers", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._DelateMarkers, self, markers)
MENU_MISSION_COMMAND:New("Status report", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.Status, self, true, self.SpawnIndex)
end
+ return self.SpawnIndex
+
end
--- Respawn a group.
@@ -853,16 +1031,22 @@ end
-- @param Wrapper.Airport#AIRBASE _destination (Optional) Destination airbase.
-- @return Wrapper.Airport#AIRBASE Departure airbase.
-- @return Wrapper.Airport#AIRBASE Destination airbase.
--- @return #table Table of flight plan waypoints.
+-- @return #table Table of flight plan waypoints.
+-- @return #nil If no valid departure or destination airport could be found.
function RAT:_SetRoute(takeoff, _departure, _destination)
-
- env.info(myid.."takeoff in _setroute: "..takeoff)
+
+ -- Max cruise speed.
+ local VxCruiseMax
+ if self.Vcruisemax then
+ -- User input.
+ VxCruiseMax = min(self.Vcruisemax, self.aircraft.Vmax)
+ else
+ -- Max cruise speed 90% of Vmax or 900 km/h whichever is lower.
+ VxCruiseMax = math.min(self.aircraft.Vmax*0.90, 250)
+ end
- -- Min cruise speed 70% of Vmax or 600 km/h whichever is lower.
- local VxCruiseMin = math.min(self.aircraft.Vmax*0.70, 166)
-
- -- Max cruise speed 90% of Vmax or 900 km/h whichever is lower.
- local VxCruiseMax = math.min(self.aircraft.Vmax*0.90, 250)
+ -- Min cruise speed 70% of max cruise or 600 km/h whichever is lower.
+ local VxCruiseMin = math.min(VxCruiseMax*0.70, 166)
-- Cruise speed (randomized).
local VxCruise = self:_Randomize((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin, 0.3 , VxCruiseMin, VxCruiseMax)
@@ -891,12 +1075,26 @@ function RAT:_SetRoute(takeoff, _departure, _destination)
-- DEPARTURE AIRPORT
-- Departure airport or zone.
- local departure
+ local departure=nil
if _departure then
- departure=AIRBASE:FindByName(_departure)
+ if self:_AirportExists(_departure) then
+ departure=AIRBASE:FindByName(_departure)
+ else
+ local text=string.format("ERROR: Specified departure airport %s does not exist!", _departure)
+ MESSAGE:New(text, 60):ToAll()
+ env.info(RAT.id..text)
+ end
else
departure=self:_PickDeparture(takeoff)
end
+
+ -- Return nil if no departure could be found.
+ if not departure then
+ local text=string.format("ERROR: No valid departure airport could be found.")
+ MESSAGE:New(text, 60):ToAll()
+ env.info(RAT.id..text)
+ return nil
+ end
-- Coordinates of departure point.
local Pdeparture
@@ -925,9 +1123,15 @@ function RAT:_SetRoute(takeoff, _departure, _destination)
end
-- DESTINATION AIRPORT
- local destination
+ local destination=nil
if _destination then
- destination=AIRBASE:FindByName(_destination)
+ if self:_AirportExists(_destination) then
+ destination=AIRBASE:FindByName(_destination)
+ else
+ local text=string.format("ERROR: Specified destination airport %s does not exist!", _destination)
+ MESSAGE:New(text, 60):ToAll()
+ env.info(RAT.id..text)
+ end
else
-- Get all destination airports within reach.
local destinations=self:_GetDestinations(Pdeparture, self.mindist, math.min(self.aircraft.Reff, self.maxdist))
@@ -940,12 +1144,20 @@ function RAT:_SetRoute(takeoff, _departure, _destination)
-- Pick a destination airport.
destination=self:_PickDestination(destinations, random_destination)
end
+
+ -- Return nil if no departure could be found.
+ if not destination then
+ local text=string.format("ERROR: No valid destination airport could be found!")
+ MESSAGE:New(text, 60):ToAll()
+ env.info(RAT.id..text)
+ return nil
+ end
-- Check that departure and destination are not the same. Should not happen due to mindist.
if destination:GetName()==departure:GetName() then
local text=string.format("%s: Destination and departure airport are identical. Airport %s (ID %d).", self.SpawnTemplatePrefix, destination:GetName(), destination:GetID())
MESSAGE:New(text, 120):ToAll()
- env.error(myid..text)
+ env.error(RAT.id..text)
end
-- Coordinates of destination airport.
@@ -1087,7 +1299,7 @@ function RAT:_SetRoute(takeoff, _departure, _destination)
text=text..string.format("Phi (slope) = %6.1f Deg\n", math.deg(phi))
text=text..string.format("Heading = %6.1f Deg\n", heading)
text=text..string.format("******************************************************\n")
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Ensure that cruise distance is positve. Can be slightly negative in special cases. And we don't want to turn back.
if d_cruise<0 then
@@ -1104,7 +1316,7 @@ function RAT:_SetRoute(takeoff, _departure, _destination)
local c6=Pdestination
--Convert coordinates into route waypoints.
- local wp0=self:_Waypoint(takeoff, c0, VxClimb, H_departure, departure)
+ local wp0=self:_Waypoint(takeoff, c0, VxClimb, H_departure, departure)
local wp1=self:_Waypoint(RAT.wp.climb, c1, VxClimb, H_departure+(FLcruise-H_departure)/2)
local wp2=self:_Waypoint(RAT.wp.cruise, c2, VxCruise, FLcruise)
local wp3=self:_Waypoint(RAT.wp.cruise, c3, VxCruise, FLcruise)
@@ -1186,24 +1398,28 @@ function RAT:_PickDeparture(takeoff)
local departure=departures[math.random(#departures)]
local text
- if takeoff==RAT.wp.air then
- text="Chosen departure zone: "..departure:GetName()
+ if departure and departure:GetName() then
+ if takeoff==RAT.wp.air then
+ text="Chosen departure zone: "..departure:GetName()
+ else
+ text="Chosen departure airport: "..departure:GetName().." (ID "..departure:GetID()..")"
+ end
+ env.info(RAT.id..text)
+ if self.debug then
+ MESSAGE:New(text, 30):ToAll()
+ end
else
- text="Chosen departure airport: "..departure:GetName().." (ID "..departure:GetID()..")"
- end
- env.info(myid..text)
- if self.debug then
- MESSAGE:New(text, 30):ToAll()
+ departure=nil
end
return departure
end
---- Set the destination airport of the AI. If no airport name is given an airport from the coalition is chosen randomly.
+--- Pick destination airport. If no airport name is given an airport from the coalition is chosen randomly.
-- @param #RAT self
-- @param #table destinations Table with destination airports.
--- @param #boolean _random Optional switch to activate a random selection of airports.
+-- @param #boolean _random (Optional) Switch to activate a random selection of airports.
-- @return Wrapper.Airbase#AIRBASE Destination airport.
function RAT:_PickDestination(destinations, _random)
@@ -1219,16 +1435,17 @@ function RAT:_PickDestination(destinations, _random)
end
-- Randomly select one possible destination.
- local destination=destinations[math.random(#destinations)] -- Wrapper.Airbase#AIRBASE
+ local destination=nil
+ if destinations and #destinations>0 then
+ destination=destinations[math.random(#destinations)] -- Wrapper.Airbase#AIRBASE
- if destination then
local text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")"
- env.info(myid..text)
+ env.info(RAT.id..text)
if self.debug then
MESSAGE:New(text, 30):ToAll()
end
else
- env.error(myid.."No destination airport found.")
+ env.error(RAT.id.."No destination airport found.")
end
return destination
@@ -1261,7 +1478,7 @@ function RAT:_GetDestinations(q, minrange, maxrange)
table.insert(destinations, airport)
end
end
- env.info(myid.."Number of possible destination airports = "..#destinations)
+ env.info(RAT.id.."Number of possible destination airports = "..#destinations)
if #destinations > 1 then
--- Compare distance of destination airports.
@@ -1275,7 +1492,7 @@ function RAT:_GetDestinations(q, minrange, maxrange)
end
table.sort(destinations, compare)
else
- env.error(myid.."No possible destination airports found!")
+ env.error(RAT.id.."No possible destination airports found!")
destinations=nil
end
@@ -1315,12 +1532,12 @@ function RAT:_GetAirportsOfMap()
-- Add airport to table.
table.insert(self.airports_map, _myab)
- if self.debug then
+ --if self.debug then
local text1="Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName()
local text2="Airport ID = "..airbase:getID().." and Name = "..airbase:getName()..", Category = "..airbase:getCategory()..", TypeName = "..airbase:getTypeName()
- env.info(myid..text1)
- env.info(myid..text2)
- end
+ env.info(RAT.id..text1)
+ env.info(RAT.id..text2)
+ --end
end
@@ -1348,7 +1565,7 @@ function RAT:_GetAirportsOfCoalition()
if #self.airports==0 then
local text="No possible departure/destination airports found!"
MESSAGE:New(text, 60):ToAll()
- env.error(myid..text)
+ env.error(RAT.id..text)
end
end
@@ -1356,7 +1573,7 @@ end
--- Report status of RAT groups.
-- @param #RAT self
--- @param #boolean message (Optional) Send message if true.
+-- @param #boolean message (Optional) Send message with report to all if true.
-- @param #number forID (Optional) Send message only for this ID.
function RAT:Status(message, forID)
@@ -1368,7 +1585,7 @@ function RAT:Status(message, forID)
if (message and not forID) or self.reportstatus then
local text=string.format("Alive groups of template %s: %d", self.SpawnTemplatePrefix, self.alive)
- env.info(myid..text)
+ env.info(RAT.id..text)
MESSAGE:New(text, 20):ToAll()
end
@@ -1389,24 +1606,54 @@ function RAT:Status(message, forID)
local departure=self.ratcraft[i].departure:GetName()
local destination=self.ratcraft[i].destination:GetName()
local type=self.aircraft.type
+ local Tnow=timer.getTime()
-- Monitor time and distance on ground.
local Tg=0
local Dg=0
+ local dTlast=0
+ local stationary=false --lets assume, we did move
if airborn then
-- Aircraft is airborn.
self.ratcraft[i]["Tground"]=nil
self.ratcraft[i]["Pground"]=nil
+ self.ratcraft[i]["Tlastcheck"]=nil
else
--Aircraft is on ground.
if self.ratcraft[i]["Tground"] then
- -- Aircraft was already on ground at last check. Calculate the time on ground.
- Tg=timer.getTime()-self.ratcraft[i]["Tground"]
- -- Distance on ground since first noticed aircraft is on ground.
+ -- Aircraft was already on ground. Calculate total time on ground.
+ Tg=Tnow-self.ratcraft[i]["Tground"]
+
+ -- Distance on ground since last check.
Dg=coords:Get2DDistance(self.ratcraft[i]["Pground"])
+
+ dTlast=Tnow-self.ratcraft[i]["Tlastcheck"]
+
+ env.info(RAT.id.."Last check "..dTlast.." seconds ago.")
+ env.info(RAT.id.."Dg = "..Dg)
+ env.info(RAT.id.."Tg = "..Tg)
+ env.info(RAT.id.."Tlast = "..self.ratcraft[i]["Tlastcheck"])
+
+ -- If more than Tinactive seconds passed since last check ==> check how much we moved meanwhile.
+ if dTlast > self.Tinactive then
+
+ -- If aircraft did not move more than 50 m since last check, we call it stationary and despawn it.
+ --local starting_engines=self.ratcraft[i].status==""
+ if Dg<50 then
+ stationary=true
+ end
+
+ env.info(RAT.id.."checking stationarz = "..tostring(stationary))
+
+ -- Set the current time to know when the next check is necessary.
+ self.ratcraft[i]["Tlastcheck"]=Tnow
+ self.ratcraft[i]["Pground"]=coords
+ end
+
else
- -- First time we see that aircraft is on ground. Initialize the time and position.
- self.ratcraft[i]["Tground"]=timer.getTime()
+ -- First time we see that the aircraft is on ground. Initialize the times and position.
+ self.ratcraft[i]["Tground"]=Tnow
+ self.ratcraft[i]["Tlastcheck"]=Tnow
self.ratcraft[i]["Pground"]=coords
end
end
@@ -1432,19 +1679,19 @@ function RAT:Status(message, forID)
else
text=text.." [on ground]\n"
end
- text=text..string.format("Fuel = %3.0f\n", fuel)
- text=text..string.format("Life = %3.0f\n", life)
+ text=text..string.format("Fuel = %3.0f %%\n", fuel)
+ text=text..string.format("Life = %3.0f %%\n", life)
text=text..string.format("FL%03d = %i m\n", alt/RAT.unit.FL2m, alt)
--text=text..string.format("Speed = %i km/h\n", vel)
- text=text..string.format("Distance travelled = %6.1f km\n", self.ratcraft[i]["Distance"]/1000)
+ text=text..string.format("Distance travelled = %6.1f km\n", self.ratcraft[i]["Distance"]/1000)
text=text..string.format("Distance to destination = %6.1f km", Ddestination/1000)
if not airborn then
text=text..string.format("\nTime on ground = %6.0f seconds\n", Tg)
- text=text..string.format("Position change = %6.1f m since last check.", Dg)
- end
- if self.debug then
- env.info(myid..text)
+ text=text..string.format("Position change = %8.1f m since %3.0f seconds.", Dg, dTlast)
end
+ --if self.debug then
+ env.info(RAT.id..text)
+ --end
if self.reportstatus or message then
MESSAGE:New(text, 20):ToAll()
end
@@ -1452,13 +1699,13 @@ function RAT:Status(message, forID)
-- Despawn groups if they are on ground and don't move or are damaged.
if not airborn then
- -- Despawn unit if it did not move more then 50 m in the last 120 seconds.
- if Tg>120 and Dg<50 then
- local text=string.format("Group %s is despawned after being %4.0f seconds inaktive on ground.", self.SpawnTemplatePrefix, Tg)
- env.info(myid..text)
+ -- Despawn unit if it did not move more then 50 m in the last 180 seconds.
+ if stationary then
+ local text=string.format("Group %s is despawned after being %4.0f seconds inaktive on ground.", self.SpawnTemplatePrefix, dTlast)
+ env.info(RAT.id..text)
self:_Despawn(group)
end
-
+ -- Despawn group if life is < 10% and distance travelled < 100 m.
if life<10 and Dtravel<100 then
local text=string.format("Damaged group %s is despawned. Life = %3.0f", self.SpawnTemplatePrefix, life)
self:_Despawn(group)
@@ -1469,7 +1716,7 @@ function RAT:Status(message, forID)
else
if self.debug then
local text=string.format("Group %i does not exist.", i)
- env.info(myid..text)
+ env.info(RAT.id..text)
end
end
end
@@ -1487,12 +1734,12 @@ function RAT:_GetLife(group)
life=unit:GetLife()/unit:GetLife0()*100
else
if self.debug then
- env.error(myid.."Unit does not exist in RAT_Getlife(). Returning zero.")
+ env.error(RAT.id.."Unit does not exist in RAT_Getlife(). Returning zero.")
end
end
else
if self.debug then
- env.error(myid.."Group does not exist in RAT_Getlife(). Returning zero.")
+ env.error(RAT.id.."Group does not exist in RAT_Getlife(). Returning zero.")
end
end
return life
@@ -1502,7 +1749,7 @@ end
-- @param #RAT self
function RAT:_SetStatus(group, status)
local index=self:GetSpawnIndexFromGroup(group)
- env.info(myid.."Index for group "..group:GetName().." "..index.." status: "..status)
+ env.info(RAT.id.."Index for group "..group:GetName().." "..index.." status: "..status)
self.ratcraft[index].status=status
end
@@ -1512,7 +1759,7 @@ end
-- @param #RAT self
function RAT:_OnBirth(EventData)
- env.info(myid.."It's a birthday!")
+ env.info(RAT.id.."It's a birthday!")
local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP
@@ -1527,7 +1774,7 @@ function RAT:_OnBirth(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." was born."
- env.info(myid..text)
+ env.info(RAT.id..text)
self:_SetStatus(SpawnGroup, "Starting engines (after birth)")
end
@@ -1556,7 +1803,7 @@ function RAT:_EngineStartup(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." started engines."
- env.info(myid..text)
+ env.info(RAT.id..text)
local status
if SpawnGroup:InAir() then
@@ -1593,7 +1840,7 @@ function RAT:_OnTakeoff(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." is airborn."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Set status.
self:_SetStatus(SpawnGroup, "On journey (after takeoff)")
@@ -1625,15 +1872,15 @@ function RAT:_OnLand(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." landed."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Set status.
self:_SetStatus(SpawnGroup, "Taxiing (after landing)")
- if self.respawn_after_landing then
+ if self.respawn_at_landing then
text="Event: Group "..SpawnGroup:GetName().." will be respawned."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Respawn group.
self:_Respawn(SpawnGroup)
@@ -1666,14 +1913,14 @@ function RAT:_OnEngineShutdown(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." shut down its engines."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Set status.
self:_SetStatus(SpawnGroup, "Parking (shutting down engines)")
- if not self.respawn_after_landing then
+ if not self.respawn_at_landing then
text="Event: Group "..SpawnGroup:GetName().." will be respawned."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Respawn group.
self:_Respawn(SpawnGroup)
@@ -1681,7 +1928,7 @@ function RAT:_OnEngineShutdown(EventData)
-- Despawn group.
text="Event: Group "..SpawnGroup:GetName().." will be destroyed now."
- env.info(myid..text)
+ env.info(RAT.id..text)
self:_Despawn(SpawnGroup)
end
@@ -1711,7 +1958,7 @@ function RAT:_OnDead(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." died."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Set status.
self:_SetStatus(SpawnGroup, "Destroyed (after dead)")
@@ -1743,7 +1990,7 @@ function RAT:_OnCrash(EventData)
if EventPrefix == self.SpawnTemplatePrefix then
local text="Event: Group "..SpawnGroup:GetName().." crashed."
- env.info(myid..text)
+ env.info(RAT.id..text)
-- Set status.
self:_SetStatus(SpawnGroup, "Crashed")
@@ -1795,8 +2042,6 @@ end
-- @return #table Waypoints for DCS task route or spawn template.
function RAT:_Waypoint(Type, Coord, Speed, Altitude, Airport)
- env.info(myid.."takeoff in _waypoint: "..Type)
-
-- Altitude of input parameter or y-component of 3D-coordinate.
local _Altitude=Altitude or Coord.y
@@ -1888,7 +2133,7 @@ function RAT:_Waypoint(Type, Coord, Speed, Altitude, Airport)
end
text=text.."******************************************************\n"
if self.debug then
- env.info(myid..text)
+ env.info(RAT.id..text)
end
-- define waypoint
@@ -1976,7 +2221,7 @@ function RAT:_Routeinfo(waypoints, comment)
-- send message
if self.debug then
- env.info(myid..text)
+ env.info(RAT.id..text)
end
-- return total route length in meters
@@ -2113,7 +2358,7 @@ function RAT:_FLmax(alpha, beta, d, phi, h0)
text=text..string.format( "FLmax = FL%3.0f = %6.1f m.\n", h2/RAT.unit.FL2m, h2)
text=text..string.format( "FLmax = FL%3.0f = %6.1f m.", h3/RAT.unit.FL2m, h3)
if self.debug then
- env.info(myid..text)
+ env.info(RAT.id..text)
end
return h3+h0
end
@@ -2138,16 +2383,13 @@ end
-- @param Wrapper.Group#GROUP group Group for which the ROE is set.
-- @param #string roe ROE of group.
function RAT:_SetROE(group, roe)
- env.info(myid.."Setting ROE to "..roe.." for group "..group:GetName())
+ env.info(RAT.id.."Setting ROE to "..roe.." for group "..group:GetName())
if self.roe==RAT.ROE.returnfire then
group:OptionROEReturnFire()
- env.info(myid.."ROE return fire")
elseif self.roe==RAT.ROE.weaponfree then
group:OptionROEWeaponFree()
- env.info(myid.."ROE weapons free")
else
group:OptionROEHoldFire()
- env.info(myid.."ROE hold fire")
end
end
@@ -2157,7 +2399,7 @@ end
-- @param Wrapper.Group#GROUP group Group for which the ROT is set.
-- @param #string rot ROT of group.
function RAT:_SetROT(group, rot)
- env.info(myid.."Setting ROT to "..rot.." for group "..group:GetName())
+ env.info(RAT.id.."Setting ROT to "..rot.." for group "..group:GetName())
if self.rot==RAT.ROT.passive then
group:OptionROTPassiveDefense()
elseif self.rot==RAT.ROT.evade then
@@ -2252,7 +2494,7 @@ function RAT:_Randomize(value, fac, lower, upper)
-- debug info
if self.debug then
local text=string.format("Random: value = %6.2f, fac = %4.2f, min = %6.2f, max = %6.2f, r = %6.2f", value, fac, min, max, r)
- env.info(myid..text)
+ env.info(RAT.id..text)
end
return r
@@ -2278,9 +2520,11 @@ end
-- @param #table wp Position of marker coming in as waypoint, i.e. has x, y and alt components.
function RAT:_SetMarker(text, wp)
RAT.markerid=RAT.markerid+1
- table.insert(self.markerids,RAT.markerid)
+ self.markerids[#self.markerids+1]=RAT.markerid
+ --table.insert(self.markerids, RAT.markerid)
+ self:E({"setmaker ids: ", self.markerids})
--if self.debug then
- env.info(myid..self.SpawnTemplatePrefix..": placing marker with ID "..RAT.markerid..": "..text)
+ env.info(RAT.id..self.SpawnTemplatePrefix..": placing marker with ID "..RAT.markerid..": "..text)
--end
-- Convert to coordinate.
local vec={x=wp.x, y=wp.alt, z=wp.y}
@@ -2290,19 +2534,18 @@ end
--- Delete all markers on F10 map.
-- @param #RAT self
--- @param #table ids (Optional) Table holding the marker IDs to be deleted.
-function RAT:_DeleteMarkers(ids)
- if ids then
- for k,v in pairs(ids) do
- env.info("Deleting maker id v= "..v)
- trigger.action.removeMark(v)
- end
- else
- for i=1,RAT.markerid do
- env.info("Deleting maker id i= "..i)
- trigger.action.removeMark(i)
- end
+function RAT:_DeleteMarkers()
+ self:E({"self ids before: ", self.markerids})
+ for k,v in ipairs(self.markerids) do
+ env.info("Deleting marker id v= "..v)
+ trigger.action.removeMark(v)
end
+ for k,v in ipairs(self.markerids) do
+ env.info("Removing marker k= "..k)
+ --table.remove(self.markerids, k)
+ self.markerids[k]=nil
+ end
+ self:E({"self ids after: ", self.markerids})
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/docs/Documentation/AI_A2A_Dispatcher.html b/docs/Documentation/AI_A2A_Dispatcher.html
index 90b339863..d96ff9627 100644
--- a/docs/Documentation/AI_A2A_Dispatcher.html
+++ b/docs/Documentation/AI_A2A_Dispatcher.html
@@ -465,6 +465,12 @@ Per one, two, three, four?
AI_A2A_DISPATCHER:GetDefenderTaskFsm(Defender) |
+ |
+
+
+ | AI_A2A_DISPATCHER:GetDefenderTaskSquadronName(Defender) |
+
+
|
@@ -2545,6 +2551,27 @@ Takeoff From the airbase hot, from the airbase cold, in the air, from the runway
+ Parameter
+
+
+
+
+-
+
+
+AI_A2A_DISPATCHER:GetDefenderTaskSquadronName(Defender)
+
+
+-
+
+
+
Parameter
diff --git a/docs/Documentation/Movement.html b/docs/Documentation/Movement.html
index 3123488df..dfb86750e 100644
--- a/docs/Documentation/Movement.html
+++ b/docs/Documentation/Movement.html
@@ -228,7 +228,6 @@ on defined intervals (currently every minute).
-
- #number
MOVEMENT.AliveUnits
@@ -237,9 +236,6 @@ on defined intervals (currently every minute).
-
-
Contains the counter how many units are currently alive
-
diff --git a/docs/Documentation/Point.html b/docs/Documentation/Point.html
index 6cff25508..057eb3003 100644
--- a/docs/Documentation/Point.html
+++ b/docs/Documentation/Point.html
@@ -383,6 +383,18 @@
|
Provides a coordinate string of the point, based on a coordinate format system:
* Uses default settings in COORDINATE.
+ |
+
+
+ | COORDINATE:ToStringA2A(Controllable, Settings) |
+
+ Provides a coordinate string of the point, based on the A2A coordinate format system.
+ |
+
+
+ | COORDINATE:ToStringA2G(Controllable, Settings) |
+
+ Provides a coordinate string of the point, based on the A2G coordinate format system.
|
@@ -1938,6 +1950,68 @@ The coordinate Text in the configured coordinate system.
-
+
+COORDINATE:ToStringA2A(Controllable, Settings)
+
+
+-
+
+
Provides a coordinate string of the point, based on the A2A coordinate format system.
+
+ Parameters
+
+ Return value
+
+#string:
+The coordinate Text in the configured coordinate system.
+
+
+
+
+-
+
+
+COORDINATE:ToStringA2G(Controllable, Settings)
+
+
+-
+
+
Provides a coordinate string of the point, based on the A2G coordinate format system.
+
+ Parameters
+
+ Return value
+
+#string:
+The coordinate Text in the configured coordinate system.
+
+
+
+
+-
+
COORDINATE:ToStringAspect(TargetCoordinate)
diff --git a/docs/Documentation/RAT.html b/docs/Documentation/RAT.html
index c3bbbb336..cbbba2eb8 100644
--- a/docs/Documentation/RAT.html
+++ b/docs/Documentation/RAT.html
@@ -107,7 +107,7 @@
Module Rat
- - Functional -- Create random airtraffic in your missions.
+ - Functional - Create random airtraffic in your missions.
@@ -116,16 +116,43 @@
-The documentation of the RAT class can be found further in this document.
+The aim of the RAT class is to fill the empty DCS world with randomized air traffic and bring more life to your airports.
+
+In particular, it is designed to spawn AI air units at random airports. These units will be assigned a random flight path to another random airport on the map.
+
+Even the mission designer will not know where aircraft will be spawned and which route they follow.
+
+Features
+
+
+ - Very simple interface. Just one unit and two lines of Lua code needed to fill your map.
+ - High degree of randomization. Aircraft will spawn at random airports, have random routes and random destinations.
+ - Specific departure and/or destination airports can be chosen.
+ - Departure and destination airports can be restricted by coalition.
+ - Planes and helicopters supported. Helicopters can also be send to FARPs and ships.
+ - Units can also be spawned in air within pre-defined zones of the map.
+ - Aircraft will be removed when they arrive at their destination (or get stuck on the ground).
+ - When a unit is removed a new unit with a different flight plan is respawned.
+ - Aircraft can report their status during the route.
+ - All of the above can be customized by the user if necessary.
+ - Can be used on all current (Caucasus, Nevada, Normandy) and future maps.
+
+
+ The RAT class creates an entry in the F10 menu which allows to
+
+
+ - Create new groups on-the-fly, i.e. at run time within the mission,
+ - Destory specific groups (e.g. if they get stuck or damaged and block a runway),
+ - Request the status of all RAT aircraft or individual groups,
+ - Place markers at waypoints on the F10 map for each group.
+
+
+Note that by its very nature, this class is suited best for civil or transport aircraft. However, it also works perfectly fine for military aircraft of any kind.
Demo Missions
-
-
-
-
@@ -134,7 +161,6 @@
-
@@ -148,14 +174,8 @@
| RAT |
- RAT class
- |
-
-
- | myid |
-
- Some ID to identify where we are
-string myid
+
+The RAT class implements an easy to use way to randomly fill your map with AI aircraft.
|
@@ -164,13 +184,13 @@
| RAT.AlphaDescent |
-
+ Default angle of descenti in degrees. A value of 3.6 follows the 3:1 rule of 3 miles of travel and 1000 ft descent.
|
| RAT.ClassName |
- Name of the Class
+Name of the Class.
|
@@ -188,25 +208,25 @@
| RAT.FLmaxuser |
-
+ Maximum flight level set by user.
|
| RAT.FLminuser |
-
+ Minimum flight level set by user.
|
| RAT.FLuser |
-
+ Flight level set by users explicitly.
|
| RAT.Menu |
-
+ F10 menu items for this RAT object.
|
@@ -225,12 +245,6 @@
| RAT:PlaceMarkers(switch) |
Place markers of waypoints on the F10 map.
- |
-
-
- | RAT.RAT |
-
-
|
@@ -374,19 +388,31 @@
| RAT.SubMenuName |
-
+ Submenu name for RAT object.
+ |
+
+
+ | RAT:TimeDestroyInactive(time) |
+
+ Set the time after which inactive groups will be destroyed.
+ |
+
+
+ | RAT.Tinactive |
+
+ Time in seconds after which inactive units will be destroyed. Default is 180 seconds.
|
| RAT.Vclimb |
-
+ Default climb rate in ft/min.
|
| RAT.Vcruisemax |
-
+ Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user.
|
@@ -596,37 +622,37 @@
| RAT.aircraft |
-
+ Table which holds the basic aircraft properties (speed, range, ...).
|
| RAT.airports |
-
+ All airports of friedly coalitions.
|
| RAT.airports_map |
-
+ All airports available on current map (Caucasus, Nevada, Normandy, ...).
|
| RAT.alive |
-
+ Number of groups which are alive.
|
| RAT.cat |
- RAT categories.
+
|
| RAT.category |
-
+ Category of aircarft: "plane" or "heli".
|
@@ -638,61 +664,67 @@
| RAT.coalition |
-
+ Coalition of spawn group template.
|
| RAT.commute |
-
+ Aircraft commute between departure and destination, i.e. when respawned the departure airport becomes the new destiation.
|
| RAT.continuejourney |
-
+ Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination.
|
| RAT.ctable |
-
+ Table with the valid coalitons from choice self.friendly.
|
| RAT.debug |
-
+ Turn debug messages on or off.
|
| RAT.departure_ports |
-
+ Array containing the names of the destination airports.
|
| RAT.departure_zones |
-
+ Array containing the names of the departure zones.
|
| RAT.destination_ports |
-
+ Array containing the names of the destination airports.
|
| RAT.f10menu |
-
+ Add an F10 menu for RAT.
|
| RAT.friendly |
-
+ Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
+ |
+
+
+ | RAT.id |
+
+ Some ID to identify who we are in output of the DCS.log file.
|
@@ -704,97 +736,97 @@
| RAT.markerids |
-
+ Array with marker IDs.
|
| RAT.maxdist |
-
+ Max distance from departure to destination in meters. Default 5000 km.
|
| RAT.mindist |
-
+ Min distance from departure to destination in meters. Default 5 km.
|
| RAT.placemarkers |
-
+ Place markers of waypoints on F10 map.
|
| RAT.random_departure |
-
+ By default a random friendly airport is chosen as departure.
|
| RAT.random_destination |
-
+ By default a random friendly airport is chosen as destination.
|
| RAT.ratcraft |
-
+ Array with the spawned RAT aircraft.
|
| RAT.reportstatus |
-
+ Aircraft report status.
|
- | RAT.respawn_after_landing |
+ RAT.respawn_at_landing |
-
+ Respawn aircraft the moment they land rather than at engine shutdown.
|
| RAT.respawn_delay |
-
+ Delay in seconds until repawn happens after landing.
|
| RAT.roe |
-
+ ROE of spawned groups, default is weapon hold (this is a peaceful class for civil aircraft or ferry missions). Possible: "hold", "return", "free".
|
| RAT.rot |
-
+ ROT of spawned groups, default is no reaction. Possible: "noreaction", "passive", "evade".
|
| RAT.spawndelay |
-
+ Delay time in seconds before first spawning happens.
|
| RAT.spawninterval |
-
+ Interval between spawning units/groups. Note that we add a randomization of 50%.
|
| RAT.statusinterval |
-
+ Intervall between status checks (and reports if enabled).
|
| RAT.takeoff |
-
+ Takeoff type. 0=coldorhot.
|
@@ -822,22 +854,154 @@
-
-
RAT class
+
+The RAT class implements an easy to use way to randomly fill your map with AI aircraft.
-
-
-
--
-
- #string
-
-myid
-
-
--
-
Some ID to identify where we are
-string myid
+
+
+Airport Selection
+
+
+
+Default settings:
+
+
+ - By default, aircraft are spawned at airports of their own coalition (blue or red) or neutral airports.
+ - Destination airports are by default also of neutral or of the same coalition as the template group of the spawned aircraft.
+ - Possible destinations are restricted by their distance to the departure airport. The maximal distance depends on the max range of spawned aircraft type and its initial fuel amount.
+
+
+The default behavior can be changed:
+
+
+ - A specific departure and/or destination airport can be chosen.
+ - Valid coalitions can be set, e.g. only red, blue or neutral, all three „colours“.
+ - It is possible to start in air within a zone defined in the mission editor or within a zone above an airport of the map.
+
+
+Flight Plan
+
+
+
+
+ - A general flight plan has five main airborn segments: Climb, cruise, descent, holding and final approach.
+ - Events monitored during the flight are: birth, engine-start, take-off, landing and engine-shutdown.
+ - The default flight level (FL) is set to ~FL200, i.e. 20000 feet ASL but randomized for each aircraft.
+ Service ceiling of aircraft type is into account for max FL as well as the distance between departure and destination.
+ - Maximal distance between destination and departure airports depends on range and initial fuel of aircraft.
+ - Climb rate is set to a moderate value of ~1500 ft/min.
+ - The standard descent rate follows the 3:1 rule, i.e. 1000 ft decent per 3 miles of travel. Hence, angle of descent is ~3.6 degrees.
+ - A holding point is randomly selected at a distance between 5 and 10 km away from destination airport.
+ - The altitude of theholding point is ~1200 m AGL. Holding patterns might or might not happen with variable duration.
+ - If an aircraft is spawned in air, the procedure omitts taxi and take-off and starts with the climb/cruising part.
+ - All values are randomized for each spawned aircraft.
+
+
+Mission Editor Setup
+
+
+
+Basic mission setup is very simple and essentially a three step process:
+
+
+ - Place your aircraft anywhere on the map. It really does not matter where you put it.
+ - Give the group a good name. In the example above the group is named "RAT_YAK".
+ - Activate the "LATE ACTIVATION" tick box. Note that this aircraft will not be spawned itself but serves a template for each RAT aircraft spawned when the mission starts.
+
+
+Voilà, your already done!
+
+Optionally, you can set a specific livery for the aircraft or give it some weapons.
+However, the aircraft will by default not engage any enemies. Think of them as beeing on a peaceful or ferry mission.
+
+Basic Lua Script
+
+
+
+The basic Lua script for one template group consits of two simple lines as shown in the picture above.
+
+
+ - Line 2 creates a new RAT object "yak". The only required parameter for the constructor RAT.New() is the name of the group as defined in the mission editor. In this example it is "RAT_YAK".
+ - Line 5 trigger the command to spawn the aircraft. The (optional) parameter for the RAT.Spawn() function is the number of aircraft to be spawned of this object.
+ By default each of these aircraft gets a random departure airport anywhere on the map and a random destination airport, which lies within range of the of the selected aircraft type.
+
+
+In this simple example aircraft are respawned with a completely new flightplan when they have reached their destination airport.
+The "old" aircraft is despawned (destroyed) after it has shut-down its engines and a new aircraft of the same type is spawned at a random departure airport anywhere on the map.
+Hence, the default flight plan for a RAT aircraft will be: Fly from airport A to B, get respawned at C and fly to D, get respawned at E and fly to F, ...
+This ensures that you always have a constant number of AI aircraft on your map.
+
+Examples
+
+Here are a few examples, how you can modify the default settings of RAT class objects.
+
+Specify Departure and Destinations
+
+
+
+In the picture above you find a few possibilities how to modify the default behaviour to spawn at random airports and fly to random destinations.
+
+In particular, you can specify fixed departure and/or destination airports. This is done via the RAT.SetDeparture() or RAT.SetDestination() functions, respectively.
+
+
+ - If you only fix a specific departure airport via RAT.SetDeparture() all aircraft will be spawned at that airport and get random destination airports.
+ - If you only fix the destination airport via RAT.SetDestination(), aircraft a spawned at random departure airports but will all fly to the destination airport.
+ - If you fix departure and destination airports, aircraft will only travel from between those airports.
+ When the aircraft reaches its destination, it will be respawned at its departure and fly again to its destination.
+
+
+There is also an option that allows aircraft to "continue their journey" from their destination. This is achieved by the RAT.ContinueJourney() function.
+In that case, when the aircraft arrives at its first destination it will be respawned at that very airport and get a new random destination.
+So the flight plan in this case would be: Fly from airport A to B, then from B to C, then from C to D, ...
+
+It is also possible to make aircraft "commute" between two airports, i.e. flying from airport A to B and then back from B to A, etc.
+This can be done by the RAT.Commute() function. Note that if no departure or destination airports are specified, the first departure and destination are chosen randomly.
+Then the aircraft will fly back and forth between those two airports indefinetly.
+
+
+Spawn in Air
+
+
+
+Aircraft can also be spawned in air rather than at airports on the ground. This is done by setting RAT.SetTakeoff() to "air".
+
+By default, aircraft are spawned randomly above airports of the map.
+
+The RAT.SetDeparture() option can be used to specify zones, which have been defined in the mission editor as departure zones.
+Aircraft will then be spawned at a random point within the zone or zones.
+
+Note that RAT.SetDeparture() also accepts airport names. For an air takeoff these are treated like zones with a radius of XX kilometers.
+Again, aircraft are spawned at random points within these zones around the airport.
+
+Misc Options
+
+
+
+The default "takeoff" type of RAT aircraft is that they are spawned with hot or cold engines.
+The choice is random, so 50% of aircraft will be spawned with hot engines while the other 50% will be spawned with cold engines.
+This setting can be changed using the RAT.SetTakeoff() function. The possible parameters for starting on ground are:
+
+
+ - RAT.SetTakeoff("cold"), which means that all aircraft are spawned with their engines off,
+ - RAT.SetTakeoff("hot"), which means that all aircraft are spawned with their engines on,
+ - RAT.SetTakeoff("runway"), which means that all aircraft are spawned already at the runway ready to takeoff.
+ Note that in this case the default spawn intervall is set to 180 seconds in order to avoid aircraft jamms on the runway. Generally, this takeoff at runways should be used with care and problems are to be expected.
+
+
+
+The options RAT.SetMinDistance() and RAT.SetMaxDistance() can be used to restrict the range from departure to destination. For example
+
+
+ - RAT.SetMinDistance(100) will cause only random destination airports to be selected which are at least 100 km away from the departure airport.
+ - RAT.SetMaxDistance(150) will allow only destination airports which are less than 150 km away from the departure airport.
+
+
+
+Certain other options like the flight level can also be specified. However, note that this might not be a good idea for random departures and/or destinations.
+For example the random route might be too short to reach that altitude, which would result in very high climb and descent rates or strange flight plans.
+
+
@@ -845,7 +1009,9 @@
-RAT class
+
Field(s)
@@ -858,7 +1024,7 @@
-
-
+
Default angle of descenti in degrees. A value of 3.6 follows the 3:1 rule of 3 miles of travel and 1000 ft descent.
@@ -872,7 +1038,7 @@
-
-
Name of the Class
+Name of the Class.
@@ -936,7 +1102,7 @@ Turn journey on=true or off=false. If no value is given switch=true.
-
-
+
Maximum flight level set by user.
@@ -950,7 +1116,7 @@ Turn journey on=true or off=false. If no value is given switch=true.
-
-
+
Minimum flight level set by user.
@@ -964,7 +1130,7 @@ Turn journey on=true or off=false. If no value is given switch=true.
-
-
+
Flight level set by users explicitly.
@@ -978,14 +1144,14 @@ Turn journey on=true or off=false. If no value is given switch=true.
-
-
+
F10 menu items for this RAT object.
-
- #RAT
+ #string
@@ -1064,21 +1230,7 @@ true=yes, false=no.
-
- #table
-
-RAT.RAT
-
-
--
-
-
-
-
-
-
--
-
- #RAT
+
RAT.ROE
@@ -1092,7 +1244,7 @@ true=yes, false=no.
-
- #RAT
+
RAT.ROT
@@ -1425,7 +1577,7 @@ Distance in km.
Default is 5 km.
-Minimum distance should not be smaller than ~500(?) meters to ensure that departure and destination are different.
+Minimum distance should not be smaller than maybe ~500 meters to ensure that departure and destination are different.
Parameter
@@ -1662,7 +1814,46 @@ true=on, false=off.
-
+
Submenu name for RAT object.
+
+
+
+-
+
+
+RAT:TimeDestroyInactive(time)
+
+
+-
+
+
Set the time after which inactive groups will be destroyed.
+
+
+Default is 180 seconds.
+
+ Parameter
+
+
+
+
+-
+
+ #number
+
+RAT.Tinactive
+
+
+-
+
+
Time in seconds after which inactive units will be destroyed. Default is 180 seconds.
@@ -1676,7 +1867,7 @@ true=on, false=off.
-
-
+
Default climb rate in ft/min.
@@ -1690,7 +1881,7 @@ true=on, false=off.
-
-
+
Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user.
@@ -2715,7 +2906,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Table which holds the basic aircraft properties (speed, range, ...).
@@ -2729,7 +2920,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
All airports of friedly coalitions.
@@ -2743,7 +2934,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
All airports available on current map (Caucasus, Nevada, Normandy, ...).
@@ -2757,21 +2948,21 @@ Waypoints for DCS task route or spawn template.
-
-
+
Number of groups which are alive.
-
- #RAT
+
RAT.cat
-
-
RAT categories.
+
@@ -2785,14 +2976,14 @@ Waypoints for DCS task route or spawn template.
-
-
+
Category of aircarft: "plane" or "heli".
-
- #RAT
+
RAT.coal
@@ -2813,7 +3004,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Coalition of spawn group template.
@@ -2827,7 +3018,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Aircraft commute between departure and destination, i.e. when respawned the departure airport becomes the new destiation.
@@ -2841,7 +3032,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination.
@@ -2855,7 +3046,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Table with the valid coalitons from choice self.friendly.
@@ -2869,7 +3060,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Turn debug messages on or off.
@@ -2883,7 +3074,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Array containing the names of the destination airports.
@@ -2897,7 +3088,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Array containing the names of the departure zones.
@@ -2911,7 +3102,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Array containing the names of the destination airports.
@@ -2925,7 +3116,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Add an F10 menu for RAT.
@@ -2939,14 +3130,28 @@ Waypoints for DCS task route or spawn template.
-
-
+
Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
-
- #RAT
+ #string
+
+RAT.id
+
+
+-
+
+
Some ID to identify who we are in output of the DCS.log file.
+
+
+
+
+-
+
+ #number
RAT.markerid
@@ -2967,7 +3172,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Array with marker IDs.
@@ -2981,7 +3186,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Max distance from departure to destination in meters. Default 5000 km.
@@ -2995,7 +3200,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Min distance from departure to destination in meters. Default 5 km.
@@ -3009,7 +3214,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Place markers of waypoints on F10 map.
@@ -3023,7 +3228,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
By default a random friendly airport is chosen as departure.
@@ -3037,7 +3242,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
By default a random friendly airport is chosen as destination.
@@ -3051,7 +3256,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Array with the spawned RAT aircraft.
@@ -3065,7 +3270,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Aircraft report status.
@@ -3073,13 +3278,13 @@ Waypoints for DCS task route or spawn template.
-
#boolean
-
-RAT.respawn_after_landing
+
+RAT.respawn_at_landing
-
-
+
Respawn aircraft the moment they land rather than at engine shutdown.
@@ -3093,7 +3298,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Delay in seconds until repawn happens after landing.
@@ -3107,7 +3312,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
ROE of spawned groups, default is weapon hold (this is a peaceful class for civil aircraft or ferry missions). Possible: "hold", "return", "free".
@@ -3121,7 +3326,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
ROT of spawned groups, default is no reaction. Possible: "noreaction", "passive", "evade".
@@ -3135,7 +3340,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Delay time in seconds before first spawning happens.
@@ -3149,7 +3354,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Interval between spawning units/groups. Note that we add a randomization of 50%.
@@ -3163,7 +3368,7 @@ Waypoints for DCS task route or spawn template.
-
-
+
Intervall between status checks (and reports if enabled).
@@ -3177,14 +3382,14 @@ Waypoints for DCS task route or spawn template.
-
-
+
Takeoff type. 0=coldorhot.
-
- #RAT
+
RAT.unit
@@ -3198,7 +3403,7 @@ Waypoints for DCS task route or spawn template.
-
- #RAT
+
RAT.wp
diff --git a/docs/Documentation/Spawn.html b/docs/Documentation/Spawn.html
index 89e15cae9..845fa5248 100644
--- a/docs/Documentation/Spawn.html
+++ b/docs/Documentation/Spawn.html
@@ -823,12 +823,6 @@ and any spaces before and after the resulting name are removed.
| SPAWN:_TranslateRotate(SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle) |
- |
-
-
- | SPAWN.uncontrolled |
-
-
|
@@ -2201,6 +2195,9 @@ The group that was spawned. You can use this group for further actions.
+
+ Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
+
@@ -2739,9 +2736,6 @@ when nothing was spawned.
-
- Overwrite unit names by default with group name.
-
@@ -3139,7 +3133,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 )
-
- #boolean
+
SPAWN.SpawnUnControlled
@@ -3743,20 +3737,6 @@ True = Continue Scheduler
-
-
-
--
-
-
-
-SPAWN.uncontrolled
-
-
--
-
-
-
diff --git a/docs/Documentation/Task.html b/docs/Documentation/Task.html
index 1314757f2..c33573603 100644
--- a/docs/Documentation/Task.html
+++ b/docs/Documentation/Task.html
@@ -238,6 +238,12 @@
TASK:GetID() |
Gets the ID of the Task
+ |
+
+
+ | TASK:GetInfo(TaskInfo) |
+
+ Gets the Information of the Task
|
@@ -418,6 +424,12 @@
| TASK.MenuAssigned |
+ |
+
+
+ | TASK:MenuMarkToGroup(TaskGroup) |
+
+
|
@@ -1334,6 +1346,33 @@ TaskID
-
+
+TASK:GetInfo(TaskInfo)
+
+
+-
+
+
Gets the Information of the Task
+
+ Parameter
+
+ Return value
+
+#string:
+TaskInfoText The Task info text.
+
+
+
+
+-
+
TASK:GetMission()
@@ -1888,6 +1927,27 @@ true if Unit is part of the Task.
+
+
+
+-
+
+
+
+-
+
+
+
+
Parameter
+
diff --git a/docs/Documentation/index.html b/docs/Documentation/index.html
index 7e76677a3..f84a58bb2 100644
--- a/docs/Documentation/index.html
+++ b/docs/Documentation/index.html
@@ -527,7 +527,7 @@ are design patterns allowing efficient (long-lasting) processes and workflows.
| Rat |
- - Functional -- Create random airtraffic in your missions.
+ - Functional - Create random airtraffic in your missions.
|
diff --git a/docs/Presentations/RAT/RAT_Airport_Selection.png b/docs/Presentations/RAT/RAT_Airport_Selection.png
new file mode 100644
index 0000000000000000000000000000000000000000..505a53353b9cf51467cbbc3a1b7a9bd798fd7a71
GIT binary patch
literal 574126
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%0Ats>eZ4_
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 Cq0J#mC!gA*IFms0qb!o5k_
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 | |