Compare commits

...

201 Commits

Author SHA1 Message Date
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
Applevangelist
5260b2b430 And don't forget Fahrenheit 2022-03-26 14:46:52 +01:00
Applevangelist
0213bc7aef Correcting Celcius to Celsius 2022-03-26 14:43:06 +01:00
Applevangelist
ca8b0899d0 docu changes 2022-03-23 07:56:52 +01:00
Applevangelist
a1f5c0ab9b CSAR/CTLD - added type to script 2022-03-22 10:38:20 +01:00
Applevangelist
b0e3f82d27 AIRBASE - added 10 new AB names in Syria 2022-03-18 09:48:50 +01:00
Applevangelist
327ab4766b changed descriptions 2022-03-18 07:59:58 +01:00
Applevangelist
3aee8a49c1 Added CONTROLLABLE SetSpeed() and SetAltitude() 2022-03-18 07:39:48 +01:00
Applevangelist
57de0b7351 docu fixes 2022-03-16 08:45:27 +01:00
Applevangelist
4df1e310a3 CSAR - remove timer check for "open the door" message to make behaviour more realistic 2022-03-14 09:12:24 +01:00
TommyC81
802a77238a Range re-formatting and documentation re-fixing (#1691)
* Update Range.lua

Code formatting.

* RANGE - Documentation fixes.
2022-03-12 09:47:14 +01:00
Applevangelist
85a7e18fae SCORING: Corrected calc error in summary scoring functions 2022-03-11 10:18:59 +01:00
Applevangelist
26b1fd3487 Update CTLD.lua (#1692)
minor nil check
2022-03-09 10:26:16 +01:00
Applevangelist
ae7a363012 CTLD - small extra nil check in _GetUnitCargoMass(Unit) 2022-03-03 12:34:43 +01:00
Applevangelist
473362af45 CTLD - small fix for finding crates when using engineers 2022-03-03 11:02:25 +01:00
Applevangelist
cde0d09f0a CSAR - remove double class 2022-02-21 19:36:22 +01:00
Applevangelist
94f093826b SEAD - adding workaround for AGM_154 which lost target data 2022-02-21 08:36:37 +01:00
Applevangelist
84f231ea08 CSAR - added wet feet check if also using csarUsePara (no landing event triggered) 2022-02-18 08:22:47 +01:00
Applevangelist
3d9bb14713 CSAR - added "wet feet" option for a 2nd template to be used over water 2022-02-17 17:41:32 +01:00
Applevangelist
6c6cdcf763 CTLD - fix list/build side effect from adding weight limits to helos 2022-02-16 10:06:04 +01:00
Applevangelist
00c8690e61 CTLD - corrected default weight limits when using CTLD:UnitCapabilities() - was setting loadable weight to zero 2022-02-15 18:07:26 +01:00
Applevangelist
a0d492cd2d added back GROUP:GetHighestThreat() 2022-02-15 14:41:31 +01:00
Applevangelist
ba5ccc1021 CTLD:SetTroopDropZoneRadius(Radius) 2022-02-13 12:08:23 +01:00
Applevangelist
a4163017d5 CSAR - CSAR:SpawnCSARAtZone(Zone ...) - Zone can now be a ZONE object as well as a string 2022-02-08 07:49:04 +01:00
Applevangelist
7f4a5c48ec CTLD - add subcategory option, added CTLD:AddCTLDZoneFromAirbase(AirbaseName, Type, Color, Active, HasBeacon) 2022-02-08 07:47:48 +01:00
Applevangelist
9f7588b245 DETECTION - added 3 missing functions 2022-02-04 08:54:02 +01:00
Applevangelist
63cbc0c55b RANGE - added option to save target sheet 2022-02-03 10:01:48 +01:00
Applevangelist
28eb7a678c CTLD - Added Hercules support for crates, troops & vehicles loaded with the help of the ground crew and dropped from the plane. Added weight checks for loaded crates. 2022-02-03 10:00:19 +01:00
Applevangelist
a95c49915a SET - correct error in intersection 2022-02-01 08:02:51 +01:00
Applevangelist
b7adc6add6 POINT - added function to name/stop fires and smoke 2022-01-30 09:47:11 +01:00
Applevangelist
2aeebf280b AI Dispatchers - add ability to add/remove resources to/from a squad 2022-01-24 09:54:16 +01:00
Applevangelist
8ac06979f0 CTLD added color options for smoke drops, droppable beacons w/ timer 2022-01-23 11:42:16 +01:00
Applevangelist
2d4f90d5eb Added new Callsigns as per 2.7.9 2022-01-23 11:37:07 +01:00
TommyC81
d7a44a639d Update Detection.lua (#1685)
Code formatting. Fix minor typos, errors, and references in documentation.
2022-01-23 11:21:59 +01:00
Applevangelist
7bfa05f47d DETECTION - corrected call for Vec2 in zone 2022-01-19 07:52:59 +01:00
Applevangelist
c7bbb09195 Added doors check for UH-60L 2022-01-16 17:07:44 +01:00
Applevangelist
41c9c15ae5 CTLD, CSAR - added support for UH-60L 2022-01-16 11:39:19 +01:00
Applevangelist
964831becf CTLD - make container shape configureable 2022-01-15 11:34:23 +01:00
Applevangelist
e847b92cce RAT - Docu corrections 2022-01-11 15:14:40 +01:00
TommyC81
c2ecd86bb4 Minor fixes (#1684)
* Update AI_A2A_Dispatcher.lua

Minor code formatting.

* Update Airbase.lua

Code formatting.
2022-01-10 15:10:30 +01:00
Applevangelist
70d922fad6 SHort name mina AP 2022-01-04 15:10:25 +01:00
Applevangelist
854bee0519 Mark visible as deprecated 2021-12-31 17:52:24 +01:00
Applevangelist
d54d991bdd Fix ATC new NTTR AFB names 2021-12-31 17:52:24 +01:00
Applevangelist
a4b600b97d Update Airbase.lua (#1680)
Change Nevada AFB names with latest stable patch
2021-12-31 17:44:01 +01:00
TommyC81
d6cfaa5050 Update Scoring.lua (#1674)
Code formatting and minor typo/documentation fixes.
2021-12-28 11:01:44 +01:00
TommyC81
00d1aec210 General fixes (#1673)
* General minor

Code formatting and minor typo/document fixes.

* Update Marker.lua

Code formatting and minor typo/document fixes. Note specifically the correction of "self.tocoaliton" to "self.tocoalition".
2021-12-28 11:01:05 +01:00
Applevangelist
f62e3391e1 SCORING - make treason and fratricide switchable 2021-12-28 08:25:31 +01:00
TommyC81
4a406604bd Core modules formatting (#1670)
* Update Fsm.lua

Code formatting and minor typo/documentation fixes.

* Update Goal.lua

Code formatting and minor typo/documentation fixes.

* Update Menu.lua

Code formatting and minor typo/documentation fixes.

* Update Message.lua

Code formatting and minor typo/documentation fixes.

* Update Report.lua

Code formatting and minor typo/documentation fixes.

* Update ScheduleDispatcher.lua

Code formatting and minor typo/documentation fixes.

* Update Scheduler.lua

Code formatting and minor typo/documentation fixes.

* Update Settings.lua

Code formatting and minor typo/documentation fixes.

* Update Spawn.lua

Code formatting and minor typo/documentation fixes.
2021-12-20 12:59:56 +01:00
TommyC81
55cee46a8d Core code formatting and typo fixes. (#1669)
* Update Beacon.lua

Code formatting and minor typo/text fixes.

* Update Database.lua

Code formatting and minor typo/text fixes.

* Update Event.lua

Code formatting and minor typo/text fixes.
2021-12-19 08:47:07 +01:00
TommyC81
607c52c0b7 Update CSAR.lua (#1665)
Code formatting and general typo/documentation fixes.
2021-12-17 09:07:32 +01:00
TommyC81
2694321256 Update CTLD.lua (#1666)
Code formatting. Minor typos and text fixes.
2021-12-17 09:07:24 +01:00
TommyC81
e8e790102a Update Base.lua (#1667)
Code Formatting. General whitespace and spelling.
2021-12-17 09:07:13 +01:00
Applevangelist
9c5561921b Noise reducing measures 2021-12-15 13:46:07 +01:00
Applevangelist
78fab9ab0c CSAR - make beacon length configureable 2021-12-14 09:50:34 +01:00
TommyC81
058c750bc6 Update Set.lua (#1663)
Additional minor code formatting and typo fixes.
2021-12-13 16:52:01 +01:00
Applevangelist
f29da39dff CSAR - override suppressmessages for menu driven information 2021-12-12 19:48:36 +01:00
Applevangelist
624a7c70c9 CTLD - corrected landheight of dropped smoke 2021-12-12 19:30:33 +01:00
TommyC81
0447ee2d9e Update Airboss.lua (#1664)
Code formatting. Typo fixing.
2021-12-12 19:15:30 +01:00
TommyC81
456fcd38d0 Code and documentation tweaks. (#1662)
* Update Point.lua

General code formatting.

* Update Set.lua

General code formatting.

* Update Positionable.lua

Code formatting, and documentation fixes.
2021-12-12 13:53:04 +01:00
Applevangelist
a3cab7097a SET - Typos 2021-12-11 19:41:02 +01:00
Applevangelist
848e2f1294 SET - Added Zone Filter for STATIC 2021-12-11 14:24:20 +01:00
Applevangelist
ef4dc48ea1 CTLD - added option for smoke/flare at position 2021-12-11 14:14:44 +01:00
Applevangelist
2138a33292 CSAR fixed KM message 2021-12-10 12:02:50 +01:00
TommyC81
a59343b987 Code formatting, spelling and documentation fixes. (#1661)
* Update Point.lua

Minor code formatting fix.

* Update Airboss.lua

Minor code formatting and documentation fixes.

* Update Set.lua

Code formatting, spelling and documentation fixes.

* Update ATIS.lua

Code formatting, spelling and documentation fixes.

* Update Task_A2A_Dispatcher.lua

Minor code formatting and documentation fixes. Added TODO re. possible unused variables.
2021-12-09 17:51:38 +01:00
TommyC81
a4ca4bdc99 Code and documentation fixes (#1659)
* Update .lua-format

Adjust for observed coding standards.

* Update ATIS.lua

Correct measurement units and spelling (also changed in Utils.lua).

* Update Utils.lua

Format the file, fix typos, adjust minor text. Rename "celcius" to "celsius". Rename "farenheit" to "fahrenheit".

* Update Warehouse.lua

Adjust measurement unit text.

* Update STTS.lua

Adjust formatting, minor typos, and fix error in documentation (missing blank rows) introduced in previous update.

* Update Range.lua

Adjust minor typos and code formatting. Adjust for celsius/fahrenheit typo correction.

* Update PseudoATC.lua

Adjust for celsius/fahrenheit typo correction in utils.lua.

* Update Point.lua

Code formatting, fix minor typos, adjust for celsius/fahrenheit corrrection in utils.lua.

* Update Range.lua

Minor documentation fix.
2021-12-08 19:52:29 +01:00
TommyC81
a57b9a9081 Update Range.lua (#1658)
Adjust some minor spelling and figure out the quirks of luadocumentor...
2021-12-07 18:13:51 +01:00
Applevangelist
249a6af456 Some false values seem to be in need of being set explicitly 2021-12-06 15:16:01 +01:00
Applevangelist
18685d1a94 Update Utils.lua (#1655)
Small bugfix for UTILS.LoadSetOfGroups
2021-12-06 14:57:49 +01:00
TommyC81
493b090534 LuaFormatter, RANGE formatting, and minor code fixes. (#1653)
* Create .lua-format

* Update Range.lua

Format code.

* Display distance in meters from bombtarget.

All other numbers, including menu lists etc. uses meters. Feet kept in parens.

* Fixed displaying of targetname when bombing.
2021-12-06 14:57:36 +01:00
Applevangelist
389adab9b8 SET - slight change to remove function 2021-12-06 10:26:32 +01:00
TommyC81
6360b8c58f General documentation and code fixes (#1650)
Documentation updates for correctness and clarity.
General code formatting updates.
Ajustment to POSITIONABLE:GetCoord to make use of existing POINT:UpdateFromVec3.
Added comments about clarifying the difference between POSITIONABLE:GetCoordinate() and POSITIONABLE:GetCoord() and to perhaps consider a renaming or merging the functions with an optional flag.
2021-12-04 18:50:05 +01:00
TommyC81
b0818977cf Code formatting. (#1651)
General code formatting and fixes of minor typos.
2021-12-04 18:49:57 +01:00
TommyC81
32deb160ef Formatting and typos (#1652)
* Formatting and typo fixes.

General formatting and typo fixes.

* Update STTS.lua

Keep class table on separate lines.
2021-12-04 18:49:47 +01:00
Applevangelist
2ba5215036 Merge pull request #1649 from TommyC81/RANGE_FIXES
Update Range.lua
2021-12-02 19:25:42 +01:00
Tommy Carlsson
73ea4c7b32 Update Range.lua
Fix documentation - including typos and updates to no longer correct text.
Remove duplicated RANGE.Defaults.goodthitrange value.
DCS seems to use headings 0-359 (i.e. <360), thus also deduct 360 from an exact 360 heading.
2021-12-02 20:49:48 +04:00
Applevangelist
46c37ff06a Merge pull request #1648 from TommyC81/A2G_DISPATCHER_FIXES
Update AI_A2G_Dispatcher.lua
2021-12-01 11:54:56 +01:00
Tommy Carlsson
ff4708b624 Update AI_A2G_Dispatcher.lua
General code formatting fixes, and correction of typos/examples.
2021-12-01 13:41:27 +04:00
Applevangelist
82b2c84f13 Merge pull request #1647 from TommyC81/A2G_DISPATCHER_DOCUMENTATION_2
Update AI_A2G_Dispatcher.lua
2021-12-01 09:17:46 +01:00
Tommy Carlsson
48e8b1a9b3 Update AI_A2G_Dispatcher.lua
More updates and fixes.

Update AI_A2G_Dispatcher.lua

General documentation updates.
2021-12-01 11:34:18 +04:00
Applevangelist
1ad7c54ace Merge pull request #1645 from TommyC81/A2G_Dispatcher_documentation
Update AI_A2G_Dispatcher.lua
2021-11-30 17:02:40 +01:00
Tommy Carlsson
9998c86c1f Update AI_A2G_Dispatcher.lua
Fix typos and incorrect references (leftovers) to A2A/CAP etc.
2021-11-30 19:37:26 +04:00
Applevangelist
01a707ae0a Small changes in GROUP 2021-11-29 07:57:07 +01:00
Applevangelist
570e8388fc Bug fixes 2021-11-27 17:30:25 +01:00
Applevangelist
9c5b5d4633 CSAR - added changes by Shagrat for Casevac
CSAR - don't make usePara default. Added coalition check if using the parachute landing event
2021-11-14 13:34:23 +01:00
Applevangelist
822bf13626 Merge pull request #1625 from FlightControl-Master/Applevangelist-unit-1
Wrapper Unit - fix for missile count
2021-11-14 12:59:51 +01:00
Applevangelist
c520de0087 Wrapper Unit - fix for missile count
Wrapper Unit - fix for missile count, issue #1624
2021-11-14 12:57:00 +01:00
Applevangelist
0e9076efa3 SEAD - align to dev changes, allow callback on SEAD events 2021-11-11 16:02:13 +01:00
Applevangelist
74cd5e3387 MANTIS - added docu and addition os SEAD events 2021-11-11 16:02:13 +01:00
Applevangelist
65c92be09e Merge pull request #1621 from FlightControl-Master/Applevangelist-casevac
Update CSAR.lua
2021-11-06 15:30:15 +01:00
Applevangelist
8e776cb3ab Update CSAR.lua
Adding CASEVAC option by Shagrat
2021-11-06 15:29:09 +01:00
Applevangelist
ab6cd2b751 Fix cleanup exisiting crates 2021-10-31 11:51:31 +01:00
Applevangelist
18c3d990fc Merge pull request #1618 from rollnthndr/master
Enabled SSML when using Google text-to-speech.
2021-10-31 08:15:25 +01:00
Rolln
19d5cb8ecb Added a command line option that will enable
SSML support when using Google text-to-speech.
2021-10-30 23:35:27 -06:00
Applevangelist
2b56a78255 Merge pull request #1617 from Penecruz/master
Airboss V/STOL updates
2021-10-30 22:05:21 +02:00
Penecruz
176d9df476 Merge branch 'FlightControl-Master:master' into master 2021-10-31 06:59:50 +11:00
Penecruz
c94275cb8b Airboss V/STOL updates
-Add additional Airboss V/STOL carrier HMAS Canberra L02
-Add Waveoff for AV-8B
-Add Cut Pass if Land without LSO clearance
-Changes to V/STOL groove timings
-Stabilise call when over the V/STOL landing spot
-larger abeam landing spot margin to allow decelerating to stable abeam and still be cleared to land
-Abeam area now extends further aft to allow LSO clearance 45-90 as per NATOPS
-Minor document changes
2021-10-31 06:41:58 +11:00
Applevangelist
45dbce3677 Speedmax returning 0 not nil 2021-10-30 16:33:18 +02:00
Applevangelist
18745158a3 Speedmax returning 0 not nil 2021-10-30 16:33:14 +02:00
Applevangelist
98c6c88391 Completed GetSetComplement 2021-10-29 18:32:13 +02:00
Frank
2e4fd72781 Update Fox.lua
Removed incomplete line `--@field #boolean`
2021-10-28 10:18:43 +02:00
Applevangelist
80ced88ef1 Fix for docs build 2021-10-28 08:31:14 +02:00
81 changed files with 25434 additions and 22889 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,33 @@
# 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

@@ -40,8 +40,8 @@
-- --
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) -- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
-- --
@@ -71,7 +71,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --

File diff suppressed because it is too large Load Diff

View File

@@ -42,8 +42,8 @@
-- --
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) -- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) -- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
-- --
@@ -73,7 +73,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --

View File

@@ -39,8 +39,8 @@
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
-- --
@@ -68,7 +68,7 @@
-- * **RTB** ( Group ): Route the AI to the home base. -- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets. -- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets. -- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set or Get the AI controllable -- ## 3. Set or Get the AI controllable
-- --
@@ -100,8 +100,8 @@
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL -- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
-- --
-- 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 treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold 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 targetted 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.
@@ -109,7 +109,7 @@
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL -- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
-- --
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on. -- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage treshold 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_A2A_PATROL.ManageDamage}() to have this proces in place. -- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
-- --
-- === -- ===

File diff suppressed because it is too large Load Diff

View File

@@ -42,8 +42,8 @@
-- --
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) -- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) -- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
-- --

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
@@ -370,11 +373,11 @@ 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 treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold 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 targetted 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 treshold 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.
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. -- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_AIR self -- @return #AI_AIR self
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime ) function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
@@ -387,14 +390,14 @@ function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
return self return self
end end
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. --- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on. -- However, damage cannot be foreseen early on.
-- Therefore, when the damage treshold is reached, -- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB). -- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated. -- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. -- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. -- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_AIR self -- @return #AI_AIR self
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold ) function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
@@ -476,7 +479,7 @@ function AI_AIR:onafterStatus()
local Fuel = self.Controllable:GetFuelMin() local Fuel = self.Controllable:GetFuelMin()
-- If the fuel in the controllable is below the treshold percentage, -- If the fuel in the controllable is below the threshold percentage,
-- then send for refuel in case of a tanker, otherwise RTB. -- then send for refuel in case of a tanker, otherwise RTB.
if Fuel < self.FuelThresholdPercentage then if Fuel < self.FuelThresholdPercentage then
@@ -576,6 +579,19 @@ 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
@@ -585,11 +601,13 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
if AIGroup and AIGroup:IsAlive() then if AIGroup and AIGroup:IsAlive() then
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" ) self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
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 +615,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

@@ -10,11 +10,11 @@
-- * Setup (CAS) Controlled Air Support squadrons, to attack closeby enemy ground units near friendly installations. -- * Setup (CAS) Controlled Air Support squadrons, to attack closeby enemy ground units near friendly installations.
-- * Setup (BAI) Battleground Air Interdiction squadrons to attack remote enemy ground units and targets. -- * Setup (BAI) Battleground Air Interdiction squadrons to attack remote enemy ground units and targets.
-- * Define and use a detection network controlled by recce. -- * Define and use a detection network controlled by recce.
-- * Define AIR defense squadrons at airbases, farps and carriers. -- * Define AIR defense squadrons at airbases, FARPs and carriers.
-- * Enable airbases for AIR defenses. -- * Enable airbases for AIR defenses.
-- * Add different planes and helicopter templates to squadrons. -- * Add different planes and helicopter templates to squadrons.
-- * Assign squadrons to execute a specific engagement type depending on threat level of the detected ground enemy unit composition. -- * Assign squadrons to execute a specific engagement type depending on threat level of the detected ground enemy unit composition.
-- * Add multiple squadrons to different airbases, farps or carriers. -- * Add multiple squadrons to different airbases, FARPs or carriers.
-- * Define different ranges to engage upon. -- * Define different ranges to engage upon.
-- * Establish an automatic in air refuel process for planes using refuel tankers. -- * Establish an automatic in air refuel process for planes using refuel tankers.
-- * Setup default settings for all squadrons and AIR defenses. -- * Setup default settings for all squadrons and AIR defenses.
@@ -40,7 +40,7 @@
-- --
-- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system. -- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system.
-- --
-- Before you start using the AI_AIR_DISPATCHER, ask youself the following questions. -- Before you start using the AI_AIR_DISPATCHER, ask yourself the following questions.
-- --
-- --
-- ## 1. Which coalition am I modeling an AIR defense system for? blue or red? -- ## 1. Which coalition am I modeling an AIR defense system for? blue or red?
@@ -128,7 +128,7 @@
-- 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.
-- --
-- --
-- ## 7. Where will the Squadrons be located? On Airbases? On Carrier Ships? On Farps? -- ## 7. Where will the Squadrons be located? On Airbases? On Carrier Ships? On FARPs?
-- --
-- Squadrons are placed at the **home base** on an **airfield**, **carrier** or **farp**. -- Squadrons are placed at the **home base** on an **airfield**, **carrier** or **farp**.
-- Carefully plan where each Squadron will be located as part of the defense system required for mission effective defenses. -- Carefully plan where each Squadron will be located as part of the defense system required for mission effective defenses.
@@ -354,7 +354,7 @@ do -- AI_AIR_DISPATCHER
-- **DetectionSetGroup** is then calling `FilterStart()`, which is starting the dynamic filtering or inclusion of these groups. -- **DetectionSetGroup** is then calling `FilterStart()`, which is starting the dynamic filtering or inclusion of these groups.
-- Note that any destroy or new spawn of a group having a name, starting with the above prefix, will be removed or added to the set. -- Note that any destroy or new spawn of a group having a name, starting with the above prefix, will be removed or added to the set.
-- --
-- Then a new detection object is created from the class `DETECTION_AREAS`. A grouping radius of 1000 meters (1km) is choosen. -- Then a new detection object is created from the class `DETECTION_AREAS`. A grouping radius of 1000 meters (1km) is chosen.
-- --
-- The `Detection` object is then passed to the @{#AI_AIR_DISPATCHER.New}() method to indicate the reconnaissance network -- The `Detection` object is then passed to the @{#AI_AIR_DISPATCHER.New}() method to indicate the reconnaissance network
-- configuration and setup the AIR defense detection mechanism. -- configuration and setup the AIR defense detection mechanism.
@@ -647,7 +647,7 @@ do -- AI_AIR_DISPATCHER
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway. -- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway.
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. -- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines.
-- --
-- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. -- You can use these methods to minimize the airbase coordination overhead and to increase the airbase efficiency.
-- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the -- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the
-- A2A defense system, as no new CAP or GCI planes can takeoff. -- A2A defense system, as no new CAP or GCI planes can takeoff.
-- Note that the method @{#AI_AIR_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft. -- Note that the method @{#AI_AIR_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft.
@@ -724,13 +724,13 @@ do -- AI_AIR_DISPATCHER
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron. -- Use the method @{#AI_AIR_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron.
-- --
-- ## 4. Set the **fuel treshold**. -- ## 4. Set the **fuel threshold**.
-- --
-- When aircraft get **out of fuel** to a certain %-tage, which is by default **15% (0.15)**, there are two possible actions that can be taken: -- When aircraft get **out of fuel** to a certain %, which is by default **15% (0.15)**, there are two possible actions that can be taken:
-- - The aircraft will go RTB, and will be replaced with a new aircraft if possible. -- - The aircraft will go RTB, and will be replaced with a new aircraft if possible.
-- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron. -- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron.
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel treshold** of the aircraft for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel threshold** of the aircraft for all squadrons.
-- --
-- ## 6. Other configuration options -- ## 6. Other configuration options
-- --
@@ -786,17 +786,17 @@ do -- AI_AIR_DISPATCHER
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons.
-- --
-- ## 10.5. Default RTB fuel treshold. -- ## 10.5. Default RTB fuel threshold.
-- --
-- When an airplane gets **out of fuel** to a certain %-tage, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable. -- When an airplane gets **out of fuel** to a certain %, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable.
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel treshold** of spawned airplanes for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel threshold** of spawned airplanes for all squadrons.
-- --
-- ## 10.6. Default RTB damage treshold. -- ## 10.6. Default RTB damage threshold.
-- --
-- When an airplane is **damaged** to a certain %-tage, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable. -- When an airplane is **damaged** to a certain %, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable.
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage treshold** of spawned airplanes for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage threshold** of spawned airplanes for all squadrons.
-- --
-- ## 10.7. Default settings for **patrol**. -- ## 10.7. Default settings for **patrol**.
-- --
@@ -829,7 +829,7 @@ do -- AI_AIR_DISPATCHER
-- --
-- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected.
-- Then, use the method @{#AI_AIR_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher. -- Then, use the method @{#AI_AIR_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher.
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the % left in the defender airplane tanks when a refuel action is needed.
-- --
-- When the tanker specified is alive and in the air, the tanker will be used for refuelling. -- When the tanker specified is alive and in the air, the tanker will be used for refuelling.
-- --
@@ -843,7 +843,7 @@ do -- AI_AIR_DISPATCHER
-- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 ) -- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 )
-- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 ) -- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 )
-- --
-- -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left. -- -- Set the default tanker for refuelling to "Tanker", when the default fuel threshold has reached 90% fuel left.
-- A2ADispatcher:SetDefaultFuelThreshold( 0.9 ) -- A2ADispatcher:SetDefaultFuelThreshold( 0.9 )
-- A2ADispatcher:SetDefaultTanker( "Tanker" ) -- A2ADispatcher:SetDefaultTanker( "Tanker" )
-- --
@@ -883,9 +883,6 @@ do -- AI_AIR_DISPATCHER
-- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes -- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes
-- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players. -- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players.
-- --
--
--
--
-- @field #AI_AIR_DISPATCHER -- @field #AI_AIR_DISPATCHER
AI_AIR_DISPATCHER = { AI_AIR_DISPATCHER = {
ClassName = "AI_AIR_DISPATCHER", ClassName = "AI_AIR_DISPATCHER",
@@ -949,7 +946,6 @@ do -- AI_AIR_DISPATCHER
--- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue --- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue
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 = {
@@ -958,9 +954,9 @@ do -- AI_AIR_DISPATCHER
} }
--- AI_AIR_DISPATCHER constructor. --- AI_AIR_DISPATCHER constructor.
-- This is defining the AIR DISPATCHER for one coaliton. -- This is defining the AIR DISPATCHER for one coalition.
-- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units. -- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units.
-- The Detection object is polymorphic, depending on the type of detection object choosen, the detection will work differently. -- The Detection object is polymorphic, depending on the type of detection object chosen, the detection will work differently.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network.
-- @return #AI_AIR_DISPATCHER self -- @return #AI_AIR_DISPATCHER self
@@ -977,7 +973,7 @@ do -- AI_AIR_DISPATCHER
-- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 ) -- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
function AI_AIR_DISPATCHER:New( Detection ) function AI_AIR_DISPATCHER:New( Detection )
@@ -1435,17 +1431,17 @@ do -- AI_AIR_DISPATCHER
end end
--- Set the default damage treshold when defenders will RTB. --- Set the default damage threshold when defenders will RTB.
-- The default damage treshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB. -- The default damage threshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB. -- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the % of the damage threshold before going RTB.
-- @return #AI_AIR_DISPATCHER -- @return #AI_AIR_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default damage treshold. -- -- Now Setup the default damage threshold.
-- AIRDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged. -- AIRDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged.
-- --
function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold ) function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold )
@@ -1989,7 +1985,7 @@ do -- AI_AIR_DISPATCHER
--- Defines the default amount of extra planes that will take-off as part of the defense system. --- Defines the default amount of extra planes that will take-off as part of the defense system.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. -- @param #number Overhead The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, -- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles... -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
@@ -2028,7 +2024,7 @@ do -- AI_AIR_DISPATCHER
--- Defines the amount of extra planes that will take-off as part of the defense system. --- Defines the amount of extra planes that will take-off as part of the defense system.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. -- @param #number Overhead The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, -- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles... -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
@@ -2068,7 +2064,7 @@ do -- AI_AIR_DISPATCHER
--- Gets the overhead of planes as part of the defense system, in comparison with the attackers. --- Gets the overhead of planes as part of the defense system, in comparison with the attackers.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @return #number The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. -- @return #number The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, -- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles... -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
@@ -2674,17 +2670,17 @@ do -- AI_AIR_DISPATCHER
return self return self
end end
--- Set the default fuel treshold when defenders will RTB or Refuel in the air. --- Set the default fuel threshold when defenders will RTB or Refuel in the air.
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed. -- The fuel threshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
-- @return #AI_AIR_DISPATCHER -- @return #AI_AIR_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold ) function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
@@ -2695,18 +2691,18 @@ do -- AI_AIR_DISPATCHER
end end
--- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air. --- Set the fuel threshold for the squadron when defenders will RTB or Refuel in the air.
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed. -- The fuel threshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
-- @return #AI_AIR_DISPATCHER -- @return #AI_AIR_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold ) function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
@@ -2726,7 +2722,7 @@ do -- AI_AIR_DISPATCHER
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
-- -- Now Setup the default tanker. -- -- Now Setup the default tanker.
@@ -2749,7 +2745,7 @@ do -- AI_AIR_DISPATCHER
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the squadron fuel treshold. -- -- Now Setup the squadron fuel threshold.
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
-- -- Now Setup the squadron tanker. -- -- Now Setup the squadron tanker.
@@ -2847,7 +2843,7 @@ do -- AI_AIR_DISPATCHER
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount ) function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount )
-- First, count the active AIGroups Units, targetting the DetectedSet -- First, count the active AIGroups Units, targeting the DetectedSet
local DefendersEngaged = 0 local DefendersEngaged = 0
local DefendersTotal = 0 local DefendersTotal = 0

View File

@@ -42,8 +42,8 @@
-- --
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) -- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) -- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
-- --
@@ -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

@@ -39,8 +39,8 @@
-- --
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) -- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
-- --
@@ -70,7 +70,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --

View File

@@ -49,7 +49,7 @@
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, -- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits. -- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. -- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- --
-- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG) -- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG)
-- --
@@ -87,7 +87,7 @@
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone. -- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
-- It can be notified to go RTB through the **RTB** event. -- It can be notified to go RTB through the **RTB** event.
-- --
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG) -- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG)
-- --
@@ -117,7 +117,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. -- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task. -- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task.
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object** -- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
-- --
@@ -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(
@@ -602,7 +602,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
self:SetRefreshTimeInterval( 2 ) self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated() self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting self:__Target( -2 ) -- Start targeting
end end
end end

View File

@@ -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

@@ -4,13 +4,13 @@
-- --
-- * Patrol AI airplanes within a given zone. -- * Patrol AI airplanes within a given zone.
-- * Trigger detected events when enemy airplanes are detected. -- * Trigger detected events when enemy airplanes are detected.
-- * Manage a fuel treshold 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)
-- --
-- === -- ===
-- --
@@ -65,8 +65,8 @@
-- --
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) -- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
-- --
@@ -96,7 +96,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --
@@ -428,8 +428,12 @@ 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 -- flight dead at this point
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() return self
end
--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(

View File

@@ -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)
-- --
-- === -- ===
-- --
@@ -49,7 +49,7 @@
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, -- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits. -- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. -- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- --
-- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG) -- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG)
-- --
@@ -87,7 +87,7 @@
-- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone. -- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone.
-- It can be notified to go RTB through the **RTB** event. -- It can be notified to go RTB through the **RTB** event.
-- --
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG) -- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG)
-- --
@@ -117,7 +117,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. -- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- === -- ===
-- --
@@ -460,7 +460,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --TODO: 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(
@@ -520,7 +520,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
self:SetRefreshTimeInterval( 2 ) self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated() self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting self:__Target( -2 ) -- Start targeting
end end
end end

View File

@@ -4,7 +4,7 @@
-- --
-- * Patrol AI airplanes within a given zone. -- * Patrol AI airplanes within a given zone.
-- * Trigger detected events when enemy airplanes are detected. -- * Trigger detected events when enemy airplanes are detected.
-- * Manage a fuel treshold to RTB on time. -- * Manage a fuel threshold to RTB on time.
-- --
-- === -- ===
-- --
@@ -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)
-- --
-- === -- ===
-- --
@@ -72,8 +72,8 @@
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
-- --
@@ -101,7 +101,7 @@
-- * **RTB** ( Group ): Route the AI to the home base. -- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets. -- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets. -- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set or Get the AI controllable -- ## 3. Set or Get the AI controllable
-- --
@@ -133,8 +133,8 @@
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE -- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
-- --
-- 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 treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold 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 targetted 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 proces in place.
@@ -142,7 +142,7 @@
-- ## 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 treshold 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 proces in place.
-- --
-- === -- ===
@@ -581,11 +581,11 @@ function AI_PATROL_ZONE:ClearDetectedUnits()
end 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 treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold 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 targetted 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 treshold 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.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. -- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_PATROL_ZONE self -- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime ) function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
@@ -596,14 +596,14 @@ function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFu
return self return self
end end
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. --- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on. -- However, damage cannot be foreseen early on.
-- Therefore, when the damage treshold is reached, -- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB). -- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated. -- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. -- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. -- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_PATROL_ZONE self -- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold ) function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
@@ -726,7 +726,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
end end
if self.Controllable:IsAlive() then local life = self.Controllable:GetLife() or 0
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 +744,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 +760,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(
@@ -870,9 +873,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

@@ -194,7 +194,7 @@
-- * is of type `Workmaterials` -- * is of type `Workmaterials`
-- * 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 dissapear 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:
-- --
@@ -220,7 +220,7 @@
-- * is of type `Workmaterials` -- * is of type `Workmaterials`
-- * 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 dissapear 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:
-- --

View File

@@ -7,7 +7,7 @@
-- * The construction and inheritance of MOOSE classes. -- * The construction and inheritance of MOOSE classes.
-- * The class naming and numbering system. -- * The class naming and numbering system.
-- * The class hierarchy search system. -- * The class hierarchy search system.
-- * The tracing of information or objects during mission execution for debuggin purposes. -- * The tracing of information or objects during mission execution for debugging purposes.
-- * The subscription to DCS events for event handling in MOOSE objects. -- * The subscription to DCS events for event handling in MOOSE objects.
-- * Object inspection. -- * Object inspection.
-- --
@@ -49,7 +49,8 @@ local _ClassID = 0
-- # 2. Trace information for debugging. -- # 2. Trace information for debugging.
-- --
-- The BASE class contains trace methods to trace progress within a mission execution of a certain object. -- The BASE class contains trace methods to trace progress within a mission execution of a certain object.
-- These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution. -- These trace methods are inherited by each MOOSE class inheriting BASE, thus all objects created from
-- a class derived from BASE can use the tracing methods to trace its execution.
-- --
-- Any type of information can be passed to these tracing methods. See the following examples: -- Any type of information can be passed to these tracing methods. See the following examples:
-- --
@@ -111,7 +112,6 @@ local _ClassID = 0
-- --
-- The method @{#BASE.IsTrace}() will validate if tracing is activated or not. -- The method @{#BASE.IsTrace}() will validate if tracing is activated or not.
-- --
--
-- # 3. DCS simulator Event Handling. -- # 3. DCS simulator Event Handling.
-- --
-- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, -- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator,
@@ -157,8 +157,6 @@ local _ClassID = 0
-- self:SmokeBlue() -- self:SmokeBlue()
-- end -- end
-- --
--
--
-- See the @{Event} module for more information about event handling. -- See the @{Event} module for more information about event handling.
-- --
-- # 4. Class identification methods. -- # 4. Class identification methods.
@@ -203,13 +201,12 @@ BASE = {
Scheduler = nil, Scheduler = nil,
} }
--- @field #BASE.__ --- @field #BASE.__
BASE.__ = {} BASE.__ = {}
--- @field #BASE._ --- @field #BASE._
BASE._ = { BASE._ = {
Schedules = {} --- Contains the Schedulers Active Schedules = {}, --- Contains the Schedulers Active
} }
--- The Formation Class --- The Formation Class
@@ -217,11 +214,9 @@ BASE._ = {
-- @field Cone A cone formation. -- @field Cone A cone formation.
FORMATION = { FORMATION = {
Cone = "Cone", Cone = "Cone",
Vee = "Vee" Vee = "Vee",
} }
--- BASE constructor. --- BASE constructor.
-- --
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE. -- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
@@ -275,7 +270,6 @@ function BASE:Inherit( Child, Parent )
return Child return Child
end end
local function getParent( Child ) local function getParent( Child )
local Parent = nil local Parent = nil
@@ -291,7 +285,6 @@ local function getParent( Child )
return Parent return Parent
end end
--- This is the worker method to retrieve the Parent class. --- This is the worker method to retrieve the Parent class.
-- Note that the Parent class must be passed to call the parent class method. -- Note that the Parent class must be passed to call the parent class method.
-- --
@@ -304,7 +297,6 @@ end
-- @return #BASE -- @return #BASE
function BASE:GetParent( Child, FromClass ) function BASE:GetParent( Child, FromClass )
local Parent local Parent
-- BASE class has no parent -- BASE class has no parent
if Child.ClassName == 'BASE' then if Child.ClassName == 'BASE' then
@@ -419,7 +411,6 @@ do -- Event Handling
return _EVENTDISPATCHER return _EVENTDISPATCHER
end end
--- Get the Class @{Event} processing Priority. --- Get the Class @{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.
@@ -480,29 +471,29 @@ do -- Event Handling
-- @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 -- initiator : The unit object the fired the weapon.
-- weapon: Weapon object that hit the target -- 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 tookoff -- initiator : The unit that took off.
-- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships -- 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 -- initiator : The unit that has landed.
-- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships -- 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 -- 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.
@@ -538,18 +529,18 @@ do -- Event Handling
-- @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 -- 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.
-- @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.
-- @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.
@@ -561,25 +552,25 @@ do -- Event Handling
-- @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 -- 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 -- 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. -- 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. -- 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.
@@ -627,7 +618,6 @@ do -- Event Handling
-- @param #BASE self -- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure. -- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Unknown precisely what creates this event, likely tied into newer damage model. Will update this page when new information become available. --- Unknown precisely what creates this event, likely tied into newer damage model. Will update this page when new information become available.
-- --
-- * initiator: The unit that had the failure. -- * initiator: The unit that had the failure.
@@ -644,7 +634,7 @@ do -- Event Handling
--- 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.
-- --
-- * 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
-- --
@@ -706,7 +696,6 @@ do -- Event Handling
-- @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.
-- **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.
@@ -716,7 +705,6 @@ do -- Event Handling
end end
--- Creation of a Birth Event. --- Creation of a Birth Event.
-- @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.
@@ -733,7 +721,7 @@ function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subpla
initiator = Initiator, initiator = Initiator,
IniUnitName = IniUnitName, IniUnitName = IniUnitName,
place = place, place = place,
subplace = subplace subplace = subplace,
} }
world.onEvent( Event ) world.onEvent( Event )
@@ -743,13 +731,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: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 ) world.onEvent( Event )
@@ -812,7 +801,7 @@ end
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 )
@@ -938,9 +927,8 @@ do -- Scheduling
end end
--- Set a state or property of the Object given a Key and a Value. --- Set a state or property of the Object given a Key and a Value.
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone. -- Note that if the Object is destroyed, set to nil, or garbage collected, then the Values and Keys will also be gone.
-- @param #BASE self -- @param #BASE self
-- @param Object The object that will hold the Value set by the Key. -- @param Object The object that will hold the Value set by the Key.
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type! -- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
@@ -956,9 +944,8 @@ function BASE:SetState( Object, Key, Value )
return self.States[ClassNameAndID][Key] return self.States[ClassNameAndID][Key]
end end
--- Get a Value given a Key from the Object. --- Get a Value given a Key from the Object.
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone. -- Note that if the Object is destroyed, set to nil, or garbage collected, then the Values and Keys will also be gone.
-- @param #BASE self -- @param #BASE self
-- @param Object The object that holds the Value set by the Key. -- @param Object The object that holds the Value set by the Key.
-- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type! -- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type!
@@ -1010,8 +997,6 @@ function BASE:TraceOff()
self:TraceOnOff( false ) self:TraceOnOff( false )
end end
--- Set trace on or off --- Set trace on or off
-- Note that when trace is off, no BASE.Debug statement is performed, increasing performance! -- Note that when trace is off, no BASE.Debug statement is performed, increasing performance!
-- When Moose is loaded statically, (as one file), tracing is switched off by default. -- When Moose is loaded statically, (as one file), tracing is switched off by default.
@@ -1020,11 +1005,13 @@ end
-- @param #BASE self -- @param #BASE self
-- @param #boolean TraceOnOff Switch the tracing on or off. -- @param #boolean TraceOnOff Switch the tracing on or off.
-- @usage -- @usage
--
-- -- Switch the tracing On -- -- Switch the tracing On
-- BASE:TraceOnOff( true ) -- BASE:TraceOnOff( true )
-- --
-- -- Switch the tracing Off -- -- Switch the tracing Off
-- BASE:TraceOnOff( false ) -- BASE:TraceOnOff( false )
--
function BASE:TraceOnOff( TraceOnOff ) function BASE:TraceOnOff( TraceOnOff )
if TraceOnOff == false then if TraceOnOff == false then
self:I( "Tracing in MOOSE is OFF" ) self:I( "Tracing in MOOSE is OFF" )
@@ -1035,7 +1022,6 @@ function BASE:TraceOnOff( TraceOnOff )
end end
end end
--- Enquires if tracing is on (for the class). --- Enquires if tracing is on (for the class).
-- @param #BASE self -- @param #BASE self
-- @return #boolean -- @return #boolean
@@ -1140,7 +1126,6 @@ function BASE:F( Arguments )
end end
end end
--- Trace a function call level 2. Must be at the beginning of the function logic. --- Trace a function call level 2. Must be at the beginning of the function logic.
-- @param #BASE self -- @param #BASE self
-- @param Arguments A #table or any field. -- @param Arguments A #table or any field.
@@ -1215,7 +1200,6 @@ function BASE:T( Arguments )
end end
end end
--- Trace a function logic level 2. Can be anywhere within the function logic. --- Trace a function logic level 2. Can be anywhere within the function logic.
-- @param #BASE self -- @param #BASE self
-- @param Arguments A #table or any field. -- @param Arguments A #table or any field.
@@ -1273,7 +1257,6 @@ function BASE:E( Arguments )
end end
--- Log an information which will be traced always. Can be anywhere within the function logic. --- Log an information which will be traced always. Can be anywhere within the function logic.
-- @param #BASE self -- @param #BASE self
-- @param Arguments A #table or any field. -- @param Arguments A #table or any field.
@@ -1301,8 +1284,6 @@ function BASE:I( Arguments )
end end
--- old stuff --- old stuff
-- function BASE:_Destructor() -- function BASE:_Destructor()
@@ -1311,7 +1292,6 @@ end
-- --self:EventRemoveAll() -- --self:EventRemoveAll()
-- end -- end
-- THIS IS WHY WE NEED LUA 5.2 ... -- THIS IS WHY WE NEED LUA 5.2 ...
-- function BASE:_SetDestructor() -- function BASE:_SetDestructor()
-- --

View File

@@ -16,20 +16,20 @@
--- *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 exemple.
-- --
-- ## 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".
@@ -101,7 +101,7 @@ BEACON.Type={
-- @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).
@@ -170,6 +170,8 @@ end
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)
@@ -187,11 +189,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
@@ -204,7 +211,7 @@ function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
-- Start beacon. -- Start beacon.
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing) self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
-- Stop sheduler. -- Stop scheduler.
if Duration then if Duration then
self.Positionable:DeactivateBeacon(Duration) self.Positionable:DeactivateBeacon(Duration)
end end
@@ -230,7 +237,7 @@ function BEACON:ActivateICLS(Channel, Callsign, Duration)
-- Start beacon. -- Start beacon.
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign) self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
-- Stop sheduler -- Stop scheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:DeactivateBeacon(Duration) self.Positionable:DeactivateBeacon(Duration)
end end
@@ -238,7 +245,35 @@ 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:DeactivateLink4(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
@@ -267,13 +302,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
@@ -281,10 +315,13 @@ 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",
} }
}) })
@@ -316,10 +353,10 @@ function BEACON:StopAATACAN()
end end
--- Activates a general pupose 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
@@ -393,7 +430,7 @@ function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDurati
end end
end end
--- Stops the AA TACAN BEACON --- Stop the Radio Beacon
-- @param #BEACON self -- @param #BEACON self
-- @return #BEACON self -- @return #BEACON self
function BEACON:StopRadioBeacon() function BEACON:StopRadioBeacon()

View File

@@ -31,7 +31,6 @@
-- @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.
@@ -51,7 +50,7 @@
-- * PLAYERS -- * PLAYERS
-- * CARGOS -- * CARGOS
-- --
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor. -- On top, for internal MOOSE administration purposes, the DATABASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
-- --
-- The singleton object **_DATABASE** is automatically created by MOOSE, that administers all objects within the mission. -- The singleton object **_DATABASE** is automatically created by MOOSE, that administers all objects within the mission.
-- Moose refers to **_DATABASE** within the framework extensively, but you can also refer to the _DATABASE object within your missions if required. -- Moose refers to **_DATABASE** within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
@@ -90,15 +89,13 @@ DATABASE = {
FLIGHTCONTROLS = {}, FLIGHTCONTROLS = {},
} }
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,
@@ -106,7 +103,6 @@ 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
@@ -154,7 +150,6 @@ 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.
@@ -178,7 +173,6 @@ function DATABASE:AddUnit( DCSUnitName )
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 )
@@ -200,7 +194,6 @@ 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 )
@@ -240,7 +233,6 @@ 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
@@ -249,17 +241,6 @@ function DATABASE:DeleteAirbase( AirbaseName )
self.AIRBASES[AirbaseName] = nil self.AIRBASES[AirbaseName] = nil
end end
--- Finds an 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
do -- Zones do -- Zones
--- Finds a @{Zone} based on the zone name. --- Finds a @{Zone} based on the zone name.
@@ -283,7 +264,6 @@ do -- Zones
end end
end end
--- Deletes a @{Zone} from the DATABASE based on the zone name. --- Deletes a @{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.
@@ -292,7 +272,6 @@ do -- Zones
self.ZONES[ZoneName] = nil self.ZONES[ZoneName] = nil
end 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
@@ -376,7 +355,6 @@ do -- Zones
end end
end -- zone end -- zone
do -- Zone_Goal do -- Zone_Goal
@@ -402,7 +380,6 @@ do -- Zone_Goal
end end
end end
--- Deletes a @{Zone} from the DATABASE based on the zone name. --- Deletes a @{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.
@@ -424,7 +401,6 @@ 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
@@ -518,7 +494,6 @@ 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.
@@ -532,7 +507,6 @@ 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
@@ -543,7 +517,6 @@ 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 )
@@ -591,7 +564,6 @@ 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
@@ -604,7 +576,6 @@ 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
@@ -617,7 +588,6 @@ 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
@@ -750,9 +720,8 @@ 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.
@@ -778,7 +747,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 {}
@@ -786,7 +757,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
@@ -797,9 +768,8 @@ 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 )
@@ -902,8 +872,6 @@ function DATABASE:GetCategoryFromAirbase( AirbaseName )
return self.AIRBASES[AirbaseName]:GetCategory() return self.AIRBASES[AirbaseName]:GetCategory()
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
@@ -927,8 +895,7 @@ 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()
@@ -969,7 +936,7 @@ function DATABASE:_RegisterGroupsAndUnits()
return self return self
end end
--- Private method that registers all Units of skill Client or Player within in the mission. --- Private method that registers all Units of skill Client or Player within the mission.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:_RegisterClients() function DATABASE:_RegisterClients()
@@ -982,7 +949,8 @@ function DATABASE:_RegisterClients()
return self return self
end end
--- @param #DATABASE self --- Private method that registers all Statics within the mission.
-- @param #DATABASE self
function DATABASE:_RegisterStatics() function DATABASE:_RegisterStatics()
local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ), GroupsNeutral = coalition.getStaticObjects( coalition.side.NEUTRAL ) } local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ), GroupsNeutral = coalition.getStaticObjects( coalition.side.NEUTRAL ) }
@@ -1043,7 +1011,6 @@ function DATABASE:_RegisterAirbases()
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.
@@ -1123,7 +1090,6 @@ 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
@@ -1178,7 +1144,6 @@ 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
@@ -1216,7 +1181,6 @@ 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
@@ -1302,7 +1266,6 @@ 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.
@@ -1315,7 +1278,6 @@ 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.
@@ -1328,7 +1290,6 @@ 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.
@@ -1341,7 +1302,6 @@ 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.
@@ -1354,7 +1314,6 @@ 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.
@@ -1379,7 +1338,6 @@ 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.
@@ -1404,7 +1362,6 @@ function DATABASE:ForEachCargo( IteratorFunction, ... )
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
@@ -1416,7 +1373,6 @@ 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
@@ -1428,7 +1384,6 @@ 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
@@ -1440,7 +1395,6 @@ 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
@@ -1452,8 +1406,6 @@ 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
@@ -1463,7 +1415,6 @@ 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

View File

@@ -14,7 +14,7 @@
-- ![Objects](..\Presentations\EVENT\Dia2.JPG) -- ![Objects](..\Presentations\EVENT\Dia2.JPG)
-- --
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc. -- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
-- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission. -- This module provides a mechanism to dispatch those events occurring within your running mission, to the different objects orchestrating your mission.
-- --
-- ![Objects](..\Presentations\EVENT\Dia3.JPG) -- ![Objects](..\Presentations\EVENT\Dia3.JPG)
-- --
@@ -32,11 +32,11 @@
-- --
-- ![Objects](..\Presentations\EVENT\Dia5.JPG) -- ![Objects](..\Presentations\EVENT\Dia5.JPG)
-- --
-- There are 5 levels of kind 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: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority. -- * SET_ derived classes: These are subsets of the _DATABASE object. 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 teh 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.
-- --
@@ -983,13 +983,12 @@ 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
@@ -1002,6 +1001,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
@@ -1010,34 +1010,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_
@@ -1063,9 +1040,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
@@ -1076,15 +1091,22 @@ 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
@@ -1097,6 +1119,11 @@ function EVENT:onEvent( Event )
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
@@ -1109,9 +1136,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()
@@ -1159,6 +1184,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()
@@ -1193,24 +1219,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
@@ -1222,8 +1247,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
@@ -1233,7 +1258,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()
@@ -1243,10 +1269,6 @@ 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
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventFunction( EventClass, Event ) return EventData.EventFunction( EventClass, Event )
@@ -1259,15 +1281,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
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event ) return EventFunction( EventClass, Event )
end, ErrorHandler ) end, ErrorHandler )
end end
end end
end end
else else
@@ -1285,7 +1304,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()
@@ -1296,10 +1316,6 @@ 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
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) ) return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
@@ -1312,10 +1328,6 @@ 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
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event, unpack( EventData.Params ) ) return EventFunction( EventClass, Event, unpack( EventData.Params ) )
@@ -1337,9 +1349,6 @@ 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
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventFunction( EventClass, Event ) return EventData.EventFunction( EventClass, Event )
@@ -1351,16 +1360,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
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall( local Result, Value = xpcall(
function() 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

@@ -21,8 +21,8 @@
-- A FSM can only be in one of a finite number of states. -- A FSM can only be in one of a finite number of states.
-- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. -- The machine is in only one state at a time; the state it is in at any given time is called the **current state**.
-- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**. -- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**.
-- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**. -- A **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**.
-- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions. -- A FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions.
-- --
-- The FSM class supports a **hierarchical implementation of a Finite State Machine**, -- The FSM class supports a **hierarchical implementation of a Finite State Machine**,
-- that is, it allows to **embed existing FSM implementations in a master FSM**. -- that is, it allows to **embed existing FSM implementations in a master FSM**.
@@ -34,32 +34,32 @@
-- orders him to destroy x targets and account the results. -- orders him to destroy x targets and account the results.
-- Other examples of ready made FSM could be: -- Other examples of ready made FSM could be:
-- --
-- * route a plane to a zone flown by a human -- * Route a plane to a zone flown by a human.
-- * detect targets by an AI and report to humans -- * Detect targets by an AI and report to humans.
-- * account for destroyed targets by human players -- * Account for destroyed targets by human players.
-- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle -- * Handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle.
-- * let an AI patrol a zone -- * Let an AI patrol a zone.
-- --
-- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes, -- The **MOOSE framework** extensively uses the FSM class and derived FSM\_ classes,
-- because **the goal of MOOSE is to simplify mission design complexity for mission building**. -- because **the goal of MOOSE is to simplify mission design complexity for mission building**.
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes. -- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used, -- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
-- 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 a FSM implementation directing **AI controlled** @{GROUP} and/or @{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 a FSM implementation executing a @{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 @{TASK}, seated in a @{CLIENT} (slot) or a @{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.
-- --
-- ##__Dislaimer:__ -- ##__Disclaimer:__
-- The FSM class development is based on a finite state machine implementation made by Conroy Kyle. -- The FSM class development is based on a finite state machine implementation made by Conroy Kyle.
-- The state machine can be found on [github](https://github.com/kyleconroy/lua-state-machine) -- The state machine can be found on [github](https://github.com/kyleconroy/lua-state-machine)
-- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. -- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator.
-- Additionally, I've added extendability and created an API that allows seamless FSM implementation. -- Additionally, I've added extendability and created an API that allows seamless FSM implementation.
-- --
-- The following derived classes are available in the MOOSE framework, that implement a specialised 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 @{Task}s.
-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. -- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s.
@@ -69,7 +69,6 @@
-- --
-- === -- ===
-- --
--
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: **funkyfranky** -- ### Contributions: **funkyfranky**
-- --
@@ -89,7 +88,6 @@ do -- FSM
-- @field #string current Current state name. -- @field #string current Current state name.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. --- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
-- --
-- A FSM can only be in one of a finite number of states. -- A FSM can only be in one of a finite number of states.
@@ -145,12 +143,12 @@ do -- FSM
-- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. -- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine.
-- --
-- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation.
-- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. -- The below documentation has a separate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**.
-- --
-- ## FSM Linear Transitions -- ## FSM Linear Transitions
-- --
-- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**.
-- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. -- The Linear transition rule evaluation will always be done from the **current state** of the FSM.
-- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.
-- --
-- ### FSM Transition Rules -- ### FSM Transition Rules
@@ -185,7 +183,7 @@ do -- FSM
-- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. -- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**.
-- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. -- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM.
-- --
-- The below code snippet shows how the two last lines can be rewritten and consensed. -- The below code snippet shows how the two last lines can be rewritten and condensed.
-- --
-- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" )
-- --
@@ -221,7 +219,7 @@ do -- FSM
-- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. -- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**.
-- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. -- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__.
-- --
-- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. -- The distinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time.
-- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. -- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact.
-- --
-- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. -- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering.
@@ -345,7 +343,6 @@ do -- FSM
-- === -- ===
-- --
-- @field #FSM -- @field #FSM
--
FSM = { FSM = {
ClassName = "FSM", ClassName = "FSM",
} }
@@ -379,7 +376,6 @@ do -- FSM
return self return self
end end
--- Sets the start state of the FSM. --- Sets the start state of the FSM.
-- @param #FSM self -- @param #FSM self
-- @param #string State A string defining the start state. -- @param #string State A string defining the start state.
@@ -388,7 +384,6 @@ do -- FSM
self.current = State self.current = State
end end
--- Returns the start state of the FSM. --- Returns the start state of the FSM.
-- @param #FSM self -- @param #FSM self
-- @return #string A string containing the start state. -- @return #string A string containing the start state.
@@ -416,7 +411,6 @@ do -- FSM
self:_eventmap( self.Events, Transition ) self:_eventmap( self.Events, Transition )
end end
--- Returns a table of the transition rules defined within the FSM. --- Returns a table of the transition rules defined within the FSM.
-- @param #FSM self -- @param #FSM self
-- @return #table Transitions. -- @return #table Transitions.
@@ -450,7 +444,6 @@ do -- FSM
return Process return Process
end end
--- Returns a table of the SubFSM rules defined within the FSM. --- Returns a table of the SubFSM rules defined within the FSM.
-- @param #FSM self -- @param #FSM self
-- @return #table Sub processes. -- @return #table Sub processes.
@@ -499,7 +492,6 @@ do -- FSM
return self._EndStates or {} return self._EndStates or {}
end end
--- Adds a score for the FSM to be achieved. --- Adds a score for the FSM to be achieved.
-- @param #FSM self -- @param #FSM self
-- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process). -- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process).
@@ -647,7 +639,9 @@ do -- FSM
-- return self[handler](self, unpack( params )) -- return self[handler](self, unpack( params ))
-- Protected call. -- Protected call.
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) local Result, Value = xpcall( function()
return self[handler]( self, unpack( params ) )
end, ErrorHandler )
return Value return Value
end end
@@ -673,7 +667,6 @@ do -- FSM
-- Parameters. -- Parameters.
local Params = { From, EventName, To, ... } local Params = { From, EventName, To, ... }
if self["onleave" .. From] or if self["onleave" .. From] or
self["OnLeave" .. From] or self["OnLeave" .. From] or
self["onbefore" .. EventName] or self["onbefore" .. EventName] or
@@ -835,7 +828,9 @@ do -- FSM
-- @param #string EventName Event name. -- @param #string EventName Event name.
-- @return #function Function. -- @return #function Function.
function FSM:_create_transition( EventName ) function FSM:_create_transition( EventName )
return function( self, ... ) return self._handler( self, EventName , ... ) end return function( self, ... )
return self._handler( self, EventName, ... )
end
end end
--- Go sub. --- Go sub.
@@ -1075,7 +1070,9 @@ do -- FSM_CONTROLLABLE
if self[handler] then if self[handler] then
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() ) self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler ) local Result, Value = xpcall( function()
return self[handler]( self, self.Controllable, unpack( params ) )
end, ErrorHandler )
return Value return Value
-- return self[handler]( self, self.Controllable, unpack( params ) ) -- return self[handler]( self, self.Controllable, unpack( params ) )
end end
@@ -1089,16 +1086,13 @@ 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 @{Task} actions, which control @{Client}s.
-- --
-- === -- ===
-- --
-- @field #FSM_PROCESS FSM_PROCESS -- @field #FSM_PROCESS FSM_PROCESS
-- --
FSM_PROCESS = { FSM_PROCESS = { ClassName = "FSM_PROCESS" }
ClassName = "FSM_PROCESS",
}
--- Creates a new FSM_PROCESS object. --- Creates a new FSM_PROCESS object.
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
@@ -1139,7 +1133,9 @@ do -- FSM_PROCESS
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
local Result, Value local Result, Value
if self.Controllable and self.Controllable:IsAlive() == true then if self.Controllable and self.Controllable:IsAlive() == true then
Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) Result, Value = xpcall( function()
return self[handler]( self, self.Controllable, self.Task, unpack( params ) )
end, ErrorHandler )
end end
return Value return Value
-- return self[handler]( self, self.Controllable, unpack( params ) ) -- return self[handler]( self, self.Controllable, unpack( params ) )
@@ -1152,7 +1148,6 @@ do -- FSM_PROCESS
function FSM_PROCESS:Copy( Controllable, Task ) function FSM_PROCESS:Copy( Controllable, Task )
self:T( { self:GetClassNameAndID() } ) self:T( { self:GetClassNameAndID() } )
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
NewFsm:Assign( Controllable, Task ) NewFsm:Assign( Controllable, Task )
@@ -1263,9 +1258,6 @@ do -- FSM_PROCESS
CC:MessageToGroup( Message, TaskGroup ) CC:MessageToGroup( Message, TaskGroup )
end end
--- Assign the process to a @{Wrapper.Unit} and activate the process. --- Assign the process to a @{Wrapper.Unit} and activate the process.
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Task.Tasking#TASK Task -- @param Task.Tasking#TASK Task
@@ -1296,7 +1288,6 @@ do -- FSM_PROCESS
self.Task:Fail() self.Task:Fail()
end end
--- StateMachine callback function for a FSM_PROCESS --- StateMachine callback function for a FSM_PROCESS
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
@@ -1375,7 +1366,9 @@ do -- FSM_TASK
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName ) self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName )
self._EventSchedules[EventName] = nil self._EventSchedules[EventName] = nil
-- return self[handler]( self, unpack( params ) ) -- return self[handler]( self, unpack( params ) )
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) local Result, Value = xpcall( function()
return self[handler]( self, unpack( params ) )
end, ErrorHandler )
return Value return Value
end end
end end
@@ -1389,14 +1382,12 @@ 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 @{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.
-- --
-- === -- ===
-- --
-- @field #FSM_SET FSM_SET -- @field #FSM_SET FSM_SET
--
FSM_SET = { FSM_SET = {
ClassName = "FSM_SET", ClassName = "FSM_SET",
} }

View File

@@ -22,13 +22,11 @@
-- @module Core.Goal -- @module Core.Goal
-- @image Core_Goal.JPG -- @image Core_Goal.JPG
do -- Goal do -- Goal
--- @type GOAL --- @type GOAL
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. --- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
-- --
-- # 1. GOAL constructor -- # 1. GOAL constructor
@@ -105,7 +103,6 @@ do -- Goal
-- @param #string Event -- @param #string Event
-- @param #string To -- @param #string To
self:SetStartState( "Pending" ) self:SetStartState( "Pending" )
self:AddTransition( "*", "Achieved", "Achieved" ) self:AddTransition( "*", "Achieved", "Achieved" )
@@ -138,7 +135,6 @@ do -- Goal
return self return self
end end
--- Add a new contribution by a player. --- Add a new contribution by a player.
-- @param #GOAL self -- @param #GOAL self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -149,14 +145,12 @@ do -- Goal
self.TotalContributions = self.TotalContributions + 1 self.TotalContributions = self.TotalContributions + 1
end end
--- @param #GOAL self --- @param #GOAL self
-- @param #number Player contribution. -- @param #number Player contribution.
function GOAL:GetPlayerContribution( PlayerName ) function GOAL:GetPlayerContribution( PlayerName )
return self.Players[PlayerName] or 0 return self.Players[PlayerName] or 0
end end
--- Get the players who contributed to achieve the goal. --- Get the players who contributed to achieve the goal.
-- The result is a list of players, sorted by the name of the players. -- The result is a list of players, sorted by the name of the players.
-- @param #GOAL self -- @param #GOAL self
@@ -165,7 +159,6 @@ do -- Goal
return self.Players or {} return self.Players or {}
end end
--- Gets the total contributions that happened to achieve the goal. --- Gets the total contributions that happened to achieve the goal.
-- The result is a number. -- The result is a number.
-- @param #GOAL self -- @param #GOAL self
@@ -174,8 +167,6 @@ do -- Goal
return self.TotalContributions or 0 return self.TotalContributions or 0
end end
--- Validates if the goal is achieved. --- Validates if the goal is achieved.
-- @param #GOAL self -- @param #GOAL self
-- @return #boolean true if the goal is achieved. -- @return #boolean true if the goal is achieved.

View File

@@ -14,7 +14,7 @@
-- * Only create or delete menus when required, and keep existing menus persistent. -- * Only create or delete menus when required, and keep existing menus persistent.
-- * Update menu structures. -- * Update menu structures.
-- * Refresh menu structures intelligently, based on a time stamp of updates. -- * Refresh menu structures intelligently, based on a time stamp of updates.
-- - Delete obscolete menus. -- - Delete obsolete menus.
-- - Create new one where required. -- - Create new one where required.
-- - Don't touch the existing ones. -- - Don't touch the existing ones.
-- * Provide a variable amount of parameters to menus. -- * Provide a variable amount of parameters to menus.
@@ -23,7 +23,7 @@
-- * Provide a great tool to manage menus in your code. -- * Provide a great tool to manage menus in your code.
-- --
-- DCS Menus can be managed using the MENU classes. -- DCS Menus can be managed using the MENU classes.
-- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scanerios where you need to -- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scenarios where you need to
-- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing -- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing
-- menus is not a easy feat if you have complex menu hierarchies defined. -- menus is not a easy feat if you have complex menu hierarchies defined.
-- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy. -- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy.
@@ -53,7 +53,6 @@
-- @module Core.Menu -- @module Core.Menu
-- @image Core_Menu.JPG -- @image Core_Menu.JPG
MENU_INDEX = {} MENU_INDEX = {}
MENU_INDEX.MenuMission = {} MENU_INDEX.MenuMission = {}
MENU_INDEX.MenuMission.Menus = {} MENU_INDEX.MenuMission.Menus = {}
@@ -64,10 +63,7 @@ MENU_INDEX.Coalition[coalition.side.RED] = {}
MENU_INDEX.Coalition[coalition.side.RED].Menus = {} 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
@@ -95,20 +91,16 @@ 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()
self.MenuMission.Menus = self.MenuMission.Menus or {} self.MenuMission.Menus = self.MenuMission.Menus or {}
end end
function MENU_INDEX:PrepareCoalition( CoalitionSide ) 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 )
@@ -119,42 +111,26 @@ function MENU_INDEX:PrepareGroup( Group )
end end
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
function MENU_INDEX:HasGroupMenu( Group, Path ) function MENU_INDEX:HasGroupMenu( Group, Path )
if Group and Group:IsAlive() then if Group and Group:IsAlive() then
local MenuGroupName = Group:GetName() local MenuGroupName = Group:GetName()
@@ -162,53 +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 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
@@ -216,10 +175,10 @@ do -- MENU_BASE
ClassName = "MENU_BASE", ClassName = "MENU_BASE",
MenuPath = nil, MenuPath = nil,
MenuText = "", MenuText = "",
MenuParentPath = nil MenuParentPath = nil,
} }
--- Consructor --- Constructor
-- @param #MENU_BASE -- @param #MENU_BASE
-- @return #MENU_BASE -- @return #MENU_BASE
function MENU_BASE:New( MenuText, ParentMenu ) function MENU_BASE:New( MenuText, ParentMenu )
@@ -228,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
@@ -248,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 {}
@@ -256,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
@@ -266,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}.
@@ -277,7 +232,6 @@ do -- MENU_BASE
return self return self
end end
--- Gets a @{Menu} from a parent @{Menu} --- Gets a @{Menu} from a parent @{Menu}
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param #string MenuText The text of the child menu. -- @param #string MenuText The text of the child menu.
@@ -285,7 +239,6 @@ do -- MENU_BASE
function MENU_BASE:GetMenu( MenuText ) function MENU_BASE:GetMenu( MenuText )
return self.Menus[MenuText] return self.Menus[MenuText]
end end
--- Sets a menu stamp for later prevention of menu removal. --- Sets a menu stamp for later prevention of menu removal.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param MenuStamp -- @param MenuStamp
@@ -323,9 +276,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
@@ -347,7 +298,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 )
@@ -380,7 +330,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!!!
@@ -391,35 +340,30 @@ 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.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
-- @field #MENU_MISSION -- @field #MENU_MISSION
MENU_MISSION = { MENU_MISSION = {
ClassName = "MENU_MISSION" ClassName = "MENU_MISSION",
} }
--- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file. --- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file.
-- @param #MENU_MISSION self -- @param #MENU_MISSION self
-- @param #string MenuText The text for the menu. -- @param #string MenuText The text for the menu.
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent 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_MISSION -- @return #MENU_MISSION
function MENU_MISSION:New( MenuText, ParentMenu ) function MENU_MISSION:New( MenuText, ParentMenu )
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
@@ -432,17 +376,15 @@ do -- MENU_MISSION
end end
end end
--- Refreshes a radio item for a mission --- Refreshes a radio item for a 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!
@@ -466,7 +408,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
@@ -487,10 +428,7 @@ do -- MENU_MISSION
return self return self
end end
end end
do -- MENU_MISSION_COMMAND do -- MENU_MISSION_COMMAND
--- @type MENU_MISSION_COMMAND --- @type MENU_MISSION_COMMAND
@@ -503,7 +441,7 @@ do -- MENU_MISSION_COMMAND
-- --
-- @field #MENU_MISSION_COMMAND -- @field #MENU_MISSION_COMMAND
MENU_MISSION_COMMAND = { MENU_MISSION_COMMAND = {
ClassName = "MENU_MISSION_COMMAND" ClassName = "MENU_MISSION_COMMAND",
} }
--- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters. --- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters.
@@ -518,7 +456,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 )
@@ -532,17 +469,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
@@ -553,7 +488,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
@@ -572,13 +506,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
@@ -634,18 +563,15 @@ do -- MENU_COALITION
-- @param #MENU_COALITION self -- @param #MENU_COALITION self
-- @param DCS#coalition.side Coalition The coalition owning the menu. -- @param DCS#coalition.side Coalition The coalition owning the menu.
-- @param #string MenuText The text for the menu. -- @param #string MenuText The text for the menu.
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent 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 )
@@ -656,17 +582,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!
@@ -689,7 +613,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
@@ -709,11 +632,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
@@ -742,7 +661,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 )
@@ -757,20 +675,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
@@ -781,7 +696,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
@@ -800,20 +714,16 @@ 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 dissapear when groups for players are destroyed and restarted. -- Menus don't disappear when groups for players are destroyed and restarted.
-- So every menu for a client created must be tracked so that program logic accidentally does not create. -- So every menu for a client created must be tracked so that program logic accidentally does not create.
-- 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
@@ -889,16 +799,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 )
@@ -906,12 +813,10 @@ do
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 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 )
@@ -921,6 +826,7 @@ do
end end
end end
return self
end end
--- Removes the sub menus recursively of this MENU_GROUP. --- Removes the sub menus recursively of this MENU_GROUP.
@@ -929,7 +835,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
@@ -938,18 +843,15 @@ do
end end
--- Removes the main menu and sub menus recursively of this MENU_GROUP. --- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self -- @param #MENU_GROUP self
-- @param MenuStamp -- @param MenuStamp
-- @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
@@ -993,18 +895,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
@@ -1015,19 +914,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.
@@ -1036,11 +933,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
@@ -1059,13 +954,9 @@ 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
@@ -1093,16 +984,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
@@ -1116,12 +1004,10 @@ do
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: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 )
@@ -1132,15 +1018,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 )
@@ -1150,6 +1033,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.
@@ -1158,7 +1042,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
@@ -1167,18 +1050,15 @@ do
end end
--- Removes the main menu and sub menus recursively of this MENU_GROUP. --- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP_DELAYED self -- @param #MENU_GROUP_DELAYED self
-- @param MenuStamp -- @param MenuStamp
-- @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
@@ -1223,18 +1103,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
@@ -1250,33 +1127,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.
@@ -1285,11 +1158,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
@@ -1308,6 +1179,5 @@ do
return self return self
end end
end end

View File

@@ -10,6 +10,8 @@
-- * 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.
--
-- --
-- === -- ===
-- --
@@ -35,6 +37,7 @@
-- --
-- * To a @{Client} using @{#MESSAGE.ToClient}(). -- * To a @{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}().
@@ -68,10 +71,9 @@ MESSAGE.Type = {
Information = "Information", Information = "Information",
Briefing = "Briefing Report", Briefing = "Briefing Report",
Overview = "Overview Report", Overview = "Overview Report",
Detailed = "Detailed Report" Detailed = "Detailed Report",
} }
--- Creates a new MESSAGE object. 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. --- Creates a new MESSAGE object. 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.
-- @param self -- @param self
-- @param #string MessageText is the text of the Message. -- @param #string MessageText is the text of the Message.
@@ -80,6 +82,7 @@ MESSAGE.Type = {
-- @param #boolean ClearScreen (optional) Clear all previous messages if true. -- @param #boolean ClearScreen (optional) Clear all previous messages if true.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
--
-- -- Create a series of new Messages. -- -- Create a series of new Messages.
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score". -- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win". -- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
@@ -89,11 +92,11 @@ MESSAGE.Type = {
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" ) -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" ) -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score") -- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
--
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen ) function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText, MessageDuration, MessageCategory } ) self:F( { MessageText, MessageDuration, MessageCategory } )
self.MessageType = nil self.MessageType = nil
-- When no MessageCategory is given, we don't show it as a title... -- When no MessageCategory is given, we don't show it as a title...
@@ -123,7 +126,6 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen
return self return self
end 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.
@@ -134,10 +136,12 @@ end
-- @param #boolean ClearScreen (optional) Clear all previous messages. -- @param #boolean ClearScreen (optional) Clear all previous messages.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
--
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information ) -- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information ) -- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update ) -- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update ) -- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
--
function MESSAGE:NewType( MessageText, MessageType, ClearScreen ) function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
@@ -156,8 +160,6 @@ function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
return self return self
end end
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc. --- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
@@ -167,14 +169,13 @@ function MESSAGE:Clear()
return self return self
end 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 Settings used to display the message.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
--
-- -- Send the 2 messages created with the @{New} method to the Client Group. -- -- Send the 2 messages created with the @{New} method to the Client Group.
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1. -- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
-- ClientGroup = Group.getByName( "ClientGroup" ) -- ClientGroup = Group.getByName( "ClientGroup" )
@@ -189,6 +190,7 @@ end
-- 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, "Score" )
-- MessageClient1:ToClient( ClientGroup ) -- MessageClient1:ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup ) -- MessageClient2:ToClient( ClientGroup )
--
function MESSAGE:ToClient( Client, Settings ) function MESSAGE:ToClient( Client, Settings )
self:F( Client ) self:F( Client )
@@ -200,10 +202,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
@@ -233,10 +238,36 @@ 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.
-- @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 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, "Score" ):ToBlue()
-- or -- or
@@ -244,6 +275,7 @@ end
-- 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, "Score" )
-- MessageBLUE:ToBlue() -- MessageBLUE:ToBlue()
--
function MESSAGE:ToBlue() function MESSAGE:ToBlue()
self:F() self:F()
@@ -256,6 +288,7 @@ end
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
-- @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, "Score" ):ToRed()
-- or -- or
@@ -263,6 +296,7 @@ end
-- 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, "Score" )
-- MessageRED:ToRed() -- MessageRED:ToRed()
--
function MESSAGE:ToRed() function MESSAGE:ToRed()
self:F() self:F()
@@ -277,6 +311,7 @@ end
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display. -- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object. -- @return #MESSAGE Message object.
-- @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, "Score" ):ToCoalition( coalition.side.RED )
-- or -- or
@@ -284,6 +319,7 @@ end
-- 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, "Score" )
-- MessageRED:ToCoalition( coalition.side.RED ) -- MessageRED:ToCoalition( coalition.side.RED )
--
function MESSAGE:ToCoalition( CoalitionSide, Settings ) function MESSAGE:ToCoalition( CoalitionSide, Settings )
self:F( CoalitionSide ) self:F( CoalitionSide )
@@ -323,6 +359,7 @@ end
-- @param Core.Settings#Settings Settings (Optional) Settings for message display. -- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE -- @return #MESSAGE
-- @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, "Win" ):ToAll()
-- or -- or
@@ -330,6 +367,7 @@ end
-- 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, "Win" )
-- MessageAll:ToAll() -- MessageAll:ToAll()
--
function MESSAGE:ToAll( Settings ) function MESSAGE:ToAll( Settings )
self:F() self:F()
@@ -347,7 +385,6 @@ function MESSAGE:ToAll(Settings)
return self return self
end 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
-- @return #MESSAGE -- @return #MESSAGE

View File

@@ -26,19 +26,28 @@
-- --
-- ### Authors: -- ### Authors:
-- --
-- * FlightControl : Design & Programming -- * FlightControl (Design & Programming)
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- * funkyfranky
-- * Applevangelist
--
-- ===
--
-- @module Core.Point -- @module Core.Point
-- @image Core_Coordinate.JPG -- @image Core_Coordinate.JPG
do -- COORDINATE do -- COORDINATE
--- @type COORDINATE --- @type COORDINATE
-- @field #string ClassName Name of the class
-- @field #number x Component of the 3D vector.
-- @field #number y Component of the 3D vector.
-- @field #number z Component of the 3D vector.
-- @field #number Heading Heading in degrees. Needs to be set first.
-- @field #number Velocity Velocity in meters per second. Needs to be set first.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -185,7 +194,12 @@ do -- COORDINATE
-- ## 9) Coordinate text generation -- ## 9) Coordinate text generation
-- --
-- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance.
-- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text. -- * @{#COORDINATE.ToStringBRA}(): Generates a Bearing, Range & Altitude text.
-- * @{#COORDINATE.ToStringBRAANATO}(): Generates a Generates a Bearing, Range, Aspect & Altitude text in NATOPS.
-- * @{#COORDINATE.ToStringLL}(): Generates a Latutide & Longitude text.
-- * @{#COORDINATE.ToStringLLDMS}(): Generates a Lat, Lon, Degree, Minute, Second text.
-- * @{#COORDINATE.ToStringLLDDM}(): Generates a Lat, Lon, Degree, decimal Minute text.
-- * @{#COORDINATE.ToStringMGRS}(): Generates a MGRS grid coordinate text.
-- --
-- ## 10) Drawings on F10 map -- ## 10) Drawings on F10 map
-- --
@@ -201,28 +215,53 @@ do -- COORDINATE
ClassName = "COORDINATE", ClassName = "COORDINATE",
} }
--- @field COORDINATE.WaypointAltType --- Waypoint altitude types.
-- @type COORDINATE.WaypointAltType
-- @field #string BARO Barometric altitude.
-- @field #string RADIO Radio altitude.
COORDINATE.WaypointAltType = { COORDINATE.WaypointAltType = {
BARO = "BARO", BARO = "BARO",
RADIO = "RADIO", RADIO = "RADIO",
} }
--- @field COORDINATE.WaypointAction --- Waypoint actions.
-- @type COORDINATE.WaypointAction
-- @field #string TurningPoint Turning point.
-- @field #string FlyoverPoint Fly over point.
-- @field #string FromParkingArea From parking area.
-- @field #string FromParkingAreaHot From parking area hot.
-- @field #string FromGroundAreaHot From ground area hot.
-- @field #string FromGroundArea From ground area.
-- @field #string FromRunway From runway.
-- @field #string Landing Landing.
-- @field #string LandingReFuAr Landing and refuel and rearm.
COORDINATE.WaypointAction = { COORDINATE.WaypointAction = {
TurningPoint = "Turning Point", TurningPoint = "Turning Point",
FlyoverPoint = "Fly Over Point", FlyoverPoint = "Fly Over Point",
FromParkingArea = "From Parking Area", FromParkingArea = "From Parking Area",
FromParkingAreaHot = "From Parking Area Hot", FromParkingAreaHot = "From Parking Area Hot",
FromGroundAreaHot = "From Ground Area Hot",
FromGroundArea = "From Ground Area",
FromRunway = "From Runway", FromRunway = "From Runway",
Landing = "Landing", Landing = "Landing",
LandingReFuAr = "LandingReFuAr", LandingReFuAr = "LandingReFuAr",
} }
--- @field COORDINATE.WaypointType --- Waypoint types.
-- @type COORDINATE.WaypointType
-- @field #string TakeOffParking Take of parking.
-- @field #string TakeOffParkingHot Take of parking hot.
-- @field #string TakeOff Take off parking hot.
-- @field #string TakeOffGroundHot Take of from ground hot.
-- @field #string TurningPoint Turning point.
-- @field #string Land Landing point.
-- @field #string LandingReFuAr Landing and refuel and rearm.
COORDINATE.WaypointType = { COORDINATE.WaypointType = {
TakeOffParking = "TakeOffParking", TakeOffParking = "TakeOffParking",
TakeOffParkingHot = "TakeOffParkingHot", TakeOffParkingHot = "TakeOffParkingHot",
TakeOff = "TakeOffParkingHot", TakeOff = "TakeOffParkingHot",
TakeOffGroundHot = "TakeOffGroundHot",
TakeOffGround = "TakeOffGround",
TurningPoint = "Turning Point", TurningPoint = "Turning Point",
Land = "Land", Land = "Land",
LandingReFuAr = "LandingReFuAr", LandingReFuAr = "LandingReFuAr",
@@ -232,13 +271,13 @@ do -- COORDINATE
--- COORDINATE constructor. --- COORDINATE constructor.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North. -- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North.
-- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right. -- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to up.
-- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right. -- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the right.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:New( x, y, z ) function COORDINATE:New( x, y, z )
--env.info("FF COORDINATE New")
local self=BASE:Inherit(self, BASE:New()) -- #COORDINATE local self=BASE:Inherit(self, BASE:New()) -- #COORDINATE
self.x = x self.x = x
self.y = y self.y = y
self.z = z self.z = z
@@ -249,7 +288,7 @@ do -- COORDINATE
--- COORDINATE constructor. --- COORDINATE constructor.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #COORDINATE Coordinate. -- @param #COORDINATE Coordinate.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:NewFromCoordinate( Coordinate ) function COORDINATE:NewFromCoordinate( Coordinate )
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
@@ -263,8 +302,8 @@ do -- COORDINATE
--- Create a new COORDINATE object from Vec2 coordinates. --- Create a new COORDINATE object from Vec2 coordinates.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Vec2 Vec2 The Vec2 point. -- @param DCS#Vec2 Vec2 The Vec2 point.
-- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. -- @param DCS#Distance LandHeightAdd (Optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) function COORDINATE:NewFromVec2( Vec2, LandHeightAdd )
local LandHeight = land.getHeight( Vec2 ) local LandHeight = land.getHeight( Vec2 )
@@ -274,8 +313,6 @@ do -- COORDINATE
local self = self:New( Vec2.x, LandHeight, Vec2.y ) -- #COORDINATE local self = self:New( Vec2.x, LandHeight, Vec2.y ) -- #COORDINATE
self:F2( self )
return self return self
end end
@@ -283,7 +320,7 @@ do -- COORDINATE
--- Create a new COORDINATE object from Vec3 coordinates. --- Create a new COORDINATE object from Vec3 coordinates.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Vec3 Vec3 The Vec3 point. -- @param DCS#Vec3 Vec3 The Vec3 point.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:NewFromVec3( Vec3 ) function COORDINATE:NewFromVec3( Vec3 )
local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE
@@ -293,6 +330,28 @@ do -- COORDINATE
return self return self
end end
--- Create a new COORDINATE object from a waypoint. This uses the components
--
-- * `waypoint.x`
-- * `waypoint.alt`
-- * `waypoint.y`
--
-- @param #COORDINATE self
-- @param DCS#Waypoint Waypoint The waypoint.
-- @return #COORDINATE self
function COORDINATE:NewFromWaypoint(Waypoint)
local self=self:New(Waypoint.x, Waypoint.alt, Waypoint.y) -- #COORDINATE
return self
end
--- Return the coordinates itself. Sounds stupid but can be useful for compatibility.
-- @param #COORDINATE self
-- @return #COORDINATE self
function COORDINATE:GetCoordinate()
return self
end
--- Return the coordinates of the COORDINATE in Vec3 format. --- Return the coordinates of the COORDINATE in Vec3 format.
-- @param #COORDINATE self -- @param #COORDINATE self
@@ -398,7 +457,7 @@ do -- COORDINATE
-- @return #boolean True if units were found. -- @return #boolean True if units were found.
-- @return #boolean True if statics were found. -- @return #boolean True if statics were found.
-- @return #boolean True if scenery objects were found. -- @return #boolean True if scenery objects were found.
-- @return #table Table of MOOSE @[#Wrapper.Unit#UNIT} objects found. -- @return #table Table of MOOSE @{Wrapper.Unit#UNIT} objects found.
-- @return #table Table of DCS static objects found. -- @return #table Table of DCS static objects found.
-- @return #table Table of DCS scenery objects found. -- @return #table Table of DCS scenery objects found.
function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery) function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
@@ -486,7 +545,7 @@ do -- COORDINATE
end end
for _,scenery in pairs(Scenery) do for _,scenery in pairs(Scenery) do
self:T(string.format("Scan found scenery %s typename=%s", scenery:getName(), scenery:getTypeName())) self:T(string.format("Scan found scenery %s typename=%s", scenery:getName(), scenery:getTypeName()))
SCENERY:Register(scenery:getName(), scenery) --SCENERY:Register(scenery:getName(), scenery)
end end
return gotunits, gotstatics, gotscenery, Units, Statics, Scenery return gotunits, gotstatics, gotscenery, Units, Statics, Scenery
@@ -532,6 +591,50 @@ do -- COORDINATE
return umin return umin
end end
--- Scan/find SCENERY objects within a certain radius around the coordinate using the world.searchObjects() DCS API function.
-- @param #COORDINATE self
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
-- @return table Set of scenery objects.
function COORDINATE:ScanScenery(radius)
local _,_,_,_,_,scenerys=self:ScanObjects(radius, false, false, true)
local set={}
for _,_scenery in pairs(scenerys) do
local scenery=_scenery --DCS#Object
local name=scenery:getName()
local s=SCENERY:Register(name, scenery)
table.insert(set, s)
end
return set
end
--- Find the closest scenery to the COORDINATE within a certain radius.
-- @param #COORDINATE self
-- @param #number radius Scan radius in meters. Default 100 m.
-- @return Wrapper.Scenery#SCENERY The closest scenery or #nil if no object is inside the given radius.
function COORDINATE:FindClosestScenery(radius)
local sceneries=self:ScanScenery(radius)
local umin=nil --Wrapper.Scenery#SCENERY
local dmin=math.huge
for _,_scenery in pairs(sceneries) do
local scenery=_scenery --Wrapper.Scenery#SCENERY
local coordinate=scenery:GetCoordinate()
local d=self:Get2DDistance(coordinate)
if d<dmin then
dmin=d
umin=scenery
end
end
return umin
end
--- Calculate the distance from a reference @{#COORDINATE}. --- Calculate the distance from a reference @{#COORDINATE}.
-- @param #COORDINATE self -- @param #COORDINATE self
@@ -597,7 +700,8 @@ do -- COORDINATE
local y=X*math.sin(phi)+Y*math.cos(phi) local y=X*math.sin(phi)+Y*math.cos(phi)
-- Coordinate assignment looks bit strange but is correct. -- Coordinate assignment looks bit strange but is correct.
return COORDINATE:NewFromVec3({x=y, y=self.y, z=x}) local coord=COORDINATE:NewFromVec3({x=y, y=self.y, z=x})
return coord
end end
--- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
@@ -640,7 +744,8 @@ do -- COORDINATE
function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius ) function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } ) self:F2( { OuterRadius, InnerRadius } )
return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) local coord=COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
return coord
end end
@@ -698,6 +803,13 @@ do -- COORDINATE
return Velocity or 0 return Velocity or 0
end end
--- Return the "name" of the COORDINATE. Obviously, a coordinate does not have a name like a unit, static or group. So here we take the MGRS coordinates of the position.
-- @param #COORDINATE self
-- @return #string MGRS coordinates.
function COORDINATE:GetName()
local name=self:ToStringMGRS()
return name
end
--- Return velocity text of the COORDINATE. --- Return velocity text of the COORDINATE.
-- @param #COORDINATE self -- @param #COORDINATE self
@@ -780,13 +892,10 @@ do -- COORDINATE
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3. -- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
-- @return DCS#Distance Distance The distance in meters. -- @return DCS#Distance Distance The distance in meters.
function COORDINATE:Get2DDistance(TargetCoordinate) function COORDINATE:Get2DDistance(TargetCoordinate)
if not TargetCoordinate then return 1000000 end
local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z} local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
local norm=UTILS.VecNorm(a)
local d=UTILS.VecNorm(a) return norm
return d
end end
--- Returns the temperature in Degrees Celsius. --- Returns the temperature in Degrees Celsius.
@@ -807,7 +916,7 @@ do -- COORDINATE
-- The text will reflect the temperature like this: -- The text will reflect the temperature like this:
-- --
-- - For Russian and European aircraft using the metric system - Degrees Celcius (°C) -- - For Russian and European aircraft using the metric system - Degrees Celcius (°C)
-- - For Americain aircraft we link to the imperial system - Degrees Farenheit (°F) -- - For American aircraft we link to the imperial system - Degrees Fahrenheit (°F)
-- --
-- A text containing a pressure will look like this: -- A text containing a pressure will look like this:
-- --
@@ -827,7 +936,7 @@ do -- COORDINATE
if Settings:IsMetric() then if Settings:IsMetric() then
return string.format( " %-2.2f °C", DegreesCelcius ) return string.format( " %-2.2f °C", DegreesCelcius )
else else
return string.format( " %-2.2f °F", UTILS.CelciusToFarenheit( DegreesCelcius ) ) return string.format( " %-2.2f °F", UTILS.CelsiusToFahrenheit( DegreesCelcius ) )
end end
else else
return " no temperature" return " no temperature"
@@ -853,7 +962,7 @@ do -- COORDINATE
-- The text will contain always the pressure in hPa and: -- The text will contain always the pressure in hPa and:
-- --
-- - For Russian and European aircraft using the metric system - hPa and mmHg -- - For Russian and European aircraft using the metric system - hPa and mmHg
-- - For Americain and European aircraft we link to the imperial system - hPa and inHg -- - For American and European aircraft we link to the imperial system - hPa and inHg
-- --
-- A text containing a pressure will look like this: -- A text containing a pressure will look like this:
-- --
@@ -946,7 +1055,7 @@ do -- COORDINATE
-- The text will reflect the wind like this: -- The text will reflect the wind like this:
-- --
-- - For Russian and European aircraft using the metric system - Wind direction in degrees (°) and wind speed in meters per second (mps). -- - For Russian and European aircraft using the metric system - Wind direction in degrees (°) and wind speed in meters per second (mps).
-- - For Americain aircraft we link to the imperial system - Wind direction in degrees (°) and wind speed in knots per second (kps). -- - For American aircraft we link to the imperial system - Wind direction in degrees (°) and wind speed in knots per second (kps).
-- --
-- A text containing a pressure will look like this: -- A text containing a pressure will look like this:
-- --
@@ -1007,25 +1116,28 @@ do -- COORDINATE
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #number Distance The distance in meters. -- @param #number Distance The distance in meters.
-- @param Core.Settings#SETTINGS Settings -- @param Core.Settings#SETTINGS Settings
-- @param #string Language (optional) "EN" or "RU"
-- @param #number Precision (optional) round to this many decimal places
-- @return #string The distance text expressed in the units of measurement. -- @return #string The distance text expressed in the units of measurement.
function COORDINATE:GetDistanceText( Distance, Settings, Language ) function COORDINATE:GetDistanceText( Distance, Settings, Language, Precision )
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
local Language = Language or "EN" local Language = Language or "EN"
local Precision = Precision or 0
local DistanceText local DistanceText
if Settings:IsMetric() then if Settings:IsMetric() then
if Language == "EN" then if Language == "EN" then
DistanceText = " for " .. UTILS.Round( Distance / 1000, 2 ) .. " km" DistanceText = " for " .. UTILS.Round( Distance / 1000, Precision ) .. " km"
elseif Language == "RU" then elseif Language == "RU" then
DistanceText = " за " .. UTILS.Round( Distance / 1000, 2 ) .. " километров" DistanceText = " за " .. UTILS.Round( Distance / 1000, Precision ) .. " километров"
end end
else else
if Language == "EN" then if Language == "EN" then
DistanceText = " for " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " miles" DistanceText = " for " .. UTILS.Round( UTILS.MetersToNM( Distance ), Precision ) .. " miles"
elseif Language == "RU" then elseif Language == "RU" then
DistanceText = " за " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " миль" DistanceText = " за " .. UTILS.Round( UTILS.MetersToNM( Distance ), Precision ) .. " миль"
end end
end end
@@ -1103,7 +1215,7 @@ do -- COORDINATE
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language ) local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language )
local DistanceText = self:GetDistanceText( Distance, Settings, Language ) local DistanceText = self:GetDistanceText( Distance, Settings, Language, 0 )
local BRText = BearingText .. DistanceText local BRText = BearingText .. DistanceText
@@ -1121,7 +1233,7 @@ do -- COORDINATE
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language ) local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language )
local DistanceText = self:GetDistanceText( Distance, Settings, Language ) local DistanceText = self:GetDistanceText( Distance, Settings, Language, 0 )
local AltitudeText = self:GetAltitudeText( Settings, Language ) local AltitudeText = self:GetAltitudeText( Settings, Language )
local BRAText = BearingText .. DistanceText .. AltitudeText -- When the POINT is a VEC2, there will be no altitude shown. local BRAText = BearingText .. DistanceText .. AltitudeText -- When the POINT is a VEC2, there will be no altitude shown.
@@ -1552,8 +1664,12 @@ do -- COORDINATE
roadtype="railroads" roadtype="railroads"
end end
local x,y = land.getClosestPointOnRoads(roadtype, self.x, self.z) local x,y = land.getClosestPointOnRoads(roadtype, self.x, self.z)
local coord=nil
if x and y then
local vec2={ x = x, y = y } local vec2={ x = x, y = y }
return COORDINATE:NewFromVec2(vec2) coord=COORDINATE:NewFromVec2(vec2)
end
return coord
end end
@@ -1659,7 +1775,7 @@ do -- COORDINATE
return self:GetSurfaceType()==land.SurfaceType.LAND return self:GetSurfaceType()==land.SurfaceType.LAND
end end
--- Checks if the surface type is road. --- Checks if the surface type is land.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @return #boolean If true, the surface type at the coordinate is land. -- @return #boolean If true, the surface type at the coordinate is land.
function COORDINATE:IsSurfaceTypeLand() function COORDINATE:IsSurfaceTypeLand()
@@ -1699,13 +1815,12 @@ do -- COORDINATE
--- Creates an explosion at the point of a certain intensity. --- Creates an explosion at the point of a certain intensity.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg. -- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg.
-- @param #number Delay Delay before explosion in seconds. -- @param #number Delay (Optional) Delay before explosion is triggered in seconds.
-- @return #COORDINATE self -- @return #COORDINATE self
function COORDINATE:Explosion( ExplosionIntensity, Delay ) function COORDINATE:Explosion( ExplosionIntensity, Delay )
self:F2( { ExplosionIntensity } )
ExplosionIntensity=ExplosionIntensity or 100 ExplosionIntensity=ExplosionIntensity or 100
if Delay and Delay>0 then if Delay and Delay>0 then
SCHEDULER:New(nil, self.Explosion, {self,ExplosionIntensity}, Delay) self:ScheduleOnce(Delay, self.Explosion, self, ExplosionIntensity)
else else
trigger.action.explosion(self:GetVec3(), ExplosionIntensity) trigger.action.explosion(self:GetVec3(), ExplosionIntensity)
end end
@@ -1714,11 +1829,17 @@ do -- COORDINATE
--- Creates an illumination bomb at the point. --- Creates an illumination bomb at the point.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #number power Power of illumination bomb in Candela. -- @param #number Power Power of illumination bomb in Candela. Default 1000 cd.
-- @param #number Delay (Optional) Delay before bomb is ignited in seconds.
-- @return #COORDINATE self -- @return #COORDINATE self
function COORDINATE:IlluminationBomb(power) function COORDINATE:IlluminationBomb(Power, Delay)
self:F2() Power=Power or 1000
trigger.action.illuminationBomb( self:GetVec3(), power ) if Delay and Delay>0 then
self:ScheduleOnce(Delay, self.IlluminationBomb, self, Power)
else
trigger.action.illuminationBomb(self:GetVec3(), Power)
end
return self
end end
@@ -1767,84 +1888,103 @@ do -- COORDINATE
--- Big smoke and fire at the coordinate. --- Big smoke and fire at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (0=small smoke and fire, 1=medium smoke and fire, 2=large smoke and fire, 3=huge smoke and fire, 4=small smoke, 5=medium smoke, 6=large smoke, 7=huge smoke). -- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke).
-- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5. -- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5.
function COORDINATE:BigSmokeAndFire( preset, density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFire( preset, density, name )
self:F2( { preset=preset, density=density } ) self:F2( { preset=preset, density=density } )
density=density or 0.5 density=density or 0.5
trigger.action.effectSmokeBig( self:GetVec3(), preset, density ) self.firename = name or "Fire-"..math.random(1,10000)
trigger.action.effectSmokeBig( self:GetVec3(), preset, density, self.firename )
end
--- Stop big smoke and fire at the coordinate.
-- @param #COORDINATE self
-- @param #string name (Optional) Name of the fire to stop it, if not using the same COORDINATE object.
function COORDINATE:StopBigSmokeAndFire( name )
self:F2( { name = name } )
name = name or self.firename
trigger.action.effectSmokeStop( name )
end end
--- Small smoke and fire at the coordinate. --- Small smoke and fire at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireSmall( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireSmall( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, density) self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, density, name)
end end
--- Medium smoke and fire at the coordinate. --- Medium smoke and fire at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireMedium( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireMedium( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density) self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density, name)
end end
--- Large smoke and fire at the coordinate. --- Large smoke and fire at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireLarge( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireLarge( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, density) self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, density, name)
end end
--- Huge smoke and fire at the coordinate. --- Huge smoke and fire at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeAndFireHuge( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeAndFireHuge( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density) self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density, name)
end end
--- Small smoke at the coordinate. --- Small smoke at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeSmall( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeSmall( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density) self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density, name)
end end
--- Medium smoke at the coordinate. --- Medium smoke at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeMedium( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeMedium( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, density) self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, density, name)
end end
--- Large smoke at the coordinate. --- Large smoke at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeLarge( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeLarge( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density) self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density,name)
end end
--- Huge smoke at the coordinate. --- Huge smoke at the coordinate.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5.
function COORDINATE:BigSmokeHuge( density ) -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number.
function COORDINATE:BigSmokeHuge( density, name )
self:F2( { density=density } ) self:F2( { density=density } )
density=density or 0.5 density=density or 0.5
self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density) self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density,name)
end end
--- Flares the point in a color. --- Flares the point in a color.
@@ -1991,7 +2131,7 @@ do -- COORDINATE
--- Line to all. --- Line to all.
-- Creates a line on the F10 map from one point to another. -- Creates a line on the F10 map from one point to another.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #COORDINATE Endpoint COORDIANTE to where the line is drawn. -- @param #COORDINATE Endpoint COORDINATE to where the line is drawn.
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
-- @param #number Alpha Transparency [0,1]. Default 1. -- @param #number Alpha Transparency [0,1]. Default 1.
@@ -2016,7 +2156,7 @@ do -- COORDINATE
--- Circle to all. --- Circle to all.
-- Creates a circle on the map with a given radius, color, fill color, and outline. -- Creates a circle on the map with a given radius, color, fill color, and outline.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #numberr Radius Radius in meters. Default 1000 m. -- @param #number Radius Radius in meters. Default 1000 m.
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
-- @param #number Alpha Transparency [0,1]. Default 1. -- @param #number Alpha Transparency [0,1]. Default 1.
@@ -2031,14 +2171,21 @@ do -- COORDINATE
if ReadOnly==nil then if ReadOnly==nil then
ReadOnly=false ReadOnly=false
end end
local vec3=self:GetVec3() local vec3=self:GetVec3()
Radius=Radius or 1000 Radius=Radius or 1000
Coalition=Coalition or -1 Coalition=Coalition or -1
Color=Color or {1,0,0} Color=Color or {1,0,0}
Color[4]=Alpha or 1.0 Color[4]=Alpha or 1.0
LineType=LineType or 1 LineType=LineType or 1
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillColor[4]=FillAlpha or 0.15 FillColor[4]=FillAlpha or 0.15
trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "") trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "")
return MarkID return MarkID
end end
@@ -2048,7 +2195,7 @@ do -- COORDINATE
--- Rectangle to all. Creates a rectangle on the map from the COORDINATE in one corner to the end COORDINATE in the opposite corner. --- Rectangle to all. Creates a rectangle on the map from the COORDINATE in one corner to the end COORDINATE in the opposite corner.
-- Creates a line on the F10 map from one point to another. -- Creates a line on the F10 map from one point to another.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #COORDINATE Endpoint COORDIANTE in the opposite corner. -- @param #COORDINATE Endpoint COORDINATE in the opposite corner.
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
-- @param #number Alpha Transparency [0,1]. Default 1. -- @param #number Alpha Transparency [0,1]. Default 1.
@@ -2063,22 +2210,28 @@ do -- COORDINATE
if ReadOnly==nil then if ReadOnly==nil then
ReadOnly=false ReadOnly=false
end end
local vec3=Endpoint:GetVec3() local vec3=Endpoint:GetVec3()
Coalition=Coalition or -1 Coalition=Coalition or -1
Color=Color or {1,0,0} Color=Color or {1,0,0}
Color[4]=Alpha or 1.0 Color[4]=Alpha or 1.0
LineType=LineType or 1 LineType=LineType or 1
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillColor[4]=FillAlpha or 0.15 FillColor[4]=FillAlpha or 0.15
trigger.action.rectToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, FillColor, LineType, ReadOnly, Text or "") trigger.action.rectToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, FillColor, LineType, ReadOnly, Text or "")
return MarkID return MarkID
end end
--- Creates a shape defined by 4 points on the F10 map. The first point is the current COORDINATE. The remaining three points need to be specified. --- Creates a shape defined by 4 points on the F10 map. The first point is the current COORDINATE. The remaining three points need to be specified.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #COORDINATE Coord2 Second COORDIANTE of the quad shape. -- @param #COORDINATE Coord2 Second COORDINATE of the quad shape.
-- @param #COORDINATE Coord3 Third COORDIANTE of the quad shape. -- @param #COORDINATE Coord3 Third COORDINATE of the quad shape.
-- @param #COORDINATE Coord4 Fourth COORDIANTE of the quad shape. -- @param #COORDINATE Coord4 Fourth COORDINATE of the quad shape.
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
-- @param #number Alpha Transparency [0,1]. Default 1. -- @param #number Alpha Transparency [0,1]. Default 1.
@@ -2093,17 +2246,23 @@ do -- COORDINATE
if ReadOnly==nil then if ReadOnly==nil then
ReadOnly=false ReadOnly=false
end end
local point1=self:GetVec3() local point1=self:GetVec3()
local point2=Coord2:GetVec3() local point2=Coord2:GetVec3()
local point3=Coord3:GetVec3() local point3=Coord3:GetVec3()
local point4=Coord4:GetVec3() local point4=Coord4:GetVec3()
Coalition=Coalition or -1 Coalition=Coalition or -1
Color=Color or {1,0,0} Color=Color or {1,0,0}
Color[4]=Alpha or 1.0 Color[4]=Alpha or 1.0
LineType=LineType or 1 LineType=LineType or 1
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillColor[4]=FillAlpha or 0.15 FillColor[4]=FillAlpha or 0.15
trigger.action.quadToAll(Coalition, MarkID, self:GetVec3(), point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "")
trigger.action.quadToAll(Coalition, MarkID, point1, point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "")
return MarkID return MarkID
end end
@@ -2152,7 +2311,7 @@ do -- COORDINATE
elseif #vecs==5 then elseif #vecs==5 then
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], Color, FillColor, LineType, ReadOnly, Text or "") trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], Color, FillColor, LineType, ReadOnly, Text or "")
elseif #vecs==6 then elseif #vecs==6 then
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], Color, FillColor, LineType, Text or "") trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], Color, FillColor, LineType, ReadOnly, Text or "")
elseif #vecs==7 then elseif #vecs==7 then
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], Color, FillColor, LineType, ReadOnly, Text or "") trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], Color, FillColor, LineType, ReadOnly, Text or "")
elseif #vecs==8 then elseif #vecs==8 then
@@ -2187,11 +2346,15 @@ do -- COORDINATE
ReadOnly=false ReadOnly=false
end end
Coalition=Coalition or -1 Coalition=Coalition or -1
Color=Color or {1,0,0} Color=Color or {1,0,0}
Color[4]=Alpha or 1.0 Color[4]=Alpha or 1.0
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillColor[4]=FillAlpha or 0.3 FillColor[4]=FillAlpha or 0.3
FontSize=FontSize or 14 FontSize=FontSize or 14
trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World") trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World")
return MarkID return MarkID
end end
@@ -2213,13 +2376,19 @@ do -- COORDINATE
if ReadOnly==nil then if ReadOnly==nil then
ReadOnly=false ReadOnly=false
end end
local vec3=Endpoint:GetVec3() local vec3=Endpoint:GetVec3()
Coalition=Coalition or -1 Coalition=Coalition or -1
Color=Color or {1,0,0} Color=Color or {1,0,0}
Color[4]=Alpha or 1.0 Color[4]=Alpha or 1.0
LineType=LineType or 1 LineType=LineType or 1
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillColor[4]=FillAlpha or 0.15 FillColor[4]=FillAlpha or 0.15
--trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World") --trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World")
trigger.action.arrowToAll(Coalition, MarkID, vec3, self:GetVec3(), Color, FillColor, LineType, ReadOnly, Text or "") trigger.action.arrowToAll(Coalition, MarkID, vec3, self:GetVec3(), Color, FillColor, LineType, ReadOnly, Text or "")
return MarkID return MarkID
@@ -2595,6 +2764,94 @@ do -- COORDINATE
return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings, Language ) return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings, Language )
end end
--- Create a BRAA NATO call string to this COORDINATE from the FromCOORDINATE. Note - BRA delivered if no aspect can be obtained and "Merged" if range < 3nm
-- @param #COORDINATE self
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
-- @param #boolean Bogey Add "Bogey" at the end if true (not yet declared hostile or friendly)
-- @param #boolean Spades Add "Spades" at the end if true (no IFF/VID ID yet known)
-- @param #boolean SSML Add SSML tags speaking aspect as 0 1 2 and "brah" instead of BRAA
-- @param #boolean Angels If true, altitude is e.g. "Angels 25" (i.e., a friendly plane), else "25 thousand"
-- @param #boolean Zeros If using SSML, be aware that Google TTS will say "oh" and not "zero" for "0"; if Zeros is set to true, "0" will be replaced with "zero"
-- @return #string The BRAA text.
function COORDINATE:ToStringBRAANATO(FromCoordinate,Bogey,Spades,SSML,Angels,Zeros)
-- Thanks to @Pikey
local BRAANATO = "Merged."
local currentCoord = FromCoordinate
local DirectionVec3 = FromCoordinate:GetDirectionVec3( self )
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
local bearing = UTILS.Round( UTILS.ToDegree( AngleRadians ),0 )
local rangeMetres = self:Get2DDistance(currentCoord)
local rangeNM = UTILS.Round( UTILS.MetersToNM(rangeMetres), 0)
local aspect = self:ToStringAspect(currentCoord)
local alt = UTILS.Round(UTILS.MetersToFeet(self.y)/1000,0)--*1000
local alttext = string.format("%d thousand",alt)
if Angels then
alttext = string.format("Angels %d",alt)
end
if alt < 1 then
alttext = "very low"
end
local track = UTILS.BearingToCardinal(bearing) or "North"
if rangeNM > 3 then
if SSML then -- google says "oh" instead of zero, be aware
if Zeros then
bearing = string.format("%03d",bearing)
local AngleDegText = string.gsub(bearing,"%d","%1 ") -- "0 5 1 "
AngleDegText = string.gsub(AngleDegText," $","") -- "0 5 1"
AngleDegText = string.gsub(AngleDegText,"0","zero")
if aspect == "" then
BRAANATO = string.format("brah %s, %d miles, %s, Track %s", AngleDegText, rangeNM, alttext, track)
else
BRAANATO = string.format("brah %s, %d miles, %s, %s, Track %s", AngleDegText, rangeNM, alttext, aspect, track)
end
else
if aspect == "" then
BRAANATO = string.format("brah <say-as interpret-as='characters'>%03d</say-as>, %d miles, %s, Track %s", bearing, rangeNM, alttext, track)
else
BRAANATO = string.format("brah <say-as interpret-as='characters'>%03d</say-as>, %d miles, %s, %s, Track %s", bearing, rangeNM, alttext, aspect, track)
end
end
if Bogey and Spades then
BRAANATO = BRAANATO..", Bogey, Spades."
elseif Bogey then
BRAANATO = BRAANATO..", Bogey."
elseif Spades then
BRAANATO = BRAANATO..", Spades."
else
BRAANATO = BRAANATO.."."
end
else
if aspect == "" then
BRAANATO = string.format("BRA %03d, %d miles, %s, Track %s",bearing, rangeNM, alttext, track)
else
BRAANATO = string.format("BRAA %03d, %d miles, %s, %s, Track %s",bearing, rangeNM, alttext, aspect, track)
end
if Bogey and Spades then
BRAANATO = BRAANATO..", Bogey, Spades."
elseif Bogey then
BRAANATO = BRAANATO..", Bogey."
elseif Spades then
BRAANATO = BRAANATO..", Spades."
else
BRAANATO = BRAANATO.."."
end
end
end
return BRAANATO
end
--- Return a BULLS string out of the BULLS of the coalition to the COORDINATE. --- Return a BULLS string out of the BULLS of the coalition to the COORDINATE.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#coalition.side Coalition The coalition. -- @param DCS#coalition.side Coalition The coalition.
@@ -2893,7 +3150,7 @@ do -- POINT_VEC3
-- @type POINT_VEC3 -- @type POINT_VEC3
-- @field #number x The x coordinate in 3D space. -- @field #number x The x coordinate in 3D space.
-- @field #number y The y coordinate in 3D space. -- @field #number y The y coordinate in 3D space.
-- @field #number z The z coordiante in 3D space. -- @field #number z The z COORDINATE in 3D space.
-- @field Utilities.Utils#SMOKECOLOR SmokeColor -- @field Utilities.Utils#SMOKECOLOR SmokeColor
-- @field Utilities.Utils#FLARECOLOR FlareColor -- @field Utilities.Utils#FLARECOLOR FlareColor
-- @field #POINT_VEC3.RoutePointAltType RoutePointAltType -- @field #POINT_VEC3.RoutePointAltType RoutePointAltType
@@ -3325,5 +3582,3 @@ do -- POINT_VEC2
end end
end end

View File

@@ -15,7 +15,6 @@
-- @module Core.Report -- @module Core.Report
-- @image Core_Report.JPG -- @image Core_Report.JPG
--- @type REPORT --- @type REPORT
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -50,7 +49,6 @@ function REPORT:HasText() --R2.1
return #self.Report > 0 return #self.Report > 0
end end
--- Set indent of a REPORT. --- Set indent of a REPORT.
-- @param #REPORT self -- @param #REPORT self
-- @param #number Indent -- @param #number Indent
@@ -60,7 +58,6 @@ function REPORT:SetIndent( Indent ) --R2.1
return self return self
end end
--- Add a new line to a REPORT. --- Add a new line to a REPORT.
-- @param #REPORT self -- @param #REPORT self
-- @param #string Text -- @param #string Text
@@ -80,7 +77,7 @@ function REPORT:AddIndent( Text, Separator )
return self return self
end end
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default. --- Produces the text of the report, taking into account an optional delimiter, which is \n by default.
-- @param #REPORT self -- @param #REPORT self
-- @param #string Delimiter (optional) A delimiter text. -- @param #string Delimiter (optional) A delimiter text.
-- @return #string The report text. -- @return #string The report text.

View File

@@ -17,7 +17,7 @@
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory. -- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory.
-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection! -- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
-- --
-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected. -- The non-persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collected when the parent object is destroyed, or set to nil and garbage collected.
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object, -- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
-- these will not be executed anymore when the SCHEDULER object has been destroyed. -- these will not be executed anymore when the SCHEDULER object has been destroyed.
-- --
@@ -38,7 +38,7 @@
-- @type SCHEDULEDISPATCHER -- @type SCHEDULEDISPATCHER
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #number CallID Call ID counter. -- @field #number CallID Call ID counter.
-- @field #table PersistentSchedulers Persistant schedulers. -- @field #table PersistentSchedulers Persistent schedulers.
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists. -- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ). -- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -58,7 +58,7 @@ SCHEDULEDISPATCHER = {
-- @field #function Function The schedule function to be called. -- @field #function Function The schedule function to be called.
-- @field #table Arguments Schedule function arguments. -- @field #table Arguments Schedule function arguments.
-- @field #number Start Start time in seconds. -- @field #number Start Start time in seconds.
-- @field #number Repeat Repeat time intervall in seconds. -- @field #number Repeat Repeat time interval in seconds.
-- @field #number Randomize Randomization factor [0,1]. -- @field #number Randomize Randomization factor [0,1].
-- @field #number Stop Stop time in seconds. -- @field #number Stop Stop time in seconds.
-- @field #number StartTime Time in seconds when the scheduler is created. -- @field #number StartTime Time in seconds when the scheduler is created.
@@ -77,7 +77,7 @@ end
--- Add a Schedule to the ScheduleDispatcher. --- Add a Schedule to the ScheduleDispatcher.
-- The development of this method was really tidy. -- The development of this method was really tidy.
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified. -- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is set to nil.
-- Nothing of this code should be modified without testing it thoroughly. -- Nothing of this code should be modified without testing it thoroughly.
-- @param #SCHEDULEDISPATCHER self -- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
@@ -85,7 +85,7 @@ end
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction. -- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
-- @param #number Start Start time in seconds. -- @param #number Start Start time in seconds.
-- @param #number Repeat Repeat interval in seconds. -- @param #number Repeat Repeat interval in seconds.
-- @param #number Randomize Radomization factor [0,1]. -- @param #number Randomize Randomization factor [0,1].
-- @param #number Stop Stop time in seconds. -- @param #number Stop Stop time in seconds.
-- @param #number TraceLevel Trace level [0,3]. -- @param #number TraceLevel Trace level [0,3].
-- @param Core.Fsm#FSM Fsm Finite state model. -- @param Core.Fsm#FSM Fsm Finite state model.
@@ -127,7 +127,6 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
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
-- This section handles the tracing of the scheduled calls. -- This section handles the tracing of the scheduled calls.
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated. -- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name. -- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
@@ -149,7 +148,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
-- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result. -- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result.
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level. -- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher. -- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
-- So this little piece of code does its magic wonderfully, preformance overhead is neglectible, as scheduled calls don't happen that often. -- So this little piece of code does its magic wonderfully, performance overhead is negligible, as scheduled calls don't happen that often.
local Info = {} local Info = {}
@@ -181,7 +180,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
return errmsg return errmsg
end end
-- Get object or persistant scheduler object. -- Get object or persistent scheduler object.
local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER
if not Scheduler then if not Scheduler then
Scheduler = self.PersistentSchedulers[CallID] Scheduler = self.PersistentSchedulers[CallID]
@@ -198,7 +197,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
-- self:T3( { Schedule = Schedule } ) -- self:T3( { Schedule = Schedule } )
local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object? local SchedulerObject = Scheduler.MasterObject -- Scheduler.SchedulerObject Now is this the Master or Scheduler object?
local ShowTrace = Scheduler.ShowTrace local ShowTrace = Scheduler.ShowTrace
local ScheduleFunction = Schedule.Function local ScheduleFunction = Schedule.Function
@@ -209,7 +208,6 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
local Stop = Schedule.Stop or 0 local Stop = Schedule.Stop or 0
local ScheduleID = Schedule.ScheduleID local ScheduleID = Schedule.ScheduleID
local Prefix = (Repeat == 0) and "--->" or "+++>" local Prefix = (Repeat == 0) and "--->" or "+++>"
local Status, Result local Status, Result
@@ -238,7 +236,6 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
-- Debug info. -- Debug info.
self:F3( { CallID = CallID, ScheduleID = ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } ) self:F3( { CallID = CallID, ScheduleID = ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
if Status and ((Result == nil) or (Result and Result ~= false)) then if Status and ((Result == nil) or (Result and Result ~= false)) then
if Repeat ~= 0 and ((Stop == 0) or (Stop ~= 0 and CurrentTime <= StartTime + Stop)) then if Repeat ~= 0 and ((Stop == 0) or (Stop ~= 0 and CurrentTime <= StartTime + Stop)) then
@@ -301,7 +298,7 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction -- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
Schedule.ScheduleID = timer.scheduleFunction( Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start ) Schedule.ScheduleID = timer.scheduleFunction( Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start )
self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID))) self:T( string.format( "Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
end end
else else
@@ -328,7 +325,7 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing. -- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
if Schedule.ScheduleID then if Schedule.ScheduleID then
self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID))) self:T( string.format( "SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction -- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
timer.removeFunction( Schedule.ScheduleID ) timer.removeFunction( Schedule.ScheduleID )
@@ -359,7 +356,7 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
end end
end end
--- Shopw tracing info. --- Show tracing info.
-- @param #SCHEDULEDISPATCHER self -- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
function SCHEDULEDISPATCHER:ShowTrace( Scheduler ) function SCHEDULEDISPATCHER:ShowTrace( 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)
-- --
@@ -48,7 +48,6 @@
-- @field #boolean ShowTrace Trace info if true. -- @field #boolean ShowTrace Trace info if true.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization. --- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization.
-- --
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**. -- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
@@ -79,7 +78,7 @@
-- --
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence... -- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
-- --
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. -- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is set to nil or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
-- --
-- ZoneObject = ZONE:New( "ZoneName" ) -- ZoneObject = ZONE:New( "ZoneName" )
-- MasterObject = SCHEDULER:New( ZoneObject ) -- MasterObject = SCHEDULER:New( ZoneObject )
@@ -149,13 +148,13 @@
-- ZoneObject = ZONE:New( "ZoneName" ) -- ZoneObject = ZONE:New( "ZoneName" )
-- MasterObject = SCHEDULER:New( ZoneObject ) -- MasterObject = SCHEDULER:New( ZoneObject )
-- --
-- Several parameters can be specified that influence the behaviour of a Schedule. -- Several parameters can be specified that influence the behavior of a Schedule.
-- --
-- ### A single schedule, immediately executed -- ### A single schedule, immediately executed
-- --
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- --
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ... -- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milliseconds ...
-- --
-- ### A single schedule, planned over time -- ### A single schedule, planned over time
-- --
@@ -235,7 +234,7 @@ end
-- @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 #number Repeat Specifies the time interval in seconds when the scheduler will call the event function. -- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. -- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe. -- @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 #table The ScheduleID of the planned schedule.
@@ -254,8 +253,7 @@ function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments
self.MasterObject = MasterObject self.MasterObject = MasterObject
-- Add schedule. -- Add schedule.
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self,
self,
SchedulerFunction, SchedulerFunction,
SchedulerArguments, SchedulerArguments,
Start, Start,

File diff suppressed because it is too large Load Diff

View File

@@ -29,15 +29,14 @@
-- @module Core.Settings -- @module Core.Settings
-- @image Core_Settings.JPG -- @image Core_Settings.JPG
--- @type SETTINGS --- @type SETTINGS
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. --- Takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
-- --
-- === -- ===
-- --
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. -- The SETTINGS class takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
-- SETTINGS can work on 2 levels: -- SETTINGS can work on 2 levels:
-- --
-- - **Default settings**: A running mission has **Default settings**. -- - **Default settings**: A running mission has **Default settings**.
@@ -59,7 +58,7 @@
-- --
-- A menu is created automatically per Command Center that allows to modify the **Default** settings. -- A menu is created automatically per Command Center that allows to modify the **Default** settings.
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**! -- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
-- Note that the **Default settings** will only be used when a player has not choosen its own settings. -- Note that the **Default settings** will only be used when a player has not chosen its own settings.
-- --
-- ## 2.2) Player settings menu -- ## 2.2) Player settings menu
-- --
@@ -69,7 +68,7 @@
-- --
-- ## 2.3) Show or Hide the Player Setting menus -- ## 2.3) Show or Hide the Player Setting menus
-- --
-- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**. -- Of course, it may be required not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus. -- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed. -- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
-- The option will only have effect when a player enters a new slot or changes a slot. -- The option will only have effect when a player enters a new slot or changes a slot.
@@ -94,8 +93,8 @@
-- --
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)). -- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted. -- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted. -- - A2G LL DMS: Latitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted. -- - A2G LL DDM: Latitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
-- --
-- ### 3.1.2) A2G coordinates setting **menu** -- ### 3.1.2) A2G coordinates setting **menu**
-- --
@@ -183,7 +182,7 @@
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
-- --
-- Each Message Type has specific timings that will be applied when the message is displayed. -- Each Message Type has specific timings that will be applied when the message is displayed.
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be choosen. -- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be chosen.
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type. -- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
-- Note that **Update** messages can be chosen not to be displayed at all! -- Note that **Update** messages can be chosen not to be displayed at all!
-- --
@@ -196,7 +195,7 @@
-- --
-- ## 3.5) **Era** of the battle -- ## 3.5) **Era** of the battle
-- --
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greather threat in WWII than on modern warfare. -- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greater threat in WWII than on modern warfare.
-- Therefore, there are 4 era that are defined within the settings: -- Therefore, there are 4 era that are defined within the settings:
-- --
-- - **WWII** era: Use for warfare with equipment during the world war II time. -- - **WWII** era: Use for warfare with equipment during the world war II time.
@@ -231,7 +230,6 @@ SETTINGS.__Enum.Era = {
Modern = 4, Modern = 4,
} }
do -- SETTINGS do -- SETTINGS
--- SETTINGS constructor. --- SETTINGS constructor.
@@ -344,7 +342,6 @@ do -- SETTINGS
self.MessageTypeTimings[MessageType] = MessageTime self.MessageTypeTimings[MessageType] = MessageTime
end end
--- Gets the SETTINGS Message Display Timing of a MessageType --- Gets the SETTINGS Message Display Timing of a MessageType
-- @param #SETTINGS self -- @param #SETTINGS self
-- @param Core.Message#MESSAGE MessageType The type of the message. -- @param Core.Message#MESSAGE MessageType The type of the message.
@@ -689,7 +686,6 @@ do -- SETTINGS
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
SettingsMenu:Remove( MenuTime ) SettingsMenu:Remove( MenuTime )
return self return self
@@ -802,7 +798,6 @@ do -- SETTINGS
end end
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu ) local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then
local text = "Lat/Lon Degree Min Sec (LL DMS)" local text = "Lat/Lon Degree Min Sec (LL DMS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
@@ -935,7 +930,6 @@ do -- SETTINGS
return self return self
end end
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem ) function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
self.A2GSystem = A2GSystem self.A2GSystem = A2GSystem
@@ -1055,7 +1049,6 @@ do -- SETTINGS
end end
--- Configures the era of the mission to be Cold war. --- Configures the era of the mission to be Cold war.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #SETTINGS self -- @return #SETTINGS self
@@ -1065,7 +1058,6 @@ do -- SETTINGS
end end
--- Configures the era of the mission to be Modern war. --- Configures the era of the mission to be Modern war.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #SETTINGS self -- @return #SETTINGS self
@@ -1075,7 +1067,4 @@ do -- SETTINGS
end end
end end

View File

@@ -10,7 +10,7 @@
-- * Randomize the spawning location between different zones. -- * Randomize the spawning location between different zones.
-- * Randomize the initial positions within the zones. -- * Randomize the initial positions within the zones.
-- * Spawn in array formation. -- * Spawn in array formation.
-- * Spawn uncontrolled (for planes or helos only). -- * Spawn uncontrolled (for planes or helicopters only).
-- * Clean up inactive helicopters that "crashed". -- * Clean up inactive helicopters that "crashed".
-- * Place a hook to capture a spawn event, and tailor with customer code. -- * Place a hook to capture a spawn event, and tailor with customer code.
-- * Spawn late activated. -- * Spawn late activated.
@@ -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)
-- --
-- === -- ===
-- --
@@ -46,7 +46,6 @@
-- @module Core.Spawn -- @module Core.Spawn
-- @image Core_Spawn.JPG -- @image Core_Spawn.JPG
--- SPAWN Class --- SPAWN Class
-- @type SPAWN -- @type SPAWN
-- @field ClassName -- @field ClassName
@@ -59,7 +58,6 @@
-- @field #SPAWN.SpawnZoneTable SpawnZoneTable -- @field #SPAWN.SpawnZoneTable SpawnZoneTable
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Allows to spawn dynamically new @{Core.Group}s. --- Allows to spawn dynamically new @{Core.Group}s.
-- --
-- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME), -- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME),
@@ -72,7 +70,7 @@
-- **the name of the template group** to be given as a string to those constructor methods. -- **the name of the template group** to be given as a string to those constructor methods.
-- --
-- Initialization settings can be applied on the SPAWN object, -- Initialization settings can be applied on the SPAWN object,
-- which modify the behaviour or the way groups are spawned. -- which modify the behavior or the way groups are spawned.
-- These initialization methods have the prefix **Init**. -- These initialization methods have the prefix **Init**.
-- There are also spawn methods with the prefix **Spawn** and will spawn new groups in various ways. -- There are also spawn methods with the prefix **Spawn** and will spawn new groups in various ways.
-- --
@@ -163,7 +161,7 @@
-- --
-- ### Array formation -- ### Array formation
-- --
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array. -- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a battalion in an array.
-- --
-- ### Position randomization -- ### Position randomization
-- --
@@ -183,16 +181,15 @@
-- --
-- ### Delay initial scheduled spawn -- ### Delay initial scheduled spawn
-- --
-- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Wrapper.Group} object. -- * @{#SPAWN.InitDelayOnOff}(): Turns the initial delay On/Off when scheduled spawning the first @{Wrapper.Group} object.
-- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Wrapper.Group} object. -- * @{#SPAWN.InitDelayOn}(): Turns the initial delay On when scheduled spawning the first @{Wrapper.Group} object.
-- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Wrapper.Group} object. -- * @{#SPAWN.InitDelayOff}(): Turns the initial delay Off when scheduled spawning the first @{Wrapper.Group} object.
-- --
-- ### Repeat spawned @{Wrapper.Group}s upon landing -- ### Repeat spawned @{Wrapper.Group}s upon landing
-- --
-- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed. -- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed.
-- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp. -- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp.
-- --
--
-- ## SPAWN **Spawn** methods -- ## SPAWN **Spawn** methods
-- --
-- Groups can be spawned at different times and methods: -- Groups can be spawned at different times and methods:
@@ -217,7 +214,6 @@
--- * @{#SPAWN.SpawnScheduleStart}(): Start or continue to spawn groups at scheduled time intervals. --- * @{#SPAWN.SpawnScheduleStart}(): Start or continue to spawn groups at scheduled time intervals.
-- * @{#SPAWN.SpawnScheduleStop}(): Stop the spawning of groups at scheduled time intervals. -- * @{#SPAWN.SpawnScheduleStop}(): Stop the spawning of groups at scheduled time intervals.
-- --
--
-- ## Retrieve alive GROUPs spawned by the SPAWN object -- ## Retrieve alive GROUPs spawned by the SPAWN object
-- --
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. -- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
@@ -233,14 +229,14 @@
-- --
-- ## Spawned cleaning of inactive groups -- ## Spawned cleaning of inactive groups
-- --
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. -- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damaged stop their activities, while remaining alive.
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, -- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
-- and it may occur that no new groups are or can be spawned as limits are reached. -- and it may occur that no new groups are or can be spawned as limits are reached.
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group. -- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. -- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... -- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. -- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
-- This models AI that has succesfully returned to their airbase, to restart their combat activities. -- This models AI that has successfully returned to their airbase, to restart their combat activities.
-- Check the @{#SPAWN.InitCleanUp}() for further info. -- Check the @{#SPAWN.InitCleanUp}() for further info.
-- --
-- ## Catch the @{Wrapper.Group} Spawn Event in a callback function! -- ## Catch the @{Wrapper.Group} Spawn Event in a callback function!
@@ -255,22 +251,19 @@
-- --
-- ## Delay the initial spawning -- ## Delay the initial spawning
-- --
-- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Wrapper.Group} -- When using the @{#SPAWN.SpawnScheduled)() method, the default behavior of this method will be that it will spawn the initial (first) @{Wrapper.Group}
-- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to -- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to
-- activate a delay before the first @{Wrapper.Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that -- activate a delay before the first @{Wrapper.Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that
-- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a -- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a
-- @{#SPAWN.SpawnScheduleStop}() ; @{#SPAWN.SpawnScheduleStart}() sequence would have been used. -- @{#SPAWN.SpawnScheduleStop}() ; @{#SPAWN.SpawnScheduleStart}() sequence would have been used.
-- --
--
-- @field #SPAWN SPAWN -- @field #SPAWN SPAWN
--
SPAWN = { SPAWN = {
ClassName = "SPAWN", ClassName = "SPAWN",
SpawnTemplatePrefix = nil, SpawnTemplatePrefix = nil,
SpawnAliasPrefix = nil, SpawnAliasPrefix = nil,
} }
--- Enumerator for spawns at airbases --- Enumerator for spawns at airbases
-- @type SPAWN.Takeoff -- @type SPAWN.Takeoff
-- @extends Wrapper.Group#GROUP.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff
@@ -286,7 +279,6 @@ SPAWN.Takeoff = {
--- @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.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
@@ -371,13 +363,13 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
self.AIOnOff = true -- The AI is on by default when spawning a group. self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group. self.DelayOnOff = false -- No initial delay when spawning the first group.
self.SpawnGrouping = nil -- No grouping. self.SpawnGrouping = nil -- No grouping.
self.SpawnInitLivery = nil -- No special livery. self.SpawnInitLivery = nil -- No special livery.
self.SpawnInitSkill = nil -- No special skill. self.SpawnInitSkill = nil -- No special skill.
self.SpawnInitFreq = nil -- No special frequency. self.SpawnInitFreq = nil -- No special frequency.
self.SpawnInitModu = nil -- No special modulation. self.SpawnInitModu = nil -- No special modulation.
self.SpawnInitRadio = nil -- No radio comms setting. self.SpawnInitRadio = nil -- No radio communication setting.
self.SpawnInitModex = nil self.SpawnInitModex = nil
self.SpawnInitAirbase = nil self.SpawnInitAirbase = nil
self.TweakedTemplate = false -- Check if the user is using self made template. self.TweakedTemplate = false -- Check if the user is using self made template.
@@ -393,7 +385,6 @@ 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.
-- @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!
@@ -404,8 +395,10 @@ end
-- -- Create a new SPAWN object based on a Group Template defined from scratch. -- -- 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' ) -- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
-- @usage -- @usage
--
-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier. -- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier.
-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" ) -- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" )
--
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix ) function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } )
@@ -432,13 +425,13 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
self.AIOnOff = true -- The AI is on by default when spawning a group. self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group. self.DelayOnOff = false -- No initial delay when spawning the first group.
self.Grouping = nil -- No grouping. self.Grouping = nil -- No grouping.
self.SpawnInitLivery = nil -- No special livery. self.SpawnInitLivery = nil -- No special livery.
self.SpawnInitSkill = nil -- No special skill. self.SpawnInitSkill = nil -- No special skill.
self.SpawnInitFreq = nil -- No special frequency. self.SpawnInitFreq = nil -- No special frequency.
self.SpawnInitModu = nil -- No special modulation. self.SpawnInitModu = nil -- No special modulation.
self.SpawnInitRadio = nil -- No radio comms setting. self.SpawnInitRadio = nil -- No radio communication setting.
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.
@@ -454,7 +447,6 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
return self return self
end end
--- Stops any more repeat spawns from happening once the UNIT count of Alive units, spawned by the same SPAWN object, exceeds the first parameter. Also can stop spawns from happening once a total GROUP still alive is met. --- Stops any more repeat spawns from happening once the UNIT count of Alive units, spawned by the same SPAWN object, exceeds the first parameter. Also can stop spawns from happening once a total GROUP still alive is met.
-- Exceptionally powerful when combined with SpawnSchedule for Respawning. -- Exceptionally powerful when combined with SpawnSchedule for Respawning.
-- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units. -- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units.
@@ -467,10 +459,12 @@ end
-- This parameter accepts the value 0, which defines that there are no maximum group limits, but there are limits on the maximum of units that can be alive at the same time. -- This parameter accepts the value 0, which defines that there are no maximum group limits, but there are limits on the maximum of units that can be alive at the same time.
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
--
-- -- NATO helicopters engaging in the battle field. -- -- NATO helicopters engaging in the battle field.
-- -- This helicopter group consists of one Unit. So, this group will SPAWN maximum 2 groups simultaneously within the DCSRTE. -- -- This helicopter group consists of one Unit. So, this group will SPAWN maximum 2 groups simultaneously within the DCSRTE.
-- -- There will be maximum 24 groups spawned during the whole mission lifetime. -- -- There will be maximum 24 groups spawned during the whole mission lifetime.
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitLimit( 2, 24 ) -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitLimit( 2, 24 )
--
function SPAWN:InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups ) function SPAWN:InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups )
self:F( { self.SpawnTemplatePrefix, SpawnMaxUnitsAlive, SpawnMaxGroups } ) self:F( { self.SpawnTemplatePrefix, SpawnMaxUnitsAlive, SpawnMaxGroups } )
@@ -500,7 +494,6 @@ function SPAWN:InitKeepUnitNames( KeepUnitNames )
return self return self
end end
--- Flags that the spawned groups must be spawned late activated. --- Flags that the spawned groups must be spawned late activated.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #boolean LateActivated (optional) If true, the spawned groups are late activated. -- @param #boolean LateActivated (optional) If true, the spawned groups are late activated.
@@ -517,7 +510,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string AirbaseName Name of the airbase. -- @param #string AirbaseName Name of the airbase.
-- @param #number Takeoff (Optional) Takeoff type. Can be SPAWN.Takeoff.Hot (default), SPAWN.Takeoff.Cold or SPAWN.Takeoff.Runway. -- @param #number Takeoff (Optional) Takeoff type. Can be SPAWN.Takeoff.Hot (default), SPAWN.Takeoff.Cold or SPAWN.Takeoff.Runway.
-- @param #number TerminalTyple (Optional) The terminal type. -- @param #number TerminalType (Optional) The terminal type.
-- @return #SPAWN self -- @return #SPAWN self
function SPAWN:InitAirbase( AirbaseName, Takeoff, TerminalType ) function SPAWN:InitAirbase( AirbaseName, Takeoff, TerminalType )
self:F() self:F()
@@ -531,7 +524,6 @@ function SPAWN:InitAirbase( AirbaseName, Takeoff, TerminalType )
return self return self
end end
--- Defines the Heading for the new spawned units. --- Defines the Heading for the new spawned units.
-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees. -- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees.
-- @param #SPAWN self -- @param #SPAWN self
@@ -557,7 +549,6 @@ function SPAWN:InitHeading( HeadingMin, HeadingMax )
return self return self
end end
--- Defines the heading of the overall formation of the new spawned group. --- Defines the heading of the overall formation of the new spawned group.
-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees. -- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees.
-- The Group's formation as laid out in its template will be rotated around the first unit in the group -- The Group's formation as laid out in its template will be rotated around the first unit in the group
@@ -575,12 +566,12 @@ end
-- -- Spawn the Group with the formation rotated +100 degrees around unit #1, compared to the mission template. -- -- Spawn the Group with the formation rotated +100 degrees around unit #1, compared to the mission template.
-- mySpawner:InitGroupHeading( 100 ) -- mySpawner:InitGroupHeading( 100 )
-- --
-- Spawn the Group with the formation rotated units between +100 and +150 degrees around unit #1, compared to the mission template, and with individual units varying by +/- 10 degrees from their templated facing. -- -- Spawn the Group with the formation rotated units between +100 and +150 degrees around unit #1, compared to the mission template, and with individual units varying by +/- 10 degrees from their templated facing.
-- mySpawner:InitGroupHeading( 100, 150, 10 ) -- mySpawner:InitGroupHeading( 100, 150, 10 )
-- --
-- Spawn the Group with the formation rotated -60 degrees around unit #1, compared to the mission template, but with all units facing due north regardless of how they were laid out in the template. -- -- Spawn the Group with the formation rotated -60 degrees around unit #1, compared to the mission template, but with all units facing due north regardless of how they were laid out in the template.
-- mySpawner:InitGroupHeading(-60):InitHeading(0) -- mySpawner:InitGroupHeading(-60):InitHeading(0)
-- or -- -- or
-- mySpawner:InitHeading(0):InitGroupHeading(-60) -- mySpawner:InitHeading(0):InitGroupHeading(-60)
-- --
function SPAWN:InitGroupHeading( HeadingMin, HeadingMax, unitVar ) function SPAWN:InitGroupHeading( HeadingMin, HeadingMax, unitVar )
@@ -592,13 +583,12 @@ function SPAWN:InitGroupHeading( HeadingMin, HeadingMax, unitVar )
return self return self
end end
--- Sets the coalition of the spawned group. Note that it might be necessary to also set the country explicitly! --- Sets the coalition of the spawned group. Note that it might be necessary to also set the country explicitly!
-- @param #SPAWN self -- @param #SPAWN self
-- @param DCS#coalition.side Coalition Coalition of the group as number of enumerator: -- @param DCS#coalition.side Coalition Coalition of the group as number of enumerator:
-- --
-- * @{DCS#coaliton.side.NEUTRAL} -- * @{DCS#coalition.side.NEUTRAL}
-- * @{DCS#coaliton.side.RED} -- * @{DCS#coalition.side.RED}
-- * @{DCS#coalition.side.BLUE} -- * @{DCS#coalition.side.BLUE}
-- --
-- @return #SPAWN self -- @return #SPAWN self
@@ -610,7 +600,7 @@ function SPAWN:InitCoalition( Coalition )
return self return self
end end
--- Sets the country of the spawn group. Note that the country determins the coalition of the group depending on which country is defined to be on which side for each specific mission! --- Sets the country of the spawn group. Note that the country determines the coalition of the group depending on which country is defined to be on which side for each specific mission!
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number Country Country id as number or enumerator: -- @param #number Country Country id as number or enumerator:
-- --
@@ -626,7 +616,6 @@ function SPAWN:InitCountry( Country )
return self return self
end end
--- Sets category ID of the group. --- Sets category ID of the group.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number Category Category id. -- @param #number Category Category id.
@@ -641,7 +630,7 @@ end
--- Sets livery of the group. --- Sets livery of the group.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string Livery Livery name. Note that this is not necessarily the same name as displayed in the mission edior. -- @param #string Livery Livery name. Note that this is not necessarily the same name as displayed in the mission editor.
-- @return #SPAWN self -- @return #SPAWN self
function SPAWN:InitLivery( Livery ) function SPAWN:InitLivery( Livery )
self:F( { livery = Livery } ) self:F( { livery = Livery } )
@@ -672,9 +661,9 @@ function SPAWN:InitSkill( Skill )
return self return self
end end
--- Sets the radio comms on or off. Same as checking/unchecking the COMM box in the mission editor. --- Sets the radio communication on or off. Same as checking/unchecking the COMM box in the mission editor.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number switch If true (or nil), enables the radio comms. If false, disables the radio for the spawned group. -- @param #number switch If true (or nil), enables the radio communication. If false, disables the radio for the spawned group.
-- @return #SPAWN self -- @return #SPAWN self
function SPAWN:InitRadioCommsOnOff( switch ) function SPAWN:InitRadioCommsOnOff( switch )
self:F( { switch = switch } ) self:F( { switch = switch } )
@@ -721,8 +710,7 @@ function SPAWN:InitModex(modex)
return self return self
end end
--- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behavior of groups.
--- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number SpawnStartPoint is the waypoint where the randomization begins. -- @param #number SpawnStartPoint is the waypoint where the randomization begins.
-- Note that the StartPoint = 0 equaling the point where the group is spawned. -- Note that the StartPoint = 0 equaling the point where the group is spawned.
@@ -732,11 +720,13 @@ end
-- @param #number SpawnHeight (optional) Specifies the **additional** height in meters that can be added to the base height specified at each waypoint in the ME. -- @param #number SpawnHeight (optional) Specifies the **additional** height in meters that can be added to the base height specified at each waypoint in the ME.
-- @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). -- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP).
-- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. -- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter.
-- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. -- -- 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 ) -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 )
--
function SPAWN:InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) function SPAWN:InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight )
self:F( { self.SpawnTemplatePrefix, SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight } ) self:F( { self.SpawnTemplatePrefix, SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight } )
@@ -773,7 +763,6 @@ function SPAWN:InitRandomizePosition( RandomizePosition, OuterRadius, InnerRadiu
return self return self
end 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 @{UNIT}s position within the group between a given outer and inner radius.
@@ -781,11 +770,13 @@ end
-- @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). -- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP).
-- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. -- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter.
-- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. -- -- 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 ) -- 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 } )
@@ -805,14 +796,15 @@ end
-- 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.
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group. -- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #string SpawnTemplatePrefixTable A table with the names of the groups defined within the mission editor, from which one will be choosen when a new group will be spawned. -- @param #string SpawnTemplatePrefixTable A table with the names of the groups defined within the mission editor, from which one will be chosen when a new group will be spawned.
-- @return #SPAWN -- @return #SPAWN
-- @usage -- @usage
--
-- -- NATO Tank Platoons invading Gori. -- -- NATO Tank Platoons invading Gori.
-- -- Choose between 13 different 'US Tank Platoon' configurations for each new SPAWN the Group to be spawned for the -- -- Choose between 13 different 'US Tank Platoon' configurations for each new SPAWN the Group to be spawned for the
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SpawnTemplatePrefixes. -- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SpawnTemplatePrefixes.
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and -- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission. -- -- with a limit set of maximum 12 Units alive simultaneously and 150 Groups to be spawned during the whole mission.
-- Spawn_US_Platoon = { 'US Tank Platoon 1', 'US Tank Platoon 2', 'US Tank Platoon 3', 'US Tank Platoon 4', 'US Tank Platoon 5', -- Spawn_US_Platoon = { 'US Tank Platoon 1', 'US Tank Platoon 2', 'US Tank Platoon 3', 'US Tank Platoon 4', 'US Tank Platoon 5',
-- 'US Tank Platoon 6', 'US Tank Platoon 7', 'US Tank Platoon 8', 'US Tank Platoon 9', 'US Tank Platoon 10', -- 'US Tank Platoon 6', 'US Tank Platoon 7', 'US Tank Platoon 8', 'US Tank Platoon 9', 'US Tank Platoon 10',
-- 'US Tank Platoon 11', 'US Tank Platoon 12', 'US Tank Platoon 13' } -- 'US Tank Platoon 11', 'US Tank Platoon 12', 'US Tank Platoon 13' }
@@ -832,7 +824,6 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
return self return self
end end
--- Randomize templates to be used as the unit representatives for the Spawned group, defined using a SET_GROUP object. --- Randomize templates to be used as the unit representatives for the Spawned group, defined using a SET_GROUP object.
-- 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.
@@ -841,20 +832,22 @@ end
-- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned. -- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned.
-- @return #SPAWN -- @return #SPAWN
-- @usage -- @usage
--
-- -- NATO Tank Platoons invading Gori. -- -- NATO Tank Platoons invading Gori.
-- --
-- -- Choose between different 'US Tank Platoon Template' configurations to be spawned for the -- -- Choose between different 'US Tank Platoon Template' configurations to be spawned for the
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects. -- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
-- --
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and -- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission. -- -- with a limit set of maximum 12 Units alive simultaneously and 150 Groups to be spawned during the whole mission.
-- --
-- Spawn_US_PlatoonSet = SET_GROUP:New():FilterPrefixes( "US Tank Platoon Templates" ):FilterOnce() -- Spawn_US_PlatoonSet = SET_GROUP:New():FilterPrefixes( "US Tank Platoon Templates" ):FilterOnce()
-- --
-- --- Now use the Spawn_US_PlatoonSet to define the templates using InitRandomizeTemplateSet. -- -- Now use the Spawn_US_PlatoonSet to define the templates using InitRandomizeTemplateSet.
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):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_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 ) -- R2.3
self:F( { self.SpawnTemplatePrefix } ) self:F( { self.SpawnTemplatePrefix } )
@@ -868,7 +861,6 @@ function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3
return self return self
end end
--- Randomize templates to be used as the unit representatives for the Spawned group, defined by specifying the prefix names. --- Randomize templates to be used as the unit representatives for the Spawned group, defined by specifying the prefix names.
-- 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.
@@ -877,17 +869,19 @@ end
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned. -- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
-- @return #SPAWN -- @return #SPAWN
-- @usage -- @usage
--
-- -- NATO Tank Platoons invading Gori. -- -- NATO Tank Platoons invading Gori.
-- --
-- -- Choose between different 'US Tank Platoon Templates' configurations to be spawned for the -- -- Choose between different 'US Tank Platoon Templates' configurations to be spawned for the
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects. -- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
-- --
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and -- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission. -- -- with a limit set of maximum 12 Units alive simultaneously and 150 Groups to be spawned during the whole mission.
-- --
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
--
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) -- R2.3 function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) -- R2.3
self:F( { self.SpawnTemplatePrefix } ) self:F( { self.SpawnTemplatePrefix } )
@@ -898,7 +892,6 @@ function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) --R2.3
return self return self
end end
--- When spawning a new group, make the grouping of the units according the InitGrouping setting. --- When spawning a new group, make the grouping of the units according the InitGrouping setting.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number Grouping Indicates the maximum amount of units in the group. -- @param #number Grouping Indicates the maximum amount of units in the group.
@@ -911,13 +904,12 @@ function SPAWN:InitGrouping( Grouping ) -- R2.2
return self return self
end 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 @{Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Zone}s objects.
-- @return #SPAWN -- @return #SPAWN
-- @usage -- @usage
--
-- -- Create a zone table of the 2 zones. -- -- Create a zone table of the 2 zones.
-- ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } -- ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) }
-- --
@@ -940,11 +932,7 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable )
return self return self
end 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...
-- Note: When the group is respawned, it will re-spawn from the original airbase where it took off. -- Note: When the group is respawned, it will re-spawn from the original airbase where it took off.
@@ -952,10 +940,10 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
--
-- -- RU Su-34 - AI Ship Attack -- -- RU Su-34 - AI Ship Attack
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically. -- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
-- SpawnRU_SU34 = SPAWN -- SpawnRU_SU34 = SPAWN:New( 'Su-34' )
-- :New( 'Su-34' )
-- :Schedule( 2, 3, 1800, 0.4 ) -- :Schedule( 2, 3, 1800, 0.4 )
-- :SpawnUncontrolled() -- :SpawnUncontrolled()
-- :InitRandomizeRoute( 1, 1, 3000 ) -- :InitRandomizeRoute( 1, 1, 3000 )
@@ -975,13 +963,14 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
--
-- -- RU Su-34 - AI Ship Attack -- -- RU Su-34 - AI Ship Attack
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically. -- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
-- SpawnRU_SU34 = SPAWN -- SpawnRU_SU34 = SPAWN:New( 'Su-34' )
-- :New( 'Su-34' )
-- :InitRandomizeRoute( 1, 1, 3000 ) -- :InitRandomizeRoute( 1, 1, 3000 )
-- :InitRepeatOnLanding() -- :InitRepeatOnLanding()
-- :Spawn() -- :Spawn()
--
function SPAWN:InitRepeatOnLanding() function SPAWN:InitRepeatOnLanding()
self:F( { self.SpawnTemplatePrefix } ) self:F( { self.SpawnTemplatePrefix } )
@@ -992,15 +981,14 @@ function SPAWN:InitRepeatOnLanding()
return self return self
end end
--- Respawn after landing when its engines have shut down. --- Respawn after landing when its engines have shut down.
-- @param #SPAWN self -- @param #SPAWN self
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
--
-- -- RU Su-34 - AI Ship Attack -- -- RU Su-34 - AI Ship Attack
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically. -- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
-- SpawnRU_SU34 = SPAWN -- SpawnRU_SU34 = SPAWN:New( 'Su-34' )
-- :New( 'Su-34' )
-- :SpawnUncontrolled() -- :SpawnUncontrolled()
-- :InitRandomizeRoute( 1, 1, 3000 ) -- :InitRandomizeRoute( 1, 1, 3000 )
-- :InitRepeatOnEngineShutDown() -- :InitRepeatOnEngineShutDown()
@@ -1015,7 +1003,6 @@ function SPAWN:InitRepeatOnEngineShutDown()
return self return self
end end
--- Delete groups that have not moved for X seconds - AIR ONLY!!! --- Delete groups that have not moved for X seconds - AIR ONLY!!!
-- DO NOT USE ON GROUPS THAT DO NOT MOVE OR YOUR SERVER WILL BURN IN HELL (Pikes - April 2020) -- DO NOT USE ON GROUPS THAT DO NOT MOVE OR YOUR SERVER WILL BURN IN HELL (Pikes - April 2020)
-- When groups are still alive and have become inactive due to damage and are unable to contribute anything, then this group will be removed at defined intervals in seconds. -- When groups are still alive and have become inactive due to damage and are unable to contribute anything, then this group will be removed at defined intervals in seconds.
@@ -1023,7 +1010,9 @@ end
-- @param #string SpawnCleanUpInterval The interval to check for inactive groups within seconds. -- @param #string SpawnCleanUpInterval The interval to check for inactive groups within seconds.
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
--
-- Spawn_Helicopter:InitCleanUp( 20 ) -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive. -- Spawn_Helicopter:InitCleanUp( 20 ) -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive.
--
function SPAWN:InitCleanUp( SpawnCleanUpInterval ) function SPAWN:InitCleanUp( SpawnCleanUpInterval )
self:F( { self.SpawnTemplatePrefix, SpawnCleanUpInterval } ) self:F( { self.SpawnTemplatePrefix, SpawnCleanUpInterval } )
@@ -1038,9 +1027,7 @@ function SPAWN:InitCleanUp( SpawnCleanUpInterval )
return self return self
end end
--- Makes the groups visible before start (like a battalion).
--- Makes the groups visible before start (like a batallion).
-- The method will take the position of the group as the first position in the array. -- The method will take the position of the group as the first position in the array.
-- CAUTION: this directive will NOT work with OnSpawnGroup function. -- CAUTION: this directive will NOT work with OnSpawnGroup function.
-- @param #SPAWN self -- @param #SPAWN self
@@ -1050,9 +1037,9 @@ end
-- @param #number SpawnDeltaY The space between each Group on the Y-axis. -- @param #number SpawnDeltaY The space between each Group on the Y-axis.
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
--
-- -- Define an array of Groups. -- -- Define an array of Groups.
-- Spawn_BE_Ground = SPAWN -- Spawn_BE_Ground = SPAWN:New( 'BE Ground' )
-- :New( 'BE Ground' )
-- :InitLimit( 2, 24 ) -- :InitLimit( 2, 24 )
-- :InitArray( 90, 10, 100, 50 ) -- :InitArray( 90, 10, 100, 50 )
-- --
@@ -1112,6 +1099,7 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY )
end end
do -- AI methods do -- AI methods
--- Turns the AI On or Off for the @{Wrapper.Group} when spawning. --- Turns the AI On or Off for the @{Wrapper.Group} when spawning.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #boolean AIOnOff A value of true sets the AI On, a value of false sets the AI Off. -- @param #boolean AIOnOff A value of true sets the AI On, a value of false sets the AI Off.
@@ -1223,7 +1211,6 @@ function SPAWN:ReSpawn( SpawnIndex )
return SpawnGroup return SpawnGroup
end end
--- Set the spawn index to a specified index number. --- Set the spawn index to a specified index number.
-- This method can be used to "reset" the spawn counter to a specific index number. -- This method can be used to "reset" the spawn counter to a specific index number.
-- This will actually enable a respawn of groups from the specific index. -- This will actually enable a respawn of groups from the specific index.
@@ -1234,7 +1221,6 @@ function SPAWN:SetSpawnIndex( SpawnIndex )
self.SpawnIndex = SpawnIndex or 0 self.SpawnIndex = SpawnIndex or 0
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. -- Uses @{DATABASE} global object defined in MOOSE.
-- @param #SPAWN self -- @param #SPAWN self
@@ -1387,12 +1373,11 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
SpawnTemplate.modulation = self.SpawnInitModu SpawnTemplate.modulation = self.SpawnInitModu
end end
-- Set country, coaliton and categroy. -- Set country, coalition and category.
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID
-- if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then -- if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then
-- if SpawnTemplate.route.points[1].type == "TakeOffParking" then -- if SpawnTemplate.route.points[1].type == "TakeOffParking" then
-- SpawnTemplate.uncontrolled = self.SpawnUnControlled -- SpawnTemplate.uncontrolled = self.SpawnUnControlled
@@ -1437,7 +1422,6 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
-- end -- end
end end
self.SpawnGroups[self.SpawnIndex].Spawned = true self.SpawnGroups[self.SpawnIndex].Spawned = true
return self.SpawnGroups[self.SpawnIndex].Group return self.SpawnGroups[self.SpawnIndex].Group
else else
@@ -1452,7 +1436,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @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 %-tage 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.
-- @return #SPAWN self -- @return #SPAWN self
-- @usage -- @usage
-- -- NATO helicopters engaging in the battle field. -- -- NATO helicopters engaging in the battle field.
@@ -1461,7 +1445,7 @@ end
-- -- This is calculated as follows: -- -- This is calculated as follows:
-- -- Low limit: 600 * ( 1 - 0.5 / 2 ) = 450 -- -- Low limit: 600 * ( 1 - 0.5 / 2 ) = 450
-- -- 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 choosen 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 )
self:F( { SpawnTime, SpawnTimeVariation } ) self:F( { SpawnTime, SpawnTimeVariation } )
@@ -1498,7 +1482,6 @@ function SPAWN:SpawnScheduleStop()
return self return self
end end
--- Allows to place a CallFunction hook when a new group spawns. --- Allows to place a CallFunction hook when a new group spawns.
-- The provided method will be called when a new group is spawned, including its given parameters. -- The provided method will be called when a new group is spawned, including its given parameters.
-- The first parameter of the SpawnFunction is the @{Wrapper.Group#GROUP} that was spawned. -- The first parameter of the SpawnFunction is the @{Wrapper.Group#GROUP} that was spawned.
@@ -1507,12 +1490,11 @@ end
-- @param SpawnFunctionArguments A random amount of arguments to be provided to the function when the group spawns. -- @param SpawnFunctionArguments A random amount of arguments to be provided to the function when the group spawns.
-- @return #SPAWN -- @return #SPAWN
-- @usage -- @usage
--
-- -- Declare SpawnObject and call a function when a new Group is spawned. -- -- Declare SpawnObject and call a function when a new Group is spawned.
-- local SpawnObject = SPAWN -- local SpawnObject = SPAWN:New( "SpawnObject" )
-- :New( "SpawnObject" )
-- :InitLimit( 2, 10 ) -- :InitLimit( 2, 10 )
-- :OnSpawnGroup( -- :OnSpawnGroup( function( SpawnGroup )
-- function( SpawnGroup )
-- SpawnGroup:E( "I am spawned" ) -- SpawnGroup:E( "I am spawned" )
-- end -- end
-- ) -- )
@@ -1546,7 +1528,7 @@ end
-- The known AIRBASE objects are automatically imported at mission start by MOOSE. -- The known AIRBASE objects are automatically imported at mission start by MOOSE.
-- Therefore, there isn't any New() constructor defined for AIRBASE objects. -- Therefore, there isn't any New() constructor defined for AIRBASE objects.
-- --
-- Ships and Farps are added within the mission, and are therefore not known. -- Ships and FARPs are added within the mission, and are therefore not known.
-- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined. -- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined.
-- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method! -- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method!
-- --
@@ -1556,9 +1538,10 @@ end
-- @param #number TakeoffAltitude (optional) The altitude above the ground. -- @param #number TakeoffAltitude (optional) The altitude above the ground.
-- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}. -- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}.
-- @param #boolean EmergencyAirSpawn (optional) If true (default), groups are spawned in air if there is no parking spot at the airbase. If false, nothing is spawned if no parking spot is available. -- @param #boolean EmergencyAirSpawn (optional) If true (default), groups are spawned in air if there is no parking spot at the airbase. If false, nothing is spawned if no parking spot is available.
-- @param #table Parkingdata (optional) Table holding the coordinates and terminal ids for all units of the group. Spawning will be forced to happen at exactily these spots! -- @param #table Parkingdata (optional) Table holding the coordinates and terminal ids for all units of the group. Spawning will be forced to happen at exactly these spots!
-- @return Wrapper.Group#GROUP The group that was spawned or nil when nothing was spawned. -- @return Wrapper.Group#GROUP The group that was spawned or nil when nothing was spawned.
-- @usage -- @usage
--
-- Spawn_Plane = SPAWN:New( "Plane" ) -- Spawn_Plane = SPAWN:New( "Plane" )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold ) -- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Hot ) -- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Hot )
@@ -1686,7 +1669,6 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
-- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship.
if spawnonground and not SpawnTemplate.parked then if spawnonground and not SpawnTemplate.parked then
-- Number of free parking spots. -- Number of free parking spots.
local nfree = 0 local nfree = 0
@@ -2021,7 +2003,6 @@ function SPAWN:SpawnAtParkingSpot(Airbase, Spots, Takeoff) -- R2.5
self:E( "ERROR: Could not find enough free parking spots!" ) self:E( "ERROR: Could not find enough free parking spots!" )
end end
else else
self:E( "ERROR: Could not get number of units in group!" ) self:E( "ERROR: Could not get number of units in group!" )
end end
@@ -2128,7 +2109,6 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
-- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship.
if spawnonground and not SpawnTemplate.parked then if spawnonground and not SpawnTemplate.parked then
-- Number of free parking spots. -- Number of free parking spots.
local nfree = 0 local nfree = 0
@@ -2380,7 +2360,7 @@ end
-- The known AIRBASE objects are automatically imported at mission start by MOOSE. -- The known AIRBASE objects are automatically imported at mission start by MOOSE.
-- Therefore, there isn't any New() constructor defined for AIRBASE objects. -- Therefore, there isn't any New() constructor defined for AIRBASE objects.
-- --
-- Ships and Farps are added within the mission, and are therefore not known. -- Ships and FARPs are added within the mission, and are therefore not known.
-- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined. -- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined.
-- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method! -- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method!
-- --
@@ -2485,7 +2465,6 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
return nil return nil
end end
--- Will spawn a group from a Coordinate in 3D space. --- Will spawn a group from a Coordinate in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. -- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
@@ -2501,8 +2480,6 @@ function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex )
return self:SpawnFromVec3( Coordinate:GetVec3(), SpawnIndex ) return self:SpawnFromVec3( Coordinate:GetVec3(), SpawnIndex )
end end
--- Will spawn a group from a PointVec3 in 3D space. --- Will spawn a group from a PointVec3 in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. -- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
@@ -2525,7 +2502,6 @@ function SPAWN:SpawnFromPointVec3( PointVec3, SpawnIndex )
return self:SpawnFromVec3( PointVec3:GetVec3(), SpawnIndex ) return self:SpawnFromVec3( PointVec3:GetVec3(), SpawnIndex )
end end
--- Will spawn a group from a Vec2 in 3D space. --- Will spawn a group from a Vec2 in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. -- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
@@ -2559,7 +2535,6 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air. return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
end end
--- Will spawn a group from a POINT_VEC2 in 3D space. --- Will spawn a group from a POINT_VEC2 in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. -- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
@@ -2587,8 +2562,6 @@ function SPAWN:SpawnFromPointVec2( PointVec2, MinHeight, MaxHeight, SpawnIndex )
return self:SpawnFromVec2( PointVec2:GetVec2(), MinHeight, MaxHeight, SpawnIndex ) return self:SpawnFromVec2( PointVec2:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
end end
--- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone. --- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
-- You can use the returned group to further define the route to be followed. -- You can use the returned group to further define the route to be followed.
@@ -2712,7 +2685,6 @@ function SPAWN:InitUnControlled( UnControlled )
return self return self
end end
--- Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object. --- Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object.
-- @param #SPAWN self -- @param #SPAWN self
-- @return Core.Point#COORDINATE The Coordinate -- @return Core.Point#COORDINATE The Coordinate
@@ -2726,7 +2698,6 @@ function SPAWN:GetCoordinate()
return nil return nil
end end
--- Will return the SpawnGroupName either with with a specific count number or without any count. --- Will return the SpawnGroupName either with with a specific count number or without any count.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number SpawnIndex Is the number of the Group that is to be spawned. -- @param #number SpawnIndex Is the number of the Group that is to be spawned.
@@ -2755,12 +2726,14 @@ end
-- @return Wrapper.Group#GROUP, #number The @{Wrapper.Group} object found, the new Index where the group was found. -- @return Wrapper.Group#GROUP, #number The @{Wrapper.Group} object found, the new Index where the group was found.
-- @return #nil, #nil When no group is found, #nil is returned. -- @return #nil, #nil When no group is found, #nil is returned.
-- @usage -- @usage
--
-- -- Find the first alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission. -- -- Find the first alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission.
-- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() -- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup()
-- while GroupPlane ~= nil do -- while GroupPlane ~= nil do
-- -- Do actions with the GroupPlane object. -- -- Do actions with the GroupPlane object.
-- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index ) -- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index )
-- end -- end
--
function SPAWN:GetFirstAliveGroup() function SPAWN:GetFirstAliveGroup()
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } )
@@ -2774,19 +2747,20 @@ function SPAWN:GetFirstAliveGroup()
return nil, nil return nil, nil
end end
--- Will find the next alive @{Wrapper.Group} object from a given Index, and return a reference to the alive @{Wrapper.Group} object and the next Index where the alive @{Wrapper.Group} has been found. --- Will find the next alive @{Wrapper.Group} object from a given Index, and return a reference to the alive @{Wrapper.Group} object and the next Index where the alive @{Wrapper.Group} has been found.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number SpawnIndexStart A Index holding the start position to search from. This method can also be used to find the first alive @{Wrapper.Group} object from the given Index. -- @param #number SpawnIndexStart A Index holding the start position to search from. This method can also be used to find the first alive @{Wrapper.Group} object from the given Index.
-- @return Wrapper.Group#GROUP, #number The next alive @{Wrapper.Group} object found, the next Index where the next alive @{Wrapper.Group} object was found. -- @return Wrapper.Group#GROUP, #number The next alive @{Wrapper.Group} object found, the next Index where the next alive @{Wrapper.Group} object was found.
-- @return #nil, #nil When no alive @{Wrapper.Group} object is found from the start Index position, #nil is returned. -- @return #nil, #nil When no alive @{Wrapper.Group} object is found from the start Index position, #nil is returned.
-- @usage -- @usage
--
-- -- Find the first alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission. -- -- Find the first alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission.
-- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() -- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup()
-- while GroupPlane ~= nil do -- while GroupPlane ~= nil do
-- -- Do actions with the GroupPlane object. -- -- Do actions with the GroupPlane object.
-- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index ) -- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index )
-- end -- end
--
function SPAWN:GetNextAliveGroup( SpawnIndexStart ) function SPAWN:GetNextAliveGroup( SpawnIndexStart )
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndexStart } ) self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndexStart } )
@@ -2806,11 +2780,13 @@ end
-- @return Wrapper.Group#GROUP, #number The last alive @{Wrapper.Group} object found, the last Index where the last alive @{Wrapper.Group} object was found. -- @return Wrapper.Group#GROUP, #number The last alive @{Wrapper.Group} object found, the last Index where the last alive @{Wrapper.Group} object was found.
-- @return #nil, #nil When no alive @{Wrapper.Group} object is found, #nil is returned. -- @return #nil, #nil When no alive @{Wrapper.Group} object is found, #nil is returned.
-- @usage -- @usage
--
-- -- Find the last alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission. -- -- Find the last alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission.
-- local GroupPlane, Index = SpawnPlanes:GetLastAliveGroup() -- local GroupPlane, Index = SpawnPlanes:GetLastAliveGroup()
-- if GroupPlane then -- GroupPlane can be nil!!! -- if GroupPlane then -- GroupPlane can be nil!!!
-- -- Do actions with the GroupPlane object. -- -- Do actions with the GroupPlane object.
-- end -- end
--
function SPAWN:GetLastAliveGroup() function SPAWN:GetLastAliveGroup()
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } )
@@ -2826,8 +2802,6 @@ function SPAWN:GetLastAliveGroup()
return nil return nil
end end
--- Get the group from an index. --- Get the group from an index.
-- Returns the group from the SpawnGroups list. -- Returns the group from the SpawnGroups list.
-- If no index is given, it will return the first group in the list. -- If no index is given, it will return the first group in the list.
@@ -2849,29 +2823,46 @@ function SPAWN:GetGroupFromIndex( SpawnIndex )
end end
end end
--- Return the prefix of a SpawnUnit. --- Return the prefix of a SpawnUnit.
-- 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, ".*#" )
if SpawnPrefix then local SpawnPrefix=self:_GetPrefixFromGroupName(GroupName)
SpawnPrefix = SpawnPrefix:sub( 1, -2 )
end
return SpawnPrefix return SpawnPrefix
end end
return nil return nil
end 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
SpawnPrefix = SpawnPrefix:sub(1, -2)
end
return SpawnPrefix
end
return nil
end
--- Get the index from a given group. --- Get the index from a given group.
-- The function will search the name of the group for a #, and will return the number behind the #-mark. -- The function will search the name of the group for a #, and will return the number behind the #-mark.
@@ -2916,8 +2907,6 @@ function SPAWN:_InitializeSpawnGroups( SpawnIndex )
return self.SpawnGroups[SpawnIndex] return self.SpawnGroups[SpawnIndex]
end end
--- Gets the CategoryID of the Group with the given SpawnPrefix --- Gets the CategoryID of the Group with the given SpawnPrefix
function SPAWN:_GetGroupCategoryID( SpawnPrefix ) function SPAWN:_GetGroupCategoryID( SpawnPrefix )
local TemplateGroup = Group.getByName( SpawnPrefix ) local TemplateGroup = Group.getByName( SpawnPrefix )
@@ -3002,7 +2991,6 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
end end
SpawnTemplate.groupId = nil SpawnTemplate.groupId = nil
-- SpawnTemplate.lateActivation = false -- SpawnTemplate.lateActivation = false
SpawnTemplate.lateActivation = self.LateActivated or false SpawnTemplate.lateActivation = self.LateActivated or false
@@ -3189,16 +3177,13 @@ function SPAWN:_TranslateRotate( SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, Spa
-- From Wikipedia: https://en.wikipedia.org/wiki/Rotation_matrix#Common_rotations -- From Wikipedia: https://en.wikipedia.org/wiki/Rotation_matrix#Common_rotations
-- x' = x \cos \theta - y \sin \theta\ -- x' = x \cos \theta - y \sin \theta\
-- y' = x \sin \theta + y \cos \theta\ -- y' = x \sin \theta + y \cos \theta\
local RotatedX = - TranslatedX * math.cos( math.rad( SpawnAngle ) ) local RotatedX = -TranslatedX * math.cos( math.rad( SpawnAngle ) ) + TranslatedY * math.sin( math.rad( SpawnAngle ) )
+ TranslatedY * math.sin( math.rad( SpawnAngle ) ) local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) ) + TranslatedY * math.cos( math.rad( SpawnAngle ) )
local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) )
+ TranslatedY * math.cos( math.rad( SpawnAngle ) )
-- Assign -- Assign
self.SpawnGroups[SpawnIndex].SpawnTemplate.x = SpawnRootX - RotatedX self.SpawnGroups[SpawnIndex].SpawnTemplate.x = SpawnRootX - RotatedX
self.SpawnGroups[SpawnIndex].SpawnTemplate.y = SpawnRootY + RotatedY self.SpawnGroups[SpawnIndex].SpawnTemplate.y = SpawnRootY + RotatedY
local SpawnUnitCount = table.getn( self.SpawnGroups[SpawnIndex].SpawnTemplate.units ) local SpawnUnitCount = table.getn( self.SpawnGroups[SpawnIndex].SpawnTemplate.units )
for u = 1, SpawnUnitCount do for u = 1, SpawnUnitCount do
@@ -3207,10 +3192,8 @@ function SPAWN:_TranslateRotate( SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, Spa
local TranslatedY = SpawnY - 10 * (u - 1) local TranslatedY = SpawnY - 10 * (u - 1)
-- Rotate -- Rotate
local RotatedX = - TranslatedX * math.cos( math.rad( SpawnAngle ) ) local RotatedX = -TranslatedX * math.cos( math.rad( SpawnAngle ) ) + TranslatedY * math.sin( math.rad( SpawnAngle ) )
+ TranslatedY * math.sin( math.rad( SpawnAngle ) ) local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) ) + TranslatedY * math.cos( math.rad( SpawnAngle ) )
local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) )
+ TranslatedY * math.cos( math.rad( SpawnAngle ) )
-- Assign -- Assign
self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].x = SpawnRootX - RotatedX self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].x = SpawnRootX - RotatedX
@@ -3249,7 +3232,6 @@ function SPAWN:_GetSpawnIndex( SpawnIndex )
return self.SpawnIndex return self.SpawnIndex
end 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
@@ -3272,24 +3254,27 @@ function SPAWN:_OnBirth( EventData )
end end
--- Obscolete
-- @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:_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
@@ -3400,7 +3385,6 @@ function SPAWN:_SpawnCleanUpScheduler()
local SpawnUnit = UnitData -- Wrapper.Unit#UNIT local SpawnUnit = UnitData -- Wrapper.Unit#UNIT
local SpawnUnitName = SpawnUnit:GetName() local SpawnUnitName = SpawnUnit:GetName()
self.SpawnCleanUpTimeStamps[SpawnUnitName] = self.SpawnCleanUpTimeStamps[SpawnUnitName] or {} self.SpawnCleanUpTimeStamps[SpawnUnitName] = self.SpawnCleanUpTimeStamps[SpawnUnitName] or {}
local Stamp = self.SpawnCleanUpTimeStamps[SpawnUnitName] local Stamp = self.SpawnCleanUpTimeStamps[SpawnUnitName]
self:T( { SpawnUnitName, Stamp } ) self:T( { SpawnUnitName, Stamp } )

View File

@@ -46,14 +46,14 @@
-- @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
@@ -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 IsCargo If true, this static is dead.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitDead(IsDead)
self.InitStaticDead=IsDead
return self return self
end end
@@ -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
@@ -479,6 +488,8 @@ 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)
else else
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template) Static=coalition.addStaticObject(CountryID, Template)
end end

View File

@@ -9,7 +9,7 @@
-- * Create polygon zones. -- * Create polygon zones.
-- * Create moving zones around a unit. -- * Create moving zones around a unit.
-- * Create moving zones around a group. -- * Create moving zones around a group.
-- * Provide the zone behaviour. Some zones are static, while others are moveable. -- * Provide the zone behavior. Some zones are static, while others are moveable.
-- * Enquiry if a coordinate is within a zone. -- * Enquiry if a coordinate is within a zone.
-- * Smoke zones. -- * Smoke zones.
-- * Set a zone probability to control zone selection. -- * Set a zone probability to control zone selection.
@@ -20,10 +20,10 @@
-- * Draw zones (circular and polygon) on the F10 map. -- * Draw zones (circular and polygon) on the F10 map.
-- --
-- --
-- There are essentially two core functions that zones accomodate: -- There are essentially two core functions that zones accommodate:
-- --
-- * Test if an object is within the zone boundaries. -- * Test if an object is within the zone boundaries.
-- * Provide the zone behaviour. Some zones are static, while others are moveable. -- * Provide the zone behavior. Some zones are static, while others are moveable.
-- --
-- The object classes are using the zone classes to test the zone boundaries, which can take various forms: -- The object classes are using the zone classes to test the zone boundaries, which can take various forms:
-- --
@@ -59,6 +59,7 @@
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
-- @field #number DrawID Unique ID of the drawn zone on the F10 map. -- @field #number DrawID Unique ID of the drawn zone on the F10 map.
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. -- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
-- @field #number ZoneID ID of zone. Only zones defined in the ME have an ID!
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@@ -108,7 +109,8 @@ ZONE_BASE = {
ZoneName = "", ZoneName = "",
ZoneProbability = 1, ZoneProbability = 1,
DrawID=nil, DrawID=nil,
Color={} Color={},
ZoneID=nil,
} }
@@ -130,6 +132,8 @@ function ZONE_BASE:New( ZoneName )
self.ZoneName = ZoneName self.ZoneName = ZoneName
--_DATABASE:AddZone(ZoneName,self)
return self return self
end end
@@ -170,6 +174,7 @@ end
-- @param DCS#Vec3 Vec3 The point to test. -- @param DCS#Vec3 Vec3 The point to test.
-- @return #boolean true if the Vec3 is within the zone. -- @return #boolean true if the Vec3 is within the zone.
function ZONE_BASE:IsVec3InZone( Vec3 ) function ZONE_BASE:IsVec3InZone( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone
end end
@@ -288,6 +293,23 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
return self.Coordinate return self.Coordinate
end end
--- Get 2D distance to a coordinate.
-- @param #ZONE_BASE self
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
-- @return #number Distance to the reference coordinate in meters.
function ZONE_BASE:Get2DDistance(Coordinate)
local a=self:GetVec2()
local b={}
if Coordinate.z then
b.x=Coordinate.x
b.y=Coordinate.z
else
b.x=Coordinate.x
b.y=Coordinate.y
end
local dist=UTILS.VecDist2D(a,b)
return dist
end
--- Define a random @{DCS#Vec2} within the zone. --- Define a random @{DCS#Vec2} within the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
@@ -601,7 +623,7 @@ function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, Lin
Color=Color or self:GetColorRGB() Color=Color or self:GetColorRGB()
Alpha=Alpha or 1 Alpha=Alpha or 1
FillColor=FillColor or Color FillColor=FillColor or UTILS.DeepCopy(Color)
FillAlpha=FillAlpha or self:GetColorAlpha() FillAlpha=FillAlpha or self:GetColorAlpha()
self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
@@ -789,7 +811,7 @@ end
--- Scan the zone for the presence of units of the given ObjectCategories. --- Scan the zone for the presence of units of the given ObjectCategories.
-- Note that after a zone has been scanned, the zone can be evaluated by: -- Note that **only after** a zone has been scanned, the zone can be evaluated by:
-- --
-- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition. -- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition.
-- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition. -- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition.
@@ -798,10 +820,10 @@ end
-- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty. -- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty.
-- @{#ZONE_RADIUS. -- @{#ZONE_RADIUS.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param ObjectCategories An array of categories of the objects to find in the zone. -- @param ObjectCategories An array of categories of the objects to find in the zone. E.g. `{Object.Category.UNIT}`
-- @param UnitCategories An array of unit categories of the objects to find in the zone. -- @param UnitCategories An array of unit categories of the objects to find in the zone. E.g. `{Unit.Category.GROUND_UNIT,Unit.Category.SHIP}`
-- @usage -- @usage
-- self.Zone:Scan() -- self.Zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) -- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
@@ -1135,7 +1157,7 @@ end
-- @return #boolean true if the location is within the zone. -- @return #boolean true if the location is within the zone.
function ZONE_RADIUS:IsVec2InZone( Vec2 ) function ZONE_RADIUS:IsVec2InZone( Vec2 )
self:F2( Vec2 ) self:F2( Vec2 )
if not Vec2 then return false end
local ZoneVec2 = self:GetVec2() local ZoneVec2 = self:GetVec2()
if ZoneVec2 then if ZoneVec2 then
@@ -1153,7 +1175,7 @@ end
-- @return #boolean true if the point is within the zone. -- @return #boolean true if the point is within the zone.
function ZONE_RADIUS:IsVec3InZone( Vec3 ) function ZONE_RADIUS:IsVec3InZone( Vec3 )
self:F2( Vec3 ) self:F2( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone
@@ -1161,24 +1183,54 @@ end
--- Returns a random Vec2 location within the zone. --- Returns a random Vec2 location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 100 times to find the right type!
-- @return DCS#Vec2 The random location within the zone. -- @return DCS#Vec2 The random location within the zone.
function ZONE_RADIUS:GetRandomVec2( inner, outer ) function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
self:F( self.ZoneName, inner, outer )
local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
local _inner = inner or 0 local _inner = inner or 0
local _outer = outer or self:GetRadius() local _outer = outer or self:GetRadius()
local angle = math.random() * math.pi * 2; if surfacetypes and type(surfacetypes)~="table" then
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); surfacetypes={surfacetypes}
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); end
self:T( { Point } ) local function _getpoint()
local point = {}
local angle = math.random() * math.pi * 2
point.x = Vec2.x + math.cos(angle) * math.random(_inner, _outer)
point.y = Vec2.y + math.sin(angle) * math.random(_inner, _outer)
return point
end
return Point local function _checkSurface(point)
local stype=land.getSurfaceType(point)
for _,sf in pairs(surfacetypes) do
if sf==stype then
return true
end
end
return false
end
local point=_getpoint()
if surfacetypes then
local N=1 ; local Nmax=100 ; local gotit=false
while gotit==false and N<=Nmax do
gotit=_checkSurface(point)
if gotit then
--env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax))
else
point=_getpoint()
N=N+1
end
end
end
return point
end end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
@@ -1230,15 +1282,15 @@ end
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. --- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#COORDINATE -- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) -- @return Core.Point#COORDINATE The random coordinate.
self:F( self.ZoneName, inner, outer ) function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) ) local vec2=self:GetRandomVec2(inner, outer, surfacetypes)
self:T3( { Coordinate = Coordinate } ) local Coordinate = COORDINATE:NewFromVec2(vec2)
return Coordinate return Coordinate
end end
@@ -1301,7 +1353,7 @@ function ZONE:New( ZoneName )
-- Error! -- Error!
if not Zone then if not Zone then
error( "Zone " .. ZoneName .. " does not exist." ) env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" )
return nil return nil
end end
@@ -1811,7 +1863,8 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph
Color=Color or self:GetColorRGB() Color=Color or self:GetColorRGB()
Alpha=Alpha or 1 Alpha=Alpha or 1
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillAlpha=FillAlpha or self:GetColorAlpha() FillAlpha=FillAlpha or self:GetColorAlpha()
@@ -1913,7 +1966,7 @@ end
-- @return #boolean true if the location is within the zone. -- @return #boolean true if the location is within the zone.
function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
self:F2( Vec2 ) self:F2( Vec2 )
if not Vec2 then return false end
local Next local Next
local Prev local Prev
local InPolygon = false local InPolygon = false
@@ -1937,32 +1990,48 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
return InPolygon return InPolygon
end end
--- Returns if a point is within the zone.
-- @param #ZONE_POLYGON_BASE self
-- @param DCS#Vec3 Vec3 The point to test.
-- @return #boolean true if the point is within the zone.
function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 )
self:F2( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone
end
--- Define a random @{DCS#Vec2} within the zone. --- Define a random @{DCS#Vec2} within the zone.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return DCS#Vec2 The Vec2 coordinate. -- @return DCS#Vec2 The Vec2 coordinate.
function ZONE_POLYGON_BASE:GetRandomVec2() function ZONE_POLYGON_BASE:GetRandomVec2()
self:F2()
--- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... -- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
local Vec2Found = false
local Vec2 -- Get the bounding square.
local BS = self:GetBoundingSquare() local BS = self:GetBoundingSquare()
self:T2( BS ) local Nmax=1000 ; local n=0
while n<Nmax do
while Vec2Found == false do -- Random point in the bounding square.
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
self:T2( Vec2 )
-- Check if this is in the polygon.
if self:IsVec2InZone(Vec2) then if self:IsVec2InZone(Vec2) then
Vec2Found = true
end
end
self:T2( Vec2 )
return Vec2 return Vec2
end end
n=n+1
end
self:E("Could not find a random point in the polygon zone!")
return nil
end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. --- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC2} -- @return @{Core.Point#POINT_VEC2}
@@ -2075,12 +2144,12 @@ end
-- --
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor! -- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
-- --
-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name. -- You can declare a ZONE_POLYGON using the DCS mission editor by adding the #ZONE_POLYGON tag in the group name.
-- --
-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. -- So, imagine you have a group declared in the mission editor, with group name `DefenseZone#ZONE_POLYGON`.
-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. -- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration.
-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group. -- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group.
-- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. -- The ZONE_POLYGON name will be the group name without the #ZONE_POLYGON tag.
-- --
-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. -- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method.
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object -- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object
@@ -2152,6 +2221,9 @@ end
do -- ZONE_AIRBASE do -- ZONE_AIRBASE
--- @type ZONE_AIRBASE --- @type ZONE_AIRBASE
-- @field #boolean isShip If `true`, airbase is a ship.
-- @field #boolean isHelipad If `true`, airbase is a helipad.
-- @field #boolean isAirdrome If `true`, airbase is an airdrome.
-- @extends #ZONE_RADIUS -- @extends #ZONE_RADIUS
@@ -2181,6 +2253,20 @@ do -- ZONE_AIRBASE
self._.ZoneAirbase = Airbase self._.ZoneAirbase = Airbase
self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2()
if Airbase:IsShip() then
self.isShip=true
self.isHelipad=false
self.isAirdrome=false
elseif Airbase:IsHelipad() then
self.isShip=false
self.isHelipad=true
self.isAirdrome=false
elseif Airbase:IsAirdrome() then
self.isShip=false
self.isHelipad=false
self.isAirdrome=true
end
-- Zone objects are added to the _DATABASE and SET_ZONE objects. -- Zone objects are added to the _DATABASE and SET_ZONE objects.
_EVENTDISPATCHER:CreateEventNewZone( self ) _EVENTDISPATCHER:CreateEventNewZone( self )
@@ -2194,9 +2280,9 @@ do -- ZONE_AIRBASE
return self._.ZoneAirbase return self._.ZoneAirbase
end end
--- Returns the current location of the @{Wrapper.Group}. --- Returns the current location of the AIRBASE.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. -- @return DCS#Vec2 The location of the zone based on the AIRBASE location.
function ZONE_AIRBASE:GetVec2() function ZONE_AIRBASE:GetVec2()
self:F( self.ZoneName ) self:F( self.ZoneName )
@@ -2214,24 +2300,6 @@ do -- ZONE_AIRBASE
return ZoneVec2 return ZoneVec2
end end
--- Returns a random location within the zone of the @{Wrapper.Group}.
-- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
function ZONE_AIRBASE:GetRandomVec2()
self:F( self.ZoneName )
local Point = {}
local Vec2 = self._.ZoneAirbase:GetVec2()
local angle = math.random() * math.pi*2;
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius();
self:T( { Point } )
return Point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.

View File

@@ -487,8 +487,10 @@ 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

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,7 @@
-- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. -- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there.
-- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false. -- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false.
-- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group. -- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group.
-- @field #boolean report Arty group sends messages about their current state or target to its coaliton. -- @field #boolean report Arty group sends messages about their current state or target to its coalition.
-- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. -- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells.
-- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets.
-- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles.

View File

@@ -19,7 +19,7 @@
-- --
-- === -- ===
-- --
-- Facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). -- Facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnaissance Units).
-- It uses the in-built detection capabilities of DCS World, but adds new functionalities. -- It uses the in-built detection capabilities of DCS World, but adds new functionalities.
-- --
-- === -- ===
@@ -37,7 +37,6 @@
-- @module Functional.Detection -- @module Functional.Detection
-- @image Detection.JPG -- @image Detection.JPG
do -- DETECTION_BASE do -- DETECTION_BASE
--- @type DETECTION_BASE --- @type DETECTION_BASE
@@ -92,7 +91,6 @@ do -- DETECTION_BASE
-- --
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
-- --
--
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list -- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
-- --
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
@@ -128,11 +126,10 @@ do -- DETECTION_BASE
-- * A probability factor based on the alpha angle between the detected object and the unit detecting. -- * A probability factor based on the alpha angle between the detected object and the unit detecting.
-- A detection from a higher altitude allows for better detection than when on the ground. -- A detection from a higher altitude allows for better detection than when on the ground.
-- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult.
-- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects adding a probability factor per zone.
-- --
-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters.
-- Only when you experience unrealistic behaviour in your missions, these filters could be applied. -- Only when you experience unrealistic behavior in your missions, these filters could be applied.
--
-- --
-- ### Distance visual detection probability -- ### Distance visual detection probability
-- --
@@ -170,9 +167,9 @@ do -- DETECTION_BASE
-- --
-- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take
-- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone.
-- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! -- Especially for ZONE_POLYGON, try to limit the amount of nodes of the polygon!
-- --
-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for -- Typically, this kind of filter would be applied for very specific areas where a detection needs to be very realistic for
-- AI not to detect so easily targets within a forrest or village rich area. -- AI not to detect so easily targets within a forrest or village rich area.
-- --
-- ## Accept / Reject detected units -- ## Accept / Reject detected units
@@ -217,7 +214,7 @@ do -- DETECTION_BASE
-- -- Start the Detection. -- -- Start the Detection.
-- Detection:Start() -- Detection:Start()
-- --
-- ### Detection rejectance if within zone(s). -- ### Detection rejection if within zone(s).
-- --
-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s).
-- Use the method @{Functional.Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. -- Use the method @{Functional.Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones.
@@ -287,11 +284,10 @@ do -- DETECTION_BASE
-- @field #boolean LastPos -- @field #boolean LastPos
-- @field #number LastVelocity -- @field #number LastVelocity
--- @type DETECTION_BASE.DetectedItems --- @type DETECTION_BASE.DetectedItems
-- @list <#DETECTION_BASE.DetectedItem> -- @list <#DETECTION_BASE.DetectedItem>
--- Detected item data structrue. --- Detected item data structure.
-- @type DETECTION_BASE.DetectedItem -- @type DETECTION_BASE.DetectedItem
-- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not. -- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not.
-- @field Core.Set#SET_UNIT Set The Set of Units in the detected area. -- @field Core.Set#SET_UNIT Set The Set of Units in the detected area.
@@ -302,7 +298,7 @@ do -- DETECTION_BASE
-- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area.
-- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area.
-- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem. -- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem.
-- @field Core.Point#COORDINATE InterceptCoord Intercept coordiante. -- @field Core.Point#COORDINATE InterceptCoord Intercept coordinate.
-- @field #number DistanceRecce Distance in meters of the Recce. -- @field #number DistanceRecce Distance in meters of the Recce.
-- @field #number Index Detected item key. Could also be a string. -- @field #number Index Detected item key. Could also be a string.
-- @field #string ItemID ItemPrefix .. "." .. self.DetectedItemMax. -- @field #string ItemID ItemPrefix .. "." .. self.DetectedItemMax.
@@ -310,7 +306,7 @@ do -- DETECTION_BASE
-- @field #table PlayersNearBy Table of nearby players. -- @field #table PlayersNearBy Table of nearby players.
-- @field #table FriendliesDistance Table of distances to friendly units. -- @field #table FriendliesDistance Table of distances to friendly units.
-- @field #string TypeName Type name of the detected unit. -- @field #string TypeName Type name of the detected unit.
-- @field #string CategoryName Catetory name of the detected unit. -- @field #string CategoryName Category name of the detected unit.
-- @field #string Name Name of the detected object. -- @field #string Name Name of the detected object.
-- @field #boolean IsVisible If true, detected object is visible. -- @field #boolean IsVisible If true, detected object is visible.
-- @field #number LastTime Last time the detected item was seen. -- @field #number LastTime Last time the detected item was seen.
@@ -441,7 +437,6 @@ do -- DETECTION_BASE
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #number Delay The delay in seconds. -- @param #number Delay The delay in seconds.
self:AddTransition( "Detecting", "Detected", "Detecting" ) self:AddTransition( "Detecting", "Detected", "Detecting" )
--- OnBefore Transition Handler for Event Detected. --- OnBefore Transition Handler for Event Detected.
@@ -566,12 +561,10 @@ do -- DETECTION_BASE
local DetectionInterval = self.DetectionCount / (self.RefreshTimeInterval - 1) local DetectionInterval = self.DetectionCount / (self.RefreshTimeInterval - 1)
self:ForEachAliveRecce( self:ForEachAliveRecce( function( DetectionGroup )
function( DetectionGroup )
self:__Detection( DetectDelay, DetectionGroup, DetectionTimeStamp ) -- Process each detection asynchronously. self:__Detection( DetectDelay, DetectionGroup, DetectionTimeStamp ) -- Process each detection asynchronously.
DetectDelay = DetectDelay + DetectionInterval DetectDelay = DetectDelay + DetectionInterval
end end )
)
self:__Detect( -self.RefreshTimeInterval ) self:__Detect( -self.RefreshTimeInterval )
@@ -594,12 +587,11 @@ do -- DETECTION_BASE
return self return self
end end
--- @param #DETECTION_BASE self --- @param #DETECTION_BASE self
-- @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 )
@@ -670,13 +662,12 @@ 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 (DetectedObjectVec3.z - DetectionGroupVec3.z) ^ 2) ^ 0.5 / 1000
) ^ 0.5 / 1000
local DetectedUnitCategory = DetectedObject:getDesc().category local DetectedUnitCategory = DetectedObject:getDesc().category
@@ -714,7 +705,7 @@ do -- DETECTION_BASE
if self.RejectZones then if self.RejectZones then
for RejectZoneID, RejectZone in pairs( self.RejectZones ) do for RejectZoneID, RejectZone in pairs( self.RejectZones ) do
local RejectZone = RejectZone -- Core.Zone#ZONE_BASE local RejectZone = RejectZone -- Core.Zone#ZONE_BASE
if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then if RejectZone:IsVec2InZone( DetectedObjectVec2 ) == true then
DetectionAccepted = false DetectionAccepted = false
end end
end end
@@ -759,7 +750,7 @@ do -- DETECTION_BASE
local ZoneProbability = ZoneData[2] -- #number local ZoneProbability = ZoneData[2] -- #number
ZoneProbability = ZoneProbability * 30 / 300 ZoneProbability = ZoneProbability * 30 / 300
if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then if ZoneObject:IsVec2InZone( DetectedObjectVec2 ) == true then
local Probability = math.random() -- Selects a number between 0 and 1 local Probability = math.random() -- Selects a number between 0 and 1
-- self:T( { Probability, ZoneProbability } ) -- self:T( { Probability, ZoneProbability } )
if Probability > ZoneProbability then if Probability > ZoneProbability then
@@ -854,10 +845,8 @@ do -- DETECTION_BASE
end end
end end
end end
end end
do -- DetectionItems Creation do -- DetectionItems Creation
@@ -906,7 +895,6 @@ do -- DETECTION_BASE
return self return self
end end
end end
do -- Initialization methods do -- Initialization methods
@@ -1151,7 +1139,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.
-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.
-- --
@@ -1184,7 +1171,6 @@ do -- DETECTION_BASE
return self return self
end end
end end
do -- Change processing do -- Change processing
@@ -1221,7 +1207,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Add a change to the detected zone. --- Add a change to the detected zone.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
@@ -1315,7 +1300,7 @@ do -- DETECTION_BASE
return DetectedItem.FriendliesNearIntercept return DetectedItem.FriendliesNearIntercept
end end
--- Returns the distance used to identify friendlies near the deteted item ... --- Returns the distance used to identify friendlies near the detected item ...
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item. -- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
-- @return #table A table of distances to friendlies. -- @return #table A table of distances to friendlies.
@@ -1327,7 +1312,7 @@ do -- DETECTION_BASE
--- Returns if there are friendlies nearby the FAC units ... --- Returns if there are friendlies nearby the FAC units ...
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
-- @return #boolean trhe if there are friendlies nearby -- @return #boolean true if there are friendlies nearby
function DETECTION_BASE:IsPlayersNearBy( DetectedItem ) function DETECTION_BASE:IsPlayersNearBy( DetectedItem )
return DetectedItem.PlayersNearBy ~= nil return DetectedItem.PlayersNearBy ~= nil
@@ -1366,7 +1351,6 @@ do -- DETECTION_BASE
point = InterceptCoord:GetVec3(), point = InterceptCoord:GetVec3(),
radius = self.FriendliesRange, radius = self.FriendliesRange,
} }
} }
--- @param DCS#Unit FoundDCSUnit --- @param DCS#Unit FoundDCSUnit
@@ -1465,8 +1449,7 @@ do -- DETECTION_BASE
end end
end end
end end
end end )
)
end end
self:F( { Friendlies = DetectedItem.FriendliesNearBy, Players = DetectedItem.PlayersNearBy } ) self:F( { Friendlies = DetectedItem.FriendliesNearBy, Players = DetectedItem.PlayersNearBy } )
@@ -1542,7 +1525,6 @@ do -- DETECTION_BASE
return nil return nil
end end
--- Gets a detected unit type name, taking into account the detection results. --- Gets a detected unit type name, taking into account the detection results.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param Wrapper.Unit#UNIT DetectedUnit -- @param Wrapper.Unit#UNIT DetectedUnit
@@ -1570,7 +1552,6 @@ do -- DETECTION_BASE
return "Undetected:" .. DetectedUnit:GetName() return "Undetected:" .. DetectedUnit:GetName()
end end
--- Adds a new DetectedItem to the DetectedItems list. --- Adds a new DetectedItem to the DetectedItems list.
-- The DetectedItem is a table and contains a SET_UNIT in the field Set. -- The DetectedItem is a table and contains a SET_UNIT in the field Set.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
@@ -1584,7 +1565,6 @@ do -- DETECTION_BASE
self.DetectedItemCount = self.DetectedItemCount + 1 self.DetectedItemCount = self.DetectedItemCount + 1
self.DetectedItemMax = self.DetectedItemMax + 1 self.DetectedItemMax = self.DetectedItemMax + 1
DetectedItemKey = DetectedItemKey or self.DetectedItemMax DetectedItemKey = DetectedItemKey or self.DetectedItemMax
self.DetectedItems[DetectedItemKey] = DetectedItem self.DetectedItems[DetectedItemKey] = DetectedItem
self.DetectedItemsByIndex[DetectedItemKey] = DetectedItem self.DetectedItemsByIndex[DetectedItemKey] = DetectedItem
@@ -1636,7 +1616,6 @@ do -- DETECTION_BASE
end end
end end
--- Get the DetectedItems by Key. --- Get the DetectedItems by Key.
-- This will return the DetectedItems collection, indexed by the Key, which can be any object that acts as the key of the detection. -- This will return the DetectedItems collection, indexed by the Key, which can be any object that acts as the key of the detection.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
@@ -1657,7 +1636,7 @@ do -- DETECTION_BASE
--- Get the amount of SETs with detected objects. --- Get the amount of SETs with detected objects.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @return #number The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but doen in intervals! -- @return #number The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but done in intervals!
function DETECTION_BASE:GetDetectedItemsCount() function DETECTION_BASE:GetDetectedItemsCount()
local DetectedCount = self.DetectedItemCount local DetectedCount = self.DetectedItemCount
@@ -1719,7 +1698,7 @@ do -- DETECTION_BASE
return "" return ""
end end
--- Get the @{Core.Set#SET_UNIT} of a detecttion area using a given numeric index. --- Get the @{Core.Set#SET_UNIT} of a detection area using a given numeric index.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
-- @return Core.Set#SET_UNIT DetectedSet -- @return Core.Set#SET_UNIT DetectedSet
@@ -1766,7 +1745,6 @@ do -- DETECTION_BASE
return DetectedItem.IsDetected return DetectedItem.IsDetected
end end
do -- Zones do -- Zones
--- Get the @{Core.Zone#ZONE_UNIT} of a detection area using a given numeric index. --- Get the @{Core.Zone#ZONE_UNIT} of a detection area using a given numeric index.
@@ -1800,7 +1778,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Unlock the detected items when created and unlock all existing detected items. --- Unlock the detected items when created and unlock all existing detected items.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @return #DETECTION_BASE -- @return #DETECTION_BASE
@@ -1824,7 +1801,6 @@ do -- DETECTION_BASE
end end
--- Lock a detected item. --- Lock a detected item.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -1847,9 +1823,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Set the detected item coordinate. --- Set the detected item coordinate.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at.
@@ -1869,7 +1842,6 @@ do -- DETECTION_BASE
end end
end end
--- Get the detected item coordinate. --- Get the detected item coordinate.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at.
@@ -1911,8 +1883,6 @@ do -- DETECTION_BASE
end end
end end
--- Get the detected item coordinate. --- Get the detected item coordinate.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -1928,7 +1898,6 @@ do -- DETECTION_BASE
return nil, "" return nil, ""
end end
--- Report summary of a detected item using a given numeric index. --- Report summary of a detected item using a given numeric index.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -1940,7 +1909,7 @@ do -- DETECTION_BASE
return nil return nil
end end
--- Report detailed of a detectedion result. --- Report detailed of a detection result.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
-- @return #string -- @return #string
@@ -1987,8 +1956,6 @@ do -- DETECTION_BASE
end end
--- Schedule the DETECTION construction. --- Schedule the DETECTION construction.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #number DelayTime The delay in seconds to wait the reporting. -- @param #number DelayTime The delay in seconds to wait the reporting.
@@ -2078,7 +2045,6 @@ do -- DETECTION_UNITS
end end
--- Create the DetectedItems list from the DetectedObjects table. --- Create the DetectedItems list from the DetectedObjects table.
-- For each DetectedItem, a one field array is created containing the Unit detected. -- For each DetectedItem, a one field array is created containing the Unit detected.
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
@@ -2130,7 +2096,6 @@ do -- DETECTION_UNITS
end end
end end
-- Now we need to loop through the unidentified detected units and add these... These are all new items. -- Now we need to loop through the unidentified detected units and add these... These are all new items.
for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do
@@ -2181,7 +2146,6 @@ do -- DETECTION_UNITS
end end
--- Report summary of a DetectedItem using a given numeric index. --- Report summary of a DetectedItem using a given numeric index.
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -2238,7 +2202,6 @@ do -- DETECTION_UNITS
return nil return nil
end end
--- Report detailed of a detection result. --- Report detailed of a detection result.
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
@@ -2332,7 +2295,6 @@ do -- DETECTION_TYPES
end end
--- Create the DetectedItems list from the DetectedObjects table. --- Create the DetectedItems list from the DetectedObjects table.
-- For each DetectedItem, a one field array is created containing the Unit detected. -- For each DetectedItem, a one field array is created containing the Unit detected.
-- @param #DETECTION_TYPES self -- @param #DETECTION_TYPES self
@@ -2371,7 +2333,6 @@ do -- DETECTION_TYPES
end end
end end
-- Now we need to loop through the unidentified detected units and add these... These are all new items. -- Now we need to loop through the unidentified detected units and add these... These are all new items.
for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do
@@ -2395,8 +2356,6 @@ do -- DETECTION_TYPES
end end
end end
-- Check if there are any friendlies nearby. -- Check if there are any friendlies nearby.
for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do
@@ -2413,8 +2372,6 @@ do -- DETECTION_TYPES
self:NearestRecce( DetectedItem ) self:NearestRecce( DetectedItem )
end end
end end
--- Report summary of a DetectedItem using a given numeric index. --- Report summary of a DetectedItem using a given numeric index.
@@ -2469,7 +2426,6 @@ do -- DETECTION_TYPES
end end
do -- DETECTION_AREAS do -- DETECTION_AREAS
--- @type DETECTION_AREAS --- @type DETECTION_AREAS
@@ -2484,14 +2440,14 @@ 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#DECTECTION_BASE} and -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DETECTION_BASE} and
-- the methods to manage the DetectedItems[].Zone(s) is 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.
-- --
-- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZones}(). -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_AREAS.GetDetectionZones}().
-- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZoneCount}(). -- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_AREAS.GetDetectionZoneCount}().
-- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZone}() with a given index. -- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_AREAS.GetDetectionZoneByID}() with a given index.
-- --
-- ## 4.4) Flare or Smoke detected units -- ## 4.4) Flare or Smoke detected units
-- --
@@ -2513,7 +2469,6 @@ do -- DETECTION_AREAS
DetectionZoneRange = nil, DetectionZoneRange = nil,
} }
--- 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 @{Set} of GROUPs in the Forward Air Controller role.
@@ -2535,6 +2490,48 @@ do -- DETECTION_AREAS
return self return self
end end
--- Retrieve set of detected zones.
-- @param #DETECTION_AREAS self
-- @return Core.Set#SET_ZONE The @{Set} of ZONE_UNIT objects detected.
function DETECTION_AREAS:GetDetectionZones()
local zoneset = SET_ZONE:New()
for _ID,_Item in pairs (self.DetectedItems) do
local item = _Item -- #DETECTION_BASE.DetectedItem
if item.Zone then
zoneset:AddZone(item.Zone)
end
end
return zoneset
end
--- Retrieve a specific zone by its ID (number)
-- @param #DETECTION_AREAS self
-- @param #number ID
-- @return Core.Zone#ZONE_UNIT The zone or nil if it does not exist
function DETECTION_AREAS:GetDetectionZoneByID(ID)
local zone = nil
for _ID,_Item in pairs (self.DetectedItems) do
local item = _Item -- #DETECTION_BASE.DetectedItem
if item.ID == ID then
zone = item.Zone
break
end
end
return zone
end
--- Retrieve number of detected zones.
-- @param #DETECTION_AREAS self
-- @return #number The number of zones.
function DETECTION_AREAS:GetDetectionZoneCount()
local zoneset = 0
for _ID,_Item in pairs (self.DetectedItems) do
if _Item.Zone then
zoneset = zoneset + 1
end
end
return zoneset
end
--- Report summary of a detected item using a given numeric index. --- Report summary of a detected item using a given numeric index.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
@@ -2593,7 +2590,6 @@ do -- DETECTION_AREAS
DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G( AttackGroup, Settings ) DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G( AttackGroup, Settings )
end end
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem ) local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
local DetectedItemsCount = DetectedSet:Count() local DetectedItemsCount = DetectedSet:Count()
local DetectedItemsTypes = DetectedSet:GetTypeNames() local DetectedItemsTypes = DetectedSet:GetTypeNames()
@@ -2630,7 +2626,6 @@ do -- DETECTION_AREAS
return ReportText return ReportText
end end
--- Calculate the optimal intercept point of the DetectedItem. --- Calculate the optimal intercept point of the DetectedItem.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
@@ -2655,8 +2650,6 @@ do -- DETECTION_AREAS
end end
--- Smoke the detected units --- Smoke the detected units
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @return #DETECTION_AREAS self -- @return #DETECTION_AREAS self
@@ -2760,13 +2753,11 @@ do -- DETECTION_AREAS
end end
--- Make a DetectionSet table. This function will be overridden in the derived classes.
--- Make a DetectionSet table. This function will be overridden in the derived clsses.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @return #DETECTION_AREAS self -- @return #DETECTION_AREAS self
function DETECTION_AREAS:CreateDetectionItems() function DETECTION_AREAS:CreateDetectionItems()
self:F( "Checking Detected Items for new Detected Units ..." ) self:F( "Checking Detected Items for new Detected Units ..." )
-- self:F( { DetectedObjects = self.DetectedObjects } ) -- self:F( { DetectedObjects = self.DetectedObjects } )
@@ -2794,8 +2785,6 @@ do -- DETECTION_AREAS
-- self:IdentifyDetectedObject( DetectedZoneObject ) -- self:IdentifyDetectedObject( DetectedZoneObject )
AreaExists = true AreaExists = true
else else
-- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area.
-- First remove the center unit from the set. -- First remove the center unit from the set.
@@ -2882,11 +2871,9 @@ do -- DETECTION_AREAS
end end
end end
-- We iterated through the existing detection areas and: -- We iterated through the existing detection areas and:
-- - We checked which units are still detected in each detection area. Those units were flagged as Identified. -- - We checked which units are still detected in each detection area. Those units were flagged as Identified.
-- - We recentered the detection area to new center units where it was needed. -- - We re-centered the detection area to new center units where it was needed.
-- --
-- Now we need to loop through the unidentified detected units and see where they belong: -- Now we need to loop through the unidentified detected units and see where they belong:
-- - They can be added to a new detection area and become the new center unit. -- - They can be added to a new detection area and become the new center unit.
@@ -2961,15 +2948,13 @@ do -- DETECTION_AREAS
self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level
self:NearestRecce( DetectedItem ) self:NearestRecce( DetectedItem )
if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then
DetectedZone.ZoneUNIT:SmokeRed() DetectedZone.ZoneUNIT:SmokeRed()
end end
-- DetectedSet:Flush( self ) -- DetectedSet:Flush( self )
DetectedSet:ForEachUnit( DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit ) function( DetectedUnit )
if DetectedUnit:IsAlive() then if DetectedUnit:IsAlive() then
-- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) -- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )
@@ -2980,8 +2965,7 @@ do -- DETECTION_AREAS
DetectedUnit:SmokeGreen() DetectedUnit:SmokeGreen()
end end
end end
end end )
)
if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then
DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0, 90 ) ) DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0, 90 ) )
end end
@@ -2999,4 +2983,3 @@ do -- DETECTION_AREAS
end end

View File

@@ -23,7 +23,6 @@
-- @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.
@@ -48,7 +47,6 @@
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec. -- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
-- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec. -- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec.
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec. -- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
-- @field #boolean
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Fox 3! --- Fox 3!

View File

@@ -19,8 +19,8 @@
-- --
-- @module Functional.Mantis -- @module Functional.Mantis
-- @image Functional.Mantis.jpg -- @image Functional.Mantis.jpg
--
-- Date: July 2021 -- Date: Nov 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE --- **MANTIS** class, extends Core.Base#BASE
@@ -101,23 +101,23 @@
-- --
-- # 2. Start up your MANTIS with a basic setting -- # 2. Start up your MANTIS with a basic setting
-- --
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)
-- `myredmantis:Start()` -- myredmantis:Start()
-- --
-- [optional] Use -- [optional] Use
-- --
-- * `MANTIS:SetEWRGrouping(radius)` -- * MANTIS:SetEWRGrouping(radius)
-- * `MANTIS:SetEWRRange(radius)` -- * MANTIS:SetEWRRange(radius)
-- * `MANTIS:SetSAMRadius(radius)` -- * MANTIS:SetSAMRadius(radius)
-- * `MANTIS:SetDetectInterval(interval)` -- * MANTIS:SetDetectInterval(interval)
-- * `MANTIS:SetAutoRelocate(hq, ewr)` -- * MANTIS:SetAutoRelocate(hq, ewr)
-- --
-- before starting #MANTIS to fine-tune your setup. -- before starting #MANTIS to fine-tune your setup.
-- --
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: -- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
-- --
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
-- `mybluemantis:Start()` -- mybluemantis:Start()
-- --
-- # 3. Default settings -- # 3. Default settings
-- --
@@ -138,7 +138,7 @@
-- --
-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option. -- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
-- --
-- E.g. `mymantis:SetAdvancedMode( true, 90 )` -- E.g. mymantis:SetAdvancedMode( true, 90 )
-- --
-- Use this option if you want to make use of or allow advanced SEAD tactics. -- Use this option if you want to make use of or allow advanced SEAD tactics.
-- --
@@ -147,16 +147,39 @@
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in -- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so: -- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
-- --
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()` -- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
-- `myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")` -- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
-- `-- now set up MANTIS` -- -- now set up MANTIS
-- `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()
-- --
-- and (optionally) remove the link later on with -- and (optionally) remove the link later on with
-- --
-- `mymantis:RemoveShorad()` -- mymantis:RemoveShorad()
--
-- # 6. Integrated SEAD
--
-- MANTIS is using @{Functional.Sead#SEAD} internally to both detect and evade HARM attacks. No extra efforts needed to set this up!
-- Once a HARM attack is detected, MANTIS (via SEAD) will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
-- vehicles around (*if they are __drivable__*, that is). There's a component of randomness in detection and evasion, which is based on the
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
-- period of time to stay defensive, before it takes evasive actions.
--
-- You can link into the SEAD driven events of MANTIS like so:
--
-- function mymantis:OnAfterSeadSuppressionPlanned(From, Event, To, Group, Name, SuppressionStartTime, SuppressionEndTime)
-- -- your code here - SAM site shutdown and evasion planned, but not yet executed
-- -- Time entries relate to timer.getTime() - see https://wiki.hoggitworld.com/view/DCS_func_getTime
-- end
--
-- function mymantis:OnAfterSeadSuppressionStart(From, Event, To, Group, Name)
-- -- your code here - SAM site is emissions off and possibly moving
-- end
--
-- function mymantis:OnAfterSeadSuppressionEnd(From, Event, To, Group, Name)
-- -- your code here - SAM site is back online
-- end
-- --
-- @field #MANTIS -- @field #MANTIS
MANTIS = { MANTIS = {
@@ -198,6 +221,7 @@ MANTIS = {
DLink = false, DLink = false,
DLTimeStamp = 0, DLTimeStamp = 0,
Padding = 10, Padding = 10,
SuppressedGroups = {},
} }
--- Advanced state enumerator --- Advanced state enumerator
@@ -227,23 +251,23 @@ do
--@return #MANTIS self --@return #MANTIS self
--@usage Start up your MANTIS with a basic setting --@usage Start up your MANTIS with a basic setting
-- --
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)
-- `myredmantis:Start()` -- myredmantis:Start()
-- --
-- [optional] Use -- [optional] Use
-- --
-- * `MANTIS:SetEWRGrouping(radius)` -- * MANTIS:SetEWRGrouping(radius)
-- * `MANTIS:SetEWRRange(radius)` -- * MANTIS:SetEWRRange(radius)
-- * `MANTIS:SetSAMRadius(radius)` -- * MANTIS:SetSAMRadius(radius)
-- * `MANTIS:SetDetectInterval(interval)` -- * MANTIS:SetDetectInterval(interval)
-- * `MANTIS:SetAutoRelocate(hq, ewr)` -- * MANTIS:SetAutoRelocate(hq, ewr)
-- --
-- before starting #MANTIS to fine-tune your setup. -- before starting #MANTIS to fine-tune your setup.
-- --
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: -- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
-- --
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
-- `mybluemantis:Start()` -- mybluemantis:Start()
-- --
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff, Padding) function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff, Padding)
@@ -284,6 +308,7 @@ do
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
self.DLink = false self.DLink = false
self.Padding = Padding or 10 self.Padding = Padding or 10
self.SuppressedGroups = {}
if EmOnOff then if EmOnOff then
if EmOnOff == false then if EmOnOff == false then
@@ -331,7 +356,7 @@ do
end end
-- @field #string version -- @field #string version
self.version="0.6.2" self.version="0.7.1"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions --- --- FSM Functions ---
@@ -348,6 +373,9 @@ do
self:AddTransition("*", "RedState", "*") -- MANTIS A SAM switching to RED state. self:AddTransition("*", "RedState", "*") -- MANTIS A SAM switching to RED state.
self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change. self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change.
self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD. self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD.
self:AddTransition("*", "SeadSuppressionStart", "*") -- SEAD has switched off one group.
self:AddTransition("*", "SeadSuppressionEnd", "*") -- SEAD has switched on one group.
self:AddTransition("*", "SeadSuppressionPlanned", "*") -- SEAD has planned a suppression.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
------------------------ ------------------------
@@ -427,6 +455,35 @@ do
-- @param #number Radius Radius around the named group to find SHORAD groups -- @param #number Radius Radius around the named group to find SHORAD groups
-- @param #number Ontime Seconds the SHORAD will stay active -- @param #number Ontime Seconds the SHORAD will stay active
--- On After "SeadSuppressionPlanned" event. Mantis has planned to switch off a site to defend SEAD attack.
-- @function [parent=#MANTIS] OnAfterSeadSuppressionPlanned
-- @param #MANTIS self
-- @param #string From The From State
-- @param #string Event The Event
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
-- @param #string Name Name of the suppressed group
-- @param #number SuppressionStartTime Model start time of the suppression from `timer.getTime()`
-- @param #number SuppressionEndTime Model end time of the suppression from `timer.getTime()`
--- On After "SeadSuppressionStart" event. Mantis has switched off a site to defend a SEAD attack.
-- @function [parent=#MANTIS] OnAfterSeadSuppressionStart
-- @param #MANTIS self
-- @param #string From The From State
-- @param #string Event The Event
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
-- @param #string Name Name of the suppressed groupe
--- On After "SeadSuppressionEnd" event. Mantis has switched on a site after a SEAD attack.
-- @function [parent=#MANTIS] OnAfterSeadSuppressionEnd
-- @param #MANTIS self
-- @param #string From The From State
-- @param #string Event The Event
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
-- @param #string Name Name of the suppressed group
return self return self
end end
@@ -625,7 +682,7 @@ do
return self return self
end end
--- Set using an #INTEL_DLINK object instead of #DETECTION. Requires Develop branch of Moose.lua. --- Set using an #INTEL_DLINK object instead of #DETECTION
-- @param #MANTIS self -- @param #MANTIS self
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used. -- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
function MANTIS:SetUsingDLink(DLink) function MANTIS:SetUsingDLink(DLink)
@@ -891,6 +948,7 @@ do
local group = _group -- Wrapper.Group#GROUP local group = _group -- Wrapper.Group#GROUP
-- TODO: add emissions on/off -- TODO: add emissions on/off
if self.UseEmOnOff then if self.UseEmOnOff then
group:OptionAlarmStateRed()
group:EnableEmission(false) group:EnableEmission(false)
--group:SetAIOff() --group:SetAIOff()
else else
@@ -909,6 +967,10 @@ do
-- make SAMs evasive -- make SAMs evasive
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
mysead:SetEngagementRange(engagerange) mysead:SetEngagementRange(engagerange)
mysead:AddCallBack(self)
if self.UseEmOnOff then
mysead:SwitchEmissions(true)
end
self.mysead = mysead self.mysead = mysead
return self return self
end end
@@ -995,22 +1057,24 @@ do
local name = _data[1] local name = _data[1]
local samgroup = GROUP:FindByName(name) local samgroup = GROUP:FindByName(name)
local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate) local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate)
if IsInZone then --check any target in zone local suppressed = self.SuppressedGroups[name] or false
if IsInZone then --check any target in zone and not curr managed by SEAD
if samgroup:IsAlive() then if samgroup:IsAlive() then
-- switch on SAM -- switch on SAM
if self.UseEmOnOff then if self.UseEmOnOff and not suppressed then
-- TODO: add emissions on/off -- DONE: add emissions on/off
--samgroup:SetAIOn() --samgroup:SetAIOn()
samgroup:EnableEmission(true) samgroup:EnableEmission(true)
end elseif not self.UseEmOnOff and not suppressed then
samgroup:OptionAlarmStateRed() samgroup:OptionAlarmStateRed()
if self.SamStateTracker[name] ~= "RED" then end
if self.SamStateTracker[name] ~= "RED" and not suppressed then
self:__RedState(1,samgroup) self:__RedState(1,samgroup)
self.SamStateTracker[name] = "RED" self.SamStateTracker[name] = "RED"
end end
-- link in to SHORAD if available -- link in to SHORAD if available
-- DONE: Test integration fully -- DONE: Test integration fully
if self.ShoradLink and Distance < self.ShoradActDistance then -- don't give SHORAD position away too early if self.ShoradLink and (Distance < self.ShoradActDistance or suppressed) then -- don't give SHORAD position away too early
local Shorad = self.Shorad local Shorad = self.Shorad
local radius = self.checkradius local radius = self.checkradius
local ontime = self.ShoradTime local ontime = self.ShoradTime
@@ -1018,7 +1082,7 @@ do
self:__ShoradActivated(1,name, radius, ontime) self:__ShoradActivated(1,name, radius, ontime)
end end
-- debug output -- debug output
if self.debug or self.verbose then if self.debug or self.verbose and not suppressed then
local text = string.format("SAM %s switched to alarm state RED!", name) local text = string.format("SAM %s switched to alarm state RED!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(self.lid..text) end if self.verbose then self:I(self.lid..text) end
@@ -1027,15 +1091,16 @@ do
else else
if samgroup:IsAlive() then if samgroup:IsAlive() then
-- switch off SAM -- switch off SAM
if self.UseEmOnOff then if self.UseEmOnOff and not suppressed then
samgroup:EnableEmission(false) samgroup:EnableEmission(false)
end elseif not self.UseEmOnOff and not suppressed then
samgroup:OptionAlarmStateGreen() samgroup:OptionAlarmStateGreen()
if self.SamStateTracker[name] ~= "GREEN" then end
if self.SamStateTracker[name] ~= "GREEN" and not suppressed then
self:__GreenState(1,samgroup) self:__GreenState(1,samgroup)
self.SamStateTracker[name] = "GREEN" self.SamStateTracker[name] = "GREEN"
end end
if self.debug or self.verbose then if self.debug or self.verbose and not suppressed then
local text = string.format("SAM %s switched to alarm state GREEN!", name) local text = string.format("SAM %s switched to alarm state GREEN!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(self.lid..text) end if self.verbose then self:I(self.lid..text) end
@@ -1264,6 +1329,47 @@ do
self:T({From, Event, To, Name, Radius, Ontime}) self:T({From, Event, To, Name, Radius, Ontime})
return self return self
end end
--- [Internal] Function triggered by Event SeadSuppressionStart
-- @param #MANTIS self
-- @param #string From The From State
-- @param #string Event The Event
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
-- @param #string Name Name of the suppressed group
function MANTIS:onafterSeadSuppressionStart(From, Event, To, Group, Name)
self:T({From, Event, To, Name})
self.SuppressedGroups[Name] = true
return self
end
--- [Internal] Function triggered by Event SeadSuppressionEnd
-- @param #MANTIS self
-- @param #string From The From State
-- @param #string Event The Event
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
-- @param #string Name Name of the suppressed group
function MANTIS:onafterSeadSuppressionEnd(From, Event, To, Group, Name)
self:T({From, Event, To, Name})
self.SuppressedGroups[Name] = false
return self
end
--- [Internal] Function triggered by Event SeadSuppressionPlanned
-- @param #MANTIS self
-- @param #string From The From State
-- @param #string Event The Event
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
-- @param #string Name Name of the suppressed group
-- @param #number SuppressionStartTime Model start time of the suppression from `timer.getTime()`
-- @param #number SuppressionEndTime Model end time of the suppression from `timer.getTime()`
function MANTIS:onafterSeadSuppressionPlanned(From, Event, To, Group, Name, SuppressionStartTime, SuppressionEndTime)
self:T({From, Event, To, Name})
return self
end
end end
----------------------------------------------------------------------- -----------------------------------------------------------------------
-- MANTIS end -- MANTIS end

View File

@@ -742,7 +742,7 @@ function PSEUDOATC:ReportWeather(GID, UID, position, location)
local T=position:GetTemperature() local T=position:GetTemperature()
-- Correct unit system. -- Correct unit system.
local _T=string.format('%d°F', UTILS.CelciusToFarenheit(T)) local _T=string.format('%d°F', UTILS.CelsiusToFahrenheit(T))
if settings:IsMetric() then if settings:IsMetric() then
_T=string.format('%d°C', T) _T=string.format('%d°C', T)
end end
@@ -949,11 +949,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

View File

@@ -69,7 +69,7 @@
-- @field #string category Category of aircarft: "plane" or "heli". -- @field #string category Category of aircarft: "plane" or "heli".
-- @field #number groupsize Number of aircraft in group. -- @field #number groupsize Number of aircraft in group.
-- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red. -- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
-- @field #table ctable Table with the valid coalitons from choice self.friendly. -- @field #table ctable Table with the valid coalitions from choice self.friendly.
-- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...). -- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...).
-- @field #number Vcruisemax Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user. -- @field #number Vcruisemax Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user.
-- @field #number Vclimb Default climb rate in ft/min. -- @field #number Vclimb Default climb rate in ft/min.
@@ -348,7 +348,7 @@ RAT={
category = nil, -- Category of aircarft: "plane" or "heli". category = nil, -- Category of aircarft: "plane" or "heli".
groupsize=nil, -- Number of aircraft in the group. groupsize=nil, -- Number of aircraft in the group.
friendly = "same", -- Possible departure/destination airport: same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red, neutral. friendly = "same", -- Possible departure/destination airport: same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red, neutral.
ctable = {}, -- Table with the valid coalitons from choice self.friendly. ctable = {}, -- Table with the valid coalitions from choice self.friendly.
aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...). aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...).
Vcruisemax=nil, -- Max cruise speed in set by user. Vcruisemax=nil, -- Max cruise speed in set by user.
Vclimb=1500, -- Default climb rate in ft/min. Vclimb=1500, -- Default climb rate in ft/min.
@@ -657,7 +657,7 @@ end
-- @param #RAT self -- @param #RAT self
-- @param #number naircraft (Optional) Number of aircraft to spawn. Default is one aircraft. -- @param #number naircraft (Optional) Number of aircraft to spawn. Default is one aircraft.
-- @return #boolean True if spawning was successful or nil if nothing was spawned. -- @return #boolean True if spawning was successful or nil if nothing was spawned.
-- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coaliton. -- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coalition.
function RAT:Spawn(naircraft) function RAT:Spawn(naircraft)
-- Make sure that this function is only been called once per RAT object. -- Make sure that this function is only been called once per RAT object.
@@ -1289,7 +1289,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDestinationsFromZone(zone) function RAT:SetDestinationsFromZone(zone)
self:F2(zone) self:F2(zone)
@@ -1305,7 +1305,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDeparturesFromZone(zone) function RAT:SetDeparturesFromZone(zone)
self:F2(zone) self:F2(zone)

View File

@@ -19,7 +19,7 @@
-- * Bomb, rocket and missile impact points can be marked by smoke. -- * Bomb, rocket and missile impact points can be marked by smoke.
-- * Direct hits on targets can trigger flares. -- * Direct hits on targets can trigger flares.
-- * Smoke and flare colors can be adjusted for each player via radio menu. -- * Smoke and flare colors can be adjusted for each player via radio menu.
-- * Range information and weather report at the range can be reported via radio menu. -- * Range information and weather at the range can be obtained via radio menu.
-- * Persistence: Bombing range results can be saved to disk and loaded the next time the mission is started. -- * Persistence: Bombing range results can be saved to disk and loaded the next time the mission is started.
-- * Range control voice overs (>40) for hit assessment. -- * Range control voice overs (>40) for hit assessment.
-- --
@@ -50,11 +50,10 @@
-- @module Functional.Range -- @module Functional.Range
-- @image Range.JPG -- @image Range.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- RANGE class --- RANGE class
-- @type RANGE -- @type RANGE
-- @field #string ClassName Name of the Class. -- @field #string ClassName Name of the Class.
-- @field #boolean Debug If true, debug info is send as messages on the screen. -- @field #boolean Debug If true, debug info is sent as messages on the screen.
-- @field #boolean verbose Verbosity level. Higher means more output to DCS log file. -- @field #boolean verbose Verbosity level. Higher means more output to DCS log file.
-- @field #string id String id of range for output in DCS log. -- @field #string id String id of range for output in DCS log.
-- @field #string rangename Name of the range. -- @field #string rangename Name of the range.
@@ -77,13 +76,13 @@
-- @field #number Tmsg Time [sec] messages to players are displayed. Default 30 sec. -- @field #number Tmsg Time [sec] messages to players are displayed. Default 30 sec.
-- @field #string examinergroupname Name of the examiner group which should get all messages. -- @field #string examinergroupname Name of the examiner group which should get all messages.
-- @field #boolean examinerexclusive If true, only the examiner gets messages. If false, clients and examiner get messages. -- @field #boolean examinerexclusive If true, only the examiner gets messages. If false, clients and examiner get messages.
-- @field #number strafemaxalt Maximum altitude above ground for registering for a strafe run. Default is 914 m = 3000 ft. -- @field #number strafemaxalt Maximum altitude in meters AGL for registering for a strafe run. Default is 914 m = 3000 ft.
-- @field #number ndisplayresult Number of (player) results that a displayed. Default is 10. -- @field #number ndisplayresult Number of (player) results that a displayed. Default is 10.
-- @field Utilities.Utils#SMOKECOLOR BombSmokeColor Color id used for smoking bomb targets. -- @field Utilities.Utils#SMOKECOLOR BombSmokeColor Color id used for smoking bomb targets.
-- @field Utilities.Utils#SMOKECOLOR StrafeSmokeColor Color id used to smoke strafe targets. -- @field Utilities.Utils#SMOKECOLOR StrafeSmokeColor Color id used to smoke strafe targets.
-- @field Utilities.Utils#SMOKECOLOR StrafePitSmokeColor Color id used to smoke strafe pit approach boxes. -- @field Utilities.Utils#SMOKECOLOR StrafePitSmokeColor Color id used to smoke strafe pit approach boxes.
-- @field #number illuminationminalt Minimum altitude AGL in meters at which illumination bombs are fired. Default is 500 m. -- @field #number illuminationminalt Minimum altitude in meters AGL at which illumination bombs are fired. Default is 500 m.
-- @field #number illuminationmaxalt Maximum altitude AGL in meters at which illumination bombs are fired. Default is 1000 m. -- @field #number illuminationmaxalt Maximum altitude in meters AGL at which illumination bombs are fired. Default is 1000 m.
-- @field #number scorebombdistance Distance from closest target up to which bomb hits are counted. Default 1000 m. -- @field #number scorebombdistance Distance from closest target up to which bomb hits are counted. Default 1000 m.
-- @field #number TdelaySmoke Time delay in seconds between impact of bomb and starting the smoke. Default 3 seconds. -- @field #number TdelaySmoke Time delay in seconds between impact of bomb and starting the smoke. Default 3 seconds.
-- @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.
@@ -99,6 +98,9 @@
-- @field #string rangecontrolrelayname Name of relay unit. -- @field #string rangecontrolrelayname Name of relay unit.
-- @field #string instructorrelayname Name of relay unit. -- @field #string instructorrelayname Name of relay unit.
-- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/". -- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
-- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save.
-- @field #string targetpath Path where to save the target sheets.
-- @field #string targetprefix File prefix for target sheet files.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven --- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
@@ -131,12 +133,12 @@
-- --
-- A strafe pit can be added to the range by the @{#RANGE.AddStrafePit}(*targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*) function. -- A strafe pit can be added to the range by the @{#RANGE.AddStrafePit}(*targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*) function.
-- --
-- * The first parameter *targetnames* defines the target or targets. This has to be given as a lua table which contains the names of @{Wrapper.Unit} or @{Static} objects defined in the mission editor. -- * The first parameter *targetnames* defines the target or targets. This can be a single item or a Table with the name(s) of @{Wrapper.Unit} or @{Static} objects defined in the mission editor.
-- * In order to perform a valid pass on the strafe pit, the pilot has to begin his run from the correct direction. Therefore, an "approach box" is defined in front -- * In order to perform a valid pass on the strafe pit, the pilot has to begin his run from the correct direction. Therefore, an "approach box" is defined in front
-- of the strafe targets. The parameters *boxlength* and *boxwidth* define the size of the box while the parameter *heading* defines its direction. -- of the strafe targets. The parameters *boxlength* and *boxwidth* define the size of the box in meters, while the *heading* parameter defines the heading of the box FROM the target.
-- If the parameter *heading* is passed as **nil**, the heading is automatically taken from the heading of the first target unit as defined in the ME. -- For example, if heading 120 is set, the approach box will start FROM the target and extend outwards on heading 120. A strafe run approach must then be flown apx. heading 300 TOWARDS the target.
-- The parameter *inverseheading* turns the heading around by 180 degrees. This is sometimes useful, since the default heading of strafe target units point in the -- If the parameter *heading* is passed as **nil**, the heading is automatically taken from the heading set in the ME for the first target unit.
-- wrong/opposite direction. -- * The parameter *inverseheading* turns the heading around by 180 degrees. This is useful when the default heading of strafe target units point in the wrong/opposite direction.
-- * The parameter *goodpass* defines the number of hits a pilot has to achieve during a run to be judged as a "good" pass. -- * The parameter *goodpass* defines the number of hits a pilot has to achieve during a run to be judged as a "good" pass.
-- * The last parameter *foulline* sets the distance from the pit targets to the foul line. Hit from closer than this line are not counted! -- * The last parameter *foulline* sets the distance from the pit targets to the foul line. Hit from closer than this line are not counted!
-- --
@@ -150,9 +152,8 @@
-- --
-- One ore multiple bombing targets can be added to the range by the @{#RANGE.AddBombingTargets}(targetnames, goodhitrange, randommove) function. -- One ore multiple bombing targets can be added to the range by the @{#RANGE.AddBombingTargets}(targetnames, goodhitrange, randommove) function.
-- --
-- * The first parameter *targetnames* has to be a lua table, which contains the names of @{Wrapper.Unit} and/or @{Static} objects defined in the mission editor. -- * The first parameter *targetnames* defines the target or targets. This can be a single item or a Table with the name(s) of @{Wrapper.Unit} or @{Static} objects defined in the mission editor.
-- Note that the @{Range} logic **automatically** determines, if a name belongs to a @{Wrapper.Unit} or @{Static} object now. -- * The (optional) parameter *goodhitrange* specifies the radius in metres around the target within which a bomb/rocket hit is considered to be "good".
-- * The (optional) parameter *goodhitrange* specifies the radius around the target. If a bomb or rocket falls at a distance smaller than this number, the hit is considered to be "good".
-- * If final (optional) parameter "*randommove*" can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone. -- * If final (optional) parameter "*randommove*" can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone.
-- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired. -- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired.
-- --
@@ -188,7 +189,7 @@
-- --
-- The main range menu can be found at "F10. Other..." --> "F*X*. On the Range..." --> "F1. <Range Name>...". -- The main range menu can be found at "F10. Other..." --> "F*X*. On the Range..." --> "F1. <Range Name>...".
-- --
-- The range menu contains the following submenues: -- The range menu contains the following submenus:
-- --
-- ![Banner Image](..\Presentations\RANGE\Menu_Main.png) -- ![Banner Image](..\Presentations\RANGE\Menu_Main.png)
-- --
@@ -233,6 +234,16 @@
-- --
-- Strafing results are currently **not** saved. -- Strafing results are currently **not** saved.
-- --
-- # FSM Events
--
-- This class creates additional events that can be used by mission designers for custom reactions
--
-- * `EnterRange` when a player enters a range zone. See @{#RANGE.OnAfterEnterRange}
-- * `ExitRange` when a player leaves a range zone. See @{#RANGE.OnAfterExitRange}
-- * `Impact` on impact of a player's weapon on a bombing target. See @{#RANGE.OnAfterImpact}
-- * `RollingIn` when a player rolls in on a strafing target. See @{#RANGE.OnAfterRollingIn}
-- * `StrafeResult` when a player finishes a strafing run. See @{#RANGE.OnAfterStrafeResult}
--
-- # Examples -- # Examples
-- --
-- ## Goldwater Range -- ## Goldwater Range
@@ -255,9 +266,9 @@
-- -- Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME. -- -- Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME.
-- GoldwaterRange:GetFoullineDistance("GWR Strafe Pit Left 1", "GWR Foul Line Left") -- GoldwaterRange:GetFoullineDistance("GWR Strafe Pit Left 1", "GWR Foul Line Left")
-- --
-- -- Add strafe pits. Each pit (left and right) consists of two targets. -- -- Add strafe pits. Each pit (left and right) consists of two targets. Where "nil" is used as input, the default value is used.
-- GoldwaterRange:AddStrafePit(strafepit_left, 3000, 300, nil, true, 20, fouldist) -- GoldwaterRange:AddStrafePit(strafepit_left, 3000, 300, nil, true, 30, 500)
-- GoldwaterRange:AddStrafePit(strafepit_right, nil, nil, nil, true, nil, fouldist) -- GoldwaterRange:AddStrafePit(strafepit_right, nil, nil, nil, true, nil, 500)
-- --
-- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target. -- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target.
-- GoldwaterRange:AddBombingTargets(bombtargets, 50) -- GoldwaterRange:AddBombingTargets(bombtargets, 50)
@@ -267,6 +278,7 @@
-- --
-- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example. -- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example.
-- --
--
-- # Debugging -- # Debugging
-- --
-- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in -- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in
@@ -286,8 +298,6 @@
-- Note that it can happen that the RANGE radio menu is not shown. Check that the range object is defined as a **global** variable rather than a local one. -- Note that it can happen that the RANGE radio menu is not shown. Check that the range object is defined as a **global** variable rather than a local one.
-- The could avoid the lua garbage collection to accidentally/falsely deallocate the RANGE objects. -- The could avoid the lua garbage collection to accidentally/falsely deallocate the RANGE objects.
-- --
--
--
-- @field #RANGE -- @field #RANGE
RANGE = { RANGE = {
ClassName = "RANGE", ClassName = "RANGE",
@@ -333,7 +343,10 @@ RANGE={
instructor = nil, instructor = nil,
rangecontrolfreq = nil, rangecontrolfreq = nil,
rangecontrol = nil, rangecontrol = nil,
soundpath = "Range Soundfiles/" soundpath = "Range Soundfiles/",
targetsheet = nil,
targetpath = nil,
targetprefix = nil,
} }
--- Default range parameters. --- Default range parameters.
@@ -349,8 +362,7 @@ RANGE.Defaults={
boxlength = 3000, boxlength = 3000,
boxwidth = 300, boxwidth = 300,
goodpass = 20, goodpass = 20,
goodhitrange=25, foulline = 610
foulline=610,
} }
--- Target type, i.e. unit, static, or coordinate. --- Target type, i.e. unit, static, or coordinate.
@@ -361,7 +373,7 @@ RANGE.Defaults={
RANGE.TargetType = { RANGE.TargetType = {
UNIT = "Unit", UNIT = "Unit",
STATIC = "Static", STATIC = "Static",
COORD="Coordinate", COORD = "Coordinate"
} }
--- Player settings. --- Player settings.
@@ -398,6 +410,14 @@ RANGE.TargetType={
-- @field #number smokepoints Number of smoke points. -- @field #number smokepoints Number of smoke points.
-- @field #number heading Heading of pit. -- @field #number heading Heading of pit.
--- Strafe status for player.
-- @type RANGE.StrafeStatus
-- @field #number hits Number of hits on target.
-- @field #number time Number of times.
-- @field #number ammo Amount of ammo.
-- @field #boolean pastfoulline If `true`, player passed foul line. Invalid pass.
-- @field #RANGE.StrafeTarget zone Strafe target.
--- Bomb target result. --- Bomb target result.
-- @type RANGE.BombResult -- @type RANGE.BombResult
-- @field #string name Name of closest target. -- @field #string name Name of closest target.
@@ -410,6 +430,13 @@ RANGE.TargetType={
-- @field #number time Time via timer.getAbsTime() in seconds of impact. -- @field #number time Time via timer.getAbsTime() in seconds of impact.
-- @field #string date OS date. -- @field #string date OS date.
--- Strafe result.
-- @type RANGE.StrafeResult
-- @field #string player Player name.
-- @field #string airframe Aircraft type of player.
-- @field #number time Time via timer.getAbsTime() in seconds of impact.
-- @field #string date OS date.
--- Sound file data. --- Sound file data.
-- @type RANGE.Soundfile -- @type RANGE.Soundfile
-- @field #string filename Name of the file -- @field #string filename Name of the file
@@ -522,7 +549,7 @@ RANGE.MenuF10Root=nil
--- Range script version. --- Range script version.
-- @field #string version -- @field #string version
RANGE.version="2.3.0" RANGE.version = "2.4.0"
-- TODO list: -- TODO list:
-- TODO: Verbosity level for messages. -- TODO: Verbosity level for messages.
@@ -573,6 +600,8 @@ function RANGE:New(rangename)
self:AddTransition("Stopped", "Start", "Running") -- Start RANGE script. self:AddTransition("Stopped", "Start", "Running") -- Start RANGE script.
self:AddTransition("*", "Status", "*") -- Status of RANGE script. self:AddTransition("*", "Status", "*") -- Status of RANGE script.
self:AddTransition("*", "Impact", "*") -- Impact of bomb/rocket/missile. self:AddTransition("*", "Impact", "*") -- Impact of bomb/rocket/missile.
self:AddTransition("*", "RollingIn", "*") -- Player rolling in on strafe target.
self:AddTransition("*", "StrafeResult", "*") -- Strafe result of player.
self:AddTransition("*", "EnterRange", "*") -- Player enters the range. self:AddTransition("*", "EnterRange", "*") -- Player enters the range.
self:AddTransition("*", "ExitRange", "*") -- Player leaves the range. self:AddTransition("*", "ExitRange", "*") -- Player leaves the range.
self:AddTransition("*", "Save", "*") -- Save player results. self:AddTransition("*", "Save", "*") -- Save player results.
@@ -630,6 +659,37 @@ function RANGE:New(rangename)
-- @param #RANGE.BombResult result Data of the bombing run. -- @param #RANGE.BombResult result Data of the bombing run.
-- @param #RANGE.PlayerData player Data of player settings etc. -- @param #RANGE.PlayerData player Data of player settings etc.
--- Triggers the FSM event "RollingIn".
-- @function [parent=#RANGE] RollingIn
-- @param #RANGE self
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeTarget target Strafe target.
--- On after "RollingIn" event user function. Called when a player rolls in to a strafe taret.
-- @function [parent=#RANGE] OnAfterRollingIn
-- @param #RANGE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeTarget target Strafe target.
--- Triggers the FSM event "StrafeResult".
-- @function [parent=#RANGE] StrafeResult
-- @param #RANGE self
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeResult result Data of the strafing run.
--- On after "StrafeResult" event user function. Called when a player finished a strafing run.
-- @function [parent=#RANGE] OnAfterStrafeResult
-- @param #RANGE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #RANGE.PlayerData player Data of player settings etc.
-- @param #RANGE.StrafeResult result Data of the strafing run.
--- Triggers the FSM event "EnterRange". --- Triggers the FSM event "EnterRange".
-- @function [parent=#RANGE] EnterRange -- @function [parent=#RANGE] EnterRange
-- @param #RANGE self -- @param #RANGE self
@@ -833,7 +893,7 @@ end
--- Set maximal strafing altitude. Player entering a strafe pit above that altitude are not registered for a valid pass. --- Set maximal strafing altitude. Player entering a strafe pit above that altitude are not registered for a valid pass.
-- @param #RANGE self -- @param #RANGE self
-- @param #number maxalt Maximum altitude AGL in meters. Default is 914 m= 3000 ft. -- @param #number maxalt Maximum altitude in meters AGL. Default is 914 m = 3000 ft.
-- @return #RANGE self -- @return #RANGE self
function RANGE:SetMaxStrafeAlt( maxalt ) function RANGE:SetMaxStrafeAlt( maxalt )
self.strafemaxalt = maxalt or RANGE.Defaults.strafemaxalt self.strafemaxalt = maxalt or RANGE.Defaults.strafemaxalt
@@ -874,6 +934,22 @@ function RANGE:SetAutosaveOff()
return self return self
end end
--- Enable saving of player's target sheets and specify an optional directory path.
-- @param #RANGE self
-- @param #string path (Optional) Path where to save the target sheets.
-- @param #string prefix (Optional) Prefix for target sheet files. File name will be saved as *prefix_aircrafttype-0001.csv*, *prefix_aircrafttype-0002.csv*, etc.
-- @return #RANGE self
function RANGE:SetTargetSheet( path, prefix )
if io then
self.targetsheet = true
self.targetpath = path
self.targetprefix = prefix
else
self:E( self.lid .. "ERROR: io is not desanitized. Cannot save target sheet." )
end
return self
end
--- Set messages to examiner. The examiner will receive messages from all clients. --- Set messages to examiner. The examiner will receive messages from all clients.
-- @param #RANGE self -- @param #RANGE self
-- @param #string examinergroupname Name of the group of the examiner. -- @param #string examinergroupname Name of the group of the examiner.
@@ -1022,7 +1098,6 @@ function RANGE:SetMessagesON()
return self return self
end end
--- Enables tracking of all bomb types. Note that this is the default setting. --- Enables tracking of all bomb types. Note that this is the default setting.
-- @param #RANGE self -- @param #RANGE self
-- @return #RANGE self -- @return #RANGE self
@@ -1071,7 +1146,6 @@ function RANGE:TrackMissilesOFF()
return self return self
end end
--- Enable range control and set frequency. --- Enable range control and set frequency.
-- @param #RANGE self -- @param #RANGE self
-- @param #number frequency Frequency in MHz. Default 256 MHz. -- @param #number frequency Frequency in MHz. Default 256 MHz.
@@ -1096,7 +1170,7 @@ end
--- Set sound files folder within miz file. --- Set sound files folder within miz file.
-- @param #RANGE self -- @param #RANGE self
-- @param #string path Path for sound files. Default "ATIS Soundfiles/". Mind the slash "/" at the end! -- @param #string path Path for sound files. Default "Range Soundfiles/". Mind the slash "/" at the end!
-- @return #RANGE self -- @return #RANGE self
function RANGE:SetSoundfilesPath( path ) function RANGE:SetSoundfilesPath( path )
self.soundpath = tostring( path or "Range Soundfiles/" ) self.soundpath = tostring( path or "Range Soundfiles/" )
@@ -1105,16 +1179,16 @@ function RANGE:SetSoundfilesPath(path)
end end
--- Add new strafe pit. For a strafe pit, hits from guns are counted. One pit can consist of several units. --- Add new strafe pit. For a strafe pit, hits from guns are counted. One pit can consist of several units.
-- Note, an approach is only valid, if the player enters via a zone in front of the pit, which defined by boxlength and boxheading. -- A strafe run approach is only valid if the player enters via a zone in front of the pit, which is defined by boxlength, boxwidth, and heading.
-- Furthermore, the player must not be too high and fly in the direction of the pit to make a valid target apporoach. -- Furthermore, the player must not be too high and fly in the direction of the pit to make a valid target apporoach.
-- @param #RANGE self -- @param #RANGE self
-- @param #table targetnames Table of unit or static names defining the strafe targets. The first target in the list determines the approach zone (heading and box). -- @param #table targetnames Single or multiple (Table) unit or static names defining the strafe targets. The first target in the list determines the approach box origin (heading and box).
-- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m. -- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m.
-- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m. -- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m.
-- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor. -- @param #number heading (Optional) Approach box heading in degrees (originating FROM the target). Default is the heading set in the ME for the first target unit
-- @param #boolean inverseheading (Optional) Take inverse heading (heading --> heading - 180 Degrees). Default is false. -- @param #boolean inverseheading (Optional) Use inverse heading (heading --> heading - 180 Degrees). Default is false.
-- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20. -- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20.
-- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line. -- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default is 610 m = 2000 ft. Set to 0 for no foul line.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline ) function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline )
self:F( { targetnames = targetnames, boxlength = boxlength, boxwidth = boxwidth, heading = heading, inverseheading = inverseheading, goodpass = goodpass, foulline = foulline } ) self:F( { targetnames = targetnames, boxlength = boxlength, boxwidth = boxwidth, heading = heading, inverseheading = inverseheading, goodpass = goodpass, foulline = foulline } )
@@ -1244,7 +1318,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
return self return self
end end
--- Add all units of a group as one new strafe target pit. --- Add all units of a group as one new strafe target pit.
-- For a strafe pit, hits from guns are counted. One pit can consist of several units. -- For a strafe pit, hits from guns are counted. One pit can consist of several units.
-- Note, an approach is only valid, if the player enters via a zone in front of the pit, which defined by boxlength and boxheading. -- Note, an approach is only valid, if the player enters via a zone in front of the pit, which defined by boxlength and boxheading.
@@ -1288,7 +1361,7 @@ end
--- Add bombing target(s) to range. --- Add bombing target(s) to range.
-- @param #RANGE self -- @param #RANGE self
-- @param #table targetnames Table containing names of unit or static objects serving as bomb targets. -- @param #table targetnames Single or multiple (Table) names of unit or static objects serving as bomb targets.
-- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m.
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
@@ -1382,7 +1455,6 @@ function RANGE:AddBombingTargetUnit(unit, goodhitrange, randommove)
return self return self
end end
--- Add a coordinate of a bombing target. This --- Add a coordinate of a bombing target. This
-- @param #RANGE self -- @param #RANGE self
-- @param Core.Point#COORDINATE coord The coordinate. -- @param Core.Point#COORDINATE coord The coordinate.
@@ -1544,7 +1616,6 @@ function RANGE:onEvent(Event)
end end
--- Range event handler for event birth. --- Range event handler for event birth.
-- @param #RANGE self -- @param #RANGE self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -1573,7 +1644,6 @@ function RANGE:OnEventBirth(EventData)
self.strafeStatus[_uid] = nil self.strafeStatus[_uid] = nil
-- Add Menu commands after a delay of 0.1 seconds. -- Add Menu commands after a delay of 0.1 seconds.
--SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName ) self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
-- By default, some bomb impact points and do not flare each hit on target. -- By default, some bomb impact points and do not flare each hit on target.
@@ -1592,7 +1662,6 @@ function RANGE:OnEventBirth(EventData)
-- Start check in zone timer. -- Start check in zone timer.
if self.planes[_uid] ~= true then if self.planes[_uid] ~= true then
--SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 ) self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 )
self.planes[_uid] = true self.planes[_uid] = true
end end
@@ -1626,7 +1695,7 @@ function RANGE:OnEventHit(EventData)
local targetname = EventData.TgtUnitName local targetname = EventData.TgtUnitName
-- Current strafe target of player. -- Current strafe target of player.
local _currentTarget = self.strafeStatus[_unitID] local _currentTarget = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
-- Player has rolled in on a strafing target. -- Player has rolled in on a strafing target.
if _currentTarget and target:IsAlive() then if _currentTarget and target:IsAlive() then
@@ -1659,6 +1728,7 @@ function RANGE:OnEventHit(EventData)
self:_DisplayMessageToGroup( _unit, text ) self:_DisplayMessageToGroup( _unit, text )
self:T2( self.id .. text ) self:T2( self.id .. text )
_currentTarget.pastfoulline = true _currentTarget.pastfoulline = true
invalidStrafe = true -- Rangeboss Edit
end end
end end
@@ -1762,8 +1832,7 @@ function RANGE:OnEventShot(EventData)
local function trackBomb( _ordnance ) local function trackBomb( _ordnance )
-- When the pcall returns a failure the weapon has hit. -- When the pcall returns a failure the weapon has hit.
local _status,_bombPos = pcall( local _status, _bombPos = pcall( function()
function()
return _ordnance:getPoint() return _ordnance:getPoint()
end ) end )
@@ -1831,7 +1900,9 @@ function RANGE:OnEventShot(EventData)
_distance = _temp _distance = _temp
_closetTarget = _bombtarget _closetTarget = _bombtarget
_closeCoord = targetcoord _closeCoord = targetcoord
if _distance <= 0.5*_bombtarget.goodhitrange then if _distance <= 1.53 then -- Rangeboss Edit
_hitquality = "SHACK" -- Rangeboss Edit
elseif _distance <= 0.5 * _bombtarget.goodhitrange then -- Rangeboss Edit
_hitquality = "EXCELLENT" _hitquality = "EXCELLENT"
elseif _distance <= _bombtarget.goodhitrange then elseif _distance <= _bombtarget.goodhitrange then
_hitquality = "GOOD" _hitquality = "GOOD"
@@ -1864,6 +1935,10 @@ function RANGE:OnEventShot(EventData)
result.player = playerData.playername result.player = playerData.playername
result.time = timer.getAbsTime() result.time = timer.getAbsTime()
result.airframe = playerData.airframe result.airframe = playerData.airframe
result.roundsFired = 0 -- Rangeboss Edit
result.roundsHit = 0 -- Rangeboss Edit
result.roundsQuality = "N/A" -- Rangeboss Edit
result.rangename = self.rangename
-- Add to table. -- Add to table.
table.insert( _results, result ) table.insert( _results, result )
@@ -1940,7 +2015,6 @@ function RANGE:onafterStatus(From, Event, To)
text = text .. string.format( ", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring( self.rangecontrolrelayname ), alive ) text = text .. string.format( ", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring( self.rangecontrolrelayname ), alive )
end end
-- Check range status. -- Check range status.
self:I( self.id .. text ) self:I( self.id .. text )
@@ -1992,7 +2066,6 @@ function RANGE:onafterExitRange(From, Event, To, player)
end end
--- Function called after bomb impact on range. --- Function called after bomb impact on range.
-- @param #RANGE self -- @param #RANGE self
-- @param #string From From state. -- @param #string From From state.
@@ -2206,6 +2279,73 @@ function RANGE:onafterLoad(From, Event, To)
end end
end end
--- Save target sheet.
-- @param #RANGE self
-- @param #string _playername Player name.
-- @param #RANGE.StrafeResult result Results table.
function RANGE:_SaveTargetSheet( _playername, result ) -- RangeBoss Specific Function
--- Function that saves data to file
local function _savefile( filename, data )
local f = io.open( filename, "wb" )
if f then
f:write( data )
f:close()
else
env.info( "RANGEBOSS EDIT - could not save target sheet to file" )
-- self:E(self.lid..string.format("ERROR: could not save target sheet to file %s.\nFile may contain invalid characters.", tostring(filename)))
end
end
-- Set path or default.
local path = self.targetpath
if lfs then
path = path or lfs.writedir() .. [[Logs\]]
end
-- Create unused file name.
local filename = nil
for i = 1, 9999 do
-- Create file name
if self.targetprefix then
filename = string.format( "%s_%s-%04d.csv", self.targetprefix, result.airframe, i )
else
local name = UTILS.ReplaceIllegalCharacters( _playername, "_" )
filename = string.format( "RANGERESULTS-%s_Targetsheet-%s-%04d.csv", self.rangename, name, i )
end
-- Set path.
if path ~= nil then
filename = path .. "\\" .. filename
end
-- Check if file exists.
local _exists = UTILS.FileExists( filename )
if not _exists then
break
end
end
-- Header line
local data = "Name,Target,Rounds Fired,Rounds Hit,Rounds Quality,Airframe,Mission Time,OS Time\n"
local target = result.name
local airframe = result.airframe
local roundsFired = result.roundsFired
local roundsHit = result.roundsHit
local strafeResult = result.roundsQuality
local time = UTILS.SecondsToClock( result.time )
local date = "n/a"
if os then
date = os.date()
end
data = data .. string.format( "%s,%s,%d,%d,%s,%s,%s,%s", _playername, target, roundsFired, roundsHit, strafeResult, airframe, time, date )
-- Save file.
_savefile( filename, data )
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Display Messages -- Display Messages
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2240,7 +2380,9 @@ function RANGE:_DisplayMyStrafePitResults(_unitName)
else else
-- Sort results table wrt number of hits. -- Sort results table wrt number of hits.
local _sort = function( a,b ) return a.hits > b.hits end local _sort = function( a, b )
return a.roundsHit > b.roundsHit
end
table.sort( _results, _sort ) table.sort( _results, _sort )
-- Prepare message of best results. -- Prepare message of best results.
@@ -2249,13 +2391,14 @@ function RANGE:_DisplayMyStrafePitResults(_unitName)
-- Loop over results -- Loop over results
for _, _result in pairs( _results ) do for _, _result in pairs( _results ) do
local result=_result --#RANGE.StrafeResult
-- Message text. -- Message text.
_message = _message..string.format("\n[%d] Hits %d - %s - %s", _count, _result.hits, _result.zone.name, _result.text) _message = _message .. string.format( "\n[%d] Hits %d - %s - %s", _count, result.roundsHit, result.name, result.roundsQuality )
-- Best result. -- Best result.
if _bestMsg == "" then if _bestMsg == "" then
_bestMsg = string.format("Hits %d - %s - %s", _result.hits, _result.zone.name, _result.text) _bestMsg = string.format( "Hits %d - %s - %s", result.roundsHit, result.name, result.roundsQuality)
end end
-- 10 runs -- 10 runs
@@ -2300,21 +2443,23 @@ function RANGE:_DisplayStrafePitResults(_unitName)
-- Get the best result of the player. -- Get the best result of the player.
local _best = nil local _best = nil
for _, _result in pairs( _results ) do for _, _result in pairs( _results ) do
if _best == nil or _result.hits > _best.hits then if _best == nil or _result.roundsHit > _best.roundsHit then
_best = _result _best = _result
end end
end end
-- Add best result to table. -- Add best result to table.
if _best ~= nil then if _best ~= nil then
local text=string.format("%s: Hits %i - %s - %s", _playerName, _best.hits, _best.zone.name, _best.text) local text = string.format( "%s: Hits %i - %s - %s", _playerName, _best.roundsHit, _best.name, _best.roundsQuality )
table.insert(_playerResults,{msg = text, hits = _best.hits}) table.insert( _playerResults, { msg = text, hits = _best.roundsHit } )
end end
end end
-- Sort list! -- Sort list!
local _sort = function( a,b ) return a.hits > b.hits end local _sort = function( a, b )
return a.hits > b.hits
end
table.sort( _playerResults, _sort ) table.sort( _playerResults, _sort )
-- Add top 10 results. -- Add top 10 results.
@@ -2355,7 +2500,9 @@ function RANGE:_DisplayMyBombingResults(_unitName)
else else
-- Sort results wrt to distance. -- Sort results wrt to distance.
local _sort = function( a,b ) return a.distance < b.distance end local _sort = function( a, b )
return a.distance < b.distance
end
table.sort( _results, _sort ) table.sort( _results, _sort )
-- Loop over results. -- Loop over results.
@@ -2425,7 +2572,9 @@ function RANGE:_DisplayBombingResults(_unitName)
end end
-- Sort list of player results. -- Sort list of player results.
local _sort = function( a,b ) return a.distance < b.distance end local _sort = function( a, b )
return a.distance < b.distance
end
table.sort( _playerResults, _sort ) table.sort( _playerResults, _sort )
-- Loop over player results. -- Loop over player results.
@@ -2518,6 +2667,26 @@ function RANGE:_DisplayRangeInfo(_unitname)
text = text .. string.format( "Max strafing alt AGL: %s\n", tstrafemaxalt ) text = text .. string.format( "Max strafing alt AGL: %s\n", tstrafemaxalt )
text = text .. string.format( "# of strafe targets: %d\n", self.nstrafetargets ) text = text .. string.format( "# of strafe targets: %d\n", self.nstrafetargets )
text = text .. string.format( "# of bomb targets: %d\n", self.nbombtargets ) text = text .. string.format( "# of bomb targets: %d\n", self.nbombtargets )
if self.instructor then
local alive = "N/A"
if self.instructorrelayname then
local relay = UNIT:FindByName( self.instructorrelayname )
if relay then
alive = tostring( relay:IsAlive() )
end
end
text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive )
end
if self.rangecontrol then
local alive = "N/A"
if self.rangecontrolrelayname then
local relay = UNIT:FindByName( self.rangecontrolrelayname )
if relay then
alive = tostring( relay:IsAlive() )
end
end
text = text .. string.format( "Control %.3f MHz (Relay=%s)\n", self.rangecontrolfreq, alive )
end
text = text .. texthit text = text .. texthit
text = text .. textbomb text = text .. textbomb
text = text .. textdelay text = text .. textdelay
@@ -2570,7 +2739,7 @@ function RANGE:_DisplayBombTargets(_unitname)
end end
end end
self:_DisplayMessageToGroup(_unit,_text, 60, true, true) self:_DisplayMessageToGroup( _unit, _text, 120, true, true )
end end
end end
@@ -2614,7 +2783,6 @@ function RANGE:_DisplayStrafePits(_unitname)
end end
end end
--- Report weather conditions at range. Temperature, QFE pressure and wind data. --- Report weather conditions at range. Temperature, QFE pressure and wind data.
-- @param #RANGE self -- @param #RANGE self
-- @param #string _unitname Name of the player unit. -- @param #string _unitname Name of the player unit.
@@ -2655,12 +2823,11 @@ function RANGE:_DisplayRangeWeather(_unitname)
local tW = string.format( "%.1f m/s", Ws ) local tW = string.format( "%.1f m/s", Ws )
local tP = string.format( "%.1f mmHg", P * hPa2mmHg ) local tP = string.format( "%.1f mmHg", P * hPa2mmHg )
if settings:IsImperial() then if settings:IsImperial() then
--tT=string.format("%d°F", UTILS.CelciusToFarenheit(T)) -- tT=string.format("%d°F", UTILS.CelsiusToFahrenheit(T))
tW = string.format( "%.1f knots", UTILS.MpsToKnots( Ws ) ) tW = string.format( "%.1f knots", UTILS.MpsToKnots( Ws ) )
tP = string.format( "%.2f inHg", P * hPa2inHg ) tP = string.format( "%.2f inHg", P * hPa2inHg )
end end
-- Message text. -- Message text.
text = text .. string.format( "Weather Report at %s:\n", self.rangename ) text = text .. string.format( "Weather Report at %s:\n", self.rangename )
text = text .. string.format( "--------------------------------------------------\n" ) text = text .. string.format( "--------------------------------------------------\n" )
@@ -2733,9 +2900,13 @@ function RANGE:_CheckInZone(_unitName)
-- Get player unit and name. -- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
local unitheading = 0 -- RangeBoss
if _unit and _playername then if _unit and _playername then
-- Player data.
local playerData=self.PlayerSettings[_playername] -- #RANGE.PlayerData
--- Function to check if unit is in zone and facing in the right direction and is below the max alt. --- Function to check if unit is in zone and facing in the right direction and is below the max alt.
local function checkme( targetheading, _zone ) local function checkme( targetheading, _zone )
local zone = _zone -- Core.Zone#ZONE local zone = _zone -- Core.Zone#ZONE
@@ -2766,7 +2937,7 @@ function RANGE:_CheckInZone(_unitName)
local _unitID = _unit:GetID() local _unitID = _unit:GetID()
-- Currently strafing? (strafeStatus is nil if not) -- Currently strafing? (strafeStatus is nil if not)
local _currentStrafeRun = self.strafeStatus[_unitID] local _currentStrafeRun = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
if _currentStrafeRun then -- player has already registered for a strafing run. if _currentStrafeRun then -- player has already registered for a strafing run.
@@ -2778,7 +2949,6 @@ function RANGE:_CheckInZone(_unitName)
-- Check if player is in strafe zone and below max alt. -- Check if player is in strafe zone and below max alt.
if unitinzone then if unitinzone then
-- Still in zone, keep counting hits. Increase counter. -- Still in zone, keep counting hits. Increase counter.
_currentStrafeRun.time = _currentStrafeRun.time + 1 _currentStrafeRun.time = _currentStrafeRun.time + 1
@@ -2808,9 +2978,11 @@ function RANGE:_CheckInZone(_unitName)
local _ammo = self:_GetAmmo( _unitName ) local _ammo = self:_GetAmmo( _unitName )
-- Result. -- Result.
local _result = self.strafeStatus[_unitID] local _result = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
local _sound = nil -- #RANGE.Soundfile local _sound = nil -- #RANGE.Soundfile
--[[ --RangeBoss commented out in order to implement strafe quality based on accuracy percentage, not the number of rounds on target
-- Judge this pass. Text is displayed on summary. -- Judge this pass. Text is displayed on summary.
if _result.hits >= _result.zone.goodPass*2 then if _result.hits >= _result.zone.goodPass*2 then
_result.text = "EXCELLENT PASS" _result.text = "EXCELLENT PASS"
@@ -2825,13 +2997,40 @@ function RANGE:_CheckInZone(_unitName)
_result.text = "POOR PASS" _result.text = "POOR PASS"
_sound=RANGE.Sound.RCPoorPass _sound=RANGE.Sound.RCPoorPass
end end
]]
-- Calculate accuracy of run. Number of hits wrt number of rounds fired. -- Calculate accuracy of run. Number of hits wrt number of rounds fired.
local shots = _result.ammo - _ammo local shots = _result.ammo - _ammo
local accur = 0 local accur = 0
if shots > 0 then if shots > 0 then
accur = _result.hits / shots * 100 accur = _result.hits / shots * 100
if accur > 100 then accur = 100 end if accur > 100 then
accur = 100
end
end
-- Results text and sound message.
local resulttext=""
if _result.pastfoulline == true then --
resulttext = "* INVALID - PASSED FOUL LINE *"
_sound = RANGE.Sound.RCPoorPass --
else
if accur >= 90 then
resulttext = "DEADEYE PASS"
_sound = RANGE.Sound.RCExcellentPass
elseif accur >= 75 then
resulttext = "EXCELLENT PASS"
_sound = RANGE.Sound.RCExcellentPass
elseif accur >= 50 then
resulttext = "GOOD PASS"
_sound = RANGE.Sound.RCGoodPass
elseif accur >= 25 then
resulttext = "INEFFECTIVE PASS"
_sound = RANGE.Sound.RCIneffectivePass
else
resulttext = "POOR PASS"
_sound = RANGE.Sound.RCPoorPass
end
end end
-- Message text. -- Message text.
@@ -2839,11 +3038,32 @@ function RANGE:_CheckInZone(_unitName)
if shots and accur then if shots and accur then
_text = _text .. string.format( "\nTotal rounds fired %d. Accuracy %.1f %%.", shots, accur ) _text = _text .. string.format( "\nTotal rounds fired %d. Accuracy %.1f %%.", shots, accur )
end end
_text=_text..string.format("\n%s", _result.text) _text = _text .. string.format( "\n%s", resulttext )
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _text ) self:_DisplayMessageToGroup( _unit, _text )
-- Strafe result.
local result = {} -- #RANGE.StrafeResult
result.player=_playername
result.name=_result.zone.name or "unknown"
result.time = timer.getAbsTime()
result.roundsFired = shots
result.roundsHit = _result.hits
result.roundsQuality = resulttext
result.strafeAccuracy = accur
result.rangename = self.rangename
result.airframe=playerData.airframe
result.invalid = _result.pastfoulline
-- Griger Results.
self:StrafeResult(playerData, result)
-- Save trap sheet.
if playerData and playerData.targeton and self.targetsheet then
self:_SaveTargetSheet( _playername, result )
end
-- Voice over. -- Voice over.
if self.rangecontrol then if self.rangecontrol then
self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath ) self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath )
@@ -2863,7 +3083,7 @@ function RANGE:_CheckInZone(_unitName)
-- Save stats so the player can retrieve them. -- Save stats so the player can retrieve them.
local _stats = self.strafePlayerResults[_playername] or {} local _stats = self.strafePlayerResults[_playername] or {}
table.insert(_stats, _result) table.insert( _stats, result )
self.strafePlayerResults[_playername] = _stats self.strafePlayerResults[_playername] = _stats
end end
@@ -2873,12 +3093,13 @@ function RANGE:_CheckInZone(_unitName)
-- Check to see if we're in any of the strafing zones (first time). -- Check to see if we're in any of the strafing zones (first time).
for _, _targetZone in pairs( self.strafeTargets ) do for _, _targetZone in pairs( self.strafeTargets ) do
local target=_targetZone --#RANGE.StrafeTarget
-- Get the current approach zone and check if player is inside. -- Get the current approach zone and check if player is inside.
local zone=_targetZone.polygon --Core.Zone#ZONE_POLYGON_BASE local zone = target.polygon -- Core.Zone#ZONE_POLYGON_BASE
-- Check if unit in zone and facing the right direction. -- Check if unit in zone and facing the right direction.
local unitinzone=checkme(_targetZone.heading, zone) local unitinzone = checkme( target.heading, zone )
-- Player is inside zone. -- Player is inside zone.
if unitinzone then if unitinzone then
@@ -2887,10 +3108,10 @@ function RANGE:_CheckInZone(_unitName)
local _ammo = self:_GetAmmo( _unitName ) local _ammo = self:_GetAmmo( _unitName )
-- Init strafe status for this player. -- Init strafe status for this player.
self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false} self.strafeStatus[_unitID] = { hits = 0, zone = target, time = 1, ammo = _ammo, pastfoulline = false }
-- Rolling in! -- Rolling in!
local _msg=string.format("%s, rolling in on strafe pit %s.", self:_myname(_unitName), _targetZone.name) local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), target.name )
if self.rangecontrol then if self.rangecontrol then
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath ) self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
@@ -2899,6 +3120,9 @@ function RANGE:_CheckInZone(_unitName)
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _msg, 10, true ) self:_DisplayMessageToGroup( _unit, _msg, 10, true )
-- Trigger event that player is rolling in.
self:RollingIn(playerData, target)
-- We found our player. Skip remaining checks. -- We found our player. Skip remaining checks.
break break
@@ -2946,7 +3170,8 @@ function RANGE:_AddF10Commands(_unitName)
-- MISSION LEVEL -- -- MISSION LEVEL --
------------------- -------------------
_rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10Root) -- _rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10Root)
_rangePath = MENU_GROUP:New( group, "On the Range" )
else else
@@ -2956,55 +3181,57 @@ function RANGE:_AddF10Commands(_unitName)
-- Main F10 menu: F10/On the Range/<Range Name>/ -- Main F10 menu: F10/On the Range/<Range Name>/
if RANGE.MenuF10[_gid] == nil then if RANGE.MenuF10[_gid] == nil then
RANGE.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid, "On the Range") -- RANGE.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid, "On the Range")
RANGE.MenuF10[_gid] = MENU_GROUP:New( group, "On the Range" )
end end
_rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10[_gid]) -- _rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10[_gid])
_rangePath = MENU_GROUP:New( group, self.rangename, RANGE.MenuF10[_gid] )
end end
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Statistics", _rangePath)
local _markPath = missionCommands.addSubMenuForGroup(_gid, "Mark Targets", _rangePath)
local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _rangePath)
local _infoPath = missionCommands.addSubMenuForGroup(_gid, "Range Info", _rangePath)
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
local _mysmokePath = missionCommands.addSubMenuForGroup(_gid, "Smoke Color", _settingsPath) local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )
local _myflarePath = missionCommands.addSubMenuForGroup(_gid, "Flare Color", _settingsPath) local _myflarePath = MENU_GROUP:New( group, "Flare Color", _settingsPath )
-- F10/On the Range/<Range Name>/Mark Targets/ -- F10/On the Range/<Range Name>/Mark Targets/
missionCommands.addCommandForGroup(_gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName) local _MoMap = MENU_GROUP_COMMAND:New( group, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName) local _IllRng = MENU_GROUP_COMMAND:New( group, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName) local _SSpit = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName) local _SStgts = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName) local _SBtgts = MENU_GROUP_COMMAND:New( group, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName )
-- F10/On the Range/<Range Name>/Stats/ -- F10/On the Range/<Range Name>/Stats/
missionCommands.addCommandForGroup(_gid, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName) local _AllSR = MENU_GROUP_COMMAND:New( group, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName )
missionCommands.addCommandForGroup(_gid, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName) local _AllBR = MENU_GROUP_COMMAND:New( group, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName )
missionCommands.addCommandForGroup(_gid, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName) local _MySR = MENU_GROUP_COMMAND:New( group, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName )
missionCommands.addCommandForGroup(_gid, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName) local _MyBR = MENU_GROUP_COMMAND:New( group, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName) local _ResetST = MENU_GROUP_COMMAND:New( group, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName )
-- F10/On the Range/<Range Name>/My Settings/Smoke Color/ -- F10/On the Range/<Range Name>/My Settings/Smoke Color/
missionCommands.addCommandForGroup(_gid, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue) local _BlueSM = MENU_GROUP_COMMAND:New( group, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue )
missionCommands.addCommandForGroup(_gid, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green) local _GrSM = MENU_GROUP_COMMAND:New( group, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green )
missionCommands.addCommandForGroup(_gid, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange) local _OrSM = MENU_GROUP_COMMAND:New( group, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange )
missionCommands.addCommandForGroup(_gid, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red) local _ReSM = MENU_GROUP_COMMAND:New( group, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red )
missionCommands.addCommandForGroup(_gid, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White) local _WhSm = MENU_GROUP_COMMAND:New( group, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White )
-- F10/On the Range/<Range Name>/My Settings/Flare Color/ -- F10/On the Range/<Range Name>/My Settings/Flare Color/
missionCommands.addCommandForGroup(_gid, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green) local _GrFl = MENU_GROUP_COMMAND:New( group, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green )
missionCommands.addCommandForGroup(_gid, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red) local _ReFl = MENU_GROUP_COMMAND:New( group, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red )
missionCommands.addCommandForGroup(_gid, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White) local _WhFl = MENU_GROUP_COMMAND:New( group, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White )
missionCommands.addCommandForGroup(_gid, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow) local _YeFl = MENU_GROUP_COMMAND:New( group, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow )
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
missionCommands.addCommandForGroup(_gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName) local _SmDe = MENU_GROUP_COMMAND:New( group, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName) local _SmIm = MENU_GROUP_COMMAND:New( group, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName) local _FlHi = MENU_GROUP_COMMAND:New( group, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName )
missionCommands.addCommandForGroup(_gid, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName) local _AlMeA = MENU_GROUP_COMMAND:New( group, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName )
local _TrpSh = MENU_GROUP_COMMAND:New( group, "Targetsheet On/Off", _settingsPath, self._TargetsheetOnOff, self, _unitName )
-- F10/On the Range/<Range Name>/Range Information -- F10/On the Range/<Range Name>/Range Information
missionCommands.addCommandForGroup(_gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName) local _WeIn = MENU_GROUP_COMMAND:New( group, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName) local _WeRe = MENU_GROUP_COMMAND:New( group, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName) local _BoTgtgs = MENU_GROUP_COMMAND:New( group, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName )
missionCommands.addCommandForGroup(_gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName) local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
end end
else else
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName ) self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName )
@@ -3056,7 +3283,6 @@ function RANGE:_GetBombTargetCoordinate(target)
return coord return coord
end end
--- Get the number of shells a unit currently has. --- Get the number of shells a unit currently has.
-- @param #RANGE self -- @param #RANGE self
-- @param #string unitname Name of the player unit. -- @param #string unitname Name of the player unit.
@@ -3281,7 +3507,7 @@ function RANGE:_SmokeBombImpactOnOff(unitname)
self.PlayerSettings[playername].smokebombimpact = false self.PlayerSettings[playername].smokebombimpact = false
text = string.format( "%s, %s, smoking impact points of bombs is now OFF.", self.rangename, playername ) text = string.format( "%s, %s, smoking impact points of bombs is now OFF.", self.rangename, playername )
else else
self.PlayerSettigs[playername].smokebombimpact=true self.PlayerSettings[playername].smokebombimpact = true
text = string.format( "%s, %s, smoking impact points of bombs is now ON.", self.rangename, playername ) text = string.format( "%s, %s, smoking impact points of bombs is now ON.", self.rangename, playername )
end end
self:_DisplayMessageToGroup( unit, text, 5, false, true ) self:_DisplayMessageToGroup( unit, text, 5, false, true )
@@ -3302,7 +3528,7 @@ function RANGE:_SmokeBombDelayOnOff(unitname)
self.PlayerSettings[playername].delaysmoke = false self.PlayerSettings[playername].delaysmoke = false
text = string.format( "%s, %s, delayed smoke of bombs is now OFF.", self.rangename, playername ) text = string.format( "%s, %s, delayed smoke of bombs is now OFF.", self.rangename, playername )
else else
self.PlayerSettigs[playername].delaysmoke=true self.PlayerSettings[playername].delaysmoke = true
text = string.format( "%s, %s, delayed smoke of bombs is now ON.", self.rangename, playername ) text = string.format( "%s, %s, delayed smoke of bombs is now ON.", self.rangename, playername )
end end
self:_DisplayMessageToGroup( unit, text, 5, false, true ) self:_DisplayMessageToGroup( unit, text, 5, false, true )
@@ -3330,6 +3556,49 @@ function RANGE:_MessagesToPlayerOnOff(unitname)
end end
--- Targetsheet saves if player on or off.
-- @param #RANGE self
-- @param #string _unitname Name of the player unit.
function RANGE:_TargetsheetOnOff( _unitname )
self:F2( _unitname )
-- Get player unit and player name.
local unit, playername = self:_GetPlayerUnitAndName( _unitname )
-- Check if we have a player.
if unit and playername then
-- Player data.
local playerData = self.PlayerSettings[playername] -- #RANGE.PlayerData
if playerData then
-- Check if option is enabled at all.
local text = ""
if self.targetsheet then
-- Invert current setting.
playerData.targeton = not playerData.targeton
-- Inform player.
if playerData and playerData.targeton == true then
text = string.format( "roger, your targetsheets are now SAVED." )
else
text = string.format( "affirm, your targetsheets are NOT SAVED." )
end
else
text = "negative, target sheet data recorder is broken on this range."
end
-- Message to player.
-- self:MessageToPlayer(playerData, text, nil, playerData.name, 5)
self:_DisplayMessageToGroup( unit, text, 5, false, false )
end
end
end
--- Toggle status of flaring direct hits of range targets. --- Toggle status of flaring direct hits of range targets.
-- @param #RANGE self -- @param #RANGE self
-- @param #string unitname Name of the player unit. -- @param #string unitname Name of the player unit.
@@ -3582,7 +3851,7 @@ function RANGE:_GetPlayerUnitAndName(_unitName)
return nil, nil return nil, nil
end end
--- Returns a string which consits of this callsign and the player name. --- Returns a string which consists of the player name.
-- @param #RANGE self -- @param #RANGE self
-- @param #string unitname Name of the player unit. -- @param #string unitname Name of the player unit.
function RANGE:_myname( unitname ) function RANGE:_myname( unitname )
@@ -3590,7 +3859,7 @@ function RANGE:_myname(unitname)
local unit = UNIT:FindByName( unitname ) local unit = UNIT:FindByName( unitname )
local pname = unit:GetPlayerName() local pname = unit:GetPlayerName()
local csign=unit:GetCallsign() -- local csign = unit:GetCallsign()
-- return string.format("%s (%s)", csign, pname) -- return string.format("%s (%s)", csign, pname)
return string.format( "%s", pname ) return string.format( "%s", pname )

View File

@@ -67,7 +67,7 @@
-- --
-- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. -- * Upload scoring to a database or a BI tool to publish the scoring results to the player community.
-- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. -- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results.
-- * Share scoring amoung players after the mission to discuss mission results. -- * Share scoring among players after the mission to discuss mission results.
-- --
-- Scores can be **reported**. **Menu options** are automatically added to **each player group** when a player joins a client slot or a CA unit. -- Scores can be **reported**. **Menu options** are automatically added to **each player group** when a player joins a client slot or a CA unit.
-- Use the radio menu F10 to consult the scores while running the mission. -- Use the radio menu F10 to consult the scores while running the mission.
@@ -88,7 +88,6 @@
-- @module Functional.Scoring -- @module Functional.Scoring
-- @image Scoring.JPG -- @image Scoring.JPG
--- @type SCORING --- @type SCORING
-- @field Players A collection of the current players that have joined the game. -- @field Players A collection of the current players that have joined the game.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -99,7 +98,6 @@
-- --
-- local Scoring = SCORING:New( "Scoring File" ) -- local Scoring = SCORING:New( "Scoring File" )
-- --
--
-- # Set the destroy score or penalty scale: -- # Set the destroy score or penalty scale:
-- --
-- Score scales can be set for scores granted when enemies or friendlies are destroyed. -- Score scales can be set for scores granted when enemies or friendlies are destroyed.
@@ -147,11 +145,11 @@
-- --
-- # (Decommissioned) Configure fratricide level. -- # (Decommissioned) Configure fratricide level.
-- --
-- **This functionality is decomissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**. -- **This functionality is decommissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**.
-- --
-- When a player commits too much damage to friendlies, his penalty score will reach a certain level. -- When a player commits too much damage to friendlies, his penalty score will reach a certain level.
-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. -- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked.
-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. -- By default, the fratricide level is the default penalty multiplier * 2 for the penalty score.
-- --
-- # Penalty score when a player changes the coalition. -- # Penalty score when a player changes the coalition.
-- --
@@ -190,8 +188,8 @@
-- --
-- --Sanitize Mission Scripting environment -- --Sanitize Mission Scripting environment
-- --This makes unavailable some unsecure functions. -- --This makes unavailable some unsecure functions.
-- --Mission downloaded from server to client may contain potentialy harmful lua code that may use these functions. -- --Mission downloaded from server to client may contain potentially harmful lua code that may use these functions.
-- --You can remove the code below and make availble these functions at your own risk. -- --You can remove the code below and make available these functions at your own risk.
-- --
-- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization. -- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization.
-- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements. -- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements.
@@ -229,14 +227,12 @@ SCORING = {
Players = {}, Players = {},
} }
local _SCORINGCoalition = local _SCORINGCoalition = {
{
[1] = "Red", [1] = "Red",
[2] = "Blue", [2] = "Blue",
} }
local _SCORINGCategory = local _SCORINGCategory = {
{
[Unit.Category.AIRPLANE] = "Plane", [Unit.Category.AIRPLANE] = "Plane",
[Unit.Category.HELICOPTER] = "Helicopter", [Unit.Category.HELICOPTER] = "Helicopter",
[Unit.Category.GROUND_UNIT] = "Vehicle", [Unit.Category.GROUND_UNIT] = "Vehicle",
@@ -264,7 +260,6 @@ function SCORING:New( GameName )
error( "A game name must be given to register the scoring results" ) error( "A game name must be given to register the scoring results" )
end end
-- Additional Object scores -- Additional Object scores
self.ScoringObjects = {} self.ScoringObjects = {}
@@ -284,9 +279,11 @@ function SCORING:New( GameName )
-- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked).
self:SetFratricide( self.ScaleDestroyPenalty * 3 ) self:SetFratricide( self.ScaleDestroyPenalty * 3 )
self.penaltyonfratricide = true
-- Default penalty when a player changes coalition. -- Default penalty when a player changes coalition.
self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty )
self.penaltyoncoalitionchange = true
self:SetDisplayMessagePrefix() self:SetDisplayMessagePrefix()
@@ -301,15 +298,12 @@ function SCORING:New( GameName )
-- During mission startup, especially for single player, -- During mission startup, especially for single player,
-- iterate the database for the player that has joined, and add him to the scoring, and set the menu. -- iterate the database for the player that has joined, and add him to the scoring, and set the menu.
-- But this can only be started one second after the mission has started, so i need to schedule this ... -- But this can only be started one second after the mission has started, so i need to schedule this ...
self.ScoringPlayerScan = BASE:ScheduleOnce( 1, self.ScoringPlayerScan = BASE:ScheduleOnce( 1, function()
function()
for PlayerName, PlayerUnit in pairs( _DATABASE:GetPlayerUnits() ) do for PlayerName, PlayerUnit in pairs( _DATABASE:GetPlayerUnits() ) do
self:_AddPlayerFromUnit( PlayerUnit ) self:_AddPlayerFromUnit( PlayerUnit )
self:SetScoringMenu( PlayerUnit:GetGroup() ) self:SetScoringMenu( PlayerUnit:GetGroup() )
end end
end end )
)
-- Create the CSV file. -- Create the CSV file.
self:OpenCSV( GameName ) self:OpenCSV( GameName )
@@ -327,7 +321,6 @@ function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix )
return self return self
end end
--- Set the scale for scoring valid destroys (enemy destroys). --- Set the scale for scoring valid destroys (enemy destroys).
-- A default calculated score is a value between 1 and 10. -- A default calculated score is a value between 1 and 10.
-- The scale magnifies the scores given to the players. -- The scale magnifies the scores given to the players.
@@ -409,7 +402,6 @@ function SCORING:RemoveStaticScore( ScoreStatic )
return self return self
end end
--- Specify a special additional score for a @{Wrapper.Group}. --- Specify a special additional score for a @{Wrapper.Group}.
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Group#GROUP ScoreGroup The @{Wrapper.Group} for which each @{Wrapper.Unit} a Score is given. -- @param Wrapper.Group#GROUP ScoreGroup The @{Wrapper.Group} for which each @{Wrapper.Unit} a Score is given.
@@ -462,7 +454,6 @@ function SCORING:RemoveZoneScore( ScoreZone )
return self return self
end end
--- Configure to send messages after a target has been hit. --- Configure to send messages after a target has been hit.
-- @param #SCORING self -- @param #SCORING self
-- @param #boolean OnOff If true is given, the messages are sent. -- @param #boolean OnOff If true is given, the messages are sent.
@@ -569,10 +560,9 @@ function SCORING:IfMessagesToCoalition()
return self.MessagesAudience == 2 return self.MessagesAudience == 2
end end
--- When a player commits too much damage to friendlies, his penalty score will reach a certain level. --- When a player commits too much damage to friendlies, his penalty score will reach a certain level.
-- Use this method to define the level when a player gets kicked. -- Use this method to define the level when a player gets kicked.
-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. -- By default, the fratricide level is the default penalty multiplier * 2 for the penalty score.
-- @param #SCORING self -- @param #SCORING self
-- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. -- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked.
-- @return #SCORING -- @return #SCORING
@@ -582,6 +572,23 @@ function SCORING:SetFratricide( Fratricide )
return self return self
end end
--- Decide if fratricide is leading to penalties (true) or not (false)
-- @param #SCORING self
-- @param #boolean OnOff Switch for Fratricide
-- @return #SCORING
function SCORING:SwitchFratricide( OnOff )
self.penaltyonfratricide = OnOff
return self
end
--- Decide if a change of coalition is leading to penalties (true) or not (false)
-- @param #SCORING self
-- @param #boolean OnOff Switch for Coalition Changes.
-- @return #SCORING
function SCORING:SwitchTreason( OnOff )
self.penaltyoncoalitionchange = OnOff
return self
end
--- When a player changes the coalition, he can receive a penalty score. --- When a player changes the coalition, he can receive a penalty score.
-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. -- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition.
@@ -595,7 +602,6 @@ function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty )
return self return self
end end
--- Sets the scoring menu. --- Sets the scoring menu.
-- @param #SCORING self -- @param #SCORING self
-- @return #SCORING -- @return #SCORING
@@ -608,7 +614,6 @@ function SCORING:SetScoringMenu( ScoringGroup )
return self return self
end end
--- Add a new player entering a Unit. --- Add a new player entering a Unit.
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Unit#UNIT UnitData -- @param Wrapper.Unit#UNIT UnitData
@@ -647,15 +652,15 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if not self.Players[PlayerName].UnitCoalition then if not self.Players[PlayerName].UnitCoalition then
self.Players[PlayerName].UnitCoalition = UnitCoalition self.Players[PlayerName].UnitCoalition = UnitCoalition
else else
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then -- TODO: switch for coalition changes, make penalty alterable
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 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].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] .. 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). " ..
"(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", self.CoalitionChangePenalty .. "Penalty points added.",
MESSAGE.Type.Information MESSAGE.Type.Information )
):ToAll() :ToAll()
self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, 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() )
UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
end end
end end
@@ -667,29 +672,25 @@ function SCORING:_AddPlayerFromUnit( UnitData )
self.Players[PlayerName].ThreatLevel = UnitThreatLevel self.Players[PlayerName].ThreatLevel = UnitThreatLevel
self.Players[PlayerName].ThreatType = UnitThreatType self.Players[PlayerName].ThreatType = UnitThreatType
-- TODO: DCS bug concerning Units with skill level client don't get destroyed in multi player. This logic is deactivated until this bug gets fixed. -- TODO: make fratricide switchable
--[[ if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 and self.penaltyonfratricide then
if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then
if self.Players[PlayerName].PenaltyWarning < 1 then if self.Players[PlayerName].PenaltyWarning < 1 then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
MESSAGE.Type.Information MESSAGE.Type.Information )
):ToAll() :ToAll()
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
end end
end end
if self.Players[PlayerName].Penalty > self.Fratricide then if self.Players[PlayerName].Penalty > self.Fratricide and self.penaltyonfratricide then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
MESSAGE.Type.Information MESSAGE.Type.Information )
):ToAll() :ToAll()
UnitData:GetGroup():Destroy() UnitData:GetGroup():Destroy()
end end
--]]
end end
end end
--- Add a goal score for a player. --- Add a goal score for a player.
-- The method takes the Player name for which the Goal score needs to be set. -- The method takes the Player name for which the Goal score needs to be set.
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. -- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
@@ -712,14 +713,14 @@ function SCORING:AddGoalScorePlayer( PlayerName, 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
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( self.DisplayMessagePrefix .. Text,
MESSAGE.Type.Information )
:ToAll()
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil ) self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil )
end end
end end
--- Add a goal score for a player. --- Add a goal score for a player.
-- The method takes the PlayerUnit for which the Goal score needs to be set. -- The method takes the PlayerUnit for which the Goal score needs to be set.
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. -- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
@@ -744,13 +745,14 @@ 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
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( self.DisplayMessagePrefix .. Text,
MESSAGE.Type.Information )
:ToAll()
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
--- Registers Scores the players completing a Mission Task. --- Registers Scores the players completing a Mission Task.
-- @param #SCORING self -- @param #SCORING self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -780,7 +782,9 @@ 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
MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score, MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score,
MESSAGE.Type.Information )
:ToAll()
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
end end
@@ -841,17 +845,15 @@ 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
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. Score .. " mission score!",
Score .. " mission score!", MESSAGE.Type.Information )
MESSAGE.Type.Information ):ToAll() :ToAll()
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end end
end end
end end
--- Handles the OnPlayerEnterUnit event for the scoring. --- Handles the OnPlayerEnterUnit event for the scoring.
-- @param #SCORING self -- @param #SCORING self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
@@ -891,7 +893,6 @@ function SCORING:OnEventPlayerLeaveUnit( Event )
end end
end end
--- Handles the OnHit event for the scoring. --- Handles the OnHit event for the scoring.
-- @param #SCORING self -- @param #SCORING self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
@@ -949,7 +950,6 @@ function SCORING:_EventOnHit( Event )
self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } ) self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } )
end end
if Event.TgtDCSUnit then if Event.TgtDCSUnit then
TargetUnit = Event.TgtDCSUnit TargetUnit = Event.TgtDCSUnit
@@ -1023,21 +1023,15 @@ function SCORING:_EventOnHit( Event )
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 MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, "Penalty: -" .. PlayerHit.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 MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, "Penalty: -" .. PlayerHit.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
@@ -1047,33 +1041,26 @@ function SCORING:_EventOnHit( Event )
PlayerHit.Score = PlayerHit.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 MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. 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( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. 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( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end end
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.", 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() )
self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
end end
end end
@@ -1125,40 +1112,35 @@ 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 Player.Penalty = Player.Penalty + 10 -- * self.ScaleDestroyPenalty
PlayerHit.Penalty = PlayerHit.Penalty + 10 PlayerHit.Penalty = PlayerHit.Penalty + 10 -- * self.ScaleDestroyPenalty
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. PlayerHit.Penalty .. " = " .. 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() )
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 Player.Score = Player.Score + 1
PlayerHit.Score = PlayerHit.Score + 1 PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. PlayerHit.Score .. " = " .. 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() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.", 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() )
self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType )
end end
end end
@@ -1252,27 +1234,21 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. TargetDestroy.Penalty .. " = " .. 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 MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. TargetDestroy.Penalty .. " = " .. 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() )
end end
Destroyed = true
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
else else
local ThreatLevelTarget = TargetThreatLevel local ThreatLevelTarget = TargetThreatLevel
@@ -1286,39 +1262,33 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.Score = TargetDestroy.Score + ThreatScore TargetDestroy.Score = TargetDestroy.Score + ThreatScore
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 MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. TargetDestroy.Score .. " = " .. 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 MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. TargetDestroy.Score .. " = " .. 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() )
end end
Destroyed = true
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
local UnitName = TargetUnit:GetName() local UnitName = TargetUnit:GetName()
local Score = self.ScoringObjects[UnitName] local Score = self.ScoringObjects[UnitName]
if Score then if Score then
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
:NewType( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information MESSAGE.Type.Information )
)
:ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true Destroyed = true
end end
@@ -1331,13 +1301,12 @@ function SCORING:_EventOnDeadOrCrash( Event )
if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:NewType( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty,
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true Destroyed = true
end end
@@ -1353,16 +1322,14 @@ function SCORING:_EventOnDeadOrCrash( Event )
if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:NewType( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty,
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. MESSAGE.Type.Information )
"Total: " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
Destroyed = true
self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
Destroyed = true
end end
end end
end end
@@ -1378,7 +1345,6 @@ function SCORING:_EventOnDeadOrCrash( Event )
end end
end end
--- Produce detailed report of player hit scores. --- Produce detailed report of player hit scores.
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -1431,7 +1397,6 @@ function SCORING:ReportDetailedPlayerHits( PlayerName )
return ScoreMessage, PlayerScore, PlayerPenalty return ScoreMessage, PlayerScore, PlayerPenalty
end end
--- Produce detailed report of player destroy scores. --- Produce detailed report of player destroy scores.
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -1597,7 +1562,6 @@ function SCORING:ReportDetailedPlayerMissions( PlayerName )
return ScoreMessage, PlayerScore, PlayerPenalty return ScoreMessage, PlayerScore, PlayerPenalty
end end
--- Report Group Score Summary --- Report Group Score Summary
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Group#GROUP PlayerGroup The player group. -- @param Wrapper.Group#GROUP PlayerGroup The player group.
@@ -1637,8 +1601,7 @@ function SCORING:ReportScoreGroupSummary( 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 = PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
PlayerName, PlayerName,
PlayerScore - PlayerPenalty, PlayerScore - PlayerPenalty,
PlayerScore, PlayerScore,
@@ -1687,10 +1650,9 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
PlayerName, PlayerName,
PlayerScore - PlayerPenalty, PlayerScore - PlayerPenalty,
PlayerScore, PlayerScore,
@@ -1743,10 +1705,9 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
PlayerName, PlayerName,
PlayerScore - PlayerPenalty, PlayerScore - PlayerPenalty,
PlayerScore, PlayerScore,
@@ -1758,7 +1719,6 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
end end
function SCORING:SecondsToClock( sSeconds ) function SCORING:SecondsToClock( sSeconds )
local nSeconds = sSeconds local nSeconds = sSeconds
if nSeconds == 0 then if nSeconds == 0 then
@@ -1793,7 +1753,7 @@ function SCORING:OpenCSV( ScoringCSV )
error( "Error: Cannot open CSV file in " .. lfs.writedir() ) error( "Error: Cannot open CSV file in " .. lfs.writedir() )
end end
self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoalition","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' )
self.RunTime = os.date( "%y-%m-%d_%H-%M-%S" ) self.RunTime = os.date( "%y-%m-%d_%H-%M-%S" )
else else
@@ -1805,7 +1765,6 @@ function SCORING:OpenCSV( ScoringCSV )
return self return self
end end
--- Registers a score for a player. --- Registers a score for a player.
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -1888,7 +1847,6 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
end end
end end
function SCORING:CloseCSV() function SCORING:CloseCSV()
if lfs and io and os then if lfs and io and os then
self.CSVFile:close() self.CSVFile:close()

View File

@@ -6,6 +6,8 @@
-- --
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible. -- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars. -- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
-- * SEAD calculates the time it takes for a HARM to reach the target - and will attempt to minimize the shut-down time.
-- * Detection and evasion of shots has a random component based on the skill level of the SAM groups.
-- --
-- === -- ===
-- --
@@ -17,7 +19,7 @@
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Aug 2021 -- Last Update: Feb 2022
-- --
-- === -- ===
-- --
@@ -31,6 +33,10 @@
--- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
-- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
-- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
-- period of time to stay defensive, before it takes evasive actions.
-- --
-- # Constructor: -- # Constructor:
-- --
@@ -51,6 +57,9 @@ SEAD = {
SuppressedGroups = {}, SuppressedGroups = {},
EngagementRange = 75, -- default 75% engagement range Feature Request #1355 EngagementRange = 75, -- default 75% engagement range Feature Request #1355
Padding = 10, Padding = 10,
CallBack = nil,
UseCallBack = false,
debug = false,
} }
--- Missile enumerators --- Missile enumerators
@@ -68,6 +77,8 @@ SEAD = {
["X_25"] = "X_25", ["X_25"] = "X_25",
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
@@ -77,7 +88,7 @@ SEAD = {
["AGM_88"] = { 150, 3}, ["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2}, ["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3}, ["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.85}, ["AGM_84"] = { 280, 0.8},
["ALARM"] = { 45, 2}, ["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4}, ["LD-10"] = { 60, 4},
["X_58"] = { 70, 4}, ["X_58"] = { 70, 4},
@@ -85,6 +96,8 @@ SEAD = {
["X_25"] = { 25, 0.76}, ["X_25"] = { 25, 0.76},
["X_31"] = {150, 3}, ["X_31"] = {150, 3},
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn
["AGM_154"] = {130, 0.61},
} }
--- 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.
@@ -93,15 +106,15 @@ SEAD = {
-- @param #SEAD self -- @param #SEAD self
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken. -- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time -- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
-- @return SEAD -- @return #SEAD self
-- @usage -- @usage
-- -- CCCP SEAD Defenses -- -- CCCP SEAD Defenses
-- -- Defends the Russian SA installations from SEAD attacks. -- -- Defends the Russian SA installations from SEAD attacks.
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
function SEAD:New( SEADGroupPrefixes, Padding ) function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, FSM:New() )
self:F( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
@@ -114,14 +127,25 @@ function SEAD:New( SEADGroupPrefixes, Padding )
local padding = Padding or 10 local padding = Padding or 10
if padding < 10 then padding = 10 end if padding < 10 then padding = 10 end
self.Padding = padding self.Padding = padding
self.UseEmissionsOnOff = true
self.debug = false
self.CallBack = nil
self.UseCallBack = false
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.3.1") -- Start State.
self:SetStartState("Running")
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.3")
return self return self
end end
--- Update the active SEAD Set --- Update the active SEAD Set (while running)
-- @param #SEAD self -- @param #SEAD self
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string -- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
-- @return #SEAD self -- @return #SEAD self
@@ -142,8 +166,8 @@ end
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355 --- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
-- @param #SEAD self -- @param #SEAD self
-- @param #number range Set the engagement range in percent, e.g. 50 -- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
-- @return self -- @return #SEAD self
function SEAD:SetEngagementRange(range) function SEAD:SetEngagementRange(range)
self:T( { range } ) self:T( { range } )
range = range or 75 range = range or 75
@@ -157,7 +181,8 @@ end
--- Set the padding in seconds, which extends the radar off time calculated by SEAD --- Set the padding in seconds, which extends the radar off time calculated by SEAD
-- @param #SEAD self -- @param #SEAD self
-- @param #number Padding Extra number of seconds to add for the switch-on -- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
-- @return #SEAD self
function SEAD:SetPadding(Padding) function SEAD:SetPadding(Padding)
self:T( { Padding } ) self:T( { Padding } )
local padding = Padding or 10 local padding = Padding or 10
@@ -166,7 +191,31 @@ function SEAD:SetPadding(Padding)
return self return self
end end
--- Check if a known HARM was fired --- Set SEAD to use emissions on/off in addition to alarm state.
-- @param #SEAD self
-- @param #boolean Switch True for on, false for off.
-- @return #SEAD self
function SEAD:SwitchEmissions(Switch)
self:T({Switch})
self.UseEmissionsOnOff = Switch
return self
end
--- Add an object to call back when going evasive.
-- @param #SEAD self
-- @param #table Object The object to call. Needs to have object functions as follows:
-- `:SeadSuppressionPlanned(Group, Name, SuppressionStartTime, SuppressionEndTime)`
-- `:SeadSuppressionStart(Group, Name)`,
-- `:SeadSuppressionEnd(Group, Name)`,
-- @return #SEAD self
function SEAD:AddCallBack(Object)
self:T({Class=Object.ClassName})
self.CallBack = Object
self.UseCallBack = true
return self
end
--- (Internal) Check if a known HARM was fired
-- @param #SEAD self -- @param #SEAD self
-- @param #string WeaponName -- @param #string WeaponName
-- @return #boolean Returns true for a match -- @return #boolean Returns true for a match
@@ -176,7 +225,7 @@ end
local hit = false local hit = false
local name = "" local name = ""
for _,_name in pairs (SEAD.Harms) do for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then if string.find(WeaponName,_name,1,true) then
hit = true hit = true
name = _name name = _name
break break
@@ -212,47 +261,94 @@ end
end end
end end
--- Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- (Internal) Calculate hit zone of an AGM-88
-- @see SEAD -- @param #SEAD self
-- @param #SEAD -- @param #table SEADWeapon DCS.Weapon object
-- @param Core.Event#EVENTDATA EventData -- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
function SEAD:HandleEventShot( EventData ) -- @param #number height Height when the missile was fired
self:T( { EventData.id } ) -- @param Wrapper.Group#GROUP SEADGroup Attacker group
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT -- @param #string SEADWeaponName Weapon Name
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE -- @return #SEAD self
local SEADUnit = EventData.IniDCSUnit function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
local SEADUnitName = EventData.IniDCSUnitName self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
local SEADWeapon = EventData.Weapon -- Identify the weapon fired if SEADWeapon and SEADWeapon:isExist() then
local SEADWeaponName = EventData.WeaponName -- return weapon type --local pos = SEADWeapon:getPoint()
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) -- postion and height
--self:T({ SEADWeapon }) 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)
if self:_CheckHarms(SEADWeaponName) then -- velocity
self:T( '*** SEAD - Weapon Match' ) local wpndata = SEAD.HarmData["AGM_88"]
local _targetskill = "Random" if string.find(SEADWeaponName,"154",1) then
wpndata = SEAD.HarmData["AGM_154"]
end
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 = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce()
local tgtcoord = targetzone:GetRandomPointVec2()
--if tgtcoord and tgtcoord.ClassName == "COORDINATE" then
--local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
local tgtgrp = seadset:GetRandom()
local _targetgroup = nil
local _targetgroupname = "none" local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target local _targetskill = "Random"
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT if tgtgrp and tgtgrp:IsAlive() then
local _targetgroup = nil -- Wrapper.Group#GROUP _targetgroup = tgtgrp
if _targetUnit and _targetUnit:IsAlive() then _targetgroupname = tgtgrp:GetName() -- group name
_targetgroup = _targetUnit:GetGroup() _targetskill = tgtgrp:GetUnit(1):GetSkill()
_targetgroupname = _targetgroup:GetName() -- group name self:T("*** Found Target = ".. _targetgroupname)
local _targetUnitName = _targetUnit:GetName() self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end end
-- see if we are shot at --end
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' )
break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked end
return self
end
--- (Internal) Handle Evasion
-- @param #SEAD self
-- @param #string _targetskill
-- @param Wrapper.Group#GROUP _targetgroup
-- @param Core.Point#COORDINATE SEADPlanePos
-- @param #string SEADWeaponName
-- @param Wrapper.Group#GROUP SEADGroup Attacker Group
-- @param #number timeoffset Offset for tti calc
-- @return #SEAD self
function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset)
local timeoffset = timeoffset or 0
if _targetskill == "Random" then -- when skill is random, choose a skill if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" } local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ] _targetskill = Skills[ math.random(1,4) ]
@@ -276,7 +372,7 @@ function SEAD:HandleEventShot( EventData )
wpnspeed = math.floor(mach * 340.29) wpnspeed = math.floor(mach * 340.29)
end end
-- time to impact -- time to impact
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
if _distance > 0 then if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km _distance = math.floor(_distance / 1000) -- km
else else
@@ -291,36 +387,132 @@ function SEAD:HandleEventShot( EventData )
local function SuppressionStart(args) local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2])) self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateGreen() local name = args[2] -- #string Group Name
local attacker = args[3] -- Wrapper.Group#GROUP
if self.UseEmissionsOnOff then
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond") grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name,attacker)
end
end end
local function SuppressionStop(args) local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2])) self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Nam
if self.UseEmissionsOnOff then
grp:EnableEmission(true)
end
grp:OptionAlarmStateRed() grp:OptionAlarmStateRed()
grp:OptionEngageRange(self.EngagementRange) grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[args[2]] = false self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end end
-- randomize switch-on time -- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if delay > _tti then delay = delay / 2 end -- speed up if delay > _tti then delay = delay / 2 end -- speed up
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar if _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime
local SuppressionStartTime = timer.getTime() + delay local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding local SuppressionEndTime = timer.getTime() + _tti + self.Padding
local _targetgroupname = _targetgroup:GetName()
if not self.SuppressedGroups[_targetgroupname] then if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay)) self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime) timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname, SEADGroup},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime) timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime, SEADGroup)
end
end end
end end
end end
end end
return self
end
--- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @param #SEAD self
-- @param Core.Event#EVENTDATA EventData
-- @return #SEAD self
function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
--self:T({ SEADWeapon })
if self:_CheckHarms(SEADWeaponName) then
self:T( '*** SEAD - Weapon Match' )
local _targetskill = "Random"
local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target
if not _target or self.debug then -- AGM-88 or 154 w/o target data
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
self:I("**** Tracking AGM-88/154 with no target data.")
local pos0 = SEADPlane:GetCoordinate()
local fheight = SEADPlane:GetHeight()
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
end
return self
end
local targetcat = _target:getCategory() -- Identify category
local _targetUnit = nil -- Wrapper.Unit#UNIT
local _targetgroup = nil -- Wrapper.Group#GROUP
self:T(string.format("*** Targetcat = %d",targetcat))
if targetcat == Object.Category.UNIT then -- UNIT
self:T("*** Target Category UNIT")
_targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
if _targetUnit and _targetUnit:IsAlive() then
_targetgroup = _targetUnit:GetGroup()
_targetgroupname = _targetgroup:GetName() -- group name
local _targetUnitName = _targetUnit:GetName()
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end
elseif targetcat == Object.Category.STATIC then
self:T("*** Target Category STATIC")
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterOnce()
local targetpoint = _target:getPoint() or {x=0,y=0,z=0}
local tgtcoord = COORDINATE:NewFromVec3(targetpoint)
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
end end
end end
-- see if we are shot at
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' )
break
end
end
if SEADGroupFound == true then -- yes we are being attacked
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
end
end
return self
end end

View File

@@ -50,7 +50,7 @@
-- @field #boolean Report If true, send status messages to coalition. -- @field #boolean Report If true, send status messages to coalition.
-- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure. -- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure.
-- @field #string alias Alias of the warehouse. Name its called when sending messages. -- @field #string alias Alias of the warehouse. Name its called when sending messages.
-- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coaliton. -- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coalition.
-- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. -- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to.
-- @field #string airbasename Name of the airbase associated to the warehouse. -- @field #string airbasename Name of the airbase associated to the warehouse.
-- @field Core.Point#COORDINATE road Closest point to warehouse on road. -- @field Core.Point#COORDINATE road Closest point to warehouse on road.
@@ -764,7 +764,7 @@
-- warehouseBatumi:Load("D:\\My Warehouse Data\\") -- warehouseBatumi:Load("D:\\My Warehouse Data\\")
-- warehouseBatumi:Start() -- warehouseBatumi:Start()
-- --
-- This sequence loads all assets from file. If a warehouse was captured in the last mission, it also respawns the static warehouse structure with the right coaliton. -- This sequence loads all assets from file. If a warehouse was captured in the last mission, it also respawns the static warehouse structure with the right coalition.
-- However, it due to DCS limitations it is not possible to set the airbase coalition. This has to be done manually in the mission editor. Or alternatively, one could -- However, it due to DCS limitations it is not possible to set the airbase coalition. This has to be done manually in the mission editor. Or alternatively, one could
-- spawn some ground units via a self request and let them capture the airbase. -- spawn some ground units via a self request and let them capture the airbase.
-- --
@@ -1601,7 +1601,7 @@ WAREHOUSE = {
-- @field #number range Range of the unit in meters. -- @field #number range Range of the unit in meters.
-- @field #number speedmax Maximum speed in km/h the group can do. -- @field #number speedmax Maximum speed in km/h the group can do.
-- @field #number size Maximum size in length and with of the asset in meters. -- @field #number size Maximum size in length and with of the asset in meters.
-- @field #number weight The weight of the whole asset group in kilo gramms. -- @field #number weight The weight of the whole asset group in kilograms.
-- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field DCS#Object.Desc DCSdesc All DCS descriptors.
-- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group.
-- @field #table cargobay Array of cargo bays of all units in an asset group. -- @field #table cargobay Array of cargo bays of all units in an asset group.
@@ -1811,7 +1811,7 @@ WAREHOUSE.version="1.0.2"
-- DONE: Add shipping lanes between warehouses. -- DONE: Add shipping lanes between warehouses.
-- DONE: Handle cases with immobile units <== should be handled by dispatcher classes. -- DONE: Handle cases with immobile units <== should be handled by dispatcher classes.
-- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them?
-- DONE: Add general message function for sending to coaliton or debug. -- DONE: Add general message function for sending to coalition or debug.
-- DONE: Fine tune event handlers. -- DONE: Fine tune event handlers.
-- DONE: Improve generalized attributes. -- DONE: Improve generalized attributes.
-- DONE: If warehouse is destroyed, all asssets are gone. -- DONE: If warehouse is destroyed, all asssets are gone.
@@ -3155,7 +3155,7 @@ end
-- @param MinAssets (Optional) Minimum number of assets the warehouse should have. Default 0. -- @param MinAssets (Optional) Minimum number of assets the warehouse should have. Default 0.
-- @param #string Descriptor (Optional) Descriptor describing the selected assets which should be in stock. See @{#WAREHOUSE.Descriptor} for possible values. -- @param #string Descriptor (Optional) Descriptor describing the selected assets which should be in stock. See @{#WAREHOUSE.Descriptor} for possible values.
-- @param DescriptorValue (Optional) Descriptor value selecting the type of assets which should be in stock. -- @param DescriptorValue (Optional) Descriptor value selecting the type of assets which should be in stock.
-- @param DCS#Coalition.side Coalition (Optional) Coalition side of the warehouse. Default is the same coaliton as the present warehouse. Set to false for any coalition. -- @param DCS#Coalition.side Coalition (Optional) Coalition side of the warehouse. Default is the same coalition as the present warehouse. Set to false for any coalition.
-- @param Core.Point#COORDINATE RefCoordinate (Optional) Coordinate to which the closest warehouse is searched. Default is the warehouse calling this function. -- @param Core.Point#COORDINATE RefCoordinate (Optional) Coordinate to which the closest warehouse is searched. Default is the warehouse calling this function.
-- @return #WAREHOUSE The the nearest warehouse object. Or nil if no warehouse is found. -- @return #WAREHOUSE The the nearest warehouse object. Or nil if no warehouse is found.
-- @return #number The distance to the nearest warehouse in meters. Or nil if no warehouse is found. -- @return #number The distance to the nearest warehouse in meters. Or nil if no warehouse is found.
@@ -3267,7 +3267,7 @@ function WAREHOUSE:onafterStart(From, Event, To)
-- Short info. -- Short info.
local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias) local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias)
text=text..string.format("Coaliton = %s\n", self:GetCoalitionName()) text=text..string.format("Coalition = %s\n", self:GetCoalitionName())
text=text..string.format("Country = %s\n", self:GetCountryName()) text=text..string.format("Country = %s\n", self:GetCountryName())
text=text..string.format("Airbase = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory()) text=text..string.format("Airbase = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory())
env.info(text) env.info(text)
@@ -8460,7 +8460,7 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall)
end end
--- Create or update mark text at warehouse, which is displayed in F10 map showing how many assets of each type are in stock. --- Create or update mark text at warehouse, which is displayed in F10 map showing how many assets of each type are in stock.
-- Only the coaliton of the warehouse owner is able to see it. -- Only the coalition of the warehouse owner is able to see it.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @return #string Text about warehouse stock -- @return #string Text about warehouse stock
function WAREHOUSE:_UpdateWarehouseMarkText() function WAREHOUSE:_UpdateWarehouseMarkText()

View File

@@ -804,7 +804,7 @@ do -- ZONE_CAPTURE_COALITION
return IsEmpty return IsEmpty
end end
--- Check if zone is "Guarded", i.e. only one (the defending) coaliton is present inside the zone. --- Check if zone is "Guarded", i.e. only one (the defending) coalition is present inside the zone.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition ) -- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsGuarded() function ZONE_CAPTURE_COALITION:IsGuarded()
@@ -826,7 +826,7 @@ do -- ZONE_CAPTURE_COALITION
return IsCaptured return IsCaptured
end end
--- Check if zone is "Attacked", i.e. another coaliton entered the zone. --- Check if zone is "Attacked", i.e. another coalition entered the zone.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition ) -- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsAttacked() function ZONE_CAPTURE_COALITION:IsAttacked()
@@ -906,7 +906,6 @@ do -- ZONE_CAPTURE_COALITION
-- Misc Functions -- Misc Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Update Mark on F10 map. --- Update Mark on F10 map.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:Mark() function ZONE_CAPTURE_COALITION:Mark()
@@ -925,7 +924,7 @@ do -- ZONE_CAPTURE_COALITION
Coord:RemoveMark(self.MarkBlue) Coord:RemoveMark(self.MarkBlue)
end end
-- Create new marks for each coaliton. -- Create new marks for each coalition.
if self.Coalition == coalition.side.BLUE then if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State ) self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )

View File

@@ -24,7 +24,6 @@ do -- ZoneGoal
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS. -- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
-- @extends Functional.ZoneGoal#ZONE_GOAL -- @extends Functional.ZoneGoal#ZONE_GOAL
--- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition. --- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
-- Derived classes implement the ways how the achievements can be realized. -- Derived classes implement the ways how the achievements can be realized.
-- --
@@ -44,7 +43,7 @@ do -- ZoneGoal
ZONE_GOAL_COALITION = { ZONE_GOAL_COALITION = {
ClassName = "ZONE_GOAL_COALITION", ClassName = "ZONE_GOAL_COALITION",
Coalition = nil, Coalition = nil,
PreviousCoaliton = nil, PreviousCoalition = nil,
UnitCategories = nil, UnitCategories = nil,
ObjectCategories = nil, ObjectCategories = nil,
} }
@@ -61,7 +60,7 @@ do -- ZoneGoal
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
if not Zone then if not Zone then
BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!") BASE:E( "ERROR: No Zone specified in ZONE_GOAL_COALITION!" )
return nil return nil
end end
@@ -79,7 +78,6 @@ do -- ZoneGoal
return self return self
end end
--- Set the owning coalition of the zone. --- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*. -- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
@@ -127,14 +125,13 @@ do -- ZoneGoal
return self.Coalition return self.Coalition
end end
--- Get the previous coaliton, i.e. the one owning the zone before the current one. --- Get the previous coalition, i.e. the one owning the zone before the current one.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @return DCSCoalition.DCSCoalition#coalition Coalition. -- @return DCSCoalition.DCSCoalition#coalition Coalition.
function ZONE_GOAL_COALITION:GetPreviousCoalition() function ZONE_GOAL_COALITION:GetPreviousCoalition()
return self.PreviousCoalition return self.PreviousCoalition
end end
--- Get the owning coalition name of the zone. --- Get the owning coalition name of the zone.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @return #string Coalition name. -- @return #string Coalition name.
@@ -142,7 +139,6 @@ do -- ZoneGoal
return UTILS.GetCoalitionName( self.Coalition ) return UTILS.GetCoalitionName( self.Coalition )
end end
--- Check status Coalition ownership. --- Check status Coalition ownership.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @return #ZONE_GOAL_COALITION -- @return #ZONE_GOAL_COALITION

View File

@@ -4,6 +4,7 @@ __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/Core/Base.lua' ) __Moose.Include( 'Scripts/Moose/Core/Base.lua' )
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' ) __Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )

View File

@@ -45,10 +45,10 @@
-- === -- ===
-- --
-- ### 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.
@@ -67,7 +67,7 @@
-- @field #string activerunway The active runway specified by the user. -- @field #string activerunway The active runway specified by the user.
-- @field #number subduration Duration how long subtitles are displayed in seconds. -- @field #number subduration Duration how long subtitles are displayed in seconds.
-- @field #boolean metric If true, use metric units. If false, use imperial (default). -- @field #boolean metric If true, use metric units. If false, use imperial (default).
-- @field #boolean PmmHg If true, give pressure in millimeters of Mercury. Default is inHg for imperial and hecto Pascal (=mili Bars) for metric units. -- @field #boolean PmmHg If true, give pressure in millimeters of Mercury. Default is inHg for imperial and hectopascal (hPa, which is the same as millibar - mbar) for metric units.
-- @field #boolean qnhonly If true, suppresses reporting QFE. Default is to report both QNH and QFE. -- @field #boolean qnhonly If true, suppresses reporting QFE. Default is to report both QNH and QFE.
-- @field #boolean TDegF If true, give temperature in degrees Fahrenheit. Default is in degrees Celsius independent of chosen unit system. -- @field #boolean TDegF If true, give temperature in degrees Fahrenheit. Default is in degrees Celsius independent of chosen unit system.
-- @field #number zuludiff Time difference local vs. zulu in hours. -- @field #number zuludiff Time difference local vs. zulu in hours.
@@ -126,7 +126,7 @@
-- ## Subtitles -- ## Subtitles
-- --
-- Currently, DCS allows for displaying subtitles of radio transmissions only from airborne units, *i.e.* airplanes and helicopters. Therefore, if you want to have subtitles, it is necessary to place an -- Currently, DCS allows for displaying subtitles of radio transmissions only from airborne units, *i.e.* airplanes and helicopters. Therefore, if you want to have subtitles, it is necessary to place an
-- additonal aircraft on the ATIS airport and set it to uncontrolled. This unit can then function as a radio relay to transmit messages with subtitles. These subtitles will only be displayed, if the -- additional aircraft on the ATIS airport and set it to uncontrolled. This unit can then function as a radio relay to transmit messages with subtitles. These subtitles will only be displayed, if the
-- player has tuned in the correct ATIS frequency. -- player has tuned in the correct ATIS frequency.
-- --
-- Radio transmissions via an airborne unit can be set via the @{#ATIS.SetRadioRelayUnitName}(*unitname*) function, where the parameter *unitname* is the name of the unit passed as string, *e.g.* -- Radio transmissions via an airborne unit can be set via the @{#ATIS.SetRadioRelayUnitName}(*unitname*) function, where the parameter *unitname* is the name of the unit passed as string, *e.g.*
@@ -142,7 +142,7 @@
-- --
-- ## Active Runway -- ## Active Runway
-- --
-- By default, the currently active runway is determined automatically by analysing the wind direction. Therefore, you should obviously set the wind speed to be greater zero in your mission. -- By default, the currently active runway is determined automatically by analyzing the wind direction. Therefore, you should obviously set the wind speed to be greater zero in your mission.
-- --
-- Note however, there are a few special cases, where automatic detection does not yield the correct or desired result. -- Note however, there are a few special cases, where automatic detection does not yield the correct or desired result.
-- For example, there are airports with more than one runway facing in the same direction (usually denoted left and right). In this case, there is obviously no *unique* result depending on the wind vector. -- For example, there are airports with more than one runway facing in the same direction (usually denoted left and right). In this case, there is obviously no *unique* result depending on the wind vector.
@@ -170,7 +170,7 @@
-- --
-- ## Nav Aids -- ## Nav Aids
-- --
-- Frequencies or channels of navigation aids can be specified by the user and are then provided as additional information. Unfortunately, it is **not possible** to aquire this information via the DCS API -- Frequencies or channels of navigation aids can be specified by the user and are then provided as additional information. Unfortunately, it is **not possible** to acquire this information via the DCS API
-- we have access to. -- we have access to.
-- --
-- As they say, all road lead to Rome but (for me) the easiest way to obtain the available nav aids data of an airport, is to start a mission and click on an airport symbol. -- As they say, all road lead to Rome but (for me) the easiest way to obtain the available nav aids data of an airport, is to start a mission and click on an airport symbol.
@@ -239,7 +239,7 @@
-- --
-- atisBatumi:SetMetricUnits() -- atisBatumi:SetMetricUnits()
-- --
-- With this, wind speed is given in meters per second, pressure in hecto Pascal (mbar), visibility in kilometers etc. -- With this, wind speed is given in meters per second, pressure in hectopascal (hPa, which is the same as millibar - mbar), visibility in kilometers etc.
-- --
-- # Sound Files -- # Sound Files
-- --
@@ -259,7 +259,7 @@
-- --
-- # Text-To-Speech -- # Text-To-Speech
-- --
-- You can enable text-to-speech ATIS information with the @{#ATIS.SetSRS}() function. This uses [SRS](http://dcssimpleradio.com/) (Version >= 1.9.6.0) for broadcasing. -- You can enable text-to-speech ATIS information with the @{#ATIS.SetSRS}() function. This uses [SRS](http://dcssimpleradio.com/) (Version >= 1.9.6.0) for broadcasting.
-- Advantages are that **no sound files** or radio relay units are necessary. Also the issue that FC3 aircraft hear all transmissions will be circumvented. -- Advantages are that **no sound files** or radio relay units are necessary. Also the issue that FC3 aircraft hear all transmissions will be circumvented.
-- --
-- The @{#ATIS.SetSRS}() requires you to specify the path to the SRS install directory or more specifically the path to the DCS-SR-ExternalAudio.exe file. -- The @{#ATIS.SetSRS}() requires you to specify the path to the SRS install directory or more specifically the path to the DCS-SR-ExternalAudio.exe file.
@@ -416,7 +416,7 @@ ATIS.ICAOPhraseology={
PersianGulf = true, PersianGulf = true,
TheChannel = true, TheChannel = true,
Syria = true, Syria = true,
MarianaIslands=true, MarianaIslands = true
} }
--- Nav point data. --- Nav point data.
@@ -582,7 +582,6 @@ ATIS.Sound = {
Zulu = { filename = "Zulu.ogg", duration = 0.62 }, Zulu = { filename = "Zulu.ogg", duration = 0.62 },
} }
--- ATIS table containing all defined ATISes. --- ATIS table containing all defined ATISes.
-- @field #table _ATIS -- @field #table _ATIS
_ATIS = {} _ATIS = {}
@@ -680,7 +679,6 @@ function ATIS:New(airbasename, frequency, modulation)
-- @param #ATIS self -- @param #ATIS self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the ATIS. --- Triggers the FSM event "Stop". Stops the ATIS.
-- @function [parent=#ATIS] Stop -- @function [parent=#ATIS] Stop
-- @param #ATIS self -- @param #ATIS self
@@ -690,7 +688,6 @@ function ATIS:New(airbasename, frequency, modulation)
-- @param #ATIS self -- @param #ATIS self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "Status". --- Triggers the FSM event "Status".
-- @function [parent=#ATIS] Status -- @function [parent=#ATIS] Status
-- @param #ATIS self -- @param #ATIS self
@@ -700,7 +697,6 @@ function ATIS:New(airbasename, frequency, modulation)
-- @param #ATIS self -- @param #ATIS self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "Broadcast". --- Triggers the FSM event "Broadcast".
-- @function [parent=#ATIS] Broadcast -- @function [parent=#ATIS] Broadcast
-- @param #ATIS self -- @param #ATIS self
@@ -710,7 +706,6 @@ function ATIS:New(airbasename, frequency, modulation)
-- @param #ATIS self -- @param #ATIS self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "CheckQueue". --- Triggers the FSM event "CheckQueue".
-- @function [parent=#ATIS] CheckQueue -- @function [parent=#ATIS] CheckQueue
-- @param #ATIS self -- @param #ATIS self
@@ -720,7 +715,6 @@ function ATIS:New(airbasename, frequency, modulation)
-- @param #ATIS self -- @param #ATIS self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "Report". --- Triggers the FSM event "Report".
-- @function [parent=#ATIS] Report -- @function [parent=#ATIS] Report
-- @param #ATIS self -- @param #ATIS self
@@ -740,7 +734,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. -- Debug trace.
if false then if false then
self.Debug = true self.Debug = true
@@ -1114,7 +1107,6 @@ function ATIS:AddPRMG(channel, runway)
return self return self
end end
--- Place marks with runway data on the F10 map. --- Place marks with runway data on the F10 map.
-- @param #ATIS self -- @param #ATIS self
-- @param #boolean markall If true, mark all runways of the map. By default only the current ATIS runways are marked. -- @param #boolean markall If true, mark all runways of the map. By default only the current ATIS runways are marked.
@@ -1243,8 +1235,7 @@ function ATIS:onafterStatus(From, Event, To)
-- Info text. -- Info text.
local text = string.format( "State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName( self.modulation ) ) local text = string.format( "State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName( self.modulation ) )
if self.useSRS then if self.useSRS then
text=text..string.format(", SRS path=%s (%s), gender=%s, culture=%s, voice=%s", text = text .. string.format( ", SRS path=%s (%s), gender=%s, culture=%s, voice=%s", tostring( self.msrs.path ), tostring( self.msrs.port ), tostring( self.msrs.gender ), tostring( self.msrs.culture ), tostring( self.msrs.voice ) )
tostring(self.msrs.path), tostring(self.msrs.port), tostring(self.msrs.gender), tostring(self.msrs.culture), tostring(self.msrs.voice))
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
@@ -1277,8 +1268,6 @@ function ATIS:onafterCheckQueue(From, Event, To)
self:T2( self.lid .. string.format( "Radio queue %d transmissions queued.", #self.radioqueue.queue ) ) self:T2( self.lid .. string.format( "Radio queue %d transmissions queued.", #self.radioqueue.queue ) )
end end
end end
-- Check back in 5 seconds. -- Check back in 5 seconds.
@@ -1322,7 +1311,6 @@ function ATIS:onafterBroadcast(From, Event, To)
local Q = P / (1 + L * height / TS) ^ (-g * M / (R * L)) -- Altimeter QNH local Q = P / (1 + L * height / TS) ^ (-g * M / (R * L)) -- Altimeter QNH
local A = (T0 / L) * ((P / q) ^ (((-R * L) / (g * M))) - 1) -- Altitude check local A = (T0 / L) * ((P / q) ^ (((-R * L) / (g * M))) - 1) -- Altitude check
-- Debug aoutput -- Debug aoutput
self:T2( self.lid .. string.format( "height=%.1f, A=%.1f, T0=%.1f, QFE=%.1f, QNH=%.1f, P=%.1f, Q=%.1f hPa = %.2f", height, A, T0 - 273.15, qfe, qnh, P / 100, Q / 100, UTILS.hPa2inHg( Q / 100 ) ) ) self:T2( self.lid .. string.format( "height=%.1f, A=%.1f, T0=%.1f, QFE=%.1f, QNH=%.1f, P=%.1f, Q=%.1f hPa = %.2f", height, A, T0 - 273.15, qfe, qnh, P / 100, Q / 100, UTILS.hPa2inHg( Q / 100 ) ) )
@@ -1331,7 +1319,6 @@ function ATIS:onafterBroadcast(From, Event, To)
end end
-- Convert to inHg. -- Convert to inHg.
if self.PmmHg then if self.PmmHg then
qfe = UTILS.hPa2mmHg( qfe ) qfe = UTILS.hPa2mmHg( qfe )
@@ -1417,7 +1404,6 @@ function ATIS:onafterBroadcast(From, Event, To)
ZULU = string.format( "%s hours", zulu[1] ) ZULU = string.format( "%s hours", zulu[1] )
end end
-- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc. -- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc.
local NATO = ATIS.Alphabet[tonumber( zulu[1] ) + 1] local NATO = ATIS.Alphabet[tonumber( zulu[1] ) + 1]
@@ -1446,7 +1432,6 @@ function ATIS:onafterBroadcast(From, Event, To)
SUNSET = string.format( "%s %s hours", sunset[1], sunset[2] ) SUNSET = string.format( "%s %s hours", sunset[1], sunset[2] )
end end
--------------------------------- ---------------------------------
--- Temperature and Dew Point --- --- Temperature and Dew Point ---
--------------------------------- ---------------------------------
@@ -1459,8 +1444,8 @@ function ATIS:onafterBroadcast(From, Event, To)
-- Convert to °F. -- Convert to °F.
if self.TDegF then if self.TDegF then
temperature=UTILS.CelciusToFarenheit(temperature) temperature = UTILS.CelsiusToFahrenheit( temperature )
dewpoint=UTILS.CelciusToFarenheit(dewpoint) dewpoint = UTILS.CelsiusToFahrenheit( dewpoint )
end end
local TEMPERATURE = string.format( "%d", math.abs( temperature ) ) local TEMPERATURE = string.format( "%d", math.abs( temperature ) )
@@ -1755,6 +1740,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
@@ -2212,7 +2201,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 )
@@ -2280,7 +2269,7 @@ function ATIS:onafterReport(From, Event, To, Text)
local text = string.gsub( text, "°F", "degrees Fahrenheit" ) local text = string.gsub( text, "°F", "degrees Fahrenheit" )
local text = string.gsub( text, "inHg", "inches of Mercury" ) local text = string.gsub( text, "inHg", "inches of Mercury" )
local text = string.gsub( text, "mmHg", "millimeters of Mercury" ) local text = string.gsub( text, "mmHg", "millimeters of Mercury" )
local text=string.gsub(text, "hPa", "hecto Pascals") 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" )
-- Replace ";" by "." -- Replace ";" by "."
@@ -2572,7 +2561,6 @@ function ATIS:GetMissionWeather()
return clouds, visibility, turbulence, fog, dust, static return clouds, visibility, turbulence, fog, dust, static
end end
--- Get thousands of a number. --- Get thousands of a number.
-- @param #ATIS self -- @param #ATIS self
-- @param #number n Number, *e.g.* 4359. -- @param #number n Number, *e.g.* 4359.

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,14 @@
-- --
-- === -- ===
-- --
-- ## Missions:--- **Ops** -- Combat Search and Rescue.
--
-- ===
--
-- **CSAR** - MOOSE based Helicopter CSAR Operations.
--
-- ===
--
-- ## Missions: -- ## Missions:
-- --
-- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR) -- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR)
@@ -22,7 +30,7 @@
-- @module Ops.CSAR -- @module Ops.CSAR
-- @image OPS_CSAR.jpg -- @image OPS_CSAR.jpg
-- Date: Oct 2021 -- Date: June 2022
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@@ -45,6 +53,7 @@
-- * Object oriented refactoring of Ciribob\'s fantastic CSAR script. -- * Object oriented refactoring of Ciribob\'s fantastic CSAR script.
-- * No need for extra MIST loading. -- * No need for extra MIST loading.
-- * Additional events to tailor your mission. -- * Additional events to tailor your mission.
-- * Optional SpawnCASEVAC to create casualties without beacon (e.g. handling dead ground vehicles and create CASVAC requests).
-- --
-- ## 0. Prerequisites -- ## 0. Prerequisites
-- --
@@ -67,61 +76,69 @@
-- --
-- The following options are available (with their defaults). Only set the ones you want changed: -- The following options are available (with their defaults). Only set the ones you want changed:
-- --
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms. -- mycsar.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms.
-- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only! -- mycsar.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only!
-- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued. -- mycsar.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
-- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near. -- mycsar.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
-- self.autosmokedistance = 1000 -- distance for autosmoke -- mycsar.autosmokedistance = 1000 -- distance for autosmoke
-- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. -- mycsar.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
-- self.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well. -- mycsar.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well.
-- self.enableForAI = false -- set to false to disable AI pilots from being rescued. -- mycsar.enableForAI = false -- set to false to disable AI pilots from being rescued.
-- self.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to self.extractDistance in meters. -- mycsar.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to mycsar.extractDistance in meters.
-- self.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter. -- mycsar.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter.
-- self.immortalcrew = true -- Set to true to make wounded crew immortal. -- mycsar.immortalcrew = true -- Set to true to make wounded crew immortal.
-- self.invisiblecrew = false -- Set to true to make wounded crew insvisible. -- mycsar.invisiblecrew = false -- Set to true to make wounded crew insvisible.
-- self.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters. -- mycsar.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters.
-- self.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes. -- mycsar.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes.
-- self.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined. -- mycsar.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined.
-- self.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages. -- mycsar.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages.
-- self.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons. -- mycsar.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons.
-- self.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue. -- mycsar.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue.
-- self.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below. -- mycsar.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below.
-- self.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor! -- mycsar.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor!
-- self.verbose = 0 -- set to > 1 for stats output for debugging. -- mycsar.verbose = 0 -- set to > 1 for stats output for debugging.
-- -- (added 0.1.4) limit amount of downed pilots spawned by **ejection** events -- -- (added 0.1.4) limit amount of downed pilots spawned by **ejection** events
-- self.limitmaxdownedpilots = true -- mycsar.limitmaxdownedpilots = true
-- self.maxdownedpilots = 10 -- mycsar.maxdownedpilots = 10
-- -- (added 0.1.8) - allow to set far/near distance for approach and optionally pilot must open doors -- -- (added 0.1.8) - allow to set far/near distance for approach and optionally pilot must open doors
-- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters -- mycsar.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
-- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters -- mycsar.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
-- self.pilotmustopendoors = false -- switch to true to enable check of open doors -- mycsar.pilotmustopendoors = false -- switch to true to enable check of open doors
-- -- (added 0.1.9) -- -- (added 0.1.9)
-- self.suppressmessages = false -- switch off all messaging if you want to do your own -- mycsar.suppressmessages = false -- switch off all messaging if you want to do your own
-- -- (added 0.1.11) -- -- (added 0.1.11)
-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters -- mycsar.rescuehoverheight = 20 -- max height for a hovering rescue in meters
-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters -- mycsar.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters
-- -- (added 0.1.12) -- -- (added 0.1.12)
-- -- Country codes for spawned pilots -- -- Country codes for spawned pilots
-- self.countryblue= country.id.USA -- mycsar.countryblue= country.id.USA
-- self.countryred = country.id.RUSSIA -- mycsar.countryred = country.id.RUSSIA
-- self.countryneutral = country.id.UN_PEACEKEEPERS -- mycsar.countryneutral = country.id.UN_PEACEKEEPERS
-- --
-- ## 2.1 Experimental Features -- ## 2.1 Experimental Features
-- --
-- WARNING - Here\'ll be dragons! -- WARNING - Here\'ll be dragons!
-- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in <DCS root>\Scripts\MissionScripting.lua -- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in <DCS root>\Scripts\MissionScripting.lua
-- Needs SRS => 1.9.6 to work (works on the **server** side of SRS) -- Needs SRS => 1.9.6 to work (works on the **server** side of SRS)
-- self.useSRS = false -- Set true to use FF\'s SRS integration -- mycsar.useSRS = false -- Set true to use FF\'s SRS integration
-- self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!) -- mycsar.SRSPath = "C:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!)
-- self.SRSchannel = 300 -- radio channel -- mycsar.SRSchannel = 300 -- radio channel
-- self.SRSModulation = radio.modulation.AM -- modulation -- mycsar.SRSModulation = radio.modulation.AM -- modulation
-- mycsar.SRSport = 5002 -- and SRS Server port
-- mycsar.SRSCulture = "en-GB" -- SRS voice culture
-- mycsar.SRSVoice = nil -- SRS voice, relevant for Google TTS
-- mycsar.SRSGPathToCredentials = nil -- Path to your Google credentials json file, set this if you want to use Google TTS
-- mycsar.SRSVolume = 1 -- Volume, between 0 and 1
-- --
-- mycsar.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection. Requires mycsar.enableForAI to be set to true. --shagrat
-- mycsar.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
-- --
-- ## 3. Results -- ## 3. Results
-- --
-- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object: -- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object:
-- --
-- self.rescues -- number of successful landings *with* saved pilots -- mycsar.rescues -- number of successful landings *with* saved pilots
-- self.rescuedpilots -- aggregated number of pilots rescued from the field (of *all* players) -- mycsar.rescuedpilots -- aggregated number of pilots rescued from the field (of *all* players)
-- --
-- ## 4. Events -- ## 4. Events
-- --
@@ -148,7 +165,7 @@
-- --
-- The pilot has been boarded to the helicopter. Use e.g. `function my_csar:OnAfterBoarded(...)` to link into this event: -- The pilot has been boarded to the helicopter. Use e.g. `function my_csar:OnAfterBoarded(...)` to link into this event:
-- --
-- function my_csar:OnAfterBoarded(from, event, to, heliname, groupname) -- function my_csar:OnAfterBoarded(from, event, to, heliname, groupname, description)
-- ... your code here ... -- ... your code here ...
-- end -- end
-- --
@@ -175,6 +192,8 @@
-- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition -- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition
-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true ) -- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true )
-- --
-- --Create a casualty and CASEVAC request from a "Point" (VEC2) for the blue coalition --shagrat
-- my_csar:SpawnCASEVAC(Point, coalition.side.BLUE)
-- --
-- @field #CSAR -- @field #CSAR
CSAR = { CSAR = {
@@ -222,8 +241,9 @@ CSAR = {
-- @field #number frequency Frequency of the NDB. -- @field #number frequency Frequency of the NDB.
-- @field #string player Player name if applicable. -- @field #string player Player name if applicable.
-- @field Wrapper.Group#GROUP group Spawned group object. -- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process -- @field #number timestamp Timestamp for approach process.
-- @field #boolean alive Group is alive or dead/rescued -- @field #boolean alive Group is alive or dead/rescued.
-- @field #boolean wetfeet Group is spawned over (deep) water.
--- All slot / Limit settings --- All slot / Limit settings
-- @type CSAR.AircraftType -- @type CSAR.AircraftType
@@ -239,10 +259,12 @@ CSAR.AircraftType["Mi-8MT"] = 12
CSAR.AircraftType["Mi-24P"] = 8 CSAR.AircraftType["Mi-24P"] = 8
CSAR.AircraftType["Mi-24V"] = 8 CSAR.AircraftType["Mi-24V"] = 8
CSAR.AircraftType["Bell-47"] = 2 CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="0.1.11r2" CSAR.version="1.0.6"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -250,7 +272,7 @@ CSAR.version="0.1.11r2"
-- DONE: SRS Integration (to be tested) -- DONE: SRS Integration (to be tested)
-- TODO: Maybe - add option to smoke/flare closest MASH -- TODO: Maybe - add option to smoke/flare closest MASH
-- TODO: shagrat Add cargoWeight to helicopter when pilot boarded
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -311,6 +333,7 @@ function CSAR:New(Coalition, Template, Alias)
self:AddTransition("*", "Status", "*") -- CSAR status update. self:AddTransition("*", "Status", "*") -- CSAR status update.
self:AddTransition("*", "PilotDown", "*") -- Downed Pilot added self:AddTransition("*", "PilotDown", "*") -- Downed Pilot added
self:AddTransition("*", "Approach", "*") -- CSAR heli closing in. self:AddTransition("*", "Approach", "*") -- CSAR heli closing in.
self:AddTransition("*", "Landed", "*") -- CSAR heli landed
self:AddTransition("*", "Boarded", "*") -- Pilot boarded. self:AddTransition("*", "Boarded", "*") -- Pilot boarded.
self:AddTransition("*", "Returning", "*") -- CSAR able to return to base. self:AddTransition("*", "Returning", "*") -- CSAR able to return to base.
self:AddTransition("*", "Rescued", "*") -- Pilot at MASH. self:AddTransition("*", "Rescued", "*") -- Pilot at MASH.
@@ -351,6 +374,7 @@ function CSAR:New(Coalition, Template, Alias)
self.extractDistance = 500 -- Distance the Downed pilot will run to the rescue helicopter self.extractDistance = 500 -- Distance the Downed pilot will run to the rescue helicopter
self.loadtimemax = 135 -- seconds self.loadtimemax = 135 -- seconds
self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK! self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
self.beaconRefresher = 29 -- seconds
self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued. self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
self.max_units = 6 --max number of pilots that can be carried self.max_units = 6 --max number of pilots that can be carried
@@ -381,6 +405,13 @@ function CSAR:New(Coalition, Template, Alias)
self.countryred = country.id.RUSSIA self.countryred = country.id.RUSSIA
self.countryneutral = country.id.UN_PEACEKEEPERS self.countryneutral = country.id.UN_PEACEKEEPERS
-- added 0.1.3
self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection
-- added 0.1.4
self.wetfeettemplate = nil
self.usewetfeet = false
-- WARNING - here\'ll be dragons -- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua -- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
-- needs SRS => 1.9.6 to work (works on the *server* side) -- needs SRS => 1.9.6 to work (works on the *server* side)
@@ -388,6 +419,11 @@ function CSAR:New(Coalition, Template, Alias)
self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!) self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!)
self.SRSchannel = 300 -- radio channel self.SRSchannel = 300 -- radio channel
self.SRSModulation = radio.modulation.AM -- modulation self.SRSModulation = radio.modulation.AM -- modulation
self.SRSport = 5002 -- port
self.SRSCulture = "en-GB"
self.SRSVoice = nil
self.SRSGPathToCredentials = nil
self.SRSVolume = 1
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
@@ -439,6 +475,15 @@ function CSAR:New(Coalition, Template, Alias)
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot\'s group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
--- On After "Landed" event. Heli landed at an airbase.
-- @function [parent=#CSAR] OnAfterLanded
-- @param #CSAR self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string HeliName Name of the #UNIT which has landed.
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where the heli landed.
--- On After "Boarded" event. Downed pilot boarded heli. --- On After "Boarded" event. Downed pilot boarded heli.
-- @function [parent=#CSAR] OnAfterBoarded -- @function [parent=#CSAR] OnAfterBoarded
-- @param #CSAR self -- @param #CSAR self
@@ -447,6 +492,7 @@ function CSAR:New(Coalition, Template, Alias)
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot\'s group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
-- @param #string Description Descriptive name of the group.
--- On After "Returning" event. Heli can return home with downed pilot(s). --- On After "Returning" event. Heli can return home with downed pilot(s).
-- @function [parent=#CSAR] OnAfterReturning -- @function [parent=#CSAR] OnAfterReturning
@@ -492,8 +538,9 @@ end
-- @param #string Typename Typename of unit. -- @param #string Typename Typename of unit.
-- @param #number Frequency Frequency of the NDB in Hz -- @param #number Frequency Frequency of the NDB in Hz
-- @param #string Playername Name of Player (if applicable) -- @param #string Playername Name of Player (if applicable)
-- @param #boolean Wetfeet Ejected over water
-- @return #CSAR self. -- @return #CSAR self.
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername) function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername}) self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
-- create new entry -- create new entry
@@ -509,6 +556,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
DownedPilot.group = Group DownedPilot.group = Group
DownedPilot.timestamp = 0 DownedPilot.timestamp = 0
DownedPilot.alive = true DownedPilot.alive = true
DownedPilot.wetfeet = Wetfeet or false
-- Add Pilot -- Add Pilot
local PilotTable = self.downedPilots local PilotTable = self.downedPilots
@@ -558,17 +606,23 @@ end
-- @param #number country Country for template. -- @param #number country Country for template.
-- @param Core.Point#COORDINATE point Coordinate to spawn at. -- @param Core.Point#COORDINATE point Coordinate to spawn at.
-- @param #number frequency Frequency of the pilot's beacon -- @param #number frequency Frequency of the pilot's beacon
-- @param #boolean wetfeet Spawn is over water
-- @return Wrapper.Group#GROUP group The #GROUP object. -- @return Wrapper.Group#GROUP group The #GROUP object.
-- @return #string alias The alias name. -- @return #string alias The alias name.
function CSAR:_SpawnPilotInField(country,point,frequency) function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
self:T({country,point,frequency}) self:T({country,point,frequency,tostring(wetfeet)})
local freq = frequency or 1000 local freq = frequency or 1000
local freq = freq / 1000 -- kHz local freq = freq / 1000 -- kHz
for i=1,10 do for i=1,10 do
math.random(i,10000) math.random(i,10000)
end end
if point:IsSurfaceTypeWater() then point.y = 0 end if point:IsSurfaceTypeWater() or wetfeet then
point.y = 0
end
local template = self.template local template = self.template
if self.usewetfeet and wetfeet then
template = self.wetfeettemplate
end
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99)) local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
local coalition = self.coalition local coalition = self.coalition
local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA? local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA?
@@ -634,21 +688,31 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description}) self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
local template = self.template local template = self.template
local wetfeet = false
local surface = _point:GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
if not _freq then if not _freq then
_freq = self:_GenerateADFFrequency() _freq = self:_GenerateADFFrequency()
if not _freq then _freq = 333000 end --noob catch if not _freq then _freq = 333000 end --noob catch
end end
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq) local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
local _typeName = _typeName or "Pilot" local _typeName = _typeName or "Pilot"
if not noMessage then if not noMessage then
if _freq ~= 0 then --shagrat different CASEVAC msg
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, self.messageTime) self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, self.messageTime)
else
self:_DisplayToAllSAR("Troops In Contact. " .. _typeName .. " requests CASEVAC. ", self.coalition, self.messageTime)
end
end end
if _freq then if (_freq and _freq ~= 0) then --shagrat only add beacon if _freq is NOT 0
self:_AddBeaconToGroup(_spawnedGroup, _freq) self:_AddBeaconToGroup(_spawnedGroup, _freq)
end end
@@ -657,25 +721,33 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
local _text = _description local _text = _description
if not forcedesc then if not forcedesc then
if _playerName ~= nil then if _playerName ~= nil then
if _freq ~= 0 then --shagrat
_text = "Pilot " .. _playerName _text = "Pilot " .. _playerName
else
_text = "TIC - " .. _playerName
end
elseif _unitName ~= nil then elseif _unitName ~= nil then
if _freq ~= 0 then --shagrat
_text = "AI Pilot of " .. _unitName _text = "AI Pilot of " .. _unitName
else
_text = "TIC - " .. _unitName
end
end end
end end
self:T({_spawnedGroup, _alias}) self:T({_spawnedGroup, _alias})
local _GroupName = _spawnedGroup:GetName() or _alias local _GroupName = _spawnedGroup:GetName() or _alias
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName) self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
self:_InitSARForPilot(_spawnedGroup, _GroupName, _freq, noMessage) self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
return self return self
end end
--- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. --- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
-- @param #CSAR self -- @param #CSAR self
-- @param #string _zone Name of the zone. -- @param #string _zone Name of the zone. Can also be passed as a (normal, round) ZONE object.
-- @param #number _coalition Coalition. -- @param #number _coalition Coalition.
-- @param #string _description (optional) Description. -- @param #string _description (optional) Description.
-- @param #boolean _randomPoint (optional) Random yes or no. -- @param #boolean _randomPoint (optional) Random yes or no.
@@ -686,7 +758,16 @@ end
function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename, forcedesc) function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename, forcedesc)
self:T(self.lid .. " _SpawnCsarAtZone") self:T(self.lid .. " _SpawnCsarAtZone")
local freq = self:_GenerateADFFrequency() local freq = self:_GenerateADFFrequency()
local _triggerZone = ZONE:New(_zone) -- trigger to use as reference position
local _triggerZone = nil
if type(_zone) == "string" then
_triggerZone = ZONE:New(_zone) -- trigger to use as reference position
elseif type(_zone) == "table" and _zone.ClassName then
if string.find(_zone.ClassName, "ZONE",1) then
_triggerZone = _zone -- is already a zone
end
end
if _triggerZone == nil then if _triggerZone == nil then
self:E(self.lid.."ERROR: Can\'t find zone called " .. _zone, 10) self:E(self.lid.."ERROR: Can\'t find zone called " .. _zone, 10)
return return
@@ -720,7 +801,7 @@ end
--- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. --- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
-- @param #CSAR self -- @param #CSAR self
-- @param #string Zone Name of the zone. -- @param #string Zone Name of the zone. Can also be passed as a (normal, round) ZONE object.
-- @param #number Coalition Coalition. -- @param #number Coalition Coalition.
-- @param #string Description (optional) Description. -- @param #string Description (optional) Description.
-- @param #boolean RandomPoint (optional) Random yes or no. -- @param #boolean RandomPoint (optional) Random yes or no.
@@ -737,6 +818,58 @@ function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessa
return self return self
end end
--- (Internal) Function to add a CSAR object into the scene at a Point coordinate (VEC_2). For mission designers wanting to add e.g. casualties to the scene, that don't use beacons.
-- @param #CSAR self
-- @param #string _Point a POINT_VEC2.
-- @param #number _coalition Coalition.
-- @param #string _description (optional) Description.
-- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR.
-- @param #string unitname (optional) Name of the lost unit.
-- @param #string typename (optional) Type of plane.
-- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names.
function CSAR:_SpawnCASEVAC( _Point, _coalition, _description, _nomessage, unitname, typename, forcedesc) --shagrat added internal Function _SpawnCASEVAC
self:T(self.lid .. " _SpawnCASEVAC")
local _description = _description or "CASEVAC"
local unitname = unitname or "CASEVAC"
local typename = typename or "Ground Commander"
local pos = {}
pos = _Point
local _country = 0
if _coalition == coalition.side.BLUE then
_country = self.countryblue
elseif _coalition == coalition.side.RED then
_country = self.countryred
else
_country = self.countryneutral
end
--shagrat set frequency to 0 as "flag" for no beacon
self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, 0, _nomessage, _description, forcedesc)
return self
end
--- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
-- @param #CSAR self
-- @param #string Point a POINT_VEC2.
-- @param #number Coalition Coalition.
-- @param #string Description (optional) Description.
-- @param #boolean addBeacon (optional) yes or no.
-- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
-- @param #string Unitname (optional) Name of the lost unit.
-- @param #string Typename (optional) Type of plane.
-- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names.
-- @usage If missions designers want to spawn downed pilots into the field, e.g. at mission begin, to give the helicopter guys work, they can do this like so:
--
-- -- Create casualty "CASEVAC" at Point #POINT_VEC2 for the blue coalition.
-- my_csar:SpawnCASEVAC( POINT_VEC2, coalition.side.BLUE )
function CSAR:SpawnCASEVAC(Point, Coalition, Description, Nomessage, Unitname, Typename, Forcedesc)
self:_SpawnCASEVAC(Point, Coalition, Description, Nomessage, Unitname, Typename, Forcedesc)
return self
end --shagrat end added CASEVAC
--- (Internal) Event handler. --- (Internal) Event handler.
-- @param #CSAR self -- @param #CSAR self
function CSAR:_EventHandler(EventData) function CSAR:_EventHandler(EventData)
@@ -745,9 +878,14 @@ function CSAR:_EventHandler(EventData)
local _event = EventData -- Core.Event#EVENTDATA local _event = EventData -- Core.Event#EVENTDATA
-- no Player
if self.enableForAI == false and _event.IniPlayerName == nil then
return self
end
-- no event -- no event
if _event == nil or _event.initiator == nil then if _event == nil or _event.initiator == nil then
return false return self
-- take off -- take off
elseif _event.id == EVENTS.Takeoff then -- taken off elseif _event.id == EVENTS.Takeoff then -- taken off
@@ -755,35 +893,43 @@ function CSAR:_EventHandler(EventData)
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
if _event.IniGroupName then if _event.IniGroupName then
self.takenOff[_event.IniUnitName] = true self.takenOff[_event.IniUnitName] = true
end end
return true return self
-- player enter unit -- player enter unit
elseif _event.id == EVENTS.PlayerEnterAircraft or _event.id == EVENTS.PlayerEnterUnit then --player entered unit elseif _event.id == EVENTS.PlayerEnterAircraft or _event.id == EVENTS.PlayerEnterUnit then --player entered unit
self:T(self.lid .. " Event unit - Player Enter") self:T(self.lid .. " Event unit - Player Enter")
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
self:T("Coalition = "..UTILS.GetCoalitionName(_coalition))
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
if _event.IniPlayerName then if _event.IniPlayerName then
self.takenOff[_event.IniPlayerName] = nil self.takenOff[_event.IniPlayerName] = nil
end end
-- jumped into flying plane?
self:T("Taken Off: "..tostring(_event.IniUnit:InAir(true)))
if _event.IniUnit:InAir(true) then
self.takenOff[_event.IniPlayerName] = true
end
local _unit = _event.IniUnit local _unit = _event.IniUnit
local _group = _event.IniGroup local _group = _event.IniGroup
if _unit:IsHelicopter() or _group:IsHelicopter() then if _unit:IsHelicopter() or _group:IsHelicopter() then
self:_AddMedevacMenuItem() self:_AddMedevacMenuItem()
end end
return true return self
elseif (_event.id == EVENTS.PilotDead and self.csarOncrash == false) then elseif (_event.id == EVENTS.PilotDead and self.csarOncrash == false) then
-- Pilot dead -- Pilot dead
@@ -795,29 +941,29 @@ function CSAR:_EventHandler(EventData)
local _group = _event.IniGroup local _group = _event.IniGroup
if _unit == nil then if _unit == nil then
return -- error! return self -- error!
end end
local _coalition = _event.IniCoalition local _coalition = _event.IniCoalition
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! return self --ignore!
end end
-- Catch multiple events here? -- Catch multiple events here?
if self.takenOff[_event.IniUnitName] == true or _group:IsAirborne() then if self.takenOff[_event.IniUnitName] == true or _group:IsAirborne() then
if self:_DoubleEjection(_unitname) then if self:_DoubleEjection(_unitname) then
return return self
end end
else else
self:T(self.lid .. " Pilot has not taken off, ignore") self:T(self.lid .. " Pilot has not taken off, ignore")
end end
return return self
elseif _event.id == EVENTS.PilotDead or _event.id == EVENTS.Ejection then elseif _event.id == EVENTS.PilotDead or _event.id == EVENTS.Ejection then
if _event.id == EVENTS.PilotDead and self.csarOncrash == false then if _event.id == EVENTS.PilotDead and self.csarOncrash == false then
return return self
end end
self:T(self.lid .. " Event unit - Pilot Ejected") self:T(self.lid .. " Event unit - Pilot Ejected")
@@ -825,38 +971,71 @@ function CSAR:_EventHandler(EventData)
local _unitname = _event.IniUnitName local _unitname = _event.IniUnitName
local _group = _event.IniGroup local _group = _event.IniGroup
self:T({_unit.UnitName, _unitname, _group.GroupName})
if _unit == nil then if _unit == nil then
return -- error! self:T("Unit NIL!")
return self -- error!
end end
local _coalition = _unit:GetCoalition() --local _coalition = _unit:GetCoalition() -- nil now for some reason
local _coalition = _group:GetCoalition()
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! self:T("Wrong coalition! Coalition = "..UTILS.GetCoalitionName(_coalition))
return self --ignore!
end end
if self.enableForAI == false and _event.IniPlayerName == nil then
return self:T("Airborne: "..tostring(_group:IsAirborne()))
end self:T("Taken Off: "..tostring(self.takenOff[_event.IniUnitName]))
if not self.takenOff[_event.IniUnitName] and not _group:IsAirborne() then if not self.takenOff[_event.IniUnitName] and not _group:IsAirborne() then
self:T(self.lid .. " Pilot has not taken off, ignore") self:T(self.lid .. " Pilot has not taken off, ignore")
return -- give up, pilot hasnt taken off -- return self -- give up, pilot hasnt taken off
end end
if self:_DoubleEjection(_unitname) then if self:_DoubleEjection(_unitname) then
return self:T("Double Ejection!")
return self
end end
-- limit no of pilots in the field. -- limit no of pilots in the field.
if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then
return self:T("Maxed Downed Pilot!")
return self
end end
-- all checks passed, get going.
local _freq = self:_GenerateADFFrequency()
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return true -- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case
-- might create dual pilots in edge cases
local wetfeet = false
local initdcscoord = nil
local initcoord = nil
if _event.id == EVENTS.Ejection then
initdcscoord = _event.TgtDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
else
initdcscoord = _event.IniDCSUnit:getPoint()
initcoord = COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
end
--local surface = _unit:GetCoordinate():GetSurfaceType()
local surface = initcoord:GetSurfaceType()
if surface == land.SurfaceType.WATER then
self:T("Wet feet!")
wetfeet = true
end
-- all checks passed, get going.
if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
local _freq = self:_GenerateADFFrequency()
self:_AddCsar(_coalition, _unit:GetCountry(), initcoord , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return self
end
elseif _event.id == EVENTS.Land then elseif _event.id == EVENTS.Land then
self:T(self.lid .. " Landing") self:T(self.lid .. " Landing")
@@ -871,12 +1050,14 @@ function CSAR:_EventHandler(EventData)
if _unit == nil then if _unit == nil then
self:T(self.lid .. " Unit nil on landing") self:T(self.lid .. " Unit nil on landing")
return -- error! return self -- error!
end end
local _coalition = _event.IniCoalition --local _coalition = _event.IniCoalition
local _coalition = _event.IniGroup:GetCoalition()
if _coalition ~= self.coalition then if _coalition ~= self.coalition then
return --ignore! self:T(self.lid .. " Wrong coalition")
return self --ignore!
end end
self.takenOff[_event.IniUnitName] = nil self.takenOff[_event.IniUnitName] = nil
@@ -885,24 +1066,44 @@ function CSAR:_EventHandler(EventData)
if _place == nil then if _place == nil then
self:T(self.lid .. " Landing Place Nil") self:T(self.lid .. " Landing Place Nil")
return -- error! return self -- error!
end end
-- anyone on board? -- anyone on board?
if self.inTransitGroups[_event.IniUnitName] == nil then if self.inTransitGroups[_event.IniUnitName] == nil then
-- ignore -- ignore
return return self
end end
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
self:__Landed(2,_event.IniUnitName, _place)
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true) self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
else else
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition())) self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
end end
end end
return true return self
end end
---- shagrat on event LANDING_AFTER_EJECTION spawn pilot at parachute location
if (_event.id == EVENTS.LandingAfterEjection and self.csarUsePara == true) then
self:T("LANDING_AFTER_EJECTION")
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
local _unitname = "Aircraft" --_event.initiator:getName() or "Aircraft" --shagrat Optional use of Object name which is unfortunately 'f15_Pilot_Parachute'
local _typename = "Ejected Pilot" --_event.Initiator.getTypeName() or "Ejected Pilot"
local _country = _event.initiator:getCountry()
local _coalition = coalition.getCountryCoalition( _country )
self:T("Country = ".._country.." Coalition = ".._coalition)
if _coalition == self.coalition then
local _freq = self:_GenerateADFFrequency()
self:I({coalition=_coalition,country= _country, coord=_LandingPos, name=_unitname, player=_event.IniPlayerName, freq=_freq})
self:_AddCsar(_coalition, _country, _LandingPos, nil, _unitname, _event.IniPlayerName, _freq, false, "none")--shagrat add CSAR at Parachute location.
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
end
end
return self return self
end end
@@ -921,8 +1122,13 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
local _leadername = _leader:GetName() local _leadername = _leader:GetName()
if not _nomessage then if not _nomessage then
local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _leadername, _coordinatesText, _freqk) if _freq ~= 0 then --shagrat
local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _groupName, _coordinatesText, _freqk)--shagrat _groupName to prevent 'f15_Pilot_Parachute'
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime) self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
else --shagrat CASEVAC msg
local _text = string.format("Pickup Zone at %s.", _coordinatesText )
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
end
end end
for _,_heliName in pairs(self.csarUnits) do for _,_heliName in pairs(self.csarUnits) do
@@ -997,7 +1203,7 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
self.heliVisibleMessage[_lookupKeyHeli] = nil self.heliVisibleMessage[_lookupKeyHeli] = nil
self.heliCloseMessage[_lookupKeyHeli] = nil self.heliCloseMessage[_lookupKeyHeli] = nil
self.landedStatus[_lookupKeyHeli] = nil self.landedStatus[_lookupKeyHeli] = nil
self:T("...helinunit nil!") self:T("...heliunit nil!")
return return
end end
@@ -1026,9 +1232,9 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
local dist = UTILS.MetersToNM(self.autosmokedistance) local dist = UTILS.MetersToNM(self.autosmokedistance)
disttext = string.format("%.0fnm",dist) disttext = string.format("%.0fnm",dist)
end end
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", _heliName, _pilotName, disttext), self.messageTime,false,true) self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", _heliName, _pilotName, disttext), self.messageTime,false,true)
else else
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud!\nRequest a flare or smoke if you need.", _heliName, _pilotName), self.messageTime,false,true) self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nRequest a flare or smoke if you need.", _heliName, _pilotName), self.messageTime,false,true)
end end
--mark as shown for THIS heli and THIS group --mark as shown for THIS heli and THIS group
self.heliVisibleMessage[_lookupKeyHeli] = true self.heliVisibleMessage[_lookupKeyHeli] = true
@@ -1060,7 +1266,7 @@ function CSAR:_PopSmokeForGroup(_woundedGroupName, _woundedLeader)
if _lastSmoke == nil or timer.getTime() > _lastSmoke then if _lastSmoke == nil or timer.getTime() > _lastSmoke then
local _smokecolor = self.smokecolor local _smokecolor = self.smokecolor
local _smokecoord = _woundedLeader:GetCoordinate() local _smokecoord = _woundedLeader:GetCoordinate():Translate( 6, math.random( 1, 360) ) --shagrat place smoke at a random 6 m distance, so smoke does not obscure the pilot
_smokecoord:Smoke(_smokecolor) _smokecoord:Smoke(_smokecolor)
self.smokeMarkers[_woundedGroupName] = timer.getTime() + 300 -- next smoke time self.smokeMarkers[_woundedGroupName] = timer.getTime() + 300 -- next smoke time
end end
@@ -1092,8 +1298,8 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
_maxUnits = self.max_units _maxUnits = self.max_units
end end
if _unitsInHelicopter + 1 > _maxUnits then if _unitsInHelicopter + 1 > _maxUnits then
self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime) self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime,false,false,true)
return true return self
end end
local found,downedgrouptable = self:_CheckNameInDownedPilots(_woundedGroupName) local found,downedgrouptable = self:_CheckNameInDownedPilots(_woundedGroupName)
@@ -1112,9 +1318,9 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true) self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true)
self:__Boarded(5,_heliName,_woundedGroupName) self:__Boarded(5,_heliName,_woundedGroupName,grouptable.desc)
return true return self
end end
--- (Internal) Move group to destination. --- (Internal) Move group to destination.
@@ -1181,31 +1387,33 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
if _time == nil then if _time == nil then
self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 ) self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 )
_time = self.landedStatus[_lookupKeyHeli] _time = self.landedStatus[_lookupKeyHeli]
_woundedGroup:OptionAlarmStateGreen()
self:_OrderGroupToMoveToPoint(_woundedGroup, _heliUnit:GetCoordinate()) self:_OrderGroupToMoveToPoint(_woundedGroup, _heliUnit:GetCoordinate())
self:_DisplayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. " gets in. \nETA " .. _time .. " more seconds.", self.messageTime, false) self:_DisplayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. " gets in. \nETA " .. _time .. " more seconds.", self.messageTime, false)
else else
_time = self.landedStatus[_lookupKeyHeli] - 10 _time = self.landedStatus[_lookupKeyHeli] - 10
self.landedStatus[_lookupKeyHeli] = _time self.landedStatus[_lookupKeyHeli] = _time
end end
if _time <= 0 or _distance < self.loadDistance then --if _time <= 0 or _distance < self.loadDistance then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if _distance < self.loadDistance + 5 or _distance <= 13 then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) if self.pilotmustopendoors and (self:_IsLoadingDoorOpen(_heliName) == false) then
return true self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true)
return false
else else
self.landedStatus[_lookupKeyHeli] = nil self.landedStatus[_lookupKeyHeli] = nil
self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
return false return true
end end
end end
end end
else else
if (_distance < self.loadDistance) then if (_distance < self.loadDistance) then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and (self:_IsLoadingDoorOpen(_heliName) == false) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true)
return true return false
else else
self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
return false return true
end end
end end
end end
@@ -1242,18 +1450,19 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
if _time > 0 then if _time > 0 then
self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true)
else else
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and (self:_IsLoadingDoorOpen(_heliName) == false) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true)
return true return false
else else
self.hoverStatus[_lookupKeyHeli] = nil self.hoverStatus[_lookupKeyHeli] = nil
self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
return false return true
end end
end end
_reset = false _reset = false
else else
self:_DisplayMessageToSAR(_heliUnit, "Too high to winch " .. _pilotName .. " \nReduce height and hover for 10 seconds!", self.messageTime, true,true) self:_DisplayMessageToSAR(_heliUnit, "Too high to winch " .. _pilotName .. " \nReduce height and hover for 10 seconds!", self.messageTime, true,true)
return false
end end
end end
@@ -1302,7 +1511,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname, isairport)
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true, true)
else else
self:_RescuePilots(_heliUnit) self:_RescuePilots(_heliUnit)
return return
@@ -1360,12 +1569,13 @@ end
-- @param #number _time Message show duration. -- @param #number _time Message show duration.
-- @param #boolean _clear (optional) Clear screen. -- @param #boolean _clear (optional) Clear screen.
-- @param #boolean _speak (optional) Speak message via SRS. -- @param #boolean _speak (optional) Speak message via SRS.
function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak) -- @param #boolean _override (optional) Override message suppression
function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _override)
self:T(self.lid .. " _DisplayMessageToSAR") self:T(self.lid .. " _DisplayMessageToSAR")
local group = _unit:GetGroup() local group = _unit:GetGroup()
local _clear = _clear or nil local _clear = _clear or nil
local _time = _time or self.messageTime local _time = _time or self.messageTime
if not self.suppressmessages then if _override or not self.suppressmessages then
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group) local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
end end
-- integrate SRS -- integrate SRS
@@ -1375,6 +1585,15 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
local modulation = self.SRSModulation local modulation = self.SRSModulation
local channel = self.SRSchannel local channel = self.SRSchannel
local msrs = MSRS:New(path,channel,modulation) local msrs = MSRS:New(path,channel,modulation)
msrs:SetPort(self.SRSport)
msrs:SetLabel("CSAR")
msrs:SetCulture(self.SRSCulture)
msrs:SetCoalition(self.coalition)
msrs:SetVoice(self.SRSVoice)
if self.SRSGPathToCredentials then
msrs:SetGoogle(self.SRSGPathToCredentials)
end
msrs:SetVolume(self.SRSVolume)
msrs:PlaySoundText(srstext, 2) msrs:PlaySoundText(srstext, 2)
end end
return self return self
@@ -1435,9 +1654,13 @@ function CSAR:_DisplayActiveSAR(_unitName)
else else
distancetext = string.format("%.1fkm", _distance/1000.0) distancetext = string.format("%.1fkm", _distance/1000.0)
end end
if _value.frequency == 0 then--shagrat insert CASEVAC without Frequency
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %s ", _value.desc, _coordinatesText, distancetext) })
else
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) }) table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
end end
end end
end
local function sortDistance(a, b) local function sortDistance(a, b)
return a.dist < b.dist return a.dist < b.dist
@@ -1449,7 +1672,7 @@ function CSAR:_DisplayActiveSAR(_unitName)
_msg = _msg .. "\n" .. _line.msg _msg = _msg .. "\n" .. _line.msg
end end
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime*2) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime*2, false, false, true)
return self return self
end end
@@ -1507,7 +1730,7 @@ function CSAR:_SignalFlare(_unitName)
local _closest = self:_GetClosestDownedPilot(_heli) local _closest = self:_GetClosestDownedPilot(_heli)
local smokedist = 8000 local smokedist = 8000
if self.approachdist_far > smokedist then smokedist = self.approachdist_far end if self.approachdist_far > smokedist then smokedist = self.approachdist_far end
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < smokedist then if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot) local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
local _distance = 0 local _distance = 0
@@ -1517,7 +1740,7 @@ function CSAR:_SignalFlare(_unitName)
_distance = string.format("%.1fkm",_closest.distance) _distance = string.format("%.1fkm",_closest.distance)
end end
local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance) local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true)
local _coord = _closest.pilot:GetCoordinate() local _coord = _closest.pilot:GetCoordinate()
_coord:FlareRed(_clockDir) _coord:FlareRed(_clockDir)
@@ -1528,7 +1751,7 @@ function CSAR:_SignalFlare(_unitName)
else else
_distance = string.format("%.1fkm",smokedist/1000) _distance = string.format("%.1fkm",smokedist/1000)
end end
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime) self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime, false, false, true)
end end
return self return self
end end
@@ -1562,16 +1785,16 @@ function CSAR:_Reqsmoke( _unitName )
local smokedist = 8000 local smokedist = 8000
if smokedist < self.approachdist_far then smokedist = self.approachdist_far end if smokedist < self.approachdist_far then smokedist = self.approachdist_far end
local _closest = self:_GetClosestDownedPilot(_heli) local _closest = self:_GetClosestDownedPilot(_heli)
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < smokedist then if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot) local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
local _distance = 0 local _distance = 0
if _SETTINGS:IsImperial() then if _SETTINGS:IsImperial() then
_distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance)) _distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance))
else else
_distance = string.format("%.1fkm",_closest.distance) _distance = string.format("%.1fkm",_closest.distance/1000)
end end
local _msg = string.format("%s - Popping signal smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance) local _msg = string.format("%s - Popping smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true)
local _coord = _closest.pilot:GetCoordinate() local _coord = _closest.pilot:GetCoordinate()
local color = self.smokecolor local color = self.smokecolor
_coord:Smoke(color) _coord:Smoke(color)
@@ -1582,7 +1805,7 @@ function CSAR:_Reqsmoke( _unitName )
else else
_distance = string.format("%.1fkm",smokedist/1000) _distance = string.format("%.1fkm",smokedist/1000)
end end
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime) self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime, false, false, true)
end end
return self return self
end end
@@ -1654,13 +1877,13 @@ function CSAR:_CheckOnboard(_unitName)
--list onboard pilots --list onboard pilots
local _inTransit = self.inTransitGroups[_unitName] local _inTransit = self.inTransitGroups[_unitName]
if _inTransit == nil then if _inTransit == nil then
self:_DisplayMessageToSAR(_unit, "No Rescued Pilots onboard", self.messageTime) self:_DisplayMessageToSAR(_unit, "No Rescued Pilots onboard", self.messageTime, false, false, true)
else else
local _text = "Onboard - RTB to FARP/Airfield or MASH: " local _text = "Onboard - RTB to FARP/Airfield or MASH: "
for _, _onboard in pairs(self.inTransitGroups[_unitName]) do for _, _onboard in pairs(self.inTransitGroups[_unitName]) do
_text = _text .. "\n" .. _onboard.desc _text = _text .. "\n" .. _onboard.desc
end end
self:_DisplayMessageToSAR(_unit, _text, self.messageTime*2) self:_DisplayMessageToSAR(_unit, _text, self.messageTime*2, false, false, true)
end end
return self return self
end end
@@ -1871,11 +2094,12 @@ end
-- @param #string To To state. -- @param #string To To state.
function CSAR:onafterStart(From, Event, To) function CSAR:onafterStart(From, Event, To)
self:T({From, Event, To}) self:T({From, Event, To})
self:I(self.lid .. "Started.") self:I(self.lid .. "Started ("..self.version..")")
-- event handler -- event handler
self:HandleEvent(EVENTS.Takeoff, self._EventHandler) self:HandleEvent(EVENTS.Takeoff, self._EventHandler)
self:HandleEvent(EVENTS.Land, self._EventHandler) self:HandleEvent(EVENTS.Land, self._EventHandler)
self:HandleEvent(EVENTS.Ejection, self._EventHandler) self:HandleEvent(EVENTS.Ejection, self._EventHandler)
self:HandleEvent(EVENTS.LandingAfterEjection, self._EventHandler) --shagrat
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler) self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
self:HandleEvent(EVENTS.PilotDead, self._EventHandler) self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
@@ -1886,6 +2110,9 @@ function CSAR:onafterStart(From, Event, To)
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end end
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also? self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
if self.wetfeettemplate then
self.usewetfeet = true
end
self:__Status(-10) self:__Status(-10)
return self return self
end end
@@ -1919,7 +2146,12 @@ function CSAR:onbeforeStatus(From, Event, To)
self:T({From, Event, To}) self:T({From, Event, To})
-- housekeeping -- housekeeping
self:_AddMedevacMenuItem() self:_AddMedevacMenuItem()
self:_RefreshRadioBeacons()
if not self.BeaconTimer or (self.BeaconTimer and not self.BeaconTimer:IsRunning()) then
self.BeaconTimer = TIMER:New(self._RefreshRadioBeacons,self)
self.BeaconTimer:Start(2,self.beaconRefresher)
end
self:_CheckDownedPilotTable() self:_CheckDownedPilotTable()
for _,_sar in pairs (self.csarUnits) do for _,_sar in pairs (self.csarUnits) do
local PilotTable = self.downedPilots local PilotTable = self.downedPilots
@@ -1986,6 +2218,7 @@ function CSAR:onafterStop(From, Event, To)
self:UnHandleEvent(EVENTS.Takeoff) self:UnHandleEvent(EVENTS.Takeoff)
self:UnHandleEvent(EVENTS.Land) self:UnHandleEvent(EVENTS.Land)
self:UnHandleEvent(EVENTS.Ejection) self:UnHandleEvent(EVENTS.Ejection)
self:UnHandleEvent(EVENTS.LandingAfterEjection) -- shagrat
self:UnHandleEvent(EVENTS.PlayerEnterUnit) self:UnHandleEvent(EVENTS.PlayerEnterUnit)
self:UnHandleEvent(EVENTS.PlayerEnterAircraft) self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
self:UnHandleEvent(EVENTS.PilotDead) self:UnHandleEvent(EVENTS.PilotDead)
@@ -2061,6 +2294,18 @@ function CSAR:onbeforePilotDown(From, Event, To, Group, Frequency, Leadername, C
self:T({From, Event, To, Group, Frequency, Leadername, CoordinatesText}) self:T({From, Event, To, Group, Frequency, Leadername, CoordinatesText})
return self return self
end end
--- (Internal) Function called before Landed() event.
-- @param #CSAR self.
-- @param #string From From state.
-- @param #string Event Event triggered.
-- @param #string To To state.
-- @param #string HeliName Name of the #UNIT which has landed.
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where the heli landed.
function CSAR:onbeforeLanded(From, Event, To, HeliName, Airbase)
self:T({From, Event, To, HeliName, Airbase})
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End Ops.CSAR -- End Ops.CSAR
-------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@@ -802,7 +802,7 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
-- Debug. -- Debug.
local text=string.format("Unit %s crashed or ejected.", unitname) local text=string.format("Unit %s crashed or ejected.", unitname)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text) self:T(self.lid..text)
-- Get coordinate of unit. -- Get coordinate of unit.
local coord=unit:GetCoordinate() local coord=unit:GetCoordinate()

View File

@@ -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).
@@ -88,15 +87,40 @@
-- --
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`. -- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
-- --
-- ## Set Google
--
-- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'.
-- By enabling this it also allows you to utilize SSML in your text for added flexibilty.
-- 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
--
-- **NOTE on using GOOGLE TTS with SRS:** You need to have the C# library installed in your SRS folder for Google to work.
-- You can obtain it e.g. here: [NuGet](https://www.nuget.org/packages/Grpc.Core)
--
-- **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 specifc 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
-- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices
-- --
-- ## 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",
@@ -112,11 +136,12 @@ 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.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -135,8 +160,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
@@ -151,6 +177,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
@@ -193,12 +226,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.
@@ -214,6 +282,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.
@@ -382,22 +451,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
@@ -423,16 +481,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
@@ -458,37 +506,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
@@ -625,8 +684,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"
@@ -639,6 +699,7 @@ 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")
@@ -648,12 +709,12 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed) --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! -- 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 /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") --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
@@ -662,7 +723,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
@@ -678,7 +739,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
-- Set google. -- Set google.
if self.google then if self.google then
command=command..string.format(' -G "%s"', self.google) command=command..string.format(' --ssml -G "%s"', self.google)
end end
-- Debug output. -- Debug output.
@@ -687,6 +748,353 @@ 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.
--- 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
--- 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)
-- 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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -183,13 +183,17 @@ do -- Sound File
--- Set path, where the sound file is located. --- Set path, where the sound file is located.
-- @param #SOUNDFILE self -- @param #SOUNDFILE self
-- @param #string Path Path to the directory, where the sound file is located. -- @param #string Path Path to the directory, where the sound file is located. In case this is nil, it defaults to the DCS mission temp directory.
-- @return #SOUNDFILE self -- @return #SOUNDFILE self
function SOUNDFILE:SetPath(Path) function SOUNDFILE:SetPath(Path)
-- Init path. -- Init path.
self.path=Path or "l10n/DEFAULT/" self.path=Path or "l10n/DEFAULT/"
if not Path and self.useSRS then -- use path to mission temp dir
self.path = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT"
end
-- Remove (back)slashes. -- Remove (back)slashes.
local nmax=1000 ; local n=1 local nmax=1000 ; local n=1
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do

View File

@@ -40,7 +40,7 @@ do -- UserSound
-- @param #USERSOUND self -- @param #USERSOUND self
-- @param #string UserSoundFileName The filename of the usersound. -- @param #string UserSoundFileName The filename of the usersound.
-- @return #USERSOUND -- @return #USERSOUND
function USERSOUND:New( UserSoundFileName ) --R2.3 function USERSOUND:New( UserSoundFileName )
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
@@ -58,7 +58,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound. -- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
-- --
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3 function USERSOUND:SetFileName( UserSoundFileName )
self.UserSoundFileName = UserSoundFileName self.UserSoundFileName = UserSoundFileName
@@ -75,7 +75,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToAll() -- Play the sound that Blue has won. -- BlueVictory:ToAll() -- Play the sound that Blue has won.
-- --
function USERSOUND:ToAll() --R2.3 function USERSOUND:ToAll()
trigger.action.outSound( self.UserSoundFileName ) trigger.action.outSound( self.UserSoundFileName )
@@ -91,7 +91,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition. -- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
-- --
function USERSOUND:ToCoalition( Coalition ) --R2.3 function USERSOUND:ToCoalition( Coalition )
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName ) trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
@@ -107,7 +107,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country. -- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
-- --
function USERSOUND:ToCountry( Country ) --R2.3 function USERSOUND:ToCountry( Country )
trigger.action.outSoundForCountry( Country, self.UserSoundFileName ) trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
@@ -123,9 +123,9 @@ do -- UserSound
-- @usage -- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player. -- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group. -- BlueVictory:ToGroup( PlayerGroup ) -- Play the victory sound to the player group.
-- --
function USERSOUND:ToGroup( Group, Delay ) --R2.3 function USERSOUND:ToGroup( Group, Delay )
Delay=Delay or 0 Delay=Delay or 0
if Delay>0 then if Delay>0 then
@@ -137,4 +137,26 @@ do -- UserSound
return self return self
end end
--- Play the usersound to the given @{Wrapper.Unit}.
-- @param #USERSOUND self
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerUnit = UNIT:FindByName( "PlayerUnit" ) -- Search for the active group named "PlayerUnit", a human player.
-- BlueVictory:ToUnit( PlayerUnit ) -- Play the victory sound to the player unit.
--
function USERSOUND:ToUnit( Unit, Delay )
Delay=Delay or 0
if Delay>0 then
SCHEDULER:New(nil, USERSOUND.ToUnit,{self, Unit}, Delay)
else
trigger.action.outSoundForUnit( Unit:GetID(), self.UserSoundFileName )
end
return self
end
end end

View File

@@ -132,7 +132,7 @@ MISSION = {
-- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players. -- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players.
-- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field. -- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field.
-- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}. -- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}.
-- @param DCS#coaliton.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL. -- @param DCS#coalition.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL.
-- @return #MISSION self -- @return #MISSION self
function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition )

View File

@@ -82,7 +82,7 @@
-- --
-- ![Mission](../Tasking/Report_Statistics_Progress.JPG) -- ![Mission](../Tasking/Report_Statistics_Progress.JPG)
-- --
-- A statistic report on the progress of the mission. Each task achievement will increase the %-tage to 100% as a goal to complete the task. -- A statistic report on the progress of the mission. Each task achievement will increase the % to 100% as a goal to complete the task.
-- --
-- ## 1.3) Join a Task. -- ## 1.3) Join a Task.
-- --

View File

@@ -38,7 +38,7 @@ do -- TASK_A2A
-- --
-- @field #TASK_A2A -- @field #TASK_A2A
TASK_A2A = { TASK_A2A = {
ClassName = "TASK_A2A", ClassName = "TASK_A2A"
} }
--- Instantiates a new TASK_A2A. --- Instantiates a new TASK_A2A.
@@ -60,7 +60,6 @@ do -- TASK_A2A
local Fsm = self:GetUnitProcess() local Fsm = self:GetUnitProcess()
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
@@ -81,8 +80,7 @@ do -- TASK_A2A
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
-- @param #FSM_PROCESS self
---- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param #TASK_CARGO Task -- @param #TASK_CARGO Task
function Fsm:OnLeaveAssigned( TaskUnit, Task ) function Fsm:OnLeaveAssigned( TaskUnit, Task )
@@ -177,8 +175,6 @@ do -- TASK_A2A
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
function TASK_A2A:GetPlannedMenuText() function TASK_A2A:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
@@ -209,8 +205,6 @@ do -- TASK_A2A
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -244,7 +238,6 @@ do -- TASK_A2A
ActRouteTarget:SetCoordinate( TargetCoordinate ) ActRouteTarget:SetCoordinate( TargetCoordinate )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
@@ -256,7 +249,6 @@ do -- TASK_A2A
return ActRouteTarget:GetCoordinate() return ActRouteTarget:GetCoordinate()
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -268,7 +260,6 @@ do -- TASK_A2A
ActRouteTarget:SetZone( TargetZone, Altitude, Heading ) ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
@@ -301,7 +292,6 @@ do -- TASK_A2A
return Distance return Distance
end end
--- This method checks every 10 seconds if the goal has been reached of the task. --- This method checks every 10 seconds if the goal has been reached of the task.
-- @param #TASK_A2A self -- @param #TASK_A2A self
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To ) function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
@@ -314,7 +304,6 @@ do -- TASK_A2A
self:__Goal( -10 ) self:__Goal( -10 )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
function TASK_A2A:UpdateTaskInfo( DetectedItem ) function TASK_A2A:UpdateTaskInfo( DetectedItem )
@@ -375,7 +364,6 @@ do -- TASK_A2A
end end
do -- TASK_A2A_INTERCEPT do -- TASK_A2A_INTERCEPT
--- The TASK_A2A_INTERCEPT class --- The TASK_A2A_INTERCEPT class
@@ -384,7 +372,7 @@ do -- TASK_A2A_INTERCEPT
-- @extends Tasking.Task#TASK -- @extends Tasking.Task#TASK
--- Defines an intercept task for a human player to be executed. --- Defines an intercept task for a human player to be executed.
-- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there! -- When enemy planes need to be intercepted by human players, use this task type to urge the players to get out there!
-- --
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks -- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
-- based on detected airborne enemy targets intruding friendly airspace. -- based on detected airborne enemy targets intruding friendly airspace.
@@ -394,11 +382,9 @@ do -- TASK_A2A_INTERCEPT
-- --
-- @field #TASK_A2A_INTERCEPT -- @field #TASK_A2A_INTERCEPT
TASK_A2A_INTERCEPT = { TASK_A2A_INTERCEPT = {
ClassName = "TASK_A2A_INTERCEPT", ClassName = "TASK_A2A_INTERCEPT"
} }
--- Instantiates a new TASK_A2A_INTERCEPT. --- Instantiates a new TASK_A2A_INTERCEPT.
-- @param #TASK_A2A_INTERCEPT self -- @param #TASK_A2A_INTERCEPT self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -413,10 +399,7 @@ do -- TASK_A2A_INTERCEPT
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Intercept incoming intruders.\n" )
TaskBriefing or
"Intercept incoming intruders.\n"
)
return self return self
end end
@@ -469,10 +452,8 @@ do -- TASK_A2A_INTERCEPT
return self return self
end end
end end
do -- TASK_A2A_SWEEP do -- TASK_A2A_SWEEP
--- The TASK_A2A_SWEEP class --- The TASK_A2A_SWEEP class
@@ -493,11 +474,9 @@ do -- TASK_A2A_SWEEP
-- --
-- @field #TASK_A2A_SWEEP -- @field #TASK_A2A_SWEEP
TASK_A2A_SWEEP = { TASK_A2A_SWEEP = {
ClassName = "TASK_A2A_SWEEP", ClassName = "TASK_A2A_SWEEP"
} }
--- Instantiates a new TASK_A2A_SWEEP. --- Instantiates a new TASK_A2A_SWEEP.
-- @param #TASK_A2A_SWEEP self -- @param #TASK_A2A_SWEEP self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -512,10 +491,7 @@ do -- TASK_A2A_SWEEP
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" )
TaskBriefing or
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
)
return self return self
end end
@@ -581,7 +557,6 @@ do -- TASK_A2A_SWEEP
end end
do -- TASK_A2A_ENGAGE do -- TASK_A2A_ENGAGE
--- The TASK_A2A_ENGAGE class --- The TASK_A2A_ENGAGE class
@@ -600,11 +575,9 @@ do -- TASK_A2A_ENGAGE
-- --
-- @field #TASK_A2A_ENGAGE -- @field #TASK_A2A_ENGAGE
TASK_A2A_ENGAGE = { TASK_A2A_ENGAGE = {
ClassName = "TASK_A2A_ENGAGE", ClassName = "TASK_A2A_ENGAGE"
} }
--- Instantiates a new TASK_A2A_ENGAGE. --- Instantiates a new TASK_A2A_ENGAGE.
-- @param #TASK_A2A_ENGAGE self -- @param #TASK_A2A_ENGAGE self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -619,10 +592,7 @@ do -- TASK_A2A_ENGAGE
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" )
TaskBriefing or
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
)
return self return self
end end

View File

@@ -108,7 +108,7 @@ do -- TASK_A2A_DISPATCHER
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is chosen, which is 6 km.
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. -- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
-- --
-- ### 2. Define the detected **target grouping radius**: -- ### 2. Define the detected **target grouping radius**:
@@ -185,7 +185,6 @@ do -- TASK_A2A_DISPATCHER
SweepZones = {}, SweepZones = {},
} }
--- TASK_A2A_DISPATCHER constructor. --- TASK_A2A_DISPATCHER constructor.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
@@ -208,7 +207,6 @@ do -- TASK_A2A_DISPATCHER
self:AddTransition( "Started", "Assign", "Started" ) self:AddTransition( "Started", "Assign", "Started" )
--- OnAfter Transition Handler for Event Assign. --- OnAfter Transition Handler for Event Assign.
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
@@ -224,7 +222,6 @@ do -- TASK_A2A_DISPATCHER
return self return self
end end
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission. --- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
-- So, if there is a target area detected and reported, -- So, if there is a target area detected and reported,
-- then any friendlies that are airborne near this target area, -- then any friendlies that are airborne near this target area,
@@ -286,7 +283,6 @@ do -- TASK_A2A_DISPATCHER
return nil return nil
end end
--- Creates an SWEEP task when there are targets for it. --- Creates an SWEEP task when there are targets for it.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
@@ -296,8 +292,7 @@ do -- TASK_A2A_DISPATCHER
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
if DetectedItem.IsDetected == false then if DetectedItem.IsDetected == false then
@@ -312,7 +307,6 @@ do -- TASK_A2A_DISPATCHER
return nil return nil
end end
--- Creates an ENGAGE task when there are human friendlies airborne near the targets. --- Creates an ENGAGE task when there are human friendlies airborne near the targets.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
@@ -322,11 +316,10 @@ do -- TASK_A2A_DISPATCHER
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone. -- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
if PlayersCount > 0 and DetectedItem.IsDetected == true then if PlayersCount > 0 and DetectedItem.IsDetected == true then
@@ -341,9 +334,6 @@ do -- TASK_A2A_DISPATCHER
return nil return nil
end end
--- Evaluates the removal of the Task from the Mission. --- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
@@ -443,7 +433,6 @@ do -- TASK_A2A_DISPATCHER
FriendlyTypesReport:Add( "-" ) FriendlyTypesReport:Add( "-" )
end end
return FriendliesCount, FriendlyTypesReport return FriendliesCount, FriendlyTypesReport
end end
@@ -487,7 +476,6 @@ do -- TASK_A2A_DISPATCHER
PlayerTypesReport:Add( "-" ) PlayerTypesReport:Add( "-" )
end end
return PlayersCount, PlayerTypesReport return PlayersCount, PlayerTypesReport
end end
@@ -496,7 +484,6 @@ do -- TASK_A2A_DISPATCHER
self.Tasks[TaskIndex] = nil self.Tasks[TaskIndex] = nil
end end
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.

View File

@@ -38,7 +38,7 @@ do -- TASK_A2G
-- --
-- @field #TASK_A2G -- @field #TASK_A2G
TASK_A2G = { TASK_A2G = {
ClassName = "TASK_A2G", ClassName = "TASK_A2G"
} }
--- Instantiates a new TASK_A2G. --- Instantiates a new TASK_A2G.
@@ -80,8 +80,6 @@ do -- TASK_A2G
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -179,8 +177,6 @@ do -- TASK_A2G
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
function TASK_A2G:GetPlannedMenuText() function TASK_A2G:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
@@ -211,8 +207,6 @@ do -- TASK_A2G
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -246,7 +240,6 @@ do -- TASK_A2G
ActRouteTarget:SetCoordinate( TargetCoordinate ) ActRouteTarget:SetCoordinate( TargetCoordinate )
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
@@ -258,7 +251,6 @@ do -- TASK_A2G
return ActRouteTarget:GetCoordinate() return ActRouteTarget:GetCoordinate()
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -270,7 +262,6 @@ do -- TASK_A2G
ActRouteTarget:SetZone( TargetZone ) ActRouteTarget:SetZone( TargetZone )
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
@@ -303,7 +294,6 @@ do -- TASK_A2G
return Distance return Distance
end end
--- This method checks every 10 seconds if the goal has been reached of the task. --- This method checks every 10 seconds if the goal has been reached of the task.
-- @param #TASK_A2G self -- @param #TASK_A2G self
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To ) function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
@@ -381,7 +371,6 @@ do -- TASK_A2G
end end
do -- TASK_A2G_SEAD do -- TASK_A2G_SEAD
--- The TASK_A2G_SEAD class --- The TASK_A2G_SEAD class
@@ -397,7 +386,7 @@ do -- TASK_A2G_SEAD
-- --
-- @field #TASK_A2G_SEAD -- @field #TASK_A2G_SEAD
TASK_A2G_SEAD = { TASK_A2G_SEAD = {
ClassName = "TASK_A2G_SEAD", ClassName = "TASK_A2G_SEAD"
} }
--- Instantiates a new TASK_A2G_SEAD. --- Instantiates a new TASK_A2G_SEAD.
@@ -414,10 +403,7 @@ do -- TASK_A2G_SEAD
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Execute a Suppression of Enemy Air Defenses." )
TaskBriefing or
"Execute a Suppression of Enemy Air Defenses."
)
return self return self
end end
@@ -470,7 +456,6 @@ do -- TASK_A2G_SEAD
return self return self
end end
end end
do -- TASK_A2G_BAI do -- TASK_A2G_BAI
@@ -488,9 +473,7 @@ do -- TASK_A2G_BAI
-- based on detected enemy ground targets. -- based on detected enemy ground targets.
-- --
-- @field #TASK_A2G_BAI -- @field #TASK_A2G_BAI
TASK_A2G_BAI = { TASK_A2G_BAI = { ClassName = "TASK_A2G_BAI" }
ClassName = "TASK_A2G_BAI",
}
--- Instantiates a new TASK_A2G_BAI. --- Instantiates a new TASK_A2G_BAI.
-- @param #TASK_A2G_BAI self -- @param #TASK_A2G_BAI self
@@ -506,10 +489,7 @@ do -- TASK_A2G_BAI
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Execute a Battlefield Air Interdiction of a group of enemy targets." )
TaskBriefing or
"Execute a Battlefield Air Interdiction of a group of enemy targets."
)
return self return self
end end
@@ -564,9 +544,6 @@ do -- TASK_A2G_BAI
end end
do -- TASK_A2G_CAS do -- TASK_A2G_CAS
--- The TASK_A2G_CAS class --- The TASK_A2G_CAS class
@@ -581,9 +558,7 @@ do -- TASK_A2G_CAS
-- based on detected enemy ground targets. -- based on detected enemy ground targets.
-- --
-- @field #TASK_A2G_CAS -- @field #TASK_A2G_CAS
TASK_A2G_CAS = { TASK_A2G_CAS = { ClassName = "TASK_A2G_CAS" }
ClassName = "TASK_A2G_CAS",
}
--- Instantiates a new TASK_A2G_CAS. --- Instantiates a new TASK_A2G_CAS.
-- @param #TASK_A2G_CAS self -- @param #TASK_A2G_CAS self
@@ -599,17 +574,11 @@ do -- TASK_A2G_CAS
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or ( "Execute a Close Air Support for a group of enemy targets. " .. "Beware of friendlies at the vicinity! " ) )
TaskBriefing or
"Execute a Close Air Support for a group of enemy targets. " ..
"Beware of friendlies at the vicinity! "
)
return self return self
end end
--- Set a score when a target in scope of the A2G attack, has been destroyed . --- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_CAS self -- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -658,5 +627,4 @@ do -- TASK_A2G_CAS
return self return self
end end
end end

View File

@@ -6,11 +6,12 @@
-- * Dynamically change the tasks as the tactical situation evolves during the mission. -- * Dynamically change the tasks as the tactical situation evolves during the mission.
-- * Dynamically assign (CAS) Close Air Support tasks for human players. -- * Dynamically assign (CAS) Close Air Support tasks for human players.
-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players. -- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players.
-- * Dynamically assign (SEAD) Supression of Enemy Air Defense tasks for human players to eliminate G2A missile threats. -- * Dynamically assign (SEAD) Suppression of Enemy Air Defense tasks for human players to eliminate G2A missile threats.
-- * Define and use an EWR (Early Warning Radar) network. -- * Define and use an EWR (Early Warning Radar) network.
-- * Define different ranges to engage upon intruders. -- * Define different ranges to engage upon intruders.
-- * Keep task achievements. -- * Keep task achievements.
-- * Score task achievements.-- -- * Score task achievements.
--
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
@@ -122,7 +123,7 @@ do -- TASK_A2G_DISPATCHER
-- F1. Command Center [Lima] -- F1. Command Center [Lima]
-- F1. Mission "Overlord (High)" -- F1. Mission "Overlord (High)"
-- --
-- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tacical mission Gamma. -- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tactical mission Gamma.
-- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority. -- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority.
-- --
-- ## 1.1. Mission Menu (Under the Command Center Menu) -- ## 1.1. Mission Menu (Under the Command Center Menu)
@@ -196,7 +197,7 @@ do -- TASK_A2G_DISPATCHER
-- --
-- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission: -- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission:
-- --
-- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a %-tage of completion. -- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a % of completion.
-- - **Report Players per Task**: Show which players are engaged on which Task within the Mission. -- - **Report Players per Task**: Show which players are engaged on which Task within the Mission.
-- --
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this: -- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
@@ -264,7 +265,6 @@ do -- TASK_A2G_DISPATCHER
-- --
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.** -- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
-- --
--
-- ## 1.3. Join Planned Task Menu -- ## 1.3. Join Planned Task Menu
-- --
-- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**. -- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**.
@@ -388,7 +388,7 @@ do -- TASK_A2G_DISPATCHER
-- - A @{Mission} object. Each task belongs to a Mission. -- - A @{Mission} object. Each task belongs to a Mission.
-- - A @{Detection} object. There are several detection grouping methods to choose from. -- - A @{Detection} object. There are several detection grouping methods to choose from.
-- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher. -- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher.
-- - A @{Set} of @{Wrapper.Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object. -- - A @{Set} of @{Wrapper.Group} objects that will detect the enemy, the RecceSet. This is attached to the @{Detection} object.
-- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object. -- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object.
-- --
-- Below an example mission declaration that is defines a Task A2G Dispatcher object. -- Below an example mission declaration that is defines a Task A2G Dispatcher object.
@@ -425,7 +425,7 @@ do -- TASK_A2G_DISPATCHER
-- --
-- -- Now we have everything to setup the main A2G TaskDispatcher. -- -- Now we have everything to setup the main A2G TaskDispatcher.
-- TaskDispatcher = TASK_A2G_DISPATCHER -- TaskDispatcher = TASK_A2G_DISPATCHER
-- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will recieve the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher. -- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will receive the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher.
-- --
-- --
-- --
@@ -434,10 +434,9 @@ do -- TASK_A2G_DISPATCHER
ClassName = "TASK_A2G_DISPATCHER", ClassName = "TASK_A2G_DISPATCHER",
Mission = nil, Mission = nil,
Detection = nil, Detection = nil,
Tasks = {}, Tasks = {}
} }
--- TASK_A2G_DISPATCHER constructor. --- TASK_A2G_DISPATCHER constructor.
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
@@ -518,7 +517,6 @@ do -- TASK_A2G_DISPATCHER
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
-- Determine if the set has ground units. -- Determine if the set has ground units.
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types. -- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
-- And there shouldn't be any radar. -- And there shouldn't be any radar.
@@ -550,7 +548,6 @@ do -- TASK_A2G_DISPATCHER
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
-- Determine if the set has ground units. -- Determine if the set has ground units.
-- There shouldn't be any ground unit friendlies nearby. -- There shouldn't be any ground unit friendlies nearby.
-- And there shouldn't be any radar. -- And there shouldn't be any radar.
@@ -571,7 +568,6 @@ do -- TASK_A2G_DISPATCHER
return nil return nil
end end
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex ) function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
self.Mission:RemoveTask( self.Tasks[TaskIndex] ) self.Mission:RemoveTask( self.Tasks[TaskIndex] )
self.Tasks[TaskIndex] = nil self.Tasks[TaskIndex] = nil
@@ -597,7 +593,6 @@ do -- TASK_A2G_DISPATCHER
return Task return Task
end end
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
@@ -800,14 +795,12 @@ do -- TASK_A2G_DISPATCHER
self:Aborted( Task ) self:Aborted( Task )
end end
TaskReport:Add( Task:GetName() ) TaskReport:Add( Task:GetName() )
else else
self:F( "This should not happen" ) self:F( "This should not happen" )
end end
end end
-- OK, so the tasking has been done, now delete the changes reported for the area. -- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedItem ) Detection:AcceptChanges( DetectedItem )
end end

View File

@@ -11,7 +11,7 @@
-- --
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit. -- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
-- --
-- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names. -- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
-- --
-- @module ENUMS -- @module ENUMS
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -126,6 +126,8 @@ ENUMS.WeaponFlag={
AnyMissile = 268402688, -- AnyASM + AnyAAM AnyMissile = 268402688, -- AnyASM + AnyAAM
--- Guns --- Guns
Cannons = 805306368, -- GUN_POD + BuiltInCannon Cannons = 805306368, -- GUN_POD + BuiltInCannon
--- Torpedo
Torpedo = 4294967296,
--- ---
-- Even More Genral -- Even More Genral
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
@@ -136,6 +138,93 @@ ENUMS.WeaponFlag={
AnyGuided = 268402702, -- Any Guided Weapon AnyGuided = 268402702, -- Any Guided Weapon
} }
--- Weapon types by category. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on hoggit wiki.
-- @type ENUMS.WeaponType
-- @field #table Bomb Bombs.
-- @field #table Rocket Rocket.
-- @field #table Gun Guns.
-- @field #table Missile Missiles.
-- @field #table AAM Air-to-Air missiles.
-- @field #table Torpedo Torpedos.
-- @field #table Any Combinations.
ENUMS.WeaponType={}
ENUMS.WeaponType.Bomb={
-- Bombs
LGB = 2,
TvGB = 4,
SNSGB = 8,
HEBomb = 16,
Penetrator = 32,
NapalmBomb = 64,
FAEBomb = 128,
ClusterBomb = 256,
Dispencer = 512,
CandleBomb = 1024,
ParachuteBomb = 2147483648,
-- Combinations
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
}
ENUMS.WeaponType.Rocket={
-- Rockets
LightRocket = 2048,
MarkerRocket = 4096,
CandleRocket = 8192,
HeavyRocket = 16384,
-- Combinations
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
}
ENUMS.WeaponType.Gun={
-- Guns
GunPod = 268435456,
BuiltInCannon = 536870912,
-- Combinations
Cannons = 805306368, -- GUN_POD + BuiltInCannon
}
ENUMS.WeaponType.Missile={
-- Missiles
AntiRadarMissile = 32768,
AntiShipMissile = 65536,
AntiTankMissile = 131072,
FireAndForgetASM = 262144,
LaserASM = 524288,
TeleASM = 1048576,
CruiseMissile = 2097152,
AntiRadarMissile2 = 1073741824,
-- Combinations
GuidedASM = 1572864, -- (LaserASM + TeleASM)
TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM)
AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile)
AnyASM2 = 1077903360, -- 4161536+1073741824,
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
AnyMissile = 268402688, -- AnyASM + AnyAAM
}
ENUMS.WeaponType.AAM={
-- Air-To-Air Missiles
SRAM = 4194304,
MRAAM = 8388608,
LRAAM = 16777216,
IR_AAM = 33554432,
SAR_AAM = 67108864,
AR_AAM = 134217728,
-- Combinations
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
}
ENUMS.WeaponType.Torpedo={
-- Torpedo
Torpedo = 4294967296,
}
ENUMS.WeaponType.Any={
-- General combinations
Weapon = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
AG = 2956984318, -- Any Air-To-Ground Weapon
AA = 264241152, -- Any Air-To-Air Weapon
Unguided = 2952822768, -- Any Unguided Weapon
Guided = 268402702, -- Any Guided Weapon
}
--- Mission tasks. --- Mission tasks.
-- @type ENUMS.MissionTask -- @type ENUMS.MissionTask
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics. -- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
@@ -360,3 +449,113 @@ ENUMS.Phonetic =
Y = 'Yankee', Y = 'Yankee',
Z = 'Zulu', Z = 'Zulu',
} }
--- Reporting Names (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_NATO_reporting_names_for_fighter_aircraft).
-- DCS known aircraft types
--
-- @type ENUMS.ReportingName
ENUMS.ReportingName =
{
NATO = {
-- Fighters
Dragon = "JF-17", -- China, correctly Fierce Dragon, Thunder for PAC
Fagot = "MiG-15",
Farmer = "MiG-19", -- Shenyang J-6 and Mikoyan-Gurevich MiG-19
Felon = "Su-57",
Fencer = "Su-24",
Fishbed = "MiG-21",
Fitter = "Su-17", -- Sukhoi Su-7 and Su-17/Su-20/Su-22
Flogger = "MiG-23", --and MiG-27
Flogger_D = "MiG-27", --and MiG-23
Flagon = "Su-15",
Foxbat = "MiG-25",
Fulcrum = "MiG-29",
Foxhound = "MiG-31",
Flanker = "Su-27", -- Sukhoi Su-27/Su-30/Su-33/Su-35/Su-37 and Shenyang J-11/J-15/J-16
Flanker_C = "Su-30",
Flanker_E = "Su-35",
Flanker_F = "Su-37",
Flanker_L = "J-11A",
Firebird = "J-10",
Sea_Flanker = "Su-33",
Fullback = "Su-34", -- also Su-32
Frogfoot = "Su-25",
Tomcat = "F-14", -- Iran
Mirage = "Mirage", -- various non-NATO
Codling = "Yak-40",
Maya = "L-39",
-- Fighters US/NATO
Warthog = "A-10",
--Mosquito = "A-20",
Skyhawk = "A-4E",
Viggen = "AJS37",
Harrier = "AV-8B",
Spirit = "B-2",
Aviojet = "C-101",
Nighthawk = "F-117A",
Eagle = "F-15C",
Mudhen = "F-15E",
Viper = "F-16",
Phantom = "F-4E",
Tiger = "F-5", -- was thinking to name this MiG-25 ;)
Sabre = "F-86",
Hornet = "A-18", -- avoiding the slash
Hawk = "Hawk",
Albatros = "L-39",
Goshawk = "T-45",
Starfighter = "F-104",
Tornado = "Tornado",
-- Transport / Bomber / Others
Atlas = "A400",
Lancer = "B1-B",
Stratofortress = "B-52H",
Hercules = "C-130",
Super_Hercules = "Hercules",
Globemaster = "C-17",
Greyhound = "C-2A",
Galaxy = "C-5",
Hawkeye = "E-2D",
Sentry = "E-3A",
Stratotanker = "KC-135",
Extender = "KC-10",
Orion = "P-3C",
Viking = "S-3B",
Osprey = "V-22",
-- Bomber Rus
Badger = "H6-J",
Bear_J = "Tu-142", -- also Tu-95
Bear = "Tu-95", -- also Tu-142
Blinder = "Tu-22",
Blackjack = "Tu-160",
-- AIC / Transport / Other
Clank = "An-30",
Curl = "An-26",
Candid = "IL-76",
Midas = "IL-78",
Mainstay = "A-50",
Mainring = "KJ-2000", -- A-50 China
Yak = "Yak-52",
-- Helos
Helix = "Ka-27",
Shark = "Ka-50",
Hind = "Mi-24",
Halo = "Mi-26",
Hip = "Mi-8",
Havoc = "Mi-28",
Gazelle = "SA342",
-- Helos US
Huey = "UH-1H",
Cobra = "AH-1",
Apache = "AH-64",
Chinook = "CH-47",
Sea_Stallion = "CH-53",
Kiowa = "OH-58",
Seahawk = "SH-60",
Blackhawk = "UH-60",
Sea_King = "S-61",
-- Drones
UCAV = "WingLoong",
Reaper = "MQ-9",
Predator = "MQ-1A",
}
}

View File

@@ -0,0 +1,773 @@
--- **UTILS** - ClassicFiFo Stack.
--
-- ===
--
-- ## Main Features:
--
-- * Build a simple multi-purpose FiFo (First-In, First-Out) stack for generic data.
-- * [Wikipedia](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
--
-- ===
--
-- ### Author: **applevangelist**
-- @module Utils.FiFo
-- @image MOOSE.JPG
-- Date: April 2022
do
--- FIFO class.
-- @type FIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of FiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @extends Core.Base#BASE
---
-- @type FIFO.IDEntry
-- @field #number pointer
-- @field #table data
-- @field #table uniqueID
---
-- @field #FIFO
FIFO = {
ClassName = "FIFO",
lid = "",
version = "0.0.5",
counter = 0,
pointer = 0,
stackbypointer = {},
stackbyid = {}
}
--- Instantiate a new FIFO Stack
-- @param #FIFO self
-- @return #FIFO self
function FIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
self.pointer = 0
self.counter = 0
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", "FiFo", self.version)
self:T(self.lid .."Created.")
return self
end
--- Empty FIFO Stack
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Clear()
self:T(self.lid.."Clear")
self.pointer = 0
self.counter = 0
self.stackbypointer = nil
self.stackbyid = nil
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
return self
end
--- FIFO Push Object to Stack
-- @param #FIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1. Note - if you intend to use `FIFO:GetIDStackSorted()` keep the UniqueID numerical!
-- @return #FIFO self
function FIFO:Push(Object,UniqueID)
self:T(self.lid.."Push")
self:T({Object,UniqueID})
self.pointer = self.pointer + 1
self.counter = self.counter + 1
local uniID = UniqueID
if not UniqueID then
self.uniquecounter = self.uniquecounter + 1
uniID = self.uniquecounter
end
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
return self
end
--- FIFO Pull Object from Stack
-- @param #FIFO self
-- @return #table Object or nil if stack is empty
function FIFO:Pull()
self:T(self.lid.."Pull")
if self.counter == 0 then return nil end
--local object = self.stackbypointer[self.pointer].data
--self.stackbypointer[self.pointer] = nil
local object = self.stackbypointer[1].data
self.stackbypointer[1] = nil
self.counter = self.counter - 1
--self.pointer = self.pointer - 1
self:Flatten()
return object
end
--- FIFO Pull Object from Stack by Pointer
-- @param #FIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty
function FIFO:PullByPointer(Pointer)
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
if self.counter == 0 then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
self.stackbypointer[Pointer] = nil
if object then self.stackbyid[object.uniqueID] = nil end
self.counter = self.counter - 1
self:Flatten()
if object then
return object.data
else
return nil
end
end
--- FIFO Read, not Pull, Object from Stack by Pointer
-- @param #FIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty or pointer does not exist
function FIFO:ReadByPointer(Pointer)
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- FIFO Read, not Pull, Object from Stack by UniqueID
-- @param #FIFO self
-- @param #number UniqueID
-- @return #table Object data or nil if stack is empty or ID does not exist
function FIFO:ReadByID(UniqueID)
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- FIFO Pull Object from Stack by UniqueID
-- @param #FIFO self
-- @param #tableUniqueID
-- @return #table Object or nil if stack is empty
function FIFO:PullByID(UniqueID)
self:T(self.lid.."PullByID " .. tostring(UniqueID))
if self.counter == 0 then return nil end
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
--self.stackbyid[UniqueID] = nil
if object then
return self:PullByPointer(object.pointer)
else
return nil
end
end
--- FIFO Housekeeping
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Flatten()
self:T(self.lid.."Flatten")
-- rebuild stacks
local pointerstack = {}
local idstack = {}
local counter = 0
for _ID,_entry in pairs(self.stackbypointer) do
counter = counter + 1
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
end
for _ID,_entry in pairs(pointerstack) do
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
end
self.stackbypointer = nil
self.stackbypointer = pointerstack
self.stackbyid = nil
self.stackbyid = idstack
self.counter = counter
self.pointer = counter
return self
end
--- FIFO Check Stack is empty
-- @param #FIFO self
-- @return #boolean empty
function FIFO:IsEmpty()
self:T(self.lid.."IsEmpty")
return self.counter == 0 and true or false
end
--- FIFO Get stack size
-- @param #FIFO self
-- @return #number size
function FIFO:GetSize()
self:T(self.lid.."GetSize")
return self.counter
end
--- FIFO Get stack size
-- @param #FIFO self
-- @return #number size
function FIFO:Count()
self:T(self.lid.."Count")
return self.counter
end
--- FIFO Check Stack is NOT empty
-- @param #FIFO self
-- @return #boolean notempty
function FIFO:IsNotEmpty()
self:T(self.lid.."IsNotEmpty")
return not self:IsEmpty()
end
--- FIFO Get the data stack by pointer
-- @param #FIFO self
-- @return #table Table of #FIFO.IDEntry entries
function FIFO:GetPointerStack()
self:T(self.lid.."GetPointerStack")
return self.stackbypointer
end
--- FIFO Check if a certain UniqeID exists
-- @param #FIFO self
-- @return #boolean exists
function FIFO:HasUniqueID(UniqueID)
self:T(self.lid.."HasUniqueID")
if self.stackbyid[UniqueID] ~= nil then
return true
else
return false
end
end
--- FIFO Get the data stack by UniqueID
-- @param #FIFO self
-- @return #table Table of #FIFO.IDEntry entries
function FIFO:GetIDStack()
self:T(self.lid.."GetIDStack")
return self.stackbyid
end
--- FIFO Get table of UniqueIDs sorted smallest to largest
-- @param #FIFO self
-- @return #table Table with index [1] to [n] of UniqueID entries
function FIFO:GetIDStackSorted()
self:T(self.lid.."GetIDStackSorted")
local stack = self:GetIDStack()
local idstack = {}
for _id,_entry in pairs(stack) do
idstack[#idstack+1] = _id
self:T({"pre",_id})
end
local function sortID(a, b)
return a < b
end
table.sort(idstack)
return idstack
end
--- FIFO Get table of data entries
-- @param #FIFO self
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
function FIFO:GetDataTable()
self:T(self.lid.."GetDataTable")
local datatable = {}
for _,_entry in pairs(self.stackbypointer) do
datatable[#datatable+1] = _entry.data
end
return datatable
end
--- FIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
-- @param #FIFO self
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
function FIFO:GetSortedDataTable()
self:T(self.lid.."GetSortedDataTable")
local datatable = {}
local idtablesorted = self:GetIDStackSorted()
for _,_entry in pairs(idtablesorted) do
datatable[#datatable+1] = self:ReadByID(_entry)
end
return datatable
end
--- Iterate the FIFO and call an iterator function for the given FIFO data, providing the object for each element of the stack and optional parameters.
-- @param #FIFO self
-- @param #function IteratorFunction The function that will be called.
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
-- @param #table FunctionArguments (Optional) Function arguments.
-- @return #FIFO self
function FIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
self:T(self.lid.."ForEach")
local Set = self:GetPointerStack() or {}
Arg = Arg or {}
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData.data
self:T( {Object} )
if Function then
if Function( unpack( FunctionArguments or {} ), Object ) == true then
IteratorFunction( Object, unpack( Arg ) )
end
else
IteratorFunction( Object, unpack( Arg ) )
end
Count = Count + 1
end
return true
end
local co = CoRoutine
local function Schedule()
local status, res = co()
self:T( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
Schedule()
return self
end
--- FIFO Print stacks to dcs.log
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Flush()
self:T(self.lid.."FiFo Flush")
self:I("FIFO Flushing Stack by Pointer")
for _id,_data in pairs (self.stackbypointer) do
local data = _data -- #FIFO.IDEntry
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("FIFO Flushing Stack by ID")
for _id,_data in pairs (self.stackbyid) do
local data = _data -- #FIFO.IDEntry
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("Counter = " .. self.counter)
self:I("Pointer = ".. self.pointer)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End FIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- LIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
do
--- **UTILS** - LiFo Stack.
--
-- **Main Features:**
--
-- * Build a simple multi-purpose LiFo (Last-In, First-Out) stack for generic data.
--
-- ===
--
-- ### Author: **applevangelist**
--- LIFO class.
-- @type LIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of LiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @extends Core.Base#BASE
---
-- @type LIFO.IDEntry
-- @field #number pointer
-- @field #table data
-- @field #table uniqueID
---
-- @field #LIFO
LIFO = {
ClassName = "LIFO",
lid = "",
version = "0.0.5",
counter = 0,
pointer = 0,
stackbypointer = {},
stackbyid = {}
}
--- Instantiate a new LIFO Stack
-- @param #LIFO self
-- @return #LIFO self
function LIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
self.pointer = 0
self.counter = 0
self.uniquecounter = 0
self.stackbypointer = {}
self.stackbyid = {}
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", "LiFo", self.version)
self:T(self.lid .."Created.")
return self
end
--- Empty LIFO Stack
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Clear()
self:T(self.lid.."Clear")
self.pointer = 0
self.counter = 0
self.stackbypointer = nil
self.stackbyid = nil
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
return self
end
--- LIFO Push Object to Stack
-- @param #LIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1
-- @return #LIFO self
function LIFO:Push(Object,UniqueID)
self:T(self.lid.."Push")
self:T({Object,UniqueID})
self.pointer = self.pointer + 1
self.counter = self.counter + 1
local uniID = UniqueID
if not UniqueID then
self.uniquecounter = self.uniquecounter + 1
uniID = self.uniquecounter
end
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
return self
end
--- LIFO Pull Object from Stack
-- @param #LIFO self
-- @return #table Object or nil if stack is empty
function LIFO:Pull()
self:T(self.lid.."Pull")
if self.counter == 0 then return nil end
local object = self.stackbypointer[self.pointer].data
self.stackbypointer[self.pointer] = nil
--local object = self.stackbypointer[1].data
--self.stackbypointer[1] = nil
self.counter = self.counter - 1
self.pointer = self.pointer - 1
self:Flatten()
return object
end
--- LIFO Pull Object from Stack by Pointer
-- @param #LIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty
function LIFO:PullByPointer(Pointer)
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
if self.counter == 0 then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
self.stackbypointer[Pointer] = nil
if object then self.stackbyid[object.uniqueID] = nil end
self.counter = self.counter - 1
self:Flatten()
if object then
return object.data
else
return nil
end
end
--- LIFO Read, not Pull, Object from Stack by Pointer
-- @param #LIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty or pointer does not exist
function LIFO:ReadByPointer(Pointer)
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
local object = self.stackbypointer[Pointer] -- #LIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- LIFO Read, not Pull, Object from Stack by UniqueID
-- @param #LIFO self
-- @param #number UniqueID
-- @return #table Object or nil if stack is empty or ID does not exist
function LIFO:ReadByID(UniqueID)
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- LIFO Pull Object from Stack by UniqueID
-- @param #LIFO self
-- @param #tableUniqueID
-- @return #table Object or nil if stack is empty
function LIFO:PullByID(UniqueID)
self:T(self.lid.."PullByID " .. tostring(UniqueID))
if self.counter == 0 then return nil end
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
--self.stackbyid[UniqueID] = nil
if object then
return self:PullByPointer(object.pointer)
else
return nil
end
end
--- LIFO Housekeeping
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Flatten()
self:T(self.lid.."Flatten")
-- rebuild stacks
local pointerstack = {}
local idstack = {}
local counter = 0
for _ID,_entry in pairs(self.stackbypointer) do
counter = counter + 1
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
end
for _ID,_entry in pairs(pointerstack) do
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
end
self.stackbypointer = nil
self.stackbypointer = pointerstack
self.stackbyid = nil
self.stackbyid = idstack
self.counter = counter
self.pointer = counter
return self
end
--- LIFO Check Stack is empty
-- @param #LIFO self
-- @return #boolean empty
function LIFO:IsEmpty()
self:T(self.lid.."IsEmpty")
return self.counter == 0 and true or false
end
--- LIFO Get stack size
-- @param #LIFO self
-- @return #number size
function LIFO:GetSize()
self:T(self.lid.."GetSize")
return self.counter
end
--- LIFO Get stack size
-- @param #LIFO self
-- @return #number size
function LIFO:Count()
self:T(self.lid.."Count")
return self.counter
end
--- LIFO Check Stack is NOT empty
-- @param #LIFO self
-- @return #boolean notempty
function LIFO:IsNotEmpty()
self:T(self.lid.."IsNotEmpty")
return not self:IsEmpty()
end
--- LIFO Get the data stack by pointer
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetPointerStack()
self:T(self.lid.."GetPointerStack")
return self.stackbypointer
end
--- LIFO Get the data stack by UniqueID
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetIDStack()
self:T(self.lid.."GetIDStack")
return self.stackbyid
end
--- LIFO Get table of UniqueIDs sorted smallest to largest
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetIDStackSorted()
self:T(self.lid.."GetIDStackSorted")
local stack = self:GetIDStack()
local idstack = {}
for _id,_entry in pairs(stack) do
idstack[#idstack+1] = _id
self:T({"pre",_id})
end
local function sortID(a, b)
return a < b
end
table.sort(idstack)
return idstack
end
--- LIFO Check if a certain UniqeID exists
-- @param #LIFO self
-- @return #boolean exists
function LIFO:HasUniqueID(UniqueID)
self:T(self.lid.."HasUniqueID")
return self.stackbyid[UniqueID] and true or false
end
--- LIFO Print stacks to dcs.log
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Flush()
self:T(self.lid.."FiFo Flush")
self:I("LIFO Flushing Stack by Pointer")
for _id,_data in pairs (self.stackbypointer) do
local data = _data -- #LIFO.IDEntry
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("LIFO Flushing Stack by ID")
for _id,_data in pairs (self.stackbyid) do
local data = _data -- #LIFO.IDEntry
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("Counter = " .. self.counter)
self:I("Pointer = ".. self.pointer)
return self
end
--- LIFO Get table of data entries
-- @param #LIFO self
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
function LIFO:GetDataTable()
self:T(self.lid.."GetDataTable")
local datatable = {}
for _,_entry in pairs(self.stackbypointer) do
datatable[#datatable+1] = _entry.data
end
return datatable
end
--- LIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
-- @param #LIFO self
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
function LIFO:GetSortedDataTable()
self:T(self.lid.."GetSortedDataTable")
local datatable = {}
local idtablesorted = self:GetIDStackSorted()
for _,_entry in pairs(idtablesorted) do
datatable[#datatable+1] = self:ReadByID(_entry)
end
return datatable
end
--- Iterate the LIFO and call an iterator function for the given LIFO data, providing the object for each element of the stack and optional parameters.
-- @param #LIFO self
-- @param #function IteratorFunction The function that will be called.
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
-- @param #table FunctionArguments (Optional) Function arguments.
-- @return #LIFO self
function LIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
self:T(self.lid.."ForEach")
local Set = self:GetPointerStack() or {}
Arg = Arg or {}
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData.data
self:T( {Object} )
if Function then
if Function( unpack( FunctionArguments or {} ), Object ) == true then
IteratorFunction( Object, unpack( Arg ) )
end
else
IteratorFunction( Object, unpack( Arg ) )
end
Count = Count + 1
end
return true
end
local co = CoRoutine
local function Schedule()
local status, res = co()
self:T( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
Schedule()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End LIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
end

View File

@@ -9,7 +9,6 @@
-- @module Utilities.PROFILER -- @module Utilities.PROFILER
-- @image MOOSE.JPG -- @image MOOSE.JPG
--- PROFILER class. --- PROFILER class.
-- @type PROFILER -- @type PROFILER
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
@@ -25,7 +24,6 @@
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value. -- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler". -- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt" -- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? * --- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
-- --
-- === -- ===
@@ -35,12 +33,11 @@
-- # The PROFILER Concept -- # The PROFILER Concept
-- --
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time. -- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
-- With this information you can optimize the perfomance of your code. -- With this information you can optimize the performance of your code.
-- --
-- # Prerequisites -- # Prerequisites
-- --
-- The modules **os** and **lfs** need to be desanizied. -- The modules **os** and **lfs** need to be de-sanitized.
--
-- --
-- # Start -- # Start
-- --
@@ -122,15 +119,15 @@ function PROFILER.Start(Delay, Duration)
-- Check if os, io and lfs are available. -- Check if os, io and lfs are available.
local go = true local go = true
if not os then if not os then
env.error("ERROR: Profiler needs os to be desanitized!") env.error( "ERROR: Profiler needs os to be de-sanitized!" )
go = false go = false
end end
if not io then if not io then
env.error("ERROR: Profiler needs io to be desanitized!") env.error( "ERROR: Profiler needs io to be de-sanitized!" )
go = false go = false
end end
if not lfs then if not lfs then
env.error("ERROR: Profiler needs lfs to be desanitized!") env.error( "ERROR: Profiler needs lfs to be de-sanitized!" )
go = false go = false
end end
if not go then if not go then
@@ -161,7 +158,6 @@ function PROFILER.Start(Delay, Duration)
env.info( string.format( "- Output file \"%s\" in CSV format", PROFILER.getfilename( "csv" ) ) ) env.info( string.format( "- Output file \"%s\" in CSV format", PROFILER.getfilename( "csv" ) ) )
env.info( '###############################################################################' ) env.info( '###############################################################################' )
-- Message on screen -- Message on screen
local duration = Duration or 600 local duration = Duration or 600
trigger.action.outText( "### Profiler running ###", duration ) trigger.action.outText( "### Profiler running ###", duration )
@@ -191,7 +187,6 @@ function PROFILER.Stop(Delay)
-- Remove hook. -- Remove hook.
debug.sethook() debug.sethook()
-- Run time game. -- Run time game.
local runTimeGame = timer.getTime() - PROFILER.TstartGame local runTimeGame = timer.getTime() - PROFILER.TstartGame
@@ -337,7 +332,6 @@ function PROFILER.printCSV(data, runTimeGame)
g:close() g:close()
end end
--- Write info to output file. --- Write info to output file.
-- @param #string ext Extension. -- @param #string ext Extension.
-- @return #string File name. -- @return #string File name.
@@ -385,25 +379,20 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
local tforgen = nil -- #PROFILER.Data local tforgen = nil -- #PROFILER.Data
local tpairs = nil -- #PROFILER.Data local tpairs = nil -- #PROFILER.Data
for func, count in pairs( PROFILER.Counters ) do for func, count in pairs( PROFILER.Counters ) do
local s, src, line, tm = PROFILER.getData( func ) local s, src, line, tm = PROFILER.getData( func )
if PROFILER.logUnknown == true then if PROFILER.logUnknown == true then
if s==nil then s="<Unknown>" end if s == nil then
s = "<Unknown>"
end
end end
if s ~= nil then if s ~= nil then
-- Profile data. -- Profile data.
local T= local T = { func = s, src = src, line = line, count = count, tm = tm } -- #PROFILER.Data
{ func=s,
src=src,
line=line,
count=count,
tm=tm,
} --#PROFILER.Data
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data. -- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
if s == "_copy" then if s == "_copy" then
@@ -473,7 +462,9 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
env.info( "##############################################################################" ) env.info( "##############################################################################" )
-- Sort by total time. -- Sort by total time.
table.sort(t, function(a,b) return a.tm>b.tm end) table.sort( t, function( a, b )
return a.tm > b.tm
end )
-- Write data. -- Write data.
PROFILER._flog( f, "" ) PROFILER._flog( f, "" )
@@ -500,7 +491,9 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
PROFILER.showTable( t, f, runTimeGame ) PROFILER.showTable( t, f, runTimeGame )
-- Sort by number of calls. -- Sort by number of calls.
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end) table.sort( t, function( a, b )
return a.tm / a.count > b.tm / b.count
end )
-- Detailed data. -- Detailed data.
PROFILER._flog( f, "" ) PROFILER._flog( f, "" )
@@ -513,7 +506,9 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
PROFILER.showTable( t, f, runTimeGame ) PROFILER.showTable( t, f, runTimeGame )
-- Sort by number of calls. -- Sort by number of calls.
table.sort(t, function(a,b) return a.count>b.count end) table.sort( t, function( a, b )
return a.count > b.count
end )
-- Detailed data. -- Detailed data.
PROFILER._flog( f, "" ) PROFILER._flog( f, "" )
@@ -536,4 +531,3 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
-- Print csv file. -- Print csv file.
PROFILER.printCSV( t, runTimeGame ) PROFILER.printCSV( t, runTimeGame )
end end

View File

@@ -1,7 +1,6 @@
--- Various routines --- Various routines
-- @module routines -- @module routines
-- @image MOOSE.JPG -- @image MOOSE.JPG
env.setErrorMessageBoxEnabled( false ) env.setErrorMessageBoxEnabled( false )
--- Extract of MIST functions. --- Extract of MIST functions.
@@ -9,7 +8,6 @@ env.setErrorMessageBoxEnabled(false)
routines = {} routines = {}
-- don't change these -- don't change these
routines.majorVersion = 3 routines.majorVersion = 3
routines.minorVersion = 3 routines.minorVersion = 3
@@ -46,7 +44,6 @@ routines.utils.deepCopy = function(object)
return objectreturn return objectreturn
end end
-- porting in Slmod's serialize_slmod2 -- porting in Slmod's serialize_slmod2
routines.utils.oneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function routines.utils.oneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
@@ -142,7 +139,6 @@ routines.utils.basicSerialize = function(s)
end end
end end
routines.utils.toDegree = function( angle ) routines.utils.toDegree = function( angle )
return angle * 180 / math.pi return angle * 180 / math.pi
end end
@@ -252,10 +248,6 @@ function routines.utils.get3DDist(point1, point2)
return routines.vec.mag( { x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z } ) return routines.vec.mag( { x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z } )
end end
-- 3D Vector manipulation -- 3D Vector manipulation
routines.vec = {} routines.vec = {}
@@ -295,16 +287,12 @@ routines.vec.rotateVec2 = function(vec2, theta)
end end
--------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------
-- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.
routines.tostringMGRS = function( MGRS, acc ) routines.tostringMGRS = function( MGRS, acc )
if acc == 0 then if acc == 0 then
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph
else else
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Easting/(10^(5-acc)), 0)) return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format( '%0' .. acc .. 'd', routines.utils.round( MGRS.Easting / (10 ^ (5 - acc)), 0 ) ) .. ' ' .. string.format( '%0' .. acc .. 'd', routines.utils.round( MGRS.Northing / (10 ^ (5 - acc)), 0 ) )
.. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Northing/(10^(5-acc)), 0))
end end
end end
@@ -366,8 +354,7 @@ routines.tostringLL = function(lat, lon, acc, DMS)
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end end
return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' return string.format( '%02d', latDeg ) .. ' ' .. string.format( '%02d', latMin ) .. '\' ' .. string.format( secFrmtStr, latSec ) .. '"' .. latHemi .. ' ' .. string.format( '%02d', lonDeg ) .. ' ' .. string.format( '%02d', lonMin ) .. '\' ' .. string.format( secFrmtStr, lonSec ) .. '"' .. lonHemi
.. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
else -- degrees, decimal minutes. else -- degrees, decimal minutes.
latMin = routines.utils.round( latMin, acc ) latMin = routines.utils.round( latMin, acc )
@@ -391,8 +378,7 @@ routines.tostringLL = function(lat, lon, acc, DMS)
minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end end
return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' return string.format( '%02d', latDeg ) .. ' ' .. string.format( minFrmtStr, latMin ) .. '\'' .. latHemi .. ' ' .. string.format( '%02d', lonDeg ) .. ' ' .. string.format( minFrmtStr, lonMin ) .. '\'' .. lonHemi
.. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi
end end
end end
@@ -433,7 +419,6 @@ routines.getNorthCorrection = function(point) --gets the correction needed for
return math.atan2( north_posit.z - point.z, north_posit.x - point.x ) return math.atan2( north_posit.z - point.z, north_posit.x - point.x )
end end
do do
local idNum = 0 local idNum = 0
@@ -489,14 +474,7 @@ function routines.getRandPointInCircle(point, radius, innerRadius)
end end
routines.goRoute = function( group, path ) routines.goRoute = function( group, path )
local misTask = { local misTask = { id = 'Mission', params = { route = { points = routines.utils.deepCopy( path ) } } }
id = 'Mission',
params = {
route = {
points = routines.utils.deepCopy(path),
},
},
}
if type( group ) == 'string' then if type( group ) == 'string' then
group = Group.getByName( group ) group = Group.getByName( group )
end end
@@ -510,7 +488,6 @@ routines.goRoute = function(group, path)
return false return false
end end
-- Useful atomic functions from mist, ported. -- Useful atomic functions from mist, ported.
routines.ground = {} routines.ground = {}
@@ -569,9 +546,7 @@ routines.ground.buildWP = function(point, overRideForm, overRideSpeed)
end end
wp.type = 'Turning Point' wp.type = 'Turning Point'
return wp return wp
end end
routines.fixedWing.buildWP = function( point, WPtype, speed, alt, altType ) routines.fixedWing.buildWP = function( point, WPtype, speed, alt, altType )
@@ -702,7 +677,6 @@ routines.groupToRandomPoint = function(vars)
local headingDegrees = vars.headingDegrees local headingDegrees = vars.headingDegrees
local speed = vars.speed or routines.utils.kmphToMps( 20 ) local speed = vars.speed or routines.utils.kmphToMps( 20 )
local useRoads local useRoads
if not vars.disableRoads then if not vars.disableRoads then
useRoads = true useRoads = true
@@ -729,7 +703,6 @@ routines.groupToRandomPoint = function(vars)
offset.z = routines.utils.round( math.cos( heading + (math.pi / 2) ) * 50 + rndCoord.y, 3 ) offset.z = routines.utils.round( math.cos( heading + (math.pi / 2) ) * 50 + rndCoord.y, 3 )
path[#path + 1] = routines.ground.buildWP( posStart, form, speed ) path[#path + 1] = routines.ground.buildWP( posStart, form, speed )
if useRoads == true and ((point.x - posStart.x) ^ 2 + (point.z - posStart.z) ^ 2) ^ 0.5 > radius * 1.3 then if useRoads == true and ((point.x - posStart.x) ^ 2 + (point.z - posStart.z) ^ 2) ^ 0.5 > radius * 1.3 then
path[#path + 1] = routines.ground.buildWP( { ['x'] = posStart.x + 11, ['z'] = posStart.z + 11 }, 'off_road', speed ) path[#path + 1] = routines.ground.buildWP( { ['x'] = posStart.x + 11, ['z'] = posStart.z + 11 }, 'off_road', speed )
path[#path + 1] = routines.ground.buildWP( posStart, 'on_road', speed ) path[#path + 1] = routines.ground.buildWP( posStart, 'on_road', speed )
@@ -742,8 +715,6 @@ routines.groupToRandomPoint = function(vars)
path[#path + 1] = routines.ground.buildWP( rndCoord, form, speed ) path[#path + 1] = routines.ground.buildWP( rndCoord, form, speed )
routines.goRoute( group, path ) routines.goRoute( group, path )
return
end end
routines.groupRandomDistSelf = function( gpData, dist, form, heading, speed ) routines.groupRandomDistSelf = function( gpData, dist, form, heading, speed )
@@ -752,8 +723,6 @@ routines.groupRandomDistSelf = function(gpData, dist, form, heading, speed)
fakeZone.radius = dist or math.random( 300, 1000 ) fakeZone.radius = dist or math.random( 300, 1000 )
fakeZone.point = { x = pos.x, y, pos.y, z = pos.z } fakeZone.point = { x = pos.x, y, pos.y, z = pos.z }
routines.groupToRandomZone( gpData, fakeZone, form, heading, speed ) routines.groupToRandomZone( gpData, fakeZone, form, heading, speed )
return
end end
routines.groupToRandomZone = function( gpData, zone, form, heading, speed ) routines.groupToRandomZone = function( gpData, zone, form, heading, speed )
@@ -780,8 +749,6 @@ routines.groupToRandomZone = function(gpData, zone, form, heading, speed)
vars.point = routines.utils.zoneToVec3( zone ) vars.point = routines.utils.zoneToVec3( zone )
routines.groupToRandomPoint( vars ) routines.groupToRandomPoint( vars )
return
end end
routines.isTerrainValid = function( coord, terrainTypes ) -- vec2/3 and enum or table of acceptable terrain types routines.isTerrainValid = function( coord, terrainTypes ) -- vec2/3 and enum or table of acceptable terrain types
@@ -829,11 +796,8 @@ routines.groupToPoint = function(gpData, point, form, heading, speed, useRoads)
vars.disableRoads = useRoads vars.disableRoads = useRoads
vars.point = routines.utils.zoneToVec3( point ) vars.point = routines.utils.zoneToVec3( point )
routines.groupToRandomPoint( vars ) routines.groupToRandomPoint( vars )
return
end end
routines.getLeadPos = function( group ) routines.getLeadPos = function( group )
if type( group ) == 'string' then -- group name if type( group ) == 'string' then -- group name
group = Group.getByName( group ) group = Group.getByName( group )
@@ -934,7 +898,6 @@ routines.getBRString = function(vars)
end end
end end
-- Returns the Vec3 coordinates of the average position of the concentration of units most in the heading direction. -- Returns the Vec3 coordinates of the average position of the concentration of units most in the heading direction.
--[[ vars for routines.getLeadingPos: --[[ vars for routines.getLeadingPos:
vars.units - table of unit names vars.units - table of unit names
@@ -992,7 +955,6 @@ routines.getLeadingPos = function(vars)
end end
end end
--[[ vars for routines.getLeadingMGRSString: --[[ vars for routines.getLeadingMGRSString:
vars.units - table of unit names vars.units - table of unit names
vars.heading - direction vars.heading - direction
@@ -1026,8 +988,6 @@ routines.getLeadingLLString = function(vars)
end end
end end
--[[ vars for routines.getLeadingBRString: --[[ vars for routines.getLeadingBRString:
vars.units - table of unit names vars.units - table of unit names
vars.heading - direction, number vars.heading - direction, number
@@ -1083,11 +1043,7 @@ routines.msgMGRS = function(vars)
newText = text .. s newText = text .. s
end end
routines.message.add{ routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor }
text = newText,
displayTime = displayTime,
msgFor = msgFor
}
end end
--[[ vars for routines.msgLL --[[ vars for routines.msgLL
@@ -1114,15 +1070,10 @@ routines.msgLL = function(vars)
newText = text .. s newText = text .. s
end end
routines.message.add{ routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor }
text = newText,
displayTime = displayTime,
msgFor = msgFor
}
end end
--[[ --[[
vars.units- table of unit names (NOT unitNameTable- maybe this should change). vars.units- table of unit names (NOT unitNameTable- maybe this should change).
vars.ref - vec3 ref point, maybe overload for vec2 as well? vars.ref - vec3 ref point, maybe overload for vec2 as well?
@@ -1149,15 +1100,10 @@ routines.msgBR = function(vars)
newText = text .. s newText = text .. s
end end
routines.message.add{ routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor }
text = newText,
displayTime = displayTime,
msgFor = msgFor
}
end end
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
-- basically, just sub-types of routines.msgBR... saves folks the work of getting the ref point. -- basically, just sub-types of routines.msgBR... saves folks the work of getting the ref point.
--[[ --[[
@@ -1228,14 +1174,9 @@ routines.msgLeadingMGRS = function(vars)
newText = text .. s newText = text .. s
end end
routines.message.add{ routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor }
text = newText,
displayTime = displayTime,
msgFor = msgFor
}
end end
--[[ vars for routines.msgLeadingLL: --[[ vars for routines.msgLeadingLL:
vars.units - table of unit names vars.units - table of unit names
vars.heading - direction, number vars.heading - direction, number
@@ -1266,12 +1207,7 @@ routines.msgLeadingLL = function(vars)
newText = text .. s newText = text .. s
end end
routines.message.add{ routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor }
text = newText,
displayTime = displayTime,
msgFor = msgFor
}
end end
--[[ --[[
@@ -1306,23 +1242,22 @@ routines.msgLeadingBR = function(vars)
newText = text .. s newText = text .. s
end end
routines.message.add{ routines.message.add { text = newText, displayTime = displayTime, msgFor = msgFor }
text = newText,
displayTime = displayTime,
msgFor = msgFor
}
end end
function spairs( t, order ) function spairs( t, order )
-- collect the keys -- collect the keys
local keys = {} local keys = {}
for k in pairs(t) do keys[#keys+1] = k end for k in pairs( t ) do
keys[#keys + 1] = k
end
-- if order function given, sort by it by passing the table and keys a, b, -- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys -- otherwise just sort the keys
if order then if order then
table.sort(keys, function(a,b) return order(t, a, b) end) table.sort( keys, function( a, b )
return order( t, a, b )
end )
else else
table.sort( keys ) table.sort( keys )
end end
@@ -1337,7 +1272,6 @@ function spairs(t, order)
end end
end end
function routines.IsPartOfGroupInZones( CargoGroup, LandingZones ) function routines.IsPartOfGroupInZones( CargoGroup, LandingZones )
-- trace.f() -- trace.f()
@@ -1359,8 +1293,6 @@ function routines.IsPartOfGroupInZones( CargoGroup, LandingZones )
return CurrentZoneID return CurrentZoneID
end end
function routines.IsUnitInZones( TransportUnit, LandingZones ) function routines.IsUnitInZones( TransportUnit, LandingZones )
-- trace.f("", "routines.IsUnitInZones" ) -- trace.f("", "routines.IsUnitInZones" )
@@ -1441,7 +1373,6 @@ function routines.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius
end end
end end
function routines.IsStaticInZones( TransportStatic, LandingZones ) function routines.IsStaticInZones( TransportStatic, LandingZones )
-- trace.f() -- trace.f()
@@ -1474,7 +1405,6 @@ function routines.IsStaticInZones( TransportStatic, LandingZones )
return TransportZoneResult return TransportZoneResult
end end
function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius )
-- trace.f() -- trace.f()
@@ -1517,7 +1447,6 @@ function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius )
return Valid return Valid
end end
function routines.ValidateString( Variable, VariableName, Valid ) function routines.ValidateString( Variable, VariableName, Valid )
-- trace.f() -- trace.f()
@@ -1546,7 +1475,6 @@ function routines.ValidateNumber( Variable, VariableName, Valid )
-- trace.r( "", "", { Valid } ) -- trace.r( "", "", { Valid } )
return Valid return Valid
end end
function routines.ValidateGroup( Variable, VariableName, Valid ) function routines.ValidateGroup( Variable, VariableName, Valid )
@@ -1672,7 +1600,6 @@ end
routines.ground.patrolRoute = function( vars ) routines.ground.patrolRoute = function( vars )
local tempRoute = {} local tempRoute = {}
local useRoute = {} local useRoute = {}
local gpData = vars.gpData local gpData = vars.gpData
@@ -1698,7 +1625,6 @@ routines.ground.patrolRoute = function(vars)
routeProvided = true routeProvided = true
end end
local overRideSpeed = vars.speed or 'default' local overRideSpeed = vars.speed or 'default'
local pType = vars.pType local pType = vars.pType
local offRoadForm = vars.offRoadForm or 'default' local offRoadForm = vars.offRoadForm or 'default'
@@ -1707,7 +1633,6 @@ routines.ground.patrolRoute = function(vars)
if routeProvided == false and #tempRoute > 0 then if routeProvided == false and #tempRoute > 0 then
local posStart = routines.getLeadPos( gpData ) local posStart = routines.getLeadPos( gpData )
useRoute[#useRoute + 1] = routines.ground.buildWP( posStart, offRoadForm, overRideSpeed ) useRoute[#useRoute + 1] = routines.ground.buildWP( posStart, offRoadForm, overRideSpeed )
for i = 1, #tempRoute do for i = 1, #tempRoute do
local tempForm = tempRoute[i].action local tempForm = tempRoute[i].action
@@ -1729,7 +1654,6 @@ routines.ground.patrolRoute = function(vars)
tempSpeed = overRideSpeed tempSpeed = overRideSpeed
end end
useRoute[#useRoute + 1] = routines.ground.buildWP( tempRoute[i], tempForm, tempSpeed ) useRoute[#useRoute + 1] = routines.ground.buildWP( tempRoute[i], tempForm, tempSpeed )
end end
@@ -1751,24 +1675,10 @@ routines.ground.patrolRoute = function(vars)
cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize( newPatrol ) cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize( newPatrol )
cTask3[#cTask3 + 1] = ')' cTask3[#cTask3 + 1] = ')'
cTask3 = table.concat( cTask3 ) cTask3 = table.concat( cTask3 )
local tempTask = { local tempTask = { id = 'WrappedAction', params = { action = { id = 'Script', params = { command = cTask3 } } } }
id = 'WrappedAction',
params = {
action = {
id = 'Script',
params = {
command = cTask3,
},
},
},
}
useRoute[#useRoute].task = tempTask useRoute[#useRoute].task = tempTask
routines.goRoute( gpData, useRoute ) routines.goRoute( gpData, useRoute )
return
end end
routines.ground.patrol = function( gpData, pType, form, speed ) routines.ground.patrol = function( gpData, pType, form, speed )
@@ -1785,8 +1695,6 @@ routines.ground.patrol = function(gpData, pType, form, speed)
vars.speed = speed vars.speed = speed
routines.ground.patrolRoute( vars ) routines.ground.patrolRoute( vars )
return
end end
function routines.GetUnitHeight( CheckUnit ) function routines.GetUnitHeight( CheckUnit )
@@ -1803,11 +1711,8 @@ function routines.GetUnitHeight( CheckUnit )
-- trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight ) -- trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight )
return UnitHeight - LandHeight return UnitHeight - LandHeight
end end
Su34Status = { status = {} } Su34Status = { status = {} }
boardMsgRed = { statusMsg = "" } boardMsgRed = { statusMsg = "" }
boardMsgAll = { timeMsg = "" } boardMsgAll = { timeMsg = "" }
@@ -1815,7 +1720,6 @@ SpawnSettings = {}
Su34MenuPath = {} Su34MenuPath = {}
Su34Menus = 0 Su34Menus = 0
function Su34AttackCarlVinson( groupName ) function Su34AttackCarlVinson( groupName )
-- trace.menu("", "Su34AttackCarlVinson") -- trace.menu("", "Su34AttackCarlVinson")
local groupSu34 = Group.getByName( groupName ) local groupSu34 = Group.getByName( groupName )
@@ -1982,7 +1886,6 @@ function Su34OverviewStatus()
boardMsgRed.statusMsg = msg boardMsgRed.statusMsg = msg
end end
function UpdateBoardMsg() function UpdateBoardMsg()
-- trace.f() -- trace.f()
Su34OverviewStatus() Su34OverviewStatus()
@@ -2007,63 +1910,22 @@ function Su34Menu(groupName)
-- env.info(( 'Su34Menu(' .. groupName .. ')' )) -- env.info(( 'Su34Menu(' .. groupName .. ')' ))
local groupSu34 = Group.getByName( groupName ) local groupSu34 = Group.getByName( groupName )
if Su34Status.status[groupName] == 1 or if Su34Status.status[groupName] == 1 or Su34Status.status[groupName] == 2 or Su34Status.status[groupName] == 3 or Su34Status.status[groupName] == 4 or Su34Status.status[groupName] == 5 then
Su34Status.status[groupName] == 2 or
Su34Status.status[groupName] == 3 or
Su34Status.status[groupName] == 4 or
Su34Status.status[groupName] == 5 then
if Su34MenuPath[groupName] == nil then if Su34MenuPath[groupName] == nil then
if planeMenuPath == nil then if planeMenuPath == nil then
planeMenuPath = missionCommands.addSubMenuForCoalition( planeMenuPath = missionCommands.addSubMenuForCoalition( coalition.side.RED, "SU-34 anti-ship flights", nil )
coalition.side.RED,
"SU-34 anti-ship flights",
nil
)
end end
Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition( Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition( coalition.side.RED, "Flight " .. groupName, planeMenuPath )
coalition.side.RED,
"Flight " .. groupName,
planeMenuPath
)
missionCommands.addCommandForCoalition( missionCommands.addCommandForCoalition( coalition.side.RED, "Attack carrier Carl Vinson", Su34MenuPath[groupName], Su34AttackCarlVinson, groupName )
coalition.side.RED,
"Attack carrier Carl Vinson",
Su34MenuPath[groupName],
Su34AttackCarlVinson,
groupName
)
missionCommands.addCommandForCoalition( missionCommands.addCommandForCoalition( coalition.side.RED, "Attack ships in the west", Su34MenuPath[groupName], Su34AttackWest, groupName )
coalition.side.RED,
"Attack ships in the west",
Su34MenuPath[groupName],
Su34AttackWest,
groupName
)
missionCommands.addCommandForCoalition( missionCommands.addCommandForCoalition( coalition.side.RED, "Attack ships in the north", Su34MenuPath[groupName], Su34AttackNorth, groupName )
coalition.side.RED,
"Attack ships in the north",
Su34MenuPath[groupName],
Su34AttackNorth,
groupName
)
missionCommands.addCommandForCoalition( missionCommands.addCommandForCoalition( coalition.side.RED, "Hold position and await instructions", Su34MenuPath[groupName], Su34Orbit, groupName )
coalition.side.RED,
"Hold position and await instructions",
Su34MenuPath[groupName],
Su34Orbit,
groupName
)
missionCommands.addCommandForCoalition( missionCommands.addCommandForCoalition( coalition.side.RED, "Report status", Su34MenuPath[groupName], Su34OverviewStatus )
coalition.side.RED,
"Report status",
Su34MenuPath[groupName],
Su34OverviewStatus
)
end end
else else
if Su34MenuPath[groupName] then if Su34MenuPath[groupName] then
@@ -2207,32 +2069,23 @@ function EscortCarrier ( CarrierGroup, EscortPrefix, EscortLastWayPoint, EscortE
EscortMission.units[u].unitId = nil EscortMission.units[u].unitId = nil
end end
EscortMission.route.points[1].task = {
EscortMission.route.points[1].task = { id = "ComboTask", id = "ComboTask",
params = params = {
{ tasks = {
tasks = [1] = {
{
[1] =
{
enabled = true, enabled = true,
auto = false, auto = false,
id = "Escort", id = "Escort",
number = 1, number = 1,
params = params = {
{
lastWptIndexFlagChangedManually = false, lastWptIndexFlagChangedManually = false,
groupId = CarrierGroup:getID(), groupId = CarrierGroup:getID(),
lastWptIndex = nil, lastWptIndex = nil,
lastWptIndexFlag = false, lastWptIndexFlag = false,
engagementDistMax = EscortEngagementDistanceMax, engagementDistMax = EscortEngagementDistanceMax,
targetTypes = EscortTargetTypes, targetTypes = EscortTargetTypes,
pos = pos = { y = 20, x = 20, z = 0 } -- end of ["pos"]
{
y = 20,
x = 20,
z = 0,
} -- end of ["pos"]
} -- end of ["params"] } -- end of ["params"]
} -- end of [1] } -- end of [1]
} -- end of ["tasks"] } -- end of ["tasks"]
@@ -2325,7 +2178,6 @@ function getCarrierHeight( CarrierGroup )
else else
return 999999 return 999999
end end
end end
function GetUnitHeight( CheckUnit ) function GetUnitHeight( CheckUnit )
@@ -2340,16 +2192,13 @@ function GetUnitHeight( CheckUnit )
-- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) -- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight ))
return UnitHeight - LandHeight return UnitHeight - LandHeight
end end
_MusicTable = {} _MusicTable = {}
_MusicTable.Files = {} _MusicTable.Files = {}
_MusicTable.Queue = {} _MusicTable.Queue = {}
_MusicTable.FileCnt = 0 _MusicTable.FileCnt = 0
function MusicRegister( SndRef, SndFile, SndTime ) function MusicRegister( SndRef, SndFile, SndTime )
-- trace.f() -- trace.f()
@@ -2357,7 +2206,6 @@ function MusicRegister( SndRef, SndFile, SndTime )
env.info( ('MusicRegister: SndFile = ' .. SndFile) ) env.info( ('MusicRegister: SndFile = ' .. SndFile) )
env.info( ('MusicRegister: SndTime = ' .. SndTime) ) env.info( ('MusicRegister: SndTime = ' .. SndTime) )
_MusicTable.FileCnt = _MusicTable.FileCnt + 1 _MusicTable.FileCnt = _MusicTable.FileCnt + 1
_MusicTable.Files[_MusicTable.FileCnt] = {} _MusicTable.Files[_MusicTable.FileCnt] = {}
@@ -2368,7 +2216,6 @@ function MusicRegister( SndRef, SndFile, SndTime )
if not _MusicTable.Function then if not _MusicTable.Function then
_MusicTable.Function = routines.scheduleFunction( MusicScheduler, {}, timer.getTime() + 10, 10 ) _MusicTable.Function = routines.scheduleFunction( MusicScheduler, {}, timer.getTime() + 10, 10 )
end end
end end
function MusicToPlayer( SndRef, PlayerName, SndContinue ) function MusicToPlayer( SndRef, PlayerName, SndContinue )
@@ -2391,7 +2238,6 @@ function MusicToPlayer( SndRef, PlayerName, SndContinue )
end end
-- env.info(( 'MusicToPlayer: end' )) -- env.info(( 'MusicToPlayer: end' ))
end end
function MusicToGroup( SndRef, SndGroup, SndContinue ) function MusicToGroup( SndRef, SndGroup, SndContinue )
@@ -2494,9 +2340,6 @@ function MusicScheduler()
end end
end end
end end
end end
env.info( ('Init: Scripts Loaded v1.1') ) env.info( ('Init: Scripts Loaded v1.1') )

View File

@@ -1,7 +1,6 @@
--- **Utilities** DCS Simple Text-To-Speech (STTS). --- **Utilities** DCS Simple Text-To-Speech (STTS).
-- --
-- --
--
-- @module Utils.STTS -- @module Utils.STTS
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -15,7 +14,7 @@
-- --
-- # DCS Modification Required -- # DCS Modification Required
-- --
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitisation. -- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitization.
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)" -- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
-- Do this without DCS running to allow mission scripts to use os functions. -- Do this without DCS running to allow mission scripts to use os functions.
-- --
@@ -23,7 +22,7 @@
-- --
-- # USAGE: -- # USAGE:
-- --
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialise it -- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialize it
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission. -- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts. -- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
-- --
@@ -43,7 +42,7 @@
-- * OPTIONAL - Speed -10 to +10 -- * OPTIONAL - Speed -10 to +10
-- * OPTIONAL - Gender male, female or neuter -- * OPTIONAL - Gender male, female or neuter
-- * OPTIONAL - Culture - en-US, en-GB etc -- * OPTIONAL - Culture - en-US, en-GB etc
-- * OPTIONAL - Voice - a specfic voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line -- * OPTIONAL - Voice - a specific voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly -- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
-- --
-- --
@@ -80,7 +79,7 @@ STTS={
DIRECTORY = "", DIRECTORY = "",
SRS_PORT = 5002, SRS_PORT = 5002,
GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json", GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
EXECUTABLE="DCS-SR-ExternalAudio.exe", EXECUTABLE = "DCS-SR-ExternalAudio.exe"
} }
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER --- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
@@ -92,10 +91,9 @@ STTS.SRS_PORT = 5002
--- Google credentials file --- Google credentials file
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json" STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
--- DONT CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING --- DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe" STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
--- Function for UUID. --- Function for UUID.
function STTS.uuid() function STTS.uuid()
local random = math.random local random = math.random
@@ -112,16 +110,20 @@ end
function STTS.round( x, n ) function STTS.round( x, n )
n = math.pow( 10, n or 0 ) n = math.pow( 10, n or 0 )
x = x * n x = x * n
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end if x >= 0 then
x = math.floor( x + 0.5 )
else
x = math.ceil( x - 0.5 )
end
return x / n return x / n
end end
--- Function returns estimated speech time in seconds. --- Function returns estimated speech time in seconds.
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word so -- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so
-- --
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second -- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
-- --
-- So lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function: -- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
-- --
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min -- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
-- --
@@ -170,7 +172,6 @@ function STTS.TextToSpeech(message, freqs, modulations, volume, name, coalition,
volume = 1 volume = 1
speed = 1 speed = 1
message = message:gsub( "\"", "\\\"" ) message = message:gsub( "\"", "\\\"" )
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name ) local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
@@ -237,8 +238,7 @@ end
-- @param #string volume Volume, e.g. "0.5". -- @param #string volume Volume, e.g. "0.5".
function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point ) function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
local cmd = string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", local cmd = string.format( "start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1" )
STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1")
if point and type( point ) == "table" and point.x then if point and type( point ) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL( point ) local lat, lon, alt = coord.LOtoLL( point )

View File

@@ -1,4 +1,4 @@
--- This module contains derived utilities taken from the MIST framework, which are excellent tools to be reused in an OO environment. --- This module contains derived utilities taken from the MIST framework, as well as a lot of added helpers from the MOOSE community.
-- --
-- ### Authors: -- ### Authors:
-- --
@@ -7,6 +7,7 @@
-- ### Contributions: -- ### Contributions:
-- --
-- * FlightControl : Rework to OO framework. -- * FlightControl : Rework to OO framework.
-- * And many more
-- --
-- @module Utils -- @module Utils
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -51,6 +52,7 @@ BIGSMOKEPRESET = {
-- @field #string TheChannel The Channel map. -- @field #string TheChannel The Channel map.
-- @field #string Syria Syria map. -- @field #string Syria Syria map.
-- @field #string MarianaIslands Mariana Islands map. -- @field #string MarianaIslands Mariana Islands map.
-- @field #string Falklands South Atlantic map.
DCSMAP = { DCSMAP = {
Caucasus="Caucasus", Caucasus="Caucasus",
NTTR="Nevada", NTTR="Nevada",
@@ -58,7 +60,8 @@ DCSMAP = {
PersianGulf="PersianGulf", PersianGulf="PersianGulf",
TheChannel="TheChannel", TheChannel="TheChannel",
Syria="Syria", Syria="Syria",
MarianaIslands="MarianaIslands" MarianaIslands="MarianaIslands",
Falklands="Falklands",
} }
@@ -130,6 +133,62 @@ CALLSIGN={
Dublin=9, Dublin=9,
Perth=10, Perth=10,
}, },
F16={
Viper=9,
Venom=10,
Lobo=11,
Cowboy=12,
Python=13,
Rattler=14,
Panther=15,
Wolf=16,
Weasel=17,
Wild=18,
Ninja=19,
Jedi=20,
},
F18={
Hornet=9,
Squid=10,
Ragin=11,
Roman=12,
Sting=13,
Jury=14,
Jokey=15,
Ram=16,
Hawk=17,
Devil=18,
Check=19,
Snake=20,
},
F15E={
Dude=9,
Thud=10,
Gunny=11,
Trek=12,
Sniper=13,
Sled=14,
Best=15,
Jazz=16,
Rage=17,
Tahoe=18,
},
B1B={
Bone=9,
Dark=10,
Vader=11
},
B52={
Buff=9,
Dump=10,
Kenworth=11,
},
TransportAircraft={
Heavy=9,
Trash=10,
Cargo=11,
Ascot=12,
},
} --#CALLSIGN } --#CALLSIGN
--- Utilities static class. --- Utilities static class.
@@ -409,13 +468,17 @@ end
-- @param #number knots Speed in knots. -- @param #number knots Speed in knots.
-- @return #number Speed in m/s. -- @return #number Speed in m/s.
UTILS.KnotsToMps = function( knots ) UTILS.KnotsToMps = function( knots )
if type(knots) == "number" then
return knots / 1.94384 --* 1852 / 3600 return knots / 1.94384 --* 1852 / 3600
else
return 0
end
end end
--- Convert temperature from Celsius to Farenheit. --- Convert temperature from Celsius to Fahrenheit.
-- @param #number Celcius Temperature in degrees Celsius. -- @param #number Celcius Temperature in degrees Celsius.
-- @return #number Temperature in degrees Farenheit. -- @return #number Temperature in degrees Fahrenheit.
UTILS.CelciusToFarenheit = function( Celcius ) UTILS.CelsiusToFahrenheit = function( Celcius )
return Celcius * 9/5 + 32 return Celcius * 9/5 + 32
end end
@@ -748,7 +811,7 @@ function UTILS.BeaufortScale(speed)
return bn,bd return bn,bd
end end
--- Split string at seperators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua --- Split string at seperators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua).
-- @param #string str Sting to split. -- @param #string str Sting to split.
-- @param #string sep Speparator for split. -- @param #string sep Speparator for split.
-- @return #table Split text. -- @return #table Split text.
@@ -970,6 +1033,15 @@ function UTILS.VecDot(a, b)
return a.x*b.x + a.y*b.y + a.z*b.z return a.x*b.x + a.y*b.y + a.z*b.z
end end
--- Calculate the [dot product](https://en.wikipedia.org/wiki/Dot_product) of two 2D vectors. The result is a number.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return #number Scalar product of the two vectors a*b.
function UTILS.Vec2Dot(a, b)
return a.x*b.x + a.y*b.y
end
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector. --- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @return #number Norm of the vector. -- @return #number Norm of the vector.
@@ -977,6 +1049,13 @@ function UTILS.VecNorm(a)
return math.sqrt(UTILS.VecDot(a, a)) return math.sqrt(UTILS.VecDot(a, a))
end end
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 2D vector.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @return #number Norm of the vector.
function UTILS.Vec2Norm(a)
return math.sqrt(UTILS.Vec2Dot(a, a))
end
--- Calculate the distance between two 2D vectors. --- Calculate the distance between two 2D vectors.
-- @param DCS#Vec2 a Vector in 3D with x, y components. -- @param DCS#Vec2 a Vector in 3D with x, y components.
-- @param DCS#Vec2 b Vector in 3D with x, y components. -- @param DCS#Vec2 b Vector in 3D with x, y components.
@@ -1020,6 +1099,14 @@ function UTILS.VecSubstract(a, b)
return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z} return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
end end
--- Calculate the difference between two 2D vectors by substracting the x,y components from each other.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return DCS#Vec2 Vector c=a-b with c(i)=a(i)-b(i), i=x,y.
function UTILS.Vec2Substract(a, b)
return {x=a.x-b.x, y=a.y-b.y}
end
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other. --- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@@ -1028,6 +1115,14 @@ function UTILS.VecAdd(a, b)
return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z} return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
end end
--- Calculate the total vector of two 2D vectors by adding the x,y components of each other.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return DCS#Vec2 Vector c=a+b with c(i)=a(i)+b(i), i=x,y.
function UTILS.Vec2Add(a, b)
return {x=a.x+b.x, y=a.y+b.y}
end
--- Calculate the angle between two 3D vectors. --- Calculate the angle between two 3D vectors.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@@ -1059,6 +1154,17 @@ function UTILS.VecHdg(a)
return h return h
end end
--- Calculate "heading" of a 2D vector in the X-Y plane.
-- @param DCS#Vec2 a Vector in "D with x, y components.
-- @return #number Heading in degrees in [0,360).
function UTILS.Vec2Hdg(a)
local h=math.deg(math.atan2(a.y, a.x))
if h<0 then
h=h+360
end
return h
end
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg. --- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
-- @param #number h1 Heading one. -- @param #number h1 Heading one.
-- @param #number h2 Heading two. -- @param #number h2 Heading two.
@@ -1095,6 +1201,22 @@ function UTILS.VecTranslate(a, distance, angle)
return {x=TX, y=a.y, z=TY} return {x=TX, y=a.y, z=TY}
end end
--- Translate 2D vector in the 2D (x,z) plane.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param #number distance The distance to translate.
-- @param #number angle Rotation angle in degrees.
-- @return DCS#Vec2 Translated vector.
function UTILS.Vec2Translate(a, distance, angle)
local SX = a.x
local SY = a.y
local Radians=math.rad(angle or 0)
local TX=distance*math.cos(Radians)+SX
local TY=distance*math.sin(Radians)+SY
return {x=TX, y=TY}
end
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. --- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number angle Rotation angle in degrees. -- @param #number angle Rotation angle in degrees.
@@ -1115,6 +1237,25 @@ function UTILS.Rotate2D(a, angle)
return A return A
end end
--- Rotate 2D vector in the 2D (x,z) plane.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param #number angle Rotation angle in degrees.
-- @return DCS#Vec2 Vector rotated in the (x,y) plane.
function UTILS.Vec2Rotate2D(a, angle)
local phi=math.rad(angle)
local x=a.x
local y=a.y
local X=x*math.cos(phi)-y*math.sin(phi)
local Y=x*math.sin(phi)+y*math.cos(phi)
local A={x=X, y=Y}
return A
end
--- Converts a TACAN Channel/Mode couple into a frequency in Hz. --- Converts a TACAN Channel/Mode couple into a frequency in Hz.
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X". -- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
@@ -1198,26 +1339,6 @@ function UTILS.GetMissionDayOfYear(Time)
end end
--- Returns the current date.
-- @return #string Mission date in yyyy/mm/dd format.
-- @return #number The year anno domini.
-- @return #number The month.
-- @return #number The day.
function UTILS.GetDate()
-- Mission start date
local date, year, month, day=UTILS.GetDCSMissionDate()
local time=timer.getAbsTime()
local clock=UTILS.SecondsToClock(time, false)
local x=tonumber(UTILS.Split(clock, "+")[2])
local day=day+x
end
--- Returns the magnetic declination of the map. --- Returns the magnetic declination of the map.
-- Returned values for the current maps are: -- Returned values for the current maps are:
-- --
@@ -1228,6 +1349,7 @@ end
-- * The Cannel Map -10 (West) -- * The Cannel Map -10 (West)
-- * Syria +5 (East) -- * Syria +5 (East)
-- * Mariana Islands +2 (East) -- * Mariana Islands +2 (East)
-- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre -- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
-- @return #number Declination in degrees. -- @return #number Declination in degrees.
function UTILS.GetMagneticDeclination(map) function UTILS.GetMagneticDeclination(map)
@@ -1250,6 +1372,8 @@ function UTILS.GetMagneticDeclination(map)
declination=5 declination=5
elseif map==DCSMAP.MarianaIslands then elseif map==DCSMAP.MarianaIslands then
declination=2 declination=2
elseif map==DCSMAP.Falklands then
declination=12
else else
declination=0 declination=0
end end
@@ -1328,6 +1452,23 @@ function UTILS.GetModulationName(Modulation)
end end
--- Get the NATO reporting name of a unit type name
-- @param #number Typename The type name.
-- @return #string The Reporting name or "Bogey".
function UTILS.GetReportingName(Typename)
local typename = string.lower(Typename)
for name, value in pairs(ENUMS.ReportingName.NATO) do
local svalue = string.lower(value)
if string.find(typename,svalue,1,true) then
return name
end
end
return "Bogey"
end
--- Get the callsign name from its enumerator value --- Get the callsign name from its enumerator value
-- @param #number Callsign The enumerator callsign. -- @param #number Callsign The enumerator callsign.
-- @return #string The callsign name or "Ghostrider". -- @return #string The callsign name or "Ghostrider".
@@ -1357,6 +1498,48 @@ function UTILS.GetCallsignName(Callsign)
end end
end end
for name, value in pairs(CALLSIGN.B1B) do
if value==Callsign then
return name
end
end
for name, value in pairs(CALLSIGN.B52) do
if value==Callsign then
return name
end
end
for name, value in pairs(CALLSIGN.F15E) do
if value==Callsign then
return name
end
end
for name, value in pairs(CALLSIGN.F16) do
if value==Callsign then
return name
end
end
for name, value in pairs(CALLSIGN.F18) do
if value==Callsign then
return name
end
end
for name, value in pairs(CALLSIGN.FARP) do
if value==Callsign then
return name
end
end
for name, value in pairs(CALLSIGN.TransportAircraft) do
if value==Callsign then
return name
end
end
return "Ghostrider" return "Ghostrider"
end end
@@ -1542,15 +1725,21 @@ end
-- @return #number Os time in seconds. -- @return #number Os time in seconds.
function UTILS.GetOSTime() function UTILS.GetOSTime()
if os then if os then
return os.clock() local ts = 0
end local t = os.date("*t")
local s = t.sec
local m = t.min * 60
local h = t.hour * 3600
ts = s+m+h
return ts
else
return nil return nil
end end
end
--- Shuffle a table accoring to Fisher Yeates algorithm --- Shuffle a table accoring to Fisher Yeates algorithm
--@param #table table to be shuffled --@param #table t Table to be shuffled.
--@return #table --@return #table Shuffled table.
function UTILS.ShuffleTable(t) function UTILS.ShuffleTable(t)
if t == nil or type(t) ~= "table" then if t == nil or type(t) ~= "table" then
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument") BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
@@ -1568,60 +1757,99 @@ function UTILS.ShuffleTable(t)
return TempTable return TempTable
end end
--- Get a random element of a table.
--@param #table t Table.
--@param #boolean replace If `true`, the drawn element is replaced, i.e. not deleted.
--@return #number Table element.
function UTILS.GetRandomTableElement(t, replace)
if t == nil or type(t) ~= "table" then
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
return
end
math.random()
math.random()
math.random()
local r=math.random(#t)
local element=t[r]
if not replace then
table.remove(t, r)
end
return element
end
--- (Helicopter) Check if one loading door is open. --- (Helicopter) Check if one loading door is open.
--@param #string unit_name Unit name to be checked --@param #string unit_name Unit name to be checked
--@return #boolean Outcome - true if a (loading door) is open, false if not, nil if none exists. --@return #boolean Outcome - true if a (loading door) is open, false if not, nil if none exists.
function UTILS.IsLoadingDoorOpen( unit_name ) function UTILS.IsLoadingDoorOpen( unit_name )
local ret_val = false
local unit = Unit.getByName(unit_name) local unit = Unit.getByName(unit_name)
if unit ~= nil then if unit ~= nil then
local type_name = unit:getTypeName() local type_name = unit:getTypeName()
BASE:T("TypeName = ".. type_name)
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0 then if type_name == "Mi-8MT" and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0) then
BASE:T(unit_name .. " Cargo doors are open or cargo door not present") BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
ret_val = true return true
end end
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then if type_name == "Mi-24P" and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1) then
BASE:T(unit_name .. " a side door is open") BASE:T(unit_name .. " a side door is open")
ret_val = true return true
end end
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then if type_name == "UH-1H" and (unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1) then
BASE:T(unit_name .. " a side door is open ") BASE:T(unit_name .. " a side door is open ")
ret_val = true return true
end end
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then if string.find(type_name, "SA342" ) and (unit:getDrawArgumentValue(34) == 1) then
BASE:T(unit_name .. " front door(s) are open") BASE:T(unit_name .. " front door(s) are open or doors removed")
ret_val = true return true
end end
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1 then if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1) then
BASE:T(unit_name .. " rear doors are open") BASE:T(unit_name .. " rear doors are open")
ret_val = true return true
end end
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then
BASE:T(unit_name .. " para doors are open") BASE:T(unit_name .. " para doors are open")
ret_val = true return true
end end
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1217) == 1 then if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1217) == 1) then
BASE:T(unit_name .. " side door is open") BASE:T(unit_name .. " side door is open")
ret_val = true return true
end end
if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers
BASE:T(unit_name .. " door is open") BASE:T(unit_name .. " door is open")
ret_val = true return true
end end
if ret_val == false then if string.find(type_name, "UH-60L") and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
BASE:T(unit_name .. " all doors are closed") BASE:T(unit_name .. " cargo door is open")
return true
end end
return ret_val
if string.find(type_name, "UH-60L" ) and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then
BASE:T(unit_name .. " front door(s) are open")
return true
end
if type_name == "AH-64D_BLK_II" then
BASE:T(unit_name .. " front door(s) are open")
return true -- no doors on this one ;)
end
return false
end -- nil end -- nil
@@ -1772,3 +2000,472 @@ function UTILS.GenerateLaserCodes()
end end
return jtacGeneratedLaserCodes return jtacGeneratedLaserCodes
end end
--- Function to save an object to a file
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file. Existing file will be overwritten.
-- @param #table Data The LUA data structure to save. This will be e.g. a table of text lines with an \\n at the end of each line.
-- @return #boolean outcome True if saving is possible, else false.
function UTILS.SaveToFile(Path,Filename,Data)
-- Thanks to @FunkyFranky
-- Check io module is available.
if not io then
BASE:E("ERROR: io not desanitized. Can't save current file.")
return false
end
-- Check default path.
if Path==nil and not lfs then
BASE:E("WARNING: lfs not desanitized. File will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
end
-- Set path or default.
local path = nil
if lfs then
path=Path or lfs.writedir()
end
-- Set file name.
local filename=Filename
if path~=nil then
filename=path.."\\"..filename
end
-- write
local f = assert(io.open(filename, "wb"))
f:write(Data)
f:close()
return true
end
--- Function to save an object to a file
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #boolean outcome True if reading is possible and successful, else false.
-- @return #table data The data read from the filesystem (table of lines of text). Each line is one single #string!
function UTILS.LoadFromFile(Path,Filename)
-- Thanks to @FunkyFranky
-- Check io module is available.
if not io then
BASE:E("ERROR: io not desanitized. Can't save current state.")
return false
end
-- Check default path.
if Path==nil and not lfs then
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
end
-- Set path or default.
local path = nil
if lfs then
path=Path or lfs.writedir()
end
-- Set file name.
local filename=Filename
if path~=nil then
filename=path.."\\"..filename
end
-- Check if file exists.
local exists=UTILS.CheckFileExists(Path,Filename)
if not exists then
BASE:E(string.format("ERROR: File %s does not exist!",filename))
return false
end
-- read
local file=assert(io.open(filename, "rb"))
local loadeddata = {}
for line in file:lines() do
loadeddata[#loadeddata+1] = line
end
file:close()
return true, loadeddata
end
--- Function to check if a file exists.
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #boolean outcome True if reading is possible, else false.
function UTILS.CheckFileExists(Path,Filename)
-- Thanks to @FunkyFranky
-- Function that check if a file exists.
local function _fileexists(name)
local f=io.open(name,"r")
if f~=nil then
io.close(f)
return true
else
return false
end
end
-- Check io module is available.
if not io then
BASE:E("ERROR: io not desanitized. Can't save current state.")
return false
end
-- Check default path.
if Path==nil and not lfs then
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
end
-- Set path or default.
local path = nil
if lfs then
path=Path or lfs.writedir()
end
-- Set file name.
local filename=Filename
if path~=nil then
filename=path.."\\"..filename
end
-- Check if file exists.
local exists=_fileexists(filename)
if not exists then
BASE:E(string.format("ERROR: File %s does not exist!",filename))
return false
else
return true
end
end
--- Function to save the state of a list of groups found by name
-- @param #table List Table of strings with groupnames
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #boolean outcome True if saving is successful, else false.
-- @usage
-- We will go through the list and find the corresponding group and save the current group size (0 when dead).
-- These groups are supposed to be put on the map in the ME and have *not* moved (e.g. stationary SAM sites).
-- Position is still saved for your usage.
-- The idea is to reduce the number of units when reloading the data again to restart the saved mission.
-- The data will be a simple comma separated list of groupname and size, with one header line.
function UTILS.SaveStationaryListOfGroups(List,Path,Filename)
local filename = Filename or "StateListofGroups"
local data = "--Save Stationary List of Groups: "..Filename .."\n"
for _,_group in pairs (List) do
local group = GROUP:FindByName(_group) -- Wrapper.Group#GROUP
if group and group:IsAlive() then
local units = group:CountAliveUnits()
local position = group:GetVec3()
data = string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z)
else
data = string.format("%s%s,0,0,0,0\n",data,_group)
end
end
-- save the data
local outcome = UTILS.SaveToFile(Path,Filename,data)
return outcome
end
--- Function to save the state of a set of Wrapper.Group#GROUP objects.
-- @param Core.Set#SET_BASE Set of objects to save
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #boolean outcome True if saving is successful, else false.
-- @usage
-- We will go through the set and find the corresponding group and save the current group size and current position.
-- The idea is to respawn the groups **spawned during an earlier run of the mission** at the given location and reduce
-- the number of units in the group when reloading the data again to restart the saved mission. Note that *dead* groups
-- cannot be covered with this.
-- **Note** Do NOT use dashes or hashes in group template names (-,#)!
-- The data will be a simple comma separated list of groupname and size, with one header line.
-- The current task/waypoint/etc cannot be restored.
function UTILS.SaveSetOfGroups(Set,Path,Filename)
local filename = Filename or "SetOfGroups"
local data = "--Save SET of groups: "..Filename .."\n"
local List = Set:GetSetObjects()
for _,_group in pairs (List) do
local group = _group -- Wrapper.Group#GROUP
if group and group:IsAlive() then
local name = group:GetName()
local template = string.gsub(name,"-(.+)$","")
if string.find(template,"#") then
template = string.gsub(name,"#(%d+)$","")
end
local units = group:CountAliveUnits()
local position = group:GetVec3()
data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z)
end
end
-- save the data
local outcome = UTILS.SaveToFile(Path,Filename,data)
return outcome
end
--- Function to save the state of a set of Wrapper.Static#STATIC objects.
-- @param Core.Set#SET_BASE Set of objects to save
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #boolean outcome True if saving is successful, else false.
-- @usage
-- We will go through the set and find the corresponding static and save the current name and postion when alive.
-- The data will be a simple comma separated list of name and state etc, with one header line.
function UTILS.SaveSetOfStatics(Set,Path,Filename)
local filename = Filename or "SetOfStatics"
local data = "--Save SET of statics: "..Filename .."\n"
local List = Set:GetSetObjects()
for _,_group in pairs (List) do
local group = _group -- Wrapper.Static#STATIC
if group and group:IsAlive() then
local name = group:GetName()
local position = group:GetVec3()
data = string.format("%s%s,%d,%d,%d\n",data,name,position.x,position.y,position.z)
end
end
-- save the data
local outcome = UTILS.SaveToFile(Path,Filename,data)
return outcome
end
--- Function to save the state of a list of statics found by name
-- @param #table List Table of strings with statics names
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #boolean outcome True if saving is successful, else false.
-- @usage
-- We will go through the list and find the corresponding static and save the current alive state as 1 (0 when dead).
-- Position is saved for your usage. **Note** this works on UNIT-name level.
-- The idea is to reduce the number of units when reloading the data again to restart the saved mission.
-- The data will be a simple comma separated list of name and state etc, with one header line.
function UTILS.SaveStationaryListOfStatics(List,Path,Filename)
local filename = Filename or "StateListofStatics"
local data = "--Save Stationary List of Statics: "..Filename .."\n"
for _,_group in pairs (List) do
local group = STATIC:FindByName(_group,false) -- Wrapper.Static#STATIC
if group and group:IsAlive() then
local position = group:GetVec3()
data = string.format("%s%s,1,%d,%d,%d\n",data,_group,position.x,position.y,position.z)
else
data = string.format("%s%s,0,0,0,0\n",data,_group)
end
end
-- save the data
local outcome = UTILS.SaveToFile(Path,Filename,data)
return outcome
end
--- Load back a stationary list of groups from file.
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @param #boolean Reduce If false, existing loaded groups will not be reduced to fit the saved number.
-- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read.
function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce)
local reduce = true
if Reduce == false then reduce = false end
local filename = Filename or "StateListofGroups"
local datatable = {}
if UTILS.CheckFileExists(Path,filename) then
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
-- remove header
table.remove(loadeddata, 1)
for _id,_entry in pairs (loadeddata) do
local dataset = UTILS.Split(_entry,",")
-- groupname,units,position.x,position.y,position.z
local groupname = dataset[1]
local size = tonumber(dataset[2])
local posx = tonumber(dataset[3])
local posy = tonumber(dataset[4])
local posz = tonumber(dataset[5])
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) }
if reduce then
local actualgroup = GROUP:FindByName(groupname)
if actualgroup and actualgroup:IsAlive() and actualgroup:CountAliveUnits() > size then
local reduction = actualgroup:CountAliveUnits() - size
BASE:I("Reducing groupsize by ".. reduction .. " units!")
-- reduce existing group
local units = actualgroup:GetUnits()
local units2 = UTILS.ShuffleTable(units) -- randomize table
for i=1,reduction do
units2[i]:Destroy(false)
end
end
end
table.insert(datatable,data)
end
else
return nil
end
return datatable
end
--- Load back a SET of groups from file.
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @param #boolean Spawn If set to false, do not re-spawn the groups loaded in location and reduce to size.
-- @return Core.Set#SET_GROUP Set of GROUP objects.
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate }`
function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
local spawn = true
if Spawn == false then spawn = false end
BASE:I("Spawn = "..tostring(spawn))
local filename = Filename or "SetOfGroups"
local setdata = SET_GROUP:New()
local datatable = {}
if UTILS.CheckFileExists(Path,filename) then
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
-- remove header
table.remove(loadeddata, 1)
for _id,_entry in pairs (loadeddata) do
local dataset = UTILS.Split(_entry,",")
-- groupname,template,units,position.x,position.y,position.z
local groupname = dataset[1]
local template = dataset[2]
local size = tonumber(dataset[3])
local posx = tonumber(dataset[4])
local posy = tonumber(dataset[5])
local posz = tonumber(dataset[6])
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
local group=nil
local data = { groupname=groupname, size=size, coordinate=coordinate }
table.insert(datatable,data)
if spawn then
local group = SPAWN:New(groupname)
:InitDelayOff()
:OnSpawnGroup(
function(spwndgrp)
setdata:AddObject(spwndgrp)
local actualsize = spwndgrp:CountAliveUnits()
if actualsize > size then
local reduction = actualsize-size
-- reduce existing group
local units = spwndgrp:GetUnits()
local units2 = UTILS.ShuffleTable(units) -- randomize table
for i=1,reduction do
units2[i]:Destroy(false)
end
end
end
)
:SpawnFromCoordinate(coordinate)
end
end
else
return nil
end
if spawn then
return setdata
else
return datatable
end
end
--- Load back a SET of statics from file.
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return Core.Set#SET_STATIC Set SET_STATIC containing the static objects.
function UTILS.LoadSetOfStatics(Path,Filename)
local filename = Filename or "SetOfStatics"
local datatable = SET_STATIC:New()
if UTILS.CheckFileExists(Path,filename) then
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
-- remove header
table.remove(loadeddata, 1)
for _id,_entry in pairs (loadeddata) do
local dataset = UTILS.Split(_entry,",")
-- staticname,position.x,position.y,position.z
local staticname = dataset[1]
local posx = tonumber(dataset[2])
local posy = tonumber(dataset[3])
local posz = tonumber(dataset[4])
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
datatable:AddObject(STATIC:FindByName(staticname,false))
end
else
return nil
end
return datatable
end
--- Load back a stationary list of statics from file.
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @param #boolean Reduce If false, do not destroy the units with size=0.
-- @return #table Table of data objects (tables) containing staticname, size (0=dead else 1), coordinate and the static object.
-- Returns nil when file cannot be read.
function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce)
local reduce = true
if Reduce == false then reduce = false end
local filename = Filename or "StateListofStatics"
local datatable = {}
if UTILS.CheckFileExists(Path,filename) then
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
-- remove header
table.remove(loadeddata, 1)
for _id,_entry in pairs (loadeddata) do
local dataset = UTILS.Split(_entry,",")
-- staticname,units(1/0),position.x,position.y,position.z)
local staticname = dataset[1]
local size = tonumber(dataset[2])
local posx = tonumber(dataset[3])
local posy = tonumber(dataset[4])
local posz = tonumber(dataset[5])
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
local data = { staticname=staticname, size=size, coordinate=coordinate, static=STATIC:FindByName(staticname,false) }
table.insert(datatable,data)
if size==0 and reduce then
local static = STATIC:FindByName(staticname,false)
if static then
static:Destroy(false)
end
end
end
else
return nil
end
return datatable
end
--- Heading Degrees (0-360) to Cardinal
-- @param #number Heading The heading
-- @return #string Cardinal, e.g. "NORTH"
function UTILS.BearingToCardinal(Heading)
if Heading >= 0 and Heading <= 22 then return "North"
elseif Heading >= 23 and Heading <= 66 then return "North-East"
elseif Heading >= 67 and Heading <= 101 then return "East"
elseif Heading >= 102 and Heading <= 146 then return "South-East"
elseif Heading >= 147 and Heading <= 201 then return "South"
elseif Heading >= 202 and Heading <= 246 then return "South-West"
elseif Heading >= 247 and Heading <= 291 then return "West"
elseif Heading >= 292 and Heading <= 338 then return "North-West"
elseif Heading >= 339 then return "North"
end
end
--- Create a BRAA NATO call string BRAA between two GROUP objects
-- @param Wrapper.Group#GROUP FromGrp GROUP object
-- @param Wrapper.Group#GROUP ToGrp GROUP object
-- @return #string Formatted BRAA NATO call
function UTILS.ToStringBRAANATO(FromGrp,ToGrp)
local BRAANATO = "Merged."
local GroupNumber = FromGrp:GetSize()
local GroupWords = "Singleton"
if GroupNumber == 2 then GroupWords = "Two-Ship"
elseif GroupNumber >= 3 then GroupWords = "Heavy"
end
local grpLeadUnit = ToGrp:GetUnit(1)
local tgtCoord = grpLeadUnit:GetCoordinate()
local currentCoord = FromGrp:GetCoordinate()
local hdg = UTILS.Round(ToGrp:GetHeading()/100,1)*100
local bearing = UTILS.Round(currentCoord:HeadingTo(tgtCoord),0)
local rangeMetres = tgtCoord:Get2DDistance(currentCoord)
local rangeNM = UTILS.Round( UTILS.MetersToNM(rangeMetres), 0)
local aspect = tgtCoord:ToStringAspect(currentCoord)
local alt = UTILS.Round(UTILS.MetersToFeet(grpLeadUnit:GetAltitude())/1000,0)--*1000
local track = UTILS.BearingToCardinal(hdg)
if rangeNM > 3 then
if aspect == "" then
BRAANATO = string.format("%s, BRA, %03d, %d miles, Angels %d, Track %s",GroupWords,bearing, rangeNM, alt, track)
else
BRAANATO = string.format("%s, BRAA, %03d, %d miles, Angels %d, %s, Track %s",GroupWords, bearing, rangeNM, alt, aspect, track)
end
end
return BRAANATO
end

View File

@@ -11,7 +11,6 @@
-- @module Wrapper.Airbase -- @module Wrapper.Airbase
-- @image Wrapper_Airbase.JPG -- @image Wrapper_Airbase.JPG
--- @type AIRBASE --- @type AIRBASE
-- @field #string ClassName Name of the class, i.e. "AIRBASE". -- @field #string ClassName Name of the class, i.e. "AIRBASE".
-- @field #table CategoryName Names of airbase categories. -- @field #table CategoryName Names of airbase categories.
@@ -52,7 +51,7 @@
-- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object. -- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
-- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name. -- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
-- --
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil). -- IMPORTANT: ONE SHOULD NEVER SANITIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
-- --
-- ## DCS Airbase APIs -- ## DCS Airbase APIs
-- --
@@ -137,7 +136,7 @@ AIRBASE.Caucasus = {
-- * AIRBASE.Nevada.Laughlin_Airport -- * AIRBASE.Nevada.Laughlin_Airport
-- * AIRBASE.Nevada.Lincoln_County -- * AIRBASE.Nevada.Lincoln_County
-- * AIRBASE.Nevada.Mesquite -- * AIRBASE.Nevada.Mesquite
-- * AIRBASE.Nevada.Mina_Airport_3Q0 -- * AIRBASE.Nevada.Mina_Airport
-- * AIRBASE.Nevada.North_Las_Vegas -- * AIRBASE.Nevada.North_Las_Vegas
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip -- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
-- * AIRBASE.Nevada.Tonopah_Airport -- * AIRBASE.Nevada.Tonopah_Airport
@@ -145,23 +144,23 @@ AIRBASE.Caucasus = {
-- --
-- @field Nevada -- @field Nevada
AIRBASE.Nevada = { AIRBASE.Nevada = {
["Creech_AFB"] = "Creech AFB", ["Creech_AFB"] = "Creech",
["Groom_Lake_AFB"] = "Groom Lake AFB", ["Groom_Lake_AFB"] = "Groom Lake",
["McCarran_International_Airport"] = "McCarran International Airport", ["McCarran_International_Airport"] = "McCarran International",
["Nellis_AFB"] = "Nellis AFB", ["Nellis_AFB"] = "Nellis",
["Beatty_Airport"] = "Beatty Airport", ["Beatty_Airport"] = "Beatty",
["Boulder_City_Airport"] = "Boulder City Airport", ["Boulder_City_Airport"] = "Boulder City",
["Echo_Bay"] = "Echo Bay", ["Echo_Bay"] = "Echo Bay",
["Henderson_Executive_Airport"] = "Henderson Executive Airport", ["Henderson_Executive_Airport"] = "Henderson Executive",
["Jean_Airport"] = "Jean Airport", ["Jean_Airport"] = "Jean",
["Laughlin_Airport"] = "Laughlin Airport", ["Laughlin_Airport"] = "Laughlin",
["Lincoln_County"] = "Lincoln County", ["Lincoln_County"] = "Lincoln County",
["Mesquite"] = "Mesquite", ["Mesquite"] = "Mesquite",
["Mina_Airport_3Q0"] = "Mina Airport 3Q0", ["Mina_Airport"] = "Mina",
["North_Las_Vegas"] = "North Las Vegas", ["North_Las_Vegas"] = "North Las Vegas",
["Pahute_Mesa_Airstrip"] = "Pahute Mesa Airstrip", ["Pahute_Mesa_Airstrip"] = "Pahute Mesa",
["Tonopah_Airport"] = "Tonopah Airport", ["Tonopah_Airport"] = "Tonopah",
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield", ["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range",
} }
--- Airbases of the Normandy map: --- Airbases of the Normandy map:
@@ -316,6 +315,9 @@ AIRBASE.PersianGulf = {
-- * AIRBASE.TheChannel.Lympne -- * AIRBASE.TheChannel.Lympne
-- * AIRBASE.TheChannel.Detling -- * AIRBASE.TheChannel.Detling
-- * AIRBASE.TheChannel.High_Halden -- * AIRBASE.TheChannel.High_Halden
-- * AIRBASE.TheChannel.Biggin_Hill
-- * AIRBASE.TheChannel.Eastchurch
-- * AIRBASE.TheChannel.Headcorn
-- --
-- @field TheChannel -- @field TheChannel
AIRBASE.TheChannel = { AIRBASE.TheChannel = {
@@ -328,6 +330,9 @@ AIRBASE.TheChannel = {
["Lympne"] = "Lympne", ["Lympne"] = "Lympne",
["Detling"] = "Detling", ["Detling"] = "Detling",
["High_Halden"] = "High Halden", ["High_Halden"] = "High Halden",
["Biggin_Hill"] = "Biggin Hill",
["Eastchurch"] = "Eastchurch",
["Headcorn"] = "Headcorn",
} }
--- Airbases of the Syria map: --- Airbases of the Syria map:
@@ -346,7 +351,6 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Wujah_Al_Hajar -- * AIRBASE.Syria.Wujah_Al_Hajar
-- * AIRBASE.Syria.Al_Dumayr -- * AIRBASE.Syria.Al_Dumayr
-- * AIRBASE.Syria.Gazipasa -- * AIRBASE.Syria.Gazipasa
-- * AIRBASE.Syria.Ru_Convoy_4
-- * AIRBASE.Syria.Hatay -- * AIRBASE.Syria.Hatay
-- * AIRBASE.Syria.Nicosia -- * AIRBASE.Syria.Nicosia
-- * AIRBASE.Syria.Pinarbashi -- * AIRBASE.Syria.Pinarbashi
@@ -364,7 +368,6 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Akrotiri -- * AIRBASE.Syria.Akrotiri
-- * AIRBASE.Syria.Naqoura -- * AIRBASE.Syria.Naqoura
-- * AIRBASE.Syria.Gaziantep -- * AIRBASE.Syria.Gaziantep
-- * AIRBASE.Syria.CVN_71
-- * AIRBASE.Syria.Sayqal -- * AIRBASE.Syria.Sayqal
-- * AIRBASE.Syria.Tiyas -- * AIRBASE.Syria.Tiyas
-- * AIRBASE.Syria.Shayrat -- * AIRBASE.Syria.Shayrat
@@ -385,6 +388,17 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Beirut_Rafic_Hariri -- * AIRBASE.Syria.Beirut_Rafic_Hariri
-- * AIRBASE.Syria.An_Nasiriyah -- * AIRBASE.Syria.An_Nasiriyah
-- * AIRBASE.Syria.Abu_al_Duhur -- * AIRBASE.Syria.Abu_al_Duhur
-- * AIRBASE.Syria.At_Tanf
-- * AIRBASE.Syria.H3
-- * AIRBASE.Syria.H3_Northwest
-- * AIRBASE.Syria.H3_Southwest
-- * AIRBASE.Syria.Kharab_Ishk
-- * AIRBASE.Syria.Raj_al_Issa_East
-- * AIRBASE.Syria.Raj_al_Issa_West
-- * AIRBASE.Syria.Ruwayshid
-- * AIRBASE.Syria.Sanliurfa
-- * AIRBASE.Syria.Tal_Siman
-- * AIRBASE.Syria.Deir_ez_Zor
-- --
--@field Syria --@field Syria
AIRBASE.Syria={ AIRBASE.Syria={
@@ -402,7 +416,7 @@ AIRBASE.Syria={
["Wujah_Al_Hajar"]="Wujah Al Hajar", ["Wujah_Al_Hajar"]="Wujah Al Hajar",
["Al_Dumayr"]="Al-Dumayr", ["Al_Dumayr"]="Al-Dumayr",
["Gazipasa"]="Gazipasa", ["Gazipasa"]="Gazipasa",
["Ru_Convoy_4"]="Ru Convoy-4", -- ["Ru_Convoy_4"]="Ru Convoy-4",
["Hatay"]="Hatay", ["Hatay"]="Hatay",
["Nicosia"]="Nicosia", ["Nicosia"]="Nicosia",
["Pinarbashi"]="Pinarbashi", ["Pinarbashi"]="Pinarbashi",
@@ -440,10 +454,19 @@ AIRBASE.Syria={
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri", ["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
["An_Nasiriyah"]="An Nasiriyah", ["An_Nasiriyah"]="An Nasiriyah",
["Abu_al_Duhur"]="Abu al-Duhur", ["Abu_al_Duhur"]="Abu al-Duhur",
["At_Tanf"]="At Tanf",
["H3"]="H3",
["H3_Northwest"]="H3 Northwest",
["H3_Southwest"]="H3 Southwest",
["Kharab_Ishk"]="Kharab Ishk",
["Raj_al_Issa_East"]="Raj al Issa East",
["Raj_al_Issa_West"]="Raj al Issa West",
["Ruwayshid"]="Ruwayshid",
["Sanliurfa"]="Sanliurfa",
["Tal_Siman"]="Tal Siman",
["Deir_ez_Zor"] = "Deir ez-Zor",
} }
--- Airbases of the Mariana Islands map: --- Airbases of the Mariana Islands map:
-- --
-- * AIRBASE.MarianaIslands.Rota_Intl -- * AIRBASE.MarianaIslands.Rota_Intl
@@ -463,6 +486,39 @@ AIRBASE.MarianaIslands={
["Olf_Orote"] = "Olf Orote", ["Olf_Orote"] = "Olf Orote",
} }
--- Airbases of the South Atlantic map:
--
-- * AIRBASE.SouthAtlantic.Port_Stanley
-- * AIRBASE.SouthAtlantic.Mount_Pleasant
-- * AIRBASE.SouthAtlantic.San_Carlos_FOB
-- * AIRBASE.SouthAtlantic.Rio_Grande
-- * AIRBASE.SouthAtlantic.Rio_Gallegos
-- * AIRBASE.SouthAtlantic.Ushuaia
-- * AIRBASE.SouthAtlantic.Ushuaia_Helo_Port
-- * AIRBASE.SouthAtlantic.Punta_Arenas
-- * AIRBASE.SouthAtlantic.Pampa_Guanaco
-- * AIRBASE.SouthAtlantic.San_Julian
-- * AIRBASE.SouthAtlantic.Puerto_Williams
-- * AIRBASE.SouthAtlantic.Puerto_Natales
-- * AIRBASE.SouthAtlantic.El_Calafate
--
--@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",
["Puerto_Williams"]="Puerto Williams",
["Puerto_Natales"]="Puerto Natales",
["El_Calafate"]="El Calafate",
}
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
-- @type AIRBASE.ParkingSpot -- @type AIRBASE.ParkingSpot
@@ -772,7 +828,6 @@ function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist)
return self return self
end end
--- Get category of airbase. --- Get category of airbase.
-- @param #AIRBASE self -- @param #AIRBASE self
-- @return #number Category of airbase from GetDesc().category. -- @return #number Category of airbase from GetDesc().category.
@@ -1112,8 +1167,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
for _, _spot in pairs( parkingdata ) do for _, _spot in pairs( parkingdata ) do
-- Mark text. -- Mark text.
local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m", local _text = string.format( "Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m", _spot.TerminalID, _spot.TerminalType, tostring( _spot.Free ), tostring( _spot.TOAC ), _spot.TerminalID0, _spot.DistToRwy )
_spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
-- Create mark on the F10 map. -- Create mark on the F10 map.
if mark then if mark then
@@ -1121,8 +1175,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
end end
-- Info to DCS.log file. -- Info to DCS.log file.
local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", local _text = string.format( "%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", airbasename, _spot.TerminalID, _spot.TerminalType, tostring( _spot.Free ), tostring( _spot.TOAC ), _spot.TerminalID0, _spot.DistToRwy )
airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
self:E( _text ) self:E( _text )
end end
end end
@@ -1197,7 +1250,6 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
az = 17 -- width az = 17 -- width
end end
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size! -- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
local _nspots = nspots or group:GetSize() local _nspots = nspots or group:GetSize()
@@ -1344,7 +1396,6 @@ function AIRBASE:_CheckParkingLists(TerminalID)
end end
end end
-- Check if a whitelist was defined. -- Check if a whitelist was defined.
if self.parkingWhitelist and #self.parkingWhitelist > 0 then if self.parkingWhitelist and #self.parkingWhitelist > 0 then
for _, terminalID in pairs( self.parkingWhitelist or {} ) do for _, terminalID in pairs( self.parkingWhitelist or {} ) do
@@ -1447,7 +1498,6 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- Airbase name. -- Airbase name.
local name = self:GetName() local name = self:GetName()
-- Exceptions -- Exceptions
if name == AIRBASE.Nevada.Jean_Airport or if name == AIRBASE.Nevada.Jean_Airport or
name == AIRBASE.Nevada.Creech_AFB or name == AIRBASE.Nevada.Creech_AFB or
@@ -1535,7 +1585,6 @@ function AIRBASE:GetRunwayData(magvar, mark)
return j return j
end end
for i = 1, N do for i = 1, N do
-- Get the other spawn point coordinate. -- Get the other spawn point coordinate.

View File

@@ -11,14 +11,11 @@
-- @module Wrapper.Controllable -- @module Wrapper.Controllable
-- @image Wrapper_Controllable.JPG -- @image Wrapper_Controllable.JPG
--- @type CONTROLLABLE --- @type CONTROLLABLE
-- @field DCS#Controllable DCSControllable The DCS controllable class. -- @field DCS#Controllable DCSControllable The DCS controllable class.
-- @field #string ControllableName The name of the controllable. -- @field #string ControllableName The name of the controllable.
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle the "DCS Controllable objects", which are Groups and Units: --- Wrapper class to handle the "DCS Controllable objects", which are Groups and Units:
-- --
-- * Support all DCS Controllable APIs. -- * Support all DCS Controllable APIs.
@@ -62,10 +59,10 @@
-- * @{#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the controllable. -- * @{#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the controllable.
-- * @{#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only. -- * @{#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only.
-- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Core.Zone#ZONE_RADIUS). -- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Core.Zone#ZONE_RADIUS).
-- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. -- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified altitude.
-- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified altitude during a specified duration with a specified speed.
-- * @{#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters. -- * @{#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters.
-- * @{#CONTROLLABLE.TaskRoute}: (AIR + GROUND) Return a Misson task to follow a given route defined by Points. -- * @{#CONTROLLABLE.TaskRoute}: (AIR + GROUND) Return a Mission task to follow a given route defined by Points.
-- * @{#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Controllable move to a given point. -- * @{#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Controllable move to a given point.
-- * @{#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Controllable move to a given point. -- * @{#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Controllable move to a given point.
-- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone. -- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone.
@@ -86,7 +83,7 @@
-- --
-- ## 2.3) Task preparation -- ## 2.3) Task preparation
-- --
-- There are certain task methods that allow to tailor the task behaviour: -- There are certain task methods that allow to tailor the task behavior:
-- --
-- * @{#CONTROLLABLE.TaskWrappedAction}: Return a WrappedAction Task taking a Command. -- * @{#CONTROLLABLE.TaskWrappedAction}: Return a WrappedAction Task taking a Command.
-- * @{#CONTROLLABLE.TaskCombo}: Return a Combo Task taking an array of Tasks. -- * @{#CONTROLLABLE.TaskCombo}: Return a Combo Task taking an array of Tasks.
@@ -133,7 +130,7 @@
-- --
-- # 5) Option methods -- # 5) Option methods
-- --
-- Controllable **Option methods** change the behaviour of the Controllable while being alive. -- Controllable **Option methods** change the behavior of the Controllable while being alive.
-- --
-- ## 5.1) Rule of Engagement: -- ## 5.1) Rule of Engagement:
-- --
@@ -215,7 +212,6 @@ end
-- Get methods -- Get methods
--- Returns the health. Dead controllables have health <= 1.0. --- Returns the health. Dead controllables have health <= 1.0.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #number The controllable health value (unit or group average). -- @return #number The controllable health value (unit or group average).
@@ -274,7 +270,7 @@ function CONTROLLABLE:GetLife0()
end end
--- Returns relative minimum amount of fuel (from 0.0 to 1.0) a unit or group has in its internal tanks. --- Returns relative minimum amount of fuel (from 0.0 to 1.0) a unit or group has in its internal tanks.
-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. -- This method returns nil to ensure polymorphic behavior! This method needs to be overridden by GROUP or UNIT.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #nil The CONTROLLABLE is not existing or alive. -- @return #nil The CONTROLLABLE is not existing or alive.
function CONTROLLABLE:GetFuelMin() function CONTROLLABLE:GetFuelMin()
@@ -284,7 +280,7 @@ function CONTROLLABLE:GetFuelMin()
end end
--- Returns relative average amount of fuel (from 0.0 to 1.0) a unit or group has in its internal tanks. --- Returns relative average amount of fuel (from 0.0 to 1.0) a unit or group has in its internal tanks.
-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. -- This method returns nil to ensure polymorphic behavior! This method needs to be overridden by GROUP or UNIT.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #nil The CONTROLLABLE is not existing or alive. -- @return #nil The CONTROLLABLE is not existing or alive.
function CONTROLLABLE:GetFuelAve() function CONTROLLABLE:GetFuelAve()
@@ -294,7 +290,7 @@ function CONTROLLABLE:GetFuelAve()
end end
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. --- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.
-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. -- This method returns nil to ensure polymorphic behavior! This method needs to be overridden by GROUP or UNIT.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #nil The CONTROLLABLE is not existing or alive. -- @return #nil The CONTROLLABLE is not existing or alive.
function CONTROLLABLE:GetFuel() function CONTROLLABLE:GetFuel()
@@ -302,7 +298,6 @@ function CONTROLLABLE:GetFuel()
return nil return nil
end end
-- Tasks -- Tasks
--- Clear all tasks from the controllable. --- Clear all tasks from the controllable.
@@ -321,7 +316,6 @@ function CONTROLLABLE:ClearTasks()
return nil return nil
end end
--- Popping current Task from the controllable. --- Popping current Task from the controllable.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return Wrapper.Controllable#CONTROLLABLE self -- @return Wrapper.Controllable#CONTROLLABLE self
@@ -440,7 +434,6 @@ function CONTROLLABLE:HasTask() --R2.2
return HasTaskResult return HasTaskResult
end end
--- Return a condition section for a controlled task. --- Return a condition section for a controlled task.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Time time DCS mission time. -- @param DCS#Time time DCS mission time.
@@ -485,8 +478,8 @@ function CONTROLLABLE:TaskControlled( DCSTask, DCSStopCondition )
id = 'ControlledTask', id = 'ControlledTask',
params = { params = {
task = DCSTask, task = DCSTask,
stopCondition = DCSStopCondition stopCondition = DCSStopCondition,
} },
} }
return DCSTaskControlled return DCSTaskControlled
@@ -501,8 +494,8 @@ function CONTROLLABLE:TaskCombo( DCSTasks )
local DCSTaskCombo = { local DCSTaskCombo = {
id = 'ComboTask', id = 'ComboTask',
params = { params = {
tasks = DCSTasks tasks = DCSTasks,
} },
} }
return DCSTaskCombo return DCSTaskCombo
@@ -540,9 +533,6 @@ function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task )
return Waypoint.task return Waypoint.task
end end
--- Executes a command action for the CONTROLLABLE. --- Executes a command action for the CONTROLLABLE.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Command DCSCommand The command to be executed. -- @param DCS#Command DCSCommand The command to be executed.
@@ -567,10 +557,11 @@ end
-- @param #number ToWayPoint -- @param #number ToWayPoint
-- @return DCS#Task -- @return DCS#Task
-- @usage -- @usage
-- --- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. --
-- -- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class.
-- HeliGroup = GROUP:FindByName( "Helicopter" ) -- HeliGroup = GROUP:FindByName( "Helicopter" )
-- --
-- --- Route the helicopter back to the FARP after 60 seconds. -- -- Route the helicopter back to the FARP after 60 seconds.
-- -- We use the SCHEDULER class to do this. -- -- We use the SCHEDULER class to do this.
-- SCHEDULER:New( nil, -- SCHEDULER:New( nil,
-- function( HeliGroup ) -- function( HeliGroup )
@@ -619,7 +610,6 @@ function CONTROLLABLE:CommandStopRoute( StopRoute )
return CommandStopRoute return CommandStopRoute
end end
--- Give an uncontrolled air controllable the start command. --- Give an uncontrolled air controllable the start command.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number delay (Optional) Delay before start command in seconds. -- @param #number delay (Optional) Delay before start command in seconds.
@@ -640,7 +630,7 @@ end
-- @param Core.Radio#BEACON.Type Type Beacon type (VOR, DME, TACAN, RSBN, ILS etc). -- @param Core.Radio#BEACON.Type Type Beacon type (VOR, DME, TACAN, RSBN, ILS etc).
-- @param Core.Radio#BEACON.System System Beacon system (VOR, DME, TACAN, RSBN, ILS etc). -- @param Core.Radio#BEACON.System System Beacon system (VOR, DME, TACAN, RSBN, ILS etc).
-- @param #number Frequency Frequency in Hz the beacon is running on. Use @{#UTILS.TACANToFrequency} to generate a frequency for TACAN beacons. -- @param #number Frequency Frequency in Hz the beacon is running on. Use @{#UTILS.TACANToFrequency} to generate a frequency for TACAN beacons.
-- @param #number UnitID The ID of the unit the beacon is attached to. Usefull if more units are in one group. -- @param #number UnitID The ID of the unit the beacon is attached to. Useful if more units are in one group.
-- @param #number Channel Channel the beacon is using. For, e.g. TACAN beacons. -- @param #number Channel Channel the beacon is using. For, e.g. TACAN beacons.
-- @param #string ModeChannel The TACAN mode of the beacon, i.e. "X" or "Y". -- @param #string ModeChannel The TACAN mode of the beacon, i.e. "X" or "Y".
-- @param #boolean AA If true, create and Air-Air beacon. IF nil, automatically set if CONTROLLABLE depending on whether unit is and aircraft or not. -- @param #boolean AA If true, create and Air-Air beacon. IF nil, automatically set if CONTROLLABLE depending on whether unit is and aircraft or not.
@@ -666,7 +656,7 @@ function CONTROLLABLE:CommandActivateBeacon(Type, System, Frequency, UnitID, Cha
["AA"] = AA, ["AA"] = AA,
["callsign"] = Callsign, ["callsign"] = Callsign,
["bearing"] = Bearing, ["bearing"] = Bearing,
} },
} }
if Delay and Delay > 0 then if Delay and Delay > 0 then
@@ -695,7 +685,7 @@ function CONTROLLABLE:CommandActivateICLS(Channel, UnitID, Callsign, Delay)
["channel"] = Channel, ["channel"] = Channel,
["unitId"] = UnitID, ["unitId"] = UnitID,
["callsign"] = Callsign, ["callsign"] = Callsign,
} },
} }
if Delay and Delay > 0 then if Delay and Delay > 0 then
@@ -707,6 +697,33 @@ function CONTROLLABLE:CommandActivateICLS(Channel, UnitID, Callsign, Delay)
return self return self
end end
--- Activate LINK4 system of the CONTROLLABLE. The controllable should be an aircraft carrier!
-- @param #CONTROLLABLE self
-- @param #number Frequency Link4 Frequency in MHz, e.g. 336
-- @param #number UnitID The DCS UNIT ID of the unit the LINK4 system is attached to. Useful if more units are in one group.
-- @param #string Callsign Morse code identification callsign.
-- @param #number Delay (Optional) Delay in seconds before the LINK4 is deactivated.
-- @return #CONTROLLABLE self
function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay)
-- Command to activate Link4 system.
local CommandActivateLink4= {
id = "ActivateLink4",
params= {
["frequency "] = Frequency*1000,
["unitId"] = UnitID,
["name"] = Callsign,
}
}
if Delay and Delay>0 then
SCHEDULER:New(nil, self.CommandActivateLink4, {self}, Delay)
else
self:SetCommand(CommandActivateLink4)
end
return self
end
--- Deactivate the active beacon of the CONTROLLABLE. --- Deactivate the active beacon of the CONTROLLABLE.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
@@ -718,7 +735,7 @@ function CONTROLLABLE:CommandDeactivateBeacon(Delay)
local CommandDeactivateBeacon = { id = 'DeactivateBeacon', params = {} } local CommandDeactivateBeacon = { id = 'DeactivateBeacon', params = {} }
if Delay and Delay > 0 then if Delay and Delay > 0 then
SCHEDULER:New(nil, self.CommandActivateBeacon, {self}, Delay) SCHEDULER:New( nil, self.CommandDeactivateBeacon, { self }, Delay )
else else
self:SetCommand( CommandDeactivateBeacon ) self:SetCommand( CommandDeactivateBeacon )
end end
@@ -744,6 +761,24 @@ function CONTROLLABLE:CommandDeactivateICLS(Delay)
return self return self
end end
--- Deactivate the active Link4 of the CONTROLLABLE.
-- @param #CONTROLLABLE self
-- @param #number Delay (Optional) Delay in seconds before the Link4 is deactivated.
-- @return #CONTROLLABLE self
function CONTROLLABLE:CommandDeactivateLink4(Delay)
-- Command to deactivate
local CommandDeactivateLink4={id='DeactivateLink4', params={}}
if Delay and Delay>0 then
SCHEDULER:New(nil, self.CommandDeactivateLink4, {self}, Delay)
else
self:SetCommand(CommandDeactivateLink4)
end
return self
end
--- Set callsign of the CONTROLLABLE. See [DCS command setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign) --- Set callsign of the CONTROLLABLE. See [DCS command setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign)
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#CALLSIGN CallName Number corresponding the the callsign identifier you wish this group to be called. -- @param DCS#CALLSIGN CallName Number corresponding the the callsign identifier you wish this group to be called.
@@ -780,8 +815,8 @@ function CONTROLLABLE:CommandEPLRS(SwitchOnOff, Delay)
id = 'EPLRS', id = 'EPLRS',
params = { params = {
value = SwitchOnOff, value = SwitchOnOff,
groupId=self:GetID() groupId = self:GetID(),
} },
} }
if Delay and Delay > 0 then if Delay and Delay > 0 then
@@ -798,7 +833,7 @@ end
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number Frequency Radio frequency in MHz. -- @param #number Frequency Radio frequency in MHz.
-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`. -- @param #number Modulation Radio modulation. Default `radio.modulation.AM`.
-- @param #number Delay (Optional) Delay in seconds before the frequncy is set. Default is immediately. -- @param #number Delay (Optional) Delay in seconds before the frequency is set. Default is immediately.
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Delay ) function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Delay )
@@ -807,7 +842,7 @@ function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay)
params = { params = {
frequency = Frequency * 1000000, frequency = Frequency * 1000000,
modulation = Modulation or radio.modulation.AM, modulation = Modulation or radio.modulation.AM,
} },
} }
if Delay and Delay > 0 then if Delay and Delay > 0 then
@@ -819,7 +854,6 @@ function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay)
return self return self
end end
--- Set EPLRS data link on/off. --- Set EPLRS data link on/off.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #boolean SwitchOnOff If true (or nil) switch EPLRS on. If false switch off. -- @param #boolean SwitchOnOff If true (or nil) switch EPLRS on. If false switch off.
@@ -836,14 +870,13 @@ function CONTROLLABLE:TaskEPLRS(SwitchOnOff, idx)
id = 'EPLRS', id = 'EPLRS',
params = { params = {
value = SwitchOnOff, value = SwitchOnOff,
groupId=self:GetID() groupId = self:GetID(),
} },
} }
return self:TaskWrappedAction( CommandEPLRS, idx or 1 ) return self:TaskWrappedAction( CommandEPLRS, idx or 1 )
end end
-- TASKS FOR AIR CONTROLLABLES -- TASKS FOR AIR CONTROLLABLES
--- (AIR) Attack a Controllable. --- (AIR) Attack a Controllable.
@@ -851,7 +884,7 @@ end
-- @param Wrapper.Group#GROUP AttackGroup The Group to be attacked. -- @param Wrapper.Group#GROUP AttackGroup The Group to be attacked.
-- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. -- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param DCS#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. -- @param DCS#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. -- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
@@ -899,7 +932,7 @@ end
-- @param Wrapper.Unit#UNIT AttackUnit The UNIT to be attacked -- @param Wrapper.Unit#UNIT AttackUnit The UNIT to be attacked
-- @param #boolean GroupAttack (Optional) If true, all units in the group will attack the Unit when found. Default false. -- @param #boolean GroupAttack (Optional) If true, all units in the group will attack the Unit when found. Default false.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (Optional) Determines how many weapons will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (Optional) Determines how many weapons will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (Optional) Limits maximal quantity of attack. The aicraft/controllable will not make more attacks than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (Optional) Limits maximal quantity of attack. The aircraft/controllable will not make more attacks than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (Optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. -- @param DCS#Azimuth Direction (Optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction.
-- @param #number Altitude (Optional) The (minimum) altitude in meters from where to attack. Default is altitude of unit to attack but at least 1000 m. -- @param #number Altitude (Optional) The (minimum) altitude in meters from where to attack. Default is altitude of unit to attack but at least 1000 m.
-- @param #number WeaponType (optional) The WeaponType. See [DCS Enumerator Weapon Type](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) on Hoggit. -- @param #number WeaponType (optional) The WeaponType. See [DCS Enumerator Weapon Type](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) on Hoggit.
@@ -919,19 +952,18 @@ function CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, Atta
attackQtyLimit = AttackQty and true or false, attackQtyLimit = AttackQty and true or false,
attackQty = AttackQty, attackQty = AttackQty,
weaponType = WeaponType or 1073741822, weaponType = WeaponType or 1073741822,
} },
} }
return DCSTask return DCSTask
end end
--- (AIR) Delivering weapon at the point on the ground. --- (AIR) Delivering weapon at the point on the ground.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. -- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at.
-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #number Altitude (optional) The altitude from where to attack. -- @param #number Altitude (optional) The altitude from where to attack.
-- @param #number WeaponType (optional) The WeaponType. -- @param #number WeaponType (optional) The WeaponType.
@@ -966,7 +998,7 @@ end
-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. -- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at.
-- @param #boolean GroupAttack (Optional) If true, all units in the group will attack the Unit when found. -- @param #boolean GroupAttack (Optional) If true, all units in the group will attack the Unit when found.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (Optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (Optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit will choose expend on its own discretion.
-- @param #number AttackQty (Optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (Optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (Optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param DCS#Azimuth Direction (Optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #number Altitude (Optional) The altitude [meters] from where to attack. Default 30 m. -- @param #number Altitude (Optional) The altitude [meters] from where to attack. Default 30 m.
-- @param #number WeaponType (Optional) The WeaponType. Default Auto=1073741822. -- @param #number WeaponType (Optional) The WeaponType. Default Auto=1073741822.
@@ -994,13 +1026,12 @@ function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, Atta
return DCSTask return DCSTask
end end
--- (AIR) Delivering weapon via CarpetBombing (all bombers in formation release at same time) at the point on the ground. --- (AIR) Delivering weapon via CarpetBombing (all bombers in formation release at same time) at the point on the ground.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. -- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at.
-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #number Altitude (optional) The altitude from where to attack. -- @param #number Altitude (optional) The altitude from where to attack.
-- @param #number WeaponType (optional) The WeaponType. -- @param #number WeaponType (optional) The WeaponType.
@@ -1025,14 +1056,12 @@ function CONTROLLABLE:TaskCarpetBombing(Vec2, GroupAttack, WeaponExpend, AttackQ
direction = Direction and math.rad(Direction) or 0, direction = Direction and math.rad(Direction) or 0,
altitudeEnabled = Altitude and true or false, altitudeEnabled = Altitude and true or false,
altitude = Altitude, altitude = Altitude,
} },
} }
return DCSTask return DCSTask
end end
--- (AIR) Following another airborne controllable. --- (AIR) Following another airborne controllable.
-- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. -- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders.
-- Used to support CarpetBombing Task -- Used to support CarpetBombing Task
@@ -1049,14 +1078,13 @@ function CONTROLLABLE:TaskFollowBigFormation(FollowControllable, Vec3, LastWaypo
groupId = FollowControllable:GetID(), groupId = FollowControllable:GetID(),
pos = Vec3, pos = Vec3,
lastWptIndexFlag = LastWaypointIndex and true or false, lastWptIndexFlag = LastWaypointIndex and true or false,
lastWptIndex = LastWaypointIndex lastWptIndex = LastWaypointIndex,
} },
} }
return DCSTask return DCSTask
end end
--- (AIR HELICOPTER) Move the controllable to a Vec2 Point, wait for a defined duration and embark infantry groups. --- (AIR HELICOPTER) Move the controllable to a Vec2 Point, wait for a defined duration and embark infantry groups.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE Coordinate The point where to pickup the troops. -- @param Core.Point#COORDINATE Coordinate The point where to pickup the troops.
@@ -1099,13 +1127,12 @@ function CONTROLLABLE:TaskEmbarking(Coordinate, GroupSetForEmbarking, Duration,
duration = Duration, duration = Duration,
distributionFlag = Distribution and true or false, distributionFlag = Distribution and true or false,
distribution = Distribution, distribution = Distribution,
} },
} }
return DCSTask return DCSTask
end end
--- Used in conjunction with the embarking task for a transport helicopter group. The Ground units will move to the specified location and wait to be picked up by a helicopter. --- Used in conjunction with the embarking task for a transport helicopter group. The Ground units will move to the specified location and wait to be picked up by a helicopter.
-- The helicopter will then fly them to their dropoff point defined by another task for the ground forces; DisembarkFromTransport task. -- The helicopter will then fly them to their dropoff point defined by another task for the ground forces; DisembarkFromTransport task.
-- The controllable has to be an infantry group! -- The controllable has to be an infantry group!
@@ -1123,13 +1150,12 @@ function CONTROLLABLE:TaskEmbarkToTransport(Coordinate, Radius, UnitType)
y = Coordinate.z, y = Coordinate.z,
zoneRadius = Radius or 200, zoneRadius = Radius or 200,
selectedType = UnitType, selectedType = UnitType,
} },
} }
return EmbarkToTransport return EmbarkToTransport
end end
--- Specifies the location infantry groups that is being transported by helicopters will be unloaded at. Used in conjunction with the EmbarkToTransport task. --- Specifies the location infantry groups that is being transported by helicopters will be unloaded at. Used in conjunction with the EmbarkToTransport task.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE Coordinate Coordinates where AI is expecting to be picked up. -- @param Core.Point#COORDINATE Coordinate Coordinates where AI is expecting to be picked up.
@@ -1155,14 +1181,13 @@ function CONTROLLABLE:TaskDisembarking(Coordinate, GroupSetToDisembark)
x = Coordinate.x, x = Coordinate.x,
y = Coordinate.z, y = Coordinate.z,
groupsForEmbarking = g4e, -- This is no bug, the entry is really "groupsForEmbarking" even if we disembark the troops. groupsForEmbarking = g4e, -- This is no bug, the entry is really "groupsForEmbarking" even if we disembark the troops.
} },
} }
return Disembarking return Disembarking
end end
--- (AIR) Orbit at a specified position at a specified altitude during a specified duration with a specified speed.
--- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Vec2 Point The point to hold the position. -- @param DCS#Vec2 Point The point to hold the position.
-- @param #number Altitude The altitude AGL in meters to hold the position. -- @param #number Altitude The altitude AGL in meters to hold the position.
@@ -1177,8 +1202,8 @@ function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed )
pattern = AI.Task.OrbitPattern.CIRCLE, pattern = AI.Task.OrbitPattern.CIRCLE,
point = Point, point = Point,
speed = Speed, speed = Speed,
altitude = Altitude + land.getHeight( Point ) altitude = Altitude + land.getHeight( Point ),
} },
} }
return DCSTask return DCSTask
@@ -1210,13 +1235,13 @@ function CONTROLLABLE:TaskOrbit(Coord, Altitude, Speed, CoordRaceTrack)
point2 = P2, point2 = P2,
speed = Speed or UTILS.KnotsToMps(250), speed = Speed or UTILS.KnotsToMps(250),
altitude = Altitude or Coord.y, altitude = Altitude or Coord.y,
} },
} }
return Task return Task
end end
--- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. --- (AIR) Orbit at the current position of the first unit of the controllable at a specified altitude.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number Altitude The altitude [m] to hold the position. -- @param #number Altitude The altitude [m] to hold the position.
-- @param #number Speed The speed [m/s] flying when holding the position. -- @param #number Speed The speed [m/s] flying when holding the position.
@@ -1235,8 +1260,6 @@ function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate )
return nil return nil
end end
--- (AIR) Hold position at the current position of the first unit of the controllable. --- (AIR) Hold position at the current position of the first unit of the controllable.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number Duration The maximum duration in seconds to hold the position. -- @param #number Duration The maximum duration in seconds to hold the position.
@@ -1247,7 +1270,6 @@ function CONTROLLABLE:TaskHoldPosition()
return self:TaskOrbitCircle( 30, 10 ) return self:TaskOrbitCircle( 30, 10 )
end end
--- (AIR) Delivering weapon on the runway. See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_bombingRunway) --- (AIR) Delivering weapon on the runway. See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_bombingRunway)
-- --
-- Make sure the aircraft has the following role: -- Make sure the aircraft has the following role:
@@ -1284,7 +1306,6 @@ function CONTROLLABLE:TaskBombingRunway(Airbase, WeaponType, WeaponExpend, Attac
return DCSTask return DCSTask
end end
--- (AIR) Refueling from the nearest tanker. No parameters. --- (AIR) Refueling from the nearest tanker. No parameters.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return DCS#Task The DCS task structure. -- @return DCS#Task The DCS task structure.
@@ -1292,13 +1313,12 @@ function CONTROLLABLE:TaskRefueling()
local DCSTask = { local DCSTask = {
id = 'Refueling', id = 'Refueling',
params={} params = {},
} }
return DCSTask return DCSTask
end end
--- (AIR HELICOPTER) Landing at the ground. For helicopters only. --- (AIR HELICOPTER) Landing at the ground. For helicopters only.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Vec2 Vec2 The point where to land. -- @param DCS#Vec2 Vec2 The point where to land.
@@ -1333,8 +1353,6 @@ function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint )
return DCSTask return DCSTask
end end
--- (AIR) Following another airborne controllable. --- (AIR) Following another airborne controllable.
-- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. -- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders.
-- If another controllable is on land the unit / controllable will orbit around. -- If another controllable is on land the unit / controllable will orbit around.
@@ -1371,14 +1389,13 @@ function CONTROLLABLE:TaskFollow( FollowControllable, Vec3, LastWaypointIndex )
lastWptIndexFlag = LastWaypointIndexFlag, lastWptIndexFlag = LastWaypointIndexFlag,
lastWptIndex = LastWaypointIndex, lastWptIndex = LastWaypointIndex,
lastWptIndexFlagChangedManually = lastWptIndexFlagChangedManually, lastWptIndexFlagChangedManually = lastWptIndexFlagChangedManually,
} },
} }
self:T3( { DCSTask } ) self:T3( { DCSTask } )
return DCSTask return DCSTask
end end
--- (AIR) Escort another airborne controllable. --- (AIR) Escort another airborne controllable.
-- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. -- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders.
-- The unit / controllable will also protect that controllable from threats of specified types. -- The unit / controllable will also protect that controllable from threats of specified types.
@@ -1419,7 +1436,6 @@ function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, E
return DCSTask return DCSTask
end end
-- GROUND TASKS -- GROUND TASKS
--- (GROUND) Fire at a VEC2 point until ammunition is finished. --- (GROUND) Fire at a VEC2 point until ammunition is finished.
@@ -1443,8 +1459,8 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti
radius = Radius, radius = Radius,
expendQty = 100, -- dummy value expendQty = 100, -- dummy value
expendQtyEnabled = false, expendQtyEnabled = false,
alt_type = ASL and 0 or 1 alt_type = ASL and 0 or 1,
} },
} }
if AmmoCount then if AmmoCount then
@@ -1473,7 +1489,6 @@ function CONTROLLABLE:TaskHold()
return DCSTask return DCSTask
end end
-- TASKS FOR AIRBORNE AND GROUND UNITS/CONTROLLABLES -- TASKS FOR AIRBORNE AND GROUND UNITS/CONTROLLABLES
--- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. --- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
@@ -1503,7 +1518,7 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation,
modulation = Modulation or radio.modulation.AM, modulation = Modulation or radio.modulation.AM,
callname = CallsignName, callname = CallsignName,
number = CallsignNumber, number = CallsignNumber,
} },
} }
return DCSTask return DCSTask
@@ -1526,14 +1541,12 @@ function CONTROLLABLE:EnRouteTaskEngageTargets( Distance, TargetTypes, Priority
maxDist = Distance, maxDist = Distance,
targetTypes = TargetTypes or {"Air"}, targetTypes = TargetTypes or {"Air"},
priority = Priority or 0, priority = Priority or 0,
} },
} }
return DCSTask return DCSTask
end end
--- (AIR) Engaging a targets of defined types at circle-shaped zone. --- (AIR) Engaging a targets of defined types at circle-shaped zone.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param DCS#Vec2 Vec2 2D-coordinates of the zone. -- @param DCS#Vec2 Vec2 2D-coordinates of the zone.
@@ -1550,20 +1563,19 @@ function CONTROLLABLE:EnRouteTaskEngageTargetsInZone( Vec2, Radius, TargetTypes,
zoneRadius = Radius, zoneRadius = Radius,
targetTypes = TargetTypes or {"Air"}, targetTypes = TargetTypes or {"Air"},
priority = Priority or 0 priority = Priority or 0
} },
} }
return DCSTask return DCSTask
end end
--- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets. --- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. -- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked.
-- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. -- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
-- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. -- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param DCS#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. -- @param DCS#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. -- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
@@ -1605,14 +1617,13 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType,
return DCSTask return DCSTask
end end
--- (AIR) Search and attack the Unit. --- (AIR) Search and attack the Unit.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Wrapper.Unit#UNIT EngageUnit The UNIT. -- @param Wrapper.Unit#UNIT EngageUnit The UNIT.
-- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. -- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param DCS#Distance Altitude (optional) Desired altitude to perform the unit engagement. -- @param DCS#Distance Altitude (optional) Desired altitude to perform the unit engagement.
-- @param #boolean Visible (optional) Unit must be visible. -- @param #boolean Visible (optional) Unit must be visible.
@@ -1641,8 +1652,6 @@ function CONTROLLABLE:EnRouteTaskEngageUnit( EngageUnit, Priority, GroupAttack,
return DCSTask return DCSTask
end end
--- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. --- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return DCS#Task The DCS task structure. -- @return DCS#Task The DCS task structure.
@@ -1656,7 +1665,6 @@ function CONTROLLABLE:EnRouteTaskAWACS( )
return DCSTask return DCSTask
end end
--- (AIR) Aircraft will act as a tanker for friendly units. No parameters. --- (AIR) Aircraft will act as a tanker for friendly units. No parameters.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return DCS#Task The DCS task structure. -- @return DCS#Task The DCS task structure.
@@ -1670,7 +1678,6 @@ function CONTROLLABLE:EnRouteTaskTanker( )
return DCSTask return DCSTask
end end
-- En-route tasks for ground units/controllables -- En-route tasks for ground units/controllables
--- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters. --- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters.
@@ -1686,7 +1693,6 @@ function CONTROLLABLE:EnRouteTaskEWR( )
return DCSTask return DCSTask
end end
-- En-route tasks for airborne and ground units/controllables -- En-route tasks for airborne and ground units/controllables
--- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. --- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets.
@@ -1709,13 +1715,12 @@ function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponT
designation = Designation, designation = Designation,
datalink = Datalink and Datalink or false, datalink = Datalink and Datalink or false,
priority = Priority or 0, priority = Priority or 0,
} },
} }
return DCSTask return DCSTask
end end
--- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets. --- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets.
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. -- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
-- If the task is assigned to the controllable lead unit will be a FAC. -- If the task is assigned to the controllable lead unit will be a FAC.
@@ -1744,7 +1749,6 @@ function CONTROLLABLE:EnRouteTaskFAC( Radius, Priority )
return DCSTask return DCSTask
end end
--- This creates a Task element, with an action to call a function as part of a Wrapped Task. --- This creates a Task element, with an action to call a function as part of a Wrapped Task.
-- This Task can then be embedded at a Waypoint by calling the method @{#CONTROLLABLE.SetTaskWaypoint}. -- This Task can then be embedded at a Waypoint by calling the method @{#CONTROLLABLE.SetTaskWaypoint}.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
@@ -1813,8 +1817,6 @@ function CONTROLLABLE:TaskFunction( FunctionString, ... )
return DCSTask return DCSTask
end end
--- (AIR + GROUND) Return a mission task from a mission template. --- (AIR + GROUND) Return a mission task from a mission template.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #table TaskMission A table containing the mission task. -- @param #table TaskMission A table containing the mission task.
@@ -1823,13 +1825,14 @@ function CONTROLLABLE:TaskMission( TaskMission )
local DCSTask = { local DCSTask = {
id = 'Mission', id = 'Mission',
params = { TaskMission, }, params = {
TaskMission,
},
} }
return DCSTask return DCSTask
end end
do -- Patrol methods do -- Patrol methods
--- (GROUND) Patrol iteratively using the waypoints the for the (parent) group. --- (GROUND) Patrol iteratively using the waypoints the for the (parent) group.
@@ -1863,7 +1866,6 @@ do -- Patrol methods
end end
end end
local Waypoint = Waypoints[1] local Waypoint = Waypoints[1]
local Speed = Waypoint.speed or (20 / 3.6) local Speed = Waypoint.speed or (20 / 3.6)
local From = FromCoord:WaypointGround( Speed ) local From = FromCoord:WaypointGround( Speed )
@@ -1922,7 +1924,7 @@ do -- Patrol methods
end end
end end
-- Loop until a waypoint has been found that is not the same as the current waypoint. -- Loop until a waypoint has been found that is not the same as the current waypoint.
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly -- Otherwise the object won't move, or drive in circles, and the algorithm would not do exactly
-- what it is supposed to do, which is making groups drive around. -- what it is supposed to do, which is making groups drive around.
local ToWaypoint local ToWaypoint
repeat repeat
@@ -2021,8 +2023,7 @@ do -- Patrol methods
end end
--- Return a Mission task to follow a given route defined by Points.
--- Return a Misson task to follow a given route defined by Points.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #table Points A table of route points. -- @param #table Points A table of route points.
-- @return DCS#Task -- @return DCS#Task
@@ -2068,7 +2069,6 @@ do -- Route methods
["steer"] = 2, ["steer"] = 2,
} }
local PointTo = {} local PointTo = {}
PointTo.x = Point.x PointTo.x = Point.x
PointTo.y = Point.y PointTo.y = Point.y
@@ -2084,7 +2084,6 @@ do -- Route methods
["steer"] = 2, ["steer"] = 2,
} }
local Points = { PointFrom, PointTo } local Points = { PointFrom, PointTo }
self:T3( Points ) self:T3( Points )
@@ -2121,7 +2120,6 @@ do -- Route methods
["steer"] = 2, ["steer"] = 2,
} }
local PointTo = {} local PointTo = {}
PointTo.x = Point.x PointTo.x = Point.x
PointTo.y = Point.z PointTo.y = Point.z
@@ -2139,7 +2137,6 @@ do -- Route methods
["steer"] = 2, ["steer"] = 2,
} }
local Points = { PointFrom, PointTo } local Points = { PointFrom, PointTo }
self:T3( Points ) self:T3( Points )
@@ -2149,8 +2146,6 @@ do -- Route methods
return self return self
end end
--- Make the controllable to follow a given route. --- Make the controllable to follow a given route.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #table Route A table of Route Points. -- @param #table Route A table of Route Points.
@@ -2187,7 +2182,6 @@ do -- Route methods
return nil return nil
end end
--- Stops the movement of the vehicle on the route. --- Stops the movement of the vehicle on the route.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE -- @return #CONTROLLABLE
@@ -2292,8 +2286,6 @@ do -- Route methods
return self return self
end end
--- Make a task for a GROUND Controllable to drive towards a specific point using (mostly) roads. --- Make a task for a GROUND Controllable to drive towards a specific point using (mostly) roads.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
@@ -2456,7 +2448,6 @@ do -- Route methods
waypointfunction( controllable, n, N, ... ) waypointfunction( controllable, n, N, ... )
end end
--- Make the AIR Controllable fly towards a specific point. --- Make the AIR Controllable fly towards a specific point.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
@@ -2478,7 +2469,6 @@ do -- Route methods
return self return self
end end
--- (AIR + GROUND) Route the controllable to a given zone. --- (AIR + GROUND) Route the controllable to a given zone.
-- The controllable final destination point can be randomized. -- The controllable final destination point can be randomized.
-- A speed can be given in km/h. -- A speed can be given in km/h.
@@ -2504,7 +2494,6 @@ do -- Route methods
PointFrom.action = Formation or "Cone" PointFrom.action = Formation or "Cone"
PointFrom.speed = 20 / 3.6 PointFrom.speed = 20 / 3.6
local PointTo = {} local PointTo = {}
local ZonePoint local ZonePoint
@@ -2564,7 +2553,6 @@ do -- Route methods
PointFrom.action = Formation or "Cone" PointFrom.action = Formation or "Cone"
PointFrom.speed = 20 / 3.6 PointFrom.speed = 20 / 3.6
local PointTo = {} local PointTo = {}
PointTo.x = Vec2.x PointTo.x = Vec2.x
@@ -2616,7 +2604,6 @@ function CONTROLLABLE:CommandDoScript( DoScript )
return DCSDoScript return DCSDoScript
end end
--- Return the mission template of the controllable. --- Return the mission template of the controllable.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #table The MissionTemplate -- @return #table The MissionTemplate
@@ -2636,8 +2623,6 @@ function CONTROLLABLE:GetTaskRoute()
return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points )
end end
--- Return the route of a controllable by using the @{Core.Database#DATABASE} class. --- Return the route of a controllable by using the @{Core.Database#DATABASE} class.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number Begin The route point from where the copy will start. The base route point is 0. -- @param #number Begin The route point from where the copy will start. The base route point is 0.
@@ -2689,7 +2674,6 @@ function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius )
return nil return nil
end end
--- Return the detected targets of the controllable. --- Return the detected targets of the controllable.
-- The optional parametes specify the detection methods that can be applied. -- The optional parametes specify the detection methods that can be applied.
-- If no detection method is given, the detection will use all the available methods by default. -- If no detection method is given, the detection will use all the available methods by default.
@@ -2715,7 +2699,6 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
local DetectionRWR = (DetectRWR and DetectRWR == true) and Controller.Detection.RWR or nil local DetectionRWR = (DetectRWR and DetectRWR == true) and Controller.Detection.RWR or nil
local DetectionDLINK = (DetectDLINK and DetectDLINK == true) and Controller.Detection.DLINK or nil local DetectionDLINK = (DetectDLINK and DetectDLINK == true) and Controller.Detection.DLINK or nil
local Params = {} local Params = {}
if DetectionVisual then if DetectionVisual then
Params[#Params + 1] = DetectionVisual Params[#Params + 1] = DetectionVisual
@@ -2736,7 +2719,6 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
Params[#Params + 1] = DetectionDLINK Params[#Params + 1] = DetectionDLINK
end end
self:T2( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } ) self:T2( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } )
return self:_GetController():getDetectedTargets( Params[1], Params[2], Params[3], Params[4], Params[5], Params[6] ) return self:_GetController():getDetectedTargets( Params[1], Params[2], Params[3], Params[4], Params[5], Params[6] )
@@ -2853,7 +2835,6 @@ function CONTROLLABLE:IsGroupDetected( Group, DetectVisual, DetectOptical, Detec
return nil return nil
end end
--- Return the detected targets of the controllable. --- Return the detected targets of the controllable.
-- The optional parametes specify the detection methods that can be applied. -- The optional parametes specify the detection methods that can be applied.
-- If **no** detection method is given, the detection will use **all** the available methods by default. -- If **no** detection method is given, the detection will use **all** the available methods by default.
@@ -2930,7 +2911,6 @@ function CONTROLLABLE:GetDetectedGroupSet(DetectVisual, DetectOptical, DetectRad
return groupset return groupset
end end
-- Options -- Options
--- Set option. --- Set option.
@@ -3199,7 +3179,6 @@ function CONTROLLABLE:OptionROTNoReactionPossible()
return nil return nil
end end
--- No evasion on enemy threats. --- No evasion on enemy threats.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@@ -3297,7 +3276,6 @@ function CONTROLLABLE:OptionROTEvadeFirePossible()
return nil return nil
end end
--- Evade on fire. --- Evade on fire.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@@ -3336,7 +3314,6 @@ function CONTROLLABLE:OptionROTVerticalPossible()
return nil return nil
end end
--- Evade on fire using vertical manoeuvres. --- Evade on fire using vertical manoeuvres.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@@ -3427,7 +3404,6 @@ function CONTROLLABLE:OptionAlarmStateRed()
return nil return nil
end end
--- Set RTB on bingo fuel. --- Set RTB on bingo fuel.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #boolean RTB true if RTB on bingo fuel (default), false if no RTB on bingo fuel. -- @param #boolean RTB true if RTB on bingo fuel (default), false if no RTB on bingo fuel.
@@ -3455,7 +3431,6 @@ function CONTROLLABLE:OptionRTBBingoFuel( RTB ) --R2.2
return nil return nil
end end
--- Set RTB on ammo. --- Set RTB on ammo.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #boolean WeaponsFlag Weapons.flag enumerator. -- @param #boolean WeaponsFlag Weapons.flag enumerator.
@@ -3477,7 +3452,6 @@ function CONTROLLABLE:OptionRTBAmmo( WeaponsFlag )
return nil return nil
end end
--- Allow to Jettison of weapons upon threat. --- Allow to Jettison of weapons upon threat.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@@ -3498,7 +3472,6 @@ function CONTROLLABLE:OptionAllowJettisonWeaponsOnThreat()
return nil return nil
end end
--- Keep weapons upon threat. --- Keep weapons upon threat.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@@ -3563,7 +3536,6 @@ function CONTROLLABLE:OptionECM_OnlyLockByRadar()
return self return self
end end
--- Defines the usage of Electronic Counter Measures by airborne forces. If the AI is being detected by a radar they will enable their ECM. --- Defines the usage of Electronic Counter Measures by airborne forces. If the AI is being detected by a radar they will enable their ECM.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
@@ -3636,7 +3608,6 @@ function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunctio
return self return self
end end
--- Executes the WayPoint plan. --- Executes the WayPoint plan.
-- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint. -- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint.
-- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1! -- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1!
@@ -3845,3 +3816,44 @@ function POSITIONABLE:IsSubmarine()
return nil return nil
end end
--- Sets the controlled group to go at the specified speed in meters per second.
-- @param #CONTROLLABLE self
-- @param #number Speed Speed in meters per second.
-- @param #boolean Keep (Optional) When set to true, will maintain the speed on passing waypoints. If not present or false, the controlled group will return to the speed as defined by their route.
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetSpeed(Speed, Keep)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local speed = Speed or 5
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
Controller:setSpeed(speed, Keep)
end
end
return self
end
--- [AIR] Sets the controlled aircraft group to fly at the specified altitude in meters.
-- @param #CONTROLLABLE self
-- @param #number Altitude Altitude in meters.
-- @param #boolean Keep (Optional) When set to true, will maintain the altitude on passing waypoints. If not present or false, the controlled group will return to the altitude as defined by their route.
-- @param #string AltType (Optional) Specifies the altitude type used. If nil, the altitude type of the current waypoint will be used. Accepted values are "BARO" and "RADIO".
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local altitude = Altitude or 1000
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsAir() then
Controller:setAltitude(altitude, Keep, AltType)
end
end
end
return self
end

View File

@@ -310,8 +310,7 @@ end
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
-- @return #nil The POSITIONABLE is not existing or alive.
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3() function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
self:F2( self.PositionableName ) self:F2( self.PositionableName )
@@ -339,9 +338,7 @@ end
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false. -- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
-- --
-- @param #GROUP self -- @param #GROUP self
-- @return #boolean true if the group is alive and active. -- @return #boolean `true` if the group is alive *and* active, `false` if the group is alive but inactive or `#nil` if the group does not exist anymore.
-- @return #boolean false if the group is alive but inactive.
-- @return #nil if the group does not exist anymore.
function GROUP:IsAlive() function GROUP:IsAlive()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -363,8 +360,7 @@ end
--- Returns if the group is activated. --- Returns if the group is activated.
-- @param #GROUP self -- @param #GROUP self
-- @return #boolean true if group is activated. -- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
-- @return #nil The group is not existing or alive.
function GROUP:IsActive() function GROUP:IsActive()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -388,7 +384,7 @@ end
-- So all event listeners will catch the destroy event of this group for each unit in the group. -- So all event listeners will catch the destroy event of this group for each unit in the group.
-- To raise these events, provide the `GenerateEvent` parameter. -- To raise these events, provide the `GenerateEvent` parameter.
-- @param #GROUP self -- @param #GROUP self
-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered. -- @param #boolean GenerateEvent If true, a crash [AIR] or dead [GROUND] event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
-- @param #number delay Delay in seconds before despawning the group. -- @param #number delay Delay in seconds before despawning the group.
-- @usage -- @usage
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. -- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
@@ -412,7 +408,6 @@ function GROUP:Destroy( GenerateEvent, delay )
self:F2( self.GroupName ) self:F2( self.GroupName )
if delay and delay>0 then if delay and delay>0 then
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent) self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
else else
@@ -568,12 +563,12 @@ function GROUP:GetSpeedMax()
local Units=self:GetUnits() local Units=self:GetUnits()
local speedmax=nil local speedmax=0
for _,unit in pairs(Units) do for _,unit in pairs(Units) do
local unit=unit --Wrapper.Unit#UNIT local unit=unit --Wrapper.Unit#UNIT
local speed=unit:GetSpeedMax() local speed=unit:GetSpeedMax()
if speedmax==nil then if speedmax==0 then
speedmax=speed speedmax=speed
elseif speed<speedmax then elseif speed<speedmax then
speedmax=speed speedmax=speed
@@ -627,7 +622,7 @@ function GROUP:GetUnits()
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnits = DCSGroup:getUnits() local DCSUnits = DCSGroup:getUnits() or {}
local Units = {} local Units = {}
for Index, UnitData in pairs( DCSUnits ) do for Index, UnitData in pairs( DCSUnits ) do
Units[#Units+1] = UNIT:Find( UnitData ) Units[#Units+1] = UNIT:Find( UnitData )
@@ -685,6 +680,30 @@ function GROUP:GetUnit( UnitNumber )
return nil return nil
end end
--- Check if an (air) group is a client or player slot. Information is retrieved from the group template.
-- @param #GROUP self
-- @return #boolean If true, group is associated with a client or player slot.
function GROUP:IsPlayer()
-- Get group.
-- local group=self:GetGroup()
-- Units of template group.
local units=self:GetTemplate().units
-- Get numbers.
for _,unit in pairs(units) do
-- Check if unit name matach and skill is Client or Player.
if unit.name==self:GetName() and (unit.skill=="Client" or unit.skill=="Player") then
return true
end
end
return false
end
--- Returns the DCS Unit with number UnitNumber. --- Returns the DCS Unit with number UnitNumber.
-- If the underlying DCS Unit does not exist, the method will return nil. . -- If the underlying DCS Unit does not exist, the method will return nil. .
-- @param #GROUP self -- @param #GROUP self
@@ -695,8 +714,21 @@ function GROUP:GetDCSUnit( UnitNumber )
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
return DCSUnitFound if DCSGroup.getUnit and DCSGroup:getUnit( UnitNumber ) then
return DCSGroup:getUnit( UnitNumber )
else
local UnitFound = nil
-- 2.7.1 dead event bug, return the first alive unit instead
local units = DCSGroup:getUnits() or {}
for _,_unit in pairs(units) do
if _unit and _unit:isExist() then
return _unit
end
end
end
end end
return nil return nil
@@ -770,8 +802,7 @@ end
--- Returns the average velocity Vec3 vector. --- Returns the average velocity Vec3 vector.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @return DCS#Vec3 The velocity Vec3 vector -- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetVelocityVec3() function GROUP:GetVelocityVec3()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -802,11 +833,19 @@ function GROUP:GetVelocityVec3()
return nil return nil
end end
--- Returns the average group altitude in meters.
-- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return #number The altitude of the group or nil if is not existing or alive.
function GROUP:GetAltitude(FromGround)
self:F2( self.GroupName )
return self:GetHeight(FromGround)
end
--- Returns the average group height in meters. --- Returns the average group height in meters.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level. -- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return DCS#Vec3 The height of the group or nil if is not existing or alive. -- @return #number The height of the group or nil if is not existing or alive.
function GROUP:GetHeight( FromGround ) function GROUP:GetHeight( FromGround )
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -906,6 +945,24 @@ function GROUP:GetTypeName()
return nil return nil
end end
--- [AIRPLANE] Get the NATO reporting name (platform, e.g. "Flanker") of a GROUP (note - first unit the group). "Bogey" if not found. Currently airplanes only!
--@param #GROUP self
--@return #string NatoReportingName or "Bogey" if unknown.
function GROUP:GetNatoReportingName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
self:T3( GroupTypeName )
return UTILS.GetReportingName(GroupTypeName)
end
return "Bogey"
end
--- Gets the player name of the group. --- Gets the player name of the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #string The player name of the group. -- @return #string The player name of the group.
@@ -999,24 +1056,32 @@ end
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP. -- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
function GROUP:GetCoordinate() function GROUP:GetCoordinate()
local FirstUnit = self:GetUnit(1) local Units = self:GetUnits() or {}
for _,_unit in pairs(Units) do
local FirstUnit = _unit -- Wrapper.Unit#UNIT
if FirstUnit then if FirstUnit then
local FirstUnitCoordinate = FirstUnit:GetCoordinate() local FirstUnitCoordinate = FirstUnit:GetCoordinate()
if FirstUnitCoordinate then
local Heading = self:GetHeading()
FirstUnitCoordinate.Heading = Heading
return FirstUnitCoordinate return FirstUnitCoordinate
end end
end
end
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } ) BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
return nil
end end
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. --- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
-- @param #GROUP self -- @param #GROUP self
-- @param #number Radius -- @param #number Radius Radius in meters.
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP. -- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP or #nil The GROUP is invalid or empty.
-- @return #nil The GROUP is invalid or empty
-- @usage -- @usage
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP -- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
function GROUP:GetRandomVec3(Radius) function GROUP:GetRandomVec3(Radius)
@@ -1037,18 +1102,19 @@ end
--- Returns the mean heading of every UNIT in the GROUP in degrees --- Returns the mean heading of every UNIT in the GROUP in degrees
-- @param #GROUP self -- @param #GROUP self
-- @return #number mean heading of the GROUP -- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetHeading() function GROUP:GetHeading()
self:F2(self.GroupName) self:F2(self.GroupName)
self:F2(self.GroupName)
local GroupSize = self:GetSize() local GroupSize = self:GetSize()
local HeadingAccumulator = 0 local HeadingAccumulator = 0
local n=0 local n=0
local Units = self:GetUnits()
if GroupSize then if GroupSize then
for i = 1, GroupSize do for _,unit in pairs(Units) do
local unit=self:GetUnit(i)
if unit and unit:IsAlive() then if unit and unit:IsAlive() then
HeadingAccumulator = HeadingAccumulator + unit:GetHeading() HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
n=n+1 n=n+1
@@ -1066,8 +1132,8 @@ end
--- Return the fuel state and unit reference for the unit with the least --- Return the fuel state and unit reference for the unit with the least
-- amount of fuel in the group. -- amount of fuel in the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #number The fuel state of the unit with the least amount of fuel -- @return #number The fuel state of the unit with the least amount of fuel.
-- @return #Unit reference to #Unit object for further processing -- @return Wrapper.Unit#UNIT reference to #Unit object for further processing.
function GROUP:GetFuelMin() function GROUP:GetFuelMin()
self:F3(self.ControllableName) self:F3(self.ControllableName)
@@ -1109,7 +1175,7 @@ function GROUP:GetFuelAvg()
local TotalFuel = 0 local TotalFuel = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
local UnitFuel = Unit:GetFuel() local UnitFuel = Unit:GetFuel() or 0
self:F( { Fuel = UnitFuel } ) self:F( { Fuel = UnitFuel } )
TotalFuel = TotalFuel + UnitFuel TotalFuel = TotalFuel + UnitFuel
end end
@@ -1186,13 +1252,14 @@ function GROUP:IsInZone( Zone )
for UnitID, UnitData in pairs(self:GetUnits()) do for UnitID, UnitData in pairs(self:GetUnits()) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
local vec2 = nil
if Unit then
-- Get 2D vector. That's all we need for the zone check. -- Get 2D vector. That's all we need for the zone check.
local vec2=Unit:GetVec2() vec2=Unit:GetVec2()
end
if Zone:IsVec2InZone(vec2) then if vec2 and Zone:IsVec2InZone(vec2) then
return true -- At least one unit is in the zone. That is enough. return true -- At least one unit is in the zone. That is enough.
else
-- This one is not but another could be.
end end
end end
@@ -2326,41 +2393,44 @@ function GROUP:GetAttribute()
local unarmedship=self:HasAttribute("Unarmed ships") local unarmedship=self:HasAttribute("Unarmed ships")
-- Define attribute. Order is important. -- Define attribute. Order of attack is important.
if transportplane then if fighter then
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
elseif awacs then
attribute=GROUP.Attribute.AIR_AWACS
elseif fighter then
attribute=GROUP.Attribute.AIR_FIGHTER attribute=GROUP.Attribute.AIR_FIGHTER
elseif bomber then elseif bomber then
attribute=GROUP.Attribute.AIR_BOMBER attribute=GROUP.Attribute.AIR_BOMBER
elseif awacs then
attribute=GROUP.Attribute.AIR_AWACS
elseif transportplane then
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
elseif tanker then elseif tanker then
attribute=GROUP.Attribute.AIR_TANKER attribute=GROUP.Attribute.AIR_TANKER
elseif transporthelo then -- helos
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
elseif attackhelicopter then elseif attackhelicopter then
attribute=GROUP.Attribute.AIR_ATTACKHELO attribute=GROUP.Attribute.AIR_ATTACKHELO
elseif transporthelo then
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
elseif uav then elseif uav then
attribute=GROUP.Attribute.AIR_UAV attribute=GROUP.Attribute.AIR_UAV
elseif apc then -- ground - order of attack
attribute=GROUP.Attribute.GROUND_APC
elseif infantry then
attribute=GROUP.Attribute.GROUND_INFANTRY
elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then
attribute=GROUP.Attribute.GROUND_TANK
elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA
elseif ewr then elseif ewr then
attribute=GROUP.Attribute.GROUND_EWR attribute=GROUP.Attribute.GROUND_EWR
elseif sam then elseif sam then
attribute=GROUP.Attribute.GROUND_SAM attribute=GROUP.Attribute.GROUND_SAM
elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA
elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then
attribute=GROUP.Attribute.GROUND_TANK
elseif apc then
attribute=GROUP.Attribute.GROUND_APC
elseif infantry then
attribute=GROUP.Attribute.GROUND_INFANTRY
elseif truck then elseif truck then
attribute=GROUP.Attribute.GROUND_TRUCK attribute=GROUP.Attribute.GROUND_TRUCK
elseif train then elseif train then
attribute=GROUP.Attribute.GROUND_TRAIN attribute=GROUP.Attribute.GROUND_TRAIN
-- ships
elseif aircraftcarrier then elseif aircraftcarrier then
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
elseif warship then elseif warship then
@@ -2611,6 +2681,41 @@ function GROUP:GetSkill()
return skill return skill
end end
--- Get the unit in the group with the highest threat level, which is still alive.
-- @param #GROUP self
-- @return Wrapper.Unit#UNIT The most dangerous unit in the group.
-- @return #number Threat level of the unit.
function GROUP:GetHighestThreat()
-- Get units of the group.
local units=self:GetUnits()
if units then
local threat=nil ; local maxtl=0
for _,_unit in pairs(units or {}) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
-- Threat level of group.
local tl=unit:GetThreatLevel()
-- Check if greater the current threat.
if tl>maxtl then
maxtl=tl
threat=unit
end
end
end
return threat, maxtl
end
return nil, nil
end
--do -- Smoke --do -- Smoke
-- --
----- Signal a flare at the position of the GROUP. ----- Signal a flare at the position of the GROUP.

View File

@@ -176,7 +176,7 @@ function IDENTIFIABLE:GetCoalitionName()
if DCSIdentifiable then if DCSIdentifiable then
-- Get coaliton ID. -- Get coalition ID.
local IdentifiableCoalition = DCSIdentifiable:getCoalition() local IdentifiableCoalition = DCSIdentifiable:getCoalition()
self:T3( IdentifiableCoalition ) self:T3( IdentifiableCoalition )

View File

@@ -15,7 +15,6 @@
-- @module Wrapper.Marker -- @module Wrapper.Marker
-- @image Wrapper_Marker.png -- @image Wrapper_Marker.png
--- Marker class. --- Marker class.
-- @type MARKER -- @type MARKER
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
@@ -24,7 +23,7 @@
-- @field #number mid Marker ID. -- @field #number mid Marker ID.
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark. -- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
-- @field #string text Text displayed in the mark panel. -- @field #string text Text displayed in the mark panel.
-- @field #string message Message dispayed when the mark is added. -- @field #string message Message displayed when the mark is added.
-- @field #boolean readonly Marker is read-only. -- @field #boolean readonly Marker is read-only.
-- @field #number coalition Coalition to which the marker is displayed. -- @field #number coalition Coalition to which the marker is displayed.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@@ -45,8 +44,8 @@
-- local Coordinate = AIRBASE:FindByName( "Batumi" ):GetCoordinate() -- local Coordinate = AIRBASE:FindByName( "Batumi" ):GetCoordinate()
-- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" ) -- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" )
-- --
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e. -- Now this does **not** show the marker yet. We still need to specify to whom it is shown. There are several options, i.e.
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group. -- show the marker to everyone, to a specific coalition only, or only to a specific group.
-- --
-- ## For Everyone -- ## For Everyone
-- --
@@ -54,17 +53,17 @@
-- --
-- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" ):ToAll() -- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" ):ToAll()
-- --
-- ## For a Coaliton -- ## For a Coalition
-- --
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function. -- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
-- --
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToCoaliton(coaliton.side.BLUE) -- mymarker = MARKER:New( Coordinate , "I am Batumi Airfield" ):ToCoalition( coalition.side.BLUE )
-- --
-- ### To Blue Coaliton -- ### To Blue Coalition
-- --
-- ### To Red Coalition -- ### To Red Coalition
-- --
-- This would show the marker only to the Blue coaliton. -- This would show the marker only to the Blue coalition.
-- --
-- ## For a Group -- ## For a Group
-- --
@@ -76,12 +75,12 @@
-- --
-- The marker text and coordinate can be updated easily as shown below. -- The marker text and coordinate can be updated easily as shown below.
-- --
-- However, note that **updateing involves to remove and recreate the marker if either text or its coordinate is changed**. -- However, note that **updating involves to remove and recreate the marker if either text or its coordinate is changed**.
-- *This is a DCS scripting engine limitation.* -- *This is a DCS scripting engine limitation.*
-- --
-- ## Update Text -- ## Update Text
-- --
-- If you created a marker "mymarker" as shown above, you can update the dispayed test by -- If you created a marker "mymarker" as shown above, you can update the displayed test by
-- --
-- mymarker:UpdateText( "I am the new text at Batumi" ) -- mymarker:UpdateText( "I am the new text at Batumi" )
-- --
@@ -116,7 +115,7 @@
-- --
-- # FSM Events -- # FSM Events
-- --
-- Moose creates addditonal events, so called FSM event, when markers are added, changed, removed, and text or the coordianteis updated. -- Moose creates additional events, so called FSM event, when markers are added, changed, removed, and text or the coordinate is updated.
-- --
-- These events can be captured and used for processing via OnAfter functions as shown below. -- These events can be captured and used for processing via OnAfter functions as shown below.
-- --
@@ -133,7 +132,6 @@
-- --
-- # Examples -- # Examples
-- --
--
-- @field #MARKER -- @field #MARKER
MARKER = { MARKER = {
ClassName = "MARKER", ClassName = "MARKER",
@@ -223,7 +221,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "Removed". --- Triggers the FSM event "Removed".
-- @function [parent=#MARKER] Removed -- @function [parent=#MARKER] Removed
-- @param #MARKER self -- @param #MARKER self
@@ -242,7 +239,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "Changed". --- Triggers the FSM event "Changed".
-- @function [parent=#MARKER] Changed -- @function [parent=#MARKER] Changed
-- @param #MARKER self -- @param #MARKER self
@@ -261,7 +257,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "TextUpdate". --- Triggers the FSM event "TextUpdate".
-- @function [parent=#MARKER] TextUpdate -- @function [parent=#MARKER] TextUpdate
-- @param #MARKER self -- @param #MARKER self
@@ -280,7 +275,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param #string Text The new text. -- @param #string Text The new text.
--- Triggers the FSM event "CoordUpdate". --- Triggers the FSM event "CoordUpdate".
-- @function [parent=#MARKER] CoordUpdate -- @function [parent=#MARKER] CoordUpdate
-- @param #MARKER self -- @param #MARKER self
@@ -299,7 +293,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate. -- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
-- Handle events. -- Handle events.
self:HandleEvent( EVENTS.MarkAdded ) self:HandleEvent( EVENTS.MarkAdded )
self:HandleEvent( EVENTS.MarkRemoved ) self:HandleEvent( EVENTS.MarkRemoved )
@@ -344,7 +337,7 @@ function MARKER:ToAll(Delay)
else else
self.toall = true self.toall = true
self.tocoaliton=nil self.tocoalition = nil
self.coalition = nil self.coalition = nil
self.togroup = nil self.togroup = nil
self.groupname = nil self.groupname = nil
@@ -367,7 +360,7 @@ end
--- Place marker visible for a specific coalition only. --- Place marker visible for a specific coalition only.
-- @param #MARKER self -- @param #MARKER self
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coaliton.side.RED`. -- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coalition.side.RED`.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToCoalition( Coalition, Delay ) function MARKER:ToCoalition( Coalition, Delay )
@@ -378,7 +371,7 @@ function MARKER:ToCoalition(Coalition, Delay)
self.coalition = Coalition self.coalition = Coalition
self.tocoaliton=true self.tocoalition = true
self.toall = false self.toall = false
self.togroup = false self.togroup = false
self.groupname = nil self.groupname = nil
@@ -426,7 +419,6 @@ function MARKER:ToNeutral(Delay)
return self return self
end end
--- Place marker visible for a specific group only. --- Place marker visible for a specific group only.
-- @param #MARKER self -- @param #MARKER self
-- @param Wrapper.Group#GROUP Group The group to which the marker is displayed. -- @param Wrapper.Group#GROUP Group The group to which the marker is displayed.
@@ -448,7 +440,7 @@ function MARKER:ToGroup(Group, Delay)
self.groupname = Group:GetName() self.groupname = Group:GetName()
self.togroup = true self.togroup = true
self.tocoaliton=nil self.tocoalition = nil
self.coalition = nil self.coalition = nil
self.toall = nil self.toall = nil
@@ -531,7 +523,7 @@ function MARKER:Refresh(Delay)
self:ToAll() self:ToAll()
elseif self.tocoaliton then elseif self.tocoalition then
self:ToCoalition( self.coalition ) self:ToCoalition( self.coalition )
@@ -595,7 +587,6 @@ function MARKER:SetText(Text)
return self return self
end end
--- Check if marker is currently visible on the F10 map. --- Check if marker is currently visible on the F10 map.
-- @param #MARKER self -- @param #MARKER self
-- @return #boolean True if the marker is currently visible. -- @return #boolean True if the marker is currently visible.
@@ -770,7 +761,3 @@ function MARKER:onafterCoordUpdate(From, Event, To, Coordinate)
self:T( self.lid .. string.format( "New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS() ) ) self:T( self.lid .. string.format( "New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS() ) )
end end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -57,7 +57,6 @@ POSITIONABLE.__ = {}
--- @field #POSITIONABLE.__.Cargo --- @field #POSITIONABLE.__.Cargo
POSITIONABLE.__.Cargo = {} POSITIONABLE.__.Cargo = {}
--- A DCSPositionable --- A DCSPositionable
-- @type DCSPositionable -- @type DCSPositionable
-- @field id_ The ID of the controllable in DCS -- @field id_ The ID of the controllable in DCS
@@ -75,16 +74,19 @@ end
--- Destroys the POSITIONABLE. --- Destroys the POSITIONABLE.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the unit. -- @param #boolean GenerateEvent (Optional) If true, generates a crash or dead event for the unit. If false, no event generated. If nil, a remove event is generated.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
-- @usage -- @usage
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. --
-- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
-- Helicopter = UNIT:FindByName( "Helicopter" ) -- Helicopter = UNIT:FindByName( "Helicopter" )
-- Helicopter:Destroy( true ) -- Helicopter:Destroy( true )
--
-- @usage -- @usage
-- -- Ground unit example: destroy the Tanks and generate a S_EVENT_DEAD for each unit in the Tanks group. -- -- Ground unit example: destroy the Tanks and generate a S_EVENT_DEAD for each unit in the Tanks group.
-- Tanks = UNIT:FindByName( "Tanks" ) -- Tanks = UNIT:FindByName( "Tanks" )
-- Tanks:Destroy( true ) -- Tanks:Destroy( true )
--
-- @usage -- @usage
-- -- Ship unit example: destroy the Ship silently. -- -- Ship unit example: destroy the Ship silently.
-- Ship = STATIC:FindByName( "Ship" ) -- Ship = STATIC:FindByName( "Ship" )
@@ -147,7 +149,7 @@ function POSITIONABLE:GetPosition()
return PositionablePosition return PositionablePosition
end end
BASE:E( { "Cannot GetPositionVec3", Positionable = self, Alive = self:IsAlive() } ) BASE:E( { "Cannot GetPosition", Positionable = self, Alive = self:IsAlive() } )
return nil return nil
end end
@@ -157,6 +159,7 @@ end
-- @return DCS#Vec3 X orientation, i.e. parallel to the direction of movement. -- @return DCS#Vec3 X orientation, i.e. parallel to the direction of movement.
-- @return DCS#Vec3 Y orientation, i.e. vertical. -- @return DCS#Vec3 Y orientation, i.e. vertical.
-- @return DCS#Vec3 Z orientation, i.e. perpendicular to the direction of movement. -- @return DCS#Vec3 Z orientation, i.e. perpendicular to the direction of movement.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetOrientation() function POSITIONABLE:GetOrientation()
local position = self:GetPosition() local position = self:GetPosition()
if position then if position then
@@ -170,6 +173,7 @@ end
--- Returns a {@DCS#Vec3} table of the objects current X orientation in 3D space, i.e. along the direction of movement. --- Returns a {@DCS#Vec3} table of the objects current X orientation in 3D space, i.e. along the direction of movement.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 X orientation, i.e. parallel to the direction of movement. -- @return DCS#Vec3 X orientation, i.e. parallel to the direction of movement.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetOrientationX() function POSITIONABLE:GetOrientationX()
local position = self:GetPosition() local position = self:GetPosition()
if position then if position then
@@ -183,6 +187,7 @@ end
--- Returns a {@DCS#Vec3} table of the objects current Y orientation in 3D space, i.e. vertical orientation. --- Returns a {@DCS#Vec3} table of the objects current Y orientation in 3D space, i.e. vertical orientation.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 Y orientation, i.e. vertical. -- @return DCS#Vec3 Y orientation, i.e. vertical.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetOrientationY() function POSITIONABLE:GetOrientationY()
local position = self:GetPosition() local position = self:GetPosition()
if position then if position then
@@ -196,6 +201,7 @@ end
--- Returns a {@DCS#Vec3} table of the objects current Z orientation in 3D space, i.e. perpendicular to direction of movement. --- Returns a {@DCS#Vec3} table of the objects current Z orientation in 3D space, i.e. perpendicular to direction of movement.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 Z orientation, i.e. perpendicular to movement. -- @return DCS#Vec3 Z orientation, i.e. perpendicular to movement.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetOrientationZ() function POSITIONABLE:GetOrientationZ()
local position = self:GetPosition() local position = self:GetPosition()
if position then if position then
@@ -228,7 +234,8 @@ end
--- Returns the @{DCS#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission. --- Returns the @{DCS#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 The 3D point vector of the POSITIONABLE or `nil` if it is not existing or alive. -- @return DCS#Vec3 The 3D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVec3() function POSITIONABLE:GetVec3()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
@@ -251,7 +258,8 @@ end
--- Returns the @{DCS#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission. --- Returns the @{DCS#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec2 The 2D point vector of the POSITIONABLE or #nil if it is not existing or alive. -- @return DCS#Vec2 The 2D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVec2() function POSITIONABLE:GetVec2()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
@@ -326,9 +334,13 @@ function POSITIONABLE:GetPointVec3()
return nil return nil
end end
--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission. --- Returns a reference to a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.
-- This function works similar to POSITIONABLE.GetCoordinate(), however, this function caches, updates and re-uses the same COORDINATE object stored
-- within the POSITIONABLE. This has higher performance, but comes with all considerations associated with the possible referencing to the same COORDINATE object.
-- This should only be used when performance is critical and there is sufficient awareness of the possible pitfalls. However, in most instances, GetCoordinate() is
-- preferred as it will return a fresh new COORDINATE and thus avoid potentially unexpected issues.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return Core.Point#COORDINATE The COORDINATE of the POSITIONABLE. -- @return Core.Point#COORDINATE A reference to the COORDINATE object of the POSITIONABLE.
function POSITIONABLE:GetCoord() function POSITIONABLE:GetCoord()
-- Get DCS object. -- Get DCS object.
@@ -337,20 +349,14 @@ function POSITIONABLE:GetCoord()
if DCSPositionable then if DCSPositionable then
-- Get the current position. -- Get the current position.
local Vec3 = self:GetVec3() local PositionableVec3 = self:GetVec3()
if self.coordinate then if self.coordinate then
-- Update COORDINATE from 3D vector.
-- Update vector. self.coordinate:UpdateFromVec3( PositionableVec3 )
self.coordinate.x=Vec3.x
self.coordinate.y=Vec3.y
self.coordinate.z=Vec3.z
else else
-- New COORDINATE. -- New COORDINATE.
self.coordinate=COORDINATE:NewFromVec3(Vec3) self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
end end
return self.coordinate return self.coordinate
@@ -362,9 +368,9 @@ function POSITIONABLE:GetCoord()
return nil return nil
end end
--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission. --- Returns a new COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return Core.Point#COORDINATE The COORDINATE of the POSITIONABLE. -- @return Core.Point#COORDINATE A new COORDINATE object of the POSITIONABLE.
function POSITIONABLE:GetCoordinate() function POSITIONABLE:GetCoordinate()
-- Get DCS object. -- Get DCS object.
@@ -376,8 +382,9 @@ function POSITIONABLE:GetCoordinate()
local PositionableVec3 = self:GetVec3() local PositionableVec3 = self:GetVec3()
local coord = COORDINATE:NewFromVec3( PositionableVec3 ) local coord = COORDINATE:NewFromVec3( PositionableVec3 )
local heading = self:GetHeading()
-- Return a new coordiante object. coord.Heading = heading
-- Return a new coordinate object.
return coord return coord
end end
@@ -463,12 +470,11 @@ function POSITIONABLE:GetRandomVec3( Radius )
return nil return nil
end end
--- Get the bounding box of the underlying POSITIONABLE DCS Object. --- Get the bounding box of the underlying POSITIONABLE DCS Object.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return DCS#Box3 The bounding box of the POSITIONABLE. -- @return DCS#Box3 The bounding box of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive. -- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetBoundingBox() --R2.1 function POSITIONABLE:GetBoundingBox()
self:F2() self:F2()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
@@ -486,7 +492,6 @@ function POSITIONABLE:GetBoundingBox() --R2.1
return nil return nil
end end
--- Get the object size. --- Get the object size.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return DCS#Distance Max size of object in x, z or 0 if bounding box could not be obtained. -- @return DCS#Distance Max size of object in x, z or 0 if bounding box could not be obtained.
@@ -510,14 +515,15 @@ end
--- Get the bounding radius of the underlying POSITIONABLE DCS Object. --- Get the bounding radius of the underlying POSITIONABLE DCS Object.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #number mindist (Optional) If bounding box is smaller than this value, mindist is returned. -- @param #number MinDist (Optional) If bounding box is smaller than this value, MinDist is returned.
-- @return DCS#Distance The bounding radius of the POSITIONABLE or #nil if the POSITIONABLE is not existing or alive. -- @return DCS#Distance The bounding radius of the POSITIONABLE
function POSITIONABLE:GetBoundingRadius(mindist) -- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetBoundingRadius( MinDist )
self:F2() self:F2()
local Box = self:GetBoundingBox() local Box = self:GetBoundingBox()
local boxmin=mindist or 0 local boxmin = MinDist or 0
if Box then if Box then
local X = Box.max.x - Box.min.x local X = Box.max.x - Box.min.x
local Z = Box.max.z - Box.min.z local Z = Box.max.z - Box.min.z
@@ -531,7 +537,7 @@ function POSITIONABLE:GetBoundingRadius(mindist)
return nil return nil
end end
--- Returns the altitude of the POSITIONABLE. --- Returns the altitude above sea level of the POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Distance The altitude of the POSITIONABLE. -- @return DCS#Distance The altitude of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive. -- @return #nil The POSITIONABLE is not existing or alive.
@@ -574,7 +580,6 @@ function POSITIONABLE:IsAboveRunway()
return nil return nil
end end
function POSITIONABLE:GetSize() function POSITIONABLE:GetSize()
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
@@ -586,11 +591,10 @@ function POSITIONABLE:GetSize()
end end
end end
--- Returns the POSITIONABLE heading in degrees. --- Returns the POSITIONABLE heading in degrees.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The POSITIONABLE heading in degrees or `nil` if not existing or alive. -- @return #number The POSITIONABLE heading in degrees.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetHeading() function POSITIONABLE:GetHeading()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
@@ -613,7 +617,6 @@ function POSITIONABLE:GetHeading()
end end
self:E( { "Cannot GetHeading", Positionable = self, Alive = self:IsAlive() } ) self:E( { "Cannot GetHeading", Positionable = self, Alive = self:IsAlive() } )
return nil return nil
end end
@@ -623,6 +626,7 @@ end
-- If the unit is a helicopter or a plane, then this method will return true, otherwise false. -- If the unit is a helicopter or a plane, then this method will return true, otherwise false.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #boolean Air category evaluation result. -- @return #boolean Air category evaluation result.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:IsAir() function POSITIONABLE:IsAir()
self:F2() self:F2()
@@ -638,6 +642,7 @@ function POSITIONABLE:IsAir()
return IsAirResult return IsAirResult
end end
self:E( { "Cannot check IsAir", Positionable = self, Alive = self:IsAlive() } )
return nil return nil
end end
@@ -645,6 +650,7 @@ end
-- If the unit is a ground vehicle or infantry, this method will return true, otherwise false. -- If the unit is a ground vehicle or infantry, this method will return true, otherwise false.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #boolean Ground category evaluation result. -- @return #boolean Ground category evaluation result.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:IsGround() function POSITIONABLE:IsGround()
self:F2() self:F2()
@@ -660,13 +666,14 @@ function POSITIONABLE:IsGround()
return IsGroundResult return IsGroundResult
end end
self:E( { "Cannot check IsGround", Positionable = self, Alive = self:IsAlive() } )
return nil return nil
end end
--- Returns if the unit is of ship category. --- Returns if the unit is of ship category.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #boolean Ship category evaluation result. -- @return #boolean Ship category evaluation result.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:IsShip() function POSITIONABLE:IsShip()
self:F2() self:F2()
@@ -674,16 +681,18 @@ function POSITIONABLE:IsShip()
if DCSUnit then if DCSUnit then
local UnitDescriptor = DCSUnit:getDesc() local UnitDescriptor = DCSUnit:getDesc()
self:T3( { UnitDescriptor.category, Unit.Category.SHIP } )
local IsShip = ( UnitDescriptor.category == Unit.Category.SHIP ) local IsShipResult = (UnitDescriptor.category == Unit.Category.SHIP)
return IsShip self:T3( IsShipResult )
return IsShipResult
end end
self:E( { "Cannot check IsShip", Positionable = self, Alive = self:IsAlive() } )
return nil return nil
end end
--- Returns if the unit is a submarine. --- Returns if the unit is a submarine.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #boolean Submarines attributes result. -- @return #boolean Submarines attributes result.
@@ -701,10 +710,10 @@ function POSITIONABLE:IsSubmarine()
end end
end end
self:E( { "Cannot check IsSubmarine", Positionable = self, Alive = self:IsAlive() } )
return nil return nil
end end
--- Returns true if the POSITIONABLE is in the air. --- Returns true if the POSITIONABLE is in the air.
-- Polymorphic, is overridden in GROUP and UNIT. -- Polymorphic, is overridden in GROUP and UNIT.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
@@ -716,8 +725,7 @@ function POSITIONABLE:InAir()
return nil return nil
end end
--- Returns the a @{Velocity} object from the POSITIONABLE.
--- Returns the a @{Velocity} object from the positionable.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return Core.Velocity#VELOCITY Velocity The Velocity object. -- @return Core.Velocity#VELOCITY Velocity The Velocity object.
-- @return #nil The POSITIONABLE is not existing or alive. -- @return #nil The POSITIONABLE is not existing or alive.
@@ -736,8 +744,6 @@ function POSITIONABLE:GetVelocity()
return nil return nil
end end
--- Returns the POSITIONABLE velocity Vec3 vector. --- Returns the POSITIONABLE velocity Vec3 vector.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 The velocity Vec3 vector -- @return DCS#Vec3 The velocity Vec3 vector
@@ -760,30 +766,29 @@ end
--- Get relative velocity with respect to another POSITIONABLE. --- Get relative velocity with respect to another POSITIONABLE.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #POSITIONABLE positionable Other positionable. -- @param #POSITIONABLE Positionable Other POSITIONABLE.
-- @return #number Relative velocity in m/s. -- @return #number Relative velocity in m/s.
function POSITIONABLE:GetRelativeVelocity(positionable) function POSITIONABLE:GetRelativeVelocity( Positionable )
self:F2( self.PositionableName ) self:F2( self.PositionableName )
local v1 = self:GetVelocityVec3() local v1 = self:GetVelocityVec3()
local v2=positionable:GetVelocityVec3() local v2 = Positionable:GetVelocityVec3()
local vtot = UTILS.VecAdd( v1, v2 ) local vtot = UTILS.VecAdd( v1, v2 )
return UTILS.VecNorm( vtot ) return UTILS.VecNorm( vtot )
end end
--- Returns the POSITIONABLE height above sea level in meters.
--- Returns the POSITIONABLE height in meters.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 The height of the positionable. -- @return DCS#Vec3 The height of the POSITIONABLE in meters.
-- @return #nil The POSITIONABLE is not existing or alive. -- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetHeight() --R2.1 function POSITIONABLE:GetHeight()
self:F2( self.PositionableName ) self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
if DCSPositionable then if DCSPositionable and DCSPositionable:isExist() then
local PositionablePosition = DCSPositionable:getPosition() local PositionablePosition = DCSPositionable:getPosition()
if PositionablePosition then if PositionablePosition then
local PositionableHeight = PositionablePosition.p.y local PositionableHeight = PositionablePosition.p.y
@@ -795,10 +800,9 @@ function POSITIONABLE:GetHeight() --R2.1
return nil return nil
end end
--- Returns the POSITIONABLE velocity in km/h. --- Returns the POSITIONABLE velocity in km/h.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The velocity in km/h -- @return #number The velocity in km/h.
function POSITIONABLE:GetVelocityKMH() function POSITIONABLE:GetVelocityKMH()
self:F2( self.PositionableName ) self:F2( self.PositionableName )
@@ -841,9 +845,10 @@ function POSITIONABLE:GetVelocityKNOTS()
return UTILS.MpsToKnots( self:GetVelocityMPS() ) return UTILS.MpsToKnots( self:GetVelocityMPS() )
end end
--- Returns the Angle of Attack of a positionable. --- Returns the Angle of Attack of a POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number Angle of attack in degrees. -- @return #number Angle of attack in degrees.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetAoA() function POSITIONABLE:GetAoA()
-- Get position of the unit. -- Get position of the unit.
@@ -889,9 +894,10 @@ function POSITIONABLE:GetAoA()
return nil return nil
end end
--- Returns the unit's climb or descent angle. --- Returns the climb or descent angle of the POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number Climb or descent angle in degrees. Or 0 if velocity vector norm is zero (or nil). Or nil, if the position of the POSITIONABLE returns nil. -- @return #number Climb or descent angle in degrees. Or 0 if velocity vector norm is zero.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetClimbAngle() function POSITIONABLE:GetClimbAngle()
-- Get position of the unit. -- Get position of the unit.
@@ -912,14 +918,16 @@ function POSITIONABLE:GetClimbAngle()
else else
return 0 return 0
end end
end end
return nil return nil
end end
--- Returns the pitch angle of a unit. --- Returns the pitch angle of a POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number Pitch ange in degrees. -- @return #number Pitch angle in degrees.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetPitch() function POSITIONABLE:GetPitch()
-- Get position of the unit. -- Get position of the unit.
@@ -934,7 +942,8 @@ end
--- Returns the roll angle of a unit. --- Returns the roll angle of a unit.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number Pitch ange in degrees. -- @return #number Pitch angle in degrees.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetRoll() function POSITIONABLE:GetRoll()
-- Get position of the unit. -- Get position of the unit.
@@ -942,34 +951,40 @@ function POSITIONABLE:GetRoll()
if unitpos then if unitpos then
--first, make a vector that is perpendicular to y and unitpos.x with cross product -- First, make a vector that is perpendicular to y and unitpos.x with cross product
local cp = UTILS.VecCross( unitpos.x, { x = 0, y = 1, z = 0 } ) local cp = UTILS.VecCross( unitpos.x, { x = 0, y = 1, z = 0 } )
--now, get dot product of of this cross product with unitpos.z -- Now, get dot product of of this cross product with unitpos.z
local dp = UTILS.VecDot( cp, unitpos.z ) local dp = UTILS.VecDot( cp, unitpos.z )
--now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) -- Now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|)
local Roll = math.acos( dp / (UTILS.VecNorm( cp ) * UTILS.VecNorm( unitpos.z )) ) local Roll = math.acos( dp / (UTILS.VecNorm( cp ) * UTILS.VecNorm( unitpos.z )) )
--now, have to get sign of roll. -- Now, have to get sign of roll. By convention, making right roll positive
-- by convention, making right roll positive -- To get sign of roll, use the y component of unitpos.z. For right roll, y component is negative.
-- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative.
if unitpos.z.y > 0 then -- left roll, flip the sign of the roll if unitpos.z.y > 0 then -- left roll, flip the sign of the roll
Roll = -Roll Roll = -Roll
end end
return math.deg( Roll ) return math.deg( Roll )
end
end end
--- Returns the yaw angle of a unit. return nil
end
--- Returns the yaw angle of a POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number Yaw ange in degrees. -- @return #number Yaw angle in degrees.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetYaw() function POSITIONABLE:GetYaw()
-- Get position of the unit.
local unitpos = self:GetPosition() local unitpos = self:GetPosition()
if unitpos then if unitpos then
-- get unit velocity -- get unit velocity
local unitvel = self:GetVelocityVec3() local unitvel = self:GetVelocityVec3()
@@ -991,38 +1006,43 @@ function POSITIONABLE:GetYaw()
end end
return Yaw return Yaw
end end
end
end end
--- Returns the message text with the callsign embedded (if there is one).
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return #string The message text
function POSITIONABLE:GetMessageText( Message, Name )
local DCSObject = self:GetDCSObject()
if DCSObject then
local Callsign = string.format( "%s", ( ( Name ~= "" and Name ) or self:GetCallsign() ~= "" and self:GetCallsign() ) or self:GetName() )
local MessageText = string.format("%s - %s", Callsign, Message )
return MessageText
end
return nil return nil
end end
--- Returns the message text with the callsign embedded (if there is one).
-- @param #POSITIONABLE self
-- @param #string Message The message text.
-- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
-- @return #string The message text.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetMessageText( Message, Name )
local DCSObject = self:GetDCSObject()
if DCSObject then
local Callsign = string.format( "%s", ((Name ~= "" and Name) or self:GetCallsign() ~= "" and self:GetCallsign()) or self:GetName() )
local MessageText = string.format( "%s - %s", Callsign, Message )
return MessageText
end
return nil
end
--- Returns a message with the callsign embedded (if there is one). --- Returns a message with the callsign embedded (if there is one).
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
-- @return Core.Message#MESSAGE -- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText function POSITIONABLE:GetMessage( Message, Duration, Name )
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
local MessageText = self:GetMessageText( Message, Name ) local MessageText = self:GetMessageText( Message, Name )
return MESSAGE:New( MessageText, Duration ) return MESSAGE:New( MessageText, Duration )
@@ -1035,7 +1055,7 @@ end
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param Core.Message#MESSAGE MessageType MessageType The message type. -- @param Core.Message#MESSAGE MessageType MessageType The message type.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
-- @return Core.Message#MESSAGE -- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessageType( Message, MessageType, Name ) -- R2.2 changed callsign and name and using GetMessageText function POSITIONABLE:GetMessageType( Message, MessageType, Name ) -- R2.2 changed callsign and name and using GetMessageText
@@ -1053,7 +1073,7 @@ end
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToAll( Message, Duration, Name ) function POSITIONABLE:MessageToAll( Message, Duration, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1071,7 +1091,7 @@ end
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param DCS#coalition MessageCoalition The Coalition receiving the message. -- @param DCS#coalition MessageCoalition The Coalition receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1085,14 +1105,13 @@ function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, N
return nil return nil
end end
--- Send a message to a coalition. --- Send a message to a coalition.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration. -- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
-- @param DCS#coalition MessageCoalition The Coalition receiving the message. -- @param DCS#coalition MessageCoalition The Coalition receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition, Name ) function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition, Name )
self:F2( { Message, MessageType } ) self:F2( { Message, MessageType } )
@@ -1106,13 +1125,12 @@ function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoali
return nil return nil
end end
--- Send a message to the red coalition. --- Send a message to the red coalition.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToRed( Message, Duration, Name ) function POSITIONABLE:MessageToRed( Message, Duration, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1129,7 +1147,7 @@ end
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToBlue( Message, Duration, Name ) function POSITIONABLE:MessageToBlue( Message, Duration, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1147,7 +1165,7 @@ end
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param Wrapper.Client#CLIENT Client The client object receiving the message. -- @param Wrapper.Client#CLIENT Client The client object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToClient( Message, Duration, Client, Name ) function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1159,13 +1177,37 @@ function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
return nil return nil
end end
--- Send a message to a @{Wrapper.Unit}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message.
-- @param Wrapper.Unit#UNIT MessageUnit The UNIT object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToUnit( Message, Duration, MessageUnit, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
if MessageUnit:IsAlive() then
self:GetMessage( Message, Duration, Name ):ToUnit( MessageUnit )
else
BASE:E( { "Message not sent to Unit; Unit is not alive...", Message = Message, MessageUnit = MessageUnit } )
end
else
BASE:E( { "Message not sent to Unit; Positionable is not alive ...", Message = Message, Positionable = self, MessageUnit = MessageUnit } )
end
end
end
--- Send a message to a @{Wrapper.Group}. --- Send a message to a @{Wrapper.Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message. -- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name ) function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1178,11 +1220,15 @@ function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
BASE:E( { "Message not sent to Group; Group is not alive...", Message = Message, MessageGroup = MessageGroup } ) BASE:E( { "Message not sent to Group; Group is not alive...", Message = Message, MessageGroup = MessageGroup } )
end end
else else
BASE:E( { "Message not sent to Group; Positionable is not alive ...", Message = Message, Positionable = self, MessageGroup = MessageGroup } ) BASE:E( {
"Message not sent to Group; Positionable is not alive ...",
Message = Message,
Positionable = self,
MessageGroup = MessageGroup
} )
end end
end end
return nil return nil
end end
@@ -1192,7 +1238,7 @@ end
-- @param #string Message The message text -- @param #string Message The message text
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration. -- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message. -- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, the Name is the type of the POSITIONABLE.
function POSITIONABLE:MessageTypeToGroup( Message, MessageType, MessageGroup, Name ) function POSITIONABLE:MessageTypeToGroup( Message, MessageType, MessageGroup, Name )
self:F2( { Message, MessageType } ) self:F2( { Message, MessageType } )
@@ -1212,16 +1258,38 @@ end
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param Core.Set#SET_GROUP MessageSetGroup The SET_GROUP collection receiving the message. -- @param Core.Set#SET_GROUP MessageSetGroup The SET_GROUP collection receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1 function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
if DCSObject:isExist() then if DCSObject:isExist() then
MessageSetGroup:ForEachGroupAlive( MessageSetGroup:ForEachGroupAlive( function( MessageGroup )
function( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup ) self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup )
end )
end
end
return nil
end
--- Send a message to a @{Core.Set#SET_UNIT}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message.
-- @param Core.Set#SET_UNIT MessageSetUnit The SET_UNIT collection receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToSetUnit( Message, Duration, MessageSetUnit, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
MessageSetUnit:ForEachUnit(
function( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToUnit( MessageGroup )
end end
) )
end end
@@ -1235,7 +1303,7 @@ end
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #string Message The message text -- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message. -- @param DCS#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @param #string Name (Optional) The Name of the sender. If not provided, Name is set to the type of the POSITIONABLE.
function POSITIONABLE:Message( Message, Duration, Name ) function POSITIONABLE:Message( Message, Duration, Name )
self:F2( { Message, Duration } ) self:F2( { Message, Duration } )
@@ -1251,7 +1319,7 @@ end
-- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message -- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return Core.Radio#RADIO Radio -- @return Core.Radio#RADIO Radio
function POSITIONABLE:GetRadio() --R2.1 function POSITIONABLE:GetRadio()
self:F2( self ) self:F2( self )
return RADIO:New( self ) return RADIO:New( self )
end end
@@ -1259,7 +1327,7 @@ end
--- Create a @{Core.Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals --- Create a @{Core.Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return Core.Radio#RADIO Radio -- @return Core.Radio#RADIO Radio
function POSITIONABLE:GetBeacon() --R2.1 function POSITIONABLE:GetBeacon()
self:F2( self ) self:F2( self )
return BEACON:New( self ) return BEACON:New( self )
end end
@@ -1270,7 +1338,7 @@ end
-- @param #number LaserCode Laser code or random number in [1000, 9999]. -- @param #number LaserCode Laser code or random number in [1000, 9999].
-- @param #number Duration Duration of lasing in seconds. -- @param #number Duration Duration of lasing in seconds.
-- @return Core.Spot#SPOT -- @return Core.Spot#SPOT
function POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1 function POSITIONABLE:LaseUnit( Target, LaserCode, Duration )
self:F2() self:F2()
LaserCode = LaserCode or math.random( 1000, 9999 ) LaserCode = LaserCode or math.random( 1000, 9999 )
@@ -1278,7 +1346,7 @@ function POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1
local RecceDcsUnit = self:GetDCSObject() local RecceDcsUnit = self:GetDCSObject()
local TargetVec3 = Target:GetVec3() local TargetVec3 = Target:GetVec3()
self:F("bulding spot") self:F( "building spot" )
self.Spot = SPOT:New( self ) -- Core.Spot#SPOT self.Spot = SPOT:New( self ) -- Core.Spot#SPOT
self.Spot:LaseOn( Target, LaserCode, Duration ) self.Spot:LaseOn( Target, LaserCode, Duration )
self.LaserCode = LaserCode self.LaserCode = LaserCode
@@ -1289,7 +1357,7 @@ end
--- Start Lasing a COORDINATE. --- Start Lasing a COORDINATE.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param Core.Point#COORDIUNATE Coordinate The coordinate where the lase is pointing at. -- @param Core.Point#COORDINATE Coordinate The coordinate where the lase is pointing at.
-- @param #number LaserCode Laser code or random number in [1000, 9999]. -- @param #number LaserCode Laser code or random number in [1000, 9999].
-- @param #number Duration Duration of lasing in seconds. -- @param #number Duration Duration of lasing in seconds.
-- @return Core.Spot#SPOT -- @return Core.Spot#SPOT
@@ -1308,7 +1376,7 @@ end
--- Stop Lasing a POSITIONABLE --- Stop Lasing a POSITIONABLE
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #POSITIONABLE -- @return #POSITIONABLE
function POSITIONABLE:LaseOff() --R2.1 function POSITIONABLE:LaseOff()
self:F2() self:F2()
if self.Spot then if self.Spot then
@@ -1322,7 +1390,7 @@ end
--- Check if the POSITIONABLE is lasing a target --- Check if the POSITIONABLE is lasing a target
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #boolean true if it is lasing a target -- @return #boolean true if it is lasing a target
function POSITIONABLE:IsLasing() --R2.1 function POSITIONABLE:IsLasing()
self:F2() self:F2()
local Lasing = false local Lasing = false
@@ -1337,7 +1405,7 @@ end
--- Get the Spot --- Get the Spot
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return Core.Spot#SPOT The Spot -- @return Core.Spot#SPOT The Spot
function POSITIONABLE:GetSpot() --R2.1 function POSITIONABLE:GetSpot()
return self.Spot return self.Spot
end end
@@ -1345,7 +1413,7 @@ end
--- Get the last assigned laser code --- Get the last assigned laser code
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @return #number The laser code -- @return #number The laser code
function POSITIONABLE:GetLaserCode() --R2.1 function POSITIONABLE:GetLaserCode()
return self.LaserCode return self.LaserCode
end end
@@ -1368,8 +1436,6 @@ do -- Cargo
return self.__.Cargo return self.__.Cargo
end end
--- Remove cargo. --- Remove cargo.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo -- @param Core.Cargo#CARGO Cargo
@@ -1431,7 +1497,7 @@ do -- Cargo
-- @return #number CargoBayFreeWeight -- @return #number CargoBayFreeWeight
function POSITIONABLE:GetCargoBayFreeWeight() function POSITIONABLE:GetCargoBayFreeWeight()
-- When there is no cargo bay weight limit set, then calculate this for this positionable! -- When there is no cargo bay weight limit set, then calculate this for this POSITIONABLE!
if not self.__.CargoBayWeightLimit then if not self.__.CargoBayWeightLimit then
self:SetCargoBayWeightLimit() self:SetCargoBayWeightLimit()
end end
@@ -1460,16 +1526,16 @@ do -- Cargo
elseif self.__.CargoBayWeightLimit ~= nil then elseif self.__.CargoBayWeightLimit ~= nil then
-- Value already set ==> Do nothing! -- Value already set ==> Do nothing!
else else
-- If weightlimit is not provided, we will calculate it depending on the type of unit. -- If WeightLimit is not provided, we will calculate it depending on the type of unit.
-- When an airplane or helicopter, we calculate the weightlimit based on the descriptor. -- When an airplane or helicopter, we calculate the WeightLimit based on the descriptor.
if self:IsAir() then if self:IsAir() then
local Desc = self:GetDesc() local Desc = self:GetDesc()
self:F( { Desc = Desc } ) self:F( { Desc = Desc } )
local Weights = { local Weights = {
["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry., ["C-17A"] = 35000, -- 77519 cannot be used, because it loads way too many APCs and infantry.
["C-130"] = 22000 --The real value cannot be used, because it loads way too much apcs and infantry., ["C-130"] = 22000 -- The real value cannot be used, because it loads way too many APCs and infantry.
} }
self.__.CargoBayWeightLimit = Weights[Desc.typeName] or (Desc.massMax - (Desc.massEmpty + Desc.fuelMassMax)) self.__.CargoBayWeightLimit = Weights[Desc.typeName] or (Desc.massMax - (Desc.massEmpty + Desc.fuelMassMax))
@@ -1485,7 +1551,7 @@ do -- Cargo
["Dry-cargo ship-2"] = 70000, ["Dry-cargo ship-2"] = 70000,
["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia). ["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia).
["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops. ["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops.
["LST_Mk2"] =2100000, -- Can carry 2100 tons according to wiki source! ["LST_Mk2"] = 2100000 -- Can carry 2100 tons according to wiki source!
} }
self.__.CargoBayWeightLimit = (Weights[Desc.typeName] or 50000) self.__.CargoBayWeightLimit = (Weights[Desc.typeName] or 50000)
@@ -1535,6 +1601,10 @@ do -- Cargo
["Ural-4320T"] = 14, ["Ural-4320T"] = 14,
["ZBD04A"] = 7, -- new by kappa ["ZBD04A"] = 7, -- new by kappa
["VAB_Mephisto"] = 8, -- new by Apple ["VAB_Mephisto"] = 8, -- new by Apple
["tt_KORD"] = 6, -- 2.7.1 HL/TT
["tt_DSHK"] = 6,
["HL_KORD"] = 6,
["HL_DSHK"] = 6,
} }
local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95 local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95
@@ -1586,9 +1656,9 @@ end
--- Smoke the POSITIONABLE. --- Smoke the POSITIONABLE.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color to smoke to positionable. -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
-- @param #number Range The range in meters to randomize the smoking around the positionable. -- @param #number Range The range in meters to randomize the smoking around the POSITIONABLE.
-- @param #number AddHeight The height in meters to add to the altitude of the positionable. -- @param #number AddHeight The height in meters to add to the altitude of the POSITIONABLE.
function POSITIONABLE:Smoke( SmokeColor, Range, AddHeight ) function POSITIONABLE:Smoke( SmokeColor, Range, AddHeight )
self:F2() self:F2()
if Range then if Range then
@@ -1638,7 +1708,6 @@ function POSITIONABLE:SmokeBlue()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end end
--- Returns true if the unit is within a @{Zone}. --- Returns true if the unit is within a @{Zone}.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param Core.Zone#ZONE_BASE Zone The zone to test. -- @param Core.Zone#ZONE_BASE Zone The zone to test.

View File

@@ -256,4 +256,3 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
return self return self
end end

View File

@@ -90,6 +90,7 @@
UNIT = { UNIT = {
ClassName="UNIT", ClassName="UNIT",
UnitName=nil, UnitName=nil,
GroupName=nil,
} }
@@ -110,11 +111,20 @@ UNIT = {
function UNIT:Register( UnitName ) function UNIT:Register( UnitName )
-- Inherit CONTROLLABLE. -- Inherit CONTROLLABLE.
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) --#UNIT
-- Set unit name. -- Set unit name.
self.UnitName = UnitName self.UnitName = UnitName
local unit=Unit.getByName(self.UnitName)
if unit then
local group = unit:getGroup()
if group then
self.GroupName=group:getName()
end
end
-- Set event prio. -- Set event prio.
self:SetEventPriority( 3 ) self:SetEventPriority( 3 )
@@ -168,8 +178,28 @@ function UNIT:GetDCSObject()
return nil return nil
end end
--- Returns the unit altitude above sea level in meters.
-- @param Wrapper.Unit#UNIT self
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return #number The height of the group or nil if is not existing or alive.
function UNIT:GetAltitude(FromGround)
local DCSUnit = Unit.getByName( self.UnitName )
if DCSUnit then
local altitude = 0
local point = DCSUnit:getPoint() --DCS#Vec3
altitude = point.y
if FromGround then
local land = land.getHeight( { x = point.x, y = point.z } ) or 0
altitude = altitude - land
end
return altitude
end
return nil
end
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. --- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
-- --
@@ -408,6 +438,17 @@ function UNIT:GetClient()
return nil return nil
end end
--- [AIRPLANE] Get the NATO reporting name of a UNIT. Currently airplanes only!
--@param #UNIT self
--@return #string NatoReportingName or "Bogey" if unknown.
function UNIT:GetNatoReportingName()
local typename = self:GetTypeName()
return UTILS.GetReportingName(typename)
end
--- Returns the unit's number in the group. --- Returns the unit's number in the group.
-- The number is the same number the unit has in ME. -- The number is the same number the unit has in ME.
-- It may not be changed during the mission. -- It may not be changed during the mission.
@@ -442,7 +483,7 @@ function UNIT:GetSpeedMax()
return SpeedMax*3.6 return SpeedMax*3.6
end end
return nil return 0
end end
--- Returns the unit's max range in meters derived from the DCS descriptors. --- Returns the unit's max range in meters derived from the DCS descriptors.
@@ -524,6 +565,63 @@ function UNIT:IsTanker()
return tanker, system return tanker, system
end end
--- Check if the unit can supply ammo. Currently, we have
--
-- * M 818
-- * Ural-375
-- * ZIL-135
--
-- This list needs to be extended, if DCS adds other units capable of supplying ammo.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply ammo.
function UNIT:IsAmmoSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M 818" then
-- Blue ammo truck.
return true
elseif typename=="Ural-375" then
-- Red ammo truck.
return true
elseif typename=="ZIL-135" then
-- Red ammo truck. Checked that it can also provide ammo.
return true
end
return false
end
--- Check if the unit can supply fuel. Currently, we have
--
-- * M978 HEMTT Tanker
-- * ATMZ-5
-- * ATMZ-10
-- * ATZ-5
--
-- This list needs to be extended, if DCS adds other units capable of supplying fuel.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply fuel.
function UNIT:IsFuelSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M978 HEMTT Tanker" then
return true
elseif typename=="ATMZ-5" then
return true
elseif typename=="ATMZ-10" then
return true
elseif typename=="ATZ-5" then
return true
end
return false
end
--- Returns the unit's group if it exist and nil otherwise. --- Returns the unit's group if it exist and nil otherwise.
-- @param Wrapper.Unit#UNIT self -- @param Wrapper.Unit#UNIT self
@@ -626,8 +724,8 @@ function UNIT:GetAmmunition()
-- Type name of current weapon. -- Type name of current weapon.
local Tammo=ammotable[w]["desc"]["typeName"] local Tammo=ammotable[w]["desc"]["typeName"]
local _weaponString = UTILS.Split(Tammo,"%.") --local _weaponString = UTILS.Split(Tammo,"%.")
local _weaponName = _weaponString[#_weaponString] --local _weaponName = _weaponString[#_weaponString]
-- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3 -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3
local Category=ammotable[w].desc.category local Category=ammotable[w].desc.category
@@ -656,7 +754,8 @@ function UNIT:GetAmmunition()
elseif Category==Weapon.Category.MISSILE then elseif Category==Weapon.Category.MISSILE then
-- Add up all cruise missiles (category 5)
-- Add up all missiles (category 5)
if MissileCategory==Weapon.MissileCategory.AAM then if MissileCategory==Weapon.MissileCategory.AAM then
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
@@ -665,6 +764,10 @@ function UNIT:GetAmmunition()
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.OTHER then elseif MissileCategory==Weapon.MissileCategory.OTHER then
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.SAM then
nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.CRUISE then
nmissiles=nmissiles+Nammo
end end
end end
@@ -678,8 +781,6 @@ function UNIT:GetAmmunition()
return nammo, nshells, nrockets, nbombs, nmissiles return nammo, nshells, nrockets, nbombs, nmissiles
end end
--- Returns the unit sensors. --- Returns the unit sensors.
-- @param #UNIT self -- @param #UNIT self
-- @return DCS#Unit.Sensors Table of sensors. -- @return DCS#Unit.Sensors Table of sensors.
@@ -996,7 +1097,7 @@ function UNIT:GetThreatLevel()
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3 not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1 elseif Attributes["Infantry"] or Attributes["EWR"] then ThreatLevel = 1
end end
ThreatText = ThreatLevels[ThreatLevel+1] ThreatText = ThreatLevels[ThreatLevel+1]

View File

@@ -5,6 +5,7 @@ Utilities/Enums.lua
Utilities/Profiler.lua Utilities/Profiler.lua
Utilities/Templates.lua Utilities/Templates.lua
Utilities/STTS.lua Utilities/STTS.lua
Utilities/FiFo.lua
Core/Base.lua Core/Base.lua
Core/Beacon.lua Core/Beacon.lua

View File

@@ -61,9 +61,10 @@ Documentation on the MOOSE class hierarchy, usage guides and background informat
## [MOOSE Youtube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) ## [MOOSE Youtube Tutorials](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
MOOSE has a [broadcast and training channel on YouTube](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) with various channels that you can watch. Pene has kindly created a [tutorial series for MOOSE](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
with various videos that you can watch.