Compare commits

..

433 Commits

Author SHA1 Message Date
Frank
8bf56b8b1a Update Airboss.lua
- Fixed AI handling (got lost in some merge with develop branch and was using FLIGHTGROUPS)
2023-07-26 16:36:44 +02:00
Applevangelist
f31741f934 #AI_ESCORT
* Improve documentation
2023-07-25 12:01:18 +02:00
Applevangelist
46258492bd #AIRBASE, #ATIS
* Additions
2023-07-23 12:37:57 +02:00
Applevangelist
6481f66e27 #MANTIS
* Added IDs for Current Hill Assets, keyword "CHM" for group names
2023-07-16 11:27:34 +02:00
Thomas
5954b8692f Update Spot.lua
Fix
2023-07-15 09:36:24 +02:00
Applevangelist
f6fdecf892 Merge remote-tracking branch 'origin/master' 2023-07-13 16:15:11 +02:00
Applevangelist
53fb77b50d #SPAWN 2023-07-13 16:15:06 +02:00
Applevangelist
04a9dc3a8c #SPAWN
* Added method to init spawn position
2023-07-13 16:13:00 +02:00
Applevangelist
10b9a32f29 #SPOT
* Fix for switching laser off and on again not follwing unit any more
2023-07-12 17:54:17 +02:00
Applevangelist
40fa929eb0 * Added omissed DetectedItem.Name found by @Nocke 2023-07-07 15:14:33 +02:00
Applevangelist
8c8ef19f01 #CONTROLLABLE
* Added TaskGroundEscort
2023-07-03 16:44:46 +02:00
Applevangelist
1eaa3d309d #SCORING
* typo
2023-07-01 13:15:59 +02:00
Applevangelist
a52cd5612a Merge remote-tracking branch 'origin/master' 2023-07-01 13:12:29 +02:00
Applevangelist
e82ed762be Merge remote-tracking branch 'origin/master' 2023-07-01 13:11:46 +02:00
Applevangelist
0bb16ec827 #AIRBASE
* Fix for nil error in finding parking for a group

#MANTIS
* Added SAM type "SHORAD" as designator

#SCORING
* fix for non local problem

#UTILS
* fix for OneLineSerialize
2023-07-01 13:11:21 +02:00
Thomas
a978420a67 Point - BRAANATO (#1969)
corrected Track to be direction of travel of bogey (self in this case)
2023-06-26 13:25:23 +02:00
Applevangelist
f59326bf10 #ATIS
* Fix for airbases which have no runways, e.g. Naqoura Syria
2023-06-24 14:03:43 +02:00
Thomas
d937ab2679 CTLD (#1967)
* Added option for troops subcategories in menu
2023-06-22 13:46:11 +02:00
Applevangelist
7164e211f2 #AIRBASE
- Nicosia unused atm
2023-06-21 10:42:35 +02:00
Applevangelist
dae78d7ac1 #Positionable, Unit - remove pcall 2023-06-16 11:06:46 +02:00
Applevangelist
254468c723 #NET
* Improvements
2023-06-15 14:51:36 +02:00
Applevangelist
71be4d99d6 #POSITIONABLE:GetRelativeCoordinate( x, y, z ) 2023-06-14 17:40:33 +02:00
Frank
f86fc845e7 Update Timer.lua
- Timer stop is using protected call now because it can crash the whole script.
2023-06-13 21:53:19 +02:00
Frank
cc907b9c14 Update Unit.lua
- UNIT:IsAlive() fixed so it is consistent with GROUP:IsAlive()
2023-06-13 19:02:55 +02:00
Applevangelist
241b2beee1 * Makes calls for GetAmmo() and GetVec3() safer with pcall()
* Small docu fix Ben-Gurion
2023-06-13 08:39:56 +02:00
Frank
12f260e857 Update Unit.lua
- Added `UNIT:IsExist` function.
2023-06-12 23:01:19 +02:00
Thomas
096d145cf9 Update Airbase.lua (#1957)
Deleted raj al … airbases #1955
2023-06-12 06:22:51 +02:00
Thomas
b3883301a2 Update Airbase.lua
Corrected enumerator for Ben-Gurion (Ben_Gurion)
2023-06-11 12:33:01 +02:00
Applevangelist
3a7521c492 #UTILS
* Sinai TIme
2023-06-10 19:08:46 +02:00
Applevangelist
caa5a96235 fix 2023-06-10 19:06:22 +02:00
Applevangelist
cee1c592ba #UTILS
* Sinai TIme
2023-06-10 19:04:40 +02:00
Frank
f1fc9f0b27 Update Utils.lua
- Added parameters time zone and magvar for Sinai map
2023-06-10 10:08:22 +02:00
Applevangelist
e9adcb0dd5 #AIRBASE
* Sinai airfield enumerator
2023-06-09 15:13:45 +02:00
Applevangelist
afe2b675e5 #RAT
* Fixes from dev
2023-06-08 13:59:59 +02:00
Applevangelist
2c14ee74b0 #SCORING
* Can leave text empty on some instances
2023-06-08 13:58:26 +02:00
Frank
f351d2a37e Merge pull request #1951 from FlightControl-Master/FF/MasterDevel
Update RAT.lua
2023-06-08 09:45:49 +02:00
Frank
4cb0e70184 Update RAT.lua
- Fixed RATMANAGER spamming due to not accounting for planned/scheduled groups
2023-06-08 09:43:11 +02:00
Applevangelist
41bb2551d3 #CTLD
* Ensure new menus can be build if player changes helos
2023-06-03 12:34:18 +02:00
Applevangelist
55ed394782 #UNIT #CTLD
* Stabilize that sometimes a unit coordinate cannot be found
2023-06-02 08:44:14 +02:00
Applevangelist
dd7a883a33 Fixes 2023-06-01 10:02:51 +02:00
Applevangelist
f7acbc3928 #CSAR
* Corrected flare distance to kms
2023-05-30 07:38:19 +02:00
Applevangelist
2318578126 #SPAWN
* Logic fix for the last parameter of `NewFromTemplate()`
2023-05-28 15:27:34 +02:00
Applevangelist
3e6f25f17c #UTILS
* Added UTILS.PrintTableToLog()
2023-05-26 08:28:08 +02:00
Applevangelist
0cd6a59ce4 #CONTROLLER
* Fix for Link4
2023-05-25 08:57:15 +02:00
Applevangelist
91a445961b #RESCUEHELO
* Small fix to get the coordinate of an ejected/crashed unit
2023-05-25 08:23:31 +02:00
Frank
749e9b7e08 Update Event.lua
Fixed bug that statics are handled incorrectly if they are the target object of an event (*e.g.* hit event)
2023-05-24 11:16:01 +02:00
Applevangelist
905d29b279 #SPAWN
* Tweaked NewFromTemplate, and gave a better example
2023-05-22 17:57:03 +02:00
Applevangelist
5f97299cb2 Small Fix 2023-05-20 12:12:18 +02:00
Applevangelist
60b75a4c8f #POINT
* Added FindClosestStatic()
2023-05-19 17:11:54 +02:00
Applevangelist
0868286f27 #WAREHOUSE
* Fixed one omission for SHIP transport types
2023-05-17 10:26:29 +02:00
Applevangelist
ffcc46cb2d #CTLD
* Changed VHF modulation to AM
* Added option to have a different sound file for UHF, in case you want to silent FC3 radios
* Thanks to Streakeagle
2023-05-17 09:34:54 +02:00
Frank
21bcd64c9d Update Controllable.lua
Corrected parameters for enroute tasks according to hoggit for
- CONTROLLABLE:EnRouteTaskFAC
- CONTROLLABLE:EnRouteTaskFAC_EngageGroup
2023-05-13 23:35:58 +02:00
Applevangelist
943b68f38f #CTLD
* placement of dropped crates to better align to unit length
2023-05-11 15:31:24 +02:00
Applevangelist
256b93087f #AI_A2X...
* Fixes
2023-05-09 09:48:21 +02:00
Applevangelist
7ed0d7eec3 #AI_A2X...
* Fixes
2023-05-09 09:44:41 +02:00
Applevangelist
9b8154246f #COORDINATE
* Added `IsInSteepArea()`and `IsInFlatArea()`
2023-05-05 10:31:23 +02:00
Frank
c360759e49 Merge pull request #1939 from FlightControl-Master/FF/MasterDevel
Update Socket.lua
2023-04-25 21:42:53 +02:00
Frank
d8b80aab1a Update Socket.lua
- Added `SOCKET:SendTextToSpeech()` function
2023-04-25 21:20:16 +02:00
Applevangelist
b96ebc1872 #SCENERY
* Added update of Life0 value if `GetLife()`is called
#SET_SCENERY
* Added Documentation
2023-04-25 09:12:31 +02:00
Applevangelist
6896dc155a #SCENERY
* Added update of Life0 value if `GetLife()`is called
#SET_SCENERY
* Added Documentation
2023-04-25 09:12:25 +02:00
Applevangelist
1060d63808 #SET_SCENERY
* Added functions to count Life0, Life and RelativeLife points of SET_SCENERY
2023-04-24 16:44:38 +02:00
Thomas
88b6540f5b AIRBASE - add Normandy AFBs (#1937)
Add Normandy AFBs
2023-04-24 11:09:03 +02:00
Frank
302e785f32 Merge pull request #1936 from FlightControl-Master/FF/MasterDevel
Remove Junk
2023-04-22 14:05:09 +02:00
Frank
1507cc0b42 Remove Junk
**ZONE**
- Added `ZONE_RADIUS:RemoveJunk()` function
- Added `ZONE_POLYGON_BASE:RemoveJunk()` function (not working)

**POSITIONABLE**
- Added `Explosion`
2023-04-22 14:02:18 +02:00
Applevangelist
240307ef94 #CTLD
* Docu changes
2023-04-20 08:23:03 +02:00
Applevangelist
90c74bd82c #SET
- Minus one log entry
2023-04-20 08:06:43 +02:00
Thomas
9414096de7 Added active ZONE filtering (#1935)
for groups, units, clients entering/leaving zones to be used with `FilterStart()`
2023-04-19 09:51:53 +02:00
Applevangelist
07c3be9d6a Fixes for getPlayername() errors 2023-04-18 10:24:56 +02:00
Applevangelist
9869dbd95a #CTLD
* Include current group structure in load/save functions
2023-04-16 16:10:46 +02:00
Frank
49b702106a Update Weapon.lua
- Decreased min possible time step to 0.00001 seconds
2023-04-16 12:29:57 +02:00
Applevangelist
8eb09beb96 Merge remote-tracking branch 'origin/master' 2023-04-15 16:19:46 +02:00
Applevangelist
b402a99a25 #CTLD
* Small fix allowing minus signs in template names for repairs
2023-04-15 16:19:35 +02:00
Applevangelist
ad8938cd74 #CTLD
* Small fix allowing minus signs in template names for repairs
2023-04-15 16:17:01 +02:00
Applevangelist
ee409c45a0 #CTLD
* Allow save/loadback with precise(r) coordinates for vehicles and troops.
2023-04-13 15:51:21 +02:00
Applevangelist
9d5da3388d smaller docu item 2023-04-13 15:28:41 +02:00
Applevangelist
9e138aa149 #DATABASE
* Small fix for CLIENT:FindByName()
2023-04-04 10:33:04 +02:00
Applevangelist
cf94c4d043 #NET
* Fix for ucid can be nil
2023-04-03 12:15:29 +02:00
Applevangelist
63b3807cf6 Added Init Methods to set Unit Positions on Spawn 2023-03-30 12:22:04 +02:00
Applevangelist
f3b6b27521 Small fix for SET_UNIT if used in capture zone coalition with a polygon zone 2023-03-30 09:24:08 +02:00
Applevangelist
ff656182e8 SET_STATIC - Added GetClosestStatic() 2023-03-28 11:03:00 +02:00
Applevangelist
a85ef6e769 Merge remote-tracking branch 'origin/master' 2023-03-23 09:21:46 +01:00
Applevangelist
0bb8c56ea9 Typo fix 2023-03-23 09:21:42 +01:00
Applevangelist
5f108aec23 Typo fix 2023-03-23 09:21:20 +01:00
Applevangelist
bf6683f993 Merge remote-tracking branch 'origin/master' 2023-03-23 08:47:07 +01:00
Applevangelist
b7a5e8dd85 Add'l check in UNIT:GetLife() 2023-03-23 08:47:04 +01:00
Applevangelist
57294aeaf5 Add'l check in UNIT:GetLife() 2023-03-23 08:45:54 +01:00
Applevangelist
20bfb8b08f #AIRBASE
* Added/changed South Atlantic Map:
  ["Hipico_Flying_Club"] = "Hipico Flying Club",
  ["Aeropuerto_de_Gobernador_Gregores"] = "Aeropuerto de Gobernador Gregores",
  ["Aerodromo_O_Higgins"] = "Aerodromo O'Higgins",
  ["Cullen_Airport"] = "Cullen Airport",
  ["Gull_Point"] = "Gull Point",
2023-03-16 08:44:16 +01:00
UglySkyfire
5585a8992c Drop crates from herc as CTLD_Cargo (#1929)
Adds option to drop crates from a herc via parachute drop as CTLD_Cargo that needs proper unpacking - can be picked up be a helo.
2023-03-15 20:12:30 +01:00
Applevangelist
d92a20050c #CTLD
* Added/completed add/get/set functions for stock
2023-03-10 10:09:08 +01:00
Applevangelist
c9a91d0683 #GROUP
* Fix for GetMaxHeight()
2023-03-09 08:49:07 +01:00
Applevangelist
ae6716ac01 #GROUP
* Callsign translation refactor
2023-03-05 11:03:26 +01:00
Applevangelist
34285b26ae #SPAWN
* clarified docu for SpawnScheduled()
* Added parameter to SpawnScheduled() for a delayed spawn
2023-02-28 08:00:06 +01:00
Thomas
5341e5faee Update Enums.lua 2023-02-26 19:03:33 +01:00
Applevangelist
a6224b484e dcs 2023-02-23 10:33:11 +01:00
Applevangelist
e95c1ad3aa #Fixes from dev 2023-02-23 10:30:59 +01:00
Applevangelist
9cfed56847 #CTLD logic correction for loading 2023-02-21 11:38:40 +01:00
Applevangelist
6c1abddb1e #NET added a couple of functions 2023-02-19 17:16:00 +01:00
Applevangelist
7dc239f506 Added CLIENT:FindByPlayerName(Name)
NET - slot blocker comments in log removed
2023-02-19 12:31:32 +01:00
Applevangelist
3e8413c6b7 #NET - further ado 2023-02-18 15:03:53 +01:00
Applevangelist
ff86bfb91d #NET Event blocker fix 2023-02-17 16:41:38 +01:00
Applevangelist
66494b7b5a NET 2023-02-17 16:23:02 +01:00
Applevangelist
973127aa8c #NET Fixes 2023-02-17 15:42:06 +01:00
Applevangelist
83866c3dd3 #NET Fixes 2023-02-17 15:02:11 +01:00
Applevangelist
4797abc287 #NET Bugfix 2023-02-17 14:32:48 +01:00
Applevangelist
50f73f1be2 #NET extended block to UCID 2023-02-17 13:22:10 +01:00
Applevangelist
2ebbc8f466 #NET - added docu 2023-02-17 08:54:15 +01:00
Frank
1a96b30a45 Merge pull request #1922 from FlightControl-Master/FF/MasterDevel
Airspeed
2023-02-16 17:28:13 +01:00
Frank
3b1b57a33e Update Positionable.lua
- Added GetGroundSpeed
2023-02-16 17:20:53 +01:00
Frank
f85c0320ec Airspeed
**POSITIONABLE**
- Added function `GetAirspeedIndicated` to return IAS
- Added function `GetAirspeedTrue` to return TAS

**UTILS**
- Added function `UTILS.IasToTas` to convert IAS to TAS
- Added function `TasToIas` to convert TAS to IAS.

**POINT**
- Added function `COORDINATE:GetWindVec3`
2023-02-16 17:09:12 +01:00
Applevangelist
bae7edb914 #NET
* Added event management for clients and block/unblock functions
2023-02-16 11:41:45 +01:00
Applevangelist
35322399e9 #SPOT - minor fixes 2023-02-15 12:23:31 +01:00
Applevangelist
5d4c0f3c87 Bugfix 2023-02-15 10:31:52 +01:00
Frank
92c5c32e8c Merge pull request #1921 from FlightControl-Master/FF/MasterDevel
Update Range.lua
2023-02-12 13:03:08 +01:00
Frank
c8780b2d5f Update Range.lua
- Coordinate of bomb target is now always updated (not only for moving targets)
- If the target is not alive any more, the last known position is used for its coordinate
2023-02-12 13:00:19 +01:00
Frank
f11dbfa367 Merge branch 'FF/MasterDevel' 2023-02-12 11:49:13 +01:00
Frank
54fb9deb3e Polygon and line drawings 2023-02-12 11:38:46 +01:00
Thomas
d3c34ef04d Update MarkerOps_Base.lua (#1919) 2023-02-11 22:11:21 +01:00
Applevangelist
3d5470eb22 #NET
* Initial release
2023-02-10 11:00:14 +01:00
Applevangelist
2a3d69d0d5 #DETECTION
* Docu fixes
2023-02-09 16:11:45 +01:00
Frank
9862c32378 Update settings.json 2023-02-08 16:30:44 +01:00
Frank
9f02232589 Update settings.json 2023-02-08 16:29:25 +01:00
Frank
ffc86bf046 Unwanted changes 2023-02-08 16:28:04 +01:00
Frank
e4c8ca34ed Update Pathline.lua 2023-02-08 16:11:47 +01:00
Frank
f02b7036a9 PATHLINE 2023-02-08 12:37:18 +01:00
Frank
6adef47340 PATHLINE
Added new class PATHLINE
2023-02-07 22:47:55 +01:00
Thomas
5b044cc036 Update CSAR.lua (#1917) 2023-02-07 07:28:00 +01:00
Mr.Alien
03ba7524b2 Comment out code for scoring hits (#1911) 2023-02-06 08:15:45 +01:00
Frank
772921c6b8 WEAPON
- Added new class WEAPON
2023-02-04 23:31:55 +01:00
Mr.Alien
d4d305d53b Fix confusing hit and kill messages with incorrect math (#1913) 2023-02-04 23:02:36 +01:00
Mr.Alien
c58918e002 Add missing part of code in previous commit (#1910) 2023-02-04 22:36:16 +01:00
Mr.Alien
dec7854476 Fix issue with getting thread level after a kill (#1908) 2023-02-04 22:01:55 +01:00
Mr.Alien
8ca102f584 Scoring credits a kill to player who hit the same unit before it spawned (#1905) 2023-02-04 21:46:34 +01:00
Applevangelist
4748c66d9c #CTLD
* Added method `CTLD:PreloadCrates(Unit,Cratesname,NumberOfCrates)`
2023-02-04 17:21:16 +01:00
Frank
bf486b9f44 Merge pull request #1901 from MrAlien753/patch-1
fix nil error message in dcs.log
2023-02-03 22:34:01 +01:00
MrAlien753
6ff433ed7a fix nil error message in dcs.log
When instant killing a target in a capture zone nil error message is printed in the dcs.log. This fixes this issue.
2023-02-03 22:14:14 +01:00
Applevangelist
f9aa392c6d #CTLD_HERCULES
* Fixed an issue spawning 90 paratroopers instead of 30
2023-02-02 09:35:53 +01:00
Applevangelist
48721859fa #CTLD
* Added Subcategories for static cargo objects
2023-01-31 11:27:12 +01:00
Applevangelist
ee59d999f5 #POINT
* Added COORDINATE:SetAtLandheight()
2023-01-30 18:00:16 +01:00
Applevangelist
2f4f98cfe0 #CTLD - nobuildmenu option 2023-01-29 18:36:44 +01:00
Frank
ac1f202c19 Update Airboss.lua
- Added CJS Superhornet Mod
2023-01-29 12:53:50 +01:00
Frank
3f97ba3bd7 Database
- Polygon drawings are registered as polygon zones
2023-01-28 18:42:29 +01:00
Frank
819912cfd3 Database
- Polygon drawings are adde as polygon zones to DB
2023-01-28 15:39:42 +01:00
Frank
50ee1ae922 Merge branch 'master' into FF/MasterDevel 2023-01-28 11:09:08 +01:00
grandpaSam
c0442fca68 Changed documentation for Marker.lua and MarkerOps_Base.lua (#1891) 2023-01-28 09:02:15 +01:00
Thomas
c20927b6d7 Update Marker.lua 2023-01-27 18:34:53 +01:00
Jason du Plessis
0d5b6d4c3e #CSAR - Add Persistence (#1889)
* Adds a modified version of ops.CTLD's Persistence to ops.CSAR
2023-01-26 21:11:15 +01:00
Applevangelist
bdc08a530d #AB 2023-01-25 17:54:13 +01:00
Applevangelist
6ab1632b4b #RANGE
* Set Google Key if given

#AIRBASE
* Added 3 Falklands AB to the enumerator
2023-01-25 17:53:29 +01:00
Thomas
cec865b9fb UTILS - exclude 243 MHz and 121.5 MHz 2023-01-25 13:37:56 +01:00
Thomas
d763e924a9 Update Utils.lua (#1886) 2023-01-24 20:16:46 +01:00
Applevangelist
57a30621e1 #CTLD added documentation 2023-01-24 15:26:32 +01:00
Applevangelist
f344084791 #1885
#PSEUDOATC - Menu shows Waypoint name if it has been set
2023-01-24 10:06:05 +01:00
Applevangelist
be68314c3a #CONTROLLABLE
* Added CommandActivateACLS()
* Added CommandDeactivateACLS()
2023-01-23 17:11:18 +01:00
Applevangelist
53cff8229b #CTLD
* Added option movecratesbeforebuild
* Added option surfacetypes for build-in reloads
* Inject function can optionally take surfacetypes
* Inject function can use the center of the zone instead of a random position

#EVENT
* Handle landing event if the place is a SCENERY object (helopad on a map, but not an airbase)

#ZONE
* docu corrections
2023-01-22 13:10:27 +01:00
Frank
1d52e27668 Update Controllable.lua
- Added enroute task SEAD
2023-01-19 19:10:56 +01:00
Applevangelist
6ec867196c #UTILS - make LoadSetOfGroups save(r) for groups spawned with SpawnScheduled 2023-01-19 14:59:54 +01:00
Applevangelist
80798f278c #Controllable - docu changes 2023-01-17 12:09:13 +01:00
Applevangelist
4e61bbb92e Merge remote-tracking branch 'origin/master' 2023-01-17 09:26:16 +01:00
Applevangelist
fabab9bfbb #ATIS docu fix 2023-01-17 09:25:01 +01:00
Applevangelist
4ae0089e4f #CSAR
* Docu corrections
2023-01-15 17:40:03 +01:00
Applevangelist
d82bce79c9 #CSAR
* Docu corrections
2023-01-15 17:38:54 +01:00
Applevangelist
55fcaf1c05 #CTLD - Adde Shark III typename 2023-01-12 13:20:12 +01:00
Applevangelist
e83df502ed #AIRBASE - docu fixes 2023-01-10 13:08:05 +01:00
Applevangelist
6501e89fa2 #PSEUDOATC
* Fix for debug messages
2023-01-10 07:47:56 +01:00
Applevangelist
91801d441f #GROUP
* Improve functionality of GetUnit(x)
* Added GetFirstUnit()
2023-01-09 17:07:21 +01:00
Frank
dd2a4ee7ff COORDINATE
- Added `GetMagneticDeclination` function
2023-01-08 19:32:38 +01:00
Applevangelist
8cedd88ce2 #SCENERY - explain destroy doesn't work 2023-01-05 10:58:40 +01:00
Applevangelist
c3fde2b698 Merge remote-tracking branch 'origin/master' 2023-01-05 10:48:55 +01:00
Applevangelist
59e8aed445 #CSAR fix for beacons 2023-01-05 10:48:50 +01:00
Applevangelist
e47b8f377c #CTLD/CSAR
* Small fix for
2023-01-05 10:45:37 +01:00
Applevangelist
793c0d988e Changes from dev 2023-01-03 10:22:10 +01:00
Applevangelist
b0eef34146 #CONTROLLABLE
* Docu fix

#WAREHOUSE
* Changes from dev

#ZONE
* Additions to ZONE_POLYGON
2023-01-03 10:15:16 +01:00
Applevangelist
3fbfb8b528 #UNIT
* Fix #1865
2023-01-02 17:27:01 +01:00
Thomas
cdd240abb7 PseudoATC - Option to display playername (#1870)
Use `myatc:SetReportPlayername()` to switch this on #1864
2023-01-02 14:28:35 +01:00
Applevangelist
5d802f0e16 #TIMER
* Added `StartIf()`
2023-01-01 12:34:02 +01:00
Applevangelist
8661d07e1e #ATIS
* Make SRS say TACAN and FARP and not spell the single characters
2022-12-29 16:33:13 +01:00
Thomas
41eec658e0 UTILS - Update Load/Save Groups and Statics (#1857)
Addresses #1855 and #1856 

Added options to save groups in a structrued manner, allowing to despawn specific unit-types on a reload. Optionally, cinematic effects like smoke and fires can be put in place of despawned units. For statics, an option to replace them with dead statics has been added, and also cinematic effects.
2022-12-28 15:40:40 +01:00
Thomas
9facf07955 ATIS - added basic FARP support (#1854)
ATIS - added basic FARP support, only works with SRS
2022-12-25 14:18:53 +01:00
Applevangelist
cd4844d26c #EVENT
* Small fix for hit event on coordinates
2022-12-24 12:03:04 +01:00
Applevangelist
9619b11c58 #CTLD
* add sound folder path option
2022-12-23 13:42:14 +01:00
Thomas
535060153a CTLD #1851 Add variable for a sound path (#1852)
CTLD #1851 Add variable for a sound path - defaults to  `self.RadioPath = "l10n/DEFAULT/"`
2022-12-23 13:39:00 +01:00
Applevangelist
f26ff52712 #UNIT
* Docu fix
2022-12-21 14:09:47 +01:00
Applevangelist
4a299ea53f #CTLD
* Added option to inject troops into helos
2022-12-21 14:05:58 +01:00
Applevangelist
6f0ba337c4 #SET_SCENERY
* Added GetAliveSet()
2022-12-21 12:55:01 +01:00
Applevangelist
8df6e2dd57 #UNIT
* Improve GetGroup() as after Dec/22 patch geGroup() might be nil
# SET_UNIT
* Improved Docu
2022-12-19 16:12:09 +01:00
Applevangelist
5010cdff71 #CTLD
* Fix to also save crates which have not been moved
2022-12-19 14:04:30 +01:00
Applevangelist
fea1839c06 #AIRBASE
* Added Rio Chico Airfield
2022-12-16 18:43:50 +01:00
Applevangelist
80e3b157ca #UTILS
* UTILS.LoadSetOfStatics(Path,Filename) ignore statics which do not exist
2022-12-15 18:28:06 +01:00
Applevangelist
d5028d86df #SET_CLIENT
* small addition
2022-12-15 11:49:28 +01:00
Applevangelist
1fac6b7c93 #SET_CLIENT
* Added `FilterCallsigns()` and `FilterPlayernames()`
2022-12-14 14:48:20 +01:00
Applevangelist
089467c15a #AI\_A2A\_GCICAP
* Fix demo mission link
2022-12-14 09:41:35 +01:00
Applevangelist
77e7f767d8 #AIRBASE
* docu correction
2022-12-12 16:23:47 +01:00
Applevangelist
324739aeb9 #SET
* Improve GetRandom() a bit
2022-12-11 15:49:50 +01:00
Applevangelist
84d301c676 #CTLD
* Added disallow building in loadzones: my_ctld.nobuildinloadzones = true
2022-12-09 12:37:39 +01:00
Applevangelist
dcfce8b619 #CTLD - enforce modulation on beacons 2022-12-07 18:50:26 +01:00
Frank
813d4edc97 CONDITION v0.3.0
- Added methods to remove condition functions
- Added option to define output if no condition functions at all were given
-
2022-12-07 18:35:12 +01:00
Applevangelist
9ea4a5dbd4 #CTLD less log noise 2022-12-06 12:49:27 +01:00
Applevangelist
508e36d327 #CTLD Fix for Beacon Zone disappearing too fast 2022-12-03 14:37:01 +01:00
Applevangelist
37b1e7366c *smaller fixes 2022-12-02 16:39:09 +01:00
Thomas
b29b9f1b2c Update README.md 2022-12-01 21:32:19 +01:00
Applevangelist
7865be43bb * Minor fixes 2022-12-01 13:27:58 +01:00
Frank
f17f688a20 Condition and Message 2022-11-30 18:37:14 +01:00
Applevangelist
df2a6a6902 #ATIS
* Added `ATIS:GetSRSText()`
2022-11-29 17:53:41 +01:00
Applevangelist
df0c0ec21e #CTLD
* Small fix for BEACON Zones
2022-11-29 15:39:58 +01:00
Thomas
53d71d9766 Update RAT.lua
Fix #1848
2022-11-28 17:42:09 +01:00
Applevangelist
eb5a72fc27 * less noise 2022-11-27 17:35:13 +01:00
Applevangelist
cec045045e * Less noise 2022-11-27 17:35:13 +01:00
Applevangelist
33f30101d9 Merge remote-tracking branch 'origin/master' 2022-11-23 09:55:50 +01:00
Applevangelist
1e139a6005 #SPAWN
* Fix callsign dupplication of numbers introduced with 2.8
2022-11-23 09:55:47 +01:00
Applevangelist
eacfbad729 #SPAWN
* Fix for unit callsign number duplication since 2.8 release (ED saving callsign.name now as "Texaco11" instead of "Texaco" for the F10 Map overview
2022-11-23 09:48:06 +01:00
Frank
6834a2e083 Spawning FARPS
**SPAWNSTATIC**
- Added event birth when static FARPS are spawned

**EVENT**
- Unknown airbases as inititiator are registered
2022-11-20 20:55:34 +01:00
Frank
2538d583ad Update Suppression.lua
- Fixed routing of group (passing waypoint function was triggered too early due to changed DCS behaviour)
2022-11-19 19:36:55 +01:00
Frank
c9ccc28251 Merge branch 'master' into FF/MasterDevel 2022-11-19 11:44:46 +01:00
Applevangelist
0f1ad9d811 # TaskRecoveryTanker 2022-11-18 11:28:52 +01:00
Applevangelist
93c986f00a #GROUP
* Added additional push of tanker task to GROUP:SetAsRecoveryTanker() for a working setup
2022-11-18 11:23:56 +01:00
Applevangelist
239e2ef86d #GROUP/CONTROLLABLE
* Added RecoveryTanker Task
2022-11-18 09:58:48 +01:00
Applevangelist
5a7a23552d #AIRBASE
* Added enumerators for
-- * AIRBASE.SouthAtlantic.Puerto_Santa_Cruz
-- * AIRBASE.SouthAtlantic.Comandante_Luis_Piedrabuena
-- * AIRBASE.SouthAtlantic.Aerodromo_De_Tolhuin
-- * AIRBASE.SouthAtlantic.Porvenir_Airfield
-- * AIRBASE.SouthAtlantic.Almirante_Schroeders
-- * AIRBASE.SouthAtlantic.Rio_Turbio
2022-11-17 17:13:33 +01:00
Applevangelist
4fb98cf72b #CSAR
* Make rescued pilot's weight configureable
2022-11-17 13:54:43 +01:00
Applevangelist
a125497fe7 #SPAWN
* Ensure we have a numbered table for InitRandomizeTemplate/Zone so math.random actually works
* Also, pre-shuffle tables
2022-11-16 11:12:47 +01:00
Applevangelist
ae2196585e #MESSAGE
* added to country
* added a couple of countries to country.id. enumerator
2022-11-16 09:39:54 +01:00
Applevangelist
fbad50973f # Bug fixes 2022-11-14 18:15:55 +01:00
Applevangelist
782cfd1fd0 #ATIS
* Added honor Stop() functionality
2022-11-14 17:36:34 +01:00
Applevangelist
d4a06089c9 #CTLD
* Change call order to move troops, vehicle on `onafter..` internally
* added pseudo-function for "OnBefore..."
2022-11-13 13:37:25 +01:00
Applevangelist
5c6efed995 Docu Headlines part II 2022-11-11 11:40:36 +01:00
Applevangelist
68a31b1eb7 Docu - Nicer Headlines 2022-11-11 09:35:54 +01:00
Applevangelist
f46f755327 #Docu fixes 2022-11-10 17:32:45 +01:00
Applevangelist
5d4f870a1d #Docu fixes 2022-11-10 17:29:40 +01:00
Applevangelist
08c5c38c7b Changes from last FF push 2022-11-09 16:09:05 +01:00
Applevangelist
0c9238651a * POINT
* Added option to get BR/BRA with add'l magnetic heading
2022-11-08 15:33:09 +01:00
Applevangelist
10a9b062b6 #MSRS
* Docu corrections
* Added `AddFrequencies()` and `AddModulations()`
2022-11-08 10:10:11 +01:00
Thomas
53c6a17ccb Fix CTLD Ship Zones
Fixes #1830
2022-11-07 19:11:08 +01:00
Applevangelist
c10617e1b0 #CTLD
* Fix for Bronco #1827
* Fix for Ship Zones
2022-11-07 17:37:02 +01:00
Thomas
be01567a1b CTLD - added Bronco (#1828)
Added data for the Bronco-OV-10A - needs further additions to ensure to work, as this is a plane, not a chopper #1827
2022-11-07 11:19:52 +01:00
TommyC81
88fc501c24 Update Range.lua (#1825)
Add meter text to RANGE bombing result.
2022-11-06 19:33:44 +01:00
Applevangelist
e93bb5ceba Merge remote-tracking branch 'origin/master' 2022-11-06 11:28:03 +01:00
Applevangelist
7983621f8e #SCORING
* Added option to switch AutoSave
2022-11-06 11:27:59 +01:00
Applevangelist
6c79d6b01f #SCORING
* Added option to switch AutoSave
2022-11-06 11:26:16 +01:00
Applevangelist
b2a351e67d UTILS 2022-11-05 14:03:38 +01:00
TommyC81
574fa8abf4 Documentation fixes. (#1820)
* Documentation fixes.

* Update Airboss.lua

Escape links.
2022-11-02 11:20:35 +01:00
Applevangelist
c4f4af5e5a #ZONE_POLYGON
* Scan for Scenery - changed scan strategy as box seems not to work properly all the time
2022-11-01 16:31:47 +01:00
Applevangelist
1da0792ed7 #CTLD
* added option for build time delay
2022-11-01 14:32:02 +01:00
Applevangelist
7d37acb1cd #CTLD_HERCULES
* Fix for `CTLD_HERCULES:Cargo_Track(cargo, initiator)` when flying very low
2022-10-31 16:06:30 +01:00
Applevangelist
fd230701e9 #EVENT
* Added events from 2.8
2022-10-31 15:43:13 +01:00
Applevangelist
91d492c579 #EVENT
* Added events from 2.8
2022-10-31 15:15:47 +01:00
Applevangelist
5eabe7e644 #GROUP
* Ensure also attribute "Tanks" is captured
2022-10-31 10:25:08 +01:00
TommyC81
368e720cab Documentation fixes. (#1816) 2022-10-29 10:07:50 +02:00
TommyC81
cba156b3dc Code formatting preparation. (#1817)
Use EmmyLuaCodeStyle that comes with "Lua" VS Code extension (https://marketplace.visualstudio.com/items?itemName=sumneko.lua). More features and configurability than LuaFormatter, and no need for additional extension (beyond "Lua").
Formatting file set up from default template with some tweaks to correspond to most common coding style observed in the code base. Further tweaks are likely required.
2022-10-29 10:07:01 +02:00
TommyC81
676bc0fef0 Documentation fixes. (#1815)
Minor documentation and code formatting fixes.
This is mostly intended to have something re-trigger the documentation generation to see if the filename capitalization is resolved.
2022-10-27 18:38:59 +02:00
TommyC81
7455c63716 Documentation fixes. (#1811)
Update documentation text and links.
Fix spelling errors.
Other minor adjustments where appropriate, such as remove whitespaces and format code.
2022-10-23 16:45:40 +02:00
TommyC81
81818705df Documentation fixes. (#1810)
Fix documentation references.
Correct spelling errors.
Remove empty whitespaces.
Correct a single mis-spelled ZONE_BASE variable, see 'Core/Zone.lua' (variable "Sureface" -> "Surface", no references to mis-spelled "Sureface" throughout the codebase).
Correct mis-spelling of "coaltion" in 'Functional/Mantis.lua', corrected to "coalition".
2022-10-22 11:07:58 +02:00
TommyC81
61ea484614 Documentation fixes. (#1809)
Fix incorrect references.
Simplify references within the same module.
Fix spelling errors and minor code formatting.
2022-10-21 13:52:09 +02:00
TommyC81
d5e8de4d74 Documentation fixes. (#1807)
Correct module links.
Replace references to missing images with MOOSE.JPG.
Add default MOOSE.JPG where no image was previously set.
Fix minor spelling error.
2022-10-21 06:10:34 +02:00
Applevangelist
b7d5144c91 #MANTIS
* Add systems from SMA mod
2022-10-20 11:39:51 +02:00
Applevangelist
7bba5ec69e #SCENERY, #SET_SCENERY
* Improvements
2022-10-19 12:37:24 +02:00
TommyC81
0441acf101 Documentation fixes. (#1803)
Improve the consistency of the module intros to the most commonly used version (single dash).

Add missing module information (abbreviated where none existed previously).

Fix broken documentation links

Make module names correspond to filenames (and fix links).

Fix typos.
2022-10-19 12:20:39 +02:00
Rolf Geuenich
89f0909a8f Fixed some errors in the documentation of Core MESSAGE (#1804)
Removed wrong forth parameter in most examples.
Removed duplicate of function ToUnit.
2022-10-19 06:32:57 +02:00
Applevangelist
ff0f1c4c24 #CTLD
* added a check for zones; not added when zone does not exist
2022-10-18 17:23:01 +02:00
Applevangelist
3a6c52ae73 #CSAR
* Added use of custom callsigns
* Added cargo weight for rescued pilots
* Improved o'clock calculation

#CTLD
* Docu additions
2022-10-18 16:59:19 +02:00
Applevangelist
7ec18ebf00 #SCENERY
* functional additions
#SET
* added SET_SCENERY
2022-10-17 18:06:29 +02:00
Thomas
66f52d41eb CTLD - No radius check (#1800)
Get rid of radius check in IsUnitInZone()
2022-10-17 12:33:17 +02:00
Applevangelist
1172cf0ae7 #GROUP
* Added function to obtain *average* Vec3 of the GROUP
* Added function to obtain *average* Coordinate of the GROUP
2022-10-16 13:36:21 +02:00
Applevangelist
1d296d1cf4 #CTLD
* Documentation additions
2022-10-14 16:50:31 +02:00
Applevangelist
a6bddc5aca #SET_UNIT - fix GetCoordinate() 2022-10-14 16:18:53 +02:00
Applevangelist
bdd40fdf7d #PLAYERTASK integration for CSAR and CTLD 2022-10-13 17:43:27 +02:00
Applevangelist
c84e153ff2 #SCENERY
* Improvements
2022-10-13 12:53:27 +02:00
Applevangelist
ab4c83d284 #SCENERY - Improvements 2022-10-13 10:49:17 +02:00
Applevangelist
cd99c053df #SCENERY
* Clarification in docu for :FindByName()
2022-10-12 16:21:55 +02:00
Applevangelist
3434ef47d9 #STATIC
* Add Life0 to STATIC Object
* Added functions for STATIC:GetLife() and STATIC:GetLife0()
2022-10-12 09:32:51 +02:00
Applevangelist
c5145a38e2 #Core.Point
* Cleanup GetClosestAirbase()
2022-10-10 17:42:02 +02:00
Applevangelist
38b9778e9a #SET
* minor enhancement
2022-10-09 12:53:45 +02:00
Jason du Plessis
2ca6168f47 #ZONE * Fixed ZONE_RADIUS Not being added to _Database (#1798)
* #ZONE * Fixed ZONE_RADIUS Not being added to _Database

Bugfix for #1797.

ZONE_RADIUS was not being registered within the _Database table, thus for CTLD, the Zone could not be found for moving units to ZONE_RADIUS zones. Because other Zone types inherit ZONE_RADIUS, I've added a RegisterZone parameter that is default to true, and modified the inheritance of the other Zone types to register the zone in the _Database depending if the zone does not get registered in that zone type.

* #ZONE * Updated ZONE_RADIUS RegisterZone to DoNotRegisterZone

Changed RegisterZone to DoNotRegisterZone and updated the different zone types values for RegisterZone.

Suggested by Applevangelist.

Co-authored-by: Jason du Plessis <jason.duplessis@ebtax.co.za>
2022-10-06 07:27:07 +02:00
Applevangelist
0da2299472 Docu changes 2022-10-05 07:32:17 +02:00
Frank
bf2ce3c4af AIRBOSS v1.3.0
- Copy from `develop` branch.
- Disabled AI handling as this is bugged (needs `FLIGHTGROUP` class)
2022-10-02 19:20:25 +02:00
Frank
c0f82eabb2 Server Name
**GLOBALS**
- Read out server name from `Config/serverSettings.lua`
**SOCKET**
- Added `server_name` to transmitted UDP data
2022-10-02 16:54:48 +02:00
Applevangelist
30aba1258d #POINT
* Added COORDINATE:ToStringFromRPShort
2022-10-02 13:14:57 +02:00
Applevangelist
f2ed920214 #SRSQUEUE, ATIS
* Added option to only transmit via SRS if there are active players
2022-10-01 11:57:22 +02:00
Applevangelist
2fc7139f6b #SET
* Added code to`SET:IsInSet(Object)` to be functional
2022-09-30 14:47:51 +02:00
Applevangelist
e8ace49e8b #SPOT - Set relative position to start lasing
#ZONE_POLYGON - Added `ZONE_POLYGON:NewFromPointsArray( ZoneName, PointsArray )`
2022-09-29 16:43:40 +02:00
Applevangelist
dddb9ff713 #ZONE_CAPTURE_COALITION - allow zone to be a ZONE_POLYGON 2022-09-28 11:49:06 +02:00
Applevangelist
f582f7df7c #ATIS
* Fixed SetILS report not working
* Use new AIRBASE additions to set takeoff/landing runway
* Fixed Visibility is reported twice
* Added SetReportmBar() to report for mBar/hpa QNH/QFE even if not metric
* Added option to output additional freetext information when using SRS SetAdditionalInformation()

#1792
2022-09-27 11:16:08 +02:00
Applevangelist
14c6d1be9b #1790 Fix for error in GetCustomCallsign if the player name contains a |-sign but no string at the end or just numbers 2022-09-27 09:27:59 +02:00
Frank
2157f8e8cd Airboss
Take animation for determining the wire
2022-09-26 23:34:00 +02:00
Applevangelist
0731e15385 #CSAR
* Added mycsar.ADFRadioPwr = 1000 for ADF beacon radio sending power
2022-09-26 17:01:18 +02:00
Applevangelist
9eb82060a5 SRS - corrected enumerator 2022-09-25 14:41:38 +02:00
Applevangelist
9f232ab5ec #ATIS - added Google TTS option 2022-09-25 14:41:19 +02:00
Applevangelist
ec0d164619 #MSRS - put "" around frequencies and modulations 2022-09-25 14:14:56 +02:00
Applevangelist
e804ab9254 Merge remote-tracking branch 'origin/master' 2022-09-24 10:10:06 +02:00
Applevangelist
25cb12a81a #FOX
* Added comfy function
2022-09-24 10:10:02 +02:00
Applevangelist
231acc7363 #RANGE
* added 2 more SRS outputs
2022-09-24 10:09:00 +02:00
Applevangelist
d8bdf6a8d3 #CSAR
* Added option to change top menu name
2022-09-23 10:28:35 +02:00
Applevangelist
d506067c72 #FOX
* Typo in Event Function fixed
2022-09-23 09:59:24 +02:00
Applevangelist
4483d3231a #CTLD
* Added: troop mass also checked when loading
2022-09-22 17:30:01 +02:00
Applevangelist
481ee186aa #AIRBOSS, #UTILS
* Alitude to Altitude
2022-09-22 15:49:31 +02:00
Thomas
8820b8a41c Nimitz deck height (#1787)
Corrected deck height of Nimitz class to 18.3m/60ft
2022-09-22 15:46:55 +02:00
Applevangelist
0971f8ffa7 #SPAWN
* Fix for InitCleanup()
2022-09-22 10:57:43 +02:00
Applevangelist
9739f94428 #ATIS
* Add MSRSQUEUE to avoid overlaps
2022-09-20 17:15:57 +02:00
Applevangelist
450f4eaec0 #AIRBASE, ATIS
* less noise in log

#SRS
* Added enumerator for voices MSRS.Voices.Microsoft... and MSRS.Voices.Google...

#RANGE
* Added SRS support, some bug fixing
2022-09-20 15:51:16 +02:00
Applevangelist
b5186fa328 #SETTINGS
* Fix IsImperial()
2022-09-20 15:26:34 +02:00
Applevangelist
526dc8363d #DESIGNATE
* Remove useless summary autolase report, restored single lines when lasing
2022-09-20 10:26:19 +02:00
Applevangelist
242d735d8c #DETECTION
* Make designate refer A2G coordinate system as set by user
2022-09-20 09:27:19 +02:00
Applevangelist
9fedd32e78 #UTILS
* Fix for group set reloading - thanks @BlueEcko!
2022-09-20 08:23:49 +02:00
Applevangelist
a146003a6a #DESIGNATE
* Some documentation clarifications
2022-09-19 18:25:54 +02:00
Applevangelist
d17df74191 ATIS
* Remove dupe inheritance of BASE
2022-09-19 13:20:45 +02:00
Applevangelist
d23403383f #CSAR
* Added option to use the OV-10A Bronco
* Slight changes to SRS approach
* Added door check in UTILS for Bronco
2022-09-19 11:43:50 +02:00
Applevangelist
877b36f8a8 #CSAR
* overcome error cannot get Vec3
2022-09-15 19:51:15 +02:00
Applevangelist
299820162b #ATIS
* No old-style radioqueue when using SRS
2022-09-15 14:01:23 +02:00
Applevangelist
5d20a59d92 #GROUP
* Customized Callsigns - remove extra spaces
2022-09-15 13:40:37 +02:00
Applevangelist
c1ab3b5476 #GROUP
* Fix ignore leading spaces in customized callsigns
2022-09-15 08:17:37 +02:00
Applevangelist
0076130483 #GROUP
* Docu clarifications
2022-09-13 10:51:25 +02:00
Applevangelist
a5194b1999 #GROUP
-- remove log output
2022-09-12 17:33:40 +02:00
Applevangelist
b53b1f083f #GROUP
* Fix IsPlayer
* Added `GROUP:GetCustomCallSign()`
2022-09-12 17:28:49 +02:00
Frank
994a36001f Merge pull request #1782 from FlightControl-Master/FF/MasterDevel
Updates from develop
2022-09-11 13:38:00 +02:00
Frank
26b91e3f66 Updates from develop
DCS.lua
Ops/ATIS.lua
Ops/RecoveryTanker.lua
Utilities/Enums.lua
2022-09-11 13:32:57 +02:00
Applevangelist
f42aedd485 #CONTROLLABLE
*Foreign class definiton removed
2022-09-10 15:14:28 +02:00
Applevangelist
a73818a615 #BUILD files 2022-09-10 12:08:30 +02:00
Applevangelist
7c22e9fe69 #Changes from Develop 2022-09-10 11:56:03 +02:00
Applevangelist
c58e91b956 #TASKING
* Merge changes from Develop
2022-09-10 11:41:41 +02:00
Applevangelist
a61059242c #AI
* Merge changes from DEV
2022-09-10 11:38:50 +02:00
Applevangelist
cea9437e66 #GROUP
Fix GetSpeedMax() always returns zero
2022-09-09 17:33:23 +02:00
Applevangelist
3f052ef1da #GROUP
* changes from Dev
#UNIT
* changes from Dev
2022-09-08 15:49:58 +02:00
Applevangelist
c137a4b06d Revert "#ZONE"
This reverts commit 1207417894.
2022-09-06 13:18:22 +02:00
Applevangelist
6c46dafc59 Revert "Auxiliary commit to revert individual files from 1207417894a369fa4a353ca0a1f6120c2fc32666"
This reverts commit 551d4a9c85c89e4aaad2c59ed058c84ff8c7ad6c.
2022-09-06 13:16:40 +02:00
Applevangelist
1207417894 #ZONE
* ZONE_ELASTIC
* Typo
2022-09-06 11:17:50 +02:00
Frank
7c717c219c Merge pull request #1771 from grandpaSam/master
Updated CARGO tag documentation
2022-09-01 22:54:22 +02:00
grandpaSam
a9fad53255 Updated CARGO tag documentation
The documentation for the CARGO tag on static objects omitted a required C= parameter. Without this parameter the database will not register the static as a CARGO object
2022-09-01 11:49:46 -07:00
Applevangelist
372cc021bd UNIT - added optical tracker to HasSEAD() 2022-09-01 08:36:40 +02:00
Applevangelist
6820774266 GROUP - added optical tracker to ID SAMs 2022-09-01 08:03:53 +02:00
Applevangelist
a0c189a349 UTILS - added NDB 305kHz in frequency generator 2022-08-31 18:11:55 +02:00
Applevangelist
1060ff16d8 GROUP - Attributes aligned to dev 2022-08-31 17:40:44 +02:00
Applevangelist
1255f48645 Update Moose.files 2022-08-30 14:38:36 +02:00
Applevangelist
7ef69208d4 #ZONE_RADIUS
* Added `ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(...)`

#MARKEROPS_BASE
* Added Class
2022-08-30 14:36:42 +02:00
Applevangelist
5277cca4e1 Range fix issue #1753 (#1767)
Issue #1753 - when using `AddBombingTargets` the randommove flag was not passed to `AddBombingTargetUnit` for a group object
2022-08-25 16:00:47 +02:00
Applevangelist
aaf77815ca #SRS
* Added MSRSQUEUE message queue
2022-08-25 10:50:45 +02:00
Applevangelist
3f488cc091 #AIRBASE
* Added 3 new airports to the enumerator SouthAtlantic
2022-08-25 10:38:44 +02:00
Applevangelist
bdbbdfe60e #RANGE
* Added Instructor and RangeControl radio info on F10
2022-08-23 09:58:50 +02:00
Applevangelist
d15c2be2d0 Range fixes (#1764)
* Fixed some typos in self.PlayerSettings
* Fixed result messages
* Thanks for [JFG] Caponi
2022-08-22 17:03:33 +02:00
Applevangelist
73994914eb Fix for #1763 UTILS.GetOSTime() (#1765)
* Fix GetOSTime fixes #1763
2022-08-22 17:03:21 +02:00
Applevangelist
63b0dae794 Update ATC_Ground.lua (#1766)
* Make ATC_Ground work on any map
2022-08-22 17:03:02 +02:00
Engines
003e865ff7 Late break bug (#1762)
* Quick update to Airboss.lua to inluce Invincible.  Not yet tested.

* Initial quick test and calibration of the landing spot; looks good.  More testing required.

* Recompiled the Moose.lua based off the last commit.

* Removing surpurflous Moose.lua for pull request.

* There was an error with the late break paratemers on the Brit carriers causing pilots to be thrown off the pattern on the break.  Fixed, tested on Invicible, working as anticipated now.

* Removing the test Moose.lua (again).

* Fixing merge confilict

* Trying again.
2022-08-15 07:15:15 +02:00
Gavin Edwards
e3c03287b7 Engines invincible (#1758)
* Quick update to Airboss.lua to inluce Invincible.  Not yet tested.

* Initial quick test and calibration of the landing spot; looks good.  More testing required.

* Recompiled the Moose.lua based off the last commit.

* Removing surpurflous Moose.lua for pull request.
2022-08-13 21:40:53 +02:00
Applevangelist
73493c3a23 Update Unit.lua (#1751) 2022-07-31 09:17:36 +02:00
Applevangelist
562a3f6208 SET - fix for dead units 2022-07-29 08:51:23 +02:00
Applevangelist
3c5f3d6c37 UNIT - added get altitude function with AGL option 2022-07-25 08:12:28 +02:00
Applevangelist
a37d4214c0 SET - fix for left over self:I() 2022-07-22 11:06:55 +02:00
Applevangelist
636d6ce324 AIRBASE - Added 2 AFB on the Falklands Map (#1748)
--@field MarianaIslands
AIRBASE.SouthAtlantic={
  ["Port_Stanley"]="Port Stanley",
  ["Mount_Pleasant"]="Mount Pleasant",
  ["San_Carlos_FOB"]="San Carlos FOB",
  ["Rio_Grande"]="Rio Grande",
  ["Rio_Gallegos"]="Rio Gallegos",
  ["Ushuaia"]="Ushuaia",
  ["Ushuaia_Helo_Port"]="Ushuaia Helo Port",
  ["Punta_Arenas"]="Punta Arenas",
  ["Pampa_Guanaco"]="Pampa Guanaco",
  ["San_Julian"]="San Julian",
}
2022-07-22 11:02:55 +02:00
Applevangelist
eef8b362d2 Beacon - added deactivate Link4 2022-07-19 08:29:43 +02:00
Applevangelist
4ae586ebaa Controllable/Beacon - added function to switch on Link4
A2A - typo in documentation
2022-07-19 08:10:54 +02:00
rfdazzle
f6e673c2bb Fixed ATIS TTS readouts for wind direction & TACAN (#1739)
Added a substitution that takes effect when self.useSRS which converts wind direction into aviation-speak, e.g. "Zero Seven One" instead of the previous behaviour which was "Zero Seventy-One".

Updated TACAN TTS string to include the 'Ray' in 'X-Ray' when SRS is in use
2022-07-07 08:41:56 +02:00
Frank
183a60159c Update Range.lua
- Fixed bug for strafing
2022-07-01 23:05:26 +02:00
Chump
1fdf4f371d Fix for issues #1735 & #1736 (#1737)
* Update Database.lua

Remove duplicate function

* Update PseudoATC.lua

Added nil check
2022-06-26 21:11:49 +01:00
Applevangelist
f50c374d04 CSAR - hand back descriptive name as 3rd parameter on event Boarded() 2022-06-25 17:24:56 +02:00
Applevangelist
d59fc331f6 UTILS - Fix for Gazelle Door Check 2022-06-25 14:27:51 +02:00
Applevangelist
b83f478294 CSAR - fix for oncrash 2022-06-16 15:42:02 +02:00
Applevangelist
d5636f4a19 CSAR - added event "Landed" (at a friendly/neutral AFB), fix for AFB rescue 2022-06-16 13:41:44 +02:00
Applevangelist
196bcf39cf Added Falklands map stuff 2022-06-14 16:56:33 +02:00
Applevangelist
afec1c3a5b COORDINATE - additions to BRAANATO 2022-06-14 13:06:55 +02:00
Applevangelist
6025339b46 CSAR - some fixes for latest open beta 2022-06-14 12:39:17 +02:00
Applevangelist
40c6cc59d3 Update Beacon.lua 2022-06-13 15:43:10 +02:00
Applevangelist
514e568e04 Update Beacon.lua 2022-06-13 15:39:39 +02:00
Applevangelist
2f34b0a5ed Update Beacon.lua (#1734) 2022-06-13 15:34:01 +02:00
Applevangelist
708c076885 CSAR - Put wounded group back into status green, so they run to the chopper 2022-06-12 12:47:23 +02:00
Applevangelist
f0e0b918af CTLD - More docu 2022-06-09 12:12:29 +02:00
Applevangelist
e45f5e1122 CTLD - further documentation 2022-06-09 11:46:29 +02:00
Applevangelist
932015668b Added documentation for CTLD_HERCULES 2022-06-09 10:56:20 +02:00
Applevangelist
4011bc3fe6 AIRBASE added South Atlantic 2022-06-08 20:24:45 +02:00
Applevangelist
1dcccdc434 CSAR - added a couple of more lines to go out via TTS 2022-06-07 08:56:13 +02:00
Applevangelist
3380ed9360 CSAR - added options to use Google TTS 2022-06-07 08:13:19 +02:00
Applevangelist
ed9c14e63d GROUP - changes in GetDCSGroup 2022-05-22 12:06:49 +02:00
Applevangelist
4762793adc Create README.md 2022-05-22 11:16:16 +02:00
Frank
7df3946189 Update Airboss.lua
- Wind is calculated at 15 m (not 50 m)
2022-05-20 20:16:45 +02:00
Applevangelist
d5fb75fe43 Positionable - added 6 passengers (cargo weight) to Toyota HL/LC new with 2.7.2 OB 2022-05-14 13:11:30 +02:00
Applevangelist
c0b32a5584 SRS - added hints on using Google with TTS 2022-05-13 16:37:41 +02:00
Applevangelist
e2b1276d7b Point - added option to add an SSML tag to ToStringBRAANATO 2022-05-12 14:49:30 +02:00
Applevangelist
f6aea13fae SRS - put volume in "" - just in case 2022-05-11 07:30:26 +02:00
Applevangelist
646b113c55 SRS - actually pass the volume to the command line 2022-05-11 06:19:09 +02:00
Applevangelist
58074f499f AIRBASE - corrected ["Deir_ez_Zor"] = "Deir ez-Zor" (minus doesn't work in enum) 2022-05-10 19:40:32 +02:00
Applevangelist
1483ffd7ff Correct link to demo missions 2022-05-10 16:18:00 +02:00
476th-Scaley
cdaef851a0 Update AI_Air.lua (#1729)
* Update AI_Air.lua

Altered RTB airspeed (slower) and target altitude over the airfield being returned to (higher) to produce more realistic and fuel efficient descent profiles. Leads to aircraft arriving overhead the airfield quite high and generally flying one orbit to descend to land. 

Scaley

* Create AI_Air.lua

Co-authored-by: Applevangelist <72444570+Applevangelist@users.noreply.github.com>
2022-05-10 10:10:41 +02:00
Applevangelist
04068d7117 Group - small change 2022-05-07 19:42:31 +02:00
Applevangelist
41e8ddea8c GROUP - change to GetUnits(n) to make it more robust, now returns first alive unit,actually. Similar changes to GetHeading() 2022-05-07 11:54:33 +02:00
Applevangelist
cc49791997 Fallout fixes 2022-05-06 11:45:11 +02:00
Applevangelist
ba4a8050ba AI Patrol - life check on route 2022-05-06 10:36:39 +02:00
Applevangelist
7c5067a59a UNIT Register - small fix for trains 2022-05-06 08:01:31 +02:00
Applevangelist
69eb920173 AI_CAP - more fallout from the dead units in the API 2022-05-05 17:40:00 +02:00
Applevangelist
07d761941a AI_AIR - restrict AB on RTB 2022-05-05 16:41:32 +02:00
Applevangelist
ca52585759 AI/ZONE - Some fixes for units unreachable 2022-05-05 12:07:56 +02:00
Applevangelist
decc9d09f8 docu update 2022-05-05 11:34:14 +02:00
Applevangelist
466a18447c SRS - adding volume setting and a test on OS and IO available 2022-05-05 08:57:27 +02:00
Frank
27902ee107 Update Range.lua
**RANGE**
- Fixed a couple of bugs
- Added new FSM events for strafing
- Updated docs
2022-05-04 22:36:41 +02:00
Applevangelist
8a8b806362 further event related stuff not working any more 2022-05-04 18:09:56 +02:00
Applevangelist
40bb181c78 Another nil check... 2022-05-04 13:29:40 +02:00
Applevangelist
8099847e29 Fixes for DEAD event and extra nil checks 2022-05-04 10:25:50 +02:00
Applevangelist
6e8edd95ec GROUP - making GetCoordinate() a bit more resilient
POINT - slight changes to ToStringBRAANATO
2022-04-29 18:48:41 +02:00
Applevangelist
5112c9598b COORDINATE - added bogey option to COORDINATE:ToStringBRAANATO(FromCoordinate,Bogey,Spades) 2022-04-29 12:18:26 +02:00
Applevangelist
749158c086 Range added nil check 2022-04-29 12:09:57 +02:00
Applevangelist
b3d4024f21 Nicefy docs 2022-04-28 17:10:26 +02:00
Applevangelist
c283b66c1d added MESSAGE:ToUnit), altered MESSAGE:ToClient() accordingly 2022-04-28 16:58:37 +02:00
Applevangelist
3209843318 Added POSITIONABLE:MessageToSetUnit and POSITIONABLE:MessageToUnit 2022-04-28 16:55:31 +02:00
Applevangelist
d35e5cc0f7 Added USERSOUND:ToUnit 2022-04-28 16:50:59 +02:00
Applevangelist
e5eeb592a2 Enums - corrected Hawkeye, added Super_Hercules 2022-04-28 13:10:34 +02:00
Applevangelist
3d38f4d17a Enums - added a couple of names 2022-04-26 10:08:50 +02:00
Applevangelist
2d91647e0b QOL changes from DEVELOP 2022-04-25 10:36:36 +02:00
Applevangelist
cac0f30673 Update Moose.files (#1717) 2022-04-25 10:35:38 +02:00
Applevangelist
c5ecba3389 Menu cleanup for Refresh() 2022-04-24 14:05:45 +02:00
Applevangelist
e0397dff47 GROUP - overwrite GetHeight() inherited from POSITIONABLE with something that is actually working for groups 2022-04-24 13:50:07 +02:00
Applevangelist
e08fb2e972 Make BRAA heading a 3digit number 2022-04-22 13:31:58 +02:00
Applevangelist
c02ae82003 Point - added ToStringBRAANATO 2022-04-21 18:59:31 +02:00
Applevangelist
e6fc301b0d Utils Typo 2022-04-20 19:14:36 +02:00
Applevangelist
a385ed57fb ZONE - added example to Scan, some minor changes
SET_GROUP - clarified return value to be a table, not a SET
2022-04-20 14:03:18 +02:00
Applevangelist
09dafe4b1d UTILS - added BearingToCardinal, ToStringBRAANATO 2022-04-20 14:02:16 +02:00
Applevangelist
ba8505c983 FIFO 2022-04-14 15:54:38 +02:00
Applevangelist
fba359d389 LIFO/FIFO enforce unique id 2022-04-14 15:06:14 +02:00
Applevangelist
c56763b68f FIFO:HasUniqueID(UniqueID) 2022-04-14 11:11:33 +02:00
Applevangelist
03c2943545 Nicefy docs 2022-04-14 08:53:37 +02:00
Applevangelist
98039b9048 UTISL - FiFo/LiFo stacks 2022-04-14 08:12:29 +02:00
Applevangelist
5065d3b068 UTILS - added FiFo
CTLD - correct imperial hover check messages
2022-04-13 16:13:39 +02:00
Applevangelist
6828f7e262 UTILS - corrected door check for AH64 2022-04-12 08:23:18 +02:00
Applevangelist
a685f3ffbd MSRS - honor port setting and coalition. Repaired command string for .bat sound file production
Added mission slash in SOUNDFILE
2022-04-10 18:29:09 +02:00
Applevangelist
e84156d2e9 SRS - add port to docu 2022-04-10 10:18:12 +02:00
Applevangelist
0ce1c31e1c CSAR - added SRS port option 2022-04-10 09:29:22 +02:00
Applevangelist
f7e14bb60c SET - logic correction in :Remove() 2022-04-08 11:27:31 +02:00
Penecruz
7b907df816 Airboss V/STOL Case III and grading (#1708)
* Update Airboss.lua

* Fix syntax error

C
2022-04-08 07:16:23 +02:00
Frank
7f5be2829c Update Database.lua
- Template statics saved under first statics unit name so they can be found.
2022-04-04 16:50:17 +02:00
Applevangelist
fa5afae783 A2A Dispatcher - nil checks to evade dead units 2022-04-04 12:58:32 +02:00
Frank
b17507d0fa Update SpawnStatic.lua
- Fixed isCargo flag not honored.
2022-04-04 12:18:45 +02:00
Applevangelist
5ed43a3190 CTLD - build object at the 1st crate location not randomly around helo 2022-04-04 11:31:35 +02:00
Applevangelist
d2a5144a23 AIRBASE, added ["Deir_ez-Zor"] = "Deir ez-Zor", 2022-03-30 12:06:25 +02:00
Applevangelist
fb2031d7ca docu nicefy 2022-03-29 12:01:28 +02:00
Applevangelist
4a42571925 CTLD - corrected error in setting Hercules min and max drop height, added documentation 2022-03-29 08:52:33 +02:00
Applevangelist
38413625c2 AIRBASE - Add'l AB for the Channels map 2022-03-28 10:12:39 +02:00
Frank
2d544b7a98 Merge pull request #1698 from gavinedwards/airboss.hermes.include
Added Hermes section to Airboss.  Will require tuning.
2022-03-26 23:34:54 +01:00
Lt Cdr Gavin Edwards
b1e5e1840e Adding leading lines that I accidentally truncated. 2022-03-26 15:23:38 -07:00
Lt Cdr Gavin Edwards
e6f9b4a125 Added Hermes section to Airboss. Will require tuning. 2022-03-26 11:54:53 -07:00
141 changed files with 25207 additions and 11571 deletions

10
.vs/VSWorkspaceState.json Normal file
View File

@@ -0,0 +1,10 @@
{
"ExpandedNodes": [
"",
"\\Moose Development",
"\\Moose Development\\Moose",
"\\Moose Development\\Moose\\Ops"
],
"SelectedNode": "\\Moose Development\\Moose\\Ops\\Airboss.lua",
"PreviewInSolutionExplorer": false
}

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View File

@@ -0,0 +1,166 @@
# Repository: https://github.com/CppCXY/EmmyLuaCodeStyle
# English documentation: https://github.com/CppCXY/EmmyLuaCodeStyle/blob/master/README_EN.md
[*.lua]
# [basic]
# optional space/tab
indent_style = space
# if indent_style is space, this is valid
indent_size = 4
# if indent_style is tab, this is valid
tab_width = 4
# none/single/double
quote_style = none
# only support number
continuation_indent_size = 0
# optional crlf/lf/cr/auto, if it is 'auto', in windows it is crlf other platforms are lf
end_of_line = auto
detect_end_of_line = false
# this mean utf8 length , if this is 'unset' then the line width is no longer checked
# this option decides when to chopdown the code
max_line_length = 9999
# this will check text end with new line
insert_final_newline = true
# [function]
# function call expression's args will align to first arg
# optional true/false/only_after_more_indention_statement/only_not_exist_cross_row_expression
align_call_args = false
# if true, all function define params will align to first param
align_function_define_params = true
remove_expression_list_finish_comma = true
# keep/remove/remove_table_only/remove_string_only/unambiguous_remove_string_only
call_arg_parentheses = keep
# [table]
#optional none/comma/semicolon
table_separator_style = none
#optional keep/never/always/smart
trailing_table_separator = keep
# see document for detail
continuous_assign_table_field_align_to_equal_sign = true
# if true, format like this "local t = { 1, 2, 3 }"
keep_one_space_between_table_and_bracket = true
# if indent_style is tab, this option is invalid
align_table_field_to_first_field = true
# [statement]
align_chained_expression_statement = false
# continous line distance
max_continuous_line_distance = 1
# see document for detail
continuous_assign_statement_align_to_equal_sign = true
# if statement will align like switch case
if_condition_align_with_each_other = false
# if true, continuation_indent_size for local or assign statement is invalid
# however, if the expression list has cross row expression, it will not be aligned to the first expression
local_assign_continuation_align_to_first_expression = false
statement_inline_comment_space = 1
# [indentation]
# if true, the label loses its current indentation
label_no_indent = false
# if true, there will be no indentation in the do statement
do_statement_no_indent = false
# if true, the conditional expression of the if statement will not be a continuation line indent
if_condition_no_continuation_indent = false
if_branch_comments_after_block_no_indent = false
# [space]
# if true, t[#t+1] will not space wrapper '+'
table_append_expression_no_space = false
long_chain_expression_allow_one_space_after_colon = false
remove_empty_header_and_footer_lines_in_function = true
space_before_function_open_parenthesis = false
space_inside_function_call_parentheses = false
space_inside_function_param_list_parentheses = false
space_before_open_square_bracket = false
space_inside_square_brackets = false
# if true, ormat like this "local t <const> = 1"
keep_one_space_between_namedef_and_attribute = true
# [row_layout]
# The following configuration supports four expressions
# minLine:${n}
# keepLine
# keepLine:${n}
# maxLine:${n}
keep_line_after_if_statement = minLine:0
keep_line_after_do_statement = minLine:0
keep_line_after_while_statement = minLine:0
keep_line_after_repeat_statement = minLine:0
keep_line_after_for_statement = minLine:0
keep_line_after_local_or_assign_statement = keepLine
keep_line_after_function_define_statement = keepLine:1
keep_line_after_expression_statement = keepLine
# [diagnostic]
# the following is code diagnostic options
enable_check_codestyle = true
# [diagnostic.name_style]
enable_name_style_check = false
# the following is name style check rule
# base option off/camel_case/snake_case/upper_snake_case/pascal_case/same(filename/first_param/'<const string>', snake_case/pascal_case/camel_case)
# all option can use '|' represent or
# for example:
# snake_case | upper_snake_case
# same(first_param, snake_case)
# same('m')
local_name_define_style = snake_case
function_param_name_style = snake_case
function_name_define_style = snake_case
local_function_name_define_style = snake_case
table_field_name_define_style = snake_case
global_variable_name_define_style = snake_case|upper_snake_case
module_name_define_style = same('m')|same(filename, snake_case)
require_module_name_style = same(first_param, snake_case)
class_name_define_style = same(filename, snake_case)

View File

@@ -1,33 +0,0 @@
# See https://github.com/Koihik/LuaFormatter
# Use '-- LuaFormatter off' and '-- LuaFormatter on' around code blocks to inhibit formatting
column_limit: 500
indent_width: 2
use_tab: false
continuation_indent_width: 2
keep_simple_control_block_one_line: false
keep_simple_function_one_line: false
align_args: true
break_after_functioncall_lp: false
break_before_functioncall_rp: false
align_parameter: true
chop_down_parameter: true
break_after_functiondef_lp: false
break_before_functiondef_rp: false
align_table_field: true
break_after_table_lb: true
break_before_table_rb: true
chop_down_table: true
chop_down_kv_table: true
column_table_limit: 500
table_sep: ','
extra_sep_at_table_end: true
break_after_operator: true
single_quote_to_double_quote: false
double_quote_to_single_quote: false
spaces_before_call: 1
spaces_inside_functiondef_parens: true
spaces_inside_functioncall_parens: true
spaces_inside_table_braces: true
spaces_around_equals_in_field: true
line_breaks_after_function_body: 1

View File

@@ -0,0 +1,17 @@
{
"Lua.workspace.preloadFileSize": 1000,
"Lua.diagnostics.disable": [
"undefined-doc-name"
],
"Lua.diagnostics.globals": [
"BASE",
"lfs",
"__Moose",
"trigger",
"coord",
"missionCommands"
],
"Lua.completion.displayContext": 5,
"Lua.runtime.version": "Lua 5.1",
"Lua.completion.callSnippet": "Both"
}

View File

@@ -1,4 +1,6 @@
--- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes. --- **AI** - Models the process of Combat Air Patrol (CAP) for airplanes.
--
-- This is a class used in the @{AI.AI_A2A_Dispatcher}.
-- --
-- === -- ===
-- --
@@ -13,8 +15,7 @@
-- @extends AI.AI_Air_Patrol#AI_AIR_PATROL -- @extends AI.AI_Air_Patrol#AI_AIR_PATROL
-- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE -- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE
--- The AI_A2A_CAP class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
@@ -81,15 +82,15 @@
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone. -- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI. -- The range is applied at the position of the AI.
-- Use the method @{AI.AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range. -- Use the method @{#AI_A2A_CAP.SetEngageRange}() to define that range.
-- --
-- ## 4. Set the Zone of Engagement -- ## 4. Set the Zone of Engagement
-- --
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) -- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
-- --
-- An optional @{Zone} can be set, -- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone. -- Use the method @{#AI_A2A_CAP.SetEngageZone}() to define that Zone.
-- --
-- === -- ===
-- --
@@ -106,7 +107,7 @@ AI_A2A_CAP = {
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO". -- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
@@ -132,7 +133,7 @@ end
--- Creates a new AI_A2A_CAP object --- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self -- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap -- @param Wrapper.Group#GROUP AICap
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -191,7 +192,7 @@ end
--- Evaluate the attack and create an AttackUnitTask list. --- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2A_CAP self -- @param #AI_A2A_CAP self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. -- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets. -- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2A_CAP self -- @return #AI_A2A_CAP self
function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )

View File

@@ -1,4 +1,4 @@
--- **AI** - (R2.2) - Manages the process of an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI. --- **AI** - Manages the process of an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI.
-- --
-- === -- ===
-- --
@@ -57,8 +57,8 @@
-- --
-- ## 2. Which type of EWR will I setup? Grouping based per AREA, per TYPE or per UNIT? (Later others will follow). -- ## 2. Which type of EWR will I setup? Grouping based per AREA, per TYPE or per UNIT? (Later others will follow).
-- --
-- The MOOSE framework leverages the @{Detection} classes to perform the EWR detection. -- The MOOSE framework leverages the @{Functional.Detection} classes to perform the EWR detection.
-- Several types of @{Detection} classes exist, and the most common characteristics of these classes is that they: -- Several types of @{Functional.Detection} classes exist, and the most common characteristics of these classes is that they:
-- --
-- * Perform detections from multiple FACs as one co-operating entity. -- * Perform detections from multiple FACs as one co-operating entity.
-- * Communicate with a Head Quarters, which consolidates each detection. -- * Communicate with a Head Quarters, which consolidates each detection.
@@ -126,7 +126,7 @@
-- * polygon zones -- * polygon zones
-- * moving zones -- * moving zones
-- --
-- Depending on the type of zone selected, a different @{Zone} object needs to be created from a ZONE_ class. -- Depending on the type of zone selected, a different @{Core.Zone} object needs to be created from a ZONE_ class.
-- --
-- ## 14. For each Squadron doing CAP, what are the time intervals and CAP amounts to be performed? -- ## 14. For each Squadron doing CAP, what are the time intervals and CAP amounts to be performed?
-- --
@@ -356,7 +356,7 @@ do -- AI_A2A_DISPATCHER
-- --
-- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia9.JPG) -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia9.JPG)
-- --
-- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. -- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE}.
-- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than
-- it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. -- it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are.
-- In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. -- In a hot war the borders are effectively defined by the ground based radar coverage of a coalition.
@@ -425,7 +425,7 @@ do -- AI_A2A_DISPATCHER
-- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield.
-- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield.
-- --
-- **The default landing method is to spawn new aircraft directly in the air.** -- **The default take-off method is to spawn new aircraft directly in the air.**
-- --
-- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. -- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency.
-- The more and the longer aircraft need to taxi at an airfield, the more risk there is that: -- The more and the longer aircraft need to taxi at an airfield, the more risk there is that:
@@ -592,7 +592,7 @@ do -- AI_A2A_DISPATCHER
-- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" )
-- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
-- --
-- Note the different @{Zone} MOOSE classes being used to create zones of different types. Please click the @{Zone} link for more information about the different zone types. -- Note the different @{Core.Zone} MOOSE classes being used to create zones of different types. Please click the @{Core.Zone} link for more information about the different zone types.
-- Zones can be circles, can be setup in the mission editor using trigger zones, but can also be setup in the mission editor as polygons and in this case GROUP objects are being used! -- Zones can be circles, can be setup in the mission editor using trigger zones, but can also be setup in the mission editor as polygons and in this case GROUP objects are being used!
-- --
-- ## 7.2. Set the squadron to execute CAP: -- ## 7.2. Set the squadron to execute CAP:
@@ -882,7 +882,8 @@ do -- AI_A2A_DISPATCHER
-- @type AI_A2A_DISPATCHER.Takeoff -- @type AI_A2A_DISPATCHER.Takeoff
-- @extends Wrapper.Group#GROUP.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff
--- @field #AI_A2A_DISPATCHER.Takeoff Takeoff ---
-- @field #AI_A2A_DISPATCHER.Takeoff Takeoff
AI_A2A_DISPATCHER.Takeoff = GROUP.Takeoff AI_A2A_DISPATCHER.Takeoff = GROUP.Takeoff
--- Defines Landing type/location. --- Defines Landing type/location.
@@ -928,6 +929,8 @@ do -- AI_A2A_DISPATCHER
self.DefenderTasks = {} -- The Defenders Tasks. self.DefenderTasks = {} -- The Defenders Tasks.
self.DefenderDefault = {} -- The Defender Default Settings over all Squadrons. self.DefenderDefault = {} -- The Defender Default Settings over all Squadrons.
self.SetSendPlayerMessages = false --#boolean Flash messages to player
-- TODO: Check detection through radar. -- TODO: Check detection through radar.
self.Detection:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) self.Detection:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
-- self.Detection:InitDetectRadar( true ) -- self.Detection:InitDetectRadar( true )
@@ -1145,7 +1148,7 @@ do -- AI_A2A_DISPATCHER
self:I( "Captured " .. AirbaseName ) self:I( "Captured " .. AirbaseName )
-- Now search for all squadrons located at the airbase, and sanatize them. -- Now search for all squadrons located at the airbase, and sanitize them.
for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do
if Squadron.AirbaseName == AirbaseName then if Squadron.AirbaseName == AirbaseName then
Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning. Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning.
@@ -1301,7 +1304,7 @@ do -- AI_A2A_DISPATCHER
--- Define a border area to simulate a **cold war** scenario. --- Define a border area to simulate a **cold war** scenario.
-- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border.
-- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it.
-- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this. -- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this.
-- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1 -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1
-- @param #AI_A2A_DISPATCHER self -- @param #AI_A2A_DISPATCHER self
-- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE.
@@ -1685,6 +1688,20 @@ do -- AI_A2A_DISPATCHER
return DefenderSquadron return DefenderSquadron
end end
--- Get a resource count from a specific squadron
-- @param #AI_A2A_DISPATCHER self
-- @param #string Squadron Name of the squadron.
-- @return #number Number of airframes available or nil if the squadron does not exist
function AI_A2A_DISPATCHER:QuerySquadron(Squadron)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount))
return Squadron.ResourceCount
end
self:F({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
return nil
end
--- [DEPRECATED - Might create problems launching planes] Set the Squadron visible before startup of the dispatcher. --- [DEPRECATED - Might create problems launching planes] Set the Squadron visible before startup of the dispatcher.
-- All planes will be spawned as uncontrolled on the parking spot. -- All planes will be spawned as uncontrolled on the parking spot.
-- They will lock the parking spot. -- They will lock the parking spot.
@@ -1710,7 +1727,7 @@ do -- AI_A2A_DISPATCHER
-- Get free parking for fighter aircraft. -- Get free parking for fighter aircraft.
local nfreeparking = DefenderSquadron.Airbase:GetFreeParkingSpotsNumber( AIRBASE.TerminalType.FighterAircraft, true ) local nfreeparking = DefenderSquadron.Airbase:GetFreeParkingSpotsNumber( AIRBASE.TerminalType.FighterAircraft, true )
-- Take number of free parking spots if no resource count was specifed. -- Take number of free parking spots if no resource count was specified.
DefenderSquadron.ResourceCount = DefenderSquadron.ResourceCount or nfreeparking DefenderSquadron.ResourceCount = DefenderSquadron.ResourceCount or nfreeparking
-- Check that resource count is not larger than free parking spots. -- Check that resource count is not larger than free parking spots.
@@ -1755,7 +1772,7 @@ do -- AI_A2A_DISPATCHER
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param #number EngageAltType The altitude type to engage, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude. -- @param #number EngageAltType The altitude type to engage, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed.
-- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed. -- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed.
-- @param #number PatrolFloorAltitude The minimum altitude at which the cap can be executed. -- @param #number PatrolFloorAltitude The minimum altitude at which the cap can be executed.
@@ -1822,7 +1839,7 @@ do -- AI_A2A_DISPATCHER
--- Set a CAP for a Squadron. --- Set a CAP for a Squadron.
-- @param #AI_A2A_DISPATCHER self -- @param #AI_A2A_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed.
-- @param #number PatrolFloorAltitude The minimum altitude at which the cap can be executed. -- @param #number PatrolFloorAltitude The minimum altitude at which the cap can be executed.
-- @param #number PatrolCeilingAltitude the maximum altitude at which the cap can be executed. -- @param #number PatrolCeilingAltitude the maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed.
@@ -2319,6 +2336,13 @@ do -- AI_A2A_DISPATCHER
return self return self
end end
--- Set flashing player messages on or off
-- @param #AI_A2A_DISPATCHER self
-- @param #boolean onoff Set messages on (true) or off (false)
function AI_A2A_DISPATCHER:SetSendMessages( onoff )
self.SetSendPlayerMessages = onoff
end
--- Sets flights to take-off in the air, as part of the defense system. --- Sets flights to take-off in the air, as part of the defense system.
-- @param #AI_A2A_DISPATCHER self -- @param #AI_A2A_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
@@ -2971,7 +2995,20 @@ do -- AI_A2A_DISPATCHER
for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do
-- We only allow to ENGAGE targets as long as the Units on both sides are balanced. -- We only allow to ENGAGE targets as long as the Units on both sides are balanced.
if AttackerCount > DefenderCount then if AttackerCount > DefenderCount then
local Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP --self:I("***** AI_A2A_DISPATCHER:CountDefendersToBeEngaged() *****\nThis is supposed to be a UNIT:")
if AIFriendly then
local classname = AIFriendly.ClassName or "No Class Name"
local unitname = AIFriendly.IdentifiableName or "No Unit Name"
--self:I("Class Name: " .. classname)
--self:I("Unit Name: " .. unitname)
--self:I({AIFriendly})
end
local Friendly = nil
if AIFriendly and AIFriendly:IsAlive() then
--self:I("AIFriendly alive, getting GROUP")
Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP
end
if Friendly and Friendly:IsAlive() then if Friendly and Friendly:IsAlive() then
-- Ok, so we have a friendly near the potential target. -- Ok, so we have a friendly near the potential target.
-- Now we need to check if the AIGroup has a Task. -- Now we need to check if the AIGroup has a Task.
@@ -3175,7 +3212,9 @@ do -- AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron then if Squadron then
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " Wheels up.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " Wheels up.", DefenderGroup )
end
AI_A2A_Fsm:__Patrol( 2 ) -- Start Patrolling AI_A2A_Fsm:__Patrol( 2 ) -- Start Patrolling
end end
end end
@@ -3187,9 +3226,9 @@ do -- AI_A2A_DISPATCHER
self:GetParent( self ).onafterPatrolRoute( self, DefenderGroup, From, Event, To ) self:GetParent( self ).onafterPatrolRoute( self, DefenderGroup, From, Event, To )
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron then if Squadron and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", patrolling.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", patrolling.", DefenderGroup )
end end
@@ -3206,7 +3245,7 @@ do -- AI_A2A_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron then if Squadron and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup )
end end
Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
@@ -3391,10 +3430,10 @@ do -- AI_A2A_DISPATCHER
local DefenderTarget = Dispatcher:GetDefenderTaskTarget( DefenderGroup ) local DefenderTarget = Dispatcher:GetDefenderTaskTarget( DefenderGroup )
if DefenderTarget then if DefenderTarget then
if Squadron.Language == "EN" then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " wheels up.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " wheels up.", DefenderGroup )
elseif Squadron.Language == "RU" then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " колеса вверх.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " колёса вверх.", DefenderGroup )
end end
-- Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit -- Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit
Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit
@@ -3412,11 +3451,11 @@ do -- AI_A2A_DISPATCHER
local FirstUnit = AttackSetUnit:GetFirst() local FirstUnit = AttackSetUnit:GetFirst()
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
if Squadron.Language == "EN" then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", intercepting bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", intercepting bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
elseif Squadron.Language == "RU" then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", перехват самолетов в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", перехватывая боги в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
elseif Squadron.Language == "DE" then elseif Squadron.Language == "DE" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", Eindringlinge abfangen bei" .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", Eindringlinge abfangen bei" .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
end end
end end
@@ -3434,10 +3473,10 @@ do -- AI_A2A_DISPATCHER
local FirstUnit = AttackSetUnit:GetFirst() local FirstUnit = AttackSetUnit:GetFirst()
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
if Squadron.Language == "EN" then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
elseif Squadron.Language == "RU" then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", захватывающие самолеты в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", задействуя боги в " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup )
end end
end end
self:GetParent( Fsm ).onafterEngage( self, DefenderGroup, From, Event, To, AttackSetUnit ) self:GetParent( Fsm ).onafterEngage( self, DefenderGroup, From, Event, To, AttackSetUnit )
@@ -3452,10 +3491,10 @@ do -- AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron then if Squadron then
if Squadron.Language == "EN" then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " returning to base.", DefenderGroup )
elseif Squadron.Language == "RU" then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", возвращаясь на базу.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", возвращение на базу.", DefenderGroup )
end end
end end
Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
@@ -3483,10 +3522,10 @@ do -- AI_A2A_DISPATCHER
local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron.Language == "EN" then if Squadron.Language == "EN" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. " landing at base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. " landing at base.", DefenderGroup )
elseif Squadron.Language == "RU" then elseif Squadron.Language == "RU" and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", захватывающие самолеты в посадка на базу.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", посадка на базу.", DefenderGroup )
end end
if Action and Action == "Destroy" then if Action and Action == "Destroy" then
@@ -3570,7 +3609,7 @@ do -- AI_A2A_DISPATCHER
end end
--- Assigns A2G AI Tasks in relation to the detected items. --- Assigns A2G AI Tasks in relation to the detected items.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2A_DISPATCHER self
function AI_A2A_DISPATCHER:Order( DetectedItem ) function AI_A2A_DISPATCHER:Order( DetectedItem )
local detection = self.Detection -- Functional.Detection#DETECTION_AREAS local detection = self.Detection -- Functional.Detection#DETECTION_AREAS
@@ -3915,11 +3954,7 @@ do
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [AI\_A2A\_GCICAP for Caucasus](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-200%20-%20AI_A2A%20-%20GCICAP%20Demonstration) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching)
-- ### [AI\_A2A\_GCICAP for NTTR](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-210%20-%20NTTR%20AI_A2A_GCICAP%20Demonstration)
-- ### [AI\_A2A\_GCICAP for Normandy](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-220%20-%20NORMANDY%20AI_A2A_GCICAP%20Demonstration)
--
-- ### [AI\_A2A\_GCICAP for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching)
-- --
-- === -- ===
-- --

View File

@@ -1,6 +1,6 @@
--- **AI** -- (R2.2) - Models the process of Ground Controlled Interception (GCI) for airplanes. --- **AI** - Models the process of Ground Controlled Interception (GCI) for airplanes.
-- --
-- This is a class used in the @{AI_A2A_Dispatcher}. -- This is a class used in the @{AI.AI_A2A_Dispatcher}.
-- --
-- === -- ===
-- --
@@ -8,7 +8,7 @@
-- --
-- === -- ===
-- --
-- @module AI.AI_A2A_GCI -- @module AI.AI_A2A_Gci
-- @image AI_Ground_Control_Intercept.JPG -- @image AI_Ground_Control_Intercept.JPG
@@ -89,9 +89,9 @@
-- --
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG) -- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
-- --
-- An optional @{Zone} can be set, -- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone. -- Use the method @{AI.AI_CAP#AI_CAP_ZONE.SetEngageZone}() to define that Zone.
-- --
-- === -- ===
-- --
@@ -153,7 +153,7 @@ end
--- Evaluate the attack and create an AttackUnitTask list. --- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2A_GCI self -- @param #AI_A2A_GCI self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. -- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets. -- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2A_GCI self -- @return #AI_A2A_GCI self
function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.2) - Models the process of air patrol of airplanes. --- **AI** - Models the process of air patrol of airplanes.
-- --
-- === -- ===
-- --
@@ -13,7 +13,7 @@
--- @type AI_A2A_PATROL --- @type AI_A2A_PATROL
-- @extends AI.AI_A2A#AI_A2A -- @extends AI.AI_A2A#AI_A2A
--- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}. --- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}.
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
-- --
@@ -102,7 +102,7 @@
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. -- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_A2A_PATROL. -- while a new AI is targeted to the AI_A2A_PATROL.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place. -- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
-- --
@@ -122,7 +122,7 @@ AI_A2A_PATROL = {
--- Creates a new AI_A2A_PATROL object --- Creates a new AI_A2A_PATROL object
-- @param #AI_A2A_PATROL self -- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The patrol group object. -- @param Wrapper.Group#GROUP AIPatrol The patrol group object.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -264,7 +264,7 @@ function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
end end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_A2A_PATROL self -- @param #AI_A2A_PATROL self
-- @return #AI_A2A_PATROL self -- @return #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM. -- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
@@ -287,7 +287,7 @@ function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
end end
--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol. --- This static method is called from the route path within the last task at the last waypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol. -- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
-- @param Wrapper.Group#GROUP AIPatrol The AI group. -- @param Wrapper.Group#GROUP AIPatrol The AI group.
-- @param #AI_A2A_PATROL Fsm The FSM. -- @param #AI_A2A_PATROL Fsm The FSM.
@@ -302,7 +302,7 @@ function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
end end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_A2A_PATROL self -- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM. -- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.

View File

@@ -1,6 +1,6 @@
--- **AI** -- Models the process of air to ground BAI engagement for airplanes and helicopters. --- **AI** - Models the process of air to ground BAI engagement for airplanes and helicopters.
-- --
-- This is a class used in the @{AI_A2G_Dispatcher}. -- This is a class used in the @{AI.AI_A2G_Dispatcher}.
-- --
-- === -- ===
-- --
@@ -11,11 +11,8 @@
-- @module AI.AI_A2G_BAI -- @module AI.AI_A2G_BAI
-- @image AI_Air_To_Ground_Engage.JPG -- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_BAI --- @type AI_A2G_BAI
-- @extends AI.AI_A2A_Engage#AI_A2A_Engage -- @extends AI.AI_A2A_Engage#AI_A2A_Engage -- TODO: Documentation. This class does not exist, unable to determine what it extends.
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
-- --
@@ -26,8 +23,6 @@ AI_A2G_BAI = {
ClassName = "AI_A2G_BAI", ClassName = "AI_A2G_BAI",
} }
--- Creates a new AI_A2G_BAI object --- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self -- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -36,7 +31,7 @@ AI_A2G_BAI = {
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO". -- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -53,7 +48,6 @@ function AI_A2G_BAI:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAl
return self return self
end end
--- Creates a new AI_A2G_BAI object --- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self -- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -61,7 +55,7 @@ end
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. -- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -76,7 +70,7 @@ end
--- Evaluate the attack and create an AttackUnitTask list. --- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_BAI self -- @param #AI_A2G_BAI self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. -- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets. -- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_BAI self -- @return #AI_A2G_BAI self
function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
@@ -95,5 +89,3 @@ function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageA
return AttackUnitTasks return AttackUnitTasks
end end

View File

@@ -1,6 +1,6 @@
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters. --- **AI** - Models the process of air to ground engagement for airplanes and helicopters.
-- --
-- This is a class used in the @{AI_A2G_Dispatcher}. -- This is a class used in the @{AI.AI_A2G_Dispatcher}.
-- --
-- === -- ===
-- --
@@ -11,11 +11,8 @@
-- @module AI.AI_A2G_CAS -- @module AI.AI_A2G_CAS
-- @image AI_Air_To_Ground_Engage.JPG -- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_CAS --- @type AI_A2G_CAS
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL -- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL TODO: Documentation. This class does not exist, unable to determine what it extends.
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
-- --
@@ -26,8 +23,6 @@ AI_A2G_CAS = {
ClassName = "AI_A2G_CAS", ClassName = "AI_A2G_CAS",
} }
--- Creates a new AI_A2G_CAS object --- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self -- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -36,7 +31,7 @@ AI_A2G_CAS = {
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO". -- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -53,7 +48,6 @@ function AI_A2G_CAS:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAl
return self return self
end end
--- Creates a new AI_A2G_CAS object --- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self -- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -61,7 +55,7 @@ end
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. -- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -76,7 +70,7 @@ end
--- Evaluate the attack and create an AttackUnitTask list. --- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_CAS self -- @param #AI_A2G_CAS self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. -- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets. -- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_CAS self -- @return #AI_A2G_CAS self
function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
@@ -95,6 +89,3 @@ function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageA
return AttackUnitTasks return AttackUnitTasks
end end

View File

@@ -1,4 +1,4 @@
--- **AI** - Create an automated A2G defense system based on a detection network of reconnaissance vehicles and air units, coordinating SEAD, BAI and CAS operations. --- **AI** - Create an automated A2G defense system with reconnaissance units, coordinating SEAD, BAI and CAS operations.
-- --
-- === -- ===
-- --
@@ -175,7 +175,7 @@
-- * polygon zones -- * polygon zones
-- * moving zones -- * moving zones
-- --
-- Depending on the type of zone selected, a different @{Zone} object needs to be created from a ZONE_ class. -- Depending on the type of zone selected, a different @{Core.Zone} object needs to be created from a ZONE_ class.
-- --
-- --
-- ## 12. Are moving defense coordinates possible? -- ## 12. Are moving defense coordinates possible?
@@ -951,7 +951,7 @@ do -- AI_A2G_DISPATCHER
AI_A2G_DISPATCHER.DefenseQueue = {} AI_A2G_DISPATCHER.DefenseQueue = {}
--- Defense approach types. --- Defense approach types.
-- @type #AI_A2G_DISPATCHER.DefenseApproach -- @type AI_A2G_DISPATCHER.DefenseApproach
AI_A2G_DISPATCHER.DefenseApproach = { AI_A2G_DISPATCHER.DefenseApproach = {
Random = 1, Random = 1,
Distance = 2, Distance = 2,
@@ -1000,6 +1000,8 @@ do -- AI_A2G_DISPATCHER
-- self.Detection:InitDetectVisual( true ) -- self.Detection:InitDetectVisual( true )
-- self.Detection:SetRefreshTimeInterval( 30 ) -- self.Detection:SetRefreshTimeInterval( 30 )
self.SetSendPlayerMessages = false --flash messages to players
self:SetDefenseRadius() self:SetDefenseRadius()
self:SetDefenseLimit( nil ) self:SetDefenseLimit( nil )
self:SetDefenseApproach( AI_A2G_DISPATCHER.DefenseApproach.Random ) self:SetDefenseApproach( AI_A2G_DISPATCHER.DefenseApproach.Random )
@@ -1387,7 +1389,7 @@ do -- AI_A2G_DISPATCHER
--- Define a border area to simulate a **cold war** scenario. --- Define a border area to simulate a **cold war** scenario.
-- A **cold war** is one where Patrol aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **cold war** is one where Patrol aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border.
-- A **hot war** is one where Patrol aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send Patrol and GCI aircraft to attack it. -- A **hot war** is one where Patrol aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send Patrol and GCI aircraft to attack it.
-- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this. -- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this.
-- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1 -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE.
@@ -1804,6 +1806,19 @@ do -- AI_A2G_DISPATCHER
return DefenderSquadron return DefenderSquadron
end end
--- Get a resource count from a specific squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron Name of the squadron.
-- @return #number Number of airframes available or nil if the squadron does not exist
function AI_A2G_DISPATCHER:QuerySquadron(Squadron)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount))
return Squadron.ResourceCount
end
self:F({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
return nil
end
--- Set the Squadron visible before startup of the dispatcher. --- Set the Squadron visible before startup of the dispatcher.
-- All planes will be spawned as uncontrolled on the parking spot. -- All planes will be spawned as uncontrolled on the parking spot.
@@ -1837,7 +1852,7 @@ do -- AI_A2G_DISPATCHER
--- Check if the Squadron is visible before startup of the dispatcher. --- Check if the Squadron is visible before startup of the dispatcher.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @return #bool true if visible. -- @return #boolean true if visible.
-- @usage -- @usage
-- --
-- -- Set the Squadron visible before startup of dispatcher. -- -- Set the Squadron visible before startup of dispatcher.
@@ -2183,7 +2198,7 @@ do -- AI_A2G_DISPATCHER
-- The Sead patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded. -- The Sead patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed. -- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed.
-- @param #number PatrolFloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed. -- @param #number PatrolFloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
@@ -2232,7 +2247,7 @@ do -- AI_A2G_DISPATCHER
-- The Sead patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded. -- The Sead patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed. -- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed. -- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
@@ -2334,7 +2349,7 @@ do -- AI_A2G_DISPATCHER
-- The Cas patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded. -- The Cas patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed. -- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed.
-- @param #number PatrolFloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed. -- @param #number PatrolFloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
@@ -2383,7 +2398,7 @@ do -- AI_A2G_DISPATCHER
-- The Cas patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded. -- The Cas patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed. -- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed. -- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
@@ -2485,7 +2500,7 @@ do -- AI_A2G_DISPATCHER
-- The Bai patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded. -- The Bai patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed. -- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed.
-- @param #number PatrolFloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed. -- @param #number PatrolFloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
@@ -2534,7 +2549,7 @@ do -- AI_A2G_DISPATCHER
-- The Bai patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded. -- The Bai patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed. -- @param Core.Zone#ZONE_BASE Zone The @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed. -- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed. -- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed. -- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
@@ -3718,7 +3733,9 @@ do -- AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron then if Squadron then
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", wheels up.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", wheels up.", DefenderGroup )
end
AI_A2G_Fsm:Patrol() -- Engage on the TargetSetUnit AI_A2G_Fsm:Patrol() -- Engage on the TargetSetUnit
end end
end end
@@ -3730,7 +3747,7 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if Squadron then if Squadron and self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", patrolling.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", patrolling.", DefenderGroup )
end end
@@ -3749,10 +3766,11 @@ do -- AI_A2G_DISPATCHER
if Squadron and AttackSetUnit:Count() > 0 then if Squadron and AttackSetUnit:Count() > 0 then
local FirstUnit = AttackSetUnit:GetFirst() local FirstUnit = AttackSetUnit:GetFirst()
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", moving on to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", moving on to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup )
end end
end end
end
function AI_A2G_Fsm:OnAfterEngage( DefenderGroup, From, Event, To, AttackSetUnit ) function AI_A2G_Fsm:OnAfterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F({"Engage Route", DefenderGroup:GetName()}) self:F({"Engage Route", DefenderGroup:GetName()})
@@ -3764,10 +3782,11 @@ do -- AI_A2G_DISPATCHER
local FirstUnit = AttackSetUnit:GetFirst() local FirstUnit = AttackSetUnit:GetFirst()
if FirstUnit then if FirstUnit then
local Coordinate = FirstUnit:GetCoordinate() local Coordinate = FirstUnit:GetCoordinate()
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup )
end end
end end
end
function AI_A2G_Fsm:onafterRTB( DefenderGroup, From, Event, To ) function AI_A2G_Fsm:onafterRTB( DefenderGroup, From, Event, To )
self:F({"RTB", DefenderGroup:GetName()}) self:F({"RTB", DefenderGroup:GetName()})
@@ -3776,8 +3795,9 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", returning to base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", returning to base.", DefenderGroup )
end
Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
end end
@@ -3789,7 +3809,9 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", lost control." ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", lost control." )
end
if DefenderGroup:IsAboveRunway() then if DefenderGroup:IsAboveRunway() then
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup ) Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
DefenderGroup:Destroy() DefenderGroup:Destroy()
@@ -3804,8 +3826,9 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", landing at base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", landing at base.", DefenderGroup )
end
if Action and Action == "Destroy" then if Action and Action == "Destroy" then
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup ) Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
DefenderGroup:Destroy() DefenderGroup:Destroy()
@@ -3861,7 +3884,9 @@ do -- AI_A2G_DISPATCHER
self:F( { DefenderTarget = DefenderTarget } ) self:F( { DefenderTarget = DefenderTarget } )
if DefenderTarget then if DefenderTarget then
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", wheels up.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", wheels up.", DefenderGroup )
end
AI_A2G_Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit AI_A2G_Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit
end end
end end
@@ -3876,9 +3901,10 @@ do -- AI_A2G_DISPATCHER
if Squadron then if Squadron then
local FirstUnit = AttackSetUnit:GetFirst() local FirstUnit = AttackSetUnit:GetFirst()
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup )
end end
end
self:GetParent(self).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit ) self:GetParent(self).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit )
end end
@@ -3892,10 +3918,11 @@ do -- AI_A2G_DISPATCHER
local FirstUnit = AttackSetUnit:GetFirst() local FirstUnit = AttackSetUnit:GetFirst()
if FirstUnit then if FirstUnit then
local Coordinate = FirstUnit:GetCoordinate() local Coordinate = FirstUnit:GetCoordinate()
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup )
end end
end end
end
function AI_A2G_Fsm:onafterRTB( DefenderGroup, From, Event, To ) function AI_A2G_Fsm:onafterRTB( DefenderGroup, From, Event, To )
self:F({"Defender RTB", DefenderGroup:GetName()}) self:F({"Defender RTB", DefenderGroup:GetName()})
@@ -3903,8 +3930,9 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", returning to base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", returning to base.", DefenderGroup )
end
self:GetParent(self).onafterRTB( self, DefenderGroup, From, Event, To ) self:GetParent(self).onafterRTB( self, DefenderGroup, From, Event, To )
Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
@@ -3918,8 +3946,9 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
--Dispatcher:MessageToPlayers( Squadron, "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " lost control." ) if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " lost control." )
end
if DefenderGroup:IsAboveRunway() then if DefenderGroup:IsAboveRunway() then
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup ) Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
DefenderGroup:Destroy() DefenderGroup:Destroy()
@@ -3934,8 +3963,9 @@ do -- AI_A2G_DISPATCHER
local DefenderName = DefenderGroup:GetCallsign() local DefenderName = DefenderGroup:GetCallsign()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
if self.SetSendPlayerMessages then
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", landing at base.", DefenderGroup ) Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", landing at base.", DefenderGroup )
end
if Action and Action == "Destroy" then if Action and Action == "Destroy" then
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup ) Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
DefenderGroup:Destroy() DefenderGroup:Destroy()
@@ -3975,7 +4005,7 @@ do -- AI_A2G_DISPATCHER
local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate ) local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate )
-- Now check if this coordinate is not in a danger zone, meaning, that the attack line is not crossing other coordinates. -- Now check if this coordinate is not in a danger zone, meaning, that the attack line is not crossing other coordinates.
-- (y1 y2)x + (x2 x1)y + (x1y2 x2y1) = 0 -- (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0
local c1 = DefenseCoordinate local c1 = DefenseCoordinate
local c2 = AttackCoordinate local c2 = AttackCoordinate
@@ -4036,7 +4066,7 @@ do -- AI_A2G_DISPATCHER
for DefenderID, DefenderGroup in pairs( DefenderFriendlies or {} ) do for DefenderID, DefenderGroup in pairs( DefenderFriendlies or {} ) do
-- Here we check if the defenders have a defense line to the attackers. -- Here we check if the defenders have a defense line to the attackers.
-- If the attackers are behind enemy lines or too close to an other defense line; then don´t engage. -- If the attackers are behind enemy lines or too close to an other defense line; then don't engage.
local DefenseCoordinate = DefenderGroup:GetCoordinate() local DefenseCoordinate = DefenderGroup:GetCoordinate()
local HasDefenseLine = self:HasDefenseLine( DefenseCoordinate, DetectedItem ) local HasDefenseLine = self:HasDefenseLine( DefenseCoordinate, DetectedItem )
@@ -4729,6 +4759,14 @@ do
self:Patrol( SquadronName, PatrolTaskType ) self:Patrol( SquadronName, PatrolTaskType )
end end
--- Set flashing player messages on or off
-- @param #AI_A2G_DISPATCHER self
-- @param #boolean onoff Set messages on (true) or off (false)
function AI_A2G_DISPATCHER:SetSendMessages( onoff )
self.SetSendPlayerMessages = onoff
end
end
--- Add resources to a Squadron --- Add resources to a Squadron
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name. -- @param #string Squadron The squadron name.
@@ -4752,6 +4790,3 @@ do
end end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount}) self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end end
end

View File

@@ -1,6 +1,6 @@
--- **AI** -- Models the process of air to ground SEAD engagement for airplanes and helicopters. --- **AI** - Models the process of air to ground SEAD engagement for airplanes and helicopters.
-- --
-- This is a class used in the @{AI_A2G_Dispatcher}. -- This is a class used in the @{AI.AI_A2G_Dispatcher}.
-- --
-- === -- ===
-- --
@@ -65,9 +65,9 @@
-- --
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG) -- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
-- --
-- An optional @{Zone} can be set, -- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2G_SEAD.SetEngageZone}() to define that Zone. -- Use the method @{AI.AI_CAP#AI_CAP_ZONE.SetEngageZone}() to define that Zone. -- TODO: Documentation. Check that this is actually correct. The originally referenced class does not exist.
-- --
-- === -- ===
-- --
@@ -76,8 +76,6 @@ AI_A2G_SEAD = {
ClassName = "AI_A2G_SEAD", ClassName = "AI_A2G_SEAD",
} }
--- Creates a new AI_A2G_SEAD object --- Creates a new AI_A2G_SEAD object
-- @param #AI_A2G_SEAD self -- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -86,7 +84,7 @@ AI_A2G_SEAD = {
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO". -- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -111,7 +109,7 @@ end
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. -- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
@@ -127,7 +125,7 @@ end
--- Evaluate the attack and create an AttackUnitTask list. --- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_SEAD self -- @param #AI_A2G_SEAD self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. -- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets. -- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_SEAD self -- @return #AI_A2G_SEAD self
function AI_A2G_SEAD:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) function AI_A2G_SEAD:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )

View File

@@ -253,6 +253,9 @@ function AI_AIR:New( AIGroup )
self.IdleCount = 0 self.IdleCount = 0
self.RTBSpeedMaxFactor = 0.6
self.RTBSpeedMinFactor = 0.5
return self return self
end end
@@ -371,7 +374,7 @@ end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR. -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targeted to the AI_AIR.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @param #number FuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. -- @param #number FuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
@@ -406,7 +409,7 @@ end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @return #AI_AIR self -- @return #AI_AIR self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
@@ -576,13 +579,25 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
end end
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
-- A random value in this bracket is then applied in the waypoint routing generation.
-- @param #AI_AIR self
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
-- @return #AI_AIR self
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
self.RTBSpeedMaxFactor = MaxFactor or 0.6
self.RTBSpeedMinFactor = MinFactor or 0.5
return self
end
--- @param #AI_AIR self --- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
function AI_AIR:onafterRTB( AIGroup, From, Event, To ) function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:F( { AIGroup, From, Event, To } ) self:F( { AIGroup, From, Event, To } )
if AIGroup and AIGroup:IsAlive() then if AIGroup and AIGroup:IsAlive() then
self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" ) self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
@@ -590,6 +605,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:ClearTargetDistance() self:ClearTargetDistance()
--AIGroup:ClearTasks() --AIGroup:ClearTasks()
AIGroup:OptionProhibitAfterburner(true)
local EngageRoute = {} local EngageRoute = {}
--- Calculate the target route point. --- Calculate the target route point.
@@ -597,12 +614,14 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
local FromCoord = AIGroup:GetCoordinate() local FromCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!) local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
local ToTargetVec3 = ToTargetCoord:GetVec3() local ToTargetVec3 = ToTargetCoord:GetVec3()
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 ) local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
if not self.RTBMinSpeed or not self.RTBMaxSpeed then if not self.RTBMinSpeed or not self.RTBMaxSpeed then
local RTBSpeedMax = AIGroup:GetSpeedMax() local RTBSpeedMax = AIGroup:GetSpeedMax()
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 ) local RTBSpeedMaxFactor = self.RTBSpeedMaxFactor or 0.6
local RTBSpeedMinFactor = self.RTBSpeedMinFactor or 0.5
self:SetRTBSpeed( RTBSpeedMax * RTBSpeedMinFactor, RTBSpeedMax * RTBSpeedMaxFactor)
end end
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed ) local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )

View File

@@ -1,4 +1,4 @@
--- **AI** - Create an automated AIR defense system based on a detection network of reconnaissance vehicles and air units, coordinating SEAD, BAI and CAP operations. --- **AI** - Create an automated AIR defense system with reconnaissance units, coordinating SEAD, BAI and CAP operations.
-- --
-- === -- ===
-- --
@@ -24,7 +24,7 @@
-- --
-- ## Missions: -- ## Missions:
-- --
-- [AID-AIR - AI AIR Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-AIR%20-%20AI%20AIR%20Dispatching) -- [AID-AIR - AI AIR Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching)
-- --
-- === -- ===
-- --
@@ -88,7 +88,7 @@
-- --
-- ## 4. How do the defenses decide **when and where to engage** on approaching enemy units? -- ## 4. How do the defenses decide **when and where to engage** on approaching enemy units?
-- --
-- The AIR dispacher needs you to setup (various) defense coordinates, which are strategic positions in the battle field to be defended. -- The AIR dispatcher needs you to setup (various) defense coordinates, which are strategic positions in the battle field to be defended.
-- Any ground based enemy approaching within the proximity of such a defense point, may trigger for a defensive action by friendly air units. -- Any ground based enemy approaching within the proximity of such a defense point, may trigger for a defensive action by friendly air units.
-- --
-- There are 2 important parameters that play a role in the defensive decision making: defensiveness and reactivity. -- There are 2 important parameters that play a role in the defensive decision making: defensiveness and reactivity.
@@ -108,7 +108,7 @@
-- ## 5. Are defense coordinates and defense reactivity the only parameters? -- ## 5. Are defense coordinates and defense reactivity the only parameters?
-- --
-- No, depending on the target type, and the threat level of the target, the probability of defense will be higher. -- No, depending on the target type, and the threat level of the target, the probability of defense will be higher.
-- In other words, when a SAM-10 radar emitter is detected, its probabilty for defense will be much higher than when a BMP-1 vehicle is -- In other words, when a SAM-10 radar emitter is detected, its probability for defense will be much higher than when a BMP-1 vehicle is
-- detected, even when both enemies are at the same distance from a defense coordinate. -- detected, even when both enemies are at the same distance from a defense coordinate.
-- This will ensure optimal defenses, SEAD tasks will be launched much more quicker against engaging radar emitters, to ensure air superiority. -- This will ensure optimal defenses, SEAD tasks will be launched much more quicker against engaging radar emitters, to ensure air superiority.
-- Approaching main battle tanks will be engaged much faster, than a group of approaching trucks. -- Approaching main battle tanks will be engaged much faster, than a group of approaching trucks.
@@ -117,12 +117,12 @@
-- ## 6. Which Squadrons will I create and which name will I give each Squadron? -- ## 6. Which Squadrons will I create and which name will I give each Squadron?
-- --
-- The AIR defense system works with **Squadrons**. Each Squadron must be given a unique name, that forms the **key** to the squadron. -- The AIR defense system works with **Squadrons**. Each Squadron must be given a unique name, that forms the **key** to the squadron.
-- Several options and activities can be set per Squadron. A free format name can be given, but always ensure that the name is meaningfull -- Several options and activities can be set per Squadron. A free format name can be given, but always ensure that the name is meaningful
-- for your mission, and remember that squadron names are used for communication to the players of your mission. -- for your mission, and remember that squadron names are used for communication to the players of your mission.
-- --
-- There are mainly 3 types of defenses: **SEAD**, **CAS** and **BAI**. -- There are mainly 3 types of defenses: **SEAD**, **CAS** and **BAI**.
-- --
-- Suppression of Air Defenses (SEAD) are effective agains radar emitters. Close Air Support (CAS) is launched when the enemy is close near friendly units. -- Suppression of Air Defenses (SEAD) are effective against radar emitters. Close Air Support (CAS) is launched when the enemy is close near friendly units.
-- Battleground Air Interdiction (BAI) tasks are launched when there are no friendlies around. -- Battleground Air Interdiction (BAI) tasks are launched when there are no friendlies around.
-- --
-- Depending on the defense type, different payloads will be needed. See further points on squadron definition. -- Depending on the defense type, different payloads will be needed. See further points on squadron definition.
@@ -174,13 +174,13 @@
-- * polygon zones -- * polygon zones
-- * moving zones -- * moving zones
-- --
-- Depending on the type of zone selected, a different @{Zone} object needs to be created from a ZONE_ class. -- Depending on the type of zone selected, a different @{Core.Zone} object needs to be created from a ZONE_ class.
-- --
-- --
-- ## 12. Are moving defense coordinates possible? -- ## 12. Are moving defense coordinates possible?
-- --
-- Yes, different COORDINATE types are possible to be used. -- Yes, different COORDINATE types are possible to be used.
-- The COORDINATE_UNIT will help you to specify a defense coodinate that is attached to a moving unit. -- The COORDINATE_UNIT will help you to specify a defense coordinate that is attached to a moving unit.
-- --
-- --
-- ## 13. How much defense coordinates do I need to create? -- ## 13. How much defense coordinates do I need to create?
@@ -214,7 +214,7 @@
-- * From a parking spot with running engines -- * From a parking spot with running engines
-- * From a parking spot with cold engines -- * From a parking spot with cold engines
-- --
-- **The default takeoff method is staight in the air.** -- **The default takeoff method is straight in the air.**
-- This takeoff method is the most useful if you want to avoid airplane clutter at airbases! -- This takeoff method is the most useful if you want to avoid airplane clutter at airbases!
-- But it is the least realistic one! -- But it is the least realistic one!
-- --
@@ -236,10 +236,10 @@
-- --
-- For each Squadron, depending on the helicopter or airplane type (modern, old) and payload, which overhead is required to provide any defense? -- For each Squadron, depending on the helicopter or airplane type (modern, old) and payload, which overhead is required to provide any defense?
-- --
-- In other words, if **X** enemy ground units are detected, how many **Y** defense helicpters or airplanes need to engage (per squadron)? -- In other words, if **X** enemy ground units are detected, how many **Y** defense helicopters or airplanes need to engage (per squadron)?
-- The **Y** is dependent on the type of airplane (era), payload, fuel levels, skills etc. -- The **Y** is dependent on the type of airplane (era), payload, fuel levels, skills etc.
-- But the most important factor is the payload, which is the amount of AIR weapons the defense can carry to attack the enemy ground units. -- But the most important factor is the payload, which is the amount of AIR weapons the defense can carry to attack the enemy ground units.
-- For example, a Ka-50 can carry 16 vikrs, that means, that it potentially can destroy at least 8 ground units without a reload of ammunication. -- For example, a Ka-50 can carry 16 vikrs, that means, that it potentially can destroy at least 8 ground units without a reload of ammunition.
-- That means, that one defender can destroy more enemy ground units. -- That means, that one defender can destroy more enemy ground units.
-- Thus, the overhead is a **factor** that will calculate dynamically how many **Y** defenses will be required based on **X** attackers detected. -- Thus, the overhead is a **factor** that will calculate dynamically how many **Y** defenses will be required based on **X** attackers detected.
-- --
@@ -259,7 +259,7 @@
-- --
-- ### Author: **FlightControl** rework of GCICAP + introduction of new concepts (squadrons). -- ### Author: **FlightControl** rework of GCICAP + introduction of new concepts (squadrons).
-- --
-- @module AI.AI_AIR_Dispatcher -- @module AI.AI_Air_Dispatcher
-- @image AI_Air_To_Ground_Dispatching.JPG -- @image AI_Air_To_Ground_Dispatching.JPG
@@ -279,7 +279,7 @@ do -- AI_AIR_DISPATCHER
-- Multiple defense coordinates can be setup. Defense coordinates can be strategic or tactical positions or references to strategic units or scenery. -- Multiple defense coordinates can be setup. Defense coordinates can be strategic or tactical positions or references to strategic units or scenery.
-- The AIR dispatcher will evaluate every x seconds the tactical situation around each defense coordinate. When a defense coordinate -- The AIR dispatcher will evaluate every x seconds the tactical situation around each defense coordinate. When a defense coordinate
-- is under threat, it will communicate through the command center that defensive actions need to be taken and will launch groups of air units for defense. -- is under threat, it will communicate through the command center that defensive actions need to be taken and will launch groups of air units for defense.
-- The level of threat to the defense coordinate varyies upon the strength and types of the enemy units, the distance to the defense point, and the defensiveness parameters. -- The level of threat to the defense coordinate varies upon the strength and types of the enemy units, the distance to the defense point, and the defensiveness parameters.
-- Defensive actions are taken through probability, but the closer and the more threat the enemy poses to the defense coordinate, the faster it will be attacked by friendly AIR units. -- Defensive actions are taken through probability, but the closer and the more threat the enemy poses to the defense coordinate, the faster it will be attacked by friendly AIR units.
-- --
-- Please study carefully the underlying explanations how to setup and use this module, as it has many features. -- Please study carefully the underlying explanations how to setup and use this module, as it has many features.
@@ -328,7 +328,7 @@ do -- AI_AIR_DISPATCHER
-- By spawning in dynamically additional recce, you can ensure that there is sufficient reconnaissance coverage so the defense mechanism is continuously -- By spawning in dynamically additional recce, you can ensure that there is sufficient reconnaissance coverage so the defense mechanism is continuously
-- alerted of new enemy ground targets. -- alerted of new enemy ground targets.
-- --
-- The following example defens a new reconnaissance network using a @{Functional.Detection#DETECTION_AREAS} object. -- The following example defense a new reconnaissance network using a @{Functional.Detection#DETECTION_AREAS} object.
-- --
-- -- Define a SET_GROUP object that builds a collection of groups that define the recce network. -- -- Define a SET_GROUP object that builds a collection of groups that define the recce network.
-- -- Here we build the network with all the groups that have a name starting with CCCP Recce. -- -- Here we build the network with all the groups that have a name starting with CCCP Recce.
@@ -473,7 +473,7 @@ do -- AI_AIR_DISPATCHER
-- the mission designer can choose to increase or reduce the amount of planes spawned. -- the mission designer can choose to increase or reduce the amount of planes spawned.
-- --
-- The method @{#AI_AIR_DISPATCHER.SetSquadron}() defines for you a new squadron. -- The method @{#AI_AIR_DISPATCHER.SetSquadron}() defines for you a new squadron.
-- The provided parameters are the squadron name, airbase name and a list of template prefixe, and a number that indicates the amount of resources. -- The provided parameters are the squadron name, airbase name and a list of template prefixes, and a number that indicates the amount of resources.
-- --
-- For example, this defines 3 new squadrons: -- For example, this defines 3 new squadrons:
-- --
@@ -619,7 +619,7 @@ do -- AI_AIR_DISPATCHER
-- Depending on the demand of requested takeoffs by the AIR dispatcher, an airfield can become overloaded. Too many aircraft need to be taken -- Depending on the demand of requested takeoffs by the AIR dispatcher, an airfield can become overloaded. Too many aircraft need to be taken
-- off at the same time, which will result in clutter as described above. In order to better control this behaviour, a takeoff scheduler is implemented, -- off at the same time, which will result in clutter as described above. In order to better control this behaviour, a takeoff scheduler is implemented,
-- which can be used to control how many aircraft are ordered for takeoff between specific time intervals. -- which can be used to control how many aircraft are ordered for takeoff between specific time intervals.
-- The takeff intervals can be specified per squadron, which make sense, as each squadron have a "home" airfield. -- The takeoff intervals can be specified per squadron, which make sense, as each squadron have a "home" airfield.
-- --
-- For this purpose, the method @{#AI_AIR_DISPATCHER.SetSquadronTakeOffInterval}() can be used to specify the takeoff intervals of -- For this purpose, the method @{#AI_AIR_DISPATCHER.SetSquadronTakeOffInterval}() can be used to specify the takeoff intervals of
-- aircraft groups per squadron to avoid cluttering of aircraft at airbases. -- aircraft groups per squadron to avoid cluttering of aircraft at airbases.
@@ -947,7 +947,7 @@ do -- AI_AIR_DISPATCHER
AI_AIR_DISPATCHER.DefenseQueue = {} AI_AIR_DISPATCHER.DefenseQueue = {}
--- Defense approach types --- Defense approach types
-- @type #AI_AIR_DISPATCHER.DefenseApproach -- @type AI_AIR_DISPATCHER.DefenseApproach
AI_AIR_DISPATCHER.DefenseApproach = { AI_AIR_DISPATCHER.DefenseApproach = {
Random = 1, Random = 1,
Distance = 2, Distance = 2,
@@ -1021,7 +1021,7 @@ do -- AI_AIR_DISPATCHER
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param Tasking.Task_AIR#AI_AIR Task -- @param AI.AI_Air#AI_AIR Task
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string PlayerName -- @param #string PlayerName
@@ -1224,7 +1224,7 @@ do -- AI_AIR_DISPATCHER
self:I( "Captured " .. AirbaseName ) self:I( "Captured " .. AirbaseName )
-- Now search for all squadrons located at the airbase, and sanatize them. -- Now search for all squadrons located at the airbase, and sanitize them.
for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do
if Squadron.AirbaseName == AirbaseName then if Squadron.AirbaseName == AirbaseName then
Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning. Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning.
@@ -1376,7 +1376,7 @@ do -- AI_AIR_DISPATCHER
--- Define a border area to simulate a **cold war** scenario. --- Define a border area to simulate a **cold war** scenario.
-- A **cold war** is one where Patrol aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **cold war** is one where Patrol aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border.
-- A **hot war** is one where Patrol aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send Patrol and GCI aircraft to attack it. -- A **hot war** is one where Patrol aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send Patrol and GCI aircraft to attack it.
-- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this. -- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{Core.Zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this.
-- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1 -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE.
@@ -1852,7 +1852,7 @@ do -- AI_AIR_DISPATCHER
--- Check if the Squadron is visible before startup of the dispatcher. --- Check if the Squadron is visible before startup of the dispatcher.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The squadron name. -- @param #string SquadronName The squadron name.
-- @return #bool true if visible. -- @return #boolean true if visible.
-- @usage -- @usage
-- --
-- -- Set the Squadron visible before startup of dispatcher. -- -- Set the Squadron visible before startup of dispatcher.

View File

@@ -1,6 +1,6 @@
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters. --- **AI** - Models the process of air to ground engagement for airplanes and helicopters.
-- --
-- This is a class used in the @{AI_A2G_Dispatcher}. -- This is a class used in the @{AI.AI_A2G_Dispatcher}.
-- --
-- === -- ===
-- --
@@ -65,9 +65,9 @@
-- --
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG) -- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
-- --
-- An optional @{Zone} can be set, -- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_AIR_ENGAGE.SetEngageZone}() to define that Zone. -- Use the method @{AI.AI_CAP#AI_AIR_ENGAGE.SetEngageZone}() to define that Zone.
-- --
-- === -- ===
-- --
@@ -533,6 +533,10 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3() local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
if not TargetCoord then
self:Return()
return
end
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )

View File

@@ -1,4 +1,4 @@
--- **AI** -- Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters. --- **AI** - Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters.
-- --
-- === -- ===
-- --
@@ -12,8 +12,7 @@
--- @type AI_AIR_PATROL --- @type AI_AIR_PATROL
-- @extends AI.AI_Air#AI_AIR -- @extends AI.AI_Air#AI_AIR
--- The AI_AIR_PATROL class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group}
--- The AI_AIR_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
@@ -86,9 +85,9 @@
-- --
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) -- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
-- --
-- An optional @{Zone} can be set, -- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_AIR_PATROL.SetEngageZone}() to define that Zone. -- Use the method @{AI.AI_CAP#AI_AIR_PATROL.SetEngageZone}() to define that Zone.
-- --
-- === -- ===
-- --
@@ -101,7 +100,7 @@ AI_AIR_PATROL = {
-- @param #AI_AIR_PATROL self -- @param #AI_AIR_PATROL self
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM. -- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
-- @param Wrapper.Group#GROUP AIGroup The AI group. -- @param Wrapper.Group#GROUP AIGroup The AI group.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h. -- @param DCS#Speed PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h.
@@ -198,13 +197,11 @@ function AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, Pa
-- @param #AI_AIR_PATROL self -- @param #AI_AIR_PATROL self
-- @param #number Delay The delay in seconds. -- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL. self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
return self return self
end end
--- Set the Engage Range when the AI will engage with airborne enemies. --- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_AIR_PATROL self -- @param #AI_AIR_PATROL self
-- @param #number EngageRange The Engage Range. -- @param #number EngageRange The Engage Range.
@@ -247,9 +244,7 @@ function AI_AIR_PATROL:SetRaceTrackPattern(LegMin, LegMax, HeadingMin, HeadingMa
end end
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_AIR_PATROL self -- @param #AI_AIR_PATROL self
-- @return #AI_AIR_PATROL self -- @return #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM. -- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
@@ -271,7 +266,7 @@ function AI_AIR_PATROL:onafterPatrol( AIPatrol, From, Event, To )
) )
end end
--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol. --- This static method is called from the route path within the last task at the last waypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol. -- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
-- @param Wrapper.Group#GROUP AIPatrol The AI group. -- @param Wrapper.Group#GROUP AIPatrol The AI group.
-- @param #AI_AIR_PATROL Fsm The FSM. -- @param #AI_AIR_PATROL Fsm The FSM.
@@ -285,7 +280,7 @@ function AI_AIR_PATROL.___PatrolRoute( AIPatrol, Fsm )
end end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_AIR_PATROL self -- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM. -- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
@@ -300,7 +295,6 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
return return
end end
if AIPatrol and AIPatrol:IsAlive() then if AIPatrol and AIPatrol:IsAlive() then
local PatrolRoute = {} local PatrolRoute = {}
@@ -386,7 +380,9 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
end end
--- @param Wrapper.Group#GROUP AIPatrol --- Resumes the AIPatrol
-- @param Wrapper.Group#GROUP AIPatrol
-- @param Core.Fsm#FSM Fsm
function AI_AIR_PATROL.Resume( AIPatrol, Fsm ) function AI_AIR_PATROL.Resume( AIPatrol, Fsm )
AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } ) AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } )

View File

@@ -1,6 +1,6 @@
--- **AI** - Models squadrons for airplanes and helicopters. --- **AI** - Models squadrons for airplanes and helicopters.
-- --
-- This is a class used in the @{AI_Air_Dispatcher} and derived dispatcher classes. -- This is a class used in the @{AI.AI_Air_Dispatcher} and derived dispatcher classes.
-- --
-- === -- ===
-- --

View File

@@ -1,4 +1,4 @@
--- **AI** -- Peform Battlefield Area Interdiction (BAI) within an engagement zone. --- **AI** - Peform Battlefield Area Interdiction (BAI) within an engagement zone.
-- --
-- **Features:** -- **Features:**
-- --
@@ -26,17 +26,17 @@
-- --
-- === -- ===
-- --
-- @module AI.AI_Bai -- @module AI.AI_BAI
-- @image AI_Battlefield_Air_Interdiction.JPG -- @image AI_Battlefield_Air_Interdiction.JPG
--- AI_BAI_ZONE class --- AI_BAI_ZONE class
-- @type AI_BAI_ZONE -- @type AI_BAI_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE -- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- Implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. --- Implements the core functions to provide BattleGround Air Interdiction in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}.
-- --
-- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
-- --
@@ -142,7 +142,7 @@ AI_BAI_ZONE = {
--- Creates a new AI_BAI_ZONE object --- Creates a new AI_BAI_ZONE object
-- @param #AI_BAI_ZONE self -- @param #AI_BAI_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
@@ -515,8 +515,8 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -566,7 +566,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks ) EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone. --- Find a random 2D point in EngageZone.
local ToTargetVec2 = self.EngageZone:GetRandomVec2() local ToTargetVec2 = self.EngageZone:GetRandomVec2()

View File

@@ -1,4 +1,4 @@
--- **AI** -- Balance player slots with AI to create an engaging simulation environment, independent of the amount of players. --- **AI** - Balance player slots with AI to create an engaging simulation environment, independent of the amount of players.
-- --
-- **Features:** -- **Features:**
-- --
@@ -9,7 +9,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
-- --
-- === -- ===
-- --

View File

@@ -1,4 +1,4 @@
--- **AI** -- Perform Combat Air Patrolling (CAP) for airplanes. --- **AI** - Perform Combat Air Patrolling (CAP) for airplanes.
-- --
-- **Features:** -- **Features:**
-- --
@@ -7,10 +7,9 @@
-- * Manage a fuel threshold to RTB on time. -- * Manage a fuel threshold to RTB on time.
-- * Engage the enemy when detected. -- * Engage the enemy when detected.
-- --
--
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
-- --
-- === -- ===
-- --
@@ -29,17 +28,15 @@
-- --
-- === -- ===
-- --
-- @module AI.AI_Cap -- @module AI.AI_CAP
-- @image AI_Combat_Air_Patrol.JPG -- @image AI_Combat_Air_Patrol.JPG
--- @type AI_CAP_ZONE --- @type AI_CAP_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE -- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}
--- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
@@ -106,15 +103,15 @@
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone. -- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI. -- The range is applied at the position of the AI.
-- Use the method @{AI.AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. -- Use the method @{#AI_CAP_ZONE.SetEngageRange}() to define that range.
-- --
-- ## 4. Set the Zone of Engagement -- ## 4. Set the Zone of Engagement
-- --
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) -- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
-- --
-- An optional @{Zone} can be set, -- An optional @{Core.Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets. -- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. -- Use the method @{#AI_CAP_ZONE.SetEngageZone}() to define that Zone.
-- --
-- === -- ===
-- --
@@ -123,11 +120,9 @@ AI_CAP_ZONE = {
ClassName = "AI_CAP_ZONE", ClassName = "AI_CAP_ZONE",
} }
--- Creates a new AI_CAP_ZONE object --- Creates a new AI_CAP_ZONE object
-- @param #AI_CAP_ZONE self -- @param #AI_CAP_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
@@ -243,7 +238,6 @@ function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
-- @param #AI_CAP_ZONE self -- @param #AI_CAP_ZONE self
-- @param #number Delay The delay in seconds. -- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
--- OnBefore Transition Handler for Event Abort. --- OnBefore Transition Handler for Event Abort.
@@ -303,7 +297,6 @@ function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
return self return self
end end
--- Set the Engage Zone which defines where the AI will engage bogies. --- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_CAP_ZONE self -- @param #AI_CAP_ZONE self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. -- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
@@ -346,7 +339,6 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
end end
--- @param AI.AI_CAP#AI_CAP_ZONE --- @param AI.AI_CAP#AI_CAP_ZONE
-- @param Wrapper.Group#GROUP EngageGroup -- @param Wrapper.Group#GROUP EngageGroup
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm ) function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
@@ -358,8 +350,6 @@ function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
end end
end end
--- @param #AI_CAP_ZONE self --- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
@@ -400,7 +390,6 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
end end
end end
--- @param #AI_CAP_ZONE self --- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
@@ -411,9 +400,6 @@ function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To )
self:__Route( 1 ) self:__Route( 1 )
end end
--- @param #AI_CAP_ZONE self --- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
@@ -428,8 +414,10 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return self end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -442,7 +430,6 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
EngageRoute[#EngageRoute+1] = CurrentRoutePoint EngageRoute[#EngageRoute+1] = CurrentRoutePoint
--- Find a random 2D point in PatrolZone. --- Find a random 2D point in PatrolZone.
local ToTargetVec2 = self.PatrolZone:GetRandomVec2() local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
self:T2( ToTargetVec2 ) self:T2( ToTargetVec2 )

View File

@@ -1,4 +1,4 @@
--- **AI** -- Perform Close Air Support (CAS) near friendlies. --- **AI** - Perform Close Air Support (CAS) near friendlies.
-- --
-- **Features:** -- **Features:**
-- --
@@ -11,7 +11,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
-- --
-- === -- ===
-- --
@@ -28,16 +28,16 @@
-- --
-- === -- ===
-- --
-- @module AI.AI_Cas -- @module AI.AI_CAS
-- @image AI_Close_Air_Support.JPG -- @image AI_Close_Air_Support.JPG
--- AI_CAS_ZONE class --- AI_CAS_ZONE class
-- @type AI_CAS_ZONE -- @type AI_CAS_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE -- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- Implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. --- Implements the core functions to provide Close Air Support in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}.
-- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
-- --
-- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) -- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG)
@@ -130,7 +130,7 @@ AI_CAS_ZONE = {
--- Creates a new AI_CAS_ZONE object --- Creates a new AI_CAS_ZONE object
-- @param #AI_CAS_ZONE self -- @param #AI_CAS_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
@@ -459,8 +459,8 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -496,7 +496,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAS_ZONE.EngageRoute", self ) AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAS_ZONE.EngageRoute", self )
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks ) EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone. --- Find a random 2D point in EngageZone.
local ToTargetVec2 = self.EngageZone:GetRandomVec2() local ToTargetVec2 = self.EngageZone:GetRandomVec2()

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes. --- **AI** - Models the intelligent transportation of infantry and other cargo using Planes.
-- --
-- ## Features: -- ## Features:
-- --

View File

@@ -1,4 +1,4 @@
--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. --- **AI** - Models the intelligent transportation of infantry and other cargo using Helicopters.
-- --
-- ## Features: -- ## Features:
-- --
@@ -174,8 +174,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZone
self:SetPickupSpeed( 350, 150 ) self:SetPickupSpeed( 350, 150 )
self:SetDeploySpeed( 350, 150 ) self:SetDeploySpeed( 350, 150 )
self:SetPickupRadius( 0, 0 ) self:SetPickupRadius( 40, 12 )
self:SetDeployRadius( 0, 0 ) self:SetDeployRadius( 40, 12 )
self:SetPickupHeight( 500, 200 ) self:SetPickupHeight( 500, 200 )
self:SetDeployHeight( 500, 200 ) self:SetDeployHeight( 500, 200 )
@@ -186,6 +186,9 @@ end
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet ) function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet )
return AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) local dispatcher = AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
dispatcher:SetLandingSpeedAndHeight(27, 6)
return dispatcher
end end

View File

@@ -1,4 +1,4 @@
--- **AI** -- (2.5.1) - Models the intelligent transportation of infantry and other cargo using Ships --- **AI** - Models the intelligent transportation of infantry and other cargo using Ships.
-- --
-- ## Features: -- ## Features:
-- --
@@ -21,7 +21,7 @@
-- === -- ===
-- --
-- @module AI.AI_Cargo_Dispatcher_Ship -- @module AI.AI_Cargo_Dispatcher_Ship
-- @image AI_Cargo_Dispatching_For_Ship.JPG -- @image AI_Cargo_Dispatcher.JPG
--- @type AI_CARGO_DISPATCHER_SHIP --- @type AI_CARGO_DISPATCHER_SHIP
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
@@ -37,14 +37,14 @@
-- --
-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**. -- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**.
-- --
-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framwork. -- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. -- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo. -- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo.
-- --
-- --
-- # 1) AI_CARGO_DISPATCHER_SHIP constructor. -- # 1) AI_CARGO_DISPATCHER_SHIP constructor.
-- --
-- * @{AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object. -- * @{#AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object.
-- --
-- --- -- ---
-- --

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.5.1) - Models the intelligent transportation of infantry and other cargo. --- **AI** - Models the intelligent transportation of infantry and other cargo.
-- --
-- === -- ===
-- --
@@ -7,7 +7,7 @@
-- === -- ===
-- --
-- @module AI.AI_Cargo_Ship -- @module AI.AI_Cargo_Ship
-- @image AI_Cargo_Dispatching_For_Ship.JPG -- @image AI_Cargo_Dispatcher.JPG
--- @type AI_CARGO_SHIP --- @type AI_CARGO_SHIP
-- @extends AI.AI_Cargo#AI_CARGO -- @extends AI.AI_Cargo#AI_CARGO
@@ -46,12 +46,12 @@
-- --
-- ## Cargo deployment. -- ## Cargo deployment.
-- --
-- Using the @{AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the -- Using the @{#AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the
-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment. -- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
-- --
-- ## Cargo pickup. -- ## Cargo pickup.
-- --
-- Using the @{AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified -- Using the @{#AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified
-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment. -- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
-- --
-- --

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Taking the lead of AI escorting your flight or of other AI. --- **AI** - Taking the lead of AI escorting your flight or of other AI.
-- --
-- === -- ===
-- --
@@ -147,8 +147,8 @@
-- @image Escorting.JPG -- @image Escorting.JPG
---
--- @type AI_ESCORT -- @type AI_ESCORT
-- @extends AI.AI_Formation#AI_FORMATION -- @extends AI.AI_Formation#AI_FORMATION
@@ -168,10 +168,13 @@
-- --
-- -- First find the GROUP object and the CLIENT object. -- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. -- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. -- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort".
-- --
-- -- Now use these 2 objects to construct the new EscortPlanes object. -- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) -- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
-- EscortPlanes:MenusAirplanes() -- create menus for airplanes
-- EscortPlanes:__Start(2)
--
-- --
-- @field #AI_ESCORT -- @field #AI_ESCORT
AI_ESCORT = { AI_ESCORT = {
@@ -189,7 +192,7 @@ AI_ESCORT = {
TaskPoints = {} TaskPoints = {}
} }
--- @field Functional.Detection#DETECTION_AREAS -- @field Functional.Detection#DETECTION_AREAS
AI_ESCORT.Detection = nil AI_ESCORT.Detection = nil
--- MENUPARAM type --- MENUPARAM type
@@ -211,10 +214,14 @@ AI_ESCORT.Detection = nil
-- --
-- -- First find the GROUP object and the CLIENT object. -- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. -- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. -- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort".
-- --
-- -- Now use these 2 objects to construct the new EscortPlanes object. -- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) -- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
-- EscortPlanes:MenusAirplanes() -- create menus for airplanes
-- EscortPlanes:__Start(2)
--
--
function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing )
local self = BASE:Inherit( self, AI_FORMATION:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT local self = BASE:Inherit( self, AI_FORMATION:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT
@@ -231,6 +238,13 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing )
self.EscortBriefing = EscortBriefing self.EscortBriefing = EscortBriefing
self.Menu = {} self.Menu = {}
self.Menu.HoldAtEscortPosition = self.Menu.HoldAtEscortPosition or {}
self.Menu.HoldAtLeaderPosition = self.Menu.HoldAtLeaderPosition or {}
self.Menu.Flare = self.Menu.Flare or {}
self.Menu.Smoke = self.Menu.Smoke or {}
self.Menu.Targets = self.Menu.Targets or {}
self.Menu.ROE = self.Menu.ROE or {}
self.Menu.ROT = self.Menu.ROT or {}
-- if not EscortBriefing then -- if not EscortBriefing then
-- EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. -- EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " ..
@@ -250,7 +264,7 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing )
EscortGroupSet:ForEachGroup( EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
-- Set EscortGroup known at EscortUnit. -- Set EscortGroup known at EscortUnit.
if not self.PlayerUnit._EscortGroups then if not self.PlayerUnit._EscortGroups then
@@ -325,14 +339,14 @@ function AI_ESCORT:_InitEscortRoute( EscortGroup )
end end
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Core.Set#SET_GROUP EscortGroupSet -- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT:onafterStart( EscortGroupSet ) function AI_ESCORT:onafterStart( EscortGroupSet )
self:F() self:F()
EscortGroupSet:ForEachGroup( EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
EscortGroup:WayPointInitialize() EscortGroup:WayPointInitialize()
@@ -370,7 +384,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet )
self:_InitFlightMenus() self:_InitFlightMenus()
self.EscortGroupSet:ForSomeGroupAlive( self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_InitEscortMenus( EscortGroup ) self:_InitEscortMenus( EscortGroup )
@@ -378,7 +392,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet )
self:SetFlightModeFormation( EscortGroup ) self:SetFlightModeFormation( EscortGroup )
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function EscortGroup:OnEventDeadOrCrash( EventData ) function EscortGroup:OnEventDeadOrCrash( EventData )
self:F( { "EventDead", EventData } ) self:F( { "EventDead", EventData } )
@@ -394,14 +408,14 @@ function AI_ESCORT:onafterStart( EscortGroupSet )
end end
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Core.Set#SET_GROUP EscortGroupSet -- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT:onafterStop( EscortGroupSet ) function AI_ESCORT:onafterStop( EscortGroupSet )
self:F() self:F()
EscortGroupSet:ForEachGroup( EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
EscortGroup:WayPointInitialize() EscortGroup:WayPointInitialize()
@@ -443,9 +457,9 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis. -- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #AI_ESCORT -- @return #AI_ESCORT
@@ -493,9 +507,9 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis. -- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #AI_ESCORT -- @return #AI_ESCORT
@@ -550,7 +564,7 @@ function AI_ESCORT:SetFlightMenuFormation( Formation )
local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation, local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation,
function ( self, Formation, ... ) function ( self, Formation, ... )
self.EscortGroupSet:ForSomeGroupAlive( self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup, self, Formation, Arguments ) function( EscortGroup, self, Formation, Arguments )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:E({FormationID=FormationID}) self:E({FormationID=FormationID})
@@ -580,7 +594,7 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationTrail( XStart, XSpace, YStart ) function AI_ESCORT:MenuFormationTrail( XStart, XSpace, YStart )
@@ -594,7 +608,7 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationStack( XStart, XSpace, YStart, YSpace ) function AI_ESCORT:MenuFormationStack( XStart, XSpace, YStart, YSpace )
@@ -609,8 +623,8 @@ end
-- This menu will appear under **Formation**. -- This menu will appear under **Formation**.
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationLeftLine( XStart, YStart, ZStart, ZSpace ) function AI_ESCORT:MenuFormationLeftLine( XStart, YStart, ZStart, ZSpace )
@@ -625,8 +639,8 @@ end
-- This menu will appear under **Formation**. -- This menu will appear under **Formation**.
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationRightLine( XStart, YStart, ZStart, ZSpace ) function AI_ESCORT:MenuFormationRightLine( XStart, YStart, ZStart, ZSpace )
@@ -642,8 +656,8 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationLeftWing( XStart, XSpace, YStart, ZStart, ZSpace ) function AI_ESCORT:MenuFormationLeftWing( XStart, XSpace, YStart, ZStart, ZSpace )
@@ -659,8 +673,8 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationRightWing( XStart, XSpace, YStart, ZStart, ZSpace ) function AI_ESCORT:MenuFormationRightWing( XStart, XSpace, YStart, ZStart, ZSpace )
@@ -676,9 +690,9 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationCenterWing( XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) function AI_ESCORT:MenuFormationCenterWing( XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
@@ -694,9 +708,9 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:MenuFormationVic( XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) function AI_ESCORT:MenuFormationVic( XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
@@ -712,9 +726,9 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis. -- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #AI_ESCORT -- @return #AI_ESCORT
@@ -764,7 +778,7 @@ end
function AI_ESCORT:SetFlightMenuHoldAtEscortPosition() function AI_ESCORT:SetFlightMenuHoldAtEscortPosition()
for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {} ) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuHoldPosition = MENU_GROUP_COMMAND local FlightMenuHoldPosition = MENU_GROUP_COMMAND
@@ -785,7 +799,7 @@ end
function AI_ESCORT:SetEscortMenuHoldAtEscortPosition( EscortGroup ) function AI_ESCORT:SetEscortMenuHoldAtEscortPosition( EscortGroup )
for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortGroup.EscortMenu ) local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortGroup.EscortMenu )
@@ -853,7 +867,7 @@ end
function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition() function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition()
for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND local FlightMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND
@@ -874,7 +888,7 @@ end
function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition( EscortGroup ) function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition( EscortGroup )
for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
@@ -999,7 +1013,7 @@ end
function AI_ESCORT:SetFlightMenuFlare() function AI_ESCORT:SetFlightMenuFlare()
for _, MenuFlare in pairs( self.Menu.Flare) do for _, MenuFlare in pairs( self.Menu.Flare or {}) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuFlare = MENU_GROUP:New( self.PlayerGroup, MenuFlare.MenuText, FlightMenuReportNavigation ) local FlightMenuFlare = MENU_GROUP:New( self.PlayerGroup, MenuFlare.MenuText, FlightMenuReportNavigation )
@@ -1014,7 +1028,7 @@ end
function AI_ESCORT:SetEscortMenuFlare( EscortGroup ) function AI_ESCORT:SetEscortMenuFlare( EscortGroup )
for _, MenuFlare in pairs( self.Menu.Flare) do for _, MenuFlare in pairs( self.Menu.Flare or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
@@ -1059,7 +1073,7 @@ end
function AI_ESCORT:SetFlightMenuSmoke() function AI_ESCORT:SetFlightMenuSmoke()
for _, MenuSmoke in pairs( self.Menu.Smoke) do for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuSmoke = MENU_GROUP:New( self.PlayerGroup, MenuSmoke.MenuText, FlightMenuReportNavigation ) local FlightMenuSmoke = MENU_GROUP:New( self.PlayerGroup, MenuSmoke.MenuText, FlightMenuReportNavigation )
@@ -1076,7 +1090,7 @@ end
function AI_ESCORT:SetEscortMenuSmoke( EscortGroup ) function AI_ESCORT:SetEscortMenuSmoke( EscortGroup )
for _, MenuSmoke in pairs( self.Menu.Smoke) do for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
@@ -1169,7 +1183,7 @@ function AI_ESCORT:SetFlightMenuTargets()
local FlightMenuAttackNearbyAir = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest airborne targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Air ):SetTag( "Attack" ) local FlightMenuAttackNearbyAir = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest airborne targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Air ):SetTag( "Attack" )
local FlightMenuAttackNearbyGround = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest ground targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Ground ):SetTag( "Attack" ) local FlightMenuAttackNearbyGround = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest ground targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Ground ):SetTag( "Attack" )
for _, MenuTargets in pairs( self.Menu.Targets) do for _, MenuTargets in pairs( self.Menu.Targets or {}) do
MenuTargets.FlightReportTargetsScheduler = SCHEDULER:New( self, self._FlightReportTargetsScheduler, {}, MenuTargets.Interval, MenuTargets.Interval ) MenuTargets.FlightReportTargetsScheduler = SCHEDULER:New( self, self._FlightReportTargetsScheduler, {}, MenuTargets.Interval, MenuTargets.Interval )
end end
@@ -1179,7 +1193,7 @@ end
function AI_ESCORT:SetEscortMenuTargets( EscortGroup ) function AI_ESCORT:SetEscortMenuTargets( EscortGroup )
for _, MenuTargets in pairs( self.Menu.Targets) do for _, MenuTargets in pairs( self.Menu.Targets or {} or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
--local EscortMenuReportTargets = MENU_GROUP:New( self.PlayerGroup, "Report targets", EscortGroup.EscortMenu ) --local EscortMenuReportTargets = MENU_GROUP:New( self.PlayerGroup, "Report targets", EscortGroup.EscortMenu )
@@ -1231,7 +1245,7 @@ function AI_ESCORT:MenuAssistedAttack()
self:F() self:F()
self.EscortGroupSet:ForSomeGroupAlive( self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if not EscortGroup:IsAir() then if not EscortGroup:IsAir() then
-- Request assistance from other escorts. -- Request assistance from other escorts.
@@ -1246,7 +1260,7 @@ end
function AI_ESCORT:SetFlightMenuROE() function AI_ESCORT:SetFlightMenuROE()
for _, MenuROE in pairs( self.Menu.ROE) do for _, MenuROE in pairs( self.Menu.ROE or {}) do
local FlightMenuROE = MENU_GROUP:New( self.PlayerGroup, "Rule Of Engagement", self.FlightMenu ) local FlightMenuROE = MENU_GROUP:New( self.PlayerGroup, "Rule Of Engagement", self.FlightMenu )
local FlightMenuROEHoldFire = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Hold fire", FlightMenuROE, AI_ESCORT._FlightROEHoldFire, self, "Holding weapons!" ) local FlightMenuROEHoldFire = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Hold fire", FlightMenuROE, AI_ESCORT._FlightROEHoldFire, self, "Holding weapons!" )
@@ -1261,7 +1275,7 @@ end
function AI_ESCORT:SetEscortMenuROE( EscortGroup ) function AI_ESCORT:SetEscortMenuROE( EscortGroup )
for _, MenuROE in pairs( self.Menu.ROE) do for _, MenuROE in pairs( self.Menu.ROE or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
@@ -1302,7 +1316,7 @@ end
function AI_ESCORT:SetFlightMenuROT() function AI_ESCORT:SetFlightMenuROT()
for _, MenuROT in pairs( self.Menu.ROT) do for _, MenuROT in pairs( self.Menu.ROT or {}) do
local FlightMenuROT = MENU_GROUP:New( self.PlayerGroup, "Reaction On Threat", self.FlightMenu ) local FlightMenuROT = MENU_GROUP:New( self.PlayerGroup, "Reaction On Threat", self.FlightMenu )
local FlightMenuROTNoReaction = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Fight until death", FlightMenuROT, AI_ESCORT._FlightROTNoReaction, self, "Fighting until death!" ) local FlightMenuROTNoReaction = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Fight until death", FlightMenuROT, AI_ESCORT._FlightROTNoReaction, self, "Fighting until death!" )
@@ -1317,7 +1331,7 @@ end
function AI_ESCORT:SetEscortMenuROT( EscortGroup ) function AI_ESCORT:SetEscortMenuROT( EscortGroup )
for _, MenuROT in pairs( self.Menu.ROT) do for _, MenuROT in pairs( self.Menu.ROT or {}) do
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName() local EscortGroupName = EscortGroup:GetName()
@@ -1375,7 +1389,7 @@ function AI_ESCORT:SetEscortMenuResumeMission( EscortGroup )
end end
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP OrbitGroup -- @param Wrapper.Group#GROUP OrbitGroup
-- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
-- @param #number OrbitHeight -- @param #number OrbitHeight
@@ -1419,7 +1433,7 @@ function AI_ESCORT:_HoldPosition( OrbitGroup, EscortGroup, OrbitHeight, OrbitSec
end end
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP OrbitGroup -- @param Wrapper.Group#GROUP OrbitGroup
-- @param #number OrbitHeight -- @param #number OrbitHeight
-- @param #number OrbitSeconds -- @param #number OrbitSeconds
@@ -1428,7 +1442,7 @@ function AI_ESCORT:_FlightHoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds )
local EscortUnit = self.PlayerUnit local EscortUnit = self.PlayerUnit
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup, OrbitGroup ) function( EscortGroup, OrbitGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
if OrbitGroup == nil then if OrbitGroup == nil then
@@ -1456,7 +1470,7 @@ end
function AI_ESCORT:_FlightJoinUp() function AI_ESCORT:_FlightJoinUp()
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_JoinUp( EscortGroup ) self:_JoinUp( EscortGroup )
@@ -1471,7 +1485,7 @@ end
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. -- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #AI_ESCORT -- @return #AI_ESCORT
function AI_ESCORT:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart ) function AI_ESCORT:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart )
@@ -1483,7 +1497,7 @@ end
function AI_ESCORT:_FlightFormationTrail( XStart, XSpace, YStart ) function AI_ESCORT:_FlightFormationTrail( XStart, XSpace, YStart )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart ) self:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart )
@@ -1510,7 +1524,7 @@ end
function AI_ESCORT:_FlightFormationStack( XStart, XSpace, YStart, YSpace ) function AI_ESCORT:_FlightFormationStack( XStart, XSpace, YStart, YSpace )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_EscortFormationStack( EscortGroup, XStart, XSpace, YStart, YSpace ) self:_EscortFormationStack( EscortGroup, XStart, XSpace, YStart, YSpace )
@@ -1533,7 +1547,7 @@ end
function AI_ESCORT:_FlightFlare( Color, Message ) function AI_ESCORT:_FlightFlare( Color, Message )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_Flare( EscortGroup, Color, Message ) self:_Flare( EscortGroup, Color, Message )
@@ -1556,7 +1570,7 @@ end
function AI_ESCORT:_FlightSmoke( Color, Message ) function AI_ESCORT:_FlightSmoke( Color, Message )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_Smoke( EscortGroup, Color, Message ) self:_Smoke( EscortGroup, Color, Message )
@@ -1587,7 +1601,7 @@ end
function AI_ESCORT:_FlightSwitchReportNearbyTargets( ReportTargets ) function AI_ESCORT:_FlightSwitchReportNearbyTargets( ReportTargets )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_EscortSwitchReportNearbyTargets( EscortGroup, ReportTargets ) self:_EscortSwitchReportNearbyTargets( EscortGroup, ReportTargets )
@@ -1679,7 +1693,7 @@ function AI_ESCORT:_ScanTargets( ScanDuration )
end end
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
-- @param #AI_ESCORT self -- @param #AI_ESCORT self
function AI_ESCORT.___Resume( EscortGroup, self ) function AI_ESCORT.___Resume( EscortGroup, self )
@@ -1701,7 +1715,7 @@ function AI_ESCORT.___Resume( EscortGroup, self )
end end
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
-- @param #number WayPoint -- @param #number WayPoint
function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint ) function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint )
@@ -1723,7 +1737,7 @@ function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint )
end end
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item. -- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item.
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem )
@@ -1743,7 +1757,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem )
local AttackUnitTasks = {} local AttackUnitTasks = {}
DetectedSet:ForEachUnit( DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit -- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks ) function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then if DetectedUnit:IsAlive() then
AttackUnitTasks[#AttackUnitTasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) AttackUnitTasks[#AttackUnitTasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit )
@@ -1767,7 +1781,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem )
local Tasks = {} local Tasks = {}
DetectedSet:ForEachUnit( DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit -- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks ) function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
@@ -1795,7 +1809,7 @@ end
function AI_ESCORT:_FlightAttackTarget( DetectedItem ) function AI_ESCORT:_FlightAttackTarget( DetectedItem )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup -- @param Core.Group#GROUP EscortGroup
function( EscortGroup, DetectedItem ) function( EscortGroup, DetectedItem )
if EscortGroup:IsAir() then if EscortGroup:IsAir() then
self:_AttackTarget( EscortGroup, DetectedItem ) self:_AttackTarget( EscortGroup, DetectedItem )
@@ -1842,7 +1856,7 @@ end
--- ---
--- @param #AI_ESCORT self -- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item. -- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item.
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem ) function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem )
@@ -1854,7 +1868,7 @@ function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem )
local Tasks = {} local Tasks = {}
DetectedSet:ForEachUnit( DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit -- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks ) function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
@@ -1881,7 +1895,7 @@ end
function AI_ESCORT:_FlightROEHoldFire( EscortROEMessage ) function AI_ESCORT:_FlightROEHoldFire( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEHoldFire, EscortROEMessage ) self:_ROE( EscortGroup, EscortGroup.OptionROEHoldFire, EscortROEMessage )
end end
@@ -1890,7 +1904,7 @@ end
function AI_ESCORT:_FlightROEOpenFire( EscortROEMessage ) function AI_ESCORT:_FlightROEOpenFire( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEOpenFire, EscortROEMessage ) self:_ROE( EscortGroup, EscortGroup.OptionROEOpenFire, EscortROEMessage )
end end
@@ -1899,7 +1913,7 @@ end
function AI_ESCORT:_FlightROEReturnFire( EscortROEMessage ) function AI_ESCORT:_FlightROEReturnFire( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEReturnFire, EscortROEMessage ) self:_ROE( EscortGroup, EscortGroup.OptionROEReturnFire, EscortROEMessage )
end end
@@ -1908,7 +1922,7 @@ end
function AI_ESCORT:_FlightROEWeaponFree( EscortROEMessage ) function AI_ESCORT:_FlightROEWeaponFree( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEWeaponFree, EscortROEMessage ) self:_ROE( EscortGroup, EscortGroup.OptionROEWeaponFree, EscortROEMessage )
end end
@@ -1924,7 +1938,7 @@ end
function AI_ESCORT:_FlightROTNoReaction( EscortROTMessage ) function AI_ESCORT:_FlightROTNoReaction( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTNoReaction, EscortROTMessage ) self:_ROT( EscortGroup, EscortGroup.OptionROTNoReaction, EscortROTMessage )
end end
@@ -1933,7 +1947,7 @@ end
function AI_ESCORT:_FlightROTPassiveDefense( EscortROTMessage ) function AI_ESCORT:_FlightROTPassiveDefense( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTPassiveDefense, EscortROTMessage ) self:_ROT( EscortGroup, EscortGroup.OptionROTPassiveDefense, EscortROTMessage )
end end
@@ -1942,7 +1956,7 @@ end
function AI_ESCORT:_FlightROTEvadeFire( EscortROTMessage ) function AI_ESCORT:_FlightROTEvadeFire( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTEvadeFire, EscortROTMessage ) self:_ROT( EscortGroup, EscortGroup.OptionROTEvadeFire, EscortROTMessage )
end end
@@ -1951,7 +1965,7 @@ end
function AI_ESCORT:_FlightROTVertical( EscortROTMessage ) function AI_ESCORT:_FlightROTVertical( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive( self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup ) function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTVertical, EscortROTMessage ) self:_ROT( EscortGroup, EscortGroup.OptionROTVertical, EscortROTMessage )
end end
@@ -2178,5 +2192,3 @@ function AI_ESCORT:_FlightReportTargetsScheduler()
return false return false
end end

View File

@@ -11,7 +11,7 @@
-- --
-- === -- ===
-- --
-- @module AI.AI_ESCORT_DISPATCHER_REQUEST -- @module AI.AI_Escort_Dispatcher_Request
-- @image MOOSE.JPG -- @image MOOSE.JPG

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Taking the lead of AI escorting your flight or of other AI, upon request using the menu. --- **AI** - Taking the lead of AI escorting your flight or of other AI, upon request using the menu.
-- --
-- === -- ===
-- --
@@ -143,7 +143,7 @@
-- --
-- === -- ===
-- --
-- @module AI.AI_Escort -- @module AI.AI_Escort_Request
-- @image Escorting.JPG -- @image Escorting.JPG

View File

@@ -1,4 +1,4 @@
--- **AI** -- Build large airborne formations of aircraft. --- **AI** - Build large airborne formations of aircraft.
-- --
-- **Features:** -- **Features:**
-- --
@@ -41,7 +41,7 @@
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader. --- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
-- --
-- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions. -- AI_FORMATION makes AI @{Wrapper.Group#GROUP}s fly in formation of various compositions.
-- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! -- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!!
-- The purpose of the class is to: -- The purpose of the class is to:
-- --
@@ -1140,7 +1140,7 @@ end
-- @param DCS#Vec3 CV2 Vec3 -- @param DCS#Vec3 CV2 Vec3
function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2) function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation and not self:Is("Stopped") then
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )}) self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})

View File

@@ -1,4 +1,4 @@
--- **AI** -- Perform Air Patrolling for airplanes. --- **AI** - Perform Air Patrolling for airplanes.
-- --
-- **Features:** -- **Features:**
-- --
@@ -16,7 +16,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
-- --
-- === -- ===
-- --
@@ -38,7 +38,7 @@
--- AI_PATROL_ZONE class --- AI_PATROL_ZONE class
-- @type AI_PATROL_ZONE -- @type AI_PATROL_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @field Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @field DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @field DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @field DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @field DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @field DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. -- @field DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
@@ -46,7 +46,7 @@
-- @field Core.Spawn#SPAWN CoordTest -- @field Core.Spawn#SPAWN CoordTest
-- @extends Core.Fsm#FSM_CONTROLLABLE -- @extends Core.Fsm#FSM_CONTROLLABLE
--- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}. --- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}.
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
-- --
@@ -135,15 +135,15 @@
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. -- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_PATROL_ZONE. -- while a new AI is targeted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place. -- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this process in place.
-- --
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE -- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
-- --
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. -- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB). -- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. -- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this process in place.
-- --
-- === -- ===
-- --
@@ -154,7 +154,7 @@ AI_PATROL_ZONE = {
--- Creates a new AI_PATROL_ZONE object --- Creates a new AI_PATROL_ZONE object
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. -- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
@@ -582,7 +582,7 @@ end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE. -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targeted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @param #number PatrolFuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. -- @param #number PatrolFuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
@@ -613,13 +613,13 @@ function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
return self return self
end end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @return #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To ) function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To )
self:F2() self:F2()
@@ -701,7 +701,7 @@ function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To )
end end
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable --- @param Wrapper.Controllable#CONTROLLABLE AIControllable
-- This statis method is called from the route path within the last task at the last waaypoint of the Controllable. -- This static method is called from the route path within the last task at the last waypoint of the Controllable.
-- Note that this method is required, as triggers the next route when patrolling for the Controllable. -- Note that this method is required, as triggers the next route when patrolling for the Controllable.
function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable ) function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable )
@@ -710,7 +710,7 @@ function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable )
end end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{#AI_PATROL_ZONE} parameters and settings.
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
@@ -725,8 +725,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
return return
end end
local life = self.Controllable:GetLife() or 0
if self.Controllable:IsAlive() then if self.Controllable:IsAlive() and life > 1 then
-- Determine if the AIControllable is within the PatrolZone. -- Determine if the AIControllable is within the PatrolZone.
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point. -- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
@@ -743,8 +743,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
if self.Controllable:InAir() == false then if self.Controllable:InAir() == false then
self:T( "Not in the air, finding route path within PatrolZone" ) self:T( "Not in the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --Done: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -758,8 +759,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
else else
self:T( "In the air, finding route path within PatrolZone" ) self:T( "In the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -773,7 +775,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
end end
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone. --- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in PatrolZone. --- Find a random 2D point in PatrolZone.
local ToTargetVec2 = self.PatrolZone:GetRandomVec2() local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
@@ -870,9 +872,10 @@ function AI_PATROL_ZONE:onafterRTB()
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return end
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(

View File

@@ -1,10 +1,10 @@
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Wrapper.Unit}s. --- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occurring on UNITs.
-- --
-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) -- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG)
-- --
-- === -- ===
-- --
-- @module Actions.Account -- @module Actions.Act_Account
-- @image MOOSE.JPG -- @image MOOSE.JPG
do -- ACT_ACCOUNT do -- ACT_ACCOUNT
@@ -20,7 +20,7 @@ do -- ACT_ACCOUNT
-- --
-- ### ACT_ACCOUNT States -- ### ACT_ACCOUNT States
-- --
-- * **Asigned**: The player is assigned. -- * **Assigned**: The player is assigned.
-- * **Waiting**: Waiting for an event. -- * **Waiting**: Waiting for an event.
-- * **Report**: Reporting. -- * **Report**: Reporting.
-- * **Account**: Account for an event. -- * **Account**: Account for an event.
@@ -104,7 +104,6 @@ do -- ACT_ACCOUNT
self:__Wait( 1 ) self:__Wait( 1 )
end end
--- StateMachine callback function --- StateMachine callback function
-- @param #ACT_ACCOUNT self -- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit -- @param Wrapper.Unit#UNIT ProcessUnit
@@ -141,7 +140,7 @@ do -- ACT_ACCOUNT_DEADS
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT} --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT}
-- --
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
-- The process is given a @{Set} of units that will be tracked upon successful destruction. -- The process is given a @{Core.Set} of units that will be tracked upon successful destruction.
-- The process will end after each target has been successfully destroyed. -- The process will end after each target has been successfully destroyed.
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered. -- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
-- --
@@ -157,7 +156,6 @@ do -- ACT_ACCOUNT_DEADS
ClassName = "ACT_ACCOUNT_DEADS", ClassName = "ACT_ACCOUNT_DEADS",
} }
--- Creates a new DESTROY process. --- Creates a new DESTROY process.
-- @param #ACT_ACCOUNT_DEADS self -- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Set#SET_UNIT TargetSetUnit
@@ -195,7 +193,6 @@ do -- ACT_ACCOUNT_DEADS
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
end end
--- StateMachine callback function --- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self -- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit -- @param Wrapper.Unit#UNIT ProcessUnit
@@ -270,7 +267,6 @@ do -- ACT_ACCOUNT_DEADS
end end
end end
--- DCS Events --- DCS Events
--- @param #ACT_ACCOUNT_DEADS self --- @param #ACT_ACCOUNT_DEADS self

View File

@@ -77,7 +77,7 @@
-- --
-- === -- ===
-- --
-- @module Actions.Assign -- @module Actions.Act_Assign
-- @image MOOSE.JPG -- @image MOOSE.JPG

View File

@@ -50,7 +50,7 @@
-- --
-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST} -- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST}
-- --
-- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}. -- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Core.Zone}.
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour. -- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
-- At random intervals, a new target is smoked. -- At random intervals, a new target is smoked.
-- --
@@ -60,7 +60,7 @@
-- --
-- === -- ===
-- --
-- @module Actions.Assist -- @module Actions.Act_Assist
-- @image MOOSE.JPG -- @image MOOSE.JPG

View File

@@ -62,7 +62,7 @@
-- --
-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE} -- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE}
-- --
-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}. -- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Core.Zone}.
-- The player receives on perioding times messages with the coordinates of the route to follow. -- The player receives on perioding times messages with the coordinates of the route to follow.
-- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended. -- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended.
-- --
@@ -72,7 +72,7 @@
-- --
-- === -- ===
-- --
-- @module Actions.Route -- @module Actions.Act_Route
-- @image MOOSE.JPG -- @image MOOSE.JPG

View File

@@ -66,7 +66,7 @@
-- you can board the cargo into the carrier `CargoCarrier`. -- you can board the cargo into the carrier `CargoCarrier`.
-- Simple, isn't it? Told you, and this is only the beginning. -- Simple, isn't it? Told you, and this is only the beginning.
-- --
-- The boarding, unboarding, loading, unloading of cargo is however something that is not meant to be coded manualy by mission designers. -- The boarding, unboarding, loading, unloading of cargo is however something that is not meant to be coded manually by mission designers.
-- It would be too low-level and not end-user friendly to deal with cargo handling complexity. -- It would be too low-level and not end-user friendly to deal with cargo handling complexity.
-- Things can become really complex if you want to make cargo being handled and behave in multiple scenarios. -- Things can become really complex if you want to make cargo being handled and behave in multiple scenarios.
-- --
@@ -77,8 +77,8 @@
-- --
-- ## 3.1) AI Cargo handlers. -- ## 3.1) AI Cargo handlers.
-- --
-- - @{AI.AI_Cargo_APC} will create for you the capatility to make an APC group handle cargo. -- - @{AI.AI_Cargo_APC} will create for you the capability to make an APC group handle cargo.
-- - @{AI.AI_Cargo_Helicopter} will create for you the capatility to make a Helicopter group handle cargo. -- - @{AI.AI_Cargo_Helicopter} will create for you the capability to make a Helicopter group handle cargo.
-- --
-- --
-- ## 3.2) AI Cargo transportation dispatchers. -- ## 3.2) AI Cargo transportation dispatchers.
@@ -86,7 +86,7 @@
-- There are also dispatchers that make AI work together to transport cargo automatically!!! -- There are also dispatchers that make AI work together to transport cargo automatically!!!
-- --
-- - @{AI.AI_Cargo_Dispatcher_APC} derived classes will create for your dynamic cargo handlers controlled by AI ground vehicle groups (APCs) to transport cargo between sites. -- - @{AI.AI_Cargo_Dispatcher_APC} derived classes will create for your dynamic cargo handlers controlled by AI ground vehicle groups (APCs) to transport cargo between sites.
-- - @{AI.AI_Cargo_Dispatcher_Helicopters} derived classes will create for your dynamic cargo handlers controlled by AI helicpter groups to transport cargo between sites. -- - @{AI.AI_Cargo_Dispatcher_Helicopters} derived classes will create for your dynamic cargo handlers controlled by AI helicopter groups to transport cargo between sites.
-- --
-- ## 3.3) Cargo transportation tasking. -- ## 3.3) Cargo transportation tasking.
-- --
@@ -94,7 +94,7 @@
-- --
-- - @{Tasking.Task_CARGO} derived classes will create for you cargo transportation tasks, that allow human players to interact with MOOSE cargo objects to complete tasks. -- - @{Tasking.Task_CARGO} derived classes will create for you cargo transportation tasks, that allow human players to interact with MOOSE cargo objects to complete tasks.
-- --
-- Please refer to the documentation reflected within these modules to understand the detailed capabilties. -- Please refer to the documentation reflected within these modules to understand the detailed capabilities.
-- --
-- # 4) Cargo SETs. -- # 4) Cargo SETs.
-- --
@@ -206,27 +206,29 @@
-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding. -- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding.
-- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters. -- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters.
-- --
-- ## 5.2) The \#CARGO tag to create CARGO_CRATE objects: -- ## 5.2) The \#CARGO tag to create CARGO_CRATE or CARGO_SLINGLOAD objects:
-- --
-- You can also use the \#CARGO tag on **static** objects, including **static cargo** objects of the mission editor. -- You can also use the \#CARGO tag on **static** objects, including **static cargo** objects of the mission editor.
-- --
-- For example, the following #CARGO naming in the **static name** of the object, will create a CARGO_CRATE object when the mission starts. -- For example, the following #CARGO naming in the **static name** of the object, will create a CARGO_CRATE object when the mission starts.
-- --
-- `Static #CARGO(T=Workmaterials,RR=500,NR=25)` -- `Static #CARGO(T=Workmaterials,C=CRATE,RR=500,NR=25)`
-- --
-- This will create a CARGO_CRATE object: -- This will create a CARGO_CRATE object:
-- --
-- * with the group name `Static #CARGO` -- * with the group name `Static #CARGO`
-- * is of type `Workmaterials` -- * is of type `Workmaterials`
-- * is of category `CRATE` (as opposed to `SLING`)
-- * will report when a carrier is within 500 meters -- * will report when a carrier is within 500 meters
-- * will board to carriers when the carrier is within 500 meters from the cargo object -- * will board to carriers when the carrier is within 500 meters from the cargo object
-- * will disappear when the cargo is within 25 meters from the carrier during boarding -- * will disappear when the cargo is within 25 meters from the carrier during boarding
-- --
-- So the overall syntax of the #CARGO naming tag and arguments are: -- So the overall syntax of the #CARGO naming tag and arguments are:
-- --
-- `StaticName #CARGO(T=CargoTypeName,RR=Range,NR=Range)` -- `StaticName #CARGO(T=CargoTypeName,C=Category,RR=Range,NR=Range)`
-- --
-- * **T=** Provide a text that contains the type name of the cargo object. This type name can be used to filter cargo within a SET_CARGO object. -- * **T=** Provide a text that contains the type name of the cargo object. This type name can be used to filter cargo within a SET_CARGO object.
-- * **C=** Provide either `CRATE` or `SLING` to have this static created as a CARGO_CRATE or CARGO_SLINGLOAD respectively.
-- * **RR=** Provide the minimal range in meters when the report to the carrier, and board to the carrier. -- * **RR=** Provide the minimal range in meters when the report to the carrier, and board to the carrier.
-- Note that this option is optional, so can be omitted. The default value of the RR is 250 meters. -- Note that this option is optional, so can be omitted. The default value of the RR is 250 meters.
-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding. -- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding.
@@ -429,7 +431,6 @@ do -- CARGO
--- @type CARGO.CargoObjects --- @type CARGO.CargoObjects
-- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. -- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo.
--- CARGO Constructor. This class is an abstract class and should not be instantiated. --- CARGO Constructor. This class is an abstract class and should not be instantiated.
-- @param #CARGO self -- @param #CARGO self
-- @param #string Type -- @param #string Type
@@ -479,11 +480,9 @@ do -- CARGO
CARGOS[self.Name] = self CARGOS[self.Name] = self
return self return self
end end
--- Find a CARGO in the _DATABASE. --- Find a CARGO in the _DATABASE.
-- @param #CARGO self -- @param #CARGO self
-- @param #string CargoName The Cargo Name. -- @param #string CargoName The Cargo Name.
@@ -527,7 +526,6 @@ do -- CARGO
end end
end end
--- Check if the cargo can be Slingloaded. --- Check if the cargo can be Slingloaded.
-- @param #CARGO self -- @param #CARGO self
function CARGO:CanSlingload() function CARGO:CanSlingload()
@@ -558,7 +556,6 @@ do -- CARGO
return true return true
end end
--- Destroy the cargo. --- Destroy the cargo.
-- @param #CARGO self -- @param #CARGO self
function CARGO:Destroy() function CARGO:Destroy()
@@ -611,7 +608,6 @@ do -- CARGO
return self.Type return self.Type
end end
--- Get the transportation method of the Cargo. --- Get the transportation method of the Cargo.
-- @param #CARGO self -- @param #CARGO self
-- @return #string The transportation method of the Cargo. -- @return #string The transportation method of the Cargo.
@@ -619,7 +615,6 @@ do -- CARGO
return self.TransportationMethod return self.TransportationMethod
end end
--- Get the coalition of the Cargo. --- Get the coalition of the Cargo.
-- @param #CARGO self -- @param #CARGO self
-- @return Coalition -- @return Coalition
@@ -631,7 +626,6 @@ do -- CARGO
end end
end end
--- Get the current coordinates of the Cargo. --- Get the current coordinates of the Cargo.
-- @param #CARGO self -- @param #CARGO self
-- @return Core.Point#COORDINATE The coordinates of the Cargo. -- @return Core.Point#COORDINATE The coordinates of the Cargo.
@@ -646,7 +640,6 @@ do -- CARGO
return self:Is( "Destroyed" ) return self:Is( "Destroyed" )
end end
--- Check if cargo is loaded. --- Check if cargo is loaded.
-- @param #CARGO self -- @param #CARGO self
-- @return #boolean true if loaded -- @return #boolean true if loaded
@@ -676,7 +669,6 @@ do -- CARGO
return self:Is( "Boarding" ) return self:Is( "Boarding" )
end end
--- Check if cargo is unboarding. --- Check if cargo is unboarding.
-- @param #CARGO self -- @param #CARGO self
-- @return #boolean true if unboarding -- @return #boolean true if unboarding
@@ -684,7 +676,6 @@ do -- CARGO
return self:Is( "UnBoarding" ) return self:Is( "UnBoarding" )
end end
--- Check if cargo is alive. --- Check if cargo is alive.
-- @param #CARGO self -- @param #CARGO self
-- @return #boolean true if unloaded -- @return #boolean true if unloaded
@@ -711,9 +702,6 @@ do -- CARGO
return self.Deployed return self.Deployed
end end
--- Template method to spawn a new representation of the CARGO in the simulator. --- Template method to spawn a new representation of the CARGO in the simulator.
-- @param #CARGO self -- @param #CARGO self
-- @return #CARGO -- @return #CARGO
@@ -799,7 +787,6 @@ do -- CARGO
self:Smoke( trigger.smokeColor.Blue, Range ) self:Smoke( trigger.smokeColor.Blue, Range )
end end
--- Set the Load radius, which is the radius till when the Cargo can be loaded. --- Set the Load radius, which is the radius till when the Cargo can be loaded.
-- @param #CARGO self -- @param #CARGO self
-- @param #number LoadRadius The radius till Cargo can be loaded. -- @param #number LoadRadius The radius till Cargo can be loaded.
@@ -815,8 +802,6 @@ do -- CARGO
return self.LoadRadius return self.LoadRadius
end end
--- Check if Cargo is in the LoadRadius for the Cargo to be Boarded or Loaded. --- Check if Cargo is in the LoadRadius for the Cargo to be Boarded or Loaded.
-- @param #CARGO self -- @param #CARGO self
-- @param Core.Point#COORDINATE Coordinate -- @param Core.Point#COORDINATE Coordinate
@@ -837,7 +822,6 @@ do -- CARGO
return false return false
end end
--- Check if the Cargo can report itself to be Boarded or Loaded. --- Check if the Cargo can report itself to be Boarded or Loaded.
-- @param #CARGO self -- @param #CARGO self
-- @param Core.Point#COORDINATE Coordinate -- @param Core.Point#COORDINATE Coordinate
@@ -884,9 +868,7 @@ do -- CARGO
return false return false
end end
--- Check if Cargo is the given @{Core.Zone}.
--- Check if Cargo is the given @{Zone}.
-- @param #CARGO self -- @param #CARGO self
-- @param Core.Zone#ZONE_BASE Zone -- @param Core.Zone#ZONE_BASE Zone
-- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. -- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone.
@@ -908,7 +890,6 @@ do -- CARGO
end end
--- Get the current PointVec2 of the cargo. --- Get the current PointVec2 of the cargo.
-- @param #CARGO self -- @param #CARGO self
-- @return Core.Point#POINT_VEC2 -- @return Core.Point#POINT_VEC2
@@ -992,7 +973,6 @@ do -- CARGO
end end
end end
--- Report to a Carrier Group with a Flaring signal. --- Report to a Carrier Group with a Flaring signal.
-- @param #CARGO self -- @param #CARGO self
-- @param Utils#UTILS.FlareColor FlareColor the color of the flare. -- @param Utils#UTILS.FlareColor FlareColor the color of the flare.
@@ -1002,7 +982,6 @@ do -- CARGO
self.ReportFlareColor = FlareColor self.ReportFlareColor = FlareColor
end end
--- Report to a Carrier Group with a Smoking signal. --- Report to a Carrier Group with a Smoking signal.
-- @param #CARGO self -- @param #CARGO self
-- @param Utils#UTILS.SmokeColor SmokeColor the color of the smoke. -- @param Utils#UTILS.SmokeColor SmokeColor the color of the smoke.
@@ -1012,7 +991,6 @@ do -- CARGO
self.ReportSmokeColor = SmokeColor self.ReportSmokeColor = SmokeColor
end end
--- Reset the reporting for a Carrier Group. --- Reset the reporting for a Carrier Group.
-- @param #CARGO self -- @param #CARGO self
-- @param #string Action The string describing the action for the cargo. -- @param #string Action The string describing the action for the cargo.
@@ -1047,9 +1025,6 @@ do -- CARGO
end end
end -- CARGO end -- CARGO
do -- CARGO_REPRESENTABLE do -- CARGO_REPRESENTABLE
@@ -1158,7 +1133,6 @@ do -- CARGO_REPRESENTABLE
end end
end -- CARGO_REPRESENTABLE end -- CARGO_REPRESENTABLE
do -- CARGO_REPORTABLE do -- CARGO_REPORTABLE
@@ -1195,16 +1169,8 @@ do -- CARGO_REPORTABLE
end end
end end
do -- CARGO_PACKAGE do -- CARGO_PACKAGE
--- @type CARGO_PACKAGE --- @type CARGO_PACKAGE
@@ -1425,5 +1391,4 @@ function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Dist
end end
end end

View File

@@ -1,4 +1,4 @@
--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. --- **Cargo** - Management of single cargo crates, which are based on a STATIC object.
-- --
-- === -- ===
-- --

View File

@@ -1,4 +1,4 @@
--- **Cargo** - Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object. --- **Cargo** - Management of grouped cargo logistics, which are based on a GROUP object.
-- --
-- === -- ===
-- --
@@ -47,7 +47,7 @@ do -- CARGO_GROUP
--- CARGO_GROUP constructor. --- CARGO_GROUP constructor.
-- This make a new CARGO_GROUP from a @{Wrapper.Group} object. -- This make a new CARGO_GROUP from a @{Wrapper.Group} object.
-- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. -- It will "ungroup" the group object within the sim, and will create a @{Core.Set} of individual Unit objects.
-- @param #CARGO_GROUP self -- @param #CARGO_GROUP self
-- @param Wrapper.Group#GROUP CargoGroup Group to be transported as cargo. -- @param Wrapper.Group#GROUP CargoGroup Group to be transported as cargo.
-- @param #string Type Cargo type, e.g. "Infantry". This is the type used in SET_CARGO:New():FilterTypes("Infantry") to define the valid cargo groups of the set. -- @param #string Type Cargo type, e.g. "Infantry". This is the type used in SET_CARGO:New():FilterTypes("Infantry") to define the valid cargo groups of the set.
@@ -727,7 +727,7 @@ do -- CARGO_GROUP
end end
end end
--- Check if the first element of the CargoGroup is the given @{Zone}. --- Check if the first element of the CargoGroup is the given @{Core.Zone}.
-- @param #CARGO_GROUP self -- @param #CARGO_GROUP self
-- @param Core.Zone#ZONE_BASE Zone -- @param Core.Zone#ZONE_BASE Zone
-- @return #boolean **true** if the first element of the CargoGroup is in the Zone -- @return #boolean **true** if the first element of the CargoGroup is in the Zone

View File

@@ -1,4 +1,4 @@
--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. The cargo can only be slingloaded. --- **Cargo** - Management of single cargo crates, which are based on a STATIC object. The cargo can only be slingloaded.
-- --
-- === -- ===
-- --

View File

@@ -1,4 +1,4 @@
--- **Cargo** - Management of single cargo logistics, which are based on a @{Wrapper.Unit} object. --- **Cargo** - Management of single cargo logistics, which are based on a UNIT object.
-- --
-- === -- ===
-- --

View File

@@ -0,0 +1,943 @@
--- **Core** - A* Pathfinding.
--
-- **Main Features:**
--
-- * Find path from A to B.
-- * Pre-defined as well as custom valid neighbour functions.
-- * Pre-defined as well as custom cost functions.
-- * Easy rectangular grid setup.
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Core.Astar
-- @image CORE_Astar.png
--- ASTAR class.
-- @type ASTAR
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file.
-- @field #table nodes Table of nodes.
-- @field #number counter Node counter.
-- @field #number Nnodes Number of nodes.
-- @field #number nvalid Number of nvalid calls.
-- @field #number nvalidcache Number of cached valid evals.
-- @field #number ncost Number of cost evaluations.
-- @field #number ncostcache Number of cached cost evals.
-- @field #ASTAR.Node startNode Start node.
-- @field #ASTAR.Node endNode End node.
-- @field Core.Point#COORDINATE startCoord Start coordinate.
-- @field Core.Point#COORDINATE endCoord End coordinate.
-- @field #function ValidNeighbourFunc Function to check if a node is valid.
-- @field #table ValidNeighbourArg Optional arguments passed to the valid neighbour function.
-- @field #function CostFunc Function to calculate the heuristic "cost" to go from one node to another.
-- @field #table CostArg Optional arguments passed to the cost function.
-- @extends Core.Base#BASE
--- *When nothing goes right... Go left!*
--
-- ===
--
-- # The ASTAR Concept
--
-- Pathfinding algorithm.
--
--
-- # Start and Goal
--
-- The first thing we need to define is obviously the place where we want to start and where we want to go eventually.
--
-- ## Start
--
-- The start
--
-- ## Goal
--
--
-- # Nodes
--
-- ## Rectangular Grid
--
-- A rectangular grid can be created using the @{#ASTAR.CreateGrid}(*ValidSurfaceTypes, BoxHY, SpaceX, deltaX, deltaY, MarkGrid*), where
--
-- * *ValidSurfaceTypes* is a table of valid surface types. By default all surface types are valid.
-- * *BoxXY* is the width of the grid perpendicular the the line between start and end node. Default is 40,000 meters (40 km).
-- * *SpaceX* is the additional space behind the start and end nodes. Default is 20,000 meters (20 km).
-- * *deltaX* is the grid spacing between nodes in the direction of start and end node. Default is 2,000 meters (2 km).
-- * *deltaY* is the grid spacing perpendicular to the direction of start and end node. Default is the same as *deltaX*.
-- * *MarkGrid* If set to *true*, this places marker on the F10 map on each grid node. Note that this can stall DCS if too many nodes are created.
--
-- ## Valid Surfaces
--
-- Certain unit types can only travel on certain surfaces types, for example
--
-- * Naval units can only travel on water (that also excludes shallow water in DCS currently),
-- * Ground units can only traval on land.
--
-- By restricting the surface type in the grid construction, we also reduce the number of nodes, which makes the algorithm more efficient.
--
-- ## Box Width (BoxHY)
--
-- The box width needs to be large enough to capture all paths you want to consider.
--
-- ## Space in X
--
-- The space in X value is important if the algorithm needs to to backwards from the start node or needs to extend even further than the end node.
--
-- ## Grid Spacing
--
-- The grid spacing is an important factor as it determines the number of nodes and hence the performance of the algorithm. It should be as large as possible.
-- However, if the value is too large, the algorithm might fail to get a valid path.
--
-- A good estimate of the grid spacing is to set it to be smaller (~ half the size) of the smallest gap you need to path.
--
-- # Valid Neighbours
--
-- The A* algorithm needs to know if a transition from one node to another is allowed or not. By default, hopping from one node to another is always possible.
--
-- ## Line of Sight
--
-- For naval
--
--
-- # Heuristic Cost
--
-- In order to determine the optimal path, the pathfinding algorithm needs to know, how costly it is to go from one node to another.
-- Often, this can simply be determined by the distance between two nodes. Therefore, the default cost function is set to be the 2D distance between two nodes.
--
--
-- # Calculate the Path
--
-- Finally, we have to calculate the path. This is done by the @{ASTAR.GetPath}(*ExcludeStart, ExcludeEnd*) function. This function returns a table of nodes, which
-- describe the optimal path from the start node to the end node.
--
-- By default, the start and end node are include in the table that is returned.
--
-- Note that a valid path must not always exist. So you should check if the function returns *nil*.
--
-- Common reasons that a path cannot be found are:
--
-- * The grid is too small ==> increase grid size, e.g. *BoxHY* and/or *SpaceX* if you use a rectangular grid.
-- * The grid spacing is too large ==> decrease *deltaX* and/or *deltaY*
-- * There simply is no valid path ==> you are screwed :(
--
--
-- # Examples
--
-- ## Strait of Hormuz
--
-- Carrier Group finds its way through the Stait of Hormuz.
--
-- ##
--
--
--
-- @field #ASTAR
ASTAR = {
ClassName = "ASTAR",
Debug = nil,
lid = nil,
nodes = {},
counter = 1,
Nnodes = 0,
ncost = 0,
ncostcache = 0,
nvalid = 0,
nvalidcache = 0,
}
--- Node data.
-- @type ASTAR.Node
-- @field #number id Node id.
-- @field Core.Point#COORDINATE coordinate Coordinate of the node.
-- @field #number surfacetype Surface type.
-- @field #table valid Cached valid/invalid nodes.
-- @field #table cost Cached cost.
--- ASTAR infinity.
-- @field #number INF
ASTAR.INF=1/0
--- ASTAR class version.
-- @field #string version
ASTAR.version="0.4.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add more valid neighbour functions.
-- TODO: Write docs.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new ASTAR object.
-- @param #ASTAR self
-- @return #ASTAR self
function ASTAR:New()
-- Inherit everything from INTEL class.
local self=BASE:Inherit(self, BASE:New()) --#ASTAR
self.lid="ASTAR | "
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set coordinate from where to start.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate Start coordinate.
-- @return #ASTAR self
function ASTAR:SetStartCoordinate(Coordinate)
self.startCoord=Coordinate
return self
end
--- Set coordinate where you want to go.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate end coordinate.
-- @return #ASTAR self
function ASTAR:SetEndCoordinate(Coordinate)
self.endCoord=Coordinate
return self
end
--- Create a node from a given coordinate.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to create the node.
-- @return #ASTAR.Node The node.
function ASTAR:GetNodeFromCoordinate(Coordinate)
local node={} --#ASTAR.Node
node.coordinate=Coordinate
node.surfacetype=Coordinate:GetSurfaceType()
node.id=self.counter
node.valid={}
node.cost={}
self.counter=self.counter+1
return node
end
--- Add a node to the table of grid nodes.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added.
-- @return #ASTAR self
function ASTAR:AddNode(Node)
self.nodes[Node.id]=Node
self.Nnodes=self.Nnodes+1
return self
end
--- Add a node to the table of grid nodes specifying its coordinate.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate The coordinate where the node is created.
-- @return #ASTAR.Node The node.
function ASTAR:AddNodeFromCoordinate(Coordinate)
local node=self:GetNodeFromCoordinate(Coordinate)
self:AddNode(node)
return node
end
--- Check if the coordinate of a node has is at a valid surface type.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added.
-- @param #table SurfaceTypes Surface types, for example `{land.SurfaceType.WATER}`. By default all surface types are valid.
-- @return #boolean If true, surface type of node is valid.
function ASTAR:CheckValidSurfaceType(Node, SurfaceTypes)
if SurfaceTypes then
if type(SurfaceTypes)~="table" then
SurfaceTypes={SurfaceTypes}
end
for _,surface in pairs(SurfaceTypes) do
if surface==Node.surfacetype then
return true
end
end
return false
else
return true
end
end
--- Add a function to determine if a neighbour of a node is valid.
-- @param #ASTAR self
-- @param #function NeighbourFunction Function that needs to return *true* for a neighbour to be valid.
-- @param ... Condition function arguments if any.
-- @return #ASTAR self
function ASTAR:SetValidNeighbourFunction(NeighbourFunction, ...)
self.ValidNeighbourFunc=NeighbourFunction
self.ValidNeighbourArg={}
if arg then
self.ValidNeighbourArg=arg
end
return self
end
--- Set valid neighbours to require line of sight between two nodes.
-- @param #ASTAR self
-- @param #number CorridorWidth Width of LoS corridor in meters.
-- @return #ASTAR self
function ASTAR:SetValidNeighbourLoS(CorridorWidth)
self:SetValidNeighbourFunction(ASTAR.LoS, CorridorWidth)
return self
end
--- Set valid neighbours to be in a certain distance.
-- @param #ASTAR self
-- @param #number MaxDistance Max distance between nodes in meters. Default is 2000 m.
-- @return #ASTAR self
function ASTAR:SetValidNeighbourDistance(MaxDistance)
self:SetValidNeighbourFunction(ASTAR.DistMax, MaxDistance)
return self
end
--- Set valid neighbours to be in a certain distance.
-- @param #ASTAR self
-- @param #number MaxDistance Max distance between nodes in meters. Default is 2000 m.
-- @return #ASTAR self
function ASTAR:SetValidNeighbourRoad(MaxDistance)
self:SetValidNeighbourFunction(ASTAR.Road, MaxDistance)
return self
end
--- Set the function which calculates the "cost" to go from one to another node.
-- The first to arguments of this function are always the two nodes under consideration. But you can add optional arguments.
-- Very often the distance between nodes is a good measure for the cost.
-- @param #ASTAR self
-- @param #function CostFunction Function that returns the "cost".
-- @param ... Condition function arguments if any.
-- @return #ASTAR self
function ASTAR:SetCostFunction(CostFunction, ...)
self.CostFunc=CostFunction
self.CostArg={}
if arg then
self.CostArg=arg
end
return self
end
--- Set heuristic cost to go from one node to another to be their 2D distance.
-- @param #ASTAR self
-- @return #ASTAR self
function ASTAR:SetCostDist2D()
self:SetCostFunction(ASTAR.Dist2D)
return self
end
--- Set heuristic cost to go from one node to another to be their 3D distance.
-- @param #ASTAR self
-- @return #ASTAR self
function ASTAR:SetCostDist3D()
self:SetCostFunction(ASTAR.Dist3D)
return self
end
--- Set heuristic cost to go from one node to another to be their 3D distance.
-- @param #ASTAR self
-- @return #ASTAR self
function ASTAR:SetCostRoad()
self:SetCostFunction(ASTAR)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Grid functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a rectangular grid of nodes between star and end coordinate.
-- The coordinate system is oriented along the line between start and end point.
-- @param #ASTAR self
-- @param #table ValidSurfaceTypes Valid surface types. By default is all surfaces are allowed.
-- @param #number BoxHY Box "height" in meters along the y-coordinate. Default 40000 meters (40 km).
-- @param #number SpaceX Additional space in meters before start and after end coordinate. Default 10000 meters (10 km).
-- @param #number deltaX Increment in the direction of start to end coordinate in meters. Default 2000 meters.
-- @param #number deltaY Increment perpendicular to the direction of start to end coordinate in meters. Default is same as deltaX.
-- @param #boolean MarkGrid If true, create F10 map markers at grid nodes.
-- @return #ASTAR self
function ASTAR:CreateGrid(ValidSurfaceTypes, BoxHY, SpaceX, deltaX, deltaY, MarkGrid)
-- Note that internally
-- x coordinate is z: x-->z Line from start to end
-- y coordinate is x: y-->x Perpendicular
-- Grid length and width.
local Dz=SpaceX or 10000
local Dx=BoxHY and BoxHY/2 or 20000
-- Increments.
local dz=deltaX or 2000
local dx=deltaY or dz
-- Heading from start to end coordinate.
local angle=self.startCoord:HeadingTo(self.endCoord)
--Distance between start and end.
local dist=self.startCoord:Get2DDistance(self.endCoord)+2*Dz
-- Origin of map. Needed to translate back to wanted position.
local co=COORDINATE:New(0, 0, 0)
local do1=co:Get2DDistance(self.startCoord)
local ho1=co:HeadingTo(self.startCoord)
-- Start of grid.
local xmin=-Dx
local zmin=-Dz
-- Number of grid points.
local nz=dist/dz+1
local nx=2*Dx/dx+1
-- Debug info.
local text=string.format("Building grid with nx=%d ny=%d => total=%d nodes", nx, nz, nx*nz)
self:T(self.lid..text)
-- Loop over x and z coordinate to create a 2D grid.
for i=1,nx do
-- x coordinate perpendicular to z.
local x=xmin+dx*(i-1)
for j=1,nz do
-- z coordinate connecting start and end.
local z=zmin+dz*(j-1)
-- Rotate 2D.
local vec3=UTILS.Rotate2D({x=x, y=0, z=z}, angle)
-- Coordinate of the node.
local c=COORDINATE:New(vec3.z, vec3.y, vec3.x):Translate(do1, ho1, true)
-- Create a node at this coordinate.
local node=self:GetNodeFromCoordinate(c)
-- Check if node has valid surface type.
if self:CheckValidSurfaceType(node, ValidSurfaceTypes) then
if MarkGrid then
c:MarkToAll(string.format("i=%d, j=%d surface=%d", i, j, node.surfacetype))
end
-- Add node to grid.
self:AddNode(node)
end
end
end
-- Debug info.
local text=string.format("Done building grid!")
self:T2(self.lid..text)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Valid neighbour functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Function to check if two nodes have line of sight (LoS).
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @param #number corridor (Optional) Width of corridor in meters.
-- @return #boolean If true, two nodes have LoS.
function ASTAR.LoS(nodeA, nodeB, corridor)
local offset=1
local dx=corridor and corridor/2 or nil
local dy=dx
local cA=nodeA.coordinate:GetVec3()
local cB=nodeB.coordinate:GetVec3()
cA.y=offset
cB.y=offset
local los=land.isVisible(cA, cB)
if los and corridor then
-- Heading from A to B.
local heading=nodeA.coordinate:HeadingTo(nodeB.coordinate)
local Ap=UTILS.VecTranslate(cA, dx, heading+90)
local Bp=UTILS.VecTranslate(cB, dx, heading+90)
los=land.isVisible(Ap, Bp)
if los then
local Am=UTILS.VecTranslate(cA, dx, heading-90)
local Bm=UTILS.VecTranslate(cB, dx, heading-90)
los=land.isVisible(Am, Bm)
end
end
return los
end
--- Function to check if two nodes have a road connection.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @return #boolean If true, two nodes are connected via a road.
function ASTAR.Road(nodeA, nodeB)
local path=land.findPathOnRoads("roads", nodeA.coordinate.x, nodeA.coordinate.z, nodeB.coordinate.x, nodeB.coordinate.z)
if path then
return true
else
return false
end
end
--- Function to check if distance between two nodes is less than a threshold distance.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @param #number distmax Max distance in meters. Default is 2000 m.
-- @return #boolean If true, distance between the two nodes is below threshold.
function ASTAR.DistMax(nodeA, nodeB, distmax)
distmax=distmax or 2000
local dist=nodeA.coordinate:Get2DDistance(nodeB.coordinate)
return dist<=distmax
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Heuristic cost functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Heuristic cost is given by the 2D distance between the nodes.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @return #number Distance between the two nodes.
function ASTAR.Dist2D(nodeA, nodeB)
local dist=nodeA.coordinate:Get2DDistance(nodeB)
return dist
end
--- Heuristic cost is given by the 3D distance between the nodes.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @return #number Distance between the two nodes.
function ASTAR.Dist3D(nodeA, nodeB)
local dist=nodeA.coordinate:Get3DDistance(nodeB.coordinate)
return dist
end
--- Heuristic cost is given by the distance between the nodes on road.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @return #number Distance between the two nodes.
function ASTAR.DistRoad(nodeA, nodeB)
-- Get the path.
local path=land.findPathOnRoads("roads", nodeA.coordinate.x, nodeA.coordinate.z, nodeB.coordinate.x, nodeB.coordinate.z)
if path then
local dist=0
for i=2,#path do
local b=path[i] --DCS#Vec2
local a=path[i-1] --DCS#Vec2
dist=dist+UTILS.VecDist2D(a,b)
end
return dist
end
return math.huge
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Find the closest node from a given coordinate.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate.
-- @return #ASTAR.Node Cloest node to the coordinate.
-- @return #number Distance to closest node in meters.
function ASTAR:FindClosestNode(Coordinate)
local distMin=math.huge
local closeNode=nil
for _,_node in pairs(self.nodes) do
local node=_node --#ASTAR.Node
local dist=node.coordinate:Get2DDistance(Coordinate)
if dist<distMin then
distMin=dist
closeNode=node
end
end
return closeNode, distMin
end
--- Find the start node.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added to the nodes table.
-- @return #ASTAR self
function ASTAR:FindStartNode()
local node, dist=self:FindClosestNode(self.startCoord)
self.startNode=node
if dist>1000 then
self:T(self.lid.."Adding start node to node grid!")
self:AddNode(node)
end
return self
end
--- Add a node.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added to the nodes table.
-- @return #ASTAR self
function ASTAR:FindEndNode()
local node, dist=self:FindClosestNode(self.endCoord)
self.endNode=node
if dist>1000 then
self:T(self.lid.."Adding end node to node grid!")
self:AddNode(node)
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Main A* pathfinding function
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- A* pathfinding function. This seaches the path along nodes between start and end nodes/coordinates.
-- @param #ASTAR self
-- @param #boolean ExcludeStartNode If *true*, do not include start node in found path. Default is to include it.
-- @param #boolean ExcludeEndNode If *true*, do not include end node in found path. Default is to include it.
-- @return #table Table of nodes from start to finish.
function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
self:FindStartNode()
self:FindEndNode()
local nodes=self.nodes
local start=self.startNode
local goal=self.endNode
-- Sets.
local openset = {}
local closedset = {}
local came_from = {}
local g_score = {}
local f_score = {}
openset[start.id]=true
local Nopen=1
-- Initial scores.
g_score[start.id]=0
f_score[start.id]=g_score[start.id]+self:_HeuristicCost(start, goal)
-- Set start time.
local T0=timer.getAbsTime()
-- Debug message.
local text=string.format("Starting A* pathfinding with %d Nodes", self.Nnodes)
self:T(self.lid..text)
local Tstart=UTILS.GetOSTime()
-- Loop while we still have an open set.
while Nopen > 0 do
-- Get current node.
local current=self:_LowestFscore(openset, f_score)
-- Check if we are at the end node.
if current.id==goal.id then
local path=self:_UnwindPath({}, came_from, goal)
if not ExcludeEndNode then
table.insert(path, goal)
end
if ExcludeStartNode then
table.remove(path, 1)
end
local Tstop=UTILS.GetOSTime()
local dT=nil
if Tstart and Tstop then
dT=Tstop-Tstart
end
-- Debug message.
local text=string.format("Found path with %d nodes (%d total)", #path, self.Nnodes)
if dT then
text=text..string.format(", OS Time %.6f sec", dT)
end
text=text..string.format(", Nvalid=%d [%d cached]", self.nvalid, self.nvalidcache)
text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
self:T(self.lid..text)
return path
end
-- Move Node from open to closed set.
openset[current.id]=nil
Nopen=Nopen-1
closedset[current.id]=true
-- Get neighbour nodes.
local neighbors=self:_NeighbourNodes(current, nodes)
-- Loop over neighbours.
for _,neighbor in pairs(neighbors) do
if self:_NotIn(closedset, neighbor.id) then
local tentative_g_score=g_score[current.id]+self:_DistNodes(current, neighbor)
if self:_NotIn(openset, neighbor.id) or tentative_g_score < g_score[neighbor.id] then
came_from[neighbor]=current
g_score[neighbor.id]=tentative_g_score
f_score[neighbor.id]=g_score[neighbor.id]+self:_HeuristicCost(neighbor, goal)
if self:_NotIn(openset, neighbor.id) then
-- Add to open set.
openset[neighbor.id]=true
Nopen=Nopen+1
end
end
end
end
end
-- Debug message.
local text=string.format("WARNING: Could NOT find valid path!")
self:E(self.lid..text)
MESSAGE:New(text, 60, "ASTAR"):ToAllIf(self.Debug)
return nil -- no valid path
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- A* pathfinding helper functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Heuristic "cost" function to go from node A to node B. Default is the distance between the nodes.
-- @param #ASTAR self
-- @param #ASTAR.Node nodeA Node A.
-- @param #ASTAR.Node nodeB Node B.
-- @return #number "Cost" to go from node A to node B.
function ASTAR:_HeuristicCost(nodeA, nodeB)
-- Counter.
self.ncost=self.ncost+1
-- Get chached cost if available.
local cost=nodeA.cost[nodeB.id]
if cost~=nil then
self.ncostcache=self.ncostcache+1
return cost
end
local cost=nil
if self.CostFunc then
cost=self.CostFunc(nodeA, nodeB, unpack(self.CostArg))
else
cost=self:_DistNodes(nodeA, nodeB)
end
nodeA.cost[nodeB.id]=cost
nodeB.cost[nodeA.id]=cost -- Symmetric problem.
return cost
end
--- Check if going from a node to a neighbour is possible.
-- @param #ASTAR self
-- @param #ASTAR.Node node A node.
-- @param #ASTAR.Node neighbor Neighbour node.
-- @return #boolean If true, transition between nodes is possible.
function ASTAR:_IsValidNeighbour(node, neighbor)
-- Counter.
self.nvalid=self.nvalid+1
local valid=node.valid[neighbor.id]
if valid~=nil then
--env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id))
self.nvalidcache=self.nvalidcache+1
return valid
end
local valid=nil
if self.ValidNeighbourFunc then
valid=self.ValidNeighbourFunc(node, neighbor, unpack(self.ValidNeighbourArg))
else
valid=true
end
node.valid[neighbor.id]=valid
neighbor.valid[node.id]=valid -- Symmetric problem.
return valid
end
--- Calculate 2D distance between two nodes.
-- @param #ASTAR self
-- @param #ASTAR.Node nodeA Node A.
-- @param #ASTAR.Node nodeB Node B.
-- @return #number Distance between nodes in meters.
function ASTAR:_DistNodes(nodeA, nodeB)
return nodeA.coordinate:Get2DDistance(nodeB.coordinate)
end
--- Function that calculates the lowest F score.
-- @param #ASTAR self
-- @param #table set The set of nodes IDs.
-- @param #number f_score F score.
-- @return #ASTAR.Node Best node.
function ASTAR:_LowestFscore(set, f_score)
local lowest, bestNode = ASTAR.INF, nil
for nid,node in pairs(set) do
local score=f_score[nid]
if score<lowest then
lowest, bestNode = score, nid
end
end
return self.nodes[bestNode]
end
--- Function to get valid neighbours of a node.
-- @param #ASTAR self
-- @param #ASTAR.Node theNode The node.
-- @param #table nodes Possible neighbours.
-- @param #table Valid neighbour nodes.
function ASTAR:_NeighbourNodes(theNode, nodes)
local neighbors = {}
for _,node in pairs(nodes) do
if theNode.id~=node.id then
local isvalid=self:_IsValidNeighbour(theNode, node)
if isvalid then
table.insert(neighbors, node)
end
end
end
return neighbors
end
--- Function to check if a node is not in a set.
-- @param #ASTAR self
-- @param #table set Set of nodes.
-- @param #ASTAR.Node theNode The node to check.
-- @return #boolean If true, the node is not in the set.
function ASTAR:_NotIn(set, theNode)
return set[theNode]==nil
end
--- Unwind path function.
-- @param #ASTAR self
-- @param #table flat_path Flat path.
-- @param #table map Map.
-- @param #ASTAR.Node current_node The current node.
-- @return #table Unwinded path.
function ASTAR:_UnwindPath( flat_path, map, current_node )
if map [current_node] then
table.insert (flat_path, 1, map[current_node])
return self:_UnwindPath(flat_path, map, map[current_node])
else
return flat_path
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -157,7 +157,7 @@ local _ClassID = 0
-- self:SmokeBlue() -- self:SmokeBlue()
-- end -- end
-- --
-- See the @{Event} module for more information about event handling. -- See the @{Core.Event} module for more information about event handling.
-- --
-- # 4. Class identification methods. -- # 4. Class identification methods.
-- --
@@ -229,7 +229,8 @@ FORMATION = {
-- @param #BASE self -- @param #BASE self
-- @return #BASE -- @return #BASE
function BASE:New() function BASE:New()
local self = routines.utils.deepCopy( self ) -- Create a new self instance --local self = routines.utils.deepCopy( self ) -- Create a new self instance
local self = UTILS.DeepCopy(self)
_ClassID = _ClassID + 1 _ClassID = _ClassID + 1
self.ClassID = _ClassID self.ClassID = _ClassID
@@ -411,20 +412,20 @@ do -- Event Handling
return _EVENTDISPATCHER return _EVENTDISPATCHER
end end
--- Get the Class @{Event} processing Priority. --- Get the Class @{Core.Event} processing Priority.
-- The Event processing Priority is a number from 1 to 10, -- The Event processing Priority is a number from 1 to 10,
-- reflecting the order of the classes subscribed to the Event to be processed. -- reflecting the order of the classes subscribed to the Event to be processed.
-- @param #BASE self -- @param #BASE self
-- @return #number The @{Event} processing Priority. -- @return #number The @{Core.Event} processing Priority.
function BASE:GetEventPriority() function BASE:GetEventPriority()
return self._.EventPriority or 5 return self._.EventPriority or 5
end end
--- Set the Class @{Event} processing Priority. --- Set the Class @{Core.Event} processing Priority.
-- The Event processing Priority is a number from 1 to 10, -- The Event processing Priority is a number from 1 to 10,
-- reflecting the order of the classes subscribed to the Event to be processed. -- reflecting the order of the classes subscribed to the Event to be processed.
-- @param #BASE self -- @param #BASE self
-- @param #number EventPriority The @{Event} processing Priority. -- @param #number EventPriority The @{Core.Event} processing Priority.
-- @return #BASE self -- @return #BASE self
function BASE:SetEventPriority( EventPriority ) function BASE:SetEventPriority( EventPriority )
self._.EventPriority = EventPriority self._.EventPriority = EventPriority
@@ -463,131 +464,152 @@ do -- Event Handling
return self return self
end end
-- Event handling function prototypes -- Event handling function prototypes - Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
--- Occurs whenever any unit in a mission fires a weapon. But not any machine gun or autocannon based weapon, those are handled by EVENT.ShootingStart. --- Occurs whenever any unit in a mission fires a weapon. But not any machine gun or autocannon based weapon, those are handled by EVENT.ShootingStart.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventShot -- @function [parent=#BASE] OnEventShot
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs whenever an object is hit by a weapon. --- Occurs whenever an object is hit by a weapon.
-- initiator : The unit object the fired the weapon. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- weapon: Weapon object that hit the target. -- initiator : The unit object the fired the weapon
-- weapon: Weapon object that hit the target
-- target: The Object that was hit. -- target: The Object that was hit.
-- @function [parent=#BASE] OnEventHit -- @function [parent=#BASE] OnEventHit
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an aircraft takes off from an airbase, farp, or ship. --- Occurs when an aircraft takes off from an airbase, farp, or ship.
-- initiator : The unit that took off. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships. -- initiator : The unit that tookoff
-- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships
-- @function [parent=#BASE] OnEventTakeoff -- @function [parent=#BASE] OnEventTakeoff
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an aircraft lands at an airbase, farp or ship --- Occurs when an aircraft lands at an airbase, farp or ship
-- initiator : The unit that has landed. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships. -- initiator : The unit that has landed
-- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships
-- @function [parent=#BASE] OnEventLand -- @function [parent=#BASE] OnEventLand
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any aircraft crashes into the ground and is completely destroyed. --- Occurs when any aircraft crashes into the ground and is completely destroyed.
-- initiator : The unit that has crashed. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that has crashed
-- @function [parent=#BASE] OnEventCrash -- @function [parent=#BASE] OnEventCrash
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a pilot ejects from an aircraft --- Occurs when a pilot ejects from an aircraft
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that has ejected -- initiator : The unit that has ejected
-- @function [parent=#BASE] OnEventEjection -- @function [parent=#BASE] OnEventEjection
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an aircraft connects with a tanker and begins taking on fuel. --- Occurs when an aircraft connects with a tanker and begins taking on fuel.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that is receiving fuel. -- initiator : The unit that is receiving fuel.
-- @function [parent=#BASE] OnEventRefueling -- @function [parent=#BASE] OnEventRefueling
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an object is dead. --- Occurs when an object is dead.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that is dead. -- initiator : The unit that is dead.
-- @function [parent=#BASE] OnEventDead -- @function [parent=#BASE] OnEventDead
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an object is completely destroyed. --- Occurs when an Event for an object is triggered.
-- initiator : The unit that is was destroyed. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that triggered the event.
-- @function [parent=#BASE] OnEvent -- @function [parent=#BASE] OnEvent
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when the pilot of an aircraft is killed. Can occur either if the player is alive and crashes or if a weapon kills the pilot without completely destroying the plane. --- Occurs when the pilot of an aircraft is killed. Can occur either if the player is alive and crashes or if a weapon kills the pilot without completely destroying the plane.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that the pilot has died in. -- initiator : The unit that the pilot has died in.
-- @function [parent=#BASE] OnEventPilotDead -- @function [parent=#BASE] OnEventPilotDead
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a ground unit captures either an airbase or a farp. --- Occurs when a ground unit captures either an airbase or a farp.
-- initiator : The unit that captured the base. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that captured the base
-- place: The airbase that was captured, can be a FARP or Airbase. When calling place:getCoalition() the faction will already be the new owning faction. -- place: The airbase that was captured, can be a FARP or Airbase. When calling place:getCoalition() the faction will already be the new owning faction.
-- @function [parent=#BASE] OnEventBaseCaptured -- @function [parent=#BASE] OnEventBaseCaptured
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a mission starts. --- Occurs when a mission starts
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventMissionStart -- @function [parent=#BASE] OnEventMissionStart
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a mission ends. --- Occurs when a mission ends
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventMissionEnd -- @function [parent=#BASE] OnEventMissionEnd
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an aircraft is finished taking fuel. --- Occurs when an aircraft is finished taking fuel.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that was receiving fuel. -- initiator : The unit that was receiving fuel.
-- @function [parent=#BASE] OnEventRefuelingStop -- @function [parent=#BASE] OnEventRefuelingStop
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any object is spawned into the mission. --- Occurs when any object is spawned into the mission.
-- initiator : The unit that was spawned. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that was spawned
-- @function [parent=#BASE] OnEventBirth -- @function [parent=#BASE] OnEventBirth
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any system fails on a human controlled aircraft. --- Occurs when any system fails on a human controlled aircraft.
-- initiator : The unit that had the failure. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that had the failure
-- @function [parent=#BASE] OnEventHumanFailure -- @function [parent=#BASE] OnEventHumanFailure
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any aircraft starts its engines. --- Occurs when any aircraft starts its engines.
-- initiator : The unit that is starting its engines.. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that is starting its engines.
-- @function [parent=#BASE] OnEventEngineStartup -- @function [parent=#BASE] OnEventEngineStartup
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any aircraft shuts down its engines. --- Occurs when any aircraft shuts down its engines.
-- initiator : The unit that is stopping its engines.. -- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that is stopping its engines.
-- @function [parent=#BASE] OnEventEngineShutdown -- @function [parent=#BASE] OnEventEngineShutdown
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any player assumes direct control of a unit. --- Occurs when any player assumes direct control of a unit. Note - not Mulitplayer safe. Use PlayerEnterAircraft.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that is being taken control of. -- initiator : The unit that is being taken control of.
-- @function [parent=#BASE] OnEventPlayerEnterUnit -- @function [parent=#BASE] OnEventPlayerEnterUnit
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any player relieves control of a unit to the AI. --- Occurs when any player relieves control of a unit to the AI.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that the player left. -- initiator : The unit that the player left.
-- @function [parent=#BASE] OnEventPlayerLeaveUnit -- @function [parent=#BASE] OnEventPlayerLeaveUnit
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any unit begins firing a weapon that has a high rate of fire. Most common with aircraft cannons (GAU-8), autocannons, and machine guns. --- Occurs when any unit begins firing a weapon that has a high rate of fire. Most common with aircraft cannons (GAU-8), autocannons, and machine guns.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that is doing the shooting. -- initiator : The unit that is doing the shooting.
-- target: The unit that is being targeted. -- target: The unit that is being targeted.
-- @function [parent=#BASE] OnEventShootingStart -- @function [parent=#BASE] OnEventShootingStart
@@ -595,24 +617,28 @@ do -- Event Handling
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when any unit stops firing its weapon. Event will always correspond with a shooting start event. --- Occurs when any unit stops firing its weapon. Event will always correspond with a shooting start event.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- initiator : The unit that was doing the shooting. -- initiator : The unit that was doing the shooting.
-- @function [parent=#BASE] OnEventShootingEnd -- @function [parent=#BASE] OnEventShootingEnd
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a new mark was added. --- Occurs when a new mark was added.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- MarkID: ID of the mark. -- MarkID: ID of the mark.
-- @function [parent=#BASE] OnEventMarkAdded -- @function [parent=#BASE] OnEventMarkAdded
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a mark was removed. --- Occurs when a mark was removed.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- MarkID: ID of the mark. -- MarkID: ID of the mark.
-- @function [parent=#BASE] OnEventMarkRemoved -- @function [parent=#BASE] OnEventMarkRemoved
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a mark text was changed. --- Occurs when a mark text was changed.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- MarkID: ID of the mark. -- MarkID: ID of the mark.
-- @function [parent=#BASE] OnEventMarkChange -- @function [parent=#BASE] OnEventMarkChange
-- @param #BASE self -- @param #BASE self
@@ -628,13 +654,15 @@ do -- Event Handling
--- Occurs when any modification to the "Score" as seen on the debrief menu would occur. --- Occurs when any modification to the "Score" as seen on the debrief menu would occur.
-- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard. -- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventScore -- @function [parent=#BASE] OnEventScore
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs on the death of a unit. Contains more and different information. Similar to unit_lost it will occur for aircraft before the aircraft crash event occurs. --- Occurs on the death of a unit. Contains more and different information. Similar to unit_lost it will occur for aircraft before the aircraft crash event occurs.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- --
-- * initiator: The unit that killed the target. -- * initiator: The unit that killed the target
-- * target: Target Object -- * target: Target Object
-- * weapon: Weapon Object -- * weapon: Weapon Object
-- --
@@ -644,11 +672,13 @@ do -- Event Handling
--- Occurs when any modification to the "Score" as seen on the debrief menu would occur. --- Occurs when any modification to the "Score" as seen on the debrief menu would occur.
-- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard. -- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventScore -- @function [parent=#BASE] OnEventScore
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when the game thinks an object is destroyed. --- Occurs when the game thinks an object is destroyed.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- --
-- * initiator: The unit that is was destroyed. -- * initiator: The unit that is was destroyed.
-- --
@@ -657,6 +687,7 @@ do -- Event Handling
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs shortly after the landing animation of an ejected pilot touching the ground and standing up. Event does not occur if the pilot lands in the water and sub combs to Davey Jones Locker. --- Occurs shortly after the landing animation of an ejected pilot touching the ground and standing up. Event does not occur if the pilot lands in the water and sub combs to Davey Jones Locker.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- --
-- * initiator: Static object representing the ejected pilot. Place : Aircraft that the pilot ejected from. -- * initiator: Static object representing the ejected pilot. Place : Aircraft that the pilot ejected from.
-- * place: may not return as a valid object if the aircraft has crashed into the ground and no longer exists. -- * place: may not return as a valid object if the aircraft has crashed into the ground and no longer exists.
@@ -667,36 +698,43 @@ do -- Event Handling
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Paratrooper landing. --- Paratrooper landing.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventParatrooperLanding -- @function [parent=#BASE] OnEventParatrooperLanding
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Discard chair after ejection. --- Discard chair after ejection.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventDiscardChairAfterEjection -- @function [parent=#BASE] OnEventDiscardChairAfterEjection
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Weapon add. Fires when entering a mission per pylon with the name of the weapon (double pylons not counted, infinite wep reload not counted. --- Weapon add. Fires when entering a mission per pylon with the name of the weapon (double pylons not counted, infinite wep reload not counted.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventParatrooperLanding -- @function [parent=#BASE] OnEventParatrooperLanding
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Trigger zone. --- Trigger zone.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventTriggerZone -- @function [parent=#BASE] OnEventTriggerZone
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Landing quality mark. --- Landing quality mark.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventLandingQualityMark -- @function [parent=#BASE] OnEventLandingQualityMark
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- BDA. --- BDA.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- @function [parent=#BASE] OnEventBDA -- @function [parent=#BASE] OnEventBDA
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when a player enters a slot and takes control of an aircraft. --- Occurs when a player enters a slot and takes control of an aircraft.
-- Have a look at the class @{Core.Event#EVENT} as these are just the prototypes.
-- **NOTE**: This is a workaround of a long standing DCS bug with the PLAYER_ENTER_UNIT event. -- **NOTE**: This is a workaround of a long standing DCS bug with the PLAYER_ENTER_UNIT event.
-- initiator : The unit that is being taken control of. -- initiator : The unit that is being taken control of.
-- @function [parent=#BASE] OnEventPlayerEnterAircraft -- @function [parent=#BASE] OnEventPlayerEnterAircraft
@@ -731,13 +769,30 @@ end
-- @param #BASE self -- @param #BASE self
-- @param DCS#Time EventTime The time stamp of the event. -- @param DCS#Time EventTime The time stamp of the event.
-- @param DCS#Object Initiator The initiating object of the event. -- @param DCS#Object Initiator The initiating object of the event.
function BASE:CreateEventCrash( EventTime, Initiator ) function BASE:CreateEventCrash( EventTime, Initiator, IniObjectCategory )
self:F( { EventTime, Initiator } ) self:F( { EventTime, Initiator } )
local Event = { local Event = {
id = world.event.S_EVENT_CRASH, id = world.event.S_EVENT_CRASH,
time = EventTime, time = EventTime,
initiator = Initiator, initiator = Initiator,
IniObjectCategory = IniObjectCategory,
}
world.onEvent( Event )
end
--- Creation of a Crash Event.
-- @param #BASE self
-- @param DCS#Time EventTime The time stamp of the event.
-- @param DCS#Object Initiator The initiating object of the event.
function BASE:CreateEventUnitLost(EventTime, Initiator)
self:F( { EventTime, Initiator } )
local Event = {
id = world.event.S_EVENT_UNIT_LOST,
time = EventTime,
initiator = Initiator,
} }
world.onEvent( Event ) world.onEvent( Event )
@@ -747,13 +802,14 @@ end
-- @param #BASE self -- @param #BASE self
-- @param DCS#Time EventTime The time stamp of the event. -- @param DCS#Time EventTime The time stamp of the event.
-- @param DCS#Object Initiator The initiating object of the event. -- @param DCS#Object Initiator The initiating object of the event.
function BASE:CreateEventDead( EventTime, Initiator ) function BASE:CreateEventDead( EventTime, Initiator, IniObjectCategory )
self:F( { EventTime, Initiator } ) self:F( { EventTime, Initiator, IniObjectCategory } )
local Event = { local Event = {
id = world.event.S_EVENT_DEAD, id = world.event.S_EVENT_DEAD,
time = EventTime, time = EventTime,
initiator = Initiator, initiator = Initiator,
IniObjectCategory = IniObjectCategory,
} }
world.onEvent( Event ) world.onEvent( Event )
@@ -800,13 +856,12 @@ function BASE:CreateEventPlayerEnterAircraft( PlayerUnit )
local Event = { local Event = {
id = EVENTS.PlayerEnterAircraft, id = EVENTS.PlayerEnterAircraft,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent(Event) world.onEvent(Event)
end end
-- TODO: Complete DCS#Event structure.
--- The main event handling function... This function captures all events generated for the class. --- The main event handling function... This function captures all events generated for the class.
-- @param #BASE self -- @param #BASE self
-- @param DCS#Event event -- @param DCS#Event event
@@ -845,20 +900,22 @@ do -- Scheduling
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. -- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. -- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. -- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #number The ScheduleID of the planned schedule. -- @return #string The Schedule ID of the planned schedule.
function BASE:ScheduleOnce( Start, SchedulerFunction, ... ) function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
self:F2( { Start } )
self:T3( { ... } )
-- Object name.
local ObjectName = "-" local ObjectName = "-"
ObjectName = self.ClassName .. self.ClassID ObjectName = self.ClassName .. self.ClassID
-- Debug info.
self:F3( { "ScheduleOnce: ", ObjectName, Start } ) self:F3( { "ScheduleOnce: ", ObjectName, Start } )
if not self.Scheduler then if not self.Scheduler then
self.Scheduler = SCHEDULER:New( self ) self.Scheduler = SCHEDULER:New( self )
end end
-- FF this was wrong!
--[[
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
self, self,
SchedulerFunction, SchedulerFunction,
@@ -868,6 +925,10 @@ do -- Scheduling
nil, nil,
nil nil
) )
]]
-- NOTE: MasterObject (first parameter) needs to be nil or it will be the first argument passed to the SchedulerFunction!
local ScheduleID = self.Scheduler:Schedule(nil, SchedulerFunction, {...}, Start)
self._.Schedules[#self._.Schedules+1] = ScheduleID self._.Schedules[#self._.Schedules+1] = ScheduleID
@@ -882,7 +943,7 @@ do -- Scheduling
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. -- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. -- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. -- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #number The ScheduleID of the planned schedule. -- @return #string The Schedule ID of the planned schedule.
function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... ) function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... )
self:F2( { Start } ) self:F2( { Start } )
self:T3( { ... } ) self:T3( { ... } )
@@ -896,8 +957,9 @@ do -- Scheduling
self.Scheduler = SCHEDULER:New( self ) self.Scheduler = SCHEDULER:New( self )
end end
-- NOTE: MasterObject (first parameter) should(!) be nil as it will be the first argument passed to the SchedulerFunction!
local ScheduleID = self.Scheduler:Schedule( local ScheduleID = self.Scheduler:Schedule(
self, nil,
SchedulerFunction, SchedulerFunction,
{ ... }, { ... },
Start, Start,
@@ -914,13 +976,13 @@ do -- Scheduling
--- Stops the Schedule. --- Stops the Schedule.
-- @param #BASE self -- @param #BASE self
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. -- @param #string SchedulerID (Optional) Scheduler ID to be stopped. If nil, all pending schedules are stopped.
function BASE:ScheduleStop( SchedulerFunction ) function BASE:ScheduleStop( SchedulerID )
self:F3( { "ScheduleStop:" } ) self:F3( { "ScheduleStop:" } )
if self.Scheduler then if self.Scheduler then
_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] ) --_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] )
_SCHEDULEDISPATCHER:Stop(self.Scheduler, SchedulerID)
end end
end end
@@ -1061,7 +1123,7 @@ end
--- Set tracing for a class --- Set tracing for a class
-- @param #BASE self -- @param #BASE self
-- @param #string Class -- @param #string Class Class name.
function BASE:TraceClass( Class ) function BASE:TraceClass( Class )
_TraceClass[Class] = true _TraceClass[Class] = true
_TraceClassMethod[Class] = {} _TraceClassMethod[Class] = {}
@@ -1070,8 +1132,8 @@ end
--- Set tracing for a specific method of class --- Set tracing for a specific method of class
-- @param #BASE self -- @param #BASE self
-- @param #string Class -- @param #string Class Class name.
-- @param #string Method -- @param #string Method Method.
function BASE:TraceClassMethod( Class, Method ) function BASE:TraceClassMethod( Class, Method )
if not _TraceClassMethod[Class] then if not _TraceClassMethod[Class] then
_TraceClassMethod[Class] = {} _TraceClassMethod[Class] = {}

View File

@@ -16,24 +16,24 @@
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon --- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
-- --
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. -- There are two types of BEACONs available : the (aircraft) TACAN Beacon and the general purpose Radio Beacon.
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is -- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very useful to simulate the battery time if your BEACON is
-- attach to a cargo crate, for exemple. -- attach to a cargo crate, for example.
-- --
-- ## AA TACAN Beacon usage -- ## Aircraft TACAN Beacon usage
-- --
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. -- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON.ActivateTACAN}() to set the beacon parameters and start the beacon.
-- Use @#BEACON:StopAATACAN}() to stop it. -- Use @{#BEACON.StopRadioBeacon}() to stop it.
-- --
-- ## General Purpose Radio Beacon usage -- ## General Purpose Radio Beacon usage
-- --
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with -- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. -- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON.RadioBeacon}() to set the beacon parameters and start the beacon.
-- Use @{#BEACON:StopRadioBeacon}() to stop it. -- Use @{#BEACON.StopRadioBeacon}() to stop it.
-- --
-- @type BEACON -- @type BEACON
-- @field #string ClassName Name of the class "BEACON". -- @field #string ClassName Name of the class "BEACON".
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities. -- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{Wrapper.Controllable#CONTROLLABLE} that will receive radio capabilities.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
BEACON = { BEACON = {
ClassName = "BEACON", ClassName = "BEACON",
@@ -95,13 +95,13 @@ BEACON.Type={
-- @field #number TACAN TACtical Air Navigation system on ground. -- @field #number TACAN TACtical Air Navigation system on ground.
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band. -- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band. -- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
-- @field #number VOR Very High Frequency Omnidirectional Range -- @field #number VOR Very High Frequency Omni-Directional Range
-- @field #number ILS_LOCALIZER ILS localizer -- @field #number ILS_LOCALIZER ILS localizer
-- @field #number ILS_GLIDESLOPE ILS glide slope. -- @field #number ILS_GLIDESLOPE ILS glide slope.
-- @field #number PRGM_LOCALIZER PRGM localizer. -- @field #number PRGM_LOCALIZER PRGM localizer.
-- @field #number PRGM_GLIDESLOPE PRGM glide slope. -- @field #number PRGM_GLIDESLOPE PRGM glide slope.
-- @field #number BROADCAST_STATION Broadcast station. -- @field #number BROADCAST_STATION Broadcast station.
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon. -- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omni-directional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band. -- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band. -- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME). -- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
@@ -130,8 +130,8 @@ BEACON.System={
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc. --- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead. -- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
-- @param #BEACON self -- @param #BEACON self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Wrapper.Positionable} that will receive radio capabilities.
-- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid. -- @return #BEACON Beacon object or #nil if the positionable is invalid.
function BEACON:New(Positionable) function BEACON:New(Positionable)
-- Inherit BASE. -- Inherit BASE.
@@ -141,14 +141,14 @@ function BEACON:New( Positionable )
self:F(Positionable) self:F(Positionable)
-- Set positionable. -- Set positionable.
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure POSITIONABLE is valid if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable self.Positionable = Positionable
self.name=Positionable:GetName() self.name=Positionable:GetName()
self:I(string.format("New BEACON %s", tostring(self.name))) self:I(string.format("New BEACON %s", tostring(self.name)))
return self return self
end end
self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } ) self:E({"The passed positionable is invalid, no BEACON created", Positionable})
return nil return nil
end end
@@ -161,16 +161,16 @@ end
-- @param #number Duration How long will the beacon last in seconds. Omit for forever. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit") -- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon -- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
--
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration) function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration}) self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
Mode=Mode or "Y"
-- Get frequency. -- Get frequency.
local Frequency=UTILS.TACANToFrequency(Channel, Mode) local Frequency=UTILS.TACANToFrequency(Channel, Mode)
@@ -188,11 +188,16 @@ function BEACON:ActivateTACAN( Channel, Mode, Message, Bearing, Duration )
-- Check if unit is an aircraft and set system accordingly. -- Check if unit is an aircraft and set system accordingly.
local AA=self.Positionable:IsAir() local AA=self.Positionable:IsAir()
if AA then if AA then
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
-- Check if "Y" mode is selected for aircraft. -- Check if "Y" mode is selected for aircraft.
if Mode ~= "Y" then if Mode=="X" then
self:E( { "WARNING: The POSITIONABLE you want to attach the AA TACAN Beacon is an aircraft: Mode should Y! The BEACON is not emitting.", self.Positionable } ) --self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable})
System=BEACON.System.TACAN_TANKER_X
else
System=BEACON.System.TACAN_TANKER_Y
end end
end end
@@ -239,7 +244,34 @@ function BEACON:ActivateICLS( Channel, Callsign, Duration )
return self return self
end end
--- Activates a TACAN BEACON on an Aircraft. --- Activates a LINK4 BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
-- @param #BEACON self
-- @param #number Frequency LINK4 FRequency in MHz, eg 336.
-- @param #string Morse The ID that is going to be coded in Morse and broadcasted by the beacon.
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
function BEACON:ActivateLink4(Frequency, Morse, Duration)
self:F({Frequency=Frequency, Morse=Morse, Duration=Duration})
-- Attached unit.
local UnitID=self.Positionable:GetID()
-- Debug
self:T2({"LINK4 BEACON started!"})
-- Start beacon.
self.Positionable:CommandActivateLink4(Frequency,UnitID,Morse)
-- Stop sheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:CommandDeactivateLink4(Duration)
end
return self
end
--- DEPRECATED: Please use @{#BEACON.ActivateTACAN}() instead.
-- Activates a TACAN BEACON on an Aircraft.
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels -- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon -- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
@@ -247,20 +279,18 @@ end
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. -- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit") -- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon -- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
--
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration) function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
self:F({TACANChannel, Message, Bearing, BeaconDuration}) self:F({TACANChannel, Message, Bearing, BeaconDuration})
local IsValid = true local IsValid = true
if not self.Positionable:IsAir() then if not self.Positionable:IsAir() then
self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } ) self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
IsValid = false IsValid = false
end end
@@ -270,13 +300,12 @@ function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration )
IsValid = false IsValid = false
end end
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not
-- or 14 (TACAN_AA_MODE_Y) if it does not
local System local System
if Bearing then if Bearing then
System = 5 System = BEACON.System.TACAN_TANKER_Y
else else
System = 14 System = BEACON.System.TACAN_AA_MODE_Y
end end
if IsValid then -- Starts the BEACON if IsValid then -- Starts the BEACON
@@ -284,15 +313,19 @@ function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration )
self.Positionable:SetCommand({ self.Positionable:SetCommand({
id = "ActivateBeacon", id = "ActivateBeacon",
params = { params = {
type = 4, type = BEACON.Type.TACAN,
system = System, system = System,
callsign = Message, callsign = Message,
AA = true,
frequency = Frequency, frequency = Frequency,
}, bearing = Bearing,
modeChannel = "Y",
}
}) })
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, function() SCHEDULER:New(nil,
function()
self:StopAATACAN() self:StopAATACAN()
end, {}, BeaconDuration) end, {}, BeaconDuration)
end end
@@ -307,19 +340,20 @@ end
function BEACON:StopAATACAN() function BEACON:StopAATACAN()
self:F() self:F()
if not self.Positionable then if not self.Positionable then
self:E( { "Start the beacon first before stopping it!" } ) self:E({"Start the beacon first before stoping it !"})
else else
self.Positionable:SetCommand({ self.Positionable:SetCommand({
id = 'DeactivateBeacon', id = 'DeactivateBeacon',
params = {}, params = {
}
}) })
end end
end end
--- Activates a general purpose Radio Beacon --- Activates a general purpose Radio Beacon
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency. -- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8. -- Although any frequency could be used, only a few DCS Modules can home on radio beacons at the time of writing, i.e. the Mi-8, Huey, Gazelle etc.
-- They can home in on these specific frequencies : -- The following e.g. can home in on these specific frequencies :
-- * **Mi8** -- * **Mi8**
-- * R-828 -> 20-60MHz -- * R-828 -> 20-60MHz
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM -- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
@@ -367,7 +401,7 @@ function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDurat
Frequency = Frequency * 1000000 -- Conversion to Hz Frequency = Frequency * 1000000 -- Conversion to Hz
-- Check the modulation -- Check the modulation
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ? if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation}) self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
IsValid = false IsValid = false
end end
@@ -377,7 +411,7 @@ function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDurat
self:E({"Power is invalid. ", Power}) self:E({"Power is invalid. ", Power})
IsValid = false IsValid = false
end end
Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
if IsValid then if IsValid then
self:T2({"Activating Beacon on ", Frequency, Modulation}) self:T2({"Activating Beacon on ", Frequency, Modulation})
@@ -385,14 +419,15 @@ function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDurat
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID)) trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, function() SCHEDULER:New( nil,
function()
self:StopRadioBeacon() self:StopRadioBeacon()
end, {}, BeaconDuration) end, {}, BeaconDuration)
end end
end end
end end
--- Stops the AA TACAN BEACON --- Stops the Radio Beacon
-- @param #BEACON self -- @param #BEACON self
-- @return #BEACON self -- @return #BEACON self
function BEACON:StopRadioBeacon() function BEACON:StopRadioBeacon()
@@ -406,7 +441,7 @@ end
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel -- @param #number TACANChannel
-- @param #string TACANMode -- @param #string TACANMode
-- @return #number Frequency -- @return #number Frequecy
-- @return #nil if parameters are invalid -- @return #nil if parameters are invalid
function BEACON:_TACANToFrequency(TACANChannel, TACANMode) function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
self:F3({TACANChannel, TACANMode}) self:F3({TACANChannel, TACANMode})

View File

@@ -0,0 +1,464 @@
--- **Core** - Define any or all conditions to be evaluated.
--
-- **Main Features:**
--
-- * Add arbitrary numbers of conditon functions
-- * Evaluate *any* or *all* conditions
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Operation).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Core.Condition
-- @image MOOSE.JPG
--- CONDITON class.
-- @type CONDITION
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string name Name of the condition.
-- @field #boolean isAny General functions are evaluated as any condition.
-- @field #boolean negateResult Negate result of evaluation.
-- @field #boolean noneResult Boolean that is returned if no condition functions at all were specified.
-- @field #table functionsGen General condition functions.
-- @field #table functionsAny Any condition functions.
-- @field #table functionsAll All condition functions.
-- @field #number functionCounter Running number to determine the unique ID of condition functions.
-- @field #boolean defaultPersist Default persistence of condition functions.
--
-- @extends Core.Base#BASE
--- *Better three hours too soon than a minute too late.* - William Shakespeare
--
-- ===
--
-- # The CONDITION Concept
--
--
--
-- @field #CONDITION
CONDITION = {
ClassName = "CONDITION",
lid = nil,
functionsGen = {},
functionsAny = {},
functionsAll = {},
functionCounter = 0,
defaultPersist = false,
}
--- Condition function.
-- @type CONDITION.Function
-- @field #number uid Unique ID of the condition function.
-- @field #string type Type of the condition function: "gen", "any", "all".
-- @field #boolean persistence If `true`, this is persistent.
-- @field #function func Callback function to check for a condition. Must return a `#boolean`.
-- @field #table arg (Optional) Arguments passed to the condition callback function if any.
--- CONDITION class version.
-- @field #string version
CONDITION.version="0.3.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Make FSM. No sure if really necessary.
-- DONE: Option to remove condition functions.
-- DONE: Persistence option for condition functions.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new CONDITION object.
-- @param #CONDITION self
-- @param #string Name (Optional) Name used in the logs.
-- @return #CONDITION self
function CONDITION:New(Name)
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) --#CONDITION
self.name=Name or "Condition X"
self:SetNoneResult(false)
self.lid=string.format("%s | ", self.name)
return self
end
--- Set that general condition functions return `true` if `any` function returns `true`. Default is that *all* functions must return `true`.
-- @param #CONDITION self
-- @param #boolean Any If `true`, *any* condition can be true. Else *all* conditions must result `true`.
-- @return #CONDITION self
function CONDITION:SetAny(Any)
self.isAny=Any
return self
end
--- Negate result.
-- @param #CONDITION self
-- @param #boolean Negate If `true`, result is negated else not.
-- @return #CONDITION self
function CONDITION:SetNegateResult(Negate)
self.negateResult=Negate
return self
end
--- Set whether `true` or `false` is returned, if no conditions at all were specified. By default `false` is returned.
-- @param #CONDITION self
-- @param #boolean ReturnValue Returns this boolean.
-- @return #CONDITION self
function CONDITION:SetNoneResult(ReturnValue)
if not ReturnValue then
self.noneResult=false
else
self.noneResult=true
end
return self
end
--- Set whether condition functions are persistent, *i.e.* are removed.
-- @param #CONDITION self
-- @param #boolean IsPersistent If `true`, condition functions are persistent.
-- @return #CONDITION self
function CONDITION:SetDefaultPersistence(IsPersistent)
self.defaultPersist=IsPersistent
return self
end
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
-- @param #CONDITION self
-- @param #function Function The function to call.
-- @param ... (Optional) Parameters passed to the function (if any).
--
-- @usage
-- local function isAequalB(a, b)
-- return a==b
-- end
--
-- myCondition:AddFunction(isAequalB, a, b)
--
-- @return #CONDITION.Function Condition function table.
function CONDITION:AddFunction(Function, ...)
-- Condition function.
local condition=self:_CreateCondition(0, Function, ...)
-- Add to table.
table.insert(self.functionsGen, condition)
return condition
end
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
-- @param #CONDITION self
-- @param #function Function The function to call.
-- @param ... (Optional) Parameters passed to the function (if any).
-- @return #CONDITION.Function Condition function table.
function CONDITION:AddFunctionAny(Function, ...)
-- Condition function.
local condition=self:_CreateCondition(1, Function, ...)
-- Add to table.
table.insert(self.functionsAny, condition)
return condition
end
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
-- @param #CONDITION self
-- @param #function Function The function to call.
-- @param ... (Optional) Parameters passed to the function (if any).
-- @return #CONDITION.Function Condition function table.
function CONDITION:AddFunctionAll(Function, ...)
-- Condition function.
local condition=self:_CreateCondition(2, Function, ...)
-- Add to table.
table.insert(self.functionsAll, condition)
return condition
end
--- Remove a condition function.
-- @param #CONDITION self
-- @param #CONDITION.Function ConditionFunction The condition function to be removed.
-- @return #CONDITION self
function CONDITION:RemoveFunction(ConditionFunction)
if ConditionFunction then
local data=nil
if ConditionFunction.type==0 then
data=self.functionsGen
elseif ConditionFunction.type==1 then
data=self.functionsAny
elseif ConditionFunction.type==2 then
data=self.functionsAll
end
if data then
for i=#data,1,-1 do
local cf=data[i] --#CONDITION.Function
if cf.uid==ConditionFunction.uid then
self:T(self.lid..string.format("Removed ConditionFunction UID=%d", cf.uid))
table.remove(data, i)
return self
end
end
end
end
return self
end
--- Remove all non-persistant condition functions.
-- @param #CONDITION self
-- @return #CONDITION self
function CONDITION:RemoveNonPersistant()
for i=#self.functionsGen,1,-1 do
local cf=self.functionsGen[i] --#CONDITION.Function
if not cf.persistence then
table.remove(self.functionsGen, i)
end
end
for i=#self.functionsAll,1,-1 do
local cf=self.functionsAll[i] --#CONDITION.Function
if not cf.persistence then
table.remove(self.functionsAll, i)
end
end
for i=#self.functionsAny,1,-1 do
local cf=self.functionsAny[i] --#CONDITION.Function
if not cf.persistence then
table.remove(self.functionsAny, i)
end
end
return self
end
--- Evaluate conditon functions.
-- @param #CONDITION self
-- @param #boolean AnyTrue If `true`, evaluation return `true` if *any* condition function returns `true`. By default, *all* condition functions must return true.
-- @return #boolean Result of condition functions.
function CONDITION:Evaluate(AnyTrue)
-- Check if at least one function was given.
if #self.functionsAll + #self.functionsAny + #self.functionsAll == 0 then
return self.noneResult
end
-- Any condition for gen.
local evalAny=self.isAny
if AnyTrue~=nil then
evalAny=AnyTrue
end
local isGen=nil
if evalAny then
isGen=self:_EvalConditionsAny(self.functionsGen)
else
isGen=self:_EvalConditionsAll(self.functionsGen)
end
-- Is any?
local isAny=self:_EvalConditionsAny(self.functionsAny)
-- Is all?
local isAll=self:_EvalConditionsAll(self.functionsAll)
-- Result.
local result=isGen and isAny and isAll
-- Negate result.
if self.negateResult then
result=not result
end
-- Debug message.
self:T(self.lid..string.format("Evaluate: isGen=%s, isAny=%s, isAll=%s (negate=%s) ==> result=%s", tostring(isGen), tostring(isAny), tostring(isAll), tostring(self.negateResult), tostring(result)))
return result
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Check if all given condition are true.
-- @param #CONDITION self
-- @param #table functions Functions to evaluate.
-- @return #boolean If true, all conditions were true (or functions was empty/nil). Returns false if at least one condition returned false.
function CONDITION:_EvalConditionsAll(functions)
-- At least one condition?
local gotone=false
-- Any stop condition must be true.
for _,_condition in pairs(functions or {}) do
local condition=_condition --#CONDITION.Function
-- At least one condition was defined.
gotone=true
-- Call function.
local istrue=condition.func(unpack(condition.arg))
-- Any false will return false.
if not istrue then
return false
end
end
-- All conditions were true.
return true
end
--- Check if any of the given conditions is true.
-- @param #CONDITION self
-- @param #table functions Functions to evaluate.
-- @return #boolean If true, at least one condition is true (or functions was emtpy/nil).
function CONDITION:_EvalConditionsAny(functions)
-- At least one condition?
local gotone=false
-- Any stop condition must be true.
for _,_condition in pairs(functions or {}) do
local condition=_condition --#CONDITION.Function
-- At least one condition was defined.
gotone=true
-- Call function.
local istrue=condition.func(unpack(condition.arg))
-- Any true will return true.
if istrue then
return true
end
end
-- No condition was true.
if gotone then
return false
else
-- No functions passed.
return true
end
end
--- Create conditon function object.
-- @param #CONDITION self
-- @param #number Ftype Function type: 0=Gen, 1=All, 2=Any.
-- @param #function Function The function to call.
-- @param ... (Optional) Parameters passed to the function (if any).
-- @return #CONDITION.Function Condition function.
function CONDITION:_CreateCondition(Ftype, Function, ...)
-- Increase counter.
self.functionCounter=self.functionCounter+1
local condition={} --#CONDITION.Function
condition.uid=self.functionCounter
condition.type=Ftype or 0
condition.persistence=self.defaultPersist
condition.func=Function
condition.arg={}
if arg then
condition.arg=arg
end
return condition
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Global Condition Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Condition to check if time is greater than a given threshold time.
-- @param #number Time Time in seconds.
-- @param #boolean Absolute If `true`, abs. mission time from `timer.getAbsTime()` is checked. Default is relative mission time from `timer.getTime()`.
-- @return #boolean Returns `true` if time is greater than give the time.
function CONDITION.IsTimeGreater(Time, Absolute)
local Tnow=nil
if Absolute then
Tnow=timer.getAbsTime()
else
Tnow=timer.getTime()
end
if Tnow>Time then
return true
else
return false
end
return nil
end
--- Function that returns `true` (success) with a certain probability. For example, if you specify `Probability=80` there is an 80% chance that `true` is returned.
-- Technically, a random number between 0 and 100 is created. If the given success probability is less then this number, `true` is returned.
-- @param #number Probability Success probability in percent. Default 50 %.
-- @return #boolean Returns `true` for success and `false` otherwise.
function CONDITION.IsRandomSuccess(Probability)
Probability=Probability or 50
-- Create some randomness.
math.random()
math.random()
math.random()
-- Number between 0 and 100.
local N=math.random()*100
if N<Probability then
return true
else
return false
end
end
--- Function that returns always `true`
-- @return #boolean Returns `true` unconditionally.
function CONDITION.ReturnTrue()
return true
end
--- Function that returns always `false`
-- @return #boolean Returns `false` unconditionally.
function CONDITION.ReturnFalse()
return false
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -31,6 +31,7 @@
-- @module Core.Database -- @module Core.Database
-- @image Core_Database.JPG -- @image Core_Database.JPG
--- @type DATABASE --- @type DATABASE
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID. -- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID.
@@ -87,15 +88,19 @@ DATABASE = {
WAREHOUSES = {}, WAREHOUSES = {},
FLIGHTGROUPS = {}, FLIGHTGROUPS = {},
FLIGHTCONTROLS = {}, FLIGHTCONTROLS = {},
OPSZONES = {},
PATHLINES = {},
} }
local _DATABASECoalition = { local _DATABASECoalition =
{
[1] = "Red", [1] = "Red",
[2] = "Blue", [2] = "Blue",
[3] = "Neutral", [3] = "Neutral",
} }
local _DATABASECategory = { local _DATABASECategory =
{
["plane"] = Unit.Category.AIRPLANE, ["plane"] = Unit.Category.AIRPLANE,
["helicopter"] = Unit.Category.HELICOPTER, ["helicopter"] = Unit.Category.HELICOPTER,
["vehicle"] = Unit.Category.GROUND_UNIT, ["vehicle"] = Unit.Category.GROUND_UNIT,
@@ -103,6 +108,7 @@ local _DATABASECategory = {
["static"] = Unit.Category.STRUCTURE, ["static"] = Unit.Category.STRUCTURE,
} }
--- Creates a new DATABASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- Creates a new DATABASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE -- @return #DATABASE
@@ -120,6 +126,7 @@ function DATABASE:New()
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
--self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) -- DCS 2.7.1 for Aerial units no dead event ATM
self:HandleEvent( EVENTS.Hit, self.AccountHits ) self:HandleEvent( EVENTS.Hit, self.AccountHits )
self:HandleEvent( EVENTS.NewCargo ) self:HandleEvent( EVENTS.NewCargo )
self:HandleEvent( EVENTS.DeleteCargo ) self:HandleEvent( EVENTS.DeleteCargo )
@@ -132,8 +139,8 @@ function DATABASE:New()
self:_RegisterGroupsAndUnits() self:_RegisterGroupsAndUnits()
self:_RegisterClients() self:_RegisterClients()
self:_RegisterStatics() self:_RegisterStatics()
-- self:_RegisterAirbases()
--self:_RegisterPlayers() --self:_RegisterPlayers()
--self:_RegisterAirbases()
self.UNITS_Position = 0 self.UNITS_Position = 0
@@ -150,6 +157,7 @@ function DATABASE:FindUnit( UnitName )
return UnitFound return UnitFound
end end
--- Adds a Unit based on the Unit Name in the DATABASE. --- Adds a Unit based on the Unit Name in the DATABASE.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string DCSUnitName Unit name. -- @param #string DCSUnitName Unit name.
@@ -157,26 +165,20 @@ end
function DATABASE:AddUnit( DCSUnitName ) function DATABASE:AddUnit( DCSUnitName )
if not self.UNITS[DCSUnitName] then if not self.UNITS[DCSUnitName] then
-- Debug info. -- Debug info.
self:T( { "Add UNIT:", DCSUnitName } ) self:T( { "Add UNIT:", DCSUnitName } )
-- local UnitRegister = UNIT:Register( DCSUnitName )
-- Register unit -- Register unit
self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName) self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName)
-- This is not used anywhere in MOOSE as far as I can see so I remove it until there comes an error somewhere.
-- table.insert(self.UNITS_Index, DCSUnitName )
end end
return self.UNITS[DCSUnitName] return self.UNITS[DCSUnitName]
end end
--- Deletes a Unit from the DATABASE based on the Unit Name. --- Deletes a Unit from the DATABASE based on the Unit Name.
-- @param #DATABASE self -- @param #DATABASE self
function DATABASE:DeleteUnit( DCSUnitName ) function DATABASE:DeleteUnit( DCSUnitName )
self.UNITS[DCSUnitName] = nil self.UNITS[DCSUnitName] = nil
end end
@@ -194,6 +196,7 @@ function DATABASE:AddStatic( DCSStaticName )
return nil return nil
end end
--- Deletes a Static from the DATABASE based on the Static Name. --- Deletes a Static from the DATABASE based on the Static Name.
-- @param #DATABASE self -- @param #DATABASE self
function DATABASE:DeleteStatic( DCSStaticName ) function DATABASE:DeleteStatic( DCSStaticName )
@@ -210,16 +213,6 @@ function DATABASE:FindStatic( StaticName )
return StaticFound return StaticFound
end end
--- Finds a AIRBASE based on the AirbaseName.
-- @param #DATABASE self
-- @param #string AirbaseName
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
function DATABASE:FindAirbase( AirbaseName )
local AirbaseFound = self.AIRBASES[AirbaseName]
return AirbaseFound
end
--- Adds a Airbase based on the Airbase Name in the DATABASE. --- Adds a Airbase based on the Airbase Name in the DATABASE.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string AirbaseName The name of the airbase. -- @param #string AirbaseName The name of the airbase.
@@ -233,6 +226,7 @@ function DATABASE:AddAirbase( AirbaseName )
return self.AIRBASES[AirbaseName] return self.AIRBASES[AirbaseName]
end end
--- Deletes a Airbase from the DATABASE based on the Airbase Name. --- Deletes a Airbase from the DATABASE based on the Airbase Name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string AirbaseName The name of the airbase -- @param #string AirbaseName The name of the airbase
@@ -251,9 +245,10 @@ function DATABASE:FindAirbase( AirbaseName )
return AirbaseFound return AirbaseFound
end end
do -- Zones
--- Finds a @{Zone} based on the zone name. do -- Zones and Pathlines
--- Finds a @{Core.Zone} based on the zone name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
-- @return Core.Zone#ZONE_BASE The found ZONE. -- @return Core.Zone#ZONE_BASE The found ZONE.
@@ -263,7 +258,7 @@ do -- Zones
return ZoneFound return ZoneFound
end end
--- Adds a @{Zone} based on the zone name in the DATABASE. --- Adds a @{Core.Zone} based on the zone name in the DATABASE.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
-- @param Core.Zone#ZONE_BASE Zone The zone. -- @param Core.Zone#ZONE_BASE Zone The zone.
@@ -274,7 +269,7 @@ do -- Zones
end end
end end
--- Deletes a @{Zone} from the DATABASE based on the zone name. --- Deletes a @{Core.Zone} from the DATABASE based on the zone name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
function DATABASE:DeleteZone( ZoneName ) function DATABASE:DeleteZone( ZoneName )
@@ -282,6 +277,40 @@ do -- Zones
self.ZONES[ZoneName] = nil self.ZONES[ZoneName] = nil
end end
--- Adds a @{Core.Pathline} based on its name in the DATABASE.
-- @param #DATABASE self
-- @param #string PathlineName The name of the pathline
-- @param Core.Pathline#PATHLINE Pathline The pathline.
function DATABASE:AddPathline( PathlineName, Pathline )
if not self.PATHLINES[PathlineName] then
self.PATHLINES[PathlineName]=Pathline
end
end
--- Finds a @{Core.Pathline} by its name.
-- @param #DATABASE self
-- @param #string PathlineName The name of the Pathline.
-- @return Core.Pathline#PATHLINE The found PATHLINE.
function DATABASE:FindPathline( PathlineName )
local pathline = self.PATHLINES[PathlineName]
return pathline
end
--- Deletes a @{Core.Pathline} from the DATABASE based on its name.
-- @param #DATABASE self
-- @param #string PathlineName The name of the PATHLINE.
function DATABASE:DeletePathline( PathlineName )
self.PATHLINES[PathlineName]=nil
return self
end
--- Private method that registers new ZONE_BASE derived objects within the DATABASE Object. --- Private method that registers new ZONE_BASE derived objects within the DATABASE Object.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
@@ -314,7 +343,7 @@ do -- Zones
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName)) self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
Zone = ZONE_POLYGON_BASE:New( ZoneName, ZoneData.verticies ) Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, ZoneData.verticies)
--for i,vec2 in pairs(ZoneData.verticies) do --for i,vec2 in pairs(ZoneData.verticies) do
-- local coord=COORDINATE:NewFromVec2(vec2) -- local coord=COORDINATE:NewFromVec2(vec2)
@@ -328,6 +357,20 @@ do -- Zones
-- Store color of zone. -- Store color of zone.
Zone.Color=color Zone.Color=color
-- Store zone ID.
Zone.ZoneID=ZoneData.zoneId
-- Store zone properties (if any)
local ZoneProperties = ZoneData.properties or nil
Zone.Properties = {}
if ZoneName and ZoneProperties then
for _,ZoneProp in ipairs(ZoneProperties) do
if ZoneProp.key then
Zone.Properties[ZoneProp.key] = ZoneProp.value
end
end
end
-- Store in DB. -- Store in DB.
self.ZONENAMES[ZoneName] = ZoneName self.ZONENAMES[ZoneName] = ZoneName
@@ -361,15 +404,153 @@ do -- Zones
-- Add zone to DB. -- Add zone to DB.
self:AddZone( ZoneName, Zone_Polygon ) self:AddZone( ZoneName, Zone_Polygon )
end end
end end
-- Drawings as zones
if env.mission.drawings and env.mission.drawings.layers then
-- Loop over layers.
for layerID, layerData in pairs(env.mission.drawings.layers or {}) do
-- Loop over objects in layers.
for objectID, objectData in pairs(layerData.objects or {}) do
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
if objectData.polygonMode and (objectData.polygonMode=="free") and objectData.points and #objectData.points>=4 then
---
-- Drawing: Polygon free
---
-- Name of the zone.
local ZoneName=objectData.name or "Unknown free Polygon Drawing"
-- Reference point. All other points need to be translated by this.
local vec2={x=objectData.mapX, y=objectData.mapY}
-- Debug stuff.
--local vec3={x=objectData.mapX, y=0, z=objectData.mapY}
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
--trigger.action.markToAll(id, "mapXY", vec3)
-- Copy points array.
local points=UTILS.DeepCopy(objectData.points)
-- Translate points.
for i,_point in pairs(points) do
local point=_point --DCS#Vec2
points[i]=UTILS.Vec2Add(point, vec2)
end
-- Remove last point.
table.remove(points, #points)
-- Debug output
self:I(string.format("Register ZONE: %s (Polygon (free) drawing with %d vertices)", ZoneName, #points))
-- Create new polygon zone.
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
-- Set color.
Zone:SetColor({1, 0, 0}, 0.15)
-- Store in DB.
self.ZONENAMES[ZoneName] = ZoneName
-- Add zone.
self:AddZone(ZoneName, Zone)
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
elseif objectData.polygonMode and objectData.polygonMode=="rect" then
---
-- Drawing: Polygon rect
---
-- Name of the zone.
local ZoneName=objectData.name or "Unknown rect Polygon Drawing"
-- Reference point (center of the rectangle).
local vec2={x=objectData.mapX, y=objectData.mapY}
-- For a rectangular polygon drawing, we have the width (y) and height (x).
local w=objectData.width
local h=objectData.height
-- Create points from center using with and height (width for y and height for x is a bit confusing, but this is how ED implemented it).
local points={}
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
points[3]={x=vec2.x+h/2, y=vec2.y-w/2} --Lower right
points[4]={x=vec2.x-h/2, y=vec2.y-w/2} --Lower left
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
-- Debug output
self:I(string.format("Register ZONE: %s (Polygon (rect) drawing with %d vertices)", ZoneName, #points))
-- Create new polygon zone.
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
-- Set color.
Zone:SetColor({1, 0, 0}, 0.15)
-- Store in DB.
self.ZONENAMES[ZoneName] = ZoneName
-- Add zone.
self:AddZone(ZoneName, Zone)
elseif objectData.lineMode and (objectData.lineMode=="segments" or objectData.lineMode=="segment" or objectData.lineMode=="free") and objectData.points and #objectData.points>=2 then
---
-- Drawing: Line (segments, segment or free)
---
-- Name of the zone.
local Name=objectData.name or "Unknown Line Drawing"
-- Reference point. All other points need to be translated by this.
local vec2={x=objectData.mapX, y=objectData.mapY}
-- Copy points array.
local points=UTILS.DeepCopy(objectData.points)
-- Translate points.
for i,_point in pairs(points) do
local point=_point --DCS#Vec2
points[i]=UTILS.Vec2Add(point, vec2)
end
-- Debug output
self:I(string.format("Register PATHLINE: %s (Line drawing with %d points)", Name, #points))
-- Create new polygon zone.
local Pathline=PATHLINE:NewFromVec2Array(Name, points)
-- Set color.
--Zone:SetColor({1, 0, 0}, 0.15)
-- Add zone.
self:AddPathline(Name,Pathline)
end
end
end
end
end end
end -- zone end -- zone
do -- Zone_Goal do -- Zone_Goal
--- Finds a @{Zone} based on the zone name. --- Finds a @{Core.Zone} based on the zone name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
-- @return Core.Zone#ZONE_BASE The found ZONE. -- @return Core.Zone#ZONE_BASE The found ZONE.
@@ -379,7 +560,7 @@ do -- Zone_Goal
return ZoneFound return ZoneFound
end end
--- Adds a @{Zone} based on the zone name in the DATABASE. --- Adds a @{Core.Zone} based on the zone name in the DATABASE.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
-- @param Core.Zone#ZONE_BASE Zone The zone. -- @param Core.Zone#ZONE_BASE Zone The zone.
@@ -390,7 +571,8 @@ do -- Zone_Goal
end end
end end
--- Deletes a @{Zone} from the DATABASE based on the zone name.
--- Deletes a @{Core.Zone} from the DATABASE based on the zone name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
function DATABASE:DeleteZoneGoal( ZoneName ) function DATABASE:DeleteZoneGoal( ZoneName )
@@ -399,6 +581,46 @@ do -- Zone_Goal
end end
end -- Zone_Goal end -- Zone_Goal
do -- OpsZone
--- Finds a @{Ops.OpsZone#OPSZONE} based on the zone name.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
-- @return Ops.OpsZone#OPSZONE The found OPSZONE.
function DATABASE:FindOpsZone( ZoneName )
local ZoneFound = self.OPSZONES[ZoneName]
return ZoneFound
end
--- Adds a @{Ops.OpsZone#OPSZONE} based on the zone name in the DATABASE.
-- @param #DATABASE self
-- @param Ops.OpsZone#OPSZONE OpsZone The zone.
function DATABASE:AddOpsZone( OpsZone )
if OpsZone then
local ZoneName=OpsZone:GetName()
if not self.OPSZONES[ZoneName] then
self.OPSZONES[ZoneName] = OpsZone
end
end
end
--- Deletes a @{Ops.OpsZone#OPSZONE} from the DATABASE based on the zone name.
-- @param #DATABASE self
-- @param #string ZoneName The name of the zone.
function DATABASE:DeleteOpsZone( ZoneName )
self.OPSZONES[ZoneName] = nil
end
end -- OpsZone
do -- cargo do -- cargo
--- Adds a Cargo based on the Cargo Name in the DATABASE. --- Adds a Cargo based on the Cargo Name in the DATABASE.
@@ -411,6 +633,7 @@ do -- cargo
end end
end end
--- Deletes a Cargo from the DATABASE based on the Cargo Name. --- Deletes a Cargo from the DATABASE based on the Cargo Name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string CargoName The name of the airbase -- @param #string CargoName The name of the airbase
@@ -504,6 +727,7 @@ function DATABASE:FindClient( ClientName )
return ClientFound return ClientFound
end end
--- Adds a CLIENT based on the ClientName in the DATABASE. --- Adds a CLIENT based on the ClientName in the DATABASE.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ClientName Name of the Client unit. -- @param #string ClientName Name of the Client unit.
@@ -517,6 +741,7 @@ function DATABASE:AddClient( ClientName )
return self.CLIENTS[ClientName] return self.CLIENTS[ClientName]
end end
--- Finds a GROUP based on the GroupName. --- Finds a GROUP based on the GroupName.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string GroupName -- @param #string GroupName
@@ -527,6 +752,7 @@ function DATABASE:FindGroup( GroupName )
return GroupFound return GroupFound
end end
--- Adds a GROUP based on the GroupName in the DATABASE. --- Adds a GROUP based on the GroupName in the DATABASE.
-- @param #DATABASE self -- @param #DATABASE self
function DATABASE:AddGroup( GroupName ) function DATABASE:AddGroup( GroupName )
@@ -574,6 +800,7 @@ function DATABASE:GetPlayers()
return self.PLAYERS return self.PLAYERS
end end
--- Get the player table from the DATABASE, which contains all UNIT objects. --- Get the player table from the DATABASE, which contains all UNIT objects.
-- The player table contains all UNIT objects of the player with the key the name of the player (PlayerName). -- The player table contains all UNIT objects of the player with the key the name of the player (PlayerName).
-- @param #DATABASE self -- @param #DATABASE self
@@ -586,6 +813,7 @@ function DATABASE:GetPlayerUnits()
return self.PLAYERUNITS return self.PLAYERUNITS
end end
--- Get the player table from the DATABASE which have joined in the mission historically. --- Get the player table from the DATABASE which have joined in the mission historically.
-- The player table contains all UNIT objects with the key the name of the player (PlayerName). -- The player table contains all UNIT objects with the key the name of the player (PlayerName).
-- @param #DATABASE self -- @param #DATABASE self
@@ -598,6 +826,7 @@ function DATABASE:GetPlayersJoined()
return self.PLAYERSJOINED return self.PLAYERSJOINED
end end
--- Instantiate new Groups within the DCSRTE. --- Instantiate new Groups within the DCSRTE.
-- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined: -- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined:
-- SpawnCountryID, SpawnCategoryID -- SpawnCountryID, SpawnCategoryID
@@ -730,8 +959,9 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID, Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
Category = self.Templates.Groups[GroupTemplateName].CategoryID, Category = self.Templates.Groups[GroupTemplateName].CategoryID,
Country = self.Templates.Groups[GroupTemplateName].CountryID, Country = self.Templates.Groups[GroupTemplateName].CountryID,
Units = UnitNames, Units = UnitNames
} ) }
)
end end
--- Get group template. --- Get group template.
@@ -757,7 +987,9 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
local StaticTemplate = UTILS.DeepCopy( StaticTemplate ) local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
local StaticTemplateName = env.getValueDictByKey( StaticTemplate.name ) local StaticTemplateGroupName = env.getValueDictByKey(StaticTemplate.name)
local StaticTemplateName=StaticTemplate.units[1].name
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
@@ -765,7 +997,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
StaticTemplate.CoalitionID = CoalitionID StaticTemplate.CoalitionID = CoalitionID
StaticTemplate.CountryID = CountryID StaticTemplate.CountryID = CountryID
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateGroupName
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1] self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
@@ -776,8 +1008,9 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
self:T( { Static = self.Templates.Statics[StaticTemplateName].StaticName, self:T( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID, Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID,
Category = self.Templates.Statics[StaticTemplateName].CategoryID, Category = self.Templates.Statics[StaticTemplateName].CategoryID,
Country = self.Templates.Statics[StaticTemplateName].CountryID, Country = self.Templates.Statics[StaticTemplateName].CountryID
} ) }
)
self:AddStatic( StaticTemplateName ) self:AddStatic( StaticTemplateName )
@@ -838,6 +1071,20 @@ function DATABASE:GetGroupTemplateFromUnitName( UnitName )
end end
end end
--- Get group template from unit name.
-- @param #DATABASE self
-- @param #string UnitName Name of the unit.
-- @return #table Group template.
function DATABASE:GetUnitTemplateFromUnitName( UnitName )
if self.Templates.Units[UnitName] then
return self.Templates.Units[UnitName]
else
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
return nil
end
end
--- Get coalition ID from client name. --- Get coalition ID from client name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ClientName Name of the Client. -- @param #string ClientName Name of the Client.
@@ -877,9 +1124,11 @@ end
-- @param #string AirbaseName Name of the airbase. -- @param #string AirbaseName Name of the airbase.
-- @return #number Category. -- @return #number Category.
function DATABASE:GetCategoryFromAirbase( AirbaseName ) function DATABASE:GetCategoryFromAirbase( AirbaseName )
return self.AIRBASES[AirbaseName]:GetCategory() return self.AIRBASES[AirbaseName]:GetAirbaseCategory()
end end
--- Private method that registers all alive players in the mission. --- Private method that registers all alive players in the mission.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
@@ -903,7 +1152,8 @@ function DATABASE:_RegisterPlayers()
return self return self
end end
--- Private method that registers all Groups and Units within the mission.
--- Private method that registers all Groups and Units within in the mission.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:_RegisterGroupsAndUnits() function DATABASE:_RegisterGroupsAndUnits()
@@ -944,20 +1194,21 @@ function DATABASE:_RegisterGroupsAndUnits()
return self return self
end end
--- Private method that registers all Units of skill Client or Player within the mission. --- Private method that registers all Units of skill Client or Player within in the mission.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:_RegisterClients() function DATABASE:_RegisterClients()
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
self:I(string.format("Register Client: %s", tostring(ClientName))) self:I(string.format("Register Client: %s", tostring(ClientName)))
self:AddClient( ClientName ) local client=self:AddClient( ClientName )
client.SpawnCoord=COORDINATE:New(ClientTemplate.x, ClientTemplate.alt, ClientTemplate.y)
end end
return self return self
end end
--- Private method that registers all Statics within the mission. --- Private method that registeres all static objects.
-- @param #DATABASE self -- @param #DATABASE self
function DATABASE:_RegisterStatics() function DATABASE:_RegisterStatics()
@@ -987,11 +1238,26 @@ function DATABASE:_RegisterAirbases()
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
self:_RegisterAirbase(DCSAirbase)
end
return self
end
--- Register a DCS airbase.
-- @param #DATABASE self
-- @param DCS#Airbase airbase Airbase.
-- @return #DATABASE self
function DATABASE:_RegisterAirbase(airbase)
if airbase then
-- Get the airbase name. -- Get the airbase name.
local DCSAirbaseName = DCSAirbase:getName() local DCSAirbaseName = airbase:getName()
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now. -- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
local airbaseID = DCSAirbase:getID() local airbaseID=airbase:getID()
-- Add and register airbase. -- Add and register airbase.
local airbase=self:AddAirbase( DCSAirbaseName ) local airbase=self:AddAirbase( DCSAirbaseName )
@@ -1000,7 +1266,7 @@ function DATABASE:_RegisterAirbases()
local airbaseUID=airbase:GetID(true) local airbaseUID=airbase:GetID(true)
-- Debug output. -- Debug output.
local text = string.format( "Register %s: %s (ID=%d UID=%d), parking=%d [", AIRBASE.CategoryName[airbase.category], tostring( DCSAirbaseName ), airbaseID, airbaseUID, airbase.NparkingTotal ) local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal)
for _,terminalType in pairs(AIRBASE.TerminalType) do for _,terminalType in pairs(AIRBASE.TerminalType) do
if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then
text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType]) text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType])
@@ -1009,16 +1275,12 @@ function DATABASE:_RegisterAirbases()
text=text.."]" text=text.."]"
self:I(text) self:I(text)
-- Check for DCS bug IDs.
if airbaseID ~= airbase:GetID() then
-- self:E("WARNING: :getID does NOT match :GetID!")
end
end end
return self return self
end end
--- Events --- Events
--- Handles the OnBirth event for the alive units set. --- Handles the OnBirth event for the alive units set.
@@ -1029,20 +1291,23 @@ function DATABASE:_EventOnBirth( Event )
if Event.IniDCSUnit then if Event.IniDCSUnit then
if Event.IniObjectCategory == 3 then if Event.IniObjectCategory == Object.Category.STATIC then
-- Add static object to DB.
self:AddStatic( Event.IniDCSUnitName ) self:AddStatic( Event.IniDCSUnitName )
else else
if Event.IniObjectCategory == 1 then if Event.IniObjectCategory == Object.Category.UNIT then
-- Add unit and group to DB.
self:AddUnit( Event.IniDCSUnitName ) self:AddUnit( Event.IniDCSUnitName )
self:AddGroup( Event.IniDCSGroupName ) self:AddGroup( Event.IniDCSGroupName )
-- Add airbase if it was spawned later in the mission. -- A unit can also be an airbase (e.g. ships).
local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName) local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName)
if DCSAirbase then if DCSAirbase then
-- Add airbase if it was spawned later in the mission.
self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName))) self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName)))
self:AddAirbase(Event.IniDCSUnitName) self:AddAirbase(Event.IniDCSUnitName)
end end
@@ -1050,7 +1315,7 @@ function DATABASE:_EventOnBirth( Event )
end end
end end
if Event.IniObjectCategory == 1 then if Event.IniObjectCategory == Object.Category.UNIT then
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
Event.IniGroup = self:FindGroup( Event.IniDCSGroupName ) Event.IniGroup = self:FindGroup( Event.IniDCSGroupName )
@@ -1098,6 +1363,7 @@ function DATABASE:_EventOnBirth( Event )
end end
--- Handles the OnDead or OnCrash event for alive units set. --- Handles the OnDead or OnCrash event for alive units set.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
@@ -1117,6 +1383,22 @@ function DATABASE:_EventOnDeadOrCrash( Event )
self:DeleteStatic( Event.IniDCSUnitName ) self:DeleteStatic( Event.IniDCSUnitName )
end end
---
-- Maybe a UNIT?
---
-- Delete unit.
if self.UNITS[Event.IniDCSUnitName] then
self:T("STATIC Event for UNIT "..tostring(Event.IniDCSUnitName))
local DCSUnit = _DATABASE:FindUnit( Event.IniDCSUnitName )
self:T({DCSUnit})
if DCSUnit then
--self:I("Creating DEAD Event for UNIT "..tostring(Event.IniDCSUnitName))
--DCSUnit:Destroy(true)
return
end
end
else else
if Event.IniObjectCategory == 1 then if Event.IniObjectCategory == 1 then
@@ -1152,6 +1434,7 @@ function DATABASE:_EventOnDeadOrCrash( Event )
self:AccountDestroys( Event ) self:AccountDestroys( Event )
end end
--- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). --- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied).
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
@@ -1189,6 +1472,7 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
end end
end end
--- Handles the OnPlayerLeaveUnit event to clean the active players table. --- Handles the OnPlayerLeaveUnit event to clean the active players table.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
@@ -1274,6 +1558,7 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
return self return self
end end
--- Iterate the DATABASE and call an iterator function for each **alive** STATIC, providing the STATIC and optional parameters. --- Iterate the DATABASE and call an iterator function for each **alive** STATIC, providing the STATIC and optional parameters.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a STATIC parameter. -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a STATIC parameter.
@@ -1286,6 +1571,7 @@ function DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) -- R2
return self return self
end end
--- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. --- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter.
@@ -1298,6 +1584,7 @@ function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... )
return self return self
end end
--- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. --- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter. -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter.
@@ -1310,6 +1597,7 @@ function DATABASE:ForEachGroup( IteratorFunction, FinalizeFunction, ... )
return self return self
end end
--- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters. --- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name. -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name.
@@ -1322,6 +1610,7 @@ function DATABASE:ForEachPlayer( IteratorFunction, FinalizeFunction, ... )
return self return self
end end
--- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters. --- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter.
@@ -1346,14 +1635,15 @@ function DATABASE:ForEachPlayerUnit( IteratorFunction, FinalizeFunction, ... )
return self return self
end end
--- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters. --- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter. -- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter.
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:ForEachClient( IteratorFunction, ... ) function DATABASE:ForEachClient( IteratorFunction, FinalizeFunction, ... )
self:F2( arg ) self:F2( arg )
self:ForEach( IteratorFunction, arg, self.CLIENTS ) self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CLIENTS )
return self return self
end end
@@ -1362,14 +1652,15 @@ end
-- @param #DATABASE self -- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter. -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter.
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:ForEachCargo( IteratorFunction, ... ) function DATABASE:ForEachCargo( IteratorFunction, FinalizeFunction, ... )
self:F2( arg ) self:F2( arg )
self:ForEach( IteratorFunction, arg, self.CARGOS ) self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CARGOS )
return self return self
end end
--- Handles the OnEventNewCargo event. --- Handles the OnEventNewCargo event.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -1381,6 +1672,7 @@ function DATABASE:OnEventNewCargo( EventData )
end end
end end
--- Handles the OnEventDeleteCargo. --- Handles the OnEventDeleteCargo.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -1392,6 +1684,7 @@ function DATABASE:OnEventDeleteCargo( EventData )
end end
end end
--- Handles the OnEventNewZone event. --- Handles the OnEventNewZone event.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -1403,6 +1696,7 @@ function DATABASE:OnEventNewZone( EventData )
end end
end end
--- Handles the OnEventDeleteZone. --- Handles the OnEventDeleteZone.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -1414,6 +1708,8 @@ function DATABASE:OnEventDeleteZone( EventData )
end end
end end
--- Gets the player settings --- Gets the player settings
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string PlayerName -- @param #string PlayerName
@@ -1423,6 +1719,7 @@ function DATABASE:GetPlayerSettings( PlayerName )
return self.PLAYERSETTINGS[PlayerName] return self.PLAYERSETTINGS[PlayerName]
end end
--- Sets the player settings --- Sets the player settings
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string PlayerName -- @param #string PlayerName
@@ -1433,19 +1730,19 @@ function DATABASE:SetPlayerSettings( PlayerName, Settings )
self.PLAYERSETTINGS[PlayerName] = Settings self.PLAYERSETTINGS[PlayerName] = Settings
end end
--- Add a flight group to the data base. --- Add an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) to the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup -- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group added to the DB.
function DATABASE:AddFlightGroup( flightgroup ) function DATABASE:AddOpsGroup(opsgroup)
self:I( { NewFlightGroup = flightgroup.groupname } ) --env.info("Adding OPSGROUP "..tostring(opsgroup.groupname))
self.FLIGHTGROUPS[flightgroup.groupname] = flightgroup self.FLIGHTGROUPS[opsgroup.groupname]=opsgroup
end end
--- Get a flight group from the data base. --- Get an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) from the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string groupname Group name of the flight group. Can also be passed as GROUP object. -- @param #string groupname Group name of the group. Can also be passed as GROUP object.
-- @return Ops.FlightGroup#FLIGHTGROUP Flight group object. -- @return Ops.OpsGroup#OPSGROUP OPS group object.
function DATABASE:GetFlightGroup( groupname ) function DATABASE:GetOpsGroup(groupname)
-- Get group and group name. -- Get group and group name.
if type(groupname)=="string" then if type(groupname)=="string" then
@@ -1453,9 +1750,53 @@ function DATABASE:GetFlightGroup( groupname )
groupname=groupname:GetName() groupname=groupname:GetName()
end end
--env.info("Getting OPSGROUP "..tostring(groupname))
return self.FLIGHTGROUPS[groupname] return self.FLIGHTGROUPS[groupname]
end end
--- Find an OPSGROUP (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) in the data base.
-- @param #DATABASE self
-- @param #string groupname Group name of the group. Can also be passed as GROUP object.
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
function DATABASE:FindOpsGroup(groupname)
-- Get group and group name.
if type(groupname)=="string" then
else
groupname=groupname:GetName()
end
--env.info("Getting OPSGROUP "..tostring(groupname))
return self.FLIGHTGROUPS[groupname]
end
--- Find an OPSGROUP (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) in the data base for a given unit.
-- @param #DATABASE self
-- @param #string unitname Unit name. Can also be passed as UNIT object.
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
function DATABASE:FindOpsGroupFromUnit(unitname)
local unit=nil --Wrapper.Unit#UNIT
local groupname
-- Get group and group name.
if type(unitname)=="string" then
unit=UNIT:FindByName(unitname)
else
unit=unitname
end
if unit then
groupname=unit:GetGroup():GetName()
end
if groupname then
return self.FLIGHTGROUPS[groupname]
else
return nil
end
end
--- Add a flight control to the data base. --- Add a flight control to the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol -- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol

View File

@@ -35,7 +35,7 @@
-- There are 5 types/levels of objects that the _EVENTDISPATCHER services: -- There are 5 types/levels of objects that the _EVENTDISPATCHER services:
-- --
-- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database. -- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database.
-- * SET_ derived classes: These are subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority. -- * SET_ derived classes: These are subsets of the global _DATABASE object (an instance of @{Core.Database#DATABASE}). These subsets are updated by the _EVENTDISPATCHER as the second priority.
-- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed UNIT object. -- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed UNIT object.
-- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object. -- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object.
-- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object. -- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object.
@@ -52,7 +52,7 @@
-- --
-- ![Objects](..\Presentations\EVENT\Dia8.JPG) -- ![Objects](..\Presentations\EVENT\Dia8.JPG)
-- --
-- The actual event subscribing and handling is not facilitated through the _EVENTDISPATCHER, but it is done through the @{BASE} class, @{UNIT} class and @{GROUP} class. -- The actual event subscribing and handling is not facilitated through the _EVENTDISPATCHER, but it is done through the @{Core.Base#BASE} class, @{Wrapper.Unit#UNIT} class and @{Wrapper.Group#GROUP} class.
-- The _EVENTDISPATCHER is a component that is quietly working in the background of MOOSE. -- The _EVENTDISPATCHER is a component that is quietly working in the background of MOOSE.
-- --
-- ![Objects](..\Presentations\EVENT\Dia9.JPG) -- ![Objects](..\Presentations\EVENT\Dia9.JPG)
@@ -131,8 +131,6 @@
-- * Weapon data: Certain events populate weapon information. -- * Weapon data: Certain events populate weapon information.
-- * Place data: Certain events populate place information. -- * Place data: Certain events populate place information.
-- --
-- Example code snippet:
--
-- --- This function is an Event Handling function that will be called when Tank1 is Dead. -- --- This function is an Event Handling function that will be called when Tank1 is Dead.
-- -- EventData is an EVENTDATA structure. -- -- EventData is an EVENTDATA structure.
-- -- We use the EventData.IniUnit to smoke the tank Green. -- -- We use the EventData.IniUnit to smoke the tank Green.
@@ -143,6 +141,7 @@
-- EventData.IniUnit:SmokeGreen() -- EventData.IniUnit:SmokeGreen()
-- end -- end
-- --
--
-- Find below an overview which events populate which information categories: -- Find below an overview which events populate which information categories:
-- --
-- ![Objects](..\Presentations\EVENT\Dia14.JPG) -- ![Objects](..\Presentations\EVENT\Dia14.JPG)
@@ -152,7 +151,6 @@
-- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. -- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated.
-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. -- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event.
-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. -- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory.
--
-- Example code snippet: -- Example code snippet:
-- --
-- if Event.IniObjectCategory == Object.Category.UNIT then -- if Event.IniObjectCategory == Object.Category.UNIT then
@@ -174,6 +172,7 @@
-- @module Core.Event -- @module Core.Event
-- @image Core_Event.JPG -- @image Core_Event.JPG
--- @type EVENT --- @type EVENT
-- @field #EVENT.Events Events -- @field #EVENT.Events Events
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -195,6 +194,7 @@ world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006 world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007 world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
--- The different types of events supported by MOOSE. --- The different types of events supported by MOOSE.
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method. -- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
-- @type EVENTS -- @type EVENTS
@@ -248,6 +248,18 @@ EVENTS = {
TriggerZone = world.event.S_EVENT_TRIGGER_ZONE or -1, TriggerZone = world.event.S_EVENT_TRIGGER_ZONE or -1,
LandingQualityMark = world.event.S_EVENT_LANDING_QUALITY_MARK or -1, LandingQualityMark = world.event.S_EVENT_LANDING_QUALITY_MARK or -1,
BDA = world.event.S_EVENT_BDA or -1, BDA = world.event.S_EVENT_BDA or -1,
-- Added with DCS 2.8.0
AIAbortMission = world.event.S_EVENT_AI_ABORT_MISSION or -1,
DayNight = world.event.S_EVENT_DAYNIGHT or -1,
FlightTime = world.event.S_EVENT_FLIGHT_TIME or -1,
SelfKillPilot = world.event.S_EVENT_PLAYER_SELF_KILL_PILOT or -1,
PlayerCaptureAirfield = world.event.S_EVENT_PLAYER_CAPTURE_AIRFIELD or -1,
EmergencyLanding = world.event.S_EVENT_EMERGENCY_LANDING or -1,
UnitCreateTask = world.event.S_EVENT_UNIT_CREATE_TASK or -1,
UnitDeleteTask = world.event.S_EVENT_UNIT_DELETE_TASK or -1,
SimulationStart = world.event.S_EVENT_SIMULATION_START or -1,
WeaponRearm = world.event.S_EVENT_WEAPON_REARM or -1,
WeaponDrop = world.event.S_EVENT_WEAPON_DROP or -1,
} }
--- The Event structure --- The Event structure
@@ -293,8 +305,8 @@ EVENTS = {
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object. -- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
-- @field #string PlaceName The name of the airbase. -- @field #string PlaceName The name of the airbase.
-- --
-- @field #table weapon The weapon used during the event. -- @field DCS#Weapon weapon The weapon used during the event.
-- @field #table Weapon -- @field DCS#Weapon Weapon The weapon used during the event.
-- @field #string WeaponName Name of the weapon. -- @field #string WeaponName Name of the weapon.
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon. -- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
-- --
@@ -304,6 +316,8 @@ EVENTS = {
-- @field Core.ZONE#ZONE Zone The zone object. -- @field Core.ZONE#ZONE Zone The zone object.
-- @field #string ZoneName The name of the zone. -- @field #string ZoneName The name of the zone.
local _EVENTMETA = { local _EVENTMETA = {
[world.event.S_EVENT_SHOT] = { [world.event.S_EVENT_SHOT] = {
Order = 1, Order = 1,
@@ -558,6 +572,67 @@ local _EVENTMETA = {
Event = "OnEventBDA", Event = "OnEventBDA",
Text = "S_EVENT_BDA" Text = "S_EVENT_BDA"
}, },
-- Added with DCS 2.8
[EVENTS.AIAbortMission] = {
Order = 1,
Side = "I",
Event = "OnEventAIAbortMission",
Text = "S_EVENT_AI_ABORT_MISSION"
},
[EVENTS.DayNight] = {
Order = 1,
Event = "OnEventDayNight",
Text = "S_EVENT_DAYNIGHT"
},
[EVENTS.FlightTime] = {
Order = 1,
Event = "OnEventFlightTime",
Text = "S_EVENT_FLIGHT_TIME"
},
[EVENTS.SelfKillPilot] = {
Order = 1,
Side = "I",
Event = "OnEventSelfKillPilot",
Text = "S_EVENT_PLAYER_SELF_KILL_PILOT"
},
[EVENTS.PlayerCaptureAirfield] = {
Order = 1,
Event = "OnEventPlayerCaptureAirfield",
Text = "S_EVENT_PLAYER_CAPTURE_AIRFIELD"
},
[EVENTS.EmergencyLanding] = {
Order = 1,
Side = "I",
Event = "OnEventEmergencyLanding",
Text = "S_EVENT_EMERGENCY_LANDING"
},
[EVENTS.UnitCreateTask] = {
Order = 1,
Event = "OnEventUnitCreateTask",
Text = "S_EVENT_UNIT_CREATE_TASK"
},
[EVENTS.UnitDeleteTask] = {
Order = 1,
Event = "OnEventUnitDeleteTask",
Text = "S_EVENT_UNIT_DELETE_TASK"
},
[EVENTS.SimulationStart] = {
Order = 1,
Event = "OnEventSimulationStart",
Text = "S_EVENT_SIMULATION_START"
},
[EVENTS.WeaponRearm] = {
Order = 1,
Side = "I",
Event = "OnEventWeaponRearm",
Text = "S_EVENT_WEAPON_REARM"
},
[EVENTS.WeaponDrop] = {
Order = 1,
Side = "I",
Event = "OnEventWeaponDrop",
Text = "S_EVENT_WEAPON_DROP"
},
} }
--- The Events structure --- The Events structure
@@ -578,6 +653,7 @@ function EVENT:New()
return self return self
end end
--- Initializes the Events structure for the event. --- Initializes the Events structure for the event.
-- @param #EVENT self -- @param #EVENT self
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
@@ -591,7 +667,7 @@ function EVENT:Init( EventID, EventClass )
self.Events[EventID] = {} self.Events[EventID] = {}
end end
-- Each event has a sub-table of EventClasses, ordered by EventPriority. -- Each event has a subtable of EventClasses, ordered by EventPriority.
local EventPriority = EventClass:GetEventPriority() local EventPriority = EventClass:GetEventPriority()
if not self.Events[EventID][EventPriority] then if not self.Events[EventID][EventPriority] then
@@ -653,6 +729,7 @@ function EVENT:Reset( EventObject ) -- R2.1
end end
end end
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object. --- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.Base#BASE EventClass The self class object for which the events are removed. -- @param Core.Base#BASE EventClass The self class object for which the events are removed.
@@ -671,6 +748,8 @@ function EVENT:RemoveAll( EventClass )
return self return self
end end
--- Create an OnDead event handler for a group --- Create an OnDead event handler for a group
-- @param #EVENT self -- @param #EVENT self
-- @param #table EventTemplate -- @param #table EventTemplate
@@ -702,6 +781,7 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
return self return self
end end
--- Set a new listener for an `S_EVENT_X` event for a UNIT. --- Set a new listener for an `S_EVENT_X` event for a UNIT.
-- @param #EVENT self -- @param #EVENT self
-- @param #string UnitName The name of the UNIT. -- @param #string UnitName The name of the UNIT.
@@ -789,6 +869,7 @@ do -- OnDead
end end
do -- OnLand do -- OnLand
--- Create an OnLand event handler for a group --- Create an OnLand event handler for a group
@@ -920,9 +1001,10 @@ do -- Event Creation
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a ZoneGoal Deletion Event. --- Creation of a ZoneGoal Deletion Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created. -- @param Functional.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
function EVENT:CreateEventDeleteZoneGoal( ZoneGoal ) function EVENT:CreateEventDeleteZoneGoal( ZoneGoal )
self:F( { ZoneGoal } ) self:F( { ZoneGoal } )
@@ -935,6 +1017,7 @@ do -- Event Creation
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Wrapper.Unit#UNIT PlayerUnit. -- @param Wrapper.Unit#UNIT PlayerUnit.
@@ -944,7 +1027,7 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterUnit, id = EVENTS.PlayerEnterUnit,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent( Event ) world.onEvent( Event )
@@ -959,7 +1042,7 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterAircraft, id = EVENTS.PlayerEnterAircraft,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent( Event ) world.onEvent( Event )
@@ -972,16 +1055,16 @@ end
-- @param #EVENTDATA Event Event data table. -- @param #EVENTDATA Event Event data table.
function EVENT:onEvent( Event ) function EVENT:onEvent( Event )
--- Function to handle errors.
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg ) env.info( "Error in SCHEDULER function:" .. errmsg )
if BASE.Debug ~= nil then if BASE.Debug ~= nil then
env.info( debug.traceback() ) env.info( debug.traceback() )
end end
return errmsg return errmsg
end end
-- Get event meta data. -- Get event meta data.
local EventMeta = _EVENTMETA[Event.id] local EventMeta = _EVENTMETA[Event.id]
@@ -990,6 +1073,7 @@ function EVENT:onEvent( Event )
if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
-- Check if mission has ended.
if Event.id and Event.id == EVENTS.MissionEnd then if Event.id and Event.id == EVENTS.MissionEnd then
self.MissionEnd = true self.MissionEnd = true
end end
@@ -998,34 +1082,11 @@ function EVENT:onEvent( Event )
Event.IniObjectCategory = Event.initiator:getCategory() Event.IniObjectCategory = Event.initiator:getCategory()
if Event.IniObjectCategory == Object.Category.UNIT then
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
if not Event.IniUnit then
-- Unit can be a CLIENT. Most likely this will be the case ...
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
end
Event.IniDCSGroupName = ""
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
-- if Event.IniGroup then
Event.IniGroupName = Event.IniDCSGroupName
-- end
end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
end
if Event.IniObjectCategory == Object.Category.STATIC then if Event.IniObjectCategory == Object.Category.STATIC then
---
-- Static
---
if Event.id==31 then if Event.id==31 then
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug. -- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID=Event.initiator.id_ local ID=Event.initiator.id_
@@ -1051,9 +1112,47 @@ function EVENT:onEvent( Event )
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
end end
-- Dead events of units can be delayed and the initiator changed to a static.
-- Take care of that.
local Unit=UNIT:FindByName(Event.IniDCSUnitName)
if Unit then
Event.IniObjectCategory = Object.Category.UNIT
end
end
if Event.IniObjectCategory == Object.Category.UNIT then
---
-- Unit
---
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
if not Event.IniUnit then
-- Unit can be a CLIENT. Most likely this will be the case ...
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
end
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
Event.IniGroupName = Event.IniDCSGroupName
end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
end end
if Event.IniObjectCategory == Object.Category.CARGO then if Event.IniObjectCategory == Object.Category.CARGO then
---
-- Cargo
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
@@ -1064,15 +1163,21 @@ function EVENT:onEvent( Event )
end end
if Event.IniObjectCategory == Object.Category.SCENERY then if Event.IniObjectCategory == Object.Category.SCENERY then
---
-- Scenery
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1! Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
end end
if Event.IniObjectCategory == Object.Category.BASE then if Event.IniObjectCategory == Object.Category.BASE then
---
-- Base Object
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
@@ -1080,11 +1185,22 @@ function EVENT:onEvent( Event )
Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
-- If the airbase does not exist in the DB, we add it (e.g. when FARPS are spawned).
if not Event.IniUnit then
_DATABASE:_RegisterAirbase(Event.initiator)
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
end
end end
end end
if Event.target then if Event.target then
---
-- TARGET
---
-- Target category.
Event.TgtObjectCategory = Event.target:getCategory() Event.TgtObjectCategory = Event.target:getCategory()
if Event.TgtObjectCategory == Object.Category.UNIT then if Event.TgtObjectCategory == Object.Category.UNIT then
@@ -1097,9 +1213,7 @@ function EVENT:onEvent( Event )
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
-- if Event.TgtGroup then
Event.TgtGroupName = Event.TgtDCSGroupName Event.TgtGroupName = Event.TgtDCSGroupName
-- end
end end
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
@@ -1147,6 +1261,7 @@ function EVENT:onEvent( Event )
end end
end end
-- Weapon.
if Event.weapon then if Event.weapon then
Event.Weapon = Event.weapon Event.Weapon = Event.weapon
Event.WeaponName = Event.Weapon:getTypeName() Event.WeaponName = Event.Weapon:getTypeName()
@@ -1166,10 +1281,12 @@ function EVENT:onEvent( Event )
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground. -- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
--Event.Place=UNIT:Find(Event.place) --Event.Place=UNIT:Find(Event.place)
else else
if Event.place:isExist() and Event.place:getCategory() ~= Object.Category.SCENERY then
Event.Place=AIRBASE:Find(Event.place) Event.Place=AIRBASE:Find(Event.place)
Event.PlaceName=Event.Place:GetName() Event.PlaceName=Event.Place:GetName()
end end
end end
end
-- Mark points. -- Mark points.
if Event.idx then if Event.idx then
@@ -1181,24 +1298,23 @@ function EVENT:onEvent( Event )
Event.MarkGroupID = Event.groupID Event.MarkGroupID = Event.groupID
end end
-- Cargo object.
if Event.cargo then if Event.cargo then
Event.Cargo = Event.cargo Event.Cargo = Event.cargo
Event.CargoName = Event.cargo.Name Event.CargoName = Event.cargo.Name
end end
-- Zone object.
if Event.zone then if Event.zone then
Event.Zone = Event.zone Event.Zone = Event.zone
Event.ZoneName = Event.zone.ZoneName Event.ZoneName = Event.zone.ZoneName
end end
-- Priority order.
local PriorityOrder = EventMeta.Order local PriorityOrder = EventMeta.Order
local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityBegin = PriorityOrder == -1 and 5 or 1
local PriorityEnd = PriorityOrder == -1 and 1 or 5 local PriorityEnd = PriorityOrder == -1 and 1 or 5
if Event.IniObjectCategory ~= Object.Category.STATIC then
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
end
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
if self.Events[Event.id][EventPriority] then if self.Events[Event.id][EventPriority] then
@@ -1210,8 +1326,8 @@ function EVENT:onEvent( Event )
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
--end --end
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.IniGroup = Event.IniGroup or GROUP:FindByName( Event.IniDCSGroupName )
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = Event.TgtGroup or GROUP:FindByName( Event.TgtDCSGroupName )
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
if EventData.EventUnit then if EventData.EventUnit then
@@ -1221,7 +1337,8 @@ function EVENT:onEvent( Event )
Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.PlayerEnterUnit or
Event.id == EVENTS.Crash or Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead or Event.id == EVENTS.Dead or
Event.id == EVENTS.RemoveUnit then Event.id == EVENTS.RemoveUnit or
Event.id == EVENTS.UnitLost then
local UnitName = EventClass:GetName() local UnitName = EventClass:GetName()
@@ -1231,11 +1348,8 @@ function EVENT:onEvent( Event )
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) function()
end
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event ) return EventData.EventFunction( EventClass, Event )
end, ErrorHandler ) end, ErrorHandler )
@@ -1246,14 +1360,12 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end
local Result, Value = xpcall( function()
return EventFunction( EventClass, Event ) return EventFunction( EventClass, Event )
end, ErrorHandler ) end, ErrorHandler )
end end
end end
end end
else else
@@ -1271,7 +1383,8 @@ function EVENT:onEvent( Event )
Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.PlayerEnterUnit or
Event.id == EVENTS.Crash or Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead or Event.id == EVENTS.Dead or
Event.id == EVENTS.RemoveUnit then Event.id == EVENTS.RemoveUnit or
Event.id == EVENTS.UnitLost then
-- We can get the name of the EventClass, which is now always a GROUP object. -- We can get the name of the EventClass, which is now always a GROUP object.
local GroupName = EventClass:GetName() local GroupName = EventClass:GetName()
@@ -1282,11 +1395,8 @@ function EVENT:onEvent( Event )
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) function()
end
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) ) return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler ) end, ErrorHandler )
@@ -1297,11 +1407,8 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end
local Result, Value = xpcall( function()
return EventFunction( EventClass, Event, unpack( EventData.Params ) ) return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler ) end, ErrorHandler )
end end
@@ -1321,10 +1428,8 @@ function EVENT:onEvent( Event )
if EventData.EventFunction then if EventData.EventFunction then
-- There is an EventFunction defined, so call the EventFunction. -- There is an EventFunction defined, so call the EventFunction.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event ) return EventData.EventFunction( EventClass, Event )
end, ErrorHandler ) end, ErrorHandler )
else else
@@ -1334,15 +1439,14 @@ function EVENT:onEvent( Event )
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end
local Result, Value = xpcall( function()
local Result, Value = EventFunction( EventClass, Event ) local Result, Value = EventFunction( EventClass, Event )
return Result, Value return Result, Value
end, ErrorHandler ) end, ErrorHandler )
end end
end end
end end

View File

@@ -47,9 +47,9 @@
-- and tailored** by mission designers through **the implementation of Transition Handlers**. -- and tailored** by mission designers through **the implementation of Transition Handlers**.
-- Each of these FSM implementation classes start either with: -- Each of these FSM implementation classes start either with:
-- --
-- * an acronym **AI\_**, which indicates a FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class. -- * an acronym **AI\_**, which indicates a FSM implementation directing **AI controlled** @{Wrapper.Group#GROUP} and/or @{Wrapper.Unit#UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
-- * an acronym **TASK\_**, which indicates a FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class. -- * an acronym **TASK\_**, which indicates a FSM implementation executing a @{Tasking.Task#TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class. -- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{Tasking.Task#TASK}, seated in a @{Wrapper.Client#CLIENT} (slot) or a @{Wrapper.Unit#UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
-- --
-- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections. -- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections.
-- --
@@ -61,10 +61,10 @@
-- --
-- The following derived classes are available in the MOOSE framework, that implement a specialized form of a FSM: -- The following derived classes are available in the MOOSE framework, that implement a specialized form of a FSM:
-- --
-- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. -- * @{#FSM_TASK}: Models Finite State Machines for @{Tasking.Task}s.
-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. -- * @{#FSM_PROCESS}: Models Finite State Machines for @{Tasking.Task} actions, which control @{Wrapper.Client}s.
-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. -- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Wrapper.Client}s.
-- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here -- * @{#FSM_SET}: Models Finite State Machines for @{Core.Set}s. Note that these FSMs control multiple objects!!! So State concerns here
-- for multiple objects or the position of the state machine in the process. -- for multiple objects or the position of the state machine in the process.
-- --
-- === -- ===
@@ -119,9 +119,9 @@ do -- FSM
-- and tailored** by mission designers through **the implementation of Transition Handlers**. -- and tailored** by mission designers through **the implementation of Transition Handlers**.
-- Each of these FSM implementation classes start either with: -- Each of these FSM implementation classes start either with:
-- --
-- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class. -- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{Wrapper.Group#GROUP} and/or @{Wrapper.Unit#UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
-- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class. -- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{Tasking.Task#TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class. -- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{Tasking.Task#TASK}, seated in a @{Wrapper.Client#CLIENT} (slot) or a @{Wrapper.Unit#UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
-- --
-- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG)
-- --
@@ -405,7 +405,7 @@ do -- FSM
Transition.To = To Transition.To = To
-- Debug message. -- Debug message.
self:T2( Transition ) --self:T3( Transition )
self._Transitions[Transition] = Transition self._Transitions[Transition] = Transition
self:_eventmap( self.Events, Transition ) self:_eventmap( self.Events, Transition )
@@ -418,7 +418,7 @@ do -- FSM
return self._Transitions or {} return self._Transitions or {}
end end
--- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Wrapper.Controllable} by the task. --- Set the default @{#FSM_PROCESS} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Wrapper.Controllable} by the task.
-- @param #FSM self -- @param #FSM self
-- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states. -- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states.
-- @param #string Event The Event name. -- @param #string Event The Event name.
@@ -426,7 +426,7 @@ do -- FSM
-- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM. -- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM.
-- @return Core.Fsm#FSM_PROCESS The SubFSM. -- @return Core.Fsm#FSM_PROCESS The SubFSM.
function FSM:AddProcess( From, Event, Process, ReturnEvents ) function FSM:AddProcess( From, Event, Process, ReturnEvents )
self:T( { From, Event } ) --self:T3( { From, Event } )
local Sub = {} local Sub = {}
Sub.From = From Sub.From = From
@@ -525,7 +525,7 @@ do -- FSM
Process._Scores[State].ScoreText = ScoreText Process._Scores[State].ScoreText = ScoreText
Process._Scores[State].Score = Score Process._Scores[State].Score = Score
self:T( Process._Scores ) --self:T3( Process._Scores )
return Process return Process
end end
@@ -568,7 +568,7 @@ do -- FSM
self[__Event] = self[__Event] or self:_delayed_transition(Event) self[__Event] = self[__Event] or self:_delayed_transition(Event)
-- Debug message. -- Debug message.
self:T2( "Added methods: " .. Event .. ", " .. __Event ) --self:T3( "Added methods: " .. Event .. ", " .. __Event )
Events[Event] = self.Events[Event] or { map = {} } Events[Event] = self.Events[Event] or { map = {} }
self:_add_to_map( Events[Event].map, EventStructure ) self:_add_to_map( Events[Event].map, EventStructure )
@@ -784,7 +784,7 @@ do -- FSM
return function( self, DelaySeconds, ... ) return function( self, DelaySeconds, ... )
-- Debug. -- Debug.
self:T2( "Delayed Event: " .. EventName ) self:T3( "Delayed Event: " .. EventName )
local CallID = 0 local CallID = 0
if DelaySeconds ~= nil then if DelaySeconds ~= nil then
@@ -802,23 +802,23 @@ do -- FSM
self._EventSchedules[EventName] = CallID self._EventSchedules[EventName] = CallID
-- Debug output. -- Debug output.
self:T2( string.format( "NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring( CallID ) ) ) self:T2(string.format("NEGATIVE Event %s delayed by %.3f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
else else
self:T2( string.format( "NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds ) ) self:T2(string.format("NEGATIVE Event %s delayed by %.3f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds))
-- reschedule -- reschedule
end end
else else
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true ) CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
self:T2( string.format( "Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring( CallID ) ) ) self:T2(string.format("Event %s delayed by %.3f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
end end
else else
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." ) error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
end end
-- Debug. -- Debug.
self:T2( { CallID = CallID } ) --self:T3( { CallID = CallID } )
end end
end end
@@ -841,7 +841,7 @@ do -- FSM
function FSM:_gosub( ParentFrom, ParentEvent ) function FSM:_gosub( ParentFrom, ParentEvent )
local fsmtable = {} local fsmtable = {}
if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then
self:T( { ParentFrom, ParentEvent, self.subs[ParentFrom], self.subs[ParentFrom][ParentEvent] } ) --self:T3( { ParentFrom, ParentEvent, self.subs[ParentFrom], self.subs[ParentFrom][ParentEvent] } )
return self.subs[ParentFrom][ParentEvent] return self.subs[ParentFrom][ParentEvent]
else else
return {} return {}
@@ -888,7 +888,7 @@ do -- FSM
end end
end end
self:T3( { Map, Event } ) --self:T3( { Map, Event } )
end end
--- Get current state. --- Get current state.
@@ -908,7 +908,7 @@ do -- FSM
--- Check if FSM is in state. --- Check if FSM is in state.
-- @param #FSM self -- @param #FSM self
-- @param #string State State name. -- @param #string State State name.
-- @param #boolean If true, FSM is in this state. -- @return #boolean If true, FSM is in this state.
function FSM:Is( State ) function FSM:Is( State )
return self.current == State return self.current == State
end end
@@ -916,7 +916,7 @@ do -- FSM
--- Check if FSM is in state. --- Check if FSM is in state.
-- @param #FSM self -- @param #FSM self
-- @param #string State State name. -- @param #string State State name.
-- @param #boolean If true, FSM is in this state. -- @return #boolean If true, FSM is in this state.
function FSM:is(state) function FSM:is(state)
return self.current == state return self.current == state
end end
@@ -953,7 +953,7 @@ do -- FSM_CONTROLLABLE
-- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @field Wrapper.Controllable#CONTROLLABLE Controllable
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. --- Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Wrapper.Client}s.
-- --
-- === -- ===
-- --
@@ -1086,7 +1086,7 @@ do -- FSM_PROCESS
-- @field Tasking.Task#TASK Task -- @field Tasking.Task#TASK Task
-- @extends Core.Fsm#FSM_CONTROLLABLE -- @extends Core.Fsm#FSM_CONTROLLABLE
--- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. --- FSM_PROCESS class models Finite State Machines for @{Tasking.Task} actions, which control @{Wrapper.Client}s.
-- --
-- === -- ===
-- --
@@ -1146,7 +1146,7 @@ do -- FSM_PROCESS
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @return #FSM_PROCESS -- @return #FSM_PROCESS
function FSM_PROCESS:Copy( Controllable, Task ) function FSM_PROCESS:Copy( Controllable, Task )
self:T( { self:GetClassNameAndID() } ) --self:T3( { self:GetClassNameAndID() } )
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
@@ -1171,13 +1171,13 @@ do -- FSM_PROCESS
-- Copy End States -- Copy End States
for EndStateID, EndState in pairs( self:GetEndStates() ) do for EndStateID, EndState in pairs( self:GetEndStates() ) do
self:T( EndState ) --self:T3( EndState )
NewFsm:AddEndState( EndState ) NewFsm:AddEndState( EndState )
end end
-- Copy the score tables -- Copy the score tables
for ScoreID, Score in pairs( self:GetScores() ) do for ScoreID, Score in pairs( self:GetScores() ) do
self:T( Score ) --self:T3( Score )
NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score ) NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score )
end end
@@ -1241,7 +1241,7 @@ do -- FSM_PROCESS
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP. -- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
--- Send a message of the @{Task} to the Group of the Unit. --- Send a message of the @{Tasking.Task} to the Group of the Unit.
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
function FSM_PROCESS:Message( Message ) function FSM_PROCESS:Message( Message )
self:F( { Message = Message } ) self:F( { Message = Message } )
@@ -1382,7 +1382,7 @@ do -- FSM_SET
-- @field Core.Set#SET_BASE Set -- @field Core.Set#SET_BASE Set
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here --- FSM_SET class models Finite State Machines for @{Core.Set}s. Note that these FSMs control multiple objects!!! So State concerns here
-- for multiple objects or the position of the state machine in the process. -- for multiple objects or the position of the state machine in the process.
-- --
-- === -- ===
@@ -1422,7 +1422,7 @@ do -- FSM_SET
-- @param #FSM_SET self -- @param #FSM_SET self
-- @return Core.Set#SET_BASE -- @return Core.Set#SET_BASE
function FSM_SET:Get() function FSM_SET:Get()
return self.Controllable return self.Set
end end
function FSM_SET:_call_handler( step, trigger, params, EventName ) function FSM_SET:_call_handler( step, trigger, params, EventName )

View File

@@ -0,0 +1,274 @@
--- **Core** - Tap into markers added to the F10 map by users.
--
-- **Main Features:**
--
-- * Create an easy way to tap into markers added to the F10 map by users.
-- * Recognize own tag and list of keywords.
-- * Matched keywords are handed down to functions.
-- ##Listen for your tag
-- myMarker = MARKEROPS_BASE:New("tag", {}, false)
-- function myMarker:OnAfterMarkChanged(From, Event, To, Text, Keywords, Coord, idx)
--
-- end
-- Make sure to use the "MarkChanged" event as "MarkAdded" comes in right after the user places a blank marker and your callback will never be called.
--
-- ===
--
-- ### Author: **Applevangelist**
--
-- Date: 5 May 2021
-- Last Update: Feb 2023
--
-- ===
---
-- @module Core.MarkerOps_Base
-- @image MOOSE_Core.JPG
--------------------------------------------------------------------------
-- MARKEROPS_BASE Class Definition.
--------------------------------------------------------------------------
--- MARKEROPS_BASE class.
-- @type MARKEROPS_BASE
-- @field #string ClassName Name of the class.
-- @field #string Tag Tag to identify commands.
-- @field #table Keywords Table of keywords to recognize.
-- @field #string version Version of #MARKEROPS_BASE.
-- @field #boolean Casesensitive Enforce case when identifying the Tag, i.e. tag ~= Tag
-- @extends Core.Fsm#FSM
--- *Fiat lux.* -- Latin proverb.
--
-- ===
--
-- # The MARKEROPS_BASE Concept
--
-- This class enable scripting text-based actions from markers.
--
-- @field #MARKEROPS_BASE
MARKEROPS_BASE = {
ClassName = "MARKEROPS",
Tag = "mytag",
Keywords = {},
version = "0.1.1",
debug = false,
Casesensitive = true,
}
--- Function to instantiate a new #MARKEROPS_BASE object.
-- @param #MARKEROPS_BASE self
-- @param #string Tagname Name to identify us from the event text.
-- @param #table Keywords Table of keywords recognized from the event text.
-- @param #boolean Casesensitive (Optional) Switch case sensitive identification of Tagname. Defaults to true.
-- @return #MARKEROPS_BASE self
function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
-- Inherit FSM
local self=BASE:Inherit(self, FSM:New()) -- #MARKEROPS_BASE
-- Set some string id for output to DCS.log file.
self.lid=string.format("MARKEROPS_BASE %s | ", tostring(self.version))
self.Tag = Tagname or "mytag"-- #string
self.Keywords = Keywords or {} -- #table - might want to use lua regex here, too
self.debug = false
self.Casesensitive = true
if Casesensitive and Casesensitive == false then
self.Casesensitive = false
end
-----------------------
--- FSM Transitions ---
-----------------------
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start the FSM.
self:AddTransition("*", "MarkAdded", "*") -- Start the FSM.
self:AddTransition("*", "MarkChanged", "*") -- Start the FSM.
self:AddTransition("*", "MarkDeleted", "*") -- Start the FSM.
self:AddTransition("Running", "Stop", "Stopped") -- Stop the FSM.
self:HandleEvent(EVENTS.MarkAdded, self.OnEventMark)
self:HandleEvent(EVENTS.MarkChange, self.OnEventMark)
self:HandleEvent(EVENTS.MarkRemoved, self.OnEventMark)
-- start
self:I(self.lid..string.format("started for %s",self.Tag))
self:__Start(1)
return self
-------------------
-- PSEUDO Functions
-------------------
--- On after "MarkAdded" event. Triggered when a Marker is added to the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkAdded
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
--- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
-- @param #number idx DCS Marker ID
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
--- "Stop" trigger. Used to stop the function an unhandle events
-- @function [parent=#MARKEROPS_BASE] Stop
end
--- (internal) Handle events.
-- @param #MARKEROPS_BASE self
-- @param Core.Event#EVENTDATA Event
function MARKEROPS_BASE:OnEventMark(Event)
self:T({Event})
if Event == nil or Event.idx == nil then
self:E("Skipping onEvent. Event or Event.idx unknown.")
return true
end
--position
local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z}
local coord=COORDINATE:NewFromVec3(vec3)
if self.debug then
local coordtext = coord:ToStringLLDDM()
local text = tostring(Event.text)
local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
end
-- decision
if Event.id==world.event.S_EVENT_MARK_ADDED then
self:T({event="S_EVENT_MARK_ADDED", carrier=self.groupname, vec3=Event.pos})
-- Handle event
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkAdded(Eventtext,matchtable,coord)
end
end
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
self:T({event="S_EVENT_MARK_CHANGE", carrier=self.groupname, vec3=Event.pos})
-- Handle event.
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx)
end
end
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
self:T({event="S_EVENT_MARK_REMOVED", carrier=self.groupname, vec3=Event.pos})
-- Hande event.
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
self:MarkDeleted()
end
end
end
end
--- (internal) Match tag.
-- @param #MARKEROPS_BASE self
-- @param #string Eventtext Text added to the marker.
-- @return #boolean
function MARKEROPS_BASE:_MatchTag(Eventtext)
local matches = false
if not self.Casesensitive then
local type = string.lower(self.Tag) -- #string
if string.find(string.lower(Eventtext),type) then
matches = true --event text contains tag
end
else
local type = self.Tag -- #string
if string.find(Eventtext,type) then
matches = true --event text contains tag
end
end
return matches
end
--- (internal) Match keywords table.
-- @param #MARKEROPS_BASE self
-- @param #string Eventtext Text added to the marker.
-- @return #table
function MARKEROPS_BASE:_MatchKeywords(Eventtext)
local matchtable = {}
local keytable = self.Keywords
for _index,_word in pairs (keytable) do
if string.find(string.lower(Eventtext),string.lower(_word))then
table.insert(matchtable,_word)
end
end
return matchtable
end
--- On before "MarkAdded" event. Triggered when a Marker is added to the F10 map.
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
function MARKEROPS_BASE:onbeforeMarkAdded(From,Event,To,Text,Keywords,Coord)
self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
end
--- On before "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
function MARKEROPS_BASE:onbeforeMarkChanged(From,Event,To,Text,Keywords,Coord)
self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
end
--- On before "MarkDeleted" event. Triggered when a Marker is removed from the F10 map.
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
function MARKEROPS_BASE:onbeforeMarkDeleted(From,Event,To)
self:T({self.lid,From,Event,To})
end
--- On enter "Stopped" event. Unsubscribe events.
-- @param #MARKEROPS_BASE self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
function MARKEROPS_BASE:onenterStopped(From,Event,To)
self:T({self.lid,From,Event,To})
-- unsubscribe from events
self:UnHandleEvent(EVENTS.MarkAdded)
self:UnHandleEvent(EVENTS.MarkChange)
self:UnHandleEvent(EVENTS.MarkRemoved)
end
--------------------------------------------------------------------------
-- MARKEROPS_BASE Class Definition End.
--------------------------------------------------------------------------

View File

@@ -64,7 +64,6 @@ MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
MENU_INDEX.Group = {} MENU_INDEX.Group = {}
function MENU_INDEX:ParentPath( ParentMenu, MenuText ) function MENU_INDEX:ParentPath( ParentMenu, MenuText )
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or "" local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
if ParentMenu then if ParentMenu then
if ParentMenu:IsInstanceOf( "MENU_GROUP" ) or ParentMenu:IsInstanceOf( "MENU_GROUP_COMMAND" ) then if ParentMenu:IsInstanceOf( "MENU_GROUP" ) or ParentMenu:IsInstanceOf( "MENU_GROUP_COMMAND" ) then
@@ -92,7 +91,6 @@ function MENU_INDEX:ParentPath( ParentMenu, MenuText )
Path = Path .. "@" .. MenuText Path = Path .. "@" .. MenuText
return Path return Path
end end
function MENU_INDEX:PrepareMission() function MENU_INDEX:PrepareMission()
@@ -103,7 +101,6 @@ function MENU_INDEX:PrepareCoalition( CoalitionSide )
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {} self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {} self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
end end
--- ---
-- @param Wrapper.Group#GROUP Group -- @param Wrapper.Group#GROUP Group
function MENU_INDEX:PrepareGroup( Group ) function MENU_INDEX:PrepareGroup( Group )
@@ -115,32 +112,22 @@ function MENU_INDEX:PrepareGroup( Group )
end end
function MENU_INDEX:HasMissionMenu( Path ) function MENU_INDEX:HasMissionMenu( Path )
return self.MenuMission.Menus[Path] return self.MenuMission.Menus[Path]
end end
function MENU_INDEX:SetMissionMenu( Path, Menu ) function MENU_INDEX:SetMissionMenu( Path, Menu )
self.MenuMission.Menus[Path] = Menu self.MenuMission.Menus[Path] = Menu
end end
function MENU_INDEX:ClearMissionMenu( Path ) function MENU_INDEX:ClearMissionMenu( Path )
self.MenuMission.Menus[Path] = nil self.MenuMission.Menus[Path] = nil
end end
function MENU_INDEX:HasCoalitionMenu( Coalition, Path ) function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
return self.Coalition[Coalition].Menus[Path] return self.Coalition[Coalition].Menus[Path]
end end
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu ) function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
self.Coalition[Coalition].Menus[Path] = Menu self.Coalition[Coalition].Menus[Path] = Menu
end end
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path ) function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
self.Coalition[Coalition].Menus[Path] = nil self.Coalition[Coalition].Menus[Path] = nil
end end
@@ -151,46 +138,36 @@ function MENU_INDEX:HasGroupMenu( Group, Path )
end end
return nil return nil
end end
function MENU_INDEX:SetGroupMenu( Group, Path, Menu ) function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
local MenuGroupName = Group:GetName() local MenuGroupName = Group:GetName()
Group:F({MenuGroupName=MenuGroupName,Path=Path}) Group:F({MenuGroupName=MenuGroupName,Path=Path})
self.Group[MenuGroupName].Menus[Path] = Menu self.Group[MenuGroupName].Menus[Path] = Menu
end end
function MENU_INDEX:ClearGroupMenu( Group, Path ) function MENU_INDEX:ClearGroupMenu( Group, Path )
local MenuGroupName = Group:GetName() local MenuGroupName = Group:GetName()
self.Group[MenuGroupName].Menus[Path] = nil self.Group[MenuGroupName].Menus[Path] = nil
end end
function MENU_INDEX:Refresh( Group ) function MENU_INDEX:Refresh( Group )
for MenuID, Menu in pairs( self.MenuMission.Menus ) do for MenuID, Menu in pairs( self.MenuMission.Menus ) do
Menu:Refresh() Menu:Refresh()
end end
for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do
Menu:Refresh() Menu:Refresh()
end end
for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do
Menu:Refresh() Menu:Refresh()
end end
local GroupName = Group:GetName() local GroupName = Group:GetName()
for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do
Menu:Refresh() Menu:Refresh()
end end
return self
end end
do -- MENU_BASE do -- MENU_BASE
--- @type MENU_BASE --- @type MENU_BASE
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Defines the main MENU class where other MENU classes are derived from. --- Defines the main MENU class where other MENU classes are derived from.
-- This is an abstract class, so don't use it. -- This is an abstract class, so don't use it.
-- @field #MENU_BASE -- @field #MENU_BASE
@@ -210,7 +187,6 @@ do -- MENU_BASE
if ParentMenu ~= nil then if ParentMenu ~= nil then
MenuParentPath = ParentMenu.MenuPath MenuParentPath = ParentMenu.MenuPath
end end
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self.MenuPath = nil self.MenuPath = nil
@@ -230,7 +206,6 @@ do -- MENU_BASE
return self return self
end end
function MENU_BASE:SetParentMenu( MenuText, Menu ) function MENU_BASE:SetParentMenu( MenuText, Menu )
if self.ParentMenu then if self.ParentMenu then
self.ParentMenu.Menus = self.ParentMenu.Menus or {} self.ParentMenu.Menus = self.ParentMenu.Menus or {}
@@ -238,7 +213,6 @@ do -- MENU_BASE
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
end end
end end
function MENU_BASE:ClearParentMenu( MenuText ) function MENU_BASE:ClearParentMenu( MenuText )
if self.ParentMenu and self.ParentMenu.Menus[MenuText] then if self.ParentMenu and self.ParentMenu.Menus[MenuText] then
self.ParentMenu.Menus[MenuText] = nil self.ParentMenu.Menus[MenuText] = nil
@@ -248,7 +222,6 @@ do -- MENU_BASE
end end
end end
end end
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
@@ -276,6 +249,7 @@ do -- MENU_BASE
return self return self
end end
--- Gets a menu stamp for later prevention of menu removal. --- Gets a menu stamp for later prevention of menu removal.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @return MenuStamp -- @return MenuStamp
@@ -283,6 +257,7 @@ do -- MENU_BASE
return timer.getTime() return timer.getTime()
end end
--- Sets a time stamp for later prevention of menu removal. --- Sets a time stamp for later prevention of menu removal.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param MenuStamp -- @param MenuStamp
@@ -302,9 +277,7 @@ do -- MENU_BASE
end end
end end
do -- MENU_COMMAND_BASE do -- MENU_COMMAND_BASE
--- @type MENU_COMMAND_BASE --- @type MENU_COMMAND_BASE
-- @field #function MenuCallHandler -- @field #function MenuCallHandler
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
@@ -326,7 +299,6 @@ do -- MENU_COMMAND_BASE
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
-- When a menu function goes into error, DCS displays an obscure menu message. -- When a menu function goes into error, DCS displays an obscure menu message.
-- This error handler catches the menu error and displays the full call stack. -- This error handler catches the menu error and displays the full call stack.
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
@@ -359,7 +331,6 @@ do -- MENU_COMMAND_BASE
self.CommandMenuFunction = CommandMenuFunction self.CommandMenuFunction = CommandMenuFunction
return self return self
end end
--- This sets the new command arguments of a menu, --- This sets the new command arguments of a menu,
-- so that if a menu is regenerated, or if command arguments change, -- so that if a menu is regenerated, or if command arguments change,
-- that the arguments set for the menu are loosely coupled with the menu itself!!! -- that the arguments set for the menu are loosely coupled with the menu itself!!!
@@ -370,14 +341,11 @@ do -- MENU_COMMAND_BASE
self.CommandMenuArguments = CommandMenuArguments self.CommandMenuArguments = CommandMenuArguments
return self return self
end end
end end
do -- MENU_MISSION do -- MENU_MISSION
--- @type MENU_MISSION --- @type MENU_MISSION
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
--- Manages the main menus for a complete mission. --- Manages the main menus for a complete mission.
-- --
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. -- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
@@ -397,7 +365,6 @@ do -- MENU_MISSION
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu then if MissionMenu then
return MissionMenu return MissionMenu
else else
@@ -415,12 +382,11 @@ do -- MENU_MISSION
-- @param #MENU_MISSION self -- @param #MENU_MISSION self
-- @return #MENU_MISSION -- @return #MENU_MISSION
function MENU_MISSION:Refresh() function MENU_MISSION:Refresh()
do do
missionCommands.removeItem( self.MenuPath ) missionCommands.removeItem( self.MenuPath )
self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath ) self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath )
end end
return self
end end
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
@@ -444,7 +410,6 @@ do -- MENU_MISSION
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu == self then if MissionMenu == self then
self:RemoveSubMenus() self:RemoveSubMenus()
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
@@ -466,7 +431,6 @@ do -- MENU_MISSION
end end
end end
do -- MENU_MISSION_COMMAND do -- MENU_MISSION_COMMAND
--- @type MENU_MISSION_COMMAND --- @type MENU_MISSION_COMMAND
@@ -494,7 +458,6 @@ do -- MENU_MISSION_COMMAND
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu then if MissionMenu then
MissionMenu:SetCommandMenuFunction( CommandMenuFunction ) MissionMenu:SetCommandMenuFunction( CommandMenuFunction )
MissionMenu:SetCommandMenuArguments( arg ) MissionMenu:SetCommandMenuArguments( arg )
@@ -508,17 +471,15 @@ do -- MENU_MISSION_COMMAND
return self return self
end end
end end
--- Refreshes a radio item for a mission --- Refreshes a radio item for a mission
-- @param #MENU_MISSION_COMMAND self -- @param #MENU_MISSION_COMMAND self
-- @return #MENU_MISSION_COMMAND -- @return #MENU_MISSION_COMMAND
function MENU_MISSION_COMMAND:Refresh() function MENU_MISSION_COMMAND:Refresh()
do do
missionCommands.removeItem( self.MenuPath ) missionCommands.removeItem( self.MenuPath )
missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a radio command item for a coalition --- Removes a radio command item for a coalition
@@ -529,7 +490,6 @@ do -- MENU_MISSION_COMMAND
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu == self then if MissionMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
@@ -548,11 +508,8 @@ do -- MENU_MISSION_COMMAND
return self return self
end end
end end
do -- MENU_COALITION do -- MENU_COALITION
--- @type MENU_COALITION --- @type MENU_COALITION
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
@@ -601,7 +558,7 @@ do -- MENU_COALITION
-- --
-- @field #MENU_COALITION -- @field #MENU_COALITION
MENU_COALITION = { MENU_COALITION = {
ClassName = "MENU_COALITION", ClassName = "MENU_COALITION"
} }
--- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition. --- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition.
@@ -611,15 +568,12 @@ do -- MENU_COALITION
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other). -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other).
-- @return #MENU_COALITION self -- @return #MENU_COALITION self
function MENU_COALITION:New( Coalition, MenuText, ParentMenu ) function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
MENU_INDEX:PrepareCoalition( Coalition ) MENU_INDEX:PrepareCoalition( Coalition )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
if CoalitionMenu then if CoalitionMenu then
return CoalitionMenu return CoalitionMenu
else else
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetCoalitionMenu( Coalition, Path, self ) MENU_INDEX:SetCoalitionMenu( Coalition, Path, self )
@@ -630,17 +584,15 @@ do -- MENU_COALITION
return self return self
end end
end end
--- Refreshes a radio item for a coalition --- Refreshes a radio item for a coalition
-- @param #MENU_COALITION self -- @param #MENU_COALITION self
-- @return #MENU_COALITION -- @return #MENU_COALITION
function MENU_COALITION:Refresh() function MENU_COALITION:Refresh()
do do
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath )
end end
return self
end end
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
@@ -663,7 +615,6 @@ do -- MENU_COALITION
MENU_INDEX:PrepareCoalition( self.Coalition ) MENU_INDEX:PrepareCoalition( self.Coalition )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
if CoalitionMenu == self then if CoalitionMenu == self then
self:RemoveSubMenus() self:RemoveSubMenus()
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
@@ -683,9 +634,7 @@ do -- MENU_COALITION
return self return self
end end
end end
do -- MENU_COALITION_COMMAND do -- MENU_COALITION_COMMAND
--- @type MENU_COALITION_COMMAND --- @type MENU_COALITION_COMMAND
@@ -698,7 +647,7 @@ do -- MENU_COALITION_COMMAND
-- --
-- @field #MENU_COALITION_COMMAND -- @field #MENU_COALITION_COMMAND
MENU_COALITION_COMMAND = { MENU_COALITION_COMMAND = {
ClassName = "MENU_COALITION_COMMAND", ClassName = "MENU_COALITION_COMMAND"
} }
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
@@ -714,7 +663,6 @@ do -- MENU_COALITION_COMMAND
MENU_INDEX:PrepareCoalition( Coalition ) MENU_INDEX:PrepareCoalition( Coalition )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
if CoalitionMenu then if CoalitionMenu then
CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction ) CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction )
CoalitionMenu:SetCommandMenuArguments( arg ) CoalitionMenu:SetCommandMenuArguments( arg )
@@ -729,19 +677,17 @@ do -- MENU_COALITION_COMMAND
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
return self return self
end end
end end
--- Refreshes a radio item for a coalition --- Refreshes a radio item for a coalition
-- @param #MENU_COALITION_COMMAND self -- @param #MENU_COALITION_COMMAND self
-- @return #MENU_COALITION_COMMAND -- @return #MENU_COALITION_COMMAND
function MENU_COALITION_COMMAND:Refresh() function MENU_COALITION_COMMAND:Refresh()
do do
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a radio command item for a coalition --- Removes a radio command item for a coalition
@@ -752,7 +698,6 @@ do -- MENU_COALITION_COMMAND
MENU_INDEX:PrepareCoalition( self.Coalition ) MENU_INDEX:PrepareCoalition( self.Coalition )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
if CoalitionMenu == self then if CoalitionMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
@@ -771,11 +716,9 @@ do -- MENU_COALITION_COMMAND
return self return self
end end
end end
--- MENU_GROUP --- MENU_GROUP
do do
-- This local variable is used to cache the menus registered under groups. -- This local variable is used to cache the menus registered under groups.
-- Menus don't disappear when groups for players are destroyed and restarted. -- Menus don't disappear when groups for players are destroyed and restarted.
@@ -783,10 +726,10 @@ do
-- the same menus twice during initialization logic. -- the same menus twice during initialization logic.
-- These menu classes are handling this logic with this variable. -- These menu classes are handling this logic with this variable.
local _MENUGROUPS = {} local _MENUGROUPS = {}
--- @type MENU_GROUP --- @type MENU_GROUP
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
--- Manages the main menus for @{Wrapper.Group}s. --- Manages the main menus for @{Wrapper.Group}s.
-- --
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
@@ -844,7 +787,7 @@ do
-- --
-- @field #MENU_GROUP -- @field #MENU_GROUP
MENU_GROUP = { MENU_GROUP = {
ClassName = "MENU_GROUP", ClassName = "MENU_GROUP"
} }
--- MENU_GROUP constructor. Creates a new radio menu item for a group. --- MENU_GROUP constructor. Creates a new radio menu item for a group.
@@ -858,16 +801,13 @@ do
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
self.GroupID = Group:GetID() self.GroupID = Group:GetID()
self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath ) self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath )
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
@@ -880,7 +820,6 @@ do
-- @param #MENU_GROUP self -- @param #MENU_GROUP self
-- @return #MENU_GROUP -- @return #MENU_GROUP
function MENU_GROUP:Refresh() function MENU_GROUP:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -890,6 +829,30 @@ do
end end
end end
return self
end
--- Refreshes a new radio item for a group and submenus, ordering by (numerical) MenuTag
-- @param #MENU_GROUP self
-- @return #MENU_GROUP
function MENU_GROUP:RefreshAndOrderByTag()
do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
local MenuTable = {}
for MenuText, Menu in pairs( self.Menus or {} ) do
local tag = Menu.MenuTag or math.random(1,10000)
MenuTable[#MenuTable+1] = {Tag=tag, Enty=Menu}
end
table.sort(MenuTable, function (k1, k2) return k1.tag < k2.tag end )
for _, Menu in pairs( MenuTable ) do
Menu.Entry:Refresh()
end
end
return self
end end
--- Removes the sub menus recursively of this MENU_GROUP. --- Removes the sub menus recursively of this MENU_GROUP.
@@ -898,7 +861,6 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP self -- @return #MENU_GROUP self
function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag ) function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag )
for MenuText, Menu in pairs( self.Menus or {} ) do for MenuText, Menu in pairs( self.Menus or {} ) do
Menu:Remove( MenuStamp, MenuTag ) Menu:Remove( MenuStamp, MenuTag )
end end
@@ -913,11 +875,9 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP:Remove( MenuStamp, MenuTag ) function MENU_GROUP:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
self:RemoveSubMenus( MenuStamp, MenuTag ) self:RemoveSubMenus( MenuStamp, MenuTag )
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
@@ -939,6 +899,7 @@ do
return self return self
end end
--- @type MENU_GROUP_COMMAND --- @type MENU_GROUP_COMMAND
-- @extends Core.Menu#MENU_COMMAND_BASE -- @extends Core.Menu#MENU_COMMAND_BASE
@@ -948,7 +909,7 @@ do
-- --
-- @field #MENU_GROUP_COMMAND -- @field #MENU_GROUP_COMMAND
MENU_GROUP_COMMAND = { MENU_GROUP_COMMAND = {
ClassName = "MENU_GROUP_COMMAND", ClassName = "MENU_GROUP_COMMAND"
} }
--- Creates a new radio command item for a group --- Creates a new radio command item for a group
@@ -960,18 +921,15 @@ do
-- @param CommandMenuArgument An argument for the function. -- @param CommandMenuArgument An argument for the function.
-- @return #MENU_GROUP_COMMAND -- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... ) function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
GroupMenu:SetCommandMenuFunction( CommandMenuFunction ) GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
GroupMenu:SetCommandMenuArguments( arg ) GroupMenu:SetCommandMenuArguments( arg )
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
@@ -982,19 +940,17 @@ do
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
return self return self
end end
end end
--- Refreshes a radio item for a group --- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND self -- @param #MENU_GROUP_COMMAND self
-- @return #MENU_GROUP_COMMAND -- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:Refresh() function MENU_GROUP_COMMAND:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a menu structure for a group. --- Removes a menu structure for a group.
@@ -1003,11 +959,9 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag ) function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
@@ -1026,16 +980,13 @@ do
return self return self
end end
end end
--- MENU_GROUP_DELAYED --- MENU_GROUP_DELAYED
do do
--- @type MENU_GROUP_DELAYED --- @type MENU_GROUP_DELAYED
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
--- The MENU_GROUP_DELAYED class manages the main menus for groups. --- The MENU_GROUP_DELAYED class manages the main menus for groups.
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
@@ -1045,7 +996,7 @@ do
-- --
-- @field #MENU_GROUP_DELAYED -- @field #MENU_GROUP_DELAYED
MENU_GROUP_DELAYED = { MENU_GROUP_DELAYED = {
ClassName = "MENU_GROUP_DELAYED", ClassName = "MENU_GROUP_DELAYED"
} }
--- MENU_GROUP_DELAYED constructor. Creates a new radio menu item for a group. --- MENU_GROUP_DELAYED constructor. Creates a new radio menu item for a group.
@@ -1059,16 +1010,13 @@ do
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
self.GroupID = Group:GetID() self.GroupID = Group:GetID()
if self.MenuParentPath then if self.MenuParentPath then
self.MenuPath = UTILS.DeepCopy( self.MenuParentPath ) self.MenuPath = UTILS.DeepCopy( self.MenuParentPath )
else else
@@ -1086,7 +1034,6 @@ do
-- @param #MENU_GROUP_DELAYED self -- @param #MENU_GROUP_DELAYED self
-- @return #MENU_GROUP_DELAYED -- @return #MENU_GROUP_DELAYED
function MENU_GROUP_DELAYED:Set() function MENU_GROUP_DELAYED:Set()
do do
if not self.MenuSet then if not self.MenuSet then
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -1097,14 +1044,12 @@ do
Menu:Set() Menu:Set()
end end
end end
end end
--- Refreshes a new radio item for a group and submenus --- Refreshes a new radio item for a group and submenus
-- @param #MENU_GROUP_DELAYED self -- @param #MENU_GROUP_DELAYED self
-- @return #MENU_GROUP_DELAYED -- @return #MENU_GROUP_DELAYED
function MENU_GROUP_DELAYED:Refresh() function MENU_GROUP_DELAYED:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -1114,6 +1059,7 @@ do
end end
end end
return self
end end
--- Removes the sub menus recursively of this MENU_GROUP_DELAYED. --- Removes the sub menus recursively of this MENU_GROUP_DELAYED.
@@ -1122,7 +1068,6 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP_DELAYED self -- @return #MENU_GROUP_DELAYED self
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag ) function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag )
for MenuText, Menu in pairs( self.Menus or {} ) do for MenuText, Menu in pairs( self.Menus or {} ) do
Menu:Remove( MenuStamp, MenuTag ) Menu:Remove( MenuStamp, MenuTag )
end end
@@ -1137,11 +1082,9 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag ) function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
self:RemoveSubMenus( MenuStamp, MenuTag ) self:RemoveSubMenus( MenuStamp, MenuTag )
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
@@ -1163,6 +1106,7 @@ do
return self return self
end end
--- @type MENU_GROUP_COMMAND_DELAYED --- @type MENU_GROUP_COMMAND_DELAYED
-- @extends Core.Menu#MENU_COMMAND_BASE -- @extends Core.Menu#MENU_COMMAND_BASE
@@ -1173,7 +1117,7 @@ do
-- --
-- @field #MENU_GROUP_COMMAND_DELAYED -- @field #MENU_GROUP_COMMAND_DELAYED
MENU_GROUP_COMMAND_DELAYED = { MENU_GROUP_COMMAND_DELAYED = {
ClassName = "MENU_GROUP_COMMAND_DELAYED", ClassName = "MENU_GROUP_COMMAND_DELAYED"
} }
--- Creates a new radio command item for a group --- Creates a new radio command item for a group
@@ -1185,18 +1129,15 @@ do
-- @param CommandMenuArgument An argument for the function. -- @param CommandMenuArgument An argument for the function.
-- @return #MENU_GROUP_COMMAND_DELAYED -- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... ) function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
GroupMenu:SetCommandMenuFunction( CommandMenuFunction ) GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
GroupMenu:SetCommandMenuArguments( arg ) GroupMenu:SetCommandMenuArguments( arg )
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
@@ -1212,33 +1153,29 @@ do
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
return self return self
end end
end end
--- Refreshes a radio item for a group --- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND_DELAYED self -- @param #MENU_GROUP_COMMAND_DELAYED self
-- @return #MENU_GROUP_COMMAND_DELAYED -- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:Set() function MENU_GROUP_COMMAND_DELAYED:Set()
do do
if not self.MenuSet then if not self.MenuSet then
self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
self.MenuSet = true self.MenuSet = true
end end
end end
end end
--- Refreshes a radio item for a group --- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND_DELAYED self -- @param #MENU_GROUP_COMMAND_DELAYED self
-- @return #MENU_GROUP_COMMAND_DELAYED -- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:Refresh() function MENU_GROUP_COMMAND_DELAYED:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a menu structure for a group. --- Removes a menu structure for a group.
@@ -1247,11 +1184,9 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag ) function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
@@ -1270,6 +1205,4 @@ do
return self return self
end end
end end

View File

@@ -10,6 +10,7 @@
-- * Send message to all players. -- * Send message to all players.
-- * Send messages to a coalition. -- * Send messages to a coalition.
-- * Send messages to a specific group. -- * Send messages to a specific group.
-- * Send messages to a specific unit or client.
-- --
-- === -- ===
-- --
@@ -33,8 +34,9 @@
-- --
-- Messages are sent: -- Messages are sent:
-- --
-- * To a @{Client} using @{#MESSAGE.ToClient}(). -- * To a @{Wrapper.Client} using @{#MESSAGE.ToClient}().
-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}() -- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
-- * To a @{Wrapper.Unit} using @{#MESSAGE.ToUnit}()
-- * To a coalition using @{#MESSAGE.ToCoalition}(). -- * To a coalition using @{#MESSAGE.ToCoalition}().
-- * To the red coalition using @{#MESSAGE.ToRed}(). -- * To the red coalition using @{#MESSAGE.ToRed}().
-- * To the blue coalition using @{#MESSAGE.ToBlue}(). -- * To the blue coalition using @{#MESSAGE.ToBlue}().
@@ -126,7 +128,7 @@ end
--- Creates a new MESSAGE object of a certain type. --- Creates a new MESSAGE object of a certain type.
-- Note that these MESSAGE objects are not yet displayed on the display panel. -- Note that these MESSAGE objects are not yet displayed on the display panel.
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients. -- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu. -- The message display times are automatically defined based on the timing settings in the @{Core.Settings} menu.
-- @param self -- @param self
-- @param #string MessageText is the text of the Message. -- @param #string MessageText is the text of the Message.
-- @param #MESSAGE.Type MessageType The type of the message. -- @param #MESSAGE.Type MessageType The type of the message.
@@ -169,7 +171,7 @@ end
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player". --- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param Wrapper.Client#CLIENT Client is the Group of the Client. -- @param Wrapper.Client#CLIENT Client is the Group of the Client.
-- @param Core.Settings#SETTINGS Settings Settings used to display the message. -- @param Core.Settings#SETTINGS Settings used to display the message.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
-- --
@@ -180,11 +182,11 @@ end
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- or -- or
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25 ):ToClient( ClientGroup )
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25 ):ToClient( ClientGroup )
-- or -- or
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ) -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25 )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ) -- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25 )
-- MessageClient1:ToClient( ClientGroup ) -- MessageClient1:ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup ) -- MessageClient2:ToClient( ClientGroup )
-- --
@@ -199,10 +201,13 @@ function MESSAGE:ToClient( Client, Settings )
self.MessageCategory = "" -- self.MessageType .. ": " self.MessageCategory = "" -- self.MessageType .. ": "
end end
local Unit = Client:GetClient()
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
local ClientGroupID = Client:GetClientGroupID() local ClientGroupID = Client:GetClientGroupID()
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) --trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
end end
end end
@@ -212,6 +217,7 @@ end
--- Sends a MESSAGE to a Group. --- Sends a MESSAGE to a Group.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param Wrapper.Group#GROUP Group to which the message is displayed. -- @param Wrapper.Group#GROUP Group to which the message is displayed.
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object. -- @return #MESSAGE Message object.
function MESSAGE:ToGroup( Group, Settings ) function MESSAGE:ToGroup( Group, Settings )
self:F( Group.GroupName ) self:F( Group.GroupName )
@@ -232,17 +238,78 @@ function MESSAGE:ToGroup( Group, Settings )
return self return self
end end
--- Sends a MESSAGE to a Unit.
-- @param #MESSAGE self
-- @param Wrapper.Unit#UNIT Unit to which the message is displayed.
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object.
function MESSAGE:ToUnit( Unit, Settings )
self:F( Unit.IdentifiableName )
if Unit then
if self.MessageType then
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
end
end
return self
end
--- Sends a MESSAGE to a Country.
-- @param #MESSAGE self
-- @param #number Country to which the message is displayed, e.g. country.id.GERMANY. For all country numbers see here: [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_country)
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object.
function MESSAGE:ToCountry( Country, Settings )
self:F(Country )
if Country then
if self.MessageType then
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForCountry( Country, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
end
end
return self
end
--- Sends a MESSAGE to a Country.
-- @param #MESSAGE self
-- @param #number Country to which the message is displayed, , e.g. country.id.GERMANY. For all country numbers see here: [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_country)
-- @param #boolean Condition Sends the message only if the condition is true.
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object.
function MESSAGE:ToCountryIf( Country, Condition, Settings )
self:F(Country )
if Country and Condition == true then
self:ToCountry( Country, Settings )
end
return self
end
--- Sends a MESSAGE to the Blue coalition. --- Sends a MESSAGE to the Blue coalition.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
-- --
-- -- Send a message created with the @{New} method to the BLUE coalition. -- -- Send a message created with the @{New} method to the BLUE coalition.
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() -- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25):ToBlue()
-- or -- or
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() -- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 ):ToBlue()
-- or -- or
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) -- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 )
-- MessageBLUE:ToBlue() -- MessageBLUE:ToBlue()
-- --
function MESSAGE:ToBlue() function MESSAGE:ToBlue()
@@ -259,11 +326,11 @@ end
-- @usage -- @usage
-- --
-- -- Send a message created with the @{New} method to the RED coalition. -- -- Send a message created with the @{New} method to the RED coalition.
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 ):ToRed()
-- or -- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() -- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 ):ToRed()
-- or -- or
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 )
-- MessageRED:ToRed() -- MessageRED:ToRed()
-- --
function MESSAGE:ToRed() function MESSAGE:ToRed()
@@ -282,11 +349,11 @@ end
-- @usage -- @usage
-- --
-- -- Send a message created with the @{New} method to the RED coalition. -- -- Send a message created with the @{New} method to the RED coalition.
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 ):ToCoalition( coalition.side.RED )
-- or -- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) -- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 ):ToCoalition( coalition.side.RED )
-- or -- or
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25 )
-- MessageRED:ToCoalition( coalition.side.RED ) -- MessageRED:ToCoalition( coalition.side.RED )
-- --
function MESSAGE:ToCoalition( CoalitionSide, Settings ) function MESSAGE:ToCoalition( CoalitionSide, Settings )
@@ -330,11 +397,11 @@ end
-- @usage -- @usage
-- --
-- -- Send a message created to all players. -- -- Send a message created to all players.
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ):ToAll()
-- or -- or
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() -- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ):ToAll()
-- or -- or
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 )
-- MessageAll:ToAll() -- MessageAll:ToAll()
-- --
function MESSAGE:ToAll( Settings ) function MESSAGE:ToAll( Settings )
@@ -356,6 +423,7 @@ end
--- Sends a MESSAGE to all players if the given Condition is true. --- Sends a MESSAGE to all players if the given Condition is true.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param #boolean Condition
-- @return #MESSAGE -- @return #MESSAGE
function MESSAGE:ToAllIf( Condition ) function MESSAGE:ToAllIf( Condition )
@@ -365,3 +433,24 @@ function MESSAGE:ToAllIf( Condition )
return self return self
end end
--- Sends a MESSAGE to DCS log file.
-- @param #MESSAGE self
-- @return #MESSAGE self
function MESSAGE:ToLog()
env.info(self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ))
return self
end
--- Sends a MESSAGE to DCS log file if the given Condition is true.
-- @param #MESSAGE self
-- @return #MESSAGE self
function MESSAGE:ToLogIf( Condition )
if Condition and Condition == true then
env.info(self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ))
end
return self
end

View File

@@ -0,0 +1,370 @@
--- **Core** - Path from A to B.
--
-- **Main Features:**
--
-- * Path from A to B
-- * Arbitrary number of points
-- * Automatically from lines drawtool
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Core.Pathline
-- @image CORE_Pathline.png
--- PATHLINE class.
-- @type PATHLINE
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string name Name of the path line.
-- @field #table points List of 3D points defining the path.
-- @extends Core.Base#BASE
--- *The shortest distance between two points is a straight line.* -- Archimedes
--
-- ===
--
-- # The PATHLINE Concept
--
-- List of points defining a path from A to B. The pathline can consist of multiple points. Each point holds the information of its position, the surface type, the land height
-- and the water depth (if over sea).
--
-- Line drawings created in the mission editor are automatically registered as pathlines and stored in the MOOSE database.
-- They can be accessed with the @{#PATHLINE.FindByName) function.
--
-- # Constructor
--
-- The @{PATHLINE.New) function creates a new PATHLINE object. This does not hold any points. Points can be added with the @{#PATHLINE.AddPointFromVec2} and @{#PATHLINE.AddPointFromVec3}
--
-- For a given table of 2D or 3D positions, a new PATHLINE object can be created with the @{#PATHLINE.NewFromVec2Array} or @{#PATHLINE.NewFromVec3Array}, respectively.
--
-- # Line Drawings
--
-- The most convenient way to create a pathline is the draw panel feature in the DCS mission editor. You can select "Line" and then "Segments", "Segment" or "Free" to draw your lines.
-- These line drawings are then automatically added to the MOOSE database as PATHLINE objects and can be retrieved with the @{#PATHLINE.FindByName) function, where the name is the one
-- you specify in the draw panel.
--
-- # Mark on F10 map
--
-- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
-- water depth.
--
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
--
-- @field #PATHLINE
PATHLINE = {
ClassName = "PATHLINE",
lid = nil,
points = {},
}
--- Point of line.
-- @type PATHLINE.Point
-- @field DCS#Vec3 vec3 3D position.
-- @field DCS#Vec2 vec2 2D position.
-- @field #number surfaceType Surface type.
-- @field #number landHeight Land height in meters.
-- @field #number depth Water depth in meters.
-- @field #number markerID Marker ID.
--- PATHLINE class version.
-- @field #string version
PATHLINE.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new PATHLINE object. Points need to be added later.
-- @param #PATHLINE self
-- @param #string Name Name of the path.
-- @return #PATHLINE self
function PATHLINE:New(Name)
-- Inherit everything from INTEL class.
local self=BASE:Inherit(self, BASE:New()) --#PATHLINE
self.name=Name or "Unknown Path"
self.lid=string.format("PATHLINE %s | ", Name)
return self
end
--- Create a new PATHLINE object from a given list of 2D points.
-- @param #PATHLINE self
-- @param #string Name Name of the pathline.
-- @param #table Vec2Array List of DCS#Vec2 points.
-- @return #PATHLINE self
function PATHLINE:NewFromVec2Array(Name, Vec2Array)
local self=PATHLINE:New(Name)
for i=1,#Vec2Array do
self:AddPointFromVec2(Vec2Array[i])
end
return self
end
--- Create a new PATHLINE object from a given list of 3D points.
-- @param #PATHLINE self
-- @param #string Name Name of the pathline.
-- @param #table Vec3Array List of DCS#Vec3 points.
-- @return #PATHLINE self
function PATHLINE:NewFromVec3Array(Name, Vec3Array)
local self=PATHLINE:New(Name)
for i=1,#Vec3Array do
self:AddPointFromVec3(Vec3Array[i])
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Find a pathline in the database.
-- @param #PATHLINE self
-- @param #string Name The name of the pathline.
-- @return #PATHLINE self
function PATHLINE:FindByName(Name)
local pathline = _DATABASE:FindPathline(Name)
return pathline
end
--- Add a point to the path from a given 2D position. The third dimension is determined from the land height.
-- @param #PATHLINE self
-- @param DCS#Vec2 Vec2 The 2D vector (x,y) to add.
-- @return #PATHLINE self
function PATHLINE:AddPointFromVec2(Vec2)
if Vec2 then
local point=self:_CreatePoint(Vec2)
table.insert(self.points, point)
end
return self
end
--- Add a point to the path from a given 3D position.
-- @param #PATHLINE self
-- @param DCS#Vec3 Vec3 The 3D vector (x,y) to add.
-- @return #PATHLINE self
function PATHLINE:AddPointFromVec3(Vec3)
if Vec3 then
local point=self:_CreatePoint(Vec3)
table.insert(self.points, point)
end
return self
end
--- Get name of pathline.
-- @param #PATHLINE self
-- @return #string Name of the pathline.
function PATHLINE:GetName()
return self.name
end
--- Get number of points.
-- @param #PATHLINE self
-- @return #number Number of points.
function PATHLINE:GetNumberOfPoints()
local N=#self.points
return N
end
--- Get points of pathline. Not that points are tables, that contain more information as just the 2D or 3D position but also the surface type etc.
-- @param #PATHLINE self
-- @return <#PATHLINE.Point> List of points.
function PATHLINE:GetPoints()
return self.points
end
--- Get 3D points of pathline.
-- @param #PATHLINE self
-- @return <DCS#Vec3> List of DCS#Vec3 points.
function PATHLINE:GetPoints3D()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
table.insert(vecs, point.vec3)
end
return vecs
end
--- Get 2D points of pathline.
-- @param #PATHLINE self
-- @return <DCS#Vec2> List of DCS#Vec2 points.
function PATHLINE:GetPoints2D()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
table.insert(vecs, point.vec2)
end
return vecs
end
--- Get COORDINATES of pathline. Note that COORDINATE objects are created when calling this function. That does involve deep copy calls and can have an impact on performance if done too often.
-- @param #PATHLINE self
-- @return <Core.Point#COORDINATE> List of COORDINATES points.
function PATHLINE:GetCoordinats()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
local coord=COORDINATE:NewFromVec3(point.vec3)
end
return vecs
end
--- Get the n-th point of the pathline.
-- @param #PATHLINE self
-- @param #number n The index of the point. Default is the first point.
-- @return #PATHLINE.Point Point.
function PATHLINE:GetPointFromIndex(n)
local N=self:GetNumberOfPoints()
n=n or 1
local point=nil --#PATHLINE.Point
if n>=1 and n<=N then
point=self.point[n]
else
self:E(self.lid..string.format("ERROR: No point in pathline for N=%s", tostring(n)))
end
return point
end
--- Get the 3D position of the n-th point.
-- @param #PATHLINE self
-- @param #number n The n-th point.
-- @return DCS#VEC3 Position in 3D.
function PATHLINE:GetPoint3DFromIndex(n)
local point=self:GetPointFromIndex(n)
if point then
return point.vec3
end
return nil
end
--- Get the 2D position of the n-th point.
-- @param #PATHLINE self
-- @param #number n The n-th point.
-- @return DCS#VEC2 Position in 3D.
function PATHLINE:GetPoint2DFromIndex(n)
local point=self:GetPointFromIndex(n)
if point then
return point.vec2
end
return nil
end
--- Mark points on F10 map.
-- @param #PATHLINE self
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
-- @return <DCS#Vec3> List of DCS#Vec3 points.
function PATHLINE:MarkPoints(Switch)
for i,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
if Switch==false then
if point.markerID then
UTILS.RemoveMark(point.markerID, Delay)
end
else
if point.markerID then
UTILS.RemoveMark(point.markerID)
end
point.markerID=UTILS.GetMarkID()
local text=string.format("Pathline %s: Point #%d\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.surfaceType, point.landHeight, point.depth)
trigger.action.markToAll(point.markerID, text, point.vec3, "")
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get 3D points of pathline.
-- @param #PATHLINE self
-- @param DCS#Vec3 Vec Position vector. Can also be a DCS#Vec2 in which case the altitude at landheight is taken.
-- @return #PATHLINE.Point
function PATHLINE:_CreatePoint(Vec)
local point={} --#PATHLINE.Point
if Vec.z then
-- Given vec is 3D
point.vec3=UTILS.DeepCopy(Vec)
point.vec2={x=Vec.x, y=Vec.z}
else
-- Given vec is 2D
point.vec2=UTILS.DeepCopy(Vec)
point.vec3={x=Vec.x, y=land.getHeight(Vec), z=Vec.y}
end
-- Get surface type.
point.surfaceType=land.getSurfaceType(point.vec2)
-- Get land height and depth.
point.landHeight, point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
point.markerID=nil
return point
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules. --- **Core** - SCHEDULEDISPATCHER dispatches the different schedules.
-- --
-- === -- ===
-- --
@@ -109,9 +109,11 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
if Scheduler.MasterObject then if Scheduler.MasterObject then
--env.info("FF Object Scheduler")
self.ObjectSchedulers[CallID] = Scheduler self.ObjectSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, ObjectScheduler = tostring( self.ObjectSchedulers[CallID] ), MasterObject = tostring( Scheduler.MasterObject ) } ) self:F3( { CallID = CallID, ObjectScheduler = tostring( self.ObjectSchedulers[CallID] ), MasterObject = tostring( Scheduler.MasterObject ) } )
else else
--env.info("FF Persistent Scheduler")
self.PersistentSchedulers[CallID] = Scheduler self.PersistentSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } ) self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
end end
@@ -122,7 +124,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
self.Schedule[Scheduler][CallID].Function = ScheduleFunction self.Schedule[Scheduler][CallID].Function = ScheduleFunction
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 ) self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
self.Schedule[Scheduler][CallID].Start = Start + 0.1 self.Schedule[Scheduler][CallID].Start = Start + 0.001
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0 self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0 self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
self.Schedule[Scheduler][CallID].Stop = Stop self.Schedule[Scheduler][CallID].Stop = Stop
@@ -217,6 +219,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
if ShowTrace then if ShowTrace then
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" ) SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end end
-- The master object is passed as first parameter. A few :Schedule() calls in MOOSE expect this currently. But in principle it should be removed.
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) ) return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
end end
Status, Result = xpcall( Timer, ErrorHandler ) Status, Result = xpcall( Timer, ErrorHandler )
@@ -314,7 +317,7 @@ end
--- Stop dispatcher. --- Stop dispatcher.
-- @param #SCHEDULEDISPATCHER self -- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
-- @param #table CallID Call ID. -- @param #string CallID (Optional) Scheduler Call ID. If nil, all pending schedules are stopped recursively.
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
self:F2( { Stop = CallID, Scheduler = Scheduler } ) self:F2( { Stop = CallID, Scheduler = Scheduler } )

View File

@@ -14,7 +14,7 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --
@@ -237,7 +237,7 @@ end
-- @param #number Stop Time interval in seconds after which the scheduler will be stopped. -- @param #number Stop Time interval in seconds after which the scheduler will be stopped.
-- @param #number TraceLevel Trace level [0,3]. Default 3. -- @param #number TraceLevel Trace level [0,3]. Default 3.
-- @param Core.Fsm#FSM Fsm Finite state model. -- @param Core.Fsm#FSM Fsm Finite state model.
-- @return #table The ScheduleID of the planned schedule. -- @return #string The Schedule ID of the planned schedule.
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm ) function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
self:F2( { Start, Repeat, RandomizeFactor, Stop } ) self:F2( { Start, Repeat, RandomizeFactor, Stop } )
self:T3( { SchedulerArguments } ) self:T3( { SchedulerArguments } )

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
--- **Core** - Manages various settings for running missions, consumed by moose classes and provides a menu system for players to tweak settings in running missions. --- **Core** - Manages various settings for missions, providing a menu for players to tweak settings in running missions.
-- --
-- === -- ===
-- --
@@ -251,6 +251,7 @@ do -- SETTINGS
self:SetMessageTime( MESSAGE.Type.Overview, 60 ) self:SetMessageTime( MESSAGE.Type.Overview, 60 )
self:SetMessageTime( MESSAGE.Type.Update, 15 ) self:SetMessageTime( MESSAGE.Type.Update, 15 )
self:SetEraModern() self:SetEraModern()
self:SetLocale("en")
return self return self
else else
local Settings = _DATABASE:GetPlayerSettings( PlayerName ) local Settings = _DATABASE:GetPlayerSettings( PlayerName )
@@ -283,6 +284,20 @@ do -- SETTINGS
self.Metric = true self.Metric = true
end end
--- Sets the SETTINGS default text locale.
-- @param #SETTINGS self
-- @param #string Locale
function SETTINGS:SetLocale(Locale)
self.Locale = Locale or "en"
end
--- Gets the SETTINGS text locale.
-- @param #SETTINGS self
-- @return #string
function SETTINGS:GetLocale()
return self.Locale or _SETTINGS:GetLocale()
end
--- Gets if the SETTINGS is metric. --- Gets if the SETTINGS is metric.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if metric. -- @return #boolean true if metric.
@@ -300,7 +315,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if imperial. -- @return #boolean true if imperial.
function SETTINGS:IsImperial() function SETTINGS:IsImperial()
return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsMetric()) return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsImperial())
end end
--- Sets the SETTINGS LL accuracy. --- Sets the SETTINGS LL accuracy.
@@ -320,7 +335,7 @@ do -- SETTINGS
--- Sets the SETTINGS MGRS accuracy. --- Sets the SETTINGS MGRS accuracy.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @param #number MGRS_Accuracy -- @param #number MGRS_Accuracy 0 to 5
-- @return #SETTINGS -- @return #SETTINGS
function SETTINGS:SetMGRS_Accuracy( MGRS_Accuracy ) function SETTINGS:SetMGRS_Accuracy( MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy self.MGRS_Accuracy = MGRS_Accuracy

View File

@@ -30,7 +30,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
-- --
-- === -- ===
-- --
@@ -163,11 +163,21 @@
-- --
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a battalion in an array. -- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a battalion in an array.
-- --
-- ### Group initial position - if wanted different from template position, for use with e.g. @{#SPAWN.SpawnScheduled}().
--
-- * @{#SPAWN.InitPositionCoordinate}(): Set initial position of group via a COORDINATE.
-- * @{#SPAWN.InitPositionVec2}(): Set initial position of group via a VEC2.
--
-- ### Set the positions of a group's units to absolute positions, or relative positions to unit No. 1
--
-- * @{#SPAWN.InitSetUnitRelativePositions}(): Spawn the UNITs of this group with individual relative positions to unit #1 and individual headings.
-- * @{#SPAWN.InitSetUnitAbsolutePositions}(): Spawn the UNITs of this group with individual absolute positions and individual headings.
--
-- ### Position randomization -- ### Position randomization
-- --
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. -- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Wrapper.Unit}s in the @{Wrapper.Group} that is spawned within a **radius band**, given an Outer and Inner radius. -- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Wrapper.Unit}s in the @{Wrapper.Group} that is spawned within a **radius band**, given an Outer and Inner radius.
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor. -- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Core.Zone}s that are declared using this function. Each zone can be given a probability factor.
-- --
-- ### Enable / Disable AI when spawning a new @{Wrapper.Group} -- ### Enable / Disable AI when spawning a new @{Wrapper.Group}
-- --
@@ -200,13 +210,13 @@
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index. -- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air). -- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ). -- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}. -- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Wrapper.Static}.
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Wrapper.Unit}. -- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Wrapper.Unit}.
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. -- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Core.Zone}.
-- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Wrapper.Airbase}, which can be an airdrome, ship or helipad. -- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Wrapper.Airbase}, which can be an airdrome, ship or helipad.
-- --
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{Wrapper.Group#GROUP.New} object, that contains a reference to the DCSGroup object. -- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{Wrapper.Group#GROUP.New} object, that contains a reference to the DCSGroup object.
-- You can use the @{GROUP} object to do further actions with the DCSGroup. -- You can use the @{Wrapper.Group#GROUP} object to do further actions with the DCSGroup.
-- --
-- ### **Scheduled** spawning methods -- ### **Scheduled** spawning methods
-- --
@@ -268,7 +278,7 @@ SPAWN = {
-- @type SPAWN.Takeoff -- @type SPAWN.Takeoff
-- @extends Wrapper.Group#GROUP.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff
--- @field #SPAWN.Takeoff Takeoff -- @field #SPAWN.Takeoff Takeoff
SPAWN.Takeoff = { SPAWN.Takeoff = {
Air = 1, Air = 1,
Runway = 2, Runway = 2,
@@ -276,7 +286,7 @@ SPAWN.Takeoff = {
Cold = 4, Cold = 4,
} }
--- @type SPAWN.SpawnZoneTable -- @type SPAWN.SpawnZoneTable
-- @list <Core.Zone#ZONE_BASE> SpawnZone -- @list <Core.Zone#ZONE_BASE> SpawnZone
--- Creates the main object to spawn a @{Wrapper.Group} defined in the DCS ME. --- Creates the main object to spawn a @{Wrapper.Group} defined in the DCS ME.
@@ -335,7 +345,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template.
-- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime. -- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime.
-- @return #SPAWN -- @return #SPAWN self
-- @usage -- @usage
-- -- NATO helicopters engaging in the battle field. -- -- NATO helicopters engaging in the battle field.
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) -- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
@@ -385,32 +395,129 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
return self return self
end end
--- Creates a new SPAWN instance to create new groups based on the provided template. --- Creates a new SPAWN instance to create new groups based on the provided template. This will also register the template for future use.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure! -- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure - see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_func_addGroup)!
-- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn. -- @param #string SpawnTemplatePrefix [Mandatory] is the name of the template and the prefix of the GROUP on spawn. The name in the template **will** be overwritten!
-- @param #string SpawnAliasPrefix (optional) is the name that will be given to the Group at runtime. -- @param #string SpawnAliasPrefix [Optional] is the prefix that will be given to the GROUP on spawn.
-- @return #SPAWN -- @param #boolean NoMooseNamingPostfix [Optional] If true, skip the Moose naming additions (like groupname#001-01) - **but** you need to ensure yourself no duplicate group names exist!
-- @usage -- @return #SPAWN self
-- -- Create a new SPAWN object based on a Group Template defined from scratch.
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
-- @usage -- @usage
-- -- Spawn a P51 Mustang from scratch
-- local ttemp =
-- {
-- ["modulation"] = 0,
-- ["tasks"] =
-- {
-- }, -- end of ["tasks"]
-- ["task"] = "Reconnaissance",
-- ["uncontrolled"] = false,
-- ["route"] =
-- {
-- ["points"] =
-- {
-- [1] =
-- {
-- ["alt"] = 2000,
-- ["action"] = "Turning Point",
-- ["alt_type"] = "BARO",
-- ["speed"] = 125,
-- ["task"] =
-- {
-- ["id"] = "ComboTask",
-- ["params"] =
-- {
-- ["tasks"] =
-- {
-- }, -- end of ["tasks"]
-- }, -- end of ["params"]
-- }, -- end of ["task"]
-- ["type"] = "Turning Point",
-- ["ETA"] = 0,
-- ["ETA_locked"] = true,
-- ["y"] = 666285.71428571,
-- ["x"] = -312000,
-- ["formation_template"] = "",
-- ["speed_locked"] = true,
-- }, -- end of [1]
-- }, -- end of ["points"]
-- }, -- end of ["route"]
-- ["groupId"] = 1,
-- ["hidden"] = false,
-- ["units"] =
-- {
-- [1] =
-- {
-- ["alt"] = 2000,
-- ["alt_type"] = "BARO",
-- ["livery_id"] = "USAF 364th FS",
-- ["skill"] = "High",
-- ["speed"] = 125,
-- ["type"] = "TF-51D",
-- ["unitId"] = 1,
-- ["psi"] = 0,
-- ["y"] = 666285.71428571,
-- ["x"] = -312000,
-- ["name"] = "P51-1-1",
-- ["payload"] =
-- {
-- ["pylons"] =
-- {
-- }, -- end of ["pylons"]
-- ["fuel"] = 340.68,
-- ["flare"] = 0,
-- ["chaff"] = 0,
-- ["gun"] = 100,
-- }, -- end of ["payload"]
-- ["heading"] = 0,
-- ["callsign"] =
-- {
-- [1] = 1,
-- [2] = 1,
-- ["name"] = "Enfield11",
-- [3] = 1,
-- }, -- end of ["callsign"]
-- ["onboard_num"] = "010",
-- }, -- end of [1]
-- }, -- end of ["units"]
-- ["y"] = 666285.71428571,
-- ["x"] = -312000,
-- ["name"] = "P51",
-- ["communication"] = true,
-- ["start_time"] = 0,
-- ["frequency"] = 124,
-- }
-- --
-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier.
-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" )
-- --
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix ) -- local mustang = SPAWN:NewFromTemplate(ttemp,"P51D")
-- -- you MUST set the next three:
-- mustang:InitCountry(country.id.FRANCE)
-- mustang:InitCategory(Group.Category.AIRPLANE)
-- mustang:InitCoalition(coalition.side.BLUE)
-- mustang:OnSpawnGroup(
-- function(grp)
-- MESSAGE:New("Group Spawned: "..grp:GetName(),15,"SPAWN"):ToAll()
-- end
-- )
-- mustang:Spawn()
--
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix, NoMooseNamingPostfix )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } )
if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then --if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then
BASE:I( "ERROR: in function NewFromTemplate, required paramter SpawnAliasPrefix is not set" ) --BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnAliasPrefix is not set" )
--return nil
--end
if SpawnTemplatePrefix == nil or SpawnTemplatePrefix == "" then
BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnTemplatePrefix is not set" )
return nil return nil
end end
if SpawnTemplate then if SpawnTemplate then
self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.SpawnTemplatePrefix = SpawnTemplatePrefix self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.SpawnAliasPrefix = SpawnAliasPrefix self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix
self.SpawnTemplate.name = SpawnTemplatePrefix
self.SpawnIndex = 0 self.SpawnIndex = 0
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.AliveUnits = 0 -- Contains the counter how many units are currently alive
@@ -435,6 +542,10 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
self.SpawnInitModex = nil self.SpawnInitModex = nil
self.SpawnInitAirbase = nil self.SpawnInitAirbase = nil
self.TweakedTemplate = true -- Check if the user is using self made template. self.TweakedTemplate = true -- Check if the user is using self made template.
self.MooseNameing = true
if NoMooseNamingPostfix == true then
self.MooseNameing = false
end
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else else
@@ -765,17 +876,15 @@ end
--- Randomizes the UNITs that are spawned within a radius band given an Outer and Inner radius. --- Randomizes the UNITs that are spawned within a radius band given an Outer and Inner radius.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #boolean RandomizeUnits If true, SPAWN will perform the randomization of the @{UNIT}s position within the group between a given outer and inner radius. -- @param #boolean RandomizeUnits If true, SPAWN will perform the randomization of the @{Wrapper.Unit#UNIT}s position within the group between a given outer and inner radius.
-- @param DCS#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. -- @param DCS#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned.
-- @param DCS#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. -- @param DCS#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned.
-- @return #SPAWN -- @return #SPAWN
-- @usage -- @usage
-- --
-- -- NATO helicopters engaging in the battle field. -- -- NATO helicopters engaging in the battle field.
-- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP). -- -- UNIT positions of this group will be randomized around the base unit #1 in a circle of 50 to 500 meters.
-- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeUnits( true, 500, 50 )
-- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters.
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 )
-- --
function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius ) function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } ) self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } )
@@ -791,6 +900,46 @@ function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
return self return self
end end
--- Spawn the UNITs of this group with individual relative positions to unit #1 and individual headings.
-- @param #SPAWN self
-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z
-- relative positions, and optionally an individual heading.
-- @return #SPAWN
-- @usage
--
-- -- NATO helicopter group of three units engaging in the battle field.
-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} }
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitRelativePositions(Positions)
--
function SPAWN:InitSetUnitRelativePositions(Positions)
self:F({self.SpawnTemplatePrefix, Positions})
self.SpawnUnitsWithRelativePositions = true
self.UnitsRelativePositions = Positions
return self
end
--- Spawn the UNITs of this group with individual absolute positions and individual headings.
-- @param #SPAWN self
-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z
-- absolute positions, and optionally an individual heading.
-- @return #SPAWN
-- @usage
--
-- -- NATO helicopter group of three units engaging in the battle field.
-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} }
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitAbsolutePositions(Positions)
--
function SPAWN:InitSetUnitAbsolutePositions(Positions)
self:F({self.SpawnTemplatePrefix, Positions})
self.SpawnUnitsWithAbsolutePositions = true
self.UnitsAbsolutePositions = Positions
return self
end
--- This method is rather complicated to understand. But I'll try to explain. --- This method is rather complicated to understand. But I'll try to explain.
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor, -- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
-- but they will all follow the same Template route and have the same prefix name. -- but they will all follow the same Template route and have the same prefix name.
@@ -814,7 +963,12 @@ end
function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable ) function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
self:F( { self.SpawnTemplatePrefix, SpawnTemplatePrefixTable } ) self:F( { self.SpawnTemplatePrefix, SpawnTemplatePrefixTable } )
self.SpawnTemplatePrefixTable = SpawnTemplatePrefixTable local temptable = {}
for _,_temp in pairs(SpawnTemplatePrefixTable) do
temptable[#temptable+1] = _temp
end
self.SpawnTemplatePrefixTable = UTILS.ShuffleTable(temptable)
self.SpawnRandomizeTemplate = true self.SpawnRandomizeTemplate = true
for SpawnGroupID = 1, self.SpawnMaxGroups do for SpawnGroupID = 1, self.SpawnMaxGroups do
@@ -848,15 +1002,11 @@ end
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
-- --
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3 function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet )
self:F( { self.SpawnTemplatePrefix } ) self:F( { self.SpawnTemplatePrefix } )
self.SpawnTemplatePrefixTable = SpawnTemplateSet:GetSetNames() local setnames = SpawnTemplateSet:GetSetNames()
self.SpawnRandomizeTemplate = true self:InitRandomizeTemplate(setnames)
for SpawnGroupID = 1, self.SpawnMaxGroups do
self:_RandomizeTemplate( SpawnGroupID )
end
return self return self
end end
@@ -906,8 +1056,8 @@ end
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types. --- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #table SpawnZoneTable A table with @{Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Zone}s objects. -- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
-- @return #SPAWN -- @return #SPAWN self
-- @usage -- @usage
-- --
-- -- Create a zone table of the 2 zones. -- -- Create a zone table of the 2 zones.
@@ -922,7 +1072,12 @@ end
function SPAWN:InitRandomizeZones( SpawnZoneTable ) function SPAWN:InitRandomizeZones( SpawnZoneTable )
self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } ) self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } )
self.SpawnZoneTable = SpawnZoneTable local temptable = {}
for _,_temp in pairs(SpawnZoneTable) do
temptable[#temptable+1] = _temp
end
self.SpawnZoneTable = UTILS.ShuffleTable(temptable)
self.SpawnRandomizeZones = true self.SpawnRandomizeZones = true
for SpawnGroupID = 1, self.SpawnMaxGroups do for SpawnGroupID = 1, self.SpawnMaxGroups do
@@ -932,6 +1087,31 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable )
return self return self
end end
--- This method sets a spawn position for the group that is different from the location of the template.
-- @param #SPAWN self
-- @param Core.Point#COORDINATE Coordinate The position to spawn from
-- @return #SPAWN self
function SPAWN:InitPositionCoordinate(Coordinate)
self:T( { self.SpawnTemplatePrefix, Coordinate:GetVec2()} )
self:InitPositionVec2(Coordinate:GetVec2())
return self
end
--- This method sets a spawn position for the group that is different from the location of the template.
-- @param #SPAWN self
-- @param DCS#Vec2 Vec2 The position to spawn from
-- @return #SPAWN self
function SPAWN:InitPositionVec2(Vec2)
self:T( { self.SpawnTemplatePrefix, Vec2} )
self.SpawnInitPosition = Vec2
self.SpawnFromNewPosition = true
self:I("MaxGroups:"..self.SpawnMaxGroups)
for SpawnGroupID = 1, self.SpawnMaxGroups do
self:_SetInitialPosition( SpawnGroupID )
end
return self
end
--- For planes and helicopters, when these groups go home and land on their home airbases and FARPs, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment. --- For planes and helicopters, when these groups go home and land on their home airbases and FARPs, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment.
-- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed. -- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed.
-- This will enable a spawned group to be re-spawned after it lands, until it is destroyed... -- This will enable a spawned group to be re-spawned after it lands, until it is destroyed...
@@ -1140,7 +1320,8 @@ do -- Delay methods
return self return self
end end
--- Turns the Delay On for the @{Wrapper.Group} when spawning. --- Turns the Delay On for the @{Wrapper.Group} when spawning with @{SpawnScheduled}(). In effect then the 1st group will only be spawned
-- after the number of seconds given in SpawnScheduled as arguments, and not immediately.
-- @param #SPAWN self -- @param #SPAWN self
-- @return #SPAWN The SPAWN object -- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOn() function SPAWN:InitDelayOn()
@@ -1159,7 +1340,7 @@ do -- Delay methods
end -- Delay methods end -- Delay methods
--- Will spawn a group based on the internal index. --- Will spawn a group based on the internal index.
-- Note: Uses @{DATABASE} module defined in MOOSE. -- Note: This method uses the global _DATABASE object (an instance of @{Core.Database#DATABASE}), which contains ALL initial and new spawned objects in MOOSE.
-- @param #SPAWN self -- @param #SPAWN self
-- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions.
function SPAWN:Spawn() function SPAWN:Spawn()
@@ -1174,7 +1355,7 @@ function SPAWN:Spawn()
end end
--- Will re-spawn a group based on a given index. --- Will re-spawn a group based on a given index.
-- Note: Uses @{DATABASE} module defined in MOOSE. -- Note: This method uses the global _DATABASE object (an instance of @{Core.Database#DATABASE}), which contains ALL initial and new spawned objects in MOOSE.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnIndex The index of the group to be spawned. -- @param #string SpawnIndex The index of the group to be spawned.
-- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions.
@@ -1222,7 +1403,7 @@ function SPAWN:SetSpawnIndex( SpawnIndex )
end end
--- Will spawn a group with a specified index number. --- Will spawn a group with a specified index number.
-- Uses @{DATABASE} global object defined in MOOSE. -- Note: This method uses the global _DATABASE object (an instance of @{Core.Database#DATABASE}), which contains ALL initial and new spawned objects in MOOSE.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnIndex The index of the group to be spawned. -- @param #string SpawnIndex The index of the group to be spawned.
-- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions.
@@ -1231,6 +1412,11 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
if self:_GetSpawnIndex( SpawnIndex ) then if self:_GetSpawnIndex( SpawnIndex ) then
if self.SpawnFromNewPosition then
self:_SetInitialPosition( SpawnIndex )
end
if self.SpawnGroups[self.SpawnIndex].Visible then if self.SpawnGroups[self.SpawnIndex].Visible then
self.SpawnGroups[self.SpawnIndex].Group:Activate() self.SpawnGroups[self.SpawnIndex].Group:Activate()
else else
@@ -1337,6 +1523,37 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
end end
end end
-- Individual relative unit positions + heading
if self.SpawnUnitsWithRelativePositions and self.UnitsRelativePositions then
local BaseX = SpawnTemplate.units[1].x or 0
local BaseY = SpawnTemplate.units[1].y or 0
local BaseZ = SpawnTemplate.units[1].z or 0
for UnitID = 1, #SpawnTemplate.units do
if self.UnitsRelativePositions[UnitID].heading then
SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsRelativePositions[UnitID].heading or 0)
end
SpawnTemplate.units[UnitID].x = BaseX + (self.UnitsRelativePositions[UnitID].x or 0)
SpawnTemplate.units[UnitID].y = BaseY + (self.UnitsRelativePositions[UnitID].y or 0)
if self.UnitsRelativePositions[UnitID].z then
SpawnTemplate.units[UnitID].z = BaseZ + (self.UnitsRelativePositions[UnitID].z or 0)
end
end
end
-- Individual asbolute unit positions + heading
if self.SpawnUnitsWithAbsolutePositions and self.UnitsAbsolutePositions then
for UnitID = 1, #SpawnTemplate.units do
if self.UnitsAbsolutePositions[UnitID].heading then
SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsAbsolutePositions[UnitID].heading or 0)
end
SpawnTemplate.units[UnitID].x = self.UnitsAbsolutePositions[UnitID].x or 0
SpawnTemplate.units[UnitID].y = self.UnitsAbsolutePositions[UnitID].y or 0
if self.UnitsAbsolutePositions[UnitID].z then
SpawnTemplate.units[UnitID].z = self.UnitsAbsolutePositions[UnitID].z or 0
end
end
end
-- Set livery. -- Set livery.
if self.SpawnInitLivery then if self.SpawnInitLivery then
for UnitID = 1, #SpawnTemplate.units do for UnitID = 1, #SpawnTemplate.units do
@@ -1437,6 +1654,8 @@ end
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups. -- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn. -- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval. -- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
-- @param #boolean WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below.
-- Effectively the same as @{InitDelayOn}().
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
-- -- NATO helicopters engaging in the battle field. -- -- NATO helicopters engaging in the battle field.
@@ -1447,12 +1666,15 @@ end
-- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750 -- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750
-- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters. -- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters.
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 ) -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 )
function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation ) function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation, WithDelay )
self:F( { SpawnTime, SpawnTimeVariation } ) self:F( { SpawnTime, SpawnTimeVariation } )
local SpawnTime = SpawnTime or 60
local SpawnTimeVariation = SpawnTimeVariation or 0.5
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
local InitialDelay = 0 local InitialDelay = 0
if self.DelayOnOff == true then if WithDelay or self.DelayOnOff == true then
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation ) InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
end end
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation ) self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
@@ -2403,8 +2625,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param DCS#Vec3 Vec3 The Vec3 coordinates where to spawn the group. -- @param DCS#Vec3 Vec3 The Vec3 coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } ) self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } )
@@ -2472,8 +2693,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group. -- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex ) function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
@@ -2487,8 +2707,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group. -- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground. -- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground.
@@ -2511,8 +2730,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2() -- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2()
@@ -2544,8 +2762,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2() -- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2()
@@ -2599,8 +2816,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnStatic = STATIC:FindByName( StaticName ) -- local SpawnStatic = STATIC:FindByName( StaticName )
@@ -2621,8 +2837,8 @@ function SPAWN:SpawnFromStatic( HostStatic, MinHeight, MaxHeight, SpawnIndex )
return nil return nil
end end
--- Will spawn a Group within a given @{Zone}. --- Will spawn a Group within a given @{Core.Zone}.
-- The @{Zone} can be of any type derived from @{Core.Zone#ZONE_BASE}. -- The @{Core.Zone} can be of any type derived from @{Core.Zone#ZONE_BASE}.
-- Once the @{Wrapper.Group} is spawned within the zone, the @{Wrapper.Group} will continue on its route. -- Once the @{Wrapper.Group} is spawned within the zone, the @{Wrapper.Group} will continue on its route.
-- The **first waypoint** (where the group is spawned) is replaced with the zone location coordinates. -- The **first waypoint** (where the group is spawned) is replaced with the zone location coordinates.
-- @param #SPAWN self -- @param #SPAWN self
@@ -2631,8 +2847,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil when nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnZone = ZONE:New( ZoneName ) -- local SpawnZone = ZONE:New( ZoneName )
@@ -2827,18 +3042,37 @@ end
-- The method will search for a #-mark, and will return the text before the #-mark. -- The method will search for a #-mark, and will return the text before the #-mark.
-- It will return nil of no prefix was found. -- It will return nil of no prefix was found.
-- @param #SPAWN self -- @param #SPAWN self
-- @param DCS#UNIT DCSUnit The @{DCSUnit} to be searched. -- @param Wrapper.Group#GROUP SpawnGroup The GROUP object.
-- @return #string The prefix -- @return #string The prefix or #nil if nothing was found.
-- @return #nil Nothing found
function SPAWN:_GetPrefixFromGroup( SpawnGroup ) function SPAWN:_GetPrefixFromGroup( SpawnGroup )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
local GroupName = SpawnGroup:GetName() local GroupName = SpawnGroup:GetName()
if GroupName then if GroupName then
local SpawnPrefix = string.match( GroupName, ".*#" )
local SpawnPrefix=self:_GetPrefixFromGroupName(GroupName)
return SpawnPrefix
end
return nil
end
--- Return the prefix of a spawned group.
-- The method will search for a `#`-mark, and will return the text before the `#`-mark. It will return nil of no prefix was found.
-- @param #SPAWN self
-- @param #string SpawnGroupName The name of the spawned group.
-- @return #string The prefix or #nil if nothing was found.
function SPAWN:_GetPrefixFromGroupName(SpawnGroupName)
if SpawnGroupName then
local SpawnPrefix=string.match(SpawnGroupName, ".*#")
if SpawnPrefix then if SpawnPrefix then
SpawnPrefix = SpawnPrefix:sub(1, -2) SpawnPrefix = SpawnPrefix:sub(1, -2)
end end
return SpawnPrefix return SpawnPrefix
end end
@@ -2925,7 +3159,7 @@ function SPAWN:_GetGroupCountryID( SpawnPrefix )
end end
--- Gets the Group Template from the ME environment definition. --- Gets the Group Template from the ME environment definition.
-- This method used the @{DATABASE} object, which contains ALL initial and new spawned object in MOOSE. -- Note: This method uses the global _DATABASE object (an instance of @{Core.Database#DATABASE}), which contains ALL initial and new spawned objects in MOOSE.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnTemplatePrefix -- @param #string SpawnTemplatePrefix
-- @return @SPAWN self -- @return @SPAWN self
@@ -2934,6 +3168,10 @@ function SPAWN:_GetTemplate( SpawnTemplatePrefix )
local SpawnTemplate = nil local SpawnTemplate = nil
if _DATABASE.Templates.Groups[SpawnTemplatePrefix] == nil then
error( 'No Template exists for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix )
end
local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template
self:F( { Template = Template } ) self:F( { Template = Template } )
@@ -2967,6 +3205,11 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then
BASE:I( "WARNING: You are using a tweaked template." ) BASE:I( "WARNING: You are using a tweaked template." )
SpawnTemplate = self.SpawnTemplate SpawnTemplate = self.SpawnTemplate
if self.MooseNameing == true then
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
else
SpawnTemplate.name = self:SpawnGroupName()
end
else else
SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
@@ -3020,6 +3263,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
if type( Callsign ) ~= "number" then -- blue callsign if type( Callsign ) ~= "number" then -- blue callsign
Callsign[2] = ((SpawnIndex - 1) % 10) + 1 Callsign[2] = ((SpawnIndex - 1) % 10) + 1
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers
local CallsignLen = CallsignName:len() local CallsignLen = CallsignName:len()
SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
else else
@@ -3096,7 +3340,58 @@ function SPAWN:_RandomizeTemplate( SpawnIndex )
return self return self
end end
--- Private method that randomizes the @{Zone}s where the Group will be spawned. --- Private method that sets the DCS#Vec2 where the Group will be spawned.
-- @param #SPAWN self
-- @param #number SpawnIndex
-- @return #SPAWN self
function SPAWN:_SetInitialPosition( SpawnIndex )
self:T( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } )
if self.SpawnFromNewPosition then
self:T( "Preparing Spawn at Vec2 ", self.SpawnInitPosition )
local SpawnVec2 = self.SpawnInitPosition
self:T( { SpawnVec2 = SpawnVec2 } )
local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate
SpawnTemplate.route = SpawnTemplate.route or {}
SpawnTemplate.route.points = SpawnTemplate.route.points or {}
SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {}
SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0
SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0
self:T( { Route = SpawnTemplate.route } )
for UnitID = 1, #SpawnTemplate.units do
local UnitTemplate = SpawnTemplate.units[UnitID]
self:T( 'Before Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. UnitTemplate.y )
local SX = UnitTemplate.x
local SY = UnitTemplate.y
local BX = SpawnTemplate.route.points[1].x
local BY = SpawnTemplate.route.points[1].y
local TX = SpawnVec2.x + (SX - BX)
local TY = SpawnVec2.y + (SY - BY)
UnitTemplate.x = TX
UnitTemplate.y = TY
-- TODO: Manage altitude based on landheight...
-- SpawnTemplate.units[UnitID].alt = SpawnVec2:
self:T( 'After Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. UnitTemplate.y )
end
SpawnTemplate.route.points[1].x = SpawnVec2.x
SpawnTemplate.route.points[1].y = SpawnVec2.y
SpawnTemplate.x = SpawnVec2.x
SpawnTemplate.y = SpawnVec2.y
end
return self
end
--- Private method that randomizes the @{Core.Zone}s where the Group will be spawned.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number SpawnIndex -- @param #number SpawnIndex
-- @return #SPAWN self -- @return #SPAWN self
@@ -3215,7 +3510,7 @@ end
-- TODO Need to delete this... _DATABASE does this now ... -- TODO Need to delete this... _DATABASE does this now ...
--- @param #SPAWN self -- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnBirth( EventData ) function SPAWN:_OnBirth( EventData )
self:F( self.SpawnTemplatePrefix ) self:F( self.SpawnTemplatePrefix )
@@ -3235,24 +3530,27 @@ function SPAWN:_OnBirth( EventData )
end end
--- Obscolete -- @param #SPAWN self
-- @todo Need to delete this... _DATABASE does this now ...
--- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnDeadOrCrash( EventData ) function SPAWN:_OnDeadOrCrash( EventData )
self:F( self.SpawnTemplatePrefix ) self:F( self.SpawnTemplatePrefix )
local SpawnGroup = EventData.IniGroup local unit=UNIT:FindByName(EventData.IniUnitName)
if unit then
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "Dead event: " .. EventPrefix } ) self:T( { "Dead event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self.AliveUnits = self.AliveUnits - 1 self.AliveUnits = self.AliveUnits - 1
self:T( "Alive Units: " .. self.AliveUnits ) self:T( "Alive Units: " .. self.AliveUnits )
end end
end end
end end
end end
@@ -3354,8 +3652,12 @@ function SPAWN:_SpawnCleanUpScheduler()
local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup()
self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } )
local IsHelo = false
while SpawnGroup do while SpawnGroup do
IsHelo = SpawnGroup:IsHelicopter()
local SpawnUnits = SpawnGroup:GetUnits() local SpawnUnits = SpawnGroup:GetUnits()
for UnitID, UnitData in pairs( SpawnUnits ) do for UnitID, UnitData in pairs( SpawnUnits ) do
@@ -3368,8 +3670,8 @@ function SPAWN:_SpawnCleanUpScheduler()
self:T( { SpawnUnitName, Stamp } ) self:T( { SpawnUnitName, Stamp } )
if Stamp.Vec2 then if Stamp.Vec2 then
if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then if (SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1) or IsHelo then
local NewVec2 = SpawnUnit:GetVec2() local NewVec2 = SpawnUnit:GetVec2() or {x=0, y=0}
if (Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y) or (SpawnUnit:GetLife() <= 1) then if (Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y) or (SpawnUnit:GetLife() <= 1) then
-- If the plane is not moving or dead , and is on the ground, assign it with a timestamp... -- If the plane is not moving or dead , and is on the ground, assign it with a timestamp...
if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then
@@ -3387,8 +3689,8 @@ function SPAWN:_SpawnCleanUpScheduler()
Stamp.Time = nil Stamp.Time = nil
end end
else else
if SpawnUnit:InAir() == false then if SpawnUnit:InAir() == false or (IsHelo and SpawnUnit:GetLife() <= 1) then
Stamp.Vec2 = SpawnUnit:GetVec2() Stamp.Vec2 = SpawnUnit:GetVec2() or {x=0, y=0}
if (SpawnUnit:GetVelocityKMH() < 1) then if (SpawnUnit:GetVelocityKMH() < 1) then
Stamp.Time = timer.getTime() Stamp.Time = timer.getTime()
end end

View File

@@ -46,26 +46,26 @@
-- @field #number InitOffsetAngle Link offset angle in degrees. -- @field #number InitOffsetAngle Link offset angle in degrees.
-- @field #number InitStaticHeading Heading of the static. -- @field #number InitStaticHeading Heading of the static.
-- @field #string InitStaticLivery Livery for aircraft. -- @field #string InitStaticLivery Livery for aircraft.
-- @field #string InitStaticShape Shape of teh static. -- @field #string InitStaticShape Shape of the static.
-- @field #string InitStaticType Type of the static. -- @field #string InitStaticType Type of the static.
-- @field #string InitStaticCategory Categrory of the static. -- @field #string InitStaticCategory Categrory of the static.
-- @field #string InitStaticName Name of the static. -- @field #string InitStaticName Name of the static.
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static. -- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
-- @field #boolean InitDead Set static to be dead if true. -- @field #boolean InitStaticDead Set static to be dead if true.
-- @field #boolean InitCargo If true, static can act as cargo. -- @field #boolean InitStaticCargo If true, static can act as cargo.
-- @field #number InitCargoMass Mass of cargo in kg. -- @field #number InitStaticCargoMass Mass of cargo in kg.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Allows to spawn dynamically new @{Static}s into your mission. --- Allows to spawn dynamically new @{Wrapper.Static}s into your mission.
-- --
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), -- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc),
-- and "copy" these properties to create a new static object and place it at the desired coordinate. -- and "copy" these properties to create a new static object and place it at the desired coordinate.
-- --
-- New spawned @{Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method. -- New spawned @{Wrapper.Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
-- By default, spawned @{Static}s will follow a naming convention at run-time: -- By default, spawned @{Wrapper.Static}s will follow a naming convention at run-time:
-- --
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, and _nnn_ is a **counter from 0 to 99999**. -- * Spawned @{Wrapper.Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, and _nnn_ is a **counter from 0 to 99999**.
-- --
-- # SPAWNSTATIC Constructors -- # SPAWNSTATIC Constructors
-- --
@@ -106,7 +106,7 @@
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**! -- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**! -- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**! -- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Zone}. Optionally, heading and name can be given. The name **must be unique**! -- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Core.Zone}. Optionally, heading and name can be given. The name **must be unique**!
-- --
-- @field #SPAWNSTATIC SPAWNSTATIC -- @field #SPAWNSTATIC SPAWNSTATIC
-- --
@@ -131,7 +131,7 @@ SPAWNSTATIC = {
-- @field #number mass Cargo mass in kg. -- @field #number mass Cargo mass in kg.
-- @field #boolean canCargo Static can be a cargo. -- @field #boolean canCargo Static can be a cargo.
--- Creates the main object to spawn a @{Static} defined in the mission editor (ME). --- Creates the main object to spawn a @{Wrapper.Static} defined in the mission editor (ME).
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param #string SpawnTemplateName Name of the static object in the ME. Each new static will have the name starting with this prefix. -- @param #string SpawnTemplateName Name of the static object in the ME. Each new static will have the name starting with this prefix.
-- @param DCS#country.id SpawnCountryID (Optional) The ID of the country. -- @param DCS#country.id SpawnCountryID (Optional) The ID of the country.
@@ -158,7 +158,7 @@ function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
return self return self
end end
--- Creates the main object to spawn a @{Static} given a template table. --- Creates the main object to spawn a @{Wrapper.Static} given a template table.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param #table SpawnTemplate Template used for spawning. -- @param #table SpawnTemplate Template used for spawning.
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`. -- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
@@ -174,7 +174,7 @@ function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
return self return self
end end
--- Creates the main object to spawn a @{Static} from a given type. --- Creates the main object to spawn a @{Wrapper.Static} from a given type.
-- NOTE that you have to init many other parameters as spawn coordinate etc. -- NOTE that you have to init many other parameters as spawn coordinate etc.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param #string StaticType Type of the static. -- @param #string StaticType Type of the static.
@@ -260,7 +260,7 @@ end
-- @param #number Mass Mass of the cargo in kg. -- @param #number Mass Mass of the cargo in kg.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargoMass(Mass) function SPAWNSTATIC:InitCargoMass(Mass)
self.InitCargoMass=Mass self.InitStaticCargoMass=Mass
return self return self
end end
@@ -269,7 +269,16 @@ end
-- @param #boolean IsCargo If true, this static can act as cargo. -- @param #boolean IsCargo If true, this static can act as cargo.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargo(IsCargo) function SPAWNSTATIC:InitCargo(IsCargo)
self.InitCargo=IsCargo self.InitStaticCargo=IsCargo
return self
end
--- Initialize as dead.
-- @param #SPAWNSTATIC self
-- @param #boolean IsDead If true, this static is dead.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitDead(IsDead)
self.InitStaticDead=IsDead
return self return self
end end
@@ -327,7 +336,7 @@ function SPAWNSTATIC:Spawn(Heading, NewName)
end end
--- Creates a new @{Static} from a POINT_VEC2. --- Creates a new @{Wrapper.Static} from a POINT_VEC2.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static. -- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. -- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
@@ -343,7 +352,7 @@ function SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, NewName)
end end
--- Creates a new @{Static} from a COORDINATE. --- Creates a new @{Wrapper.Static} from a COORDINATE.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static. -- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
-- @param #number Heading (Optional) Heading The heading of the static in degrees. Default is 0 degrees. -- @param #number Heading (Optional) Heading The heading of the static in degrees. Default is 0 degrees.
@@ -366,7 +375,7 @@ function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName)
end end
--- Creates a new @{Static} from a @{Zone}. --- Creates a new @{Wrapper.Static} from a @{Core.Zone}.
-- @param #SPAWNSTATIC self -- @param #SPAWNSTATIC self
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static. -- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
-- @param #number Heading (Optional)The heading of the static in degrees. Default is the heading of the template. -- @param #number Heading (Optional)The heading of the static in degrees. Default is the heading of the template.
@@ -417,16 +426,16 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template.livery_id=self.InitStaticLivery Template.livery_id=self.InitStaticLivery
end end
if self.InitDead~=nil then if self.InitStaticDead~=nil then
Template.dead=self.InitDead Template.dead=self.InitStaticDead
end end
if self.InitCargo~=nil then if self.InitStaticCargo~=nil then
Template.canCargo=self.InitCargo Template.canCargo=self.InitStaticCargo
end end
if self.InitCargoMass~=nil then if self.InitStaticCargoMass~=nil then
Template.mass=self.InitCargoMass Template.mass=self.InitStaticCargoMass
end end
if self.InitLinkUnit then if self.InitLinkUnit then
@@ -458,7 +467,7 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
self:T(Template) self:T(Template)
-- Add static to the game. -- Add static to the game.
local Static=nil local Static=nil --DCS#StaticObject
if self.InitFarp then if self.InitFarp then
@@ -478,7 +487,20 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- ED's dirty way to spawn FARPS. -- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup) Static=coalition.addGroup(CountryID, -1, TemplateGroup)
-- Currently DCS 2.8 does not trigger birth events if FAPRS are spawned!
-- We create such an event. The airbase is registered in Core.Event
local Event = {
id = EVENTS.Birth,
time = timer.getTime(),
initiator = Static
}
-- Create BIRTH event.
world.onEvent(Event)
else else
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template) Static=coalition.addStaticObject(CountryID, Template)
end end

View File

@@ -5,7 +5,7 @@
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
-- --
-- * Spot for a defined duration. -- * Spot for a defined duration.
-- * Updates of laer spot position every 0.2 seconds for moving targets. -- * Updates of laser spot position every 0.2 seconds for moving targets.
-- * Wiggle the spot at the target. -- * Wiggle the spot at the target.
-- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Provide a @{Wrapper.Unit} as a target, instead of a point.
-- * Implement a status machine, LaseOn, LaseOff. -- * Implement a status machine, LaseOn, LaseOff.
@@ -14,17 +14,7 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [SPOT Demo Missions source code]() -- ### [Demo Missions on GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
--
-- ### [SPOT Demo Missions, only for beta testers]()
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ===
--
-- # YouTube Channel
--
-- ### [SPOT YouTube Channel]()
-- --
-- === -- ===
-- --
@@ -43,21 +33,22 @@
do do
--- @type SPOT ---
-- @type SPOT
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to: --- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
-- --
-- * Mark targets for a defined duration. -- * Mark targets for a defined duration.
-- * Updates of laer spot position every 0.2 seconds for moving targets. -- * Updates of laser spot position every 0.25 seconds for moving targets.
-- * Wiggle the spot at the target. -- * Wiggle the spot at the target.
-- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Provide a @{Wrapper.Unit} as a target, instead of a point.
-- * Implement a status machine, LaseOn, LaseOff. -- * Implement a status machine, LaseOn, LaseOff.
-- --
-- ## 1. SPOT constructor -- ## 1. SPOT constructor
-- --
-- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object. -- * @{#SPOT.New}(): Creates a new SPOT object.
-- --
-- ## 2. SPOT is a FSM -- ## 2. SPOT is a FSM
-- --
@@ -218,6 +209,8 @@ do
self.Recce = Recce self.Recce = Recce
self.RecceName = self.Recce:GetName()
self.LaseScheduler = SCHEDULER:New( self ) self.LaseScheduler = SCHEDULER:New( self )
self:SetEventPriority( 5 ) self:SetEventPriority( 5 )
@@ -236,21 +229,27 @@ do
-- @param #number LaserCode Laser code. -- @param #number LaserCode Laser code.
-- @param #number Duration Duration of lasing in seconds. -- @param #number Duration Duration of lasing in seconds.
function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration ) function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration )
self:F( { "LaseOn", Target, LaserCode, Duration } ) self:T({From, Event, To})
self:T2( { "LaseOn", Target, LaserCode, Duration } )
local function StopLase( self ) local function StopLase( self )
self:LaseOff() self:LaseOff()
end end
self.Target = Target self.Target = Target
self.TargetName = Target:GetName()
self.LaserCode = LaserCode self.LaserCode = LaserCode
self.Lasing = true self.Lasing = true
local RecceDcsUnit = self.Recce:GetDCSObject() local RecceDcsUnit = self.Recce:GetDCSObject()
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3() ) local relativespot = self.relstartpos or { x = 0, y = 2, z = 0 }
self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3(), LaserCode )
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, relativespot, Target:GetPointVec3():AddY(1):GetVec3() )
self.SpotLaser = Spot.createLaser( RecceDcsUnit, relativespot, Target:GetPointVec3():AddY(1):GetVec3(), LaserCode )
if Duration then if Duration then
self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration ) self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration )
@@ -259,6 +258,8 @@ do
self:HandleEvent( EVENTS.Dead ) self:HandleEvent( EVENTS.Dead )
self:__Lasing( -1 ) self:__Lasing( -1 )
return self
end end
@@ -271,7 +272,7 @@ do
-- @param #number LaserCode Laser code. -- @param #number LaserCode Laser code.
-- @param #number Duration Duration of lasing in seconds. -- @param #number Duration Duration of lasing in seconds.
function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration) function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration)
self:F( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } ) self:T2( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } )
local function StopLase( self ) local function StopLase( self )
self:LaseOff() self:LaseOff()
@@ -293,31 +294,45 @@ do
end end
self:__Lasing(-1) self:__Lasing(-1)
return self
end end
--- @param #SPOT self ---
-- @param #SPOT self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SPOT:OnEventDead(EventData) function SPOT:OnEventDead(EventData)
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) self:T2( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
if self.Target then if self.Target then
if EventData.IniDCSUnitName == self.Target:GetName() then if EventData.IniDCSUnitName == self.TargetName then
self:F( {"Target dead ", self.Target:GetName() } ) self:F( {"Target dead ", self.TargetName } )
self:Destroyed() self:Destroyed()
self:LaseOff() self:LaseOff()
end end
end end
if self.Recce then
if EventData.IniDCSUnitName == self.RecceName then
self:F( {"Recce dead ", self.RecceName } )
self:LaseOff()
end
end
return self
end end
--- @param #SPOT self ---
-- @param #SPOT self
-- @param From -- @param From
-- @param Event -- @param Event
-- @param To -- @param To
function SPOT:onafterLasing( From, Event, To ) function SPOT:onafterLasing( From, Event, To )
self:T({From, Event, To})
if self.Lasing then
if self.Target and self.Target:IsAlive() then if self.Target and self.Target:IsAlive() then
self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() ) self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() )
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() ) self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
self:__Lasing( -0.2 )
self:__Lasing(0.2)
elseif self.TargetCoord then elseif self.TargetCoord then
-- Wiggle the IR spot a bit. -- Wiggle the IR spot a bit.
@@ -327,21 +342,24 @@ do
self.SpotIR:setPoint(irvec3) self.SpotIR:setPoint(irvec3)
self.SpotLaser:setPoint(lsvec3) self.SpotLaser:setPoint(lsvec3)
self:__Lasing(-0.25) self:__Lasing(0.2)
else else
self:F( { "Target is not alive", self.Target:IsAlive() } ) self:F( { "Target is not alive", self.Target:IsAlive() } )
end end
end
return self
end end
--- @param #SPOT self ---
-- @param #SPOT self
-- @param From -- @param From
-- @param Event -- @param Event
-- @param To -- @param To
-- @return #SPOT -- @return #SPOT
function SPOT:onafterLaseOff( From, Event, To ) function SPOT:onafterLaseOff( From, Event, To )
self:T({From, Event, To})
self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } ) self:T2( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
self.Lasing = false self.Lasing = false
@@ -368,4 +386,16 @@ do
return self.Lasing return self.Lasing
end end
--- Set laser start position relative to the lasing unit.
-- @param #SPOT self
-- @param #table position Start position of the laser relative to the lasing unit. Default is { x = 0, y = 2, z = 0 }
-- @return #SPOT self
-- @usage
-- -- Set lasing position to be the position of the optics of the Gazelle M:
-- myspot:SetRelativeStartPosition({ x = 1.7, y = 1.2, z = 0 })
function SPOT:SetRelativeStartPosition(position)
self.relstartpos = position or { x = 0, y = 2, z = 0 }
return self
end
end end

View File

@@ -0,0 +1,202 @@
--- **Core** - A Moose GetText system.
--
-- ===
--
-- ## Main Features:
--
-- * A GetText for Moose
-- * Build a set of localized text entries, alongside their sounds and subtitles
-- * Aimed at class developers to offer localizable language support
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/).
--
-- ===
--
-- ### Author: **applevangelist**
-- ## Date: April 2022
--
-- ===
--
-- @module Core.TextAndSound
-- @image MOOSE.JPG
--- Text and Sound class.
-- @type TEXTANDSOUND
-- @field #string ClassName Name of this class.
-- @field #string version Versioning.
-- @field #string lid LID for log entries.
-- @field #string locale Default locale of this object.
-- @field #table entries Table of entries.
-- @field #string textclass Name of the class the texts belong to.
-- @extends Core.Base#BASE
---
--
-- @field #TEXTANDSOUND
TEXTANDSOUND = {
ClassName = "TEXTANDSOUND",
version = "0.0.1",
lid = "",
locale = "en",
entries = {},
textclass = "",
}
--- Text and Sound entry.
-- @type TEXTANDSOUND.Entry
-- @field #string Classname Name of the class this entry is for.
-- @field #string Locale Locale of this entry, defaults to "en".
-- @field #table Data The list of entries.
--- Text and Sound data
-- @type TEXTANDSOUND.Data
-- @field #string ID ID of this entry for retrieval.
-- @field #string Text Text of this entry.
-- @field #string Soundfile (optional) Soundfile File name of the corresponding sound file.
-- @field #number Soundlength (optional) Length of the sound file in seconds.
-- @field #string Subtitle (optional) Subtitle for the sound file.
--- Instantiate a new object
-- @param #TEXTANDSOUND self
-- @param #string ClassName Name of the class this instance is providing texts for.
-- @param #string Defaultlocale (Optional) Default locale of this instance, defaults to "en".
-- @return #TEXTANDSOUND self
function TEXTANDSOUND:New(ClassName,Defaultlocale)
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", self.ClassName, self.version)
self.locale = Defaultlocale or (_SETTINGS:GetLocale() or "en")
self.textclass = ClassName or "none"
self.entries = {}
local initentry = {} -- #TEXTANDSOUND.Entry
initentry.Classname = ClassName
initentry.Data = {}
initentry.Locale = self.locale
self.entries[self.locale] = initentry
self:I(self.lid .. "Instantiated.")
self:T({self.entries[self.locale]})
return self
end
--- Add an entry
-- @param #TEXTANDSOUND self
-- @param #string Locale Locale to set for this entry, e.g. "de".
-- @param #string ID Unique(!) ID of this entry under this locale (i.e. use the same ID to get localized text for the entry in another language).
-- @param #string Text Text for this entry.
-- @param #string Soundfile (Optional) Sound file name for this entry.
-- @param #number Soundlength (Optional) Length of the sound file in seconds.
-- @param #string Subtitle (Optional) Subtitle to be used alongside the sound file.
-- @return #TEXTANDSOUND self
function TEXTANDSOUND:AddEntry(Locale,ID,Text,Soundfile,Soundlength,Subtitle)
self:T(self.lid .. "AddEntry")
local locale = Locale or self.locale
local dataentry = {} -- #TEXTANDSOUND.Data
dataentry.ID = ID or "1"
dataentry.Text = Text or "none"
dataentry.Soundfile = Soundfile
dataentry.Soundlength = Soundlength or 0
dataentry.Subtitle = Subtitle
if not self.entries[locale] then
local initentry = {} -- #TEXTANDSOUND.Entry
initentry.Classname = self.textclass -- class name entry
initentry.Data = {} -- data array
initentry.Locale = locale -- default locale
self.entries[locale] = initentry
end
self.entries[locale].Data[ID] = dataentry
self:T({self.entries[locale].Data})
return self
end
--- Get an entry
-- @param #TEXTANDSOUND self
-- @param #string ID The unique ID of the data to be retrieved.
-- @param #string Locale (Optional) The locale of the text to be retrieved - defauls to default locale set with `New()`.
-- @return #string Text Text or nil if not found and no fallback.
-- @return #string Soundfile Filename or nil if not found and no fallback.
-- @return #string Soundlength Length of the sound or 0 if not found and no fallback.
-- @return #string Subtitle Text for subtitle or nil if not found and no fallback.
function TEXTANDSOUND:GetEntry(ID,Locale)
self:T(self.lid .. "GetEntry")
local locale = Locale or self.locale
if not self.entries[locale] then
-- fall back to default "en"
locale = self.locale
end
local Text,Soundfile,Soundlength,Subtitle = nil, nil, 0, nil
if self.entries[locale] then
if self.entries[locale].Data then
local data = self.entries[locale].Data[ID] -- #TEXTANDSOUND.Data
if data then
Text = data.Text
Soundfile = data.Soundfile
Soundlength = data.Soundlength
Subtitle = data.Subtitle
elseif self.entries[self.locale].Data[ID] then
-- no matching entry, try default
local data = self.entries[self.locale].Data[ID]
Text = data.Text
Soundfile = data.Soundfile
Soundlength = data.Soundlength
Subtitle = data.Subtitle
end
end
else
return nil, nil, 0, nil
end
return Text,Soundfile,Soundlength,Subtitle
end
--- Get the default locale of this object
-- @param #TEXTANDSOUND self
-- @return #string locale
function TEXTANDSOUND:GetDefaultLocale()
self:T(self.lid .. "GetDefaultLocale")
return self.locale
end
--- Set default locale of this object
-- @param #TEXTANDSOUND self
-- @param #string locale
-- @return #TEXTANDSOUND self
function TEXTANDSOUND:SetDefaultLocale(locale)
self:T(self.lid .. "SetDefaultLocale")
self.locale = locale or "en"
return self
end
--- Check if a locale exists
-- @param #TEXTANDSOUND self
-- @return #boolean outcome
function TEXTANDSOUND:HasLocale(Locale)
self:T(self.lid .. "HasLocale")
return self.entries[Locale] and true or false
end
--- Flush all entries to the log
-- @param #TEXTANDSOUND self
-- @return #TEXTANDSOUND self
function TEXTANDSOUND:FlushToLog()
self:I(self.lid .. "Flushing entries:")
local text = string.format("Textclass: %s | Default Locale: %s",self.textclass, self.locale)
for _,_entry in pairs(self.entries) do
local entry = _entry -- #TEXTANDSOUND.Entry
local text = string.format("Textclassname: %s | Locale: %s",entry.Classname, entry.Locale)
self:I(text)
for _ID,_data in pairs(entry.Data) do
local data = _data -- #TEXTANDSOUND.Data
local text = string.format("ID: %s\nText: %s\nSoundfile: %s With length: %d\nSubtitle: %s",tostring(_ID), data.Text or "none",data.Soundfile or "none",data.Soundlength or 0,data.Subtitle or "none")
self:I(text)
end
end
return self
end
----------------------------------------------------------------
-- End TextAndSound
----------------------------------------------------------------

View File

@@ -34,8 +34,6 @@
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\Timer\TIMER_Main.jpg)
--
-- # The TIMER Concept -- # The TIMER Concept
-- --
-- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases. -- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
@@ -107,19 +105,17 @@ TIMER = {
--- Timer ID. --- Timer ID.
_TIMERID=0 _TIMERID=0
--- Timer data base.
--_TIMERDB={}
--- TIMER class version. --- TIMER class version.
-- @field #string version -- @field #string version
TIMER.version="0.1.1" TIMER.version="0.2.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot. -- TODO: Randomization.
-- TODO: Write docs. -- TODO: Pause/unpause.
-- DONE: Write docs.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
@@ -156,13 +152,10 @@ function TIMER:New(Function, ...)
-- Log id. -- Log id.
self.lid=string.format("TIMER UID=%d | ", self.uid) self.lid=string.format("TIMER UID=%d | ", self.uid)
-- Add to DB.
--_TIMERDB[self.uid]=self
return self return self
end end
--- Create a new TIMER object. --- Start TIMER object.
-- @param #TIMER self -- @param #TIMER self
-- @param #number Tstart Relative start time in seconds. -- @param #number Tstart Relative start time in seconds.
-- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once. -- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once.
@@ -174,7 +167,7 @@ function TIMER:Start(Tstart, dT, Duration)
local Tnow=timer.getTime() local Tnow=timer.getTime()
-- Start time in sec. -- Start time in sec.
self.Tstart=Tstart and Tnow+Tstart or Tnow+0.001 -- one millisecond delay if Tstart=nil self.Tstart=Tstart and Tnow+math.max(Tstart, 0.001) or Tnow+0.001 -- one millisecond delay if Tstart=nil
-- Set time interval. -- Set time interval.
self.dT=dT self.dT=dT
@@ -199,6 +192,20 @@ function TIMER:Start(Tstart, dT, Duration)
return self return self
end end
--- Start TIMER object if a condition is met. Useful for e.g. debugging.
-- @param #TIMER self
-- @param #boolean Condition Must be true for the TIMER to start
-- @param #number Tstart Relative start time in seconds.
-- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once.
-- @param #number Duration Time in seconds for how long the timer is running. If not specified `nil`, the timer runs forever or until stopped manually by the `TIMER:Stop()` function.
-- @return #TIMER self
function TIMER:StartIf(Condition,Tstart, dT, Duration)
if Condition then
self:Start(Tstart, dT, Duration)
end
return self
end
--- Stop the timer by removing the timer function. --- Stop the timer by removing the timer function.
-- @param #TIMER self -- @param #TIMER self
-- @param #number Delay (Optional) Delay in seconds, before the timer is stopped. -- @param #number Delay (Optional) Delay in seconds, before the timer is stopped.
@@ -215,14 +222,24 @@ function TIMER:Stop(Delay)
-- Remove timer function. -- Remove timer function.
self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls)) self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls))
-- We use a pcall here because if the DCS timer does not exist any more, it crashes the whole script!
local status=pcall(
function ()
timer.removeFunction(self.tid) timer.removeFunction(self.tid)
end
)
-- Debug messages.
if status then
self:T2(self.lid..string.format("Stopped timer!"))
else
self:E(self.lid..string.format("WARNING: Could not remove timer function! isrunning=%s", tostring(self.isrunning)))
end
-- Not running any more. -- Not running any more.
self.isrunning=false self.isrunning=false
-- Remove DB entry.
--_TIMERDB[self.uid]=nil
end end
end end
@@ -239,6 +256,15 @@ function TIMER:SetMaxFunctionCalls(Nmax)
return self return self
end end
--- Set time interval. Can also be set when the timer is already running and is applied after the next function call.
-- @param #TIMER self
-- @param #number dT Time interval in seconds.
-- @return #TIMER self
function TIMER:SetTimeInterval(dT)
self.dT=dT
return self
end
--- Check if the timer has been started and was not stopped. --- Check if the timer has been started and was not stopped.
-- @param #TIMER self -- @param #TIMER self
-- @return #boolean If `true`, the timer is running. -- @return #boolean If `true`, the timer is running.

View File

@@ -77,7 +77,6 @@ do -- UserFlag
return self return self
end end
--- Get the userflag Number. --- Get the userflag Number.
-- @param #USERFLAG self -- @param #USERFLAG self
-- @return #number Number The number value to be checked if it is the same as the userflag. -- @return #number Number The number value to be checked if it is the same as the userflag.
@@ -90,8 +89,6 @@ do -- UserFlag
return trigger.misc.getUserFlag( self.UserFlagName ) return trigger.misc.getUserFlag( self.UserFlagName )
end end
--- Check if the userflag has a value of Number. --- Check if the userflag has a value of Number.
-- @param #USERFLAG self -- @param #USERFLAG self
-- @param #number Number The number value to be checked if it is the same as the userflag. -- @param #number Number The number value to be checked if it is the same as the userflag.

View File

@@ -95,8 +95,7 @@ do -- Velocity
return UTILS.MpsToMiph( self.Velocity ) return UTILS.MpsToMiph( self.Velocity )
end end
--- Get the velocity in text, according the player @{Core.Settings}.
--- Get the velocity in text, according the player @{Settings}.
-- @param #VELOCITY self -- @param #VELOCITY self
-- @param Core.Settings#SETTINGS Settings -- @param Core.Settings#SETTINGS Settings
-- @return #string The velocity in text. -- @return #string The velocity in text.
@@ -113,11 +112,11 @@ do -- Velocity
end end
end end
--- Get the velocity in text, according the player or default @{Settings}. --- Get the velocity in text, according the player or default @{Core.Settings}.
-- @param #VELOCITY self -- @param #VELOCITY self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param Core.Settings#SETTINGS Settings -- @param Core.Settings#SETTINGS Settings
-- @return #string The velocity in text according the player or default @{Settings} -- @return #string The velocity in text according the player or default @{Core.Settings}
function VELOCITY:ToString( VelocityGroup, Settings ) -- R2.3 function VELOCITY:ToString( VelocityGroup, Settings ) -- R2.3
self:F( { Group = VelocityGroup and VelocityGroup:GetName() } ) self:F( { Group = VelocityGroup and VelocityGroup:GetName() } )
local Settings = Settings or ( VelocityGroup and _DATABASE:GetPlayerSettings( VelocityGroup:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( VelocityGroup and _DATABASE:GetPlayerSettings( VelocityGroup:GetPlayerName() ) ) or _SETTINGS
@@ -134,7 +133,7 @@ do -- VELOCITY_POSITIONABLE
--- # VELOCITY_POSITIONABLE class, extends @{Core.Base#BASE} --- # VELOCITY_POSITIONABLE class, extends @{Core.Base#BASE}
-- --
-- VELOCITY_POSITIONABLE monitors the speed of an @{Positionable} in the simulation, which can be expressed in various formats according the Settings. -- @{#VELOCITY_POSITIONABLE} monitors the speed of a @{Wrapper.Positionable#POSITIONABLE} in the simulation, which can be expressed in various formats according the Settings.
-- --
-- ## 1. VELOCITY_POSITIONABLE constructor -- ## 1. VELOCITY_POSITIONABLE constructor
-- --
@@ -178,9 +177,9 @@ do -- VELOCITY_POSITIONABLE
return UTILS.MpsToMiph( self.Positionable:GetVelocityMPS() or 0 ) return UTILS.MpsToMiph( self.Positionable:GetVelocityMPS() or 0 )
end end
--- Get the velocity in text, according the player or default @{Settings}. --- Get the velocity in text, according the player or default @{Core.Settings}.
-- @param #VELOCITY_POSITIONABLE self -- @param #VELOCITY_POSITIONABLE self
-- @return #string The velocity in text according the player or default @{Settings} -- @return #string The velocity in text according the player or default @{Core.Settings}
function VELOCITY_POSITIONABLE:ToString() -- R2.3 function VELOCITY_POSITIONABLE:ToString() -- R2.3
self:F( { Group = self.Positionable and self.Positionable:GetName() } ) self:F( { Group = self.Positionable and self.Positionable:GetName() } )
local Settings = Settings or ( self.Positionable and _DATABASE:GetPlayerSettings( self.Positionable:GetPlayerName() ) ) or _SETTINGS local Settings = Settings or ( self.Positionable and _DATABASE:GetPlayerSettings( self.Positionable:GetPlayerName() ) ) or _SETTINGS

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
--- **Core** - The ZONE_DETECTION class, defined by a zone name, a detection object and a radius.
-- @module Core.Zone_Detection
-- @image MOOSE.JPG
--- The ZONE_DETECTION class, defined by a zone name, a detection object and a radius. --- @type ZONE_DETECTION
-- @type ZONE_DETECTION
-- @field DCS#Vec2 Vec2 The current location of the zone. -- @field DCS#Vec2 Vec2 The current location of the zone.
-- @field DCS#Distance Radius The radius of the zone. -- @field DCS#Distance Radius The radius of the zone.
-- @extends #ZONE_BASE -- @extends #ZONE_BASE
@@ -49,7 +51,6 @@ function ZONE_DETECTION:BoundZone( Points, CountryID, UnBound )
local Angle local Angle
local RadialBase = math.pi*2 local RadialBase = math.pi*2
--
for Angle = 0, 360, (360 / Points ) do for Angle = 0, 360, (360 / Points ) do
local Radial = Angle * RadialBase / 360 local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()

View File

@@ -1,4 +1,6 @@
--- **DCS API** Prototypes --- **DCS API** Prototypes.
--
-- ===
-- --
-- See the [Simulator Scripting Engine Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation) on Hoggit for further explanation and examples. -- See the [Simulator Scripting Engine Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation) on Hoggit for further explanation and examples.
-- --
@@ -191,21 +193,29 @@ do -- land
--- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land) --- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land)
-- @type land.SurfaceType -- @type land.SurfaceType
-- @field LAND -- @field LAND Land=1
-- @field SHALLOW_WATER -- @field SHALLOW_WATER Shallow water=2
-- @field WATER -- @field WATER Water=3
-- @field ROAD -- @field ROAD Road=4
-- @field RUNWAY -- @field RUNWAY Runway=5
--- Returns altitude MSL of the point. --- Returns the distance from sea level (y-axis) of a given vec2 point.
-- @function [parent=#land] getHeight -- @function [parent=#land] getHeight
-- @param #Vec2 point point on the ground. -- @param #Vec2 point Point on the ground.
-- @return #Distance -- @return #number Height in meters.
--- returns surface type at the given point. --- Returns the surface height and depth of a point. Useful for checking if the path is deep enough to support a given ship.
-- Both values are positive. When checked over water at sea level the first value is always zero.
-- When checked over water at altitude, for example the reservoir of the Inguri Dam, the first value is the corresponding altitude the water level is at.
-- @function [parent=#land] getSurfaceHeightWithSeabed
-- @param #Vec2 point Position where to check.
-- @return #number Height in meters.
-- @return #number Depth in meters.
--- Returns surface type at the given point.
-- @function [parent=#land] getSurfaceType -- @function [parent=#land] getSurfaceType
-- @param #Vec2 point Point on the land. -- @param #Vec2 point Point on the land.
-- @return #land.SurfaceType -- @return #number Enumerator value from `land.SurfaceType` (LAND=1, SHALLOW_WATER=2, WATER=3, ROAD=4, RUNWAY=5)
--- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land) --- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land)
land = {} --#land land = {} --#land
@@ -306,6 +316,11 @@ do -- country
-- @field Argentinia -- @field Argentinia
-- @field Cyprus -- @field Cyprus
-- @field Slovenia -- @field Slovenia
-- @field BOLIVIA
-- @field GHANA
-- @field NIGERIA
-- @field PERU
-- @field ECUADOR
country = {} --#country country = {} --#country
@@ -334,9 +349,23 @@ do -- coalition
-- @field RED -- @field RED
-- @field BLUE -- @field BLUE
--- @function [parent=#coalition] getCountryCoalition --- Get country coalition.
-- @param #number countryId -- @function [parent=#coalition] getCountryCoalition
-- @return #number coalitionId -- @param #number countryId Country ID.
-- @return #number coalitionId Coalition ID.
--- Dynamically spawns a group. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addGroup)
-- @function [parent=#coalition] addGroup
-- @param #number countryId Id of the country.
-- @param #number groupCategory Group category. Set -1 for spawning FARPS.
-- @param #table groupData Group data table.
-- @return DCS#Group The spawned Group object.
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addGroup)
-- @function [parent=#coalition] addStaticObject
-- @param #number countryId Id of the country.
-- @param #table groupData Group data table.
-- @return DCS#Static The spawned static object.
coalition = {} -- #coalition coalition = {} -- #coalition
@@ -406,8 +435,8 @@ do -- Types
--- Vec3 type is a 3D-vector. --- Vec3 type is a 3D-vector.
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain. -- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
-- @type Vec3 -- @type Vec3
-- @field #Distance x is directed to the north -- @field #Distance x is directed to the North
-- @field #Distance z is directed to the east -- @field #Distance z is directed to the East
-- @field #Distance y is directed up -- @field #Distance y is directed up
--- Vec2 is a 2D-vector for the ground plane as a reference plane. --- Vec2 is a 2D-vector for the ground plane as a reference plane.
@@ -471,6 +500,22 @@ do -- Types
--@field #boolean lateActivated --@field #boolean lateActivated
--@field #boolean uncontrolled --@field #boolean uncontrolled
--- DCS template data structure.
-- @type Template
-- @field #boolean uncontrolled Aircraft is uncontrolled.
-- @field #boolean lateActivation Group is late activated.
-- @field #number x 2D Position on x-axis in meters.
-- @field #number y 2D Position on y-axis in meters.
-- @field #table units Unit list.
--
--- Unit data structure.
--@type Template.Unit
--@field #string name Name of the unit.
--@field #number x
--@field #number y
--@field #number alt
end -- end --
@@ -487,8 +532,9 @@ do -- Object
-- @field UNIT -- @field UNIT
-- @field WEAPON -- @field WEAPON
-- @field STATIC -- @field STATIC
-- @field SCENERY
-- @field BASE -- @field BASE
-- @field SCENERY
-- @field CARGO
--- @type Object.Desc --- @type Object.Desc
-- @extends #Desc -- @extends #Desc
@@ -499,6 +545,10 @@ do -- Object
-- @param #Object self -- @param #Object self
-- @return #boolean -- @return #boolean
--- @function [parent=#Object] isActive
-- @param #Object self
-- @return #boolean
--- @function [parent=#Object] destroy --- @function [parent=#Object] destroy
-- @param #Object self -- @param #Object self
@@ -637,10 +687,11 @@ do -- Weapon
--- Weapon.Category enum that stores weapon categories. --- Weapon.Category enum that stores weapon categories.
-- @type Weapon.Category -- @type Weapon.Category
-- @field SHELL -- @field #number SHELL Shell.
-- @field MISSILE -- @field #number MISSILE Missile
-- @field ROCKET -- @field #number ROCKET Rocket.
-- @field BOMB -- @field #number BOMB Bomb.
-- @field #number TORPEDO Torpedo.
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB). --- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
@@ -959,8 +1010,8 @@ do -- Unit
--- Enum that stores aircraft refueling system types. --- Enum that stores aircraft refueling system types.
-- @type Unit.RefuelingSystem -- @type Unit.RefuelingSystem
-- @field BOOM_AND_RECEPTACLE -- @field BOOM_AND_RECEPTACLE Tanker with a boom.
-- @field PROBE_AND_DROGUE -- @field PROBE_AND_DROGUE Tanker with a probe.
--- Enum that stores sensor types. --- Enum that stores sensor types.
-- @type Unit.SensorType -- @type Unit.SensorType
@@ -1131,6 +1182,11 @@ do -- Unit
-- @param #Unit self -- @param #Unit self
-- @return #Unit.Ammo -- @return #Unit.Ammo
--- Returns the number of infantry that can be embark onto the aircraft. Only returns a value if run on airplanes or helicopters. Returns nil if run on ground or ship units.
-- @function [parent=#Unit] getDescentCapacity
-- @param #Unit self
-- @return #number Number of soldiers that embark.
--- Returns the unit sensors. --- Returns the unit sensors.
-- @function [parent=#Unit] getSensors -- @function [parent=#Unit] getSensors
-- @param #Unit self -- @param #Unit self
@@ -1261,6 +1317,42 @@ do -- Group
end -- Group end -- Group
do -- StaticObject
--- Represents a static object.
-- @type StaticObject
-- @extends DCS#Object
--- Returns the static object.
-- @function [parent=#StaticObject] getByName
-- @param #string name Name of the static object.
-- @return #StaticObject
StaticObject = {} --#StaticObject
end
do --Event
--- Event structure. Note that present fields depend on type of event.
-- @type Event
-- @field #number id Event ID.
-- @field #number time Mission time in seconds.
-- @field DCS#Unit initiator Unit initiating the event.
-- @field DCS#Unit target Target unit.
-- @field DCS#Airbase place Airbase.
-- @field number subPlace Subplace. Unknown and often just 0.
-- @field #string weapon_name Weapoin name.
-- @field #number idx Mark ID.
-- @field #number coalition Coalition ID.
-- @field #number groupID Group ID, *e.g.* of group that added mark point.
-- @field #string text Text, *e.g.* of mark point.
-- @field DCS#Vec3 pos Position vector, *e.g.* of mark point.
-- @field #string comment Comment, *e.g.* LSO score.
Event={} --#Event
end
do -- AI do -- AI
@@ -1348,7 +1440,9 @@ do -- AI
--- @type AI.Option.Ground --- @type AI.Option.Ground
-- @field #AI.Option.Ground.id id -- @field #AI.Option.Ground.id id
-- @field #AI.Option.Ground.val val -- @field #AI.Option.Ground.val val
-- @field #AI.Option.Ground.mid mid
-- @field #AI.Option.Ground.mval mval
--
--- @type AI.Option.Naval --- @type AI.Option.Naval
-- @field #AI.Option.Naval.id id -- @field #AI.Option.Naval.id id
-- @field #AI.Option.Naval.val val -- @field #AI.Option.Naval.val val
@@ -1371,6 +1465,11 @@ do -- AI
-- @field PROHIBIT_AG -- @field PROHIBIT_AG
-- @field MISSILE_ATTACK -- @field MISSILE_ATTACK
-- @field PROHIBIT_WP_PASS_REPORT -- @field PROHIBIT_WP_PASS_REPORT
-- @field OPTION_RADIO_USAGE_CONTACT
-- @field OPTION_RADIO_USAGE_ENGAGE
-- @field OPTION_RADIO_USAGE_KILL
-- @field JETT_TANKS_IF_EMPTY
-- @field FORCED_ATTACK
--- @type AI.Option.Air.id.FORMATION --- @type AI.Option.Air.id.FORMATION
-- @field LINE_ABREAST -- @field LINE_ABREAST
@@ -1440,19 +1539,35 @@ do -- AI
--- @type AI.Option.Ground.id --- @type AI.Option.Ground.id
-- @field NO_OPTION -- @field NO_OPTION
-- @field ROE @{#AI.Option.Ground.val.ROE} -- @field ROE @{#AI.Option.Ground.val.ROE}
-- @field FORMATION
-- @field DISPERSE_ON_ATTACK true or false -- @field DISPERSE_ON_ATTACK true or false
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE} -- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
-- @field ENGAGE_AIR_WEAPONS -- @field ENGAGE_AIR_WEAPONS
-- @field AC_ENGAGEMENT_RANGE_RESTRICTION
--- @type AI.Option.Ground.mid -- Moose added
-- @field RESTRICT_AAA_MIN 27
-- @field RESTRICT_AAA_MAX 29
-- @field RESTRICT_TARGETS @{#AI.Option.Ground.mval.ENGAGE_TARGETS} 28
--- @type AI.Option.Ground.val --- @type AI.Option.Ground.val
-- @field #AI.Option.Ground.val.ROE ROE -- @field #AI.Option.Ground.val.ROE ROE
-- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE -- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE
-- @field #AI.Option.Ground.val.ENGAGE_TARGETS RESTRICT_TARGETS
--- @type AI.Option.Ground.val.ROE --- @type AI.Option.Ground.val.ROE
-- @field OPEN_FIRE -- @field OPEN_FIRE
-- @field RETURN_FIRE -- @field RETURN_FIRE
-- @field WEAPON_HOLD -- @field WEAPON_HOLD
--- @type AI.Option.Ground.mval -- Moose added
-- @field #AI.Option.Ground.mval.ENGAGE_TARGETS ENGAGE_TARGETS
--- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added
-- @field ANY_TARGET -- 0
-- @field AIR_UNITS_ONLY -- 1
-- @field GROUND_UNITS_ONLY -- 2
--- @type AI.Option.Ground.val.ALARM_STATE --- @type AI.Option.Ground.val.ALARM_STATE
-- @field AUTO -- @field AUTO
-- @field GREEN -- @field GREEN

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@
-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)
-- --
-- ==== -- ====
-- @module Functional.Arty -- @module Functional.Artillery
-- @image Artillery.JPG -- @image Artillery.JPG
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -103,12 +103,13 @@
-- @field #number coalition The coalition of the arty group. -- @field #number coalition The coalition of the arty group.
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead. -- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
-- @field #number respawndelay Respawn delay in seconds. -- @field #number respawndelay Respawn delay in seconds.
-- @field #number dtTrack Time interval in seconds for weapon tracking.
-- @extends Core.Fsm#FSM_CONTROLLABLE -- @extends Core.Fsm#FSM_CONTROLLABLE
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can --- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
-- interact with the process at certain events or states. -- interact with the process at certain events or states.
-- --
-- A new ARTY object can be created with the @{#ARTY.New}(*group*) contructor. -- A new ARTY object can be created with the @{#ARTY.New}(*group*) constructor.
-- The parameter *group* has to be a MOOSE Group object and defines ARTY group. -- The parameter *group* has to be a MOOSE Group object and defines ARTY group.
-- --
-- The ARTY FSM process can be started by the @{#ARTY.Start}() command. -- The ARTY FSM process can be started by the @{#ARTY.Start}() command.
@@ -146,7 +147,7 @@
-- When a new target is assigned via the @{#ARTY.AssignTargetCoord}() function (see below), the **NewTarget** event is triggered. -- When a new target is assigned via the @{#ARTY.AssignTargetCoord}() function (see below), the **NewTarget** event is triggered.
-- --
-- ## Assigning Targets -- ## Assigning Targets
-- Assigning targets is a central point of the ARTY class. Multiple targets can be assigned simultanioulsly and are put into a queue. -- Assigning targets is a central point of the ARTY class. Multiple targets can be assigned simultaneously and are put into a queue.
-- Of course, targets can be added at any time during the mission. For example, once they are detected by a reconnaissance unit. -- Of course, targets can be added at any time during the mission. For example, once they are detected by a reconnaissance unit.
-- --
-- In order to add a target, the function @{#ARTY.AssignTargetCoord}(*coord*, *prio*, *radius*, *nshells*, *maxengage*, *time*, *weapontype*, *name*) has to be used. -- In order to add a target, the function @{#ARTY.AssignTargetCoord}(*coord*, *prio*, *radius*, *nshells*, *maxengage*, *time*, *weapontype*, *name*) has to be used.
@@ -161,7 +162,7 @@
-- * *maxengage*: Number of times a target is engaged. -- * *maxengage*: Number of times a target is engaged.
-- * *time*: Time of day the engagement is schedule in the format "hh:mm:ss" for hh=hours, mm=minutes, ss=seconds. -- * *time*: Time of day the engagement is schedule in the format "hh:mm:ss" for hh=hours, mm=minutes, ss=seconds.
-- For example "10:15:35". In the case the attack will be executed at a quarter past ten in the morning at the day the mission started. -- For example "10:15:35". In the case the attack will be executed at a quarter past ten in the morning at the day the mission started.
-- If the engagement should start on the following day the format can be specified as "10:15:35+1", where the +1 denots the following day. -- If the engagement should start on the following day the format can be specified as "10:15:35+1", where the +1 denotes the following day.
-- This is useful for longer running missions or if the mission starts at 23:00 hours and the attack should be scheduled at 01:00 hours on the following day. -- This is useful for longer running missions or if the mission starts at 23:00 hours and the attack should be scheduled at 01:00 hours on the following day.
-- Of course, later days are also possible by appending "+2", "+3", etc. -- Of course, later days are also possible by appending "+2", "+3", etc.
-- **Note** that the time has to be given as a string. So the enclosing quotation marks "" are important. -- **Note** that the time has to be given as a string. So the enclosing quotation marks "" are important.
@@ -179,7 +180,7 @@
-- Let's first consider the case that none of the targets is scheduled to be executed at a certain time (*time*=nil). -- Let's first consider the case that none of the targets is scheduled to be executed at a certain time (*time*=nil).
-- The ARTY group will first engage the target with higher priority (*prio*=10). After the engagement is finished, the target with lower priority is attacked. -- The ARTY group will first engage the target with higher priority (*prio*=10). After the engagement is finished, the target with lower priority is attacked.
-- This is because the target with lower prio has been attacked one time less. After the attack on the lower priority task is finished and both targets -- This is because the target with lower prio has been attacked one time less. After the attack on the lower priority task is finished and both targets
-- have been engaged equally often, the target with the higher priority is engaged again. This coninues until a target has engaged three times. -- have been engaged equally often, the target with the higher priority is engaged again. This continues until a target has engaged three times.
-- Once the maximum number of engagements is reached, the target is deleted from the queue. -- Once the maximum number of engagements is reached, the target is deleted from the queue.
-- --
-- In other words, the queue is first sorted with respect to the number of engagements and targets with the same number of engagements are sorted with -- In other words, the queue is first sorted with respect to the number of engagements and targets with the same number of engagements are sorted with
@@ -190,7 +191,7 @@
-- As mentioned above, targets can be engaged at a specific time of the day via the *time* parameter. -- As mentioned above, targets can be engaged at a specific time of the day via the *time* parameter.
-- --
-- If the *time* parameter is specified for a target, the first engagement of that target will happen at that time of the day and not before. -- If the *time* parameter is specified for a target, the first engagement of that target will happen at that time of the day and not before.
-- This also applies when multiple engagements are requested via the *maxengage* parameter. The first attack will not happen before the specifed time. -- This also applies when multiple engagements are requested via the *maxengage* parameter. The first attack will not happen before the specified time.
-- When that timed attack is finished, the *time* parameter is deleted and the remaining engagements are carried out in the same manner as for untimed targets (described above). -- When that timed attack is finished, the *time* parameter is deleted and the remaining engagements are carried out in the same manner as for untimed targets (described above).
-- --
-- Of course, it can happen that a scheduled task should be executed at a time, when another target is already under attack. -- Of course, it can happen that a scheduled task should be executed at a time, when another target is already under attack.
@@ -201,7 +202,7 @@
-- --
-- ## Determining the Amount of Ammo -- ## Determining the Amount of Ammo
-- --
-- In order to determin when a unit is out of ammo and possible initiate the rearming process it is necessary to know which types of weapons have to be counted. -- In order to determine when a unit is out of ammo and possible initiate the rearming process it is necessary to know which types of weapons have to be counted.
-- For most artillery unit types, this is simple because they only have one type of weapon and hence ammunition. -- For most artillery unit types, this is simple because they only have one type of weapon and hence ammunition.
-- --
-- However, there are more complex scenarios. For example, naval units carry a big arsenal of different ammunition types ranging from various cannon shell types -- However, there are more complex scenarios. For example, naval units carry a big arsenal of different ammunition types ranging from various cannon shell types
@@ -217,7 +218,7 @@
-- **Note** that the default parameters "weapons.shells", "weapons.nurs", "weapons.missiles" **should in priciple** capture all the corresponding ammo types. -- **Note** that the default parameters "weapons.shells", "weapons.nurs", "weapons.missiles" **should in priciple** capture all the corresponding ammo types.
-- However, the logic searches for the string "weapon.missies" in the ammo type. Especially for missiles, this string is often not contained in the ammo type descriptor. -- However, the logic searches for the string "weapon.missies" in the ammo type. Especially for missiles, this string is often not contained in the ammo type descriptor.
-- --
-- One way to determin which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}(). -- One way to determine which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}().
-- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file. -- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file.
-- --
-- ## Employing Selected Weapons -- ## Employing Selected Weapons
@@ -274,7 +275,7 @@
-- --
-- ## Simulated Weapons -- ## Simulated Weapons
-- --
-- In addtion to the standard weapons a group has available some special weapon types that are not possible to use in the native DCS environment are simulated. -- In addition to the standard weapons a group has available some special weapon types that are not possible to use in the native DCS environment are simulated.
-- --
-- ### Tactical Nukes -- ### Tactical Nukes
-- --
@@ -283,9 +284,9 @@
-- --
-- By default, they group does not have any nukes available. To give the group the ability the function @{#ARTY.SetTacNukeShells}(*n*) can be used. -- By default, they group does not have any nukes available. To give the group the ability the function @{#ARTY.SetTacNukeShells}(*n*) can be used.
-- This supplies the group with *n* nuclear shells, where *n* is restricted to the number of conventional shells the group can carry. -- This supplies the group with *n* nuclear shells, where *n* is restricted to the number of conventional shells the group can carry.
-- Note that the group must always have convenctional shells left in order to fire a nuclear shell. -- Note that the group must always have conventional shells left in order to fire a nuclear shell.
-- --
-- The default explostion strength is 0.075 kilo tons TNT. The can be changed with the @{#ARTY.SetTacNukeWarhead}(*strength*), where *strength* is given in kilo tons TNT. -- The default explosion strength is 0.075 kilo tons TNT. The can be changed with the @{#ARTY.SetTacNukeWarhead}(*strength*), where *strength* is given in kilo tons TNT.
-- --
-- ### Illumination Shells -- ### Illumination Shells
-- --
@@ -301,12 +302,12 @@
-- --
-- ### Smoke Shells -- ### Smoke Shells
-- --
-- In a similar way to illumination shells, ARTY groups can also employ smoke shells. The numer of smoke shells the group has available is set by the function -- In a similar way to illumination shells, ARTY groups can also employ smoke shells. The number of smoke shells the group has available is set by the function
-- @{#ARTY.SetSmokeShells}(*n*, *color*), where *n* is the number of shells and *color* defines the smoke color. Default is SMOKECOLOR.Red. -- @{#ARTY.SetSmokeShells}(*n*, *color*), where *n* is the number of shells and *color* defines the smoke color. Default is SMOKECOLOR.Red.
-- --
-- The weapon type to be used in the @{#ARTY.AssignTargetCoord}() function is *ARTY.WeaponType.SmokeShells*. -- The weapon type to be used in the @{#ARTY.AssignTargetCoord}() function is *ARTY.WeaponType.SmokeShells*.
-- --
-- The explosive shell the group fired is destroyed shortly before its impact on the ground and smoke of the speficied color is triggered at that position. -- The explosive shell the group fired is destroyed shortly before its impact on the ground and smoke of the specified color is triggered at that position.
-- --
-- --
-- ## Assignments via Markers on F10 Map -- ## Assignments via Markers on F10 Map
@@ -320,15 +321,15 @@
-- ### Target Assignments -- ### Target Assignments
-- A new target can be assigned by writing **arty engage** in the marker text. -- A new target can be assigned by writing **arty engage** in the marker text.
-- This is followed by a **comma separated list** of (optional) keywords and parameters. -- This is followed by a **comma separated list** of (optional) keywords and parameters.
-- First, it is important to address the ARTY group or groups that should engage. This can be done in numrous ways. The keywords are *battery*, *alias*, *cluster*. -- First, it is important to address the ARTY group or groups that should engage. This can be done in numerous ways. The keywords are *battery*, *alias*, *cluster*.
-- It is also possible to address all ARTY groups by the keyword *everyone* or *allbatteries*. These two can be used synonymously. -- It is also possible to address all ARTY groups by the keyword *everyone* or *allbatteries*. These two can be used synonymously.
-- **Note that**, if no battery is assigned nothing will happen. -- **Note that**, if no battery is assigned nothing will happen.
-- --
-- * *everyone* or *allbatteries* The target is assigned to all batteries. -- * *everyone* or *allbatteries* The target is assigned to all batteries.
-- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition. -- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition.
-- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks. -- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks.
-- * *cluster* The cluster of ARTY groups that is addessed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks. -- * *cluster* The cluster of ARTY groups that is addressed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks.
-- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. -- * *key* A number to authorize the target assignment. Only specifying the correct number will trigger an engagement.
-- * *time* Time for which which the engagement is schedules, e.g. 08:42. Default is as soon as possible. -- * *time* Time for which which the engagement is schedules, e.g. 08:42. Default is as soon as possible.
-- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50, i.e. medium priority. -- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50, i.e. medium priority.
-- * *shots* Number of shots (shells, rockets or missiles) fired at each engagement. Default is 5. -- * *shots* Number of shots (shells, rockets or missiles) fired at each engagement. Default is 5.
@@ -353,8 +354,8 @@
-- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 -- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15
-- arty engage, battery "Horwitzer 1", lldms 41:51:00N 41:47:58E -- arty engage, battery "Horwitzer 1", lldms 41:51:00N 41:47:58E
-- --
-- Note that the keywords and parameters are *case insensitve*. Only exception are the battery, alias and cluster names. -- Note that the keywords and parameters are *case insensitive*. Only exception are the battery, alias and cluster names.
-- These must be exactly the same as the names of the goups defined in the mission editor or the aliases and cluster names defined in the script. -- These must be exactly the same as the names of the groups defined in the mission editor or the aliases and cluster names defined in the script.
-- --
-- ### Relocation Assignments -- ### Relocation Assignments
-- --
@@ -363,11 +364,11 @@
-- * *time* Time for which which the relocation/move is schedules, e.g. 08:42. Default is as soon as possible. -- * *time* Time for which which the relocation/move is schedules, e.g. 08:42. Default is as soon as possible.
-- * *speed* The speed in km/h the group will drive at. Default is 70% of its max possible speed. -- * *speed* The speed in km/h the group will drive at. Default is 70% of its max possible speed.
-- * *on road* Group will use mainly roads. Default is off, i.e. it will go in a straight line from its current position to the assigned coordinate. -- * *on road* Group will use mainly roads. Default is off, i.e. it will go in a straight line from its current position to the assigned coordinate.
-- * *canceltarget* Group will cancel all running firing engagements and immidiately start to move. Default is that group will wait until is current assignment is over. -- * *canceltarget* Group will cancel all running firing engagements and immediately start to move. Default is that group will wait until is current assignment is over.
-- * *battery* Name of the ARTY group that the relocation is assigned to. -- * *battery* Name of the ARTY group that the relocation is assigned to.
-- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks. -- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks.
-- * *cluster* The cluster of ARTY groups that is addessed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks. -- * *cluster* The cluster of ARTY groups that is addressed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks.
-- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. -- * *key* A number to authorize the target assignment. Only specifying the correct number will trigger an engagement.
-- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant. The group will move to the coordinates given in the lldms keyword. -- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant. The group will move to the coordinates given in the lldms keyword.
-- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below. -- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below.
-- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. -- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker.
@@ -410,12 +411,12 @@
-- --
-- A few options can be set by marks. The corresponding keyword is **arty set**. This can be used to define the rearming place and group for a battery. -- A few options can be set by marks. The corresponding keyword is **arty set**. This can be used to define the rearming place and group for a battery.
-- --
-- To set the reamring place of a group at the marker position type -- To set the rearming place of a group at the marker position type
-- arty set, battery "Paladin Alpha", rearming place -- arty set, battery "Paladin Alpha", rearming place
-- --
-- Setting the rearming group is independent of the position of the mark. Just create one anywhere on the map and type -- Setting the rearming group is independent of the position of the mark. Just create one anywhere on the map and type
-- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M818" -- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M818"
-- Note that the name of the rearming group has to be given in quotation marks and spellt exactly as the group name defined in the mission editor. -- Note that the name of the rearming group has to be given in quotation marks and spelt exactly as the group name defined in the mission editor.
-- --
-- ## Transporting -- ## Transporting
-- --
@@ -693,7 +694,7 @@ ARTY.db={
--- Arty script version. --- Arty script version.
-- @field #string version -- @field #string version
ARTY.version="1.2.0" ARTY.version="1.3.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -802,6 +803,9 @@ function ARTY:New(group, alias)
self.ismobile=false self.ismobile=false
end end
-- Set track time interval.
self.dtTrack=0.2
-- Set speed to 0.7 of maximum. -- Set speed to 0.7 of maximum.
self.Speed=self.SpeedMax * 0.7 self.Speed=self.SpeedMax * 0.7
@@ -1497,6 +1501,15 @@ function ARTY:SetStatusInterval(interval)
return self return self
end end
--- Set time interval for weapon tracking.
-- @param #ARTY self
-- @param #number interval Time interval in seconds. Default 0.2 seconds.
-- @return self
function ARTY:SetTrackInterval(interval)
self.dtTrack=interval or 0.2
return self
end
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed. --- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
-- @param #ARTY self -- @param #ARTY self
-- @param #number waittime Time in seconds. Default 300 seconds. -- @param #number waittime Time in seconds. Default 300 seconds.
@@ -2129,6 +2142,95 @@ end
-- Event Handling -- Event Handling
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Function called during tracking of weapon.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #ARTY self ARTY object.
-- @param #ARTY.Target target Target of the weapon.
function ARTY._FuncTrack(weapon, self, target)
-- Coordinate and distance to target.
local _coord=weapon.coordinate
local _dist=_coord:Get2DDistance(target.coord)
local _destroyweapon=false
-- Debug
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
if target.weapontype==ARTY.WeaponType.IlluminationShells then
-- Check if within distace.
if _dist<target.radius then
-- Get random coordinate within certain radius of the target.
local _cr=target.coord:GetRandomCoordinateInRadius(target.radius)
-- Get random altitude over target.
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
-- Adjust explosion height of coordinate.
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
-- Create illumination flare.
_ci:IlluminationBomb(self.illuPower)
-- Destroy actual shell.
_destroyweapon=true
end
elseif target.weapontype==ARTY.WeaponType.SmokeShells then
if _dist<target.radius then
-- Get random coordinate within a certain radius.
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Fire smoke at this coordinate.
_cr:Smoke(self.smokeColor)
-- Destroy actual shell.
_destroyweapon=true
end
end
if _destroyweapon then
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
-- Destroy weapon and stop timer.
weapon:Destroy()
-- No more tracking.
weapon.tracking=false
end
end
--- Function called after impact of weapon.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #ARTY self ARTY object.
-- @param #ARTY.Target target Target of the weapon.
function ARTY._FuncImpact(weapon, self, target)
-- Debug info.
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
-- Get impact coordinate.
local _impactcoord=weapon:GetImpactCoordinate()
-- Create a "nuclear" explosion and blast at the impact point.
if target.weapontype==ARTY.WeaponType.TacticalNukes then
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
--SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
self:ScheduleOnce(1.0, ARTY._NuclearBlast, self, _impactcoord)
end
end
--- Eventhandler for shot event. --- Eventhandler for shot event.
-- @param #ARTY self -- @param #ARTY self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -2162,128 +2264,32 @@ function ARTY:OnEventShot(EventData)
self:T(self.lid..text) self:T(self.lid..text)
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug) MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
-- Last known position of the weapon fired.
local _lastpos={x=0, y=0, z=0}
--- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell.
-- @param #table _weapon
local function _TrackWeapon(_data)
-- When the pcall status returns false the weapon has hit.
local _weaponalive,_currpos = pcall(
function()
return _data.weapon:getPoint()
end)
-- Debug
self:T3(self.lid..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_weaponalive)))
-- Destroy weapon before impact.
local _destroyweapon=false
if _weaponalive then
-- Update last position.
_lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z}
-- Coordinate and distance to target.
local _coord=COORDINATE:NewFromVec3(_lastpos)
local _dist=_coord:Get2DDistance(_data.target.coord)
-- Debug
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then
-- Check if within distace.
if _dist<_data.target.radius then
-- Get random coordinate within certain radius of the target.
local _cr=_data.target.coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Get random altitude over target.
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
-- Adjust explosion height of coordinate.
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
-- Create illumination flare.
_ci:IlluminationBomb(self.illuPower)
-- Destroy actual shell.
_destroyweapon=true
end
elseif _data.target.weapontype==ARTY.WeaponType.SmokeShells then
if _dist<_data.target.radius then
-- Get random coordinate within a certain radius.
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Fire smoke at this coordinate.
_cr:Smoke(self.smokeColor)
-- Destroy actual shell.
_destroyweapon=true
end
end
if _destroyweapon then
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
-- Destroy weapon and stop timer.
_data.weapon:destroy()
return nil
else
-- TODO: Make dt input parameter.
local dt=0.02
self:T3(self.lid..string.format("ARTY %s tracking weapon again in %.3f seconds", self.groupname, dt))
-- Check again in 0.05 seconds.
return timer.getTime() + dt
end
else
-- Get impact coordinate.
local _impactcoord=COORDINATE:NewFromVec3(_lastpos)
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
-- Create a "nuclear" explosion and blast at the impact point.
if _data.target.weapontype==ARTY.WeaponType.TacticalNukes then
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
end
-- Stop timer.
return nil
end
end
-- Start track the shell if we want to model a tactical nuke. -- Start track the shell if we want to model a tactical nuke.
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0 local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0 local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0 local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
if _tracknuke or _trackillu or _tracksmoke then if _tracknuke or _trackillu or _tracksmoke then
-- Debug info.
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname)) self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
local _peter={} -- Create a weapon object.
_peter.weapon=EventData.weapon local weapon=WEAPON:New(EventData.weapon)
_peter.target=UTILS.DeepCopy(self.currentTarget)
timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0) -- Set time step for tracking.
weapon:SetTimeStepTrack(self.dtTrack)
-- Copy target. We need a copy because it might already be overwritten with the next target during flight of weapon.
local target=UTILS.DeepCopy(self.currentTarget)
-- Set callback functions.
weapon:SetFuncTrack(ARTY._FuncTrack, self, target)
weapon:SetFuncImpact(ARTY._FuncImpact, self, target)
-- Start tracking in 2 sec (arty ammo should fly a bit).
weapon:StartTrack(2)
end end
-- Get current ammo. -- Get current ammo.
@@ -3422,7 +3428,7 @@ function ARTY:onafterMove(Controllable, From, Event, To, move)
-- Set current move. -- Set current move.
self.currentMove=move self.currentMove=move
-- Route group to coodinate. -- Route group to coordinate.
self:_Move(self.Controllable, move.coord, move.speed, move.onroad) self:_Move(self.Controllable, move.coord, move.speed, move.onroad)
end end
@@ -3931,9 +3937,10 @@ function ARTY:GetAmmo(display)
return nammo, nshells, nrockets, nmissiles return nammo, nshells, nrockets, nmissiles
end end
for _,unit in pairs(units) do for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then if unit then
-- Output. -- Output.
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName()) local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Keep airbases clean of crashing or colliding airplanes, and kill missiles when being fired at airbases. --- **Functional** - Keep airbases clean of crashing or colliding airplanes, and kill missiles when being fired at airbases.
-- --
-- === -- ===
-- --

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Management of target **Designation**. Lase, smoke and illuminate targets. --- **Functional** - Management of target **Designation**. Lase, smoke and illuminate targets.
-- --
-- === -- ===
-- --
@@ -48,7 +48,7 @@
-- --
-- ![Banner Image](..\Presentations\DESIGNATE\Dia3.JPG) -- ![Banner Image](..\Presentations\DESIGNATE\Dia3.JPG)
-- --
-- A typical mission setup would require Recce (a @{Set} of Recce) to be detecting potential targets. -- A typical mission setup would require Recce (a @{Core.Set} of Recce) to be detecting potential targets.
-- The DetectionObject will group the detected targets based on the detection method being used. -- The DetectionObject will group the detected targets based on the detection method being used.
-- Possible detection methods could be by Area, by Type or by Unit. -- Possible detection methods could be by Area, by Type or by Unit.
-- Each grouping will result in a **TargetGroup**, for terminology and clarity we will use this term throughout the document. -- Each grouping will result in a **TargetGroup**, for terminology and clarity we will use this term throughout the document.
@@ -213,7 +213,7 @@ do -- DESIGNATE
-- In order to prevent an overflow of designations due to many Detected Targets, there is a -- In order to prevent an overflow of designations due to many Detected Targets, there is a
-- Maximum Designations scope that is set in the DesignationObject. -- Maximum Designations scope that is set in the DesignationObject.
-- --
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject. -- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations (target groups) put in scope of the DesignationObject.
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected. -- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
-- --
-- # 4. Laser codes -- # 4. Laser codes
@@ -276,7 +276,7 @@ do -- DESIGNATE
-- # 7. Designate Menu Location for a Mission -- # 7. Designate Menu Location for a Mission
-- --
-- You can make DESIGNATE work for a @{Tasking.Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission. -- You can make DESIGNATE work for a @{Tasking.Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission.
-- Use the method @{#DESIGNATE.SetMission}() to set the @{Mission} object for the designate function. -- Use the method @{#DESIGNATE.SetMission}() to set the @{Tasking.Mission} object for the designate function.
-- --
-- # 8. Status Report -- # 8. Status Report
-- --
@@ -562,7 +562,8 @@ do -- DESIGNATE
end end
--- Set the maximum amount of designations. --- Set the maximum amount of designations (target groups). This will put a limit on the amount of designations in scope.
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
-- @param #DESIGNATE self -- @param #DESIGNATE self
-- @param #number MaximumDesignations -- @param #number MaximumDesignations
-- @return #DESIGNATE -- @return #DESIGNATE
@@ -602,7 +603,7 @@ do -- DESIGNATE
end end
--- Set the maximum amount of markings FACs will do, per designated target group. --- Set the maximum amount of markings FACs will do, per designated target group. This will limit the number of parallelly marked units of a target group.
-- @param #DESIGNATE self -- @param #DESIGNATE self
-- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group. -- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group.
-- @return #DESIGNATE -- @return #DESIGNATE
@@ -911,8 +912,8 @@ do -- DESIGNATE
for DesignateIndex, Designating in pairs( self.Designating ) do for DesignateIndex, Designating in pairs( self.Designating ) do
local DetectedItem = DetectedItems[DesignateIndex] local DetectedItem = DetectedItems[DesignateIndex]
if DetectedItem then if DetectedItem then
local Report = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " ) local Report = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup, nil, true ):Text( ", " )
DetectedReport:Add( string.rep( "-", 140 ) ) DetectedReport:Add( string.rep( "-", 40 ) )
DetectedReport:Add( " - " .. Report ) DetectedReport:Add( " - " .. Report )
if string.find( Designating, "L" ) then if string.find( Designating, "L" ) then
DetectedReport:Add( " - " .. "Lasing Targets" ) DetectedReport:Add( " - " .. "Lasing Targets" )
@@ -1192,8 +1193,8 @@ do -- DESIGNATE
local MarkingCount = 0 local MarkingCount = 0
local MarkedTypes = {} local MarkedTypes = {}
local ReportTypes = REPORT:New() --local ReportTypes = REPORT:New()
local ReportLaserCodes = REPORT:New() --local ReportLaserCodes = REPORT:New()
TargetSetUnit:Flush( self ) TargetSetUnit:Flush( self )
@@ -1243,7 +1244,7 @@ do -- DESIGNATE
if not Recce then if not Recce then
self:F( "Lasing..." ) self:F( "Lasing..." )
self.RecceSet:Flush( self) --self.RecceSet:Flush( self)
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
@@ -1282,13 +1283,13 @@ do -- DESIGNATE
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function. -- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
MarkingCount = MarkingCount + 1 MarkingCount = MarkingCount + 1
local TargetUnitType = TargetUnit:GetTypeName() local TargetUnitType = TargetUnit:GetTypeName()
--RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
-- 5, self.AttackSet, DesignateName ) 10, self.AttackSet, DesignateName )
if not MarkedTypes[TargetUnitType] then if not MarkedTypes[TargetUnitType] then
MarkedTypes[TargetUnitType] = true MarkedTypes[TargetUnitType] = true
ReportTypes:Add(TargetUnitType) --ReportTypes:Add(TargetUnitType)
end end
ReportLaserCodes:Add(RecceUnit.LaserCode) --ReportLaserCodes:Add(RecceUnit.LaserCode)
return return
end end
else else
@@ -1303,16 +1304,16 @@ do -- DESIGNATE
if Recce then if Recce then
Recce:LaseOff() Recce:LaseOff()
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet, self.DesignateName ) Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 10, self.AttackSet, self.DesignateName )
end end
else else
--MarkingCount = MarkingCount + 1 --MarkingCount = MarkingCount + 1
local TargetUnitType = TargetUnit:GetTypeName() local TargetUnitType = TargetUnit:GetTypeName()
if not MarkedTypes[TargetUnitType] then if not MarkedTypes[TargetUnitType] then
MarkedTypes[TargetUnitType] = true MarkedTypes[TargetUnitType] = true
ReportTypes:Add(TargetUnitType) --ReportTypes:Add(TargetUnitType)
end end
ReportLaserCodes:Add(RecceUnit.LaserCode) --ReportLaserCodes:Add(RecceUnit.LaserCode)
end end
end end
end end
@@ -1322,19 +1323,19 @@ do -- DESIGNATE
local TargetUnitType = TargetUnit:GetTypeName() local TargetUnitType = TargetUnit:GetTypeName()
if not MarkedTypes[TargetUnitType] then if not MarkedTypes[TargetUnitType] then
MarkedTypes[TargetUnitType] = true MarkedTypes[TargetUnitType] = true
ReportTypes:Add(TargetUnitType) --ReportTypes:Add(TargetUnitType)
end end
ReportLaserCodes:Add(Recce.LaserCode) --ReportLaserCodes:Add(Recce.LaserCode)
--Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet ) Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 10, self.AttackSet )
end end
end end
end end
end end
) )
local MarkedTypesText = ReportTypes:Text(', ') --local MarkedTypesText = ReportTypes:Text(', ')
local MarkedLaserCodesText = ReportLaserCodes:Text(', ') --local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName ) --self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested ) self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested )

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Models the detection of enemy units by FACs or RECCEs and group them according various methods. --- **Functional** - Models the detection of enemy units by FACs or RECCEs and group them according various methods.
-- --
-- === -- ===
-- --
@@ -40,7 +40,7 @@
do -- DETECTION_BASE do -- DETECTION_BASE
--- @type DETECTION_BASE --- @type DETECTION_BASE
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
-- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected.
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
-- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified.
@@ -318,7 +318,7 @@ do -- DETECTION_BASE
--- DETECTION constructor. --- DETECTION constructor.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param Core.Set#SET_GROUP DetectionSet The @{Set} of @{Group}s that is used to detect the units. -- @param Core.Set#SET_GROUP DetectionSet The @{Core.Set} of @{Wrapper.Group}s that is used to detect the units.
-- @return #DETECTION_BASE self -- @return #DETECTION_BASE self
function DETECTION_BASE:New( DetectionSet ) function DETECTION_BASE:New( DetectionSet )
@@ -474,7 +474,7 @@ do -- DETECTION_BASE
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param #table DetectedItem The DetectedItem. -- @param #DetectedItem DetectedItem The DetectedItem data structure.
self:AddTransition( "*", "Stop", "Stopped" ) self:AddTransition( "*", "Stop", "Stopped" )
@@ -591,7 +591,7 @@ do -- DETECTION_BASE
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
-- @param #string To The To State string. -- @param #string To The To State string.
-- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. -- @param Wrapper.Group#GROUP Detection The Group detecting.
-- @param #number DetectionTimeStamp Time stamp of detection event. -- @param #number DetectionTimeStamp Time stamp of detection event.
function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp ) function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
@@ -662,12 +662,13 @@ do -- DETECTION_BASE
local DetectedObjectVec3 = DetectedObject:getPoint() local DetectedObjectVec3 = DetectedObject:getPoint()
local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z }
local DetectionGroupVec3 = Detection:GetVec3() local DetectionGroupVec3 = Detection:GetVec3() or {x=0,y=0,z=0}
local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z }
local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 +
( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 +
(DetectedObjectVec3.z - DetectionGroupVec3.z) ^ 2) ^ 0.5 / 1000 ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2
) ^ 0.5 / 1000
local DetectedUnitCategory = DetectedObject:getDesc().category local DetectedUnitCategory = DetectedObject:getDesc().category
@@ -1981,7 +1982,7 @@ do -- DETECTION_UNITS
--- Will detect units within the battle zone. --- Will detect units within the battle zone.
-- --
-- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{Wrapper.Unit#UNIT} object reference.
-- Beware that when the amount of units detected is large, the DetectedItems list will be large also. -- Beware that when the amount of units detected is large, the DetectedItems list will be large also.
-- --
-- @field #DETECTION_UNITS -- @field #DETECTION_UNITS
@@ -1992,7 +1993,7 @@ do -- DETECTION_UNITS
--- DETECTION_UNITS constructor. --- DETECTION_UNITS constructor.
-- @param Functional.Detection#DETECTION_UNITS self -- @param Functional.Detection#DETECTION_UNITS self
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
-- @return Functional.Detection#DETECTION_UNITS self -- @return Functional.Detection#DETECTION_UNITS self
function DETECTION_UNITS:New( DetectionSetGroup ) function DETECTION_UNITS:New( DetectionSetGroup )
@@ -2151,8 +2152,9 @@ do -- DETECTION_UNITS
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
-- @param Core.Settings#SETTINGS Settings Message formatting settings to use. -- @param Core.Settings#SETTINGS Settings Message formatting settings to use.
-- @param #boolean ForceA2GCoordinate Set creation of A2G coordinate
-- @return Core.Report#REPORT The report of the detection items. -- @return Core.Report#REPORT The report of the detection items.
function DETECTION_UNITS:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings ) function DETECTION_UNITS:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings, ForceA2GCoordinate )
self:F( { DetectedItem = DetectedItem } ) self:F( { DetectedItem = DetectedItem } )
local DetectedItemID = self:GetDetectedItemID( DetectedItem ) local DetectedItemID = self:GetDetectedItemID( DetectedItem )
@@ -2188,6 +2190,10 @@ do -- DETECTION_UNITS
local DetectedItemCoordinate = self:GetDetectedItemCoordinate( DetectedItem ) local DetectedItemCoordinate = self:GetDetectedItemCoordinate( DetectedItem )
local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings ) local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings )
if ForceA2GCoordinate then
DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G(AttackGroup,Settings)
end
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem ) local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
local Report = REPORT:New() local Report = REPORT:New()
@@ -2231,7 +2237,7 @@ do -- DETECTION_TYPES
--- Will detect units within the battle zone. --- Will detect units within the battle zone.
-- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected.
-- Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{Wrapper.Unit#UNIT} object reference.
-- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also.
-- --
-- @field #DETECTION_TYPES -- @field #DETECTION_TYPES
@@ -2242,7 +2248,7 @@ do -- DETECTION_TYPES
--- DETECTION_TYPES constructor. --- DETECTION_TYPES constructor.
-- @param Functional.Detection#DETECTION_TYPES self -- @param Functional.Detection#DETECTION_TYPES self
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Recce role.
-- @return Functional.Detection#DETECTION_TYPES self -- @return Functional.Detection#DETECTION_TYPES self
function DETECTION_TYPES:New( DetectionSetGroup ) function DETECTION_TYPES:New( DetectionSetGroup )
@@ -2348,6 +2354,7 @@ do -- DETECTION_TYPES
if not DetectedItem then if not DetectedItem then
DetectedItem = self:AddDetectedItem( "TYPE", DetectedTypeName ) DetectedItem = self:AddDetectedItem( "TYPE", DetectedTypeName )
DetectedItem.TypeName = DetectedTypeName DetectedItem.TypeName = DetectedTypeName
DetectedItem.Name = DetectedUnitName -- fix by @Nocke
end end
DetectedItem.Set:AddUnit( DetectedUnit ) DetectedItem.Set:AddUnit( DetectedUnit )
@@ -2430,7 +2437,7 @@ do -- DETECTION_AREAS
--- @type DETECTION_AREAS --- @type DETECTION_AREAS
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
-- @extends Functional.Detection#DETECTION_BASE -- @extends Functional.Detection#DETECTION_BASE
--- Detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), --- Detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s),
@@ -2440,7 +2447,7 @@ do -- DETECTION_AREAS
-- --
-- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones
-- --
-- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DETECTION_BASE} and -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DECTECTION_BASE} and
-- the methods to manage the DetectedItems[].Zone(s) are implemented in @{Functional.Detection#DETECTION_AREAS}. -- the methods to manage the DetectedItems[].Zone(s) are implemented in @{Functional.Detection#DETECTION_AREAS}.
-- --
-- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned. -- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned.
@@ -2471,15 +2478,15 @@ do -- DETECTION_AREAS
--- DETECTION_AREAS constructor. --- DETECTION_AREAS constructor.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
-- @param DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @param #number DetectionZoneRange The range in meters within which targets are grouped upon the first detected target. Default 5000m.
-- @return #DETECTION_AREAS -- @return #DETECTION_AREAS
function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange )
-- Inherits from DETECTION_BASE -- Inherits from DETECTION_BASE
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) )
self.DetectionZoneRange = DetectionZoneRange self.DetectionZoneRange = DetectionZoneRange or 5000
self._SmokeDetectedUnits = false self._SmokeDetectedUnits = false
self._FlareDetectedUnits = false self._FlareDetectedUnits = false
@@ -2492,7 +2499,7 @@ do -- DETECTION_AREAS
--- Retrieve set of detected zones. --- Retrieve set of detected zones.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @return Core.Set#SET_ZONE The @{Set} of ZONE_UNIT objects detected. -- @return Core.Set#SET_ZONE The @{Core.Set} of ZONE_UNIT objects detected.
function DETECTION_AREAS:GetDetectionZones() function DETECTION_AREAS:GetDetectionZones()
local zoneset = SET_ZONE:New() local zoneset = SET_ZONE:New()
for _ID,_Item in pairs (self.DetectedItems) do for _ID,_Item in pairs (self.DetectedItems) do
@@ -2982,4 +2989,3 @@ do -- DETECTION_AREAS
end end
end end

View File

@@ -1,8 +1,12 @@
--- **Functional** - Captures the class DETECTION_ZONES.
-- @module Functional.DetectionZones
-- @image MOOSE.JPG
do -- DETECTION_ZONES do -- DETECTION_ZONES
--- @type DETECTION_ZONES --- @type DETECTION_ZONES
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
-- @extends Functional.Detection#DETECTION_BASE -- @extends Functional.Detection#DETECTION_BASE
--- (old, to be revised ) Detect units within the battle zone for a list of @{Core.Zone}s detecting targets following (a) detection method(s), --- (old, to be revised ) Detect units within the battle zone for a list of @{Core.Zone}s detecting targets following (a) detection method(s),
@@ -44,7 +48,7 @@ do -- DETECTION_ZONES
--- DETECTION_ZONES constructor. --- DETECTION_ZONES constructor.
-- @param #DETECTION_ZONES self -- @param #DETECTION_ZONES self
-- @param Core.Set#SET_ZONE DetectionSetZone The @{Set} of ZONE_RADIUS. -- @param Core.Set#SET_ZONE DetectionSetZone The @{Core.Set} of ZONE_RADIUS.
-- @param DCS#Coalition.side DetectionCoalition The coalition of the detection. -- @param DCS#Coalition.side DetectionCoalition The coalition of the detection.
-- @return #DETECTION_ZONES -- @return #DETECTION_ZONES
function DETECTION_ZONES:New( DetectionSetZone, DetectionCoalition ) function DETECTION_ZONES:New( DetectionSetZone, DetectionCoalition )

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Taking the lead of AI escorting your flight. --- **Functional** - Taking the lead of AI escorting your flight.
-- --
-- === -- ===
-- --

View File

@@ -1,4 +1,4 @@
--- **Functional** - (R2.5) - Yet Another Missile Trainer. --- **Functional** - Yet Another Missile Trainer.
-- --
-- --
-- Practice to evade missiles without being destroyed. -- Practice to evade missiles without being destroyed.
@@ -20,12 +20,13 @@
-- === -- ===
-- --
-- ### Author: **funkyfranky** -- ### Author: **funkyfranky**
-- @module Functional.FOX -- @module Functional.Fox
-- @image Functional_FOX.png -- @image Functional_FOX.png
--- FOX class. --- FOX class.
-- @type FOX -- @type FOX
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field #boolean Debug Debug mode. Messages to all about status. -- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #table menuadded Table of groups the menu was added for. -- @field #table menuadded Table of groups the menu was added for.
@@ -124,6 +125,7 @@
-- @field #FOX -- @field #FOX
FOX = { FOX = {
ClassName = "FOX", ClassName = "FOX",
verbose = 0,
Debug = false, Debug = false,
lid = nil, lid = nil,
menuadded = {}, menuadded = {},
@@ -168,7 +170,7 @@ FOX = {
--- Missile data table. --- Missile data table.
-- @type FOX.MissileData -- @type FOX.MissileData
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit. -- @field DCS#Weapon weapon Missile weapon object.
-- @field #boolean active If true the missile is active. -- @field #boolean active If true the missile is active.
-- @field #string missileType Type of missile. -- @field #string missileType Type of missile.
-- @field #string missileName Name of missile. -- @field #string missileName Name of missile.
@@ -185,6 +187,8 @@ FOX = {
-- @field #string targetName Name of the target unit or "unknown". -- @field #string targetName Name of the target unit or "unknown".
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched. -- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil. -- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
-- @field Core.Point#COORDINATE missileCoord Missile coordinate during tracking.
-- @field Wrapper.Weapon#WEAPON Weapon Weapon object.
--- Main radio menu on group level. --- Main radio menu on group level.
-- @field #table MenuF10 Root menu table on group level. -- @field #table MenuF10 Root menu table on group level.
@@ -196,7 +200,7 @@ FOX.MenuF10Root=nil
--- FOX class version. --- FOX class version.
-- @field #string version -- @field #string version
FOX.version="0.6.1" FOX.version="0.8.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -492,7 +496,6 @@ end
--- Disable F10 menu for all players. --- Disable F10 menu for all players.
-- @param #FOX self -- @param #FOX self
-- @param #boolean switch If true debug mode on. If false/nil debug mode off
-- @return #FOX self -- @return #FOX self
function FOX:SetDisableF10Menu() function FOX:SetDisableF10Menu()
@@ -501,6 +504,26 @@ function FOX:SetDisableF10Menu()
return self return self
end end
--- Enable F10 menu for all players.
-- @param #FOX self
-- @return #FOX self
function FOX:SetEnableF10Menu()
self.menudisabled=false
return self
end
--- Set verbosity level.
-- @param #FOX self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #FOX self
function FOX:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set default player setting for missile destruction. --- Set default player setting for missile destruction.
-- @param #FOX self -- @param #FOX self
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed. -- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
@@ -596,7 +619,9 @@ function FOX:onafterStatus(From, Event, To)
local clock=UTILS.SecondsToClock(time) local clock=UTILS.SecondsToClock(time)
-- Status. -- Status.
if self.verbose>=1 then
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate)) self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
end
-- Check missile status. -- Check missile status.
self:_CheckMissileStatus() self:_CheckMissileStatus()
@@ -704,7 +729,9 @@ function FOX:_CheckMissileStatus()
if #self.missiles==0 then if #self.missiles==0 then
text=text.." none" text=text.." none"
end end
if self.verbose>=2 then
self:I(self.lid..text) self:I(self.lid..text)
end
-- Remove inactive missiles. -- Remove inactive missiles.
for i=#self.missiles,1,-1 do for i=#self.missiles,1,-1 do
@@ -734,7 +761,7 @@ function FOX:_IsProtected(targetunit)
if targetgroup then if targetgroup then
local targetname=targetgroup:GetName() local targetname=targetgroup:GetName()
for _,_group in pairs(self.protectedset:GetSetObjects()) do for _,_group in pairs(self.protectedset:GetSet()) do
local group=_group --Wrapper.Group#GROUP local group=_group --Wrapper.Group#GROUP
if group then if group then
@@ -753,101 +780,25 @@ function FOX:_IsProtected(targetunit)
return false return false
end end
--- Missle launch event.
-- @param #FOX self --- Function called from weapon tracking.
-- @param #string From From state. -- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #string Event Event. -- @param #FOX self FOX object.
-- @param #string To To state.
-- @param #FOX.MissileData missile Fired missile -- @param #FOX.MissileData missile Fired missile
function FOX:onafterMissileLaunch(From, Event, To, missile) function FOX._FuncTrack(weapon, self, missile)
-- Tracking info and init of last bomb position.
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
self:I(FOX.lid..text)
MESSAGE:New(text, 10):ToAllIf(self.Debug)
-- Loop over players.
for _,_player in pairs(self.players) do
local player=_player --#FOX.PlayerData
-- Player position.
local playerUnit=player.unit
-- Check that player is alive and of the opposite coalition.
if playerUnit and playerUnit:IsAlive() and player.coalition~=missile.shooterCoalition then
-- Player missile distance.
local distance=playerUnit:GetCoordinate():Get3DDistance(missile.shotCoord)
-- Player bearing to missile.
local bearing=playerUnit:GetCoordinate():HeadingTo(missile.shotCoord)
-- Alert that missile has been launched.
if player.launchalert then
-- Alert directly targeted players or players that are within missile max range.
if (missile.targetPlayer and player.unitname==missile.targetPlayer.unitname) or (distance<missile.missileRange) then
-- Inform player.
local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing)
-- Say notching headings.
BASE:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon)
--TODO: ALERT or INFO depending on whether this is a direct target.
--TODO: lauchalertall option.
MESSAGE:New(text, 5, "ALERT"):ToClient(player.client)
end
end
-- Mark coordinate.
if player.marklaunch then
local text=string.format("Missile launch coordinates:\n%s\n%s", missile.shotCoord:ToStringLLDMS(), missile.shotCoord:ToStringBULLS(player.coalition))
missile.shotCoord:MarkToGroup(text, player.group)
end
end
end
-- Init missile position.
local _lastBombPos = {x=0,y=0,z=0}
-- Missile coordinate. -- Missile coordinate.
local missileCoord = nil --Core.Point#COORDINATE local missileCoord= missile.missileCoord:UpdateFromVec3(weapon.vec3) --COORDINATE:NewFromVec3(_lastBombPos)
-- Target unit of the missile.
local target=nil --Wrapper.Unit#UNIT
--- Function monitoring the position of a bomb until impact.
local function trackMissile(_ordnance)
-- When the pcall returns a failure the weapon has hit.
local _status,_bombPos = pcall(
function()
return _ordnance:getPoint()
end)
-- Check if status is not nil. If so, we have a valid point.
if _status then
----------------------------------------------
-- Still in the air. Remember this position --
----------------------------------------------
-- Missile position.
_lastBombPos = {x=_bombPos.x, y=_bombPos.y, z=_bombPos.z}
-- Missile coordinate.
missileCoord=COORDINATE:NewFromVec3(_lastBombPos)
-- Missile velocity in m/s. -- Missile velocity in m/s.
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity()) local missileVelocity=weapon:GetSpeed() --UTILS.VecNorm(_ordnance:getVelocity())
-- Update missile target if necessary. -- Update missile target if necessary.
self:GetMissileTarget(missile) self:GetMissileTarget(missile)
-- Target unit of the missile.
local target=nil --Wrapper.Unit#UNIT
if missile.targetUnit then if missile.targetUnit then
----------------------------------- -----------------------------------
@@ -938,13 +889,13 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
if unit:GetName()~=missile.shooterName then if unit:GetName()~=missile.shooterName then
-- Player position. -- Player position.
local playerCoord=unit:GetCoordinate() local playerVec3=unit:GetVec3()
-- Distance. -- Distance.
local dist=missileCoord:Get3DDistance(playerCoord) local dist=missileCoord:Get3DDistance(playerVec3)
-- Distance from shooter to player. -- Distance from shooter to player.
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord) local Dshooter2player=missile.shotCoord:Get3DDistance(playerVec3)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin. -- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
@@ -968,23 +919,22 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
if target then if target then
-- Target coordinate. -- Target coordinate.
local targetCoord=target:GetCoordinate() local targetVec3=target:GetVec3() --target:GetCoordinate()
-- Distance from missile to target. -- Distance from missile to target.
local distance=missileCoord:Get3DDistance(targetCoord) local distance=missileCoord:Get3DDistance(targetVec3)
-- Distance missile to shooter. -- Distance missile to shooter.
local distShooter=nil local distShooter=nil
if missile.shooterUnit and missile.shooterUnit:IsAlive() then if missile.shooterUnit and missile.shooterUnit:IsAlive() then
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate()) distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetVec3())
end end
-- Debug output. -- Debug output.
if self.Debug then if self.Debug then
local bearing=targetCoord:HeadingTo(missileCoord) local bearing=missileCoord:HeadingTo(targetVec3)
local eta=distance/missileVelocity local eta=distance/missileVelocity
-- Debug distance check. -- Debug distance check.
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta)) self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
end end
@@ -998,12 +948,12 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
end end
-- If missile is 150 m from target ==> destroy missile if in safe zone. -- If missile is 150 m from target ==> destroy missile if in safe zone.
if destroymissile and self:_CheckCoordSafe(targetCoord) then if destroymissile and self:_CheckCoordSafe(targetVec3) then
-- Destroy missile. -- Destroy missile.
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m", self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance)) missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
_ordnance:destroy() weapon:Destroy()
-- Missile is not active any more. -- Missile is not active any more.
missile.active=false missile.active=false
@@ -1011,7 +961,6 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
-- Debug smoke. -- Debug smoke.
if self.Debug then if self.Debug then
missileCoord:SmokeRed() missileCoord:SmokeRed()
targetCoord:SmokeGreen()
end end
-- Create event. -- Create event.
@@ -1033,8 +982,8 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
missile.targetPlayer.dead=missile.targetPlayer.dead+1 missile.targetPlayer.dead=missile.targetPlayer.dead+1
end end
-- Terminate timer. -- We could disable the tracking here but then the impact function would not be called.
return nil --weapon.tracking=false
else else
@@ -1057,30 +1006,30 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
dt=self.dt00 --0.01 dt=self.dt00 --0.01
end end
-- Check again in dt seconds. -- Set time step.
return timer.getTime()+dt weapon:SetTimeStepTrack(dt)
end end
else else
-- Destroy missile. -- No current target.
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName)) self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
return timer.getTime()+0.1 weapon:SetTimeStepTrack(0.1)
-- No target ==> terminate timer.
--return nil
end end
else end
------------------------------------- --- Callback function on impact or destroy otherwise.
-- Missile does not exist any more -- -- @param Wrapper.Weapon#WEAPON weapon Weapon object.
------------------------------------- -- @param #FOX self FOX object.
-- @param #FOX.MissileData missile Fired missile.
function FOX._FuncImpact(weapon, self, missile)
if target then if missile.targetPlayer then
-- Get human player. -- Get human player.
local player=self:_GetPlayerFromUnit(target) local player=missile.targetPlayer
-- Check for player and distance < 10 km. -- Check for player and distance < 10 km.
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
@@ -1098,15 +1047,79 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
--Terminate the timer. --Terminate the timer.
self:T(FOX.lid..string.format("Terminating missile track timer.")) self:T(FOX.lid..string.format("Terminating missile track timer."))
return nil weapon.tracking=false
end -- _status check end
--- Missle launch event.
-- @param #FOX self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #FOX.MissileData missile Fired missile
function FOX:onafterMissileLaunch(From, Event, To, missile)
-- Tracking info and init of last bomb position.
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
self:I(FOX.lid..text)
MESSAGE:New(text, 10):ToAllIf(self.Debug)
-- Loop over players.
for _,_player in pairs(self.players) do
local player=_player --#FOX.PlayerData
-- Player position.
local playerUnit=player.unit
-- Check that player is alive and of the opposite coalition.
if playerUnit and playerUnit:IsAlive() and player.coalition~=missile.shooterCoalition then
-- Player missile distance.
local distance=playerUnit:GetCoordinate():Get3DDistance(missile.shotCoord)
-- Player bearing to missile.
local bearing=playerUnit:GetCoordinate():HeadingTo(missile.shotCoord)
-- Alert that missile has been launched.
if player.launchalert then
-- Alert directly targeted players or players that are within missile max range.
if (missile.targetPlayer and player.unitname==missile.targetPlayer.unitname) or (distance<missile.missileRange) then
-- Inform player.
local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing)
-- Say notching headings.
self:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon)
--TODO: ALERT or INFO depending on whether this is a direct target.
--TODO: lauchalertall option.
MESSAGE:New(text, 5, "ALERT"):ToClient(player.client)
end
end
-- Mark coordinate.
if player.marklaunch then
local text=string.format("Missile launch coordinates:\n%s\n%s", missile.shotCoord:ToStringLLDMS(), missile.shotCoord:ToStringBULLS(player.coalition))
missile.shotCoord:MarkToGroup(text, player.group)
end
end
end
-- Set callback function for tracking.
missile.Weapon:SetFuncTrack(FOX._FuncTrack, self, missile)
-- Set callback function for impact.
missile.Weapon:SetFuncImpact(FOX._FuncImpact, self, missile)
end -- end function trackBomb
-- Weapon is not yet "alife" just yet. Start timer with a little delay. -- Weapon is not yet "alife" just yet. Start timer with a little delay.
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds.")) self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001) --timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
missile.Weapon:StartTrack(0.0001)
end end
@@ -1114,6 +1127,13 @@ end
-- Event Functions -- Event Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- FOX event handler for event birth.
-- @param #FOX self
-- @param Core.Event#EVENTDATA EventData
function FOX:OnEventPlayerEnterAircraft(EventData)
end
--- FOX event handler for event birth. --- FOX event handler for event birth.
-- @param #FOX self -- @param #FOX self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -1155,7 +1175,7 @@ function FOX:OnEventBirth(EventData)
-- Add F10 radio menu for player. -- Add F10 radio menu for player.
if not self.menudisabled then if not self.menudisabled then
SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1) self:ScheduleOnce(0.1, self._AddF10Commands, self, _unitName)
end end
-- Player data. -- Player data.
@@ -1231,29 +1251,28 @@ end
function FOX:OnEventShot(EventData) function FOX:OnEventShot(EventData)
self:T2({eventshot=EventData}) self:T2({eventshot=EventData})
if EventData.Weapon==nil then -- Nil checks.
return if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.weapon==nil then
end
if EventData.IniDCSUnit==nil then
return return
end end
-- Create a weapon object.
local weapon=WEAPON:New(EventData.weapon)
-- Weapon data. -- Weapon data.
local _weapon = EventData.WeaponName local _weapon = weapon:GetTypeName()
local _target = EventData.Weapon:getTarget() local _target = EventData.Weapon:getTarget()
local _targetName = "unknown" local _targetName = "unknown"
local _targetUnit = nil --Wrapper.Unit#UNIT local _targetUnit = nil --Wrapper.Unit#UNIT
-- Weapon descriptor. -- Weapon descriptor.
local desc=EventData.Weapon:getDesc() local desc=weapon.desc
self:T2({desc=desc}) self:T2({desc=desc})
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
local weaponcategory=desc.category
-- Missile category: 1=AAM, 2=SAM, 6=OTHER -- Missile category: 1=AAM, 2=SAM, 6=OTHER
local missilecategory=desc.missileCategory local missilecategory=desc.missileCategory
-- Missile range.
local missilerange=nil local missilerange=nil
if missilecategory then if missilecategory then
missilerange=desc.rangeMaxAltMax missilerange=desc.rangeMaxAltMax
@@ -1263,8 +1282,8 @@ function FOX:OnEventShot(EventData)
self:T2(FOX.lid.."EVENT SHOT: FOX") self:T2(FOX.lid.."EVENT SHOT: FOX")
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName))) self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName))) self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(_weapon))) self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(weapon:GetTypeName())))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weaponcategory))) self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weapon:GetCategory())))
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory))) self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange))) self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
@@ -1276,7 +1295,7 @@ function FOX:OnEventShot(EventData)
end end
-- Track missiles of type AAM=1, SAM=2 or OTHER=6 -- Track missiles of type AAM=1, SAM=2 or OTHER=6
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6) local _track = weapon:IsMissile() and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
-- Only track missiles -- Only track missiles
if _track then if _track then
@@ -1285,6 +1304,7 @@ function FOX:OnEventShot(EventData)
missile.active=true missile.active=true
missile.weapon=EventData.weapon missile.weapon=EventData.weapon
missile.Weapon=weapon
missile.missileType=_weapon missile.missileType=_weapon
missile.missileRange=missilerange missile.missileRange=missilerange
missile.missileName=EventData.weapon:getName() missile.missileName=EventData.weapon:getName()
@@ -1297,6 +1317,7 @@ function FOX:OnEventShot(EventData)
missile.fuseDist=desc.fuseDist missile.fuseDist=desc.fuseDist
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
missile.targetOrig=missile.targetName missile.targetOrig=missile.targetName
missile.missileCoord=COORDINATE:New(0,0,0)
-- Set missile target name, unit and player. -- Set missile target name, unit and player.
self:GetMissileTarget(missile) self:GetMissileTarget(missile)
@@ -1422,10 +1443,10 @@ function FOX:_AddF10Commands(_unitName)
end end
else else
self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.", _unitName)) self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.", _unitName or "unknown"))
end end
else else
self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.", _unitName)) self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.", _unitName or "unknown"))
end end
end end
@@ -1615,7 +1636,7 @@ end
--- Check if a coordinate lies within a safe training zone. --- Check if a coordinate lies within a safe training zone.
-- @param #FOX self -- @param #FOX self
-- @param Core.Point#COORDINATE coord Coordinate to check. -- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec3.
-- @return #boolean True if safe. -- @return #boolean True if safe.
function FOX:_CheckCoordSafe(coord) function FOX:_CheckCoordSafe(coord)
@@ -1627,7 +1648,9 @@ function FOX:_CheckCoordSafe(coord)
-- Loop over all zones. -- Loop over all zones.
for _,_zone in pairs(self.safezones) do for _,_zone in pairs(self.safezones) do
local zone=_zone --Core.Zone#ZONE local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsCoordinateInZone(coord) local Vec2={x=coord.x, y=coord.z}
local inzone=zone:IsVec2InZone(Vec2)
--local inzone=zone:IsCoordinateInZone(coord)
if inzone then if inzone then
return true return true
end end
@@ -1638,7 +1661,7 @@ end
--- Check if a coordinate lies within a launch zone. --- Check if a coordinate lies within a launch zone.
-- @param #FOX self -- @param #FOX self
-- @param Core.Point#COORDINATE coord Coordinate to check. -- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec2.
-- @return #boolean True if in launch zone. -- @return #boolean True if in launch zone.
function FOX:_CheckCoordLaunch(coord) function FOX:_CheckCoordLaunch(coord)
@@ -1650,7 +1673,9 @@ function FOX:_CheckCoordLaunch(coord)
-- Loop over all zones. -- Loop over all zones.
for _,_zone in pairs(self.launchzones) do for _,_zone in pairs(self.launchzones) do
local zone=_zone --Core.Zone#ZONE local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsCoordinateInZone(coord) local Vec2={x=coord.x, y=coord.z}
local inzone=zone:IsVec2InZone(Vec2)
--local inzone=zone:IsCoordinateInZone(coord)
if inzone then if inzone then
return true return true
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
--- **Functional** -- Train missile defence and deflection. --- **Functional** - Train missile defence and deflection.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. -- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range ° -- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range
-- * Provide alerts when a missile would have killed your aircraft. -- * Provide alerts when a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs. -- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options. -- * Enable / Disable and Configure the Missile Trainer using the various menu options.
@@ -28,14 +28,14 @@
-- * **Messages Off**: Disable all messages. -- * **Messages Off**: Disable all messages.
-- * **Tracking**: Menu to configure missile tracking messages. -- * **Tracking**: Menu to configure missile tracking messages.
-- * **To All**: Shows missile tracking messages to all players. -- * **To All**: Shows missile tracking messages to all players.
-- * **To Target**: Shows missile tracking messages only to the player where the missile is targetted at. -- * **To Target**: Shows missile tracking messages only to the player where the missile is targeted at.
-- * **Tracking On**: Show missile tracking messages. -- * **Tracking On**: Show missile tracking messages.
-- * **Tracking Off**: Disable missile tracking messages. -- * **Tracking Off**: Disable missile tracking messages.
-- * **Frequency Increase**: Increases the missile tracking message frequency with one second. -- * **Frequency Increase**: Increases the missile tracking message frequency with one second.
-- * **Frequency Decrease**: Decreases the missile tracking message frequency with one second. -- * **Frequency Decrease**: Decreases the missile tracking message frequency with one second.
-- * **Alerts**: Menu to configure alert messages. -- * **Alerts**: Menu to configure alert messages.
-- * **To All**: Shows alert messages to all players. -- * **To All**: Shows alert messages to all players.
-- * **To Target**: Shows alert messages only to the player where the missile is (was) targetted at. -- * **To Target**: Shows alert messages only to the player where the missile is (was) targeted at.
-- * **Hits On**: Show missile hit alert messages. -- * **Hits On**: Show missile hit alert messages.
-- * **Hits Off**: Disable missile hit alert messages. -- * **Hits Off**: Disable missile hit alert messages.
-- * **Launches On**: Show missile launch messages. -- * **Launches On**: Show missile launch messages.
@@ -88,7 +88,7 @@
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods: -- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
-- --
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF. -- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
-- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targetted to you. -- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targeted to you.
-- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF. -- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF.
-- * @{#MISSILETRAINER.InitTrackingFrequency}: Increases, decreases the missile tracking message display frequency with the provided time interval in seconds. -- * @{#MISSILETRAINER.InitTrackingFrequency}: Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
-- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you. -- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you.
@@ -256,7 +256,7 @@ function MISSILETRAINER:InitMessagesOnOff( MessagesOnOff )
return self return self
end end
--- Sets by default the missile tracking report for all players or only for those missiles targetted to you. --- Sets by default the missile tracking report for all players or only for those missiles targeted to you.
-- @param #MISSILETRAINER self -- @param #MISSILETRAINER self
-- @param #boolean TrackingToAll true or false -- @param #boolean TrackingToAll true or false
-- @return #MISSILETRAINER self -- @return #MISSILETRAINER self

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Limit the movement of simulaneous moving ground vehicles. --- **Functional** - Limit the movement of simulaneous moving ground vehicles.
-- --
-- === -- ===
-- --
@@ -36,9 +36,9 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum )
else else
self.MovePrefixes = { MovePrefixes } self.MovePrefixes = { MovePrefixes }
end end
self.MoveCount = 0 -- The internal counter of the amount of Moveing the has happened since MoveStart. self.MoveCount = 0 -- The internal counter of the amount of Moving the has happened since MoveStart.
self.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move... self.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move.
self.AliveUnits = 0 -- Contains the counter how many units are currently alive self.AliveUnits = 0 -- Contains the counter how many units are currently alive.
self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not.
self:HandleEvent( EVENTS.Birth ) self:HandleEvent( EVENTS.Birth )
@@ -67,7 +67,7 @@ function MOVEMENT:ScheduleStop()
end end
--- Captures the birth events when new Units were spawned. --- Captures the birth events when new Units were spawned.
-- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -- @todo This method should become obsolete. The global _DATABASE object (an instance of @{Core.Database#DATABASE}) will handle the collection administration.
-- @param #MOVEMENT self -- @param #MOVEMENT self
-- @param Core.Event#EVENTDATA self -- @param Core.Event#EVENTDATA self
function MOVEMENT:OnEventBirth( EventData ) function MOVEMENT:OnEventBirth( EventData )
@@ -93,7 +93,7 @@ function MOVEMENT:OnEventBirth( EventData )
end end
--- Captures the Dead or Crash events when Units crash or are destroyed. --- Captures the Dead or Crash events when Units crash or are destroyed.
-- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -- @todo This method should become obsolete. The global _DATABASE object (an instance of @{Core.Database#DATABASE}) will handle the collection administration.
function MOVEMENT:OnDeadOrCrash( Event ) function MOVEMENT:OnDeadOrCrash( Event )
self:F( { Event } ) self:F( { Event } )

View File

@@ -1,4 +1,4 @@
--- **Functional** - Rudimentary ATC. --- **Functional** - Basic ATC.
-- --
-- ![Banner Image](..\Presentations\PSEUDOATC\PSEUDOATC_Main.jpg) -- ![Banner Image](..\Presentations\PSEUDOATC\PSEUDOATC_Main.jpg)
-- --
@@ -26,9 +26,9 @@
-- --
-- === -- ===
-- --
-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- ### Author: **funkyfranky**
-- --
-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- ### Contributions: FlightControl, Applevangelist
-- --
-- ==== -- ====
-- @module Functional.PseudoATC -- @module Functional.PseudoATC
@@ -44,7 +44,8 @@
-- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. Default is 120 sec. -- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. Default is 120 sec.
-- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec. -- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec.
-- @field #boolean chatty Display some messages on events like take-off and touchdown. -- @field #boolean chatty Display some messages on events like take-off and touchdown.
-- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @field #boolean eventsmoose [Deprecated] If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler.
-- @field #boolean reportplayername If true, use playername not callsign on callouts
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Adds some rudimentary ATC functionality via the radio menu. --- Adds some rudimentary ATC functionality via the radio menu.
@@ -88,6 +89,7 @@ PSEUDOATC={
talt=3, talt=3,
chatty=true, chatty=true,
eventsmoose=true, eventsmoose=true,
reportplayername = false,
} }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -98,13 +100,14 @@ PSEUDOATC.id="PseudoATC | "
--- PSEUDOATC version. --- PSEUDOATC version.
-- @field #number version -- @field #number version
PSEUDOATC.version="0.9.2" PSEUDOATC.version="0.10.5"
----------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
-- DONE: Add takeoff event. -- DONE: Add takeoff event.
-- DONE: Add user functions. -- DONE: Add user functions.
-- DONE: Refactor to use Moose event handling only
----------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------
@@ -129,23 +132,14 @@ function PSEUDOATC:Start()
self:F() self:F()
-- Debug info -- Debug info
self:E(PSEUDOATC.id.."Starting PseudoATC") self:I(PSEUDOATC.id.."Starting PseudoATC")
-- Handle events. -- Handle events.
if self.eventsmoose then
self:T(PSEUDOATC.id.."Events are handled by MOOSE.")
self:HandleEvent(EVENTS.Birth, self._OnBirth) self:HandleEvent(EVENTS.Birth, self._OnBirth)
self:HandleEvent(EVENTS.Land, self._PlayerLanded) self:HandleEvent(EVENTS.Land, self._PlayerLanded)
self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff) self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff)
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft)
self:HandleEvent(EVENTS.Crash, self._PlayerLeft) self:HandleEvent(EVENTS.Crash, self._PlayerLeft)
--self:HandleEvent(EVENTS.Ejection, self._PlayerLeft)
--self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft)
else
self:T(PSEUDOATC.id.."Events are handled by DCS.")
-- Events are handled directly by DCS.
world.addEventHandler(self)
end
end end
@@ -183,6 +177,13 @@ function PSEUDOATC:SetMessageDuration(duration)
self.mdur=duration or 30 self.mdur=duration or 30
end end
--- Use player name, not call sign, in callouts
-- @param #PSEUDOATC self
function PSEUDOATC:SetReportPlayername()
self.reportplayername = true
return self
end
--- Set time interval after which the F10 radio menu is refreshed. --- Set time interval after which the F10 radio menu is refreshed.
-- @param #PSEUDOATC self -- @param #PSEUDOATC self
-- @param #number interval Interval in seconds. Default is every 120 sec. -- @param #number interval Interval in seconds. Default is every 120 sec.
@@ -190,7 +191,7 @@ function PSEUDOATC:SetMenuRefresh(interval)
self.mrefresh=interval or 120 self.mrefresh=interval or 120
end end
--- Enable/disable event handling by MOOSE or DCS. --- [Deprecated] Enable/disable event handling by MOOSE or DCS.
-- @param #PSEUDOATC self -- @param #PSEUDOATC self
-- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS. -- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS.
function PSEUDOATC:SetEventsMoose(switch) function PSEUDOATC:SetEventsMoose(switch)
@@ -207,84 +208,6 @@ end
----------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------
-- Event Handling -- Event Handling
--- Event handler for suppressed groups.
--@param #PSEUDOATC self
--@param #table Event Event data table. Holds event.id, event.initiator and event.target etc.
function PSEUDOATC:onEvent(Event)
if Event == nil or Event.initiator == nil or Unit.getByName(Event.initiator:getName()) == nil then
return true
end
local DCSiniunit = Event.initiator
local DCSplace = Event.place
local DCSsubplace = Event.subplace
local EventData={}
local _playerunit=nil
local _playername=nil
if Event.initiator then
EventData.IniUnitName = Event.initiator:getName()
EventData.IniDCSGroup = Event.initiator:getGroup()
EventData.IniGroupName = Event.initiator:getGroup():getName()
-- Get player unit and name. This returns nil,nil if the event was not fired by a player unit. And these are the only events we are interested in.
_playerunit, _playername = self:_GetPlayerUnitAndName(EventData.IniUnitName)
end
if Event.place then
EventData.Place=Event.place
EventData.PlaceName=Event.place:getName()
end
if Event.subplace then
EventData.SubPlace=Event.subplace
EventData.SubPlaceName=Event.subplace:getName()
end
-- Event info.
self:T3(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s", tostring(Event.id)))
self:T3(PSEUDOATC.id..string.format("EVENT: Ini unit = %s" , tostring(EventData.IniUnitName)))
self:T3(PSEUDOATC.id..string.format("EVENT: Ini group = %s" , tostring(EventData.IniGroupName)))
self:T3(PSEUDOATC.id..string.format("EVENT: Ini player = %s" , tostring(_playername)))
self:T3(PSEUDOATC.id..string.format("EVENT: Place = %s" , tostring(EventData.PlaceName)))
self:T3(PSEUDOATC.id..string.format("EVENT: SubPlace = %s" , tostring(EventData.SubPlaceName)))
-- Event birth.
if Event.id == world.event.S_EVENT_BIRTH and _playername then
self:_OnBirth(EventData)
end
-- Event takeoff.
if Event.id == world.event.S_EVENT_TAKEOFF and _playername and EventData.Place then
self:_PlayerTakeOff(EventData)
end
-- Event land.
if Event.id == world.event.S_EVENT_LAND and _playername and EventData.Place then
self:_PlayerLanded(EventData)
end
-- Event player left unit
if Event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and _playername then
self:_PlayerLeft(EventData)
end
-- Event crash ==> player left unit
if Event.id == world.event.S_EVENT_CRASH and _playername then
self:_PlayerLeft(EventData)
end
--[[
-- Event eject ==> player left unit
if Event.id == world.event.S_EVENT_EJECTION and _playername then
self:_PlayerLeft(EventData)
end
-- Event pilot dead ==> player left unit
if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then
self:_PlayerLeft(EventData)
end
]]
end
--- Function called my MOOSE event handler when a player enters a unit. --- Function called my MOOSE event handler when a player enters a unit.
-- @param #PSEUDOATC self -- @param #PSEUDOATC self
@@ -294,7 +217,9 @@ function PSEUDOATC:_OnBirth(EventData)
-- Get unit and player. -- Get unit and player.
local _unitName=EventData.IniUnitName local _unitName=EventData.IniUnitName
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) --local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
local _unit = EventData.IniUnit
local _playername = EventData.IniPlayerName
-- Check if a player entered. -- Check if a player entered.
if _unit and _playername then if _unit and _playername then
@@ -311,7 +236,10 @@ function PSEUDOATC:_PlayerLeft(EventData)
-- Get unit and player. -- Get unit and player.
local _unitName=EventData.IniUnitName local _unitName=EventData.IniUnitName
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) --local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
local _unit = EventData.IniUnit
local _playername = EventData.IniPlayerName
-- Check if a player left. -- Check if a player left.
if _unit and _playername then if _unit and _playername then
@@ -327,17 +255,15 @@ function PSEUDOATC:_PlayerLanded(EventData)
-- Get unit, player and place. -- Get unit, player and place.
local _unitName=EventData.IniUnitName local _unitName=EventData.IniUnitName
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) local _unit = EventData.IniUnit
local _playername = EventData.IniPlayerName
--local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
local _base=nil local _base=nil
local _baseName=nil local _baseName=nil
if EventData.place then if EventData.place then
_base=EventData.place _base=EventData.place
_baseName=EventData.place:getName() _baseName=EventData.place:getName()
end end
-- if EventData.subplace then
-- local _subPlace=EventData.subplace
-- local _subPlaceName=EventData.subplace:getName()
-- end
-- Call landed function. -- Call landed function.
if _unit and _playername and _base then if _unit and _playername and _base then
@@ -353,7 +279,9 @@ function PSEUDOATC:_PlayerTakeOff(EventData)
-- Get unit, player and place. -- Get unit, player and place.
local _unitName=EventData.IniUnitName local _unitName=EventData.IniUnitName
local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) local _unit = EventData.IniUnit
local _playername = EventData.IniPlayerName
--local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
local _base=nil local _base=nil
local _baseName=nil local _baseName=nil
if EventData.place then if EventData.place then
@@ -441,14 +369,15 @@ function PSEUDOATC:PlayerLanded(unit, place)
local group=unit:GetGroup() local group=unit:GetGroup()
local GID=group:GetID() local GID=group:GetID()
local UID=unit:GetDCSObject():getID() local UID=unit:GetDCSObject():getID()
local PlayerName=self.group[GID].player[UID].playername local PlayerName = unit:GetPlayerName() or "Ghost"
local UnitName=self.group[GID].player[UID].unitname local UnitName = unit:GetName() or "Ghostplane"
local GroupName=self.group[GID].player[UID].groupname local GroupName = group:GetName() or "Ghostgroup"
if self.Debug then
-- Debug message. -- Debug message.
local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, GID, place) local text=string.format("Player %s in unit %s of group %s landed at %s.", PlayerName, UnitName, GroupName, place)
self:T(PSEUDOATC.id..text) self:T(PSEUDOATC.id..text)
MESSAGE:New(text, 30):ToAllIf(self.Debug) MESSAGE:New(text, 30):ToAllIf(self.Debug)
end
-- Stop altitude reporting timer if its activated. -- Stop altitude reporting timer if its activated.
self:AltitudeTimerStop(GID,UID) self:AltitudeTimerStop(GID,UID)
@@ -470,21 +399,22 @@ function PSEUDOATC:PlayerTakeOff(unit, place)
-- Gather some information. -- Gather some information.
local group=unit:GetGroup() local group=unit:GetGroup()
local GID=group:GetID() local PlayerName = unit:GetPlayerName() or "Ghost"
local UID=unit:GetDCSObject():getID() local UnitName = unit:GetName() or "Ghostplane"
local PlayerName=self.group[GID].player[UID].playername local GroupName = group:GetName() or "Ghostgroup"
local CallSign=self.group[GID].player[UID].callsign local CallSign = unit:GetCallsign() or "Ghost11"
local UnitName=self.group[GID].player[UID].unitname if self.Debug then
local GroupName=self.group[GID].player[UID].groupname
-- Debug message. -- Debug message.
local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, GID, place) local text=string.format("Player %s in unit %s of group %s took off at %s.", PlayerName, UnitName, GroupName, place)
self:T(PSEUDOATC.id..text) self:T(PSEUDOATC.id..text)
MESSAGE:New(text, 30):ToAllIf(self.Debug) MESSAGE:New(text, 30):ToAllIf(self.Debug)
end
-- Bye-Bye message. -- Bye-Bye message.
if place and self.chatty then if place and self.chatty then
local text=string.format("%s, %s, you are airborne. Have a safe trip!", place, CallSign) local text=string.format("%s, %s, you are airborne. Have a safe trip!", place, CallSign)
if self.reportplayername then
text=string.format("%s, %s, you are airborne. Have a safe trip!", place, PlayerName)
end
MESSAGE:New(text, self.mdur):ToGroup(group) MESSAGE:New(text, self.mdur):ToGroup(group)
end end
@@ -501,7 +431,7 @@ function PSEUDOATC:PlayerLeft(unit)
local GID=group:GetID() local GID=group:GetID()
local UID=unit:GetDCSObject():getID() local UID=unit:GetDCSObject():getID()
if self.group[GID].player[UID] then if self.group[GID] and self.group[GID].player and self.group[GID].player[UID] then
local PlayerName=self.group[GID].player[UID].playername local PlayerName=self.group[GID].player[UID].playername
local CallSign=self.group[GID].player[UID].callsign local CallSign=self.group[GID].player[UID].callsign
local UnitName=self.group[GID].player[UID].unitname local UnitName=self.group[GID].player[UID].unitname
@@ -687,7 +617,9 @@ function PSEUDOATC:MenuWaypoints(GID, UID)
-- Position of Waypoint -- Position of Waypoint
local pos=COORDINATE:New(wp.x, wp.alt, wp.y) local pos=COORDINATE:New(wp.x, wp.alt, wp.y)
local name=string.format("Waypoint %d", i-1) local name=string.format("Waypoint %d", i-1)
if wp.name and wp.name ~= "" then
name = string.format("Waypoint %s",wp.name)
end
-- "F10/PseudoATC/Waypoints/Waypoint X" -- "F10/PseudoATC/Waypoints/Waypoint X"
local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_waypoints) local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_waypoints)
@@ -844,6 +776,7 @@ function PSEUDOATC:ReportHeight(GID, UID, dt, _clear)
local position=unit:GetCoordinate() local position=unit:GetCoordinate()
local height=get_AGL(position) local height=get_AGL(position)
local callsign=unit:GetCallsign() local callsign=unit:GetCallsign()
local PlayerName=self.group[GID].player[UID].playername
-- Settings. -- Settings.
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
@@ -856,7 +789,9 @@ function PSEUDOATC:ReportHeight(GID, UID, dt, _clear)
-- Message text. -- Message text.
local _text=string.format("%s, your altitude is %s AGL.", callsign, Hs) local _text=string.format("%s, your altitude is %s AGL.", callsign, Hs)
if self.reportplayername then
_text=string.format("%s, your altitude is %s AGL.", PlayerName, Hs)
end
-- Append flight level. -- Append flight level.
if _clear==false then if _clear==false then
_text=_text..string.format(" FL%03d.", position.y/30.48) _text=_text..string.format(" FL%03d.", position.y/30.48)
@@ -901,7 +836,7 @@ function PSEUDOATC:AltitudeTimeStart(GID, UID)
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID)) self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID))
-- Start timer. Altitude is reported every ~3 seconds. -- Start timer. Altitude is reported every ~3 seconds.
self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 0.1, true}, 1, 3) self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 1, true}, 1, 3)
end end
--- Stop/destroy DCS scheduler function for reporting altitude. --- Stop/destroy DCS scheduler function for reporting altitude.
@@ -949,11 +884,14 @@ function PSEUDOATC:LocalAirports(GID, UID)
for _,airbase in pairs(airports) do for _,airbase in pairs(airports) do
local name=airbase:getName() local name=airbase:getName()
local q=AIRBASE:FindByName(name):GetCoordinate() local a=AIRBASE:FindByName(name)
if a then
local q=a:GetCoordinate()
local d=q:Get2DDistance(pos) local d=q:Get2DDistance(pos)
-- Add to table. -- Add to table.
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name}) table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
end
end end
end end
@@ -1041,6 +979,3 @@ function PSEUDOATC:_myname(unitname)
return string.format("%s (%s)", csign, pname) return string.format("%s (%s)", csign, pname)
end end

View File

@@ -51,7 +51,7 @@
-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)
-- --
-- === -- ===
-- @module Functional.Rat -- @module Functional.RAT
-- @image RAT.JPG -- @image RAT.JPG
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2049,9 +2049,15 @@ function RAT:_InitAircraft(DCSgroup)
--self.aircraft.descriptors=DCSdesc --self.aircraft.descriptors=DCSdesc
-- aircraft dimensions -- aircraft dimensions
if DCSdesc.box then
self.aircraft.length=DCSdesc.box.max.x self.aircraft.length=DCSdesc.box.max.x
self.aircraft.height=DCSdesc.box.max.y self.aircraft.height=DCSdesc.box.max.y
self.aircraft.width=DCSdesc.box.max.z self.aircraft.width=DCSdesc.box.max.z
elseif DCStype == "Mirage-F1CE" then
self.aircraft.length=16
self.aircraft.height=5
self.aircraft.width=9
end
self.aircraft.box=math.max(self.aircraft.length,self.aircraft.width) self.aircraft.box=math.max(self.aircraft.length,self.aircraft.width)
-- info message -- info message
@@ -3486,7 +3492,7 @@ function RAT:Status(message, forID)
local fuel=group:GetFuel()*100.0 local fuel=group:GetFuel()*100.0
local airborne=group:InAir() local airborne=group:InAir()
local coords=group:GetCoordinate() local coords=group:GetCoordinate()
local alt=coords.y local alt=coords.y or 1000
--local vel=group:GetVelocityKMH() --local vel=group:GetVelocityKMH()
local departure=ratcraft.departure:GetName() local departure=ratcraft.departure:GetName()
local destination=ratcraft.destination:GetName() local destination=ratcraft.destination:GetName()
@@ -5371,7 +5377,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
if spawnonground then if spawnonground then
-- Sh°ps and FARPS seem to have a build in queue. -- Ships and FARPS seem to have a build in queue.
if spawnonship or spawnonfarp or spawnonrunway or automatic then if spawnonship or spawnonfarp or spawnonrunway or automatic then
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName())) self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
@@ -5437,7 +5443,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
SpawnTemplate.units[UnitID]["onboard_num"] = string.format("%s%d%02d", self.onboardnum, (self.SpawnIndex-1)%10, (self.onboardnum0-1)+UnitID) SpawnTemplate.units[UnitID]["onboard_num"] = string.format("%s%d%02d", self.onboardnum, (self.SpawnIndex-1)%10, (self.onboardnum0-1)+UnitID)
end end
-- Modify coaltion and country of template. -- Modify coalition and country of template.
SpawnTemplate.CoalitionID=self.coalition SpawnTemplate.CoalitionID=self.coalition
if self.country then if self.country then
SpawnTemplate.CountryID=self.country SpawnTemplate.CountryID=self.country
@@ -5665,6 +5671,9 @@ function RAT:_ATCClearForLanding(airport, flight)
-- Debug message. -- Debug message.
local text1=string.format("ATC %s: Flight %s cleared for landing (flag=%d).", airport, flight, flagvalue) local text1=string.format("ATC %s: Flight %s cleared for landing (flag=%d).", airport, flight, flagvalue)
if string.find(flight,"#") then
flight = string.match(flight,"^(.+)#")
end
local text2=string.format("ATC %s: Flight %s you are cleared for landing.", airport, flight) local text2=string.format("ATC %s: Flight %s you are cleared for landing.", airport, flight)
BASE:T( RAT.id..text1) BASE:T( RAT.id..text1)
MESSAGE:New(text2, 10):ToAllIf(RAT.ATC.messages) MESSAGE:New(text2, 10):ToAllIf(RAT.ATC.messages)
@@ -5707,6 +5716,9 @@ function RAT:_ATCFlightLanded(name)
local text1=string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60) local text1=string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60)
local text2=string.format("ATC %s: Number of flights still on final %d.", dest, RAT.ATC.airport[dest].Nonfinal) local text2=string.format("ATC %s: Number of flights still on final %d.", dest, RAT.ATC.airport[dest].Nonfinal)
local text3=string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, RAT.ATC.airport[dest].traffic, TrafficPerHour) local text3=string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, RAT.ATC.airport[dest].traffic, TrafficPerHour)
if string.find(name,"#") then
name = string.match(name,"^(.+)#")
end
local text4=string.format("ATC %s: Flight %s landed. Welcome to %s.", dest, name, dest) local text4=string.format("ATC %s: Flight %s landed. Welcome to %s.", dest, name, dest)
BASE:T(RAT.id..text1) BASE:T(RAT.id..text1)
BASE:T(RAT.id..text2) BASE:T(RAT.id..text2)
@@ -5820,6 +5832,7 @@ RATMANAGER={
rat={}, rat={},
name={}, name={},
alive={}, alive={},
planned={},
min={}, min={},
nrat=0, nrat=0,
ntot=nil, ntot=nil,
@@ -5868,6 +5881,7 @@ function RATMANAGER:Add(ratobject,min)
self.rat[self.nrat]=ratobject self.rat[self.nrat]=ratobject
self.alive[self.nrat]=0 self.alive[self.nrat]=0
self.planned[self.nrat]=0
self.name[self.nrat]=ratobject.alias self.name[self.nrat]=ratobject.alias
self.min[self.nrat]=min or 1 self.min[self.nrat]=min or 1
@@ -6008,11 +6022,25 @@ function RATMANAGER:_Manage()
for i=1,self.nrat do for i=1,self.nrat do
for j=1,N[i] do for j=1,N[i] do
time=time+self.dTspawn time=time+self.dTspawn
SCHEDULER:New(nil, RAT._SpawnWithRoute, {self.rat[i]}, time) self.planned[i]=self.planned[i]+1
SCHEDULER:New(nil, RATMANAGER._Spawn, {self, i}, time)
end end
end end
end end
--- Instantly starts the RAT manager and spawns the initial random number RAT groups for each RAT object.
-- @param #RATMANAGER self
-- @param #RATMANAGER RATMANAGER self object.
-- @param #number i Index.
function RATMANAGER:_Spawn(i)
local rat=self.rat[i] --#RAT
rat:_SpawnWithRoute()
self.planned[i]=self.planned[i]-1
end
--- Counts the number of alive RAT objects. --- Counts the number of alive RAT objects.
-- @param #RATMANAGER self -- @param #RATMANAGER self
function RATMANAGER:_Count() function RATMANAGER:_Count()
@@ -6041,7 +6069,7 @@ function RATMANAGER:_Count()
ntotal=ntotal+n ntotal=ntotal+n
-- Debug output. -- Debug output.
local text=string.format("Number of alive groups of %s = %d", self.name[i], n) local text=string.format("Number of alive groups of %s = %d, planned=%d", self.name[i], n, self.planned[i])
self:T(RATMANAGER.id..text) self:T(RATMANAGER.id..text)
end end
@@ -6071,9 +6099,10 @@ function RATMANAGER:_RollDice(nrat,ntot,min,alive)
local M={} local M={}
local P={} local P={}
for i=1,nrat do for i=1,nrat do
local a=alive[i]+self.planned[i]
N[#N+1]=0 N[#N+1]=0
M[#M+1]=math.max(alive[i], min[i]) M[#M+1]=math.max(a, min[i])
P[#P+1]=math.max(min[i]-alive[i],0) P[#P+1]=math.max(min[i]-a,0)
end end
-- Min/max group arrays. -- Min/max group arrays.
@@ -6090,7 +6119,7 @@ function RATMANAGER:_RollDice(nrat,ntot,min,alive)
-- Number of new groups to be added. -- Number of new groups to be added.
local nnew=ntot local nnew=ntot
for i=1,nrat do for i=1,nrat do
nnew=nnew-alive[i] nnew=nnew-alive[i]-self.planned[i]
end end
for i=1,nrat-1 do for i=1,nrat-1 do
@@ -6122,7 +6151,7 @@ function RATMANAGER:_RollDice(nrat,ntot,min,alive)
end end
-- Debug info -- Debug info
self:T3(string.format("RATMANAGER: i=%d, alive=%d, min=%d, mini=%d, maxi=%d, add=%d, sumN=%d, sumP=%d", j, alive[j], min[j], mini[j], maxi[j], N[j],sN, sP)) self:T3(string.format("RATMANAGER: i=%d, alive=%d, planned=%d, min=%d, mini=%d, maxi=%d, add=%d, sumN=%d, sumP=%d", j, alive[j], self.planned[i], min[j], mini[j], maxi[j], N[j],sN, sP))
end end
@@ -6137,7 +6166,7 @@ function RATMANAGER:_RollDice(nrat,ntot,min,alive)
-- Debug info -- Debug info
local text=RATMANAGER.id.."\n" local text=RATMANAGER.id.."\n"
for i=1,nrat do for i=1,nrat do
text=text..string.format("%s: i=%d, alive=%d, min=%d, mini=%d, maxi=%d, add=%d\n", self.name[i], i, alive[i], min[i], mini[i], maxi[i], N[i]) text=text..string.format("%s: i=%d, alive=%d, planned=%d, min=%d, mini=%d, maxi=%d, add=%d\n", self.name[i], i, alive[i], self.planned[i], min[i], mini[i], maxi[i], N[i])
end end
text=text..string.format("Total # of groups to add = %d", sum(N, done)) text=text..string.format("Total # of groups to add = %d", sum(N, done))
self:T(text) self:T(text)
@@ -6145,4 +6174,3 @@ function RATMANAGER:_RollDice(nrat,ntot,min,alive)
-- Return number of groups to be spawned. -- Return number of groups to be spawned.
return N return N
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
--- **Functional** - Administer the scoring of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites. --- **Functional** - Administer the scoring of player achievements, file and log the scoring events for use at websites.
-- --
-- === -- ===
-- --
@@ -12,7 +12,7 @@
-- * Score the hits and destroys of units. -- * Score the hits and destroys of units.
-- * Score the hits and destroys of statics. -- * Score the hits and destroys of statics.
-- * Score the hits and destroys of scenery. -- * Score the hits and destroys of scenery.
-- * Log scores into a CSV file. -- * (optional) Log scores into a CSV file.
-- * Connect to a remote server using JSON and IP. -- * Connect to a remote server using JSON and IP.
-- --
-- === -- ===
@@ -59,7 +59,7 @@
-- --
-- ![Banner Image](..\Presentations\SCORING\Dia9.JPG) -- ![Banner Image](..\Presentations\SCORING\Dia9.JPG)
-- --
-- Various @{Zone}s can be defined for which scores are also granted when objects in that @{Zone} are destroyed. -- Various @{Core.Zone}s can be defined for which scores are also granted when objects in that @{Core.Zone} are destroyed.
-- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed. -- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed.
-- --
-- With a small change in MissionScripting.lua, the scoring results can also be logged in a **CSV file**. -- With a small change in MissionScripting.lua, the scoring results can also be logged in a **CSV file**.
@@ -115,7 +115,7 @@
-- --
-- Special targets can be set that will give extra scores to the players when these are destroyed. -- Special targets can be set that will give extra scores to the players when these are destroyed.
-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Wrapper.Unit}s. -- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Wrapper.Unit}s.
-- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. -- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Wrapper.Static}s.
-- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Wrapper.Group}s. -- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Wrapper.Group}s.
-- --
-- local Scoring = SCORING:New( "Scoring File" ) -- local Scoring = SCORING:New( "Scoring File" )
@@ -131,11 +131,11 @@
-- # Define destruction zones that will give extra scores: -- # Define destruction zones that will give extra scores:
-- --
-- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. -- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points.
-- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. -- Use the method @{#SCORING.AddZoneScore}() to add a @{Core.Zone} for additional scoring.
-- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. -- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Core.Zone} for additional scoring.
-- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Core.Zone#ZONE_UNIT}, -- There are interesting variations that can be achieved with this functionality. For example, if the @{Core.Zone} is a @{Core.Zone#ZONE_UNIT},
-- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. -- then the zone is a moving zone, and anything destroyed within that @{Core.Zone} will generate points.
-- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, -- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Core.Zone},
-- just large enough around that building. -- just large enough around that building.
-- --
-- # Add extra Goal scores upon an event or a condition: -- # Add extra Goal scores upon an event or a condition:
@@ -225,6 +225,7 @@ SCORING = {
ClassName = "SCORING", ClassName = "SCORING",
ClassID = 0, ClassID = 0,
Players = {}, Players = {},
AutoSave = true,
} }
local _SCORINGCoalition = { local _SCORINGCoalition = {
@@ -306,6 +307,7 @@ function SCORING:New( GameName )
end ) end )
-- Create the CSV file. -- Create the CSV file.
self.AutoSave = true
self:OpenCSV( GameName ) self:OpenCSV( GameName )
return self return self
@@ -373,11 +375,11 @@ function SCORING:RemoveUnitScore( ScoreUnit )
return self return self
end end
--- Add a @{Static} for additional scoring when the @{Static} is destroyed. --- Add a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
-- Note that if there was already a @{Static} declared within the scoring with the same name, -- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
-- then the old @{Static} will be replaced with the new @{Static}. -- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. -- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param #number Score The Score value. -- @param #number Score The Score value.
-- @return #SCORING -- @return #SCORING
function SCORING:AddStaticScore( ScoreStatic, Score ) function SCORING:AddStaticScore( ScoreStatic, Score )
@@ -389,9 +391,9 @@ function SCORING:AddStaticScore( ScoreStatic, Score )
return self return self
end end
--- Removes a @{Static} for additional scoring when the @{Static} is destroyed. --- Removes a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. -- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @return #SCORING -- @return #SCORING
function SCORING:RemoveStaticScore( ScoreStatic ) function SCORING:RemoveStaticScore( ScoreStatic )
@@ -419,11 +421,11 @@ function SCORING:AddScoreGroup( ScoreGroup, Score )
return self return self
end end
--- Add a @{Zone} to define additional scoring when any object is destroyed in that zone. --- Add a @{Core.Zone} to define additional scoring when any object is destroyed in that zone.
-- Note that if a @{Zone} with the same name is already within the scoring added, the @{Zone} (type) and Score will be replaced! -- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
-- This allows for a dynamic destruction zone evolution within your mission. -- This allows for a dynamic destruction zone evolution within your mission.
-- @param #SCORING self -- @param #SCORING self
-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. -- @param Core.Zone#ZONE_BASE ScoreZone The @{Core.Zone} which defines the destruction score perimeters.
-- Note that a zone can be a polygon or a moving zone. -- Note that a zone can be a polygon or a moving zone.
-- @param #number Score The Score value. -- @param #number Score The Score value.
-- @return #SCORING -- @return #SCORING
@@ -438,11 +440,11 @@ function SCORING:AddZoneScore( ScoreZone, Score )
return self return self
end end
--- Remove a @{Zone} for additional scoring. --- Remove a @{Core.Zone} for additional scoring.
-- The scoring will search if any @{Zone} is added with the given name, and will remove that zone from the scoring. -- The scoring will search if any @{Core.Zone} is added with the given name, and will remove that zone from the scoring.
-- This allows for a dynamic destruction zone evolution within your mission. -- This allows for a dynamic destruction zone evolution within your mission.
-- @param #SCORING self -- @param #SCORING self
-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. -- @param Core.Zone#ZONE_BASE ScoreZone The @{Core.Zone} which defines the destruction score perimeters.
-- Note that a zone can be a polygon or a moving zone. -- Note that a zone can be a polygon or a moving zone.
-- @return #SCORING -- @return #SCORING
function SCORING:RemoveZoneScore( ScoreZone ) function SCORING:RemoveZoneScore( ScoreZone )
@@ -656,11 +658,12 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition and self.penaltyoncoalitionchange then if self.Players[PlayerName].UnitCoalition ~= UnitCoalition and self.penaltyoncoalitionchange then
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + self.CoalitionChangePenalty or 50 self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + self.CoalitionChangePenalty or 50
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
self.CoalitionChangePenalty .. "Penalty points added.", "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). ".. self.CoalitionChangePenalty .."Penalty points added.",
MESSAGE.Type.Information ) MESSAGE.Type.Information
:ToAll() ):ToAll()
self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -1 * self.CoalitionChangePenalty, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() ) self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -1*self.CoalitionChangePenalty, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType,
UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
end end
end end
@@ -712,11 +715,11 @@ function SCORING:AddGoalScorePlayer( PlayerName, GoalTag, Text, Score )
PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 }
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
if Text then
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE:NewType( self.DisplayMessagePrefix .. Text,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAll() :ToAll()
end
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil ) self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil )
end end
end end
@@ -735,7 +738,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
local PlayerName = PlayerUnit:GetPlayerName() local PlayerName = PlayerUnit:GetPlayerName()
self:F( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) self:T2( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } )
-- PlayerName can be nil, if the Unit with the player crashed or due to another reason. -- PlayerName can be nil, if the Unit with the player crashed or due to another reason.
if PlayerName then if PlayerName then
@@ -745,10 +748,11 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
if Text then
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE:NewType( self.DisplayMessagePrefix .. Text,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAll() :ToAll()
end
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
end end
end end
@@ -782,10 +786,11 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
if Text then
MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score, MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAll() :ToAll()
end
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
end end
end end
@@ -818,7 +823,9 @@ function SCORING:_AddMissionGoalScore( Mission, PlayerName, Text, Score )
PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
if Text then
MESSAGE:NewType( string.format( "%s%s: %s! Player %s receives %d score!", self.DisplayMessagePrefix, Mission:GetText(), Text, PlayerName, Score ), MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( string.format( "%s%s: %s! Player %s receives %d score!", self.DisplayMessagePrefix, Mission:GetText(), Text, PlayerName, Score ), MESSAGE.Type.Information ):ToAll()
end
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score ) self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end end
@@ -845,10 +852,11 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score
if Text then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. Score .. " mission score!", MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. Score .. " mission score!",
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAll() :ToAll()
end
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end end
end end
@@ -870,8 +878,10 @@ end
function SCORING:OnEventBirth( Event ) function SCORING:OnEventBirth( Event )
if Event.IniUnit then if Event.IniUnit then
Event.IniUnit.ThreatLevel, Event.IniUnit.ThreatType = Event.IniUnit:GetThreatLevel()
if Event.IniObjectCategory == 1 then if Event.IniObjectCategory == 1 then
local PlayerName = Event.IniUnit:GetPlayerName() local PlayerName = Event.IniUnit:GetPlayerName()
Event.IniUnit.BirthTime = timer.getTime()
if PlayerName then if PlayerName then
self:_AddPlayerFromUnit( Event.IniUnit ) self:_AddPlayerFromUnit( Event.IniUnit )
self:SetScoringMenu( Event.IniGroup ) self:SetScoringMenu( Event.IniGroup )
@@ -1002,7 +1012,18 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil then
PlayerHit.ThreatLevel = 1
PlayerHit.ThreatType = "Unknown"
end
else
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
end
-- Only grant hit scores if there was more than one second between the last hit. -- Only grant hit scores if there was more than one second between the last hit.
if timer.getTime() - PlayerHit.TimeStamp > 1 then if timer.getTime() - PlayerHit.TimeStamp > 1 then
@@ -1018,27 +1039,30 @@ function SCORING:_EventOnHit( Event )
if InitCoalition then -- A coalition object was hit. if InitCoalition then -- A coalition object was hit.
if InitCoalition == TargetCoalition then if InitCoalition == TargetCoalition then
Player.Penalty = Player.Penalty + 10 local Penalty = 10
PlayerHit.Penalty = PlayerHit.Penalty + 10 Player.Penalty = Player.Penalty + Penalty
PlayerHit.Penalty = PlayerHit.Penalty + Penalty
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ... if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, "Penalty: -" .. Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update ) MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else else
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, "Penalty: -" .. Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update ) MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end end
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else else
Player.Score = Player.Score + 1 -- Hitting a target multiple times before destoying it should not result in a higger score
PlayerHit.Score = PlayerHit.Score + 1 -- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
-- Player.Score = Player.Score + 1
-- PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ... if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
@@ -1101,7 +1125,18 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil then
PlayerHit.ThreatLevel = 1
PlayerHit.ThreatType = "Unknown"
end
else
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
end
-- Only grant hit scores if there was more than one second between the last hit. -- Only grant hit scores if there was more than one second between the last hit.
if timer.getTime() - PlayerHit.TimeStamp > 1 then if timer.getTime() - PlayerHit.TimeStamp > 1 then
@@ -1112,23 +1147,28 @@ function SCORING:_EventOnHit( Event )
if InitCoalition then -- A coalition object was hit, probably a static. if InitCoalition then -- A coalition object was hit, probably a static.
if InitCoalition == TargetCoalition then if InitCoalition == TargetCoalition then
-- TODO: Penalty according scale -- TODO: Penalty according scale
Player.Penalty = Player.Penalty + 10 -- * self.ScaleDestroyPenalty local Penalty = 10
PlayerHit.Penalty = PlayerHit.Penalty + 10 -- * self.ScaleDestroyPenalty Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. MESSAGE
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty, :NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
MESSAGE.Type.Update ) TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else else
Player.Score = Player.Score + 1 -- Hitting a target multiple times before destoying it should not result in a higger score
PlayerHit.Score = PlayerHit.Score + 1 -- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
-- Player.Score = Player.Score + 1
-- PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty, "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update ) MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1206,7 +1246,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
local Destroyed = false local Destroyed = false
-- What is the player destroying? -- What is the player destroying?
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
@@ -1235,13 +1275,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else else
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
@@ -1263,13 +1303,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else else
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
@@ -1652,7 +1692,8 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", PlayerMessage =
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
PlayerName, PlayerName,
PlayerScore - PlayerPenalty, PlayerScore - PlayerPenalty,
PlayerScore, PlayerScore,
@@ -1707,7 +1748,8 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", PlayerMessage =
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
PlayerName, PlayerName,
PlayerScore - PlayerPenalty, PlayerScore - PlayerPenalty,
PlayerScore, PlayerScore,
@@ -1725,9 +1767,9 @@ function SCORING:SecondsToClock( sSeconds )
-- return nil; -- return nil;
return "00:00:00"; return "00:00:00";
else else
nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) ); local nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) );
nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) ); local nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) );
nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) ); local nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) );
return nHours .. ":" .. nMins .. ":" .. nSecs return nHours .. ":" .. nMins .. ":" .. nSecs
end end
end end
@@ -1743,7 +1785,7 @@ end
function SCORING:OpenCSV( ScoringCSV ) function SCORING:OpenCSV( ScoringCSV )
self:F( ScoringCSV ) self:F( ScoringCSV )
if lfs and io and os then if lfs and io and os and self.AutoSave then
if ScoringCSV then if ScoringCSV then
self.ScoringCSV = ScoringCSV self.ScoringCSV = ScoringCSV
local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv" local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv"
@@ -1823,7 +1865,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
TargetUnitType = TargetUnitType or "" TargetUnitType = TargetUnitType or ""
TargetUnitName = TargetUnitName or "" TargetUnitName = TargetUnitName or ""
if lfs and io and os then if lfs and io and os and self.AutoSave then
self.CSVFile:write( self.CSVFile:write(
'"' .. self.GameName .. '"' .. ',' .. '"' .. self.GameName .. '"' .. ',' ..
'"' .. self.RunTime .. '"' .. ',' .. '"' .. self.RunTime .. '"' .. ',' ..
@@ -1847,9 +1889,20 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
end end
end end
--- Close CSV file
-- @param #SCORING self
-- @return #SCORING self
function SCORING:CloseCSV() function SCORING:CloseCSV()
if lfs and io and os then if lfs and io and os and self.AutoSave then
self.CSVFile:close() self.CSVFile:close()
end end
end end
--- Registers a score for a player.
-- @param #SCORING self
-- @param #boolean OnOff Switch saving to CSV on = true or off = false
-- @return #SCORING self
function SCORING:SwitchAutoSave(OnOff)
self.AutoSave = OnOff
return self
end

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- **Functional** - Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- === -- ===
-- --
@@ -79,6 +79,7 @@ SEAD = {
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109", ["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154", ["AGM_154"] = "AGM_154",
["HY-2"] = "HY-2",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
@@ -98,6 +99,7 @@ SEAD = {
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn ["BGM_109"] = {460, 0.705}, --in-game ~465kn
["AGM_154"] = {130, 0.61}, ["AGM_154"] = {130, 0.61},
["HY-2"] = {90,1},
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.

View File

@@ -1,9 +1,11 @@
--- **Functional** -- Short Range Air Defense System --- **Functional** - Short Range Air Defense System.
-- --
-- === -- ===
-- --
-- **SHORAD** - Short Range Air Defense System -- ## Features:
-- Controls a network of short range air/missile defense groups. --
-- * Short Range Air Defense System
-- * Controls a network of short range air/missile defense groups.
-- --
-- === -- ===
-- --
@@ -18,7 +20,7 @@
-- @module Functional.Shorad -- @module Functional.Shorad
-- @image Functional.Shorad.jpg -- @image Functional.Shorad.jpg
-- --
-- Date: July 2021 -- Date: Nov 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **SHORAD** class, extends Core.Base#BASE --- **SHORAD** class, extends Core.Base#BASE
@@ -41,6 +43,7 @@
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green. -- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie) --- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
-- --
-- Simple Class for a more intelligent Short Range Air Defense System -- Simple Class for a more intelligent Short Range Air Defense System
@@ -131,6 +134,7 @@ do
["Kh29"] = "Kh29", ["Kh29"] = "Kh29",
["Kh31"] = "Kh31", ["Kh31"] = "Kh31",
["Kh66"] = "Kh66", ["Kh66"] = "Kh66",
--["BGM_109"] = "BGM_109",
} }
--- Instantiates a new SHORAD object --- Instantiates a new SHORAD object
@@ -138,13 +142,13 @@ do
-- @param #string Name Name of this SHORAD -- @param #string Name Name of this SHORAD
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP -- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
-- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend -- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
-- @param #number Radius Defense radius in meters, used to switch on groups -- @param #number Radius Defense radius in meters, used to switch on SHORAD groups **within** this radius
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call -- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral" -- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch) -- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
-- @retunr #SHORAD self -- @retunr #SHORAD self
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff) function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, FSM:New() )
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition}) self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart() local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
@@ -162,11 +166,17 @@ do
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
self:I("*** SHORAD - Started Version 0.2.8") self:I("*** SHORAD - Started Version 0.3.1")
-- Set the string id for output to DCS.log file. -- Set the string id for output to DCS.log file.
self.lid=string.format("SHORAD %s | ", self.name) self.lid=string.format("SHORAD %s | ", self.name)
self:_InitState() self:_InitState()
self:HandleEvent(EVENTS.Shot, self.HandleEventShot) self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
-- Start State.
self:SetStartState("Running")
self:AddTransition("*", "WakeUpShorad", "*")
self:AddTransition("*", "CalculateHitZone", "*")
return self return self
end end
@@ -310,7 +320,7 @@ do
local hit = false local hit = false
if self.DefendHarms then if self.DefendHarms then
for _,_name in pairs (SHORAD.Harms) do for _,_name in pairs (SHORAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end if string.find(WeaponName,_name,1,true) then hit = true end
end end
end end
return hit return hit
@@ -326,7 +336,7 @@ do
local hit = false local hit = false
if self.DefendMavs then if self.DefendMavs then
for _,_name in pairs (SHORAD.Mavs) do for _,_name in pairs (SHORAD.Mavs) do
if string.find(WeaponName,_name,1) then hit = true end if string.find(WeaponName,_name,1,true) then hit = true end
end end
end end
return hit return hit
@@ -367,7 +377,7 @@ do
local returnname = false local returnname = false
for _,_groups in pairs (shoradset) do for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName() local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1) then if string.find(groupname, tgtgrp, 1, true) then
returnname = true returnname = true
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive --_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
end end
@@ -388,7 +398,7 @@ do
local returnname = false local returnname = false
for _,_groups in pairs (shoradset) do for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName() local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1) then if string.find(groupname, tgtgrp, 1, true) then
returnname = true returnname = true
end end
end end
@@ -400,6 +410,7 @@ do
-- @return #boolean Returns true for a detection, else false -- @return #boolean Returns true for a detection, else false
function SHORAD:_ShotIsDetected() function SHORAD:_ShotIsDetected()
self:T(self.lid .. " _ShotIsDetected") self:T(self.lid .. " _ShotIsDetected")
if self.debug then return true end
local IsDetected = false local IsDetected = false
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
local ActualDetection = math.random(1,100) -- value for this shot local ActualDetection = math.random(1,100) -- value for this shot
@@ -423,7 +434,7 @@ do
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs") -- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
-- mymantis:AddShorad(myshorad,720) -- mymantis:AddShorad(myshorad,720)
-- mymantis:Start() -- mymantis:Start()
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat) function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat)
self:T(self.lid .. " WakeUpShorad") self:T(self.lid .. " WakeUpShorad")
self:T({TargetGroup, Radius, ActiveTimer, TargetCat}) self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
local targetcat = TargetCat or Object.Category.UNIT local targetcat = TargetCat or Object.Category.UNIT
@@ -477,6 +488,76 @@ do
return self return self
end end
--- (Internal) Calculate hit zone of an AGM-88
-- @param #SHORAD self
-- @param #table SEADWeapon DCS.Weapon object
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @return #SHORAD self
function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup)
self:T("**** Calculating hit zone")
if SEADWeapon and SEADWeapon:isExist() then
--local pos = SEADWeapon:getPoint()
-- postion and height
local position = SEADWeapon:getPosition()
local mheight = height
-- heading
local wph = math.atan2(position.x.z, position.x.x)
if wph < 0 then
wph=wph+2*math.pi
end
wph=math.deg(wph)
-- velocity
local wpndata = SEAD.HarmData["AGM_88"]
local mveloc = math.floor(wpndata[2] * 340.29)
local c1 = (2*mheight*9.81)/(mveloc^2)
local c2 = (mveloc^2) / 9.81
local Ropt = c2 * math.sqrt(c1+1)
if height <= 5000 then
Ropt = Ropt * 0.72
elseif height <= 7500 then
Ropt = Ropt * 0.82
elseif height <= 10000 then
Ropt = Ropt * 0.87
elseif height <= 12500 then
Ropt = Ropt * 0.98
end
-- look at a couple of zones across the trajectory
for n=1,3 do
local dist = Ropt - ((n-1)*20000)
local predpos= pos0:Translate(dist,wph)
if predpos then
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
if self.debug then
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
end
local seadset = self.Groupset
local tgtcoord = targetzone:GetRandomPointVec2()
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
local _targetgroup = nil
local _targetgroupname = "none"
local _targetskill = "Random"
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
end
end
end
end
return self
end
--- Main function - work on the EventData --- Main function - work on the EventData
-- @param #SHORAD self -- @param #SHORAD self
-- @param Core.Event#EVENTDATA EventData The event details table data set -- @param Core.Event#EVENTDATA EventData The event details table data set
@@ -504,13 +585,48 @@ do
if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
-- get target data -- get target data
local targetdata = EventData.Weapon:getTarget() -- Identify target local targetdata = EventData.Weapon:getTarget() -- Identify target
-- Is there target data?
if not targetdata or self.debug then
if string.find(ShootingWeaponName,"AGM_88",1,true) then
self:I("**** Tracking AGM-88 with no target data.")
local pos0 = EventData.IniUnit:GetCoordinate()
local fheight = EventData.IniUnit:GetHeight()
self:__CalculateHitZone(20,ShootingWeapon,pos0,fheight,EventData.IniGroup)
end
return self
end
local targetcat = targetdata:getCategory() -- Identify category local targetcat = targetdata:getCategory() -- Identify category
self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat))) self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
self:T({targetdata})
local targetunit = nil local targetunit = nil
if targetcat == Object.Category.UNIT then -- UNIT if targetcat == Object.Category.UNIT then -- UNIT
targetunit = UNIT:Find(targetdata) targetunit = UNIT:Find(targetdata)
elseif targetcat == Object.Category.STATIC then -- STATIC elseif targetcat == Object.Category.STATIC then -- STATIC
targetunit = STATIC:Find(targetdata) --self:T("Static Target Data")
--self:T({targetdata:isExist()})
--self:T({targetdata:getPoint()})
local tgtcoord = COORDINATE:NewFromVec3(targetdata:getPoint())
--tgtcoord:MarkToAll("Missile Target",true)
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
local tgtcoord1 = tgtgrp1:GetCoordinate()
--tgtcoord1:MarkToAll("Close target SAM",true)
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
local tgtcoord2 = tgtgrp2:GetCoordinate()
--tgtcoord2:MarkToAll("Close target SHORAD",true)
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
if dist1 < dist2 then
targetunit = tgtgrp1
targetcat = Object.Category.UNIT
else
targetunit = tgtgrp2
targetcat = Object.Category.UNIT
end
end end
--local targetunitname = Unit.getName(targetdata) -- Unit name --local targetunitname = Unit.getName(targetdata) -- Unit name
if targetunit and targetunit:IsAlive() then if targetunit and targetunit:IsAlive() then
@@ -519,7 +635,11 @@ do
local targetgroup = nil local targetgroup = nil
local targetgroupname = "none" local targetgroupname = "none"
if targetcat == Object.Category.UNIT then if targetcat == Object.Category.UNIT then
if targetunit.ClassName == "UNIT" then
targetgroup = targetunit:GetGroup() targetgroup = targetunit:GetGroup()
elseif targetunit.ClassName == "GROUP" then
targetgroup = targetunit
end
targetgroupname = targetgroup:GetName() -- group name targetgroupname = targetgroup:GetName() -- group name
elseif targetcat == Object.Category.STATIC then elseif targetcat == Object.Category.STATIC then
targetgroup = targetunit targetgroup = targetunit
@@ -540,6 +660,7 @@ do
end end
end end
end end
return self
end end
-- --
end end

View File

@@ -85,6 +85,7 @@
-- @field #boolean eventmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. Default true. -- @field #boolean eventmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. Default true.
-- @field Core.Zone#ZONE BattleZone -- @field Core.Zone#ZONE BattleZone
-- @field #boolean AutoEngage -- @field #boolean AutoEngage
-- @field #table waypoints Waypoints of the group as defined in the ME.
-- @extends Core.Fsm#FSM_CONTROLLABLE -- @extends Core.Fsm#FSM_CONTROLLABLE
-- --
@@ -265,6 +266,7 @@ SUPPRESSION={
DefaultAlarmState = "Auto", DefaultAlarmState = "Auto",
DefaultROE = "Weapon Free", DefaultROE = "Weapon Free",
eventmoose = true, eventmoose = true,
waypoints = {},
} }
--- Enumerator of possible rules of engagement. --- Enumerator of possible rules of engagement.
@@ -295,7 +297,7 @@ SUPPRESSION.MenuF10=nil
--- PSEUDOATC version. --- PSEUDOATC version.
-- @field #number version -- @field #number version
SUPPRESSION.version="0.9.3" SUPPRESSION.version="0.9.4"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -309,7 +311,7 @@ SUPPRESSION.version="0.9.3"
--- Creates a new AI_suppression object. --- Creates a new AI_suppression object.
-- @param #SUPPRESSION self -- @param #SUPPRESSION self
-- @param Wrapper.Group#GROUP group The GROUP object for which suppression should be applied. -- @param Wrapper.Group#GROUP group The GROUP object for which suppression should be applied.
-- @return #SUPPRESSION SUPPRESSION object or *nil* if group does not exist or is not a ground group. -- @return #SUPPRESSION self
function SUPPRESSION:New(group) function SUPPRESSION:New(group)
-- Inherits from FSM_CONTROLLABLE -- Inherits from FSM_CONTROLLABLE
@@ -320,7 +322,7 @@ function SUPPRESSION:New(group)
self.lid=string.format("SUPPRESSION %s | ", tostring(group:GetName())) self.lid=string.format("SUPPRESSION %s | ", tostring(group:GetName()))
self:T(self.lid..string.format("SUPPRESSION version %s. Activating suppressive fire for group %s", SUPPRESSION.version, group:GetName())) self:T(self.lid..string.format("SUPPRESSION version %s. Activating suppressive fire for group %s", SUPPRESSION.version, group:GetName()))
else else
self:E(self.lid.."SUPPRESSION | Requested group does not exist! (Has to be a MOOSE group.)") self:E("SUPPRESSION | Requested group does not exist! (Has to be a MOOSE group)")
return nil return nil
end end
@@ -1186,6 +1188,16 @@ function SUPPRESSION:onafterFightBack(Controllable, From, Event, To)
-- Set ROE and alarm state back to default. -- Set ROE and alarm state back to default.
self:_SetROE() self:_SetROE()
self:_SetAlarmState() self:_SetAlarmState()
local group=Controllable --Wrapper.Group#GROUP
local Waypoints = group:GetTemplateRoutePoints()
-- env.info("FF waypoints",showMessageBox)
-- self:I(Waypoints)
group:Route(Waypoints, 5)
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -1251,7 +1263,7 @@ function SUPPRESSION:onafterFallBack(Controllable, From, Event, To, AttackUnit)
self:_SetROE(SUPPRESSION.ROE.Hold) self:_SetROE(SUPPRESSION.ROE.Hold)
-- Set alarm state to GREEN and let the unit run away. -- Set alarm state to GREEN and let the unit run away.
self:_SetAlarmState(SUPPRESSION.AlarmState.Green) self:_SetAlarmState(SUPPRESSION.AlarmState.Auto)
-- Make the group run away. -- Make the group run away.
self:_Run(Coord, self.Speed, self.Formation, self.FallbackWait) self:_Run(Coord, self.Speed, self.Formation, self.FallbackWait)
@@ -1537,7 +1549,7 @@ end
-- @param #SUPPRESSION self -- @param #SUPPRESSION self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SUPPRESSION:_OnEventHit(EventData) function SUPPRESSION:_OnEventHit(EventData)
self:F(EventData) self:F3(EventData)
local GroupNameSelf=self.Controllable:GetName() local GroupNameSelf=self.Controllable:GetName()
local GroupNameTgt=EventData.TgtGroupName local GroupNameTgt=EventData.TgtGroupName
@@ -1676,15 +1688,15 @@ end
function SUPPRESSION:_Run(fin, speed, formation, wait) function SUPPRESSION:_Run(fin, speed, formation, wait)
speed=speed or 20 speed=speed or 20
formation=formation or "Off road" formation=formation or ENUMS.Formation.Vehicle.OffRoad
wait=wait or 30 wait=wait or 30
local group=self.Controllable -- Wrapper.Controllable#CONTROLLABLE local group=self.Controllable -- Wrapper.Group#GROUP
if group and group:IsAlive() then if group and group:IsAlive() then
-- Clear all tasks. -- Clear all tasks.
group:ClearTasks() --group:ClearTasks()
-- Current coordinates of group. -- Current coordinates of group.
local ini=group:GetCoordinate() local ini=group:GetCoordinate()
@@ -1695,56 +1707,17 @@ function SUPPRESSION:_Run(fin, speed, formation, wait)
-- Heading from ini to fin. -- Heading from ini to fin.
local heading=self:_Heading(ini, fin) local heading=self:_Heading(ini, fin)
-- Number of waypoints.
local nx
if dist <= 50 then
nx=2
elseif dist <= 100 then
nx=3
elseif dist <= 500 then
nx=4
else
nx=5
end
-- Number of intermediate waypoints.
local dx=dist/(nx-1)
-- Waypoint and task arrays. -- Waypoint and task arrays.
local wp={} local wp={}
local tasks={} local tasks={}
-- First waypoint is the current position of the group. -- First waypoint is the current position of the group.
wp[1]=ini:WaypointGround(speed, formation) wp[1]=ini:WaypointGround(speed, formation)
tasks[1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, 1, false)
if self.Debug then if self.Debug then
local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)", #wp, self.Controllable:GetName())) local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)", #wp, self.Controllable:GetName()))
end end
self:T2(self.lid..string.format("Number of waypoints %d", nx))
for i=1,nx-2 do
local x=dx*i
local coord=ini:Translate(x, heading)
wp[#wp+1]=coord:WaypointGround(speed, formation)
tasks[#tasks+1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, false)
self:T2(self.lid..string.format("%d x = %4.1f", i, x))
if self.Debug then
local MarkerID=coord:MarkToAll(string.format("Waypoing %d of group %s", #wp, self.Controllable:GetName()))
end
end
self:T2(self.lid..string.format("Total distance: %4.1f", dist))
-- Final waypoint.
wp[#wp+1]=fin:WaypointGround(speed, formation)
if self.Debug then
local MarkerID=fin:MarkToAll(string.format("Waypoing %d of group %s (final)", #wp, self.Controllable:GetName()))
end
-- Task to hold. -- Task to hold.
local ConditionWait=group:TaskCondition(nil, nil, nil, nil, wait, nil) local ConditionWait=group:TaskCondition(nil, nil, nil, nil, wait, nil)
local TaskHold = group:TaskHold() local TaskHold = group:TaskHold()
@@ -1754,24 +1727,14 @@ function SUPPRESSION:_Run(fin, speed, formation, wait)
TaskComboFin[#TaskComboFin+1] = group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, true) TaskComboFin[#TaskComboFin+1] = group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, true)
TaskComboFin[#TaskComboFin+1] = group:TaskControlled(TaskHold, ConditionWait) TaskComboFin[#TaskComboFin+1] = group:TaskControlled(TaskHold, ConditionWait)
-- Add final task. -- Final waypoint.
tasks[#tasks+1]=group:TaskCombo(TaskComboFin) wp[#wp+1]=fin:WaypointGround(speed, formation, TaskComboFin)
if self.Debug then
-- Original waypoints of the group. local MarkerID=fin:MarkToAll(string.format("Waypoing %d of group %s (final)", #wp, self.Controllable:GetName()))
local Waypoints = group:GetTemplateRoutePoints()
-- New points are added to the default route.
for i,p in ipairs(wp) do
table.insert(Waypoints, i, wp[i])
end
-- Set task for all waypoints.
for i,wp in ipairs(Waypoints) do
group:SetTaskWaypoint(Waypoints[i], tasks[i])
end end
-- Submit task and route group along waypoints. -- Submit task and route group along waypoints.
group:Route(Waypoints) group:Route(wp)
else else
self:E(self.lid..string.format("ERROR: Group is not alive!")) self:E(self.lid..string.format("ERROR: Group is not alive!"))
@@ -1790,7 +1753,7 @@ function SUPPRESSION._Passing_Waypoint(group, Fsm, i, final)
local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final)) local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final))
MESSAGE:New(text,10):ToAllIf(Fsm.Debug) MESSAGE:New(text,10):ToAllIf(Fsm.Debug)
if Fsm.Debug then if Fsm.Debug then
env.info(self.lid..text) env.info(Fsm.lid..text)
end end
if final then if final then
@@ -1891,7 +1854,7 @@ function SUPPRESSION:_GetLife()
local groupstrength=#units/self.IniGroupStrength*100 local groupstrength=#units/self.IniGroupStrength*100
self.T2(self.lid..string.format("Group %s _GetLife nunits = %d", self.Controllable:GetName(), #units)) self:T2(self.lid..string.format("Group %s _GetLife nunits = %d", self.Controllable:GetName(), #units))
for _,unit in pairs(units) do for _,unit in pairs(units) do

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Models the process to zone guarding and capturing. --- **Functional** - Models the process to zone guarding and capturing.
-- --
-- === -- ===
-- --
@@ -68,9 +68,9 @@ do -- ZONE_CAPTURE_COALITION
-- --
-- In order to use ZONE_CAPTURE_COALITION, you need to: -- In order to use ZONE_CAPTURE_COALITION, you need to:
-- --
-- * Create a @{Zone} object from one of the ZONE_ classes. -- * Create a @{Core.Zone} object from one of the ZONE_ classes.
-- Note that ZONE_POLYGON_ classes are not yet functional. -- The functional ZONE_ classses are those derived from a ZONE_RADIUS.
-- The only functional ZONE_ classses are those derived from a ZONE_RADIUS. -- In order to use a ZONE_POLYGON, hand over the **GROUP name** of a late activated group forming a polygon with it's waypoints.
-- * Set the state of the zone. Most of the time, Guarded would be the initial state. -- * Set the state of the zone. Most of the time, Guarded would be the initial state.
-- * Start the zone capturing **monitoring process**. -- * Start the zone capturing **monitoring process**.
-- This will check the presence of friendly and/or enemy units within the zone and will transition the state of the zone when the tactical situation changed. -- This will check the presence of friendly and/or enemy units within the zone and will transition the state of the zone when the tactical situation changed.
@@ -363,7 +363,7 @@ do -- ZONE_CAPTURE_COALITION
--- ZONE_CAPTURE_COALITION Constructor. --- ZONE_CAPTURE_COALITION Constructor.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. -- @param Core.Zone#ZONE Zone A @{Core.Zone} object with the goal to be achieved. Alternatively, can be handed as the name of late activated group describing a @{ZONE_POLYGON} with its waypoints.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}. -- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS. -- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
@@ -715,7 +715,7 @@ do -- ZONE_CAPTURE_COALITION
local UnitHit = EventData.TgtUnit local UnitHit = EventData.TgtUnit
if UnitHit.ClassName ~= "SCENERY" then if UnitHit and UnitHit.ClassName ~= "SCENERY" then
-- Check if unit is inside the capture zone and that it is of the defending coalition. -- Check if unit is inside the capture zone and that it is of the defending coalition.
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then

View File

@@ -1,4 +1,4 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone. --- **Functional** - Base class that models processes to achieve goals involving a Zone.
-- --
-- === -- ===
-- --
@@ -8,7 +8,7 @@
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: **funkyfranky** -- ### Contributions: **funkyfranky**, **Applevangelist**
-- --
-- === -- ===
-- --
@@ -26,7 +26,6 @@ do -- Zone
-- @field #boolean SmokeZone If true, smoke zone. -- @field #boolean SmokeZone If true, smoke zone.
-- @extends Core.Zone#ZONE_RADIUS -- @extends Core.Zone#ZONE_RADIUS
--- Models processes that have a Goal with a defined achievement involving a Zone. --- Models processes that have a Goal with a defined achievement involving a Zone.
-- Derived classes implement the ways how the achievements can be realized. -- Derived classes implement the ways how the achievements can be realized.
-- --
@@ -56,13 +55,18 @@ do -- Zone
--- ZONE_GOAL Constructor. --- ZONE_GOAL Constructor.
-- @param #ZONE_GOAL self -- @param #ZONE_GOAL self
-- @param Core.Zone#ZONE_RADIUS Zone A @{Zone} object with the goal to be achieved. -- @param Core.Zone#ZONE_RADIUS Zone A @{Core.Zone} object with the goal to be achieved. Alternatively, can be handed as the name of late activated group describing a ZONE_POLYGON with its waypoints.
-- @return #ZONE_GOAL -- @return #ZONE_GOAL
function ZONE_GOAL:New( Zone ) function ZONE_GOAL:New( Zone )
local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL BASE:I({Zone=Zone})
local self = BASE:Inherit( self, BASE:New())
if type(Zone) == "string" then
self = BASE:Inherit( self, ZONE_POLYGON:NewFromGroupName(Zone) )
else
self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL
self:F( { Zone = Zone } ) self:F( { Zone = Zone } )
end
-- Goal object. -- Goal object.
self.Goal = GOAL:New() self.Goal = GOAL:New()
@@ -109,7 +113,6 @@ do -- Zone
return self:GetName() return self:GetName()
end end
--- Activate smoking of zone with the color or the current owner. --- Activate smoking of zone with the color or the current owner.
-- @param #ZONE_GOAL self -- @param #ZONE_GOAL self
-- @param #boolean switch If *true* or *nil* activate smoke. If *false* or *nil*, no smoke. -- @param #boolean switch If *true* or *nil* activate smoke. If *false* or *nil*, no smoke.
@@ -135,7 +138,6 @@ do -- Zone
self.SmokeColor = SmokeColor self.SmokeColor = SmokeColor
end end
--- Flare the zone boundary. --- Flare the zone boundary.
-- @param #ZONE_GOAL self -- @param #ZONE_GOAL self
-- @param DCS#SMOKECOLOR.Color FlareColor -- @param DCS#SMOKECOLOR.Color FlareColor
@@ -143,7 +145,6 @@ do -- Zone
self:FlareZone( FlareColor, 30) self:FlareZone( FlareColor, 30)
end end
--- When started, check the Smoke and the Zone status. --- When started, check the Smoke and the Zone status.
-- @param #ZONE_GOAL self -- @param #ZONE_GOAL self
function ZONE_GOAL:onafterGuard() function ZONE_GOAL:onafterGuard()
@@ -155,7 +156,6 @@ do -- Zone
end end
end end
--- Check status Smoke. --- Check status Smoke.
-- @param #ZONE_GOAL self -- @param #ZONE_GOAL self
function ZONE_GOAL:StatusSmoke() function ZONE_GOAL:StatusSmoke()
@@ -178,7 +178,6 @@ do -- Zone
end end
--- @param #ZONE_GOAL self --- @param #ZONE_GOAL self
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
function ZONE_GOAL:__Destroyed( EventData ) function ZONE_GOAL:__Destroyed( EventData )
@@ -209,7 +208,6 @@ do -- Zone
end end
--- Activate the event UnitDestroyed to be fired when a unit is destroyed in the zone. --- Activate the event UnitDestroyed to be fired when a unit is destroyed in the zone.
-- @param #ZONE_GOAL self -- @param #ZONE_GOAL self
function ZONE_GOAL:MonitorDestroyedUnits() function ZONE_GOAL:MonitorDestroyedUnits()

View File

@@ -1,4 +1,4 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone and Cargo. --- **Functional** - Base class that models processes to achieve goals involving a Zone and Cargo.
-- --
-- === -- ===
-- --
@@ -55,7 +55,7 @@ do -- ZoneGoal
--- ZONE_GOAL_CARGO Constructor. --- ZONE_GOAL_CARGO Constructor.
-- @param #ZONE_GOAL_CARGO self -- @param #ZONE_GOAL_CARGO self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. -- @param Core.Zone#ZONE Zone A @{Core.Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @return #ZONE_GOAL_CARGO -- @return #ZONE_GOAL_CARGO
function ZONE_GOAL_CARGO:New( Zone, Coalition ) function ZONE_GOAL_CARGO:New( Zone, Coalition )

View File

@@ -1,4 +1,4 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone for a Coalition. --- **Functional** - Base class that models processes to achieve goals involving a Zone for a Coalition.
-- --
-- === -- ===
-- --
@@ -53,7 +53,7 @@ do -- ZoneGoal
--- ZONE_GOAL_COALITION Constructor. --- ZONE_GOAL_COALITION Constructor.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. -- @param Core.Zone#ZONE Zone A @{Core.Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL. -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL.
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}. -- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
-- @return #ZONE_GOAL_COALITION -- @return #ZONE_GOAL_COALITION

View File

@@ -44,3 +44,15 @@ end
if __na then if __na then
BASE:I("Check <DCS install folder>/Scripts/MissionScripting.lua and comment out the lines with sanitizeModule(''). Use at your own risk!)") BASE:I("Check <DCS install folder>/Scripts/MissionScripting.lua and comment out the lines with sanitizeModule(''). Use at your own risk!)")
end end
BASE.ServerName = "Unknown"
if lfs and loadfile then
local serverfile = lfs.writedir() .. 'Config/serverSettings.lua'
if UTILS.FileExists(serverfile) then
loadfile(serverfile)()
if cfg and cfg.name then
BASE.ServerName = cfg.name
end
end
BASE.ServerName = BASE.ServerName or "Unknown"
BASE:I("Server Name: " .. tostring(BASE.ServerName))
end

View File

@@ -4,9 +4,13 @@ __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/FiFo.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Socket.lua' )
__Moose.Include( 'Scripts/Moose/Core/Base.lua' ) __Moose.Include( 'Scripts/Moose/Core/Base.lua' )
__Moose.Include( 'Scripts/Moose/Core/Astar.lua' )
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' ) __Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
__Moose.Include( 'Scripts/Moose/Core/Condition.lua' )
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' ) __Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
__Moose.Include( 'Scripts/Moose/Core/Report.lua' ) __Moose.Include( 'Scripts/Moose/Core/Report.lua' )
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' ) __Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
@@ -27,6 +31,9 @@ __Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' ) __Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' ) __Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' ) __Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
__Moose.Include( 'Scripts/Moose/Core/Pathline.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
@@ -39,6 +46,8 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Weapon.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Net.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )

View File

@@ -1,3 +1,4 @@
---@diagnostic disable: cast-local-type
--- **Ops** - Automatic Terminal Information Service (ATIS). --- **Ops** - Automatic Terminal Information Service (ATIS).
-- --
-- === -- ===
@@ -46,13 +47,12 @@
-- --
-- ### Author: **funkyfranky** -- ### Author: **funkyfranky**
-- --
-- @module Ops.Atis -- @module Ops.ATIS
-- @image OPS_ATIS.png -- @image OPS_ATIS.png
--- ATIS class. --- ATIS class.
-- @type ATIS -- @type ATIS
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #string theatre DCS map name. -- @field #string theatre DCS map name.
-- @field #string airbasename The name of the airbase. -- @field #string airbasename The name of the airbase.
@@ -92,6 +92,10 @@
-- @field #boolean useSRS If true, use SRS for transmission. -- @field #boolean useSRS If true, use SRS for transmission.
-- @field Sound.SRS#MSRS msrs Moose SRS object. -- @field Sound.SRS#MSRS msrs Moose SRS object.
-- @field #number dTQueueCheck Time interval to check the radio queue. Default 5 sec or 90 sec if SRS is used. -- @field #number dTQueueCheck Time interval to check the radio queue. Default 5 sec or 90 sec if SRS is used.
-- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights
-- @field #boolean TransmitOnlyWithPlayers For SRS - If true, only transmit if there are alive Players.
-- @field #string SRSText Text of the complete SRS message (if done at least once, else nil)
-- @field #boolean ATISforFARPs Will be set to true if the base given is a FARP/Helipad
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
@@ -121,7 +125,7 @@
-- The @{#ATIS.New}(*airbasename*, *frequency*) creates a new ATIS object. The parameter *airbasename* is the name of the airbase or airport. Note that this has to be spelled exactly as in the DCS mission editor. -- The @{#ATIS.New}(*airbasename*, *frequency*) creates a new ATIS object. The parameter *airbasename* is the name of the airbase or airport. Note that this has to be spelled exactly as in the DCS mission editor.
-- The parameter *frequency* is the frequency the ATIS broadcasts in MHz. -- The parameter *frequency* is the frequency the ATIS broadcasts in MHz.
-- --
-- Broadcasting is started via the @{#ATIS.Start}() function. The start can be delayed by useing @{#ATIS.__Start}(*delay*), where *delay* is the delay in seconds. -- Broadcasting is started via the @{#ATIS.Start}() function. The start can be delayed by using @{#ATIS.__Start}(*delay*), where *delay* is the delay in seconds.
-- --
-- ## Subtitles -- ## Subtitles
-- --
@@ -267,6 +271,8 @@
-- Unfortunately, it is not possible to determine the duration of the complete transmission. So once the transmission is finished, there might be some radio silence before -- Unfortunately, it is not possible to determine the duration of the complete transmission. So once the transmission is finished, there might be some radio silence before
-- the next iteration begins. You can fine tune the time interval between transmissions with the @{#ATIS.SetQueueUpdateTime}() function. The default interval is 90 seconds. -- the next iteration begins. You can fine tune the time interval between transmissions with the @{#ATIS.SetQueueUpdateTime}() function. The default interval is 90 seconds.
-- --
-- An SRS Setup-Guide can be found here: [Moose TTS Setup Guide](https://github.com/FlightControl-Master/MOOSE_GUIDES/blob/master/documents/Moose%20TTS%20Setup%20Guide.pdf)
--
-- # Examples -- # Examples
-- --
-- ## Caucasus: Batumi -- ## Caucasus: Batumi
@@ -306,10 +312,22 @@
-- --
-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux). -- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux).
-- --
-- ## FARPS
--
-- ATIS is working with FARPS, but this requires the usage of SRS. The airbase name for the `New()-method` is the UNIT name of the FARP:
--
-- atis = ATIS:New("FARP Gold",119,radio.modulation.AM)
-- atis:SetMetricUnits()
-- atis:SetTransmitOnlyWithPlayers(true)
-- atis:SetReportmBar(true)
-- atis:SetTowerFrequencies(127.50)
-- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US",nil,5002)
-- atis:SetAdditionalInformation("Welcome to the Jungle!")
-- atis:__Start(3)
--
-- @field #ATIS -- @field #ATIS
ATIS = { ATIS = {
ClassName = "ATIS", ClassName = "ATIS",
Debug = false,
lid = nil, lid = nil,
theatre = nil, theatre = nil,
airbasename = nil, airbasename = nil,
@@ -346,6 +364,9 @@ ATIS = {
usemarker = nil, usemarker = nil,
markerid = nil, markerid = nil,
relHumidity = nil, relHumidity = nil,
ReportmBar = false,
TransmitOnlyWithPlayers = false,
ATISforFARPs = false,
} }
--- NATO alphabet. --- NATO alphabet.
@@ -398,6 +419,8 @@ ATIS.RunwayM2T = {
TheChannel = -10, TheChannel = -10,
Syria = 5, Syria = 5,
MarianaIslands = 2, MarianaIslands = 2,
Falklands = 12,
Sinai = 5,
} }
--- Whether ICAO phraseology is used for ATIS broadcasts. --- Whether ICAO phraseology is used for ATIS broadcasts.
@@ -409,6 +432,8 @@ ATIS.RunwayM2T = {
-- @field #boolean TheChannel true. -- @field #boolean TheChannel true.
-- @field #boolean Syria true. -- @field #boolean Syria true.
-- @field #boolean MarianaIslands true. -- @field #boolean MarianaIslands true.
-- @field #boolean Falklands true.
-- @field #boolean Sinai true.
ATIS.ICAOPhraseology = { ATIS.ICAOPhraseology = {
Caucasus = true, Caucasus = true,
Nevada = false, Nevada = false,
@@ -416,7 +441,9 @@ ATIS.ICAOPhraseology = {
PersianGulf = true, PersianGulf = true,
TheChannel = true, TheChannel = true,
Syria = true, Syria = true,
MarianaIslands = true MarianaIslands = true,
Falklands = true,
Sinai = true,
} }
--- Nav point data. --- Nav point data.
@@ -588,15 +615,19 @@ _ATIS = {}
--- ATIS class version. --- ATIS class version.
-- @field #string version -- @field #string version
ATIS.version = "0.9.6" ATIS.version = "0.9.15"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add new Normany airfields.
-- TODO: Zulu time --> Zulu in output.
-- TODO: Correct fog for elevation. -- TODO: Correct fog for elevation.
-- DONE: Zulu time --> Zulu in output.
-- DONE: Fix for AB not having a runway - Helopost like Naqoura
-- DONE: Add new Normandy airfields.
-- DONE: Use new AIRBASE system to set start/landing runway
-- DONE: SetILS doesn't work
-- DONE: Visibility reported twice over SRS
-- DONE: Add text report for output. -- DONE: Add text report for output.
-- DONE: Add stop FMS functions. -- DONE: Add stop FMS functions.
-- NOGO: Use local time. Not realisitc! -- NOGO: Use local time. Not realisitc!
@@ -611,28 +642,28 @@ ATIS.version = "0.9.6"
-- Constructor -- Constructor
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new ATIS class object for a specific aircraft carrier unit. --- Create a new ATIS class object for a specific airbase.
-- @param #ATIS self -- @param #ATIS self
-- @param #string airbasename Name of the airbase. -- @param #string AirbaseName Name of the airbase.
-- @param #number frequency Radio frequency in MHz. Default 143.00 MHz. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz.
-- @param #number modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators -- @param #number Modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators.
-- @return #ATIS self -- @return #ATIS self
function ATIS:New( airbasename, frequency, modulation ) function ATIS:New(AirbaseName, Frequency, Modulation)
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self = BASE:Inherit( self, FSM:New() ) -- #ATIS local self = BASE:Inherit( self, FSM:New() ) -- #ATIS
self.airbasename = airbasename self.airbasename=AirbaseName
self.airbase = AIRBASE:FindByName( airbasename ) self.airbase=AIRBASE:FindByName(AirbaseName)
if self.airbase==nil then if self.airbase==nil then
self:E( "ERROR: Airbase %s for ATIS could not be found!", tostring( airbasename ) ) self:E("ERROR: Airbase %s for ATIS could not be found!", tostring(AirbaseName))
return nil return nil
end end
-- Default freq and modulation. -- Default freq and modulation.
self.frequency = frequency or 143.00 self.frequency=Frequency or 143.00
self.modulation = modulation or 0 self.modulation=Modulation or 0
-- Get map. -- Get map.
self.theatre = env.mission.theatre self.theatre = env.mission.theatre
@@ -653,6 +684,7 @@ function ATIS:New( airbasename, frequency, modulation )
self:SetMapMarks( false ) self:SetMapMarks( false )
self:SetRelativeHumidity() self:SetRelativeHumidity()
self:SetQueueUpdateTime() self:SetQueueUpdateTime()
self:SetReportmBar(false)
-- Start State. -- Start State.
self:SetStartState( "Stopped" ) self:SetStartState( "Stopped" )
@@ -734,14 +766,6 @@ function ATIS:New( airbasename, frequency, modulation )
-- @param #string To To state. -- @param #string To To state.
-- @param #string Text Report text. -- @param #string Text Report text.
-- Debug trace.
if false then
self.Debug = true
BASE:TraceOnOff( true )
BASE:TraceClass( self.ClassName )
BASE:TraceLevel( 1 )
end
return self return self
end end
@@ -755,7 +779,7 @@ end
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetSoundfilesPath( path ) function ATIS:SetSoundfilesPath( path )
self.soundpath = tostring( path or "ATIS Soundfiles/" ) self.soundpath = tostring( path or "ATIS Soundfiles/" )
self:I( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) ) self:T( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
return self return self
end end
@@ -766,7 +790,7 @@ end
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetRadioRelayUnitName( unitname ) function ATIS:SetRadioRelayUnitName( unitname )
self.relayunitname = unitname self.relayunitname = unitname
self:I( self.lid .. string.format( "Setting radio relay unit to %s", self.relayunitname ) ) self:T( self.lid .. string.format( "Setting radio relay unit to %s", self.relayunitname ) )
return self return self
end end
@@ -784,13 +808,52 @@ function ATIS:SetTowerFrequencies( freqs )
return self return self
end end
--- Set active runway. This can be used if the automatic runway determination via the wind direction gives incorrect results. --- For SRS - Switch to only transmit if there are players on the server.
-- @param #ATIS self
-- @param #boolean Switch If true, only send SRS if there are alive Players.
-- @return #ATIS self
function ATIS:SetTransmitOnlyWithPlayers(Switch)
self.TransmitOnlyWithPlayers = Switch
if self.msrsQ then
self.msrsQ:SetTransmitOnlyWithPlayers(Switch)
end
return self
end
--- Set active runway for **landing** operations. This can be used if the automatic runway determination via the wind direction gives incorrect results.
-- For example, use this if there are two runways with the same directions. -- For example, use this if there are two runways with the same directions.
-- @param #ATIS self -- @param #ATIS self
-- @param #string runway Active runway, *e.g.* "31L". -- @param #string runway Active runway, *e.g.* "31L".
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetActiveRunway( runway ) function ATIS:SetActiveRunway( runway )
self.activerunway = tostring( runway ) self.activerunway = tostring( runway )
local prefer = nil
if string.find(string.lower(runway),"l") then
prefer = true
elseif string.find(string.lower(runway),"r") then
prefer = false
end
self.airbase:SetActiveRunway(runway,prefer)
return self
end
--- Set the active runway for landing.
-- @param #ATIS self
-- @param #string runway : Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
-- @param #boolean preferleft : If true, perfer the left runway. If false, prefer the right runway. If nil (default), do not care about left or right.
-- @return #ATIS self
function ATIS:SetActiveRunwayLanding(runway, preferleft)
self.airbase:SetActiveRunwayLanding(runway,preferleft)
return self
end
--- Set the active runway for take-off.
-- @param #ATIS self
-- @param #string runway : Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
-- @param #boolean preferleft : If true, perfer the left runway. If false, prefer the right runway. If nil (default), do not care about left or right.
-- @return #ATIS self
function ATIS:SetActiveRunwayTakeoff(runway,preferleft)
self.airbase:SetActiveRunwayTakeoff(runway,preferleft)
return self return self
end end
@@ -802,6 +865,15 @@ function ATIS:SetRunwayLength()
return self return self
end end
--- Give information on runway length.
-- @param #ATIS self
-- @return #ATIS self
function ATIS:SetRunwayLength()
self.rwylength=true
return self
end
--- Give information on airfield elevation --- Give information on airfield elevation
-- @param #ATIS self -- @param #ATIS self
-- @return #ATIS self -- @return #ATIS self
@@ -832,6 +904,13 @@ function ATIS:SetMapMarks( switch )
return self return self
end end
--- Return the complete SRS Text block, if at least generated once. Else nil.
-- @param #ATIS self
-- @return #string SRSText
function ATIS:GetSRSText()
return self.SRSText
end
--- Set magnetic runway headings as depicted on the runway, *e.g.* "13" for 130° or "25L" for the left runway with magnetic heading 250°. --- Set magnetic runway headings as depicted on the runway, *e.g.* "13" for 130° or "25L" for the left runway with magnetic heading 250°.
-- @param #ATIS self -- @param #ATIS self
-- @param #table headings Magnetic headings. Inverse (-180°) headings are added automatically. You only need to specify one heading per runway direction. "L"eft and "R" right can also be appended. -- @param #table headings Magnetic headings. Inverse (-180°) headings are added automatically. You only need to specify one heading per runway direction. "L"eft and "R" right can also be appended.
@@ -852,7 +931,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings )
end end
-- Add runway heading to table. -- Add runway heading to table.
self:I( self.lid .. string.format( "Adding user specified magnetic runway heading %s", heading ) ) self:T( self.lid .. string.format( "Adding user specified magnetic runway heading %s", heading ) )
table.insert( self.runwaymag, heading ) table.insert( self.runwaymag, heading )
local h = self:GetRunwayWithoutLR( heading ) local h = self:GetRunwayWithoutLR( heading )
@@ -874,7 +953,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings )
end end
-- Add inverse runway heading to table. -- Add inverse runway heading to table.
self:I( self.lid .. string.format( "Adding user specified magnetic runway heading %s (inverse)", head2 ) ) self:T( self.lid .. string.format( "Adding user specified magnetic runway heading %s (inverse)", head2 ) )
table.insert( self.runwaymag, head2 ) table.insert( self.runwaymag, head2 )
end end
@@ -948,6 +1027,28 @@ function ATIS:SetAltimeterQNH( switch )
return self return self
end end
--- Additionally report altimeter QNH/QFE in hPa, even if not set to metric.
-- @param #ATIS self
-- @param #boolean switch If true or nil, report mBar/hPa in addition.
-- @return #ATIS self
function ATIS:SetReportmBar(switch)
if switch == true or switch == nil then
self.ReportmBar = true
else
self.ReportmBar = false
end
return self
end
--- Additionally report free text, only working with SRS(!)
-- @param #ATIS self
-- @param #string text The text to report at the end of the ATIS message, e.g. runway closure, warnings, etc.
-- @return #ATIS self
function ATIS:SetAdditionalInformation(text)
self.AdditionalInformation = text
return self
end
--- Suppresses QFE readout. Default is to report both QNH and QFE. --- Suppresses QFE readout. Default is to report both QNH and QFE.
-- @param #ATIS self -- @param #ATIS self
-- @return #ATIS self -- @return #ATIS self
@@ -971,7 +1072,7 @@ end
-- --
-- * 186° on the Caucaus map -- * 186° on the Caucaus map
-- * 192° on the Nevada map -- * 192° on the Nevada map
-- * 170° on the Normany map -- * 170° on the Normandy map
-- * 182° on the Persian Gulf map -- * 182° on the Persian Gulf map
-- --
-- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation. -- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation.
@@ -979,7 +1080,7 @@ end
-- Or you make your life simple and just include the sign so you don't have to bother about East/West. -- Or you make your life simple and just include the sign so you don't have to bother about East/West.
-- --
-- @param #ATIS self -- @param #ATIS self
-- @param #number magvar Magnetic variation in degrees. Positive for easterly and negative for westerly variation. Default is magnatic declinaton of the used map, c.f. @{Utilities.UTils#UTILS.GetMagneticDeclination}. -- @param #number magvar Magnetic variation in degrees. Positive for easterly and negative for westerly variation. Default is magnatic declinaton of the used map, c.f. @{Utilities.Utils#UTILS.GetMagneticDeclination}.
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetMagneticDeclination( magvar ) function ATIS:SetMagneticDeclination( magvar )
self.magvar = magvar or UTILS.GetMagneticDeclination() self.magvar = magvar or UTILS.GetMagneticDeclination()
@@ -1127,8 +1228,10 @@ end
-- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Culture Culture, e.g. "en-GB" (default).
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
-- @param #number Port SRS port. Default 5002. -- @param #number Port SRS port. Default 5002.
-- @param #string GoogleKey Path to Google JSON-Key.
-- @return #ATIS self -- @return #ATIS self
function ATIS:SetSRS( PathToSRS, Gender, Culture, Voice, Port ) function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey)
if PathToSRS then
self.useSRS=true self.useSRS=true
self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation)
self.msrs:SetGender(Gender) self.msrs:SetGender(Gender)
@@ -1136,9 +1239,16 @@ function ATIS:SetSRS( PathToSRS, Gender, Culture, Voice, Port )
self.msrs:SetVoice(Voice) self.msrs:SetVoice(Voice)
self.msrs:SetPort(Port) self.msrs:SetPort(Port)
self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetCoalition(self:GetCoalition())
self.msrs:SetLabel("ATIS")
self.msrs:SetGoogle(GoogleKey)
self.msrsQ = MSRSQUEUE:New("ATIS")
self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
if self.dTQueueCheck<=10 then if self.dTQueueCheck<=10 then
self:SetQueueUpdateTime(90) self:SetQueueUpdateTime(90)
end end
else
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
end
return self return self
end end
@@ -1168,17 +1278,26 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
function ATIS:onafterStart( From, Event, To ) function ATIS:onafterStart( From, Event, To )
self:I("Airbase category is "..self.airbase:GetAirbaseCategory())
-- Check that this is an airdrome. -- Check that this is an airdrome.
if self.airbase:GetAirbaseCategory() ~= Airbase.Category.AIRDROME then if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then
self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT FARPS or SHIPS.", self.airbasename ) ) self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) )
return return
end end
-- Check that if is a Helipad.
if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then
self:E( self.lid .. string.format( "EXPERIMENTAL: Starting ATIS for Helipad %s! SRS must be ON", self.airbasename ) )
self.ATISforFARPs = true
self.useSRS = true
end
-- Info. -- Info.
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) ) self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) )
-- Start radio queue. -- Start radio queue.
if not self.useSRS then
self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) ) self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) )
-- Send coordinate is airbase coord. -- Send coordinate is airbase coord.
@@ -1204,6 +1323,7 @@ function ATIS:onafterStart( From, Event, To )
-- Start radio queue. -- Start radio queue.
self.radioqueue:Start( 1, 0.1 ) self.radioqueue:Start( 1, 0.1 )
end
-- Handle airbase capture -- Handle airbase capture
-- Handle events. -- Handle events.
@@ -1239,10 +1359,12 @@ function ATIS:onafterStatus( From, Event, To )
else else
text = text .. string.format( ", Relay unit=%s (alive=%s)", tostring( self.relayunitname ), relayunitstatus ) text = text .. string.format( ", Relay unit=%s (alive=%s)", tostring( self.relayunitname ), relayunitstatus )
end end
self:I( self.lid .. text ) self:T( self.lid .. text )
if not self:Is("Stopped") then
self:__Status( -60 ) self:__Status( -60 )
end end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events -- FSM Events
@@ -1270,9 +1392,11 @@ function ATIS:onafterCheckQueue( From, Event, To )
end end
if not self:Is("Stopped") then
-- Check back in 5 seconds. -- Check back in 5 seconds.
self:__CheckQueue( -math.abs( self.dTQueueCheck ) ) self:__CheckQueue( -math.abs( self.dTQueueCheck ) )
end end
end
--- Broadcast ATIS radio message. --- Broadcast ATIS radio message.
-- @param #ATIS self -- @param #ATIS self
@@ -1319,6 +1443,9 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
local mBarqnh = qnh
local mBarqfe = qfe
-- Convert to inHg. -- Convert to inHg.
if self.PmmHg then if self.PmmHg then
qfe = UTILS.hPa2mmHg( qfe ) qfe = UTILS.hPa2mmHg( qfe )
@@ -1378,7 +1505,17 @@ function ATIS:onafterBroadcast( From, Event, To )
--- Runway --- --- Runway ---
-------------- --------------
local runway, rwyLeft = self:GetActiveRunway()
local runwayLanding, rwyLandingLeft
local runwayTakeoff, rwyTakeoffLeft
if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then
runwayLanding, rwyLandingLeft="PAD 01",false
runwayTakeoff, rwyTakeoffLeft="PAD 02",false
else
runwayLanding, rwyLandingLeft=self:GetActiveRunway()
runwayTakeoff, rwyTakeoffLeft=self:GetActiveRunway(true)
end
------------ ------------
--- Time --- --- Time ---
@@ -1693,7 +1830,10 @@ function ATIS:onafterBroadcast( From, Event, To )
-- Airbase name -- Airbase name
subtitle = string.format( "%s", self.airbasename ) subtitle = string.format( "%s", self.airbasename )
if self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil
and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil
and self.airbasename:find( "Field" ) == nil
then
subtitle = subtitle .. " Airport" subtitle = subtitle .. " Airport"
end end
if not self.useSRS then if not self.useSRS then
@@ -1740,6 +1880,10 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
-- Wind -- Wind
-- Adding a space after each digit of WINDFROM to convert this to aviation-speak for TTS via SRS
if self.useSRS then
WINDFROM = string.gsub(WINDFROM,".", "%1 ")
end
if self.metric then if self.metric then
subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED ) subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED )
else else
@@ -1782,6 +1926,7 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
alltext = alltext .. ";\n" .. subtitle alltext = alltext .. ";\n" .. subtitle
subtitle = ""
-- Weather phenomena -- Weather phenomena
local wp = false local wp = false
local wpsub = "" local wpsub = ""
@@ -1881,8 +2026,9 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
end end
end end
alltext = alltext .. ";\n" .. subtitle
alltext = alltext .. ";\n" .. subtitle
subtitle = ""
-- Temperature -- Temperature
if self.TDegF then if self.TDegF then
if temperature < 0 then if temperature < 0 then
@@ -1963,6 +2109,15 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
end end
end end
if self.ReportmBar and not self.metric then
if self.qnhonly then
subtitle = string.format( "%s;\nAltimeter %d hPa", subtitle, mBarqnh )
else
subtitle = string.format( "%s;\nAltimeter: QNH %d, QFE %d hPa", subtitle, mBarqnh, mBarqfe)
end
end
local _ALTIMETER = subtitle local _ALTIMETER = subtitle
if not self.useSRS then if not self.useSRS then
self:Transmission( ATIS.Sound.Altimeter, 1.0, subtitle ) self:Transmission( ATIS.Sound.Altimeter, 1.0, subtitle )
@@ -1997,20 +2152,25 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
alltext = alltext .. ";\n" .. subtitle alltext = alltext .. ";\n" .. subtitle
local _RUNACT
if not self.ATISforFARPs then
-- Active runway. -- Active runway.
local subtitle = string.format( "Active runway %s", runway ) if runwayLanding then
if rwyLeft == true then local subtitle=string.format("Active runway %s", runwayLanding)
if rwyLandingLeft==true then
subtitle=subtitle.." Left" subtitle=subtitle.." Left"
elseif rwyLeft == false then elseif rwyLandingLeft==false then
subtitle=subtitle.." Right" subtitle=subtitle.." Right"
end end
local _RUNACT = subtitle end
_RUNACT = subtitle
if not self.useSRS then if not self.useSRS then
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle) self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
self.radioqueue:Number2Transmission( runway ) self.radioqueue:Number2Transmission(runwayLanding)
if rwyLeft == true then if rwyLandingLeft==true then
self:Transmission(ATIS.Sound.Left, 0.2) self:Transmission(ATIS.Sound.Left, 0.2)
elseif rwyLeft == false then elseif rwyLandingLeft==false then
self:Transmission(ATIS.Sound.Right, 0.2) self:Transmission(ATIS.Sound.Right, 0.2)
end end
end end
@@ -2055,7 +2215,7 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
alltext = alltext .. ";\n" .. subtitle alltext = alltext .. ";\n" .. subtitle
end end
end
-- Airfield elevation -- Airfield elevation
if self.elevation then if self.elevation then
@@ -2122,7 +2282,7 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
-- ILS -- ILS
local ils = self:GetNavPoint( self.ils, runway, rwyLeft ) local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft)
if ils then if ils then
subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency ) subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency )
if not self.useSRS then if not self.useSRS then
@@ -2140,7 +2300,7 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
-- Outer NDB -- Outer NDB
local ndb = self:GetNavPoint( self.ndbouter, runway, rwyLeft ) local ndb=self:GetNavPoint(self.ndbouter, runwayLanding, rwyLandingLeft)
if ndb then if ndb then
subtitle = string.format( "Outer NDB frequency %.2f MHz", ndb.frequency ) subtitle = string.format( "Outer NDB frequency %.2f MHz", ndb.frequency )
if not self.useSRS then if not self.useSRS then
@@ -2158,7 +2318,7 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
-- Inner NDB -- Inner NDB
local ndb = self:GetNavPoint( self.ndbinner, runway, rwyLeft ) local ndb=self:GetNavPoint(self.ndbinner, runwayLanding, rwyLandingLeft)
if ndb then if ndb then
subtitle = string.format( "Inner NDB frequency %.2f MHz", ndb.frequency ) subtitle = string.format( "Inner NDB frequency %.2f MHz", ndb.frequency )
if not self.useSRS then if not self.useSRS then
@@ -2197,7 +2357,7 @@ function ATIS:onafterBroadcast( From, Event, To )
-- TACAN -- TACAN
if self.tacan then if self.tacan then
subtitle = string.format( "TACAN channel %dX", self.tacan ) subtitle=string.format("TACAN channel %dX Ray", self.tacan)
if not self.useSRS then if not self.useSRS then
self:Transmission( ATIS.Sound.TACANChannel, 1.0, subtitle ) self:Transmission( ATIS.Sound.TACANChannel, 1.0, subtitle )
self.radioqueue:Number2Transmission( tostring( self.tacan ), nil, 0.2 ) self.radioqueue:Number2Transmission( tostring( self.tacan ), nil, 0.2 )
@@ -2217,7 +2377,7 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
-- PRMG -- PRMG
local ndb = self:GetNavPoint( self.prmg, runway, rwyLeft ) local ndb=self:GetNavPoint(self.prmg, runwayLanding, rwyLandingLeft)
if ndb then if ndb then
subtitle = string.format( "PRMG channel %d", ndb.frequency ) subtitle = string.format( "PRMG channel %d", ndb.frequency )
if not self.useSRS then if not self.useSRS then
@@ -2227,6 +2387,11 @@ function ATIS:onafterBroadcast( From, Event, To )
alltext = alltext .. ";\n" .. subtitle alltext = alltext .. ";\n" .. subtitle
end end
-- additional info, if any
if self.useSRS and self.AdditionalInformation then
alltext = alltext .. ";\n"..self.AdditionalInformation
end
-- Advice on initial... -- Advice on initial...
subtitle = string.format( "Advise on initial contact, you have information %s", NATO ) subtitle = string.format( "Advise on initial contact, you have information %s", NATO )
if not self.useSRS then if not self.useSRS then
@@ -2267,6 +2432,8 @@ function ATIS:onafterReport( From, Event, To, Text )
local text = string.gsub( text, "mmHg", "millimeters of Mercury" ) local text = string.gsub( text, "mmHg", "millimeters of Mercury" )
local text = string.gsub( text, "hPa", "hectopascals" ) local text = string.gsub( text, "hPa", "hectopascals" )
local text = string.gsub( text, "m/s", "meters per second" ) local text = string.gsub( text, "m/s", "meters per second" )
local text = string.gsub( text, "TACAN", "tackan" )
local text = string.gsub( text, "FARP", "farp" )
-- Replace ";" by "." -- Replace ";" by "."
local text = string.gsub( text, ";", " . " ) local text = string.gsub( text, ";", " . " )
@@ -2275,7 +2442,10 @@ function ATIS:onafterReport( From, Event, To, Text )
self:T( "SRS TTS: " .. text ) self:T( "SRS TTS: " .. text )
-- Play text-to-speech report. -- Play text-to-speech report.
self.msrs:PlayText( text ) local duration = STTS.getSpeechTime(text,0.95)
self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2)
--self.msrs:PlayText( text )
self.SRSText = text
end end
@@ -2344,39 +2514,22 @@ end
--- Get active runway runway. --- Get active runway runway.
-- @param #ATIS self -- @param #ATIS self
-- @param #boolean Takeoff If `true`, get runway for takeoff. Default is for landing.
-- @return #string Active runway, e.g. "31" for 310 deg. -- @return #string Active runway, e.g. "31" for 310 deg.
-- @return #boolean Use Left=true, Right=false, or nil. -- @return #boolean Use Left=true, Right=false, or nil.
function ATIS:GetActiveRunway() function ATIS:GetActiveRunway(Takeoff)
local coord = self.airbase:GetCoordinate() local runway=nil --Wrapper.Airbase#AIRBASE.Runway
local height = coord:GetLandHeight() if Takeoff then
runway=self.airbase:GetActiveRunwayTakeoff()
-- Get wind direction and speed in m/s. else
local windFrom, windSpeed = coord:GetWind( height + 10 ) runway=self.airbase:GetActiveRunwayLanding()
-- Get active runway data based on wind direction.
local runact = self.airbase:GetActiveRunway( self.runwaym2t )
-- Active runway "31".
local runway = self:GetMagneticRunway( windFrom ) or runact.idx
-- Left or right in case there are two runways with the same heading.
local rwyLeft = nil
-- Check if user explicitly specified a runway.
if self.activerunway then
-- Get explicit runway heading if specified.
local runwayno = self:GetRunwayWithoutLR( self.activerunway )
if runwayno ~= "" then
runway = runwayno
end end
if runway then -- some ABs have NO runways, e.g. Syria Naqoura
-- Was "L"eft or "R"ight given? return runway.name, runway.isLeft
rwyLeft = self:GetRunwayLR( self.activerunway ) else
return nil, nil
end end
return runway, rwyLeft
end end
--- Get runway from user supplied magnetic heading. --- Get runway from user supplied magnetic heading.
@@ -2442,7 +2595,7 @@ end
-- @return #string Runway heading without left or right, *e.g.* "31". -- @return #string Runway heading without left or right, *e.g.* "31".
function ATIS:GetRunwayWithoutLR( runway ) function ATIS:GetRunwayWithoutLR( runway )
local rwywo = runway:gsub( "%D+", "" ) local rwywo = runway:gsub( "%D+", "" )
-- self:I(string.format("FF runway=%s ==> rwywo=%s", runway, rwywo)) -- self:T(string.format("FF runway=%s ==> rwywo=%s", runway, rwywo))
return rwywo return rwywo
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,7 @@
-- @field #string tankergroupname Name of the late activated tanker template group. -- @field #string tankergroupname Name of the late activated tanker template group.
-- @field Wrapper.Group#GROUP tanker Tanker group. -- @field Wrapper.Group#GROUP tanker Tanker group.
-- @field Wrapper.Airbase#AIRBASE airbase The home airbase object of the tanker. Normally the aircraft carrier. -- @field Wrapper.Airbase#AIRBASE airbase The home airbase object of the tanker. Normally the aircraft carrier.
-- @field Core.Radio#BEACON beacon Tanker TACAN beacon. -- @field Core.Beacon#BEACON beacon Tanker TACAN beacon.
-- @field #number TACANchannel TACAN channel. Default 1. -- @field #number TACANchannel TACAN channel. Default 1.
-- @field #string TACANmode TACAN mode, i.e. "X" or "Y". Default "Y". Use only "Y" for AA TACAN stations! -- @field #string TACANmode TACAN mode, i.e. "X" or "Y". Default "Y". Use only "Y" for AA TACAN stations!
-- @field #string TACANmorse TACAN morse code. Three letters identifying the TACAN station. Default "TKR". -- @field #string TACANmorse TACAN morse code. Three letters identifying the TACAN station. Default "TKR".
@@ -198,7 +198,7 @@
-- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9. -- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9.
-- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign). -- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign).
-- --
-- TexacoStennis:SetCAllsign(CALLSIGN.Tanker.Arco) -- TexacoStennis:SetCallsign(CALLSIGN.Tanker.Arco)
-- --
-- For convenience, MOOSE has a CALLSIGN enumerator introduced. -- For convenience, MOOSE has a CALLSIGN enumerator introduced.
-- --
@@ -784,10 +784,11 @@ end
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param #number channel TACAN channel. Default 1. -- @param #number channel TACAN channel. Default 1.
-- @param #string morse TACAN morse code identifier. Three letters. Default "TKR". -- @param #string morse TACAN morse code identifier. Three letters. Default "TKR".
-- @param #string mode TACAN mode, which can be either "Y" (default) or "X".
-- @return #RECOVERYTANKER self -- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetTACAN(channel, morse) function RECOVERYTANKER:SetTACAN(channel, morse, mode)
self.TACANchannel=channel or 1 self.TACANchannel=channel or 1
self.TACANmode="Y" self.TACANmode=mode or "Y"
self.TACANmorse=morse or "TKR" self.TACANmorse=morse or "TKR"
self.TACANon=true self.TACANon=true
return self return self
@@ -1625,7 +1626,6 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
if delay and delay>0 then if delay and delay>0 then
-- Schedule TACAN activation. -- Schedule TACAN activation.
--SCHEDULER:New(nil, self._ActivateTACAN, {self}, delay)
self:ScheduleOnce(delay, RECOVERYTANKER._ActivateTACAN, self) self:ScheduleOnce(delay, RECOVERYTANKER._ActivateTACAN, self)
else else

View File

@@ -805,7 +805,9 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
self:T(self.lid..text) self:T(self.lid..text)
-- Get coordinate of unit. -- Get coordinate of unit.
local coord=unit:GetCoordinate() --local coord=unit:GetCoordinate()
local Vec3 = EventData.IniDCSUnit:getPoint() -- Vec3
local coord = COORDINATE:NewFromVec3(Vec3)
if coord and self.rescuezone:IsCoordinateInZone(coord) then if coord and self.rescuezone:IsCoordinateInZone(coord) then

View File

@@ -9,13 +9,13 @@
-- What are radio communications in DCS? -- What are radio communications in DCS?
-- --
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), -- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. -- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmitter's antenna can be set, and the transmission can be **looped**.
-- --
-- How to supply DCS my own Sound Files? -- How to supply DCS my own Sound Files?
-- --
-- * Your sound files need to be encoded in **.ogg** or .wav, -- * Your sound files need to be encoded in **.ogg** or .wav,
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, -- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), -- * They need to be added in .\l10n\DEFAULT\ in you .miz file (which can be decompressed like a .zip file),
-- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. -- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
-- --
-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE} -- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE}
@@ -26,7 +26,7 @@
-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, -- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft,
-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). -- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below).
-- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible, -- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible,
-- you won't hear/be able to use the TACAN beacon informations. -- you won't hear/be able to use the TACAN beacon information.
-- --
-- === -- ===
-- --
@@ -98,12 +98,12 @@ RADIO = {
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast. --- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast.
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead. -- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead.
-- @param #RADIO self -- @param #RADIO self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Wrapper.Positionable#POSITIONABLE} that will receive radio capabilities.
-- @return #RADIO The RADIO object or #nil if Positionable is invalid. -- @return #RADIO The RADIO object or #nil if Positionable is invalid.
function RADIO:New(Positionable) function RADIO:New(Positionable)
-- Inherit base -- Inherit base
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO local self = BASE:Inherit( self, BASE:New() ) -- Sound.Radio#RADIO
self:F(Positionable) self:F(Positionable)
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
@@ -375,7 +375,7 @@ end
--- Stops a transmission --- Stops a transmission
-- This function is especially usefull to stop the broadcast of looped transmissions -- This function is especially useful to stop the broadcast of looped transmissions
-- @param #RADIO self -- @param #RADIO self
-- @return #RADIO self -- @return #RADIO self
function RADIO:StopBroadcast() function RADIO:StopBroadcast()

View File

@@ -174,6 +174,7 @@ RADIOSPEECH.Vocabulary.RU = {
["br"] = { "br", 1.1 }, ["br"] = { "br", 1.1 },
["bra"] = { "bra", 0.3 }, ["bra"] = { "bra", 0.3 },
["возвращение на базу"] = { "returning_to_base", 1.40 }, ["возвращение на базу"] = { "returning_to_base", 1.40 },
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 }, ["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
["перехват боги"] = { "intercepting_bogeys", 1.22 }, ["перехват боги"] = { "intercepting_bogeys", 1.22 },

View File

@@ -1,4 +1,4 @@
--- **Sound** - Simple Radio Standalone (SRS) Integration. --- **Sound** - Simple Radio Standalone (SRS) Integration and Text-to-Speech.
-- --
-- === -- ===
-- --
@@ -26,7 +26,7 @@
-- === -- ===
-- --
-- ### Author: **funkyfranky** -- ### Author: **funkyfranky**
-- @module Sound.MSRS -- @module Sound.SRS
-- @image Sound_MSRS.png -- @image Sound_MSRS.png
--- MSRS class. --- MSRS class.
@@ -45,14 +45,13 @@
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
-- @field #string path Path to the SRS exe. This includes the final slash "/". -- @field #string path Path to the SRS exe. This includes the final slash "/".
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
-- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\ATIS\ATIS_Main.png)
--
-- # The MSRS Concept -- # The MSRS Concept
-- --
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS). -- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
@@ -95,17 +94,35 @@
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech -- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml -- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
-- --
--
-- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing.
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
-- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
-- Plays a message on 255AM for the blue coalition in-game.
--
-- ## Set Voice -- ## Set Voice
-- --
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Use a specific voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
-- Note that this must be installed on your windows system. -- Note that this must be installed on your windows system.
-- If enabling SetGoogle(), you can use voices provided by Google -- If enabling SetGoogle(), you can use voices provided by Google
-- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices -- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices
-- For voices there are enumerators in this class to help you out on voice names:
--
-- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS
-- MSRS.Voices.Google -- e.g. MSRS.Voices.Google.Standard.en_AU_Standard_A or MSRS.Voices.Google.Wavenet.de_DE_Wavenet_C - The Google enumerator contains voices for EN, DE, IT, FR and ES.
-- --
-- ## Set Coordinate -- ## Set Coordinate
-- --
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted. -- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
-- --
-- ## Set SRS Port
--
-- Use @{#MSRS.SetPort} to define the SRS port. Defaults to 5002.
--
-- ## Set SRS Volume
--
-- Use @{#MSRS.SetVolume} to define the SRS volume. Defaults to 1.0. Allowed values are between 0.0 and 1.0, from silent to loudest.
--
-- @field #MSRS -- @field #MSRS
MSRS = { MSRS = {
ClassName = "MSRS", ClassName = "MSRS",
@@ -121,17 +138,119 @@ MSRS = {
volume = 1, volume = 1,
speed = 1, speed = 1,
coordinate = nil, coordinate = nil,
Label = "ROBOT",
} }
--- MSRS class version. --- MSRS class version.
-- @field #string version -- @field #string version
MSRS.version="0.0.3" MSRS.version="0.1.1"
--- Voices
-- @type Voices
MSRS.Voices = {
Microsoft = {
["Hedda"] = "Microsoft Hedda Desktop", -- de-DE
["Hazel"] = "Microsoft Hazel Desktop", -- en-GB
["David"] = "Microsoft David Desktop", -- en-US
["Zira"] = "Microsoft Zira Desktop", -- en-US
["Hortense"] = "Microsoft Hortense Desktop", --fr-FR
},
Google = {
Standard = {
["en_AU_Standard_A"] = 'en-AU-Standard-A', -- [1] FEMALE
["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE
["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE
["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- [5] FEMALE
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- [6] MALE
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- [7] MALE
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- [8] FEMALE
["en_GB_Standard_A"] = 'en-GB-Standard-A', -- [9] FEMALE
["en_GB_Standard_B"] = 'en-GB-Standard-B', -- [10] MALE
["en_GB_Standard_C"] = 'en-GB-Standard-C', -- [11] FEMALE
["en_GB_Standard_D"] = 'en-GB-Standard-D', -- [12] MALE
["en_GB_Standard_F"] = 'en-GB-Standard-F', -- [13] FEMALE
["en_US_Standard_A"] = 'en-US-Standard-A', -- [14] MALE
["en_US_Standard_B"] = 'en-US-Standard-B', -- [15] MALE
["en_US_Standard_C"] = 'en-US-Standard-C', -- [16] FEMALE
["en_US_Standard_D"] = 'en-US-Standard-D', -- [17] MALE
["en_US_Standard_E"] = 'en-US-Standard-E', -- [18] FEMALE
["en_US_Standard_F"] = 'en-US-Standard-F', -- [19] FEMALE
["en_US_Standard_G"] = 'en-US-Standard-G', -- [20] FEMALE
["en_US_Standard_H"] = 'en-US-Standard-H', -- [21] FEMALE
["en_US_Standard_I"] = 'en-US-Standard-I', -- [22] MALE
["en_US_Standard_J"] = 'en-US-Standard-J', -- [23] MALE
["fr_FR_Standard_A"] = "fr-FR-Standard-A", -- Female
["fr_FR_Standard_B"] = "fr-FR-Standard-B", -- Male
["fr_FR_Standard_C"] = "fr-FR-Standard-C", -- Female
["fr_FR_Standard_D"] = "fr-FR-Standard-D", -- Male
["fr_FR_Standard_E"] = "fr-FR-Standard-E", -- Female
["de_DE_Standard_A"] = "de-DE-Standard-A", -- Female
["de_DE_Standard_B"] = "de-DE-Standard-B", -- Male
["de_DE_Standard_C"] = "de-DE-Standard-C", -- Female
["de_DE_Standard_D"] = "de-DE-Standard-D", -- Male
["de_DE_Standard_E"] = "de-DE-Standard-E", -- Male
["de_DE_Standard_F"] = "de-DE-Standard-F", -- Female
["es_ES_Standard_A"] = "es-ES-Standard-A", -- Female
["es_ES_Standard_B"] = "es-ES-Standard-B", -- Male
["es_ES_Standard_C"] = "es-ES-Standard-C", -- Female
["es_ES_Standard_D"] = "es-ES-Standard-D", -- Female
["it_IT_Standard_A"] = "it-IT-Standard-A", -- Female
["it_IT_Standard_B"] = "it-IT-Standard-B", -- Female
["it_IT_Standard_C"] = "it-IT-Standard-C", -- Male
["it_IT_Standard_D"] = "it-IT-Standard-D", -- Male
},
Wavenet = {
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- [1] FEMALE
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- [2] MALE
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- [3] FEMALE
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- [4] MALE
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- [5] FEMALE
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- [6] MALE
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- [7] MALE
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- [8] FEMALE
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-A', -- [9] FEMALE
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-B', -- [10] MALE
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-C', -- [11] FEMALE
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-D', -- [12] MALE
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-F', -- [13] FEMALE
["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- [14] MALE
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- [15] MALE
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- [16] FEMALE
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- [17] MALE
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- [18] FEMALE
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- [19] FEMALE
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- [20] FEMALE
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- [21] FEMALE
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- [22] MALE
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- [23] MALE
["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-A", -- Female
["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-B", -- Male
["fr_FR_Wavenet_C"] = "fr-FR-Wavenet-C", -- Female
["fr_FR_Wavenet_D"] = "fr-FR-Wavenet-D", -- Male
["fr_FR_Wavenet_E"] = "fr-FR-Wavenet-E", -- Female
["de_DE_Wavenet_A"] = "de-DE-Wavenet-A", -- Female
["de_DE_Wavenet_B"] = "de-DE-Wavenet-B", -- Male
["de_DE_Wavenet_C"] = "de-DE-Wavenet-C", -- Female
["de_DE_Wavenet_D"] = "de-DE-Wavenet-D", -- Male
["de_DE_Wavenet_E"] = "de-DE-Wavenet-E", -- Male
["de_DE_Wavenet_F"] = "de-DE-Wavenet-F", -- Female
["es_ES_Wavenet_B"] = "es-ES-Wavenet-B", -- Male
["es_ES_Wavenet_C"] = "es-ES-Wavenet-C", -- Female
["es_ES_Wavenet_D"] = "es-ES-Wavenet-D", -- Female
["it_IT_Wavenet_A"] = "it-IT-Wavenet-A", -- Female
["it_IT_Wavenet_B"] = "it-IT-Wavenet-B", -- Female
["it_IT_Wavenet_C"] = "it-IT-Wavenet-C", -- Male
["it_IT_Wavenet_D"] = "it-IT-Wavenet-D", -- Male
} ,
},
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add functions to add/remove freqs and modulations. -- TODO: Add functions to remove freqs and modulations.
-- DONE: Add coordinate. -- DONE: Add coordinate.
-- DONE: Add google. -- DONE: Add google.
@@ -144,8 +263,9 @@ MSRS.version="0.0.3"
-- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #string PathToSRS Path to the directory, where SRS is located.
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self -- @return #MSRS self
function MSRS:New(PathToSRS, Frequency, Modulation) function MSRS:New(PathToSRS, Frequency, Modulation, Volume)
-- Defaults. -- Defaults.
Frequency =Frequency or 143 Frequency =Frequency or 143
@@ -160,6 +280,13 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
self:SetModulations(Modulation) self:SetModulations(Modulation)
self:SetGender() self:SetGender()
self:SetCoalition() self:SetCoalition()
self:SetLabel()
self:SetVolume()
self.lid = string.format("%s-%s | ", self.name, self.version)
if not io or not os then
self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!")
end
return self return self
end end
@@ -202,12 +329,47 @@ function MSRS:GetPath()
return self.path return self.path
end end
--- Set SRS volume.
-- @param #MSRS self
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self
function MSRS:SetVolume(Volume)
local volume = Volume or 1
if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end
self.volume = volume
return self
end
--- Get SRS volume.
-- @param #MSRS self
-- @return #number Volume Volume - 1.0 is max, 0.0 is silence
function MSRS:GetVolume()
return self.volume
end
--- Set label.
-- @param #MSRS self
-- @param #number Label. Default "ROBOT"
-- @return #MSRS self
function MSRS:SetLabel(Label)
self.Label=Label or "ROBOT"
return self
end
--- Get label.
-- @param #MSRS self
-- @return #number Label.
function MSRS:GetLabel()
return self.Label
end
--- Set port. --- Set port.
-- @param #MSRS self -- @param #MSRS self
-- @param #number Port Port. Default 5002. -- @param #number Port Port. Default 5002.
-- @return #MSRS self -- @return #MSRS self
function MSRS:SetPort(Port) function MSRS:SetPort(Port)
self.port=Port or 5002 self.port=Port or 5002
return self
end end
--- Get port. --- Get port.
@@ -223,6 +385,7 @@ end
-- @return #MSRS self -- @return #MSRS self
function MSRS:SetCoalition(Coalition) function MSRS:SetCoalition(Coalition)
self.coalition=Coalition or 0 self.coalition=Coalition or 0
return self
end end
--- Get coalition. --- Get coalition.
@@ -249,6 +412,24 @@ function MSRS:SetFrequencies(Frequencies)
return self return self
end end
--- Add frequencies.
-- @param #MSRS self
-- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used.
-- @return #MSRS self
function MSRS:AddFrequencies(Frequencies)
-- Ensure table.
if type(Frequencies)~="table" then
Frequencies={Frequencies}
end
for _,_freq in pairs(Frequencies) do
table.insert(self.frequencies,_freq)
end
return self
end
--- Get frequencies. --- Get frequencies.
-- @param #MSRS self -- @param #MSRS self
-- @param #table Frequencies in MHz. -- @param #table Frequencies in MHz.
@@ -273,6 +454,24 @@ function MSRS:SetModulations(Modulations)
return self return self
end end
--- Add modulations.
-- @param #MSRS self
-- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used.
-- @return #MSRS self
function MSRS:AddModulations(Modulations)
-- Ensure table.
if type(Modulations)~="table" then
Modulations={Modulations}
end
for _,_mod in pairs(Modulations) do
table.insert(self.modulations,_mod)
end
return self
end
--- Get modulations. --- Get modulations.
-- @param #MSRS self -- @param #MSRS self
-- @param #table Modulations. -- @param #table Modulations.
@@ -391,22 +590,11 @@ function MSRS:PlaySoundFile(Soundfile, Delay)
local command=self:_GetCommand() local command=self:_GetCommand()
-- Append file. -- Append file.
command=command.." --file="..tostring(soundfile) command=command..' --file="'..tostring(soundfile)..'"'
-- Execute command.
self:_ExecCommand(command) self:_ExecCommand(command)
--[[
command=command.." > bla.txt"
-- Debug output.
self:I(string.format("MSRS PlaySoundfile command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
]]
end end
return self return self
@@ -432,16 +620,6 @@ function MSRS:PlaySoundText(SoundText, Delay)
-- Execute command. -- Execute command.
self:_ExecCommand(command) self:_ExecCommand(command)
--[[
command=command.." > bla.txt"
-- Debug putput.
self:I(string.format("MSRS PlaySoundfile command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
]]
end end
return self return self
@@ -467,37 +645,48 @@ function MSRS:PlayText(Text, Delay)
-- Execute command. -- Execute command.
self:_ExecCommand(command) self:_ExecCommand(command)
--[[
-- Check that length of command is max 255 chars or os.execute() will not work!
if string.len(command)>255 then
-- Create a tmp file.
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat"
local script = io.open(filename, "w+")
script:write(command.." && exit")
script:close()
-- Play command.
command=string.format("\"%s\"", filename)
-- Play file in 0.05 seconds
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
else
-- Debug output.
self:I(string.format("MSRS Text command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
end end
]] return self
end
--- Play text message via STTS with explicitly specified options.
-- @param #MSRS self
-- @param #string Text Text message.
-- @param #number Delay Delay in seconds, before the message is played.
-- @param #table Frequencies Radio frequencies.
-- @param #table Modulations Radio modulations.
-- @param #string Gender Gender.
-- @param #string Culture Culture.
-- @param #string Voice Voice.
-- @param #number Volume Volume.
-- @param #string Label Label.
-- @return #MSRS self
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
else
-- Ensure table.
if Frequencies and type(Frequencies)~="table" then
Frequencies={Frequencies}
end
-- Ensure table.
if Modulations and type(Modulations)~="table" then
Modulations={Modulations}
end
-- Get command line.
local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label)
-- Append text.
command=command..string.format(" --text=\"%s\"", tostring(Text))
-- Execute command.
self:_ExecCommand(command)
end end
return self return self
@@ -634,8 +823,9 @@ end
-- @param #number volume Volume. -- @param #number volume Volume.
-- @param #number speed Speed. -- @param #number speed Speed.
-- @param #number port Port. -- @param #number port Port.
-- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed!
-- @return #string Command. -- @return #string Command.
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port) function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label)
local path=self:GetPath() or STTS.DIRECTORY local path=self:GetPath() or STTS.DIRECTORY
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
@@ -648,21 +838,14 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
volume=volume or self.volume volume=volume or self.volume
speed=speed or self.speed speed=speed or self.speed
port=port or self.port port=port or self.port
label=label or self.Label
-- Replace modulation -- Replace modulation
modus=modus:gsub("0", "AM") modus=modus:gsub("0", "AM")
modus=modus:gsub("1", "FM") modus=modus:gsub("1", "FM")
-- This did not work well. Stopped if the transmission was a bit longer with no apparent error.
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
-- Command from orig STTS script. Works better for some unknown reason!
local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
-- Command. -- Command.
local command=string.format('%s/%s -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, "ROBOT") local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
-- Set voice or gender/culture. -- Set voice or gender/culture.
if voice then if voice then
@@ -671,7 +854,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
else else
-- Add gender. -- Add gender.
if gender and gender~="female" then if gender and gender~="female" then
command=command..string.format(" --gender=%s", tostring(gender)) command=command..string.format(" -g %s", tostring(gender))
end end
-- Add culture. -- Add culture.
if culture and culture~="en-GB" then if culture and culture~="en-GB" then
@@ -696,6 +879,378 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
return command return command
end end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Manages radio transmissions.
--
-- The purpose of the MSRSQUEUE class is to manage SRS text-to-speech (TTS) messages using the MSRS class.
-- This can be used to submit multiple TTS messages and the class takes care that they are transmitted one after the other (and not overlapping).
--
-- @type MSRSQUEUE
-- @field #string ClassName Name of the class "MSRSQUEUE".
-- @field #string lid ID for dcs.log.
-- @field #table queue The queue of transmissions.
-- @field #string alias Name of the radio queue.
-- @field #number dt Time interval in seconds for checking the radio queue.
-- @field #number Tlast Time (abs) when the last transmission finished.
-- @field #boolean checking If `true`, the queue update function is scheduled to be called again.
-- @extends Core.Base#BASE
MSRSQUEUE = {
ClassName = "MSRSQUEUE",
Debugmode = nil,
lid = nil,
queue = {},
alias = nil,
dt = nil,
Tlast = nil,
checking = nil,
}
--- Radio queue transmission data.
-- @type MSRSQUEUE.Transmission
-- @field #string text Text to be transmitted.
-- @field Sound.SRS#MSRS msrs MOOSE SRS object.
-- @field #number duration Duration in seconds.
-- @field #table subgroups Groups to send subtitle to.
-- @field #string subtitle Subtitle of the transmission.
-- @field #number subduration Duration of the subtitle being displayed.
-- @field #number frequency Frequency.
-- @field #number modulation Modulation.
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
-- @field #boolean isplaying If true, transmission is currently playing.
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
-- @field #number interval Interval in seconds before next transmission.
-- @field #boolean TransmitOnlyWithPlayers If true, only transmit if there are alive Players.
-- @field Core.Set#SET_CLIENT PlayerSet PlayerSet created when TransmitOnlyWithPlayers == true
--- Create a new MSRSQUEUE object for a given radio frequency/modulation.
-- @param #MSRSQUEUE self
-- @param #string alias (Optional) Name of the radio queue.
-- @return #MSRSQUEUE self The MSRSQUEUE object.
function MSRSQUEUE:New(alias)
-- Inherit base
local self=BASE:Inherit(self, BASE:New()) --#MSRSQUEUE
self.alias=alias or "My Radio"
self.dt=1.0
self.lid=string.format("MSRSQUEUE %s | ", self.alias)
return self
end
--- Clear the radio queue.
-- @param #MSRSQUEUE self
-- @return #MSRSQUEUE self The MSRSQUEUE object.
function MSRSQUEUE:Clear()
self:I(self.lid.."Clearning MSRSQUEUE")
self.queue={}
return self
end
--- Add a transmission to the radio queue.
-- @param #MSRSQUEUE self
-- @param #MSRSQUEUE.Transmission transmission The transmission data table.
-- @return #MSRSQUEUE self
function MSRSQUEUE:AddTransmission(transmission)
-- Init.
transmission.isplaying=false
transmission.Tstarted=nil
-- Add to queue.
table.insert(self.queue, transmission)
-- Start checking.
if not self.checking then
self:_CheckRadioQueue()
end
return self
end
--- Switch to only transmit if there are players on the server.
-- @param #MSRSQUEUE self
-- @param #boolean Switch If true, only send SRS if there are alive Players.
-- @return #MSRSQUEUE self
function MSRSQUEUE:SetTransmitOnlyWithPlayers(Switch)
self.TransmitOnlyWithPlayers = Switch
if Switch == false or Switch==nil then
if self.PlayerSet then
self.PlayerSet:FilterStop()
end
self.PlayerSet = nil
else
self.PlayerSet = SET_CLIENT:New():FilterStart()
end
return self
end
--- Create a new transmission and add it to the radio queue.
-- @param #MSRSQUEUE self
-- @param #string text Text to play.
-- @param #number duration Duration in seconds the file lasts. Default is determined by number of characters of the text message.
-- @param Sound.SRS#MSRS msrs MOOSE SRS object.
-- @param #number tstart Start time (abs) seconds. Default now.
-- @param #number interval Interval in seconds after the last transmission finished.
-- @param #table subgroups Groups that should receive the subtiltle.
-- @param #string subtitle Subtitle displayed when the message is played.
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
-- @param #number frequency Radio frequency if other than MSRS default.
-- @param #number modulation Radio modulation if other then MSRS default.
-- @return #MSRSQUEUE.Transmission Radio transmission table.
function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation)
if self.TransmitOnlyWithPlayers then
if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then
return self
end
end
-- Sanity checks.
if not text then
self:E(self.lid.."ERROR: No text specified.")
return nil
end
if type(text)~="string" then
self:E(self.lid.."ERROR: Text specified is NOT a string.")
return nil
end
-- Create a new transmission object.
local transmission={} --#MSRSQUEUE.Transmission
transmission.text=text
transmission.duration=duration or STTS.getSpeechTime(text)
transmission.msrs=msrs
transmission.Tplay=tstart or timer.getAbsTime()
transmission.subtitle=subtitle
transmission.interval=interval or 0
transmission.frequency=frequency
transmission.modulation=modulation
transmission.subgroups=subgroups
if transmission.subtitle then
transmission.subduration=subduration or transmission.duration
else
transmission.subduration=0 --nil
end
-- Add transmission to queue.
self:AddTransmission(transmission)
return transmission
end
--- Broadcast radio message.
-- @param #MSRSQUEUE self
-- @param #MSRSQUEUE.Transmission transmission The transmission.
function MSRSQUEUE:Broadcast(transmission)
if transmission.frequency then
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label)
else
transmission.msrs:PlayText(transmission.text)
end
local function texttogroup(gid)
-- Text to group.
trigger.action.outTextForGroup(gid, transmission.subtitle, transmission.subduration, true)
end
if transmission.subgroups and #transmission.subgroups>0 then
for _,_group in pairs(transmission.subgroups) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
local gid=group:GetID()
self:ScheduleOnce(4, texttogroup, gid)
end
end
end
end
--- Calculate total transmission duration of all transmission in the queue.
-- @param #MSRSQUEUE self
-- @return #number Total transmission duration.
function MSRSQUEUE:CalcTransmisstionDuration()
local Tnow=timer.getAbsTime()
local T=0
for _,_transmission in pairs(self.queue) do
local transmission=_transmission --#MSRSQUEUE.Transmission
if transmission.isplaying then
-- Playing for dt seconds.
local dt=Tnow-transmission.Tstarted
T=T+transmission.duration-dt
else
T=T+transmission.duration
end
end
return T
end
--- Check radio queue for transmissions to be broadcasted.
-- @param #MSRSQUEUE self
-- @param #number delay Delay in seconds before checking.
function MSRSQUEUE:_CheckRadioQueue(delay)
-- Transmissions in queue.
local N=#self.queue
-- Debug info.
self:T2(self.lid..string.format("Check radio queue %s: delay=%.3f sec, N=%d, checking=%s", self.alias, delay or 0, N, tostring(self.checking)))
if delay and delay>0 then
-- Delayed call.
self:ScheduleOnce(delay, MSRSQUEUE._CheckRadioQueue, self)
-- Checking on.
self.checking=true
else
-- Check if queue is empty.
if N==0 then
-- Debug info.
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
-- Queue is now empty. Nothing to else to do. We start checking again, if a transmission is added.
self.checking=false
return
end
-- Get current abs time.
local time=timer.getAbsTime()
-- Checking on.
self.checking=true
-- Set dt.
local dt=self.dt
local playing=false
local next=nil --#MSRSQUEUE.Transmission
local remove=nil
for i,_transmission in ipairs(self.queue) do
local transmission=_transmission --#MSRSQUEUE.Transmission
-- Check if transmission time has passed.
if time>=transmission.Tplay then
-- Check if transmission is currently playing.
if transmission.isplaying then
-- Check if transmission is finished.
if time>=transmission.Tstarted+transmission.duration then
-- Transmission over.
transmission.isplaying=false
-- Remove ith element in queue.
remove=i
-- Store time last transmission finished.
self.Tlast=time
else -- still playing
-- Transmission is still playing.
playing=true
dt=transmission.duration-(time-transmission.Tstarted)
end
else -- not playing yet
local Tlast=self.Tlast
if transmission.interval==nil then
-- Not playing ==> this will be next.
if next==nil then
next=transmission
end
else
if Tlast==nil or time-Tlast>=transmission.interval then
next=transmission
else
end
end
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
if next or Tlast then
break
end
end
else
-- Transmission not due yet.
end
end
-- Found a new transmission.
if next~=nil and not playing then
-- Debug info.
self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time))
-- Call SRS.
self:Broadcast(next)
next.isplaying=true
next.Tstarted=time
dt=next.duration
end
-- Remove completed call from queue.
if remove then
-- Remove from queue.
table.remove(self.queue, remove)
N=N-1
-- Check if queue is empty.
if #self.queue==0 then
-- Debug info.
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
self.checking=false
return
end
end
-- Check queue.
self:_CheckRadioQueue(dt)
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More