Compare commits

...

1297 Commits

Author SHA1 Message Date
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
Applevangelist
8af3f89c14 Adjustments for Forrestal by Pene 2021-10-24 14:35:55 +02:00
Applevangelist
fe3079caad Added Bell-47 2021-10-22 17:04:23 +02:00
Applevangelist
61ac6b4131 Added Bell-47 2021-10-22 17:04:19 +02:00
Frank
36cb189512 Merge pull request #1612 from FlightControl-Master/FF/MasterDevel
AIRBOSS v1.2.0
2021-10-20 19:55:19 +02:00
Frank
15f9843878 AIRBOSS v1.2.0
- Added Forrestal carrier CV-59
2021-10-16 12:11:34 +02:00
Frank
67f847dd16 Update Group.lua
- Fixed SetInvisible and SetImmortal functions to acknowledge parameter false.
2021-10-12 22:16:18 +02:00
Applevangelist
8b9143d3f1 CTLD - added option to force opening of doors 2021-10-12 08:32:34 +02:00
Applevangelist
0388d47f23 CSAR - Added country options for spawned pilots 2021-10-12 08:31:55 +02:00
Applevangelist
de9b173d9b UTILS - added door check for Hercules 2021-10-07 18:14:29 +02:00
Applevangelist
2cecc526fb ZONE_CAPTURE_COALITION - fixed an issue when monitoring hits and SCENERY was delivered as hit UNIT 2021-10-05 19:10:21 +02:00
Applevangelist
968d178317 Update README.md 2021-10-02 10:18:13 +02:00
Applevangelist
3c477b872a CSAR - hovering rescued parameters added 2021-10-01 14:54:31 +02:00
Applevangelist
77e6088114 UTILS - corrected open door check MI-8 2021-10-01 14:54:16 +02:00
Applevangelist
edd6594953 CTLD: added user-friendly function to inject static cargos: CTLD:InjectStaticFromTemplate(Zone, Template, Mass) 2021-10-01 10:43:17 +02:00
Applevangelist
f8c05c99d0 RADIO - delete frequency check 2021-09-30 08:07:34 +02:00
Applevangelist
50f6d98b49 push for a new build 2021-09-29 09:25:26 +02:00
Frank
147eeb05f6 Merge pull request #1607 from FlightControl-Master/FF/MasterDevel
DATABASE
2021-09-29 09:22:41 +02:00
Frank
d8cb15a577 Update Airbase.lua 2021-09-29 09:01:47 +02:00
Frank
0daac876ea Update Airbase.lua
- Register oil rigs and gas platforms as helipads. DCS bug registers them as ship (Airbase.Category.SHIP instead of Airbase.Category.HELIPAD).
2021-09-29 09:00:53 +02:00
Frank
1832125022 Globals
- Moved _DATABASE:_RegisterAirbases() to Globals.lua
2021-09-29 08:58:46 +02:00
Applevangelist
c311c40b72 GROUP:GetAmmunition() - fix to also return bomb count (#1606)
GROUP:GetAmmunition() - fix to also return bomb count
2021-09-28 16:53:53 +02:00
Applevangelist
db516a2077 Fix "local" error 2021-09-27 15:48:45 +02:00
Applevangelist
ff8766669c Small fix for Airbase Parking Spot Finder 2021-09-26 09:51:14 +02:00
Applevangelist
06dc9a732e bugfix 2021-09-24 18:37:13 +02:00
Applevangelist
50c74d0852 Added option for slingload: enableslingload 2021-09-24 11:08:23 +02:00
Applevangelist
1c97eb6f3c SPAWNSTATIC - bugfix on canCargo, mass could be set but not transported into the template spawn 2021-09-24 11:04:54 +02:00
Applevangelist
69449430d1 CTLD - Added Statics as cargo (#1600)
CTLD - Added Statics as cargo, and the ability to load and save them (alongside your dropped buildable crates).
2021-09-22 15:54:37 +02:00
Applevangelist
663cd34aa3 CTLD - fix when using SAVE or LOAD w/o filename and path 2021-09-21 07:48:09 +02:00
Applevangelist
cfed6f5153 SET - Added SET_CLIENT:CountAlive() 2021-09-21 07:47:43 +02:00
Applevangelist
2b22d5288c CTLD - added persistence 2021-09-20 14:27:45 +02:00
Applevangelist
a64424ecc8 Positionable - Add IsSubmarine, Passenger seats for VAB Mephisto 2021-09-20 14:27:22 +02:00
Applevangelist
fd1b2ecb86 ZONE - Docu bug fix 2021-09-20 14:26:42 +02:00
Applevangelist
6cae3e62cf CTLD - small bug fix on stock removal 2021-09-12 17:37:32 +02:00
Applevangelist
05ce7e4513 CTLD - added alternative crate spawn by @mousepilot. Add menu item to list stock.Injected troops will not lead to cargo type duplication. 2021-09-11 15:20:20 +02:00
Applevangelist
136bd19f19 Bug fixing 2021-09-11 10:03:49 +02:00
Applevangelist
8873504daf CTLD: Align to Dev changes 2021-09-07 19:52:14 +02:00
Applevangelist
a844a5d697 CSAR: Align to Dev changes 2021-09-07 19:52:13 +02:00
Applevangelist
a49f4eaa21 Merge pull request #1597 from Penecruz/Airboss-V/Stol
Airboss v/stol
2021-09-06 07:12:20 +02:00
Penecruz
e6e2651f8c Bug fix to AV-8B grading WIP 2021-09-06 11:08:29 +10:00
Penecruz
b93ba13644 bug fix to V/Stol groove. 2021-09-06 08:20:37 +10:00
Frank
e4a51951b0 Merge pull request #1596 from Penecruz/Airboss-V/Stol
Airboss v/stol
2021-09-04 15:20:03 +02:00
Penecruz
ad56e39942 Docs AV-8B clarifications 2021-09-04 13:40:10 +10:00
Penecruz
ea09dc5a6e Vstol groove timing 2021-09-04 11:25:44 +10:00
Penecruz
8ecfd913a3 Av-8B specific deviation counts adj. 2021-09-04 11:25:25 +10:00
Penecruz
53367c786e AV-8B LIG and Unicorn fix 2021-09-04 11:24:47 +10:00
Applevangelist
db5797bb4e Bug fixing 2021-09-02 18:48:40 +02:00
Applevangelist
5e8fe97752 CTLD Added method to inject troops into the field. 2021-09-01 13:34:13 +02:00
Applevangelist
393fa0bfbb MANTIS - Changes from the dev branch merged 2021-08-28 14:01:37 +02:00
Applevangelist
4f51884b9d SEAD - make padding a variable (radar switch-back-on time) 2021-08-28 14:01:37 +02:00
Frank
4c5c320073 Merge pull request #1594 from Penecruz/Pene-LHA-and-LHD-edits
Pene lha and lhd edits
2021-08-28 10:52:12 +02:00
Penecruz
9098590568 Sound Pack Gabriella add 2021-08-28 14:51:30 +10:00
Penecruz
555bb7e68b Update instructions for AV-8B Harrier 2021-08-28 14:27:32 +10:00
Penecruz
c0a18957f0 AoA for harrier and JC Spot 5 timings 2021-08-28 13:31:16 +10:00
Penecruz
2cf939560e Allow for JC Spot 5 Voice over 2021-08-28 11:02:05 +10:00
Penecruz
9d3a7aae78 Add Landing Spot 5 to JC 2021-08-28 10:26:49 +10:00
Applevangelist
f6ed592f92 CSAR - remove noise 2021-08-27 18:47:13 +02:00
Applevangelist
c98757d13c CTLD - added ENGINEERING 2021-08-27 18:46:59 +02:00
Applevangelist
17378f509e SEAD - Code cleanup and enabled delayed switch off 2021-08-27 18:46:45 +02:00
Applevangelist
7f18ea0e7a UNIT/GROUP - added function to get the skill of a unit. SEAD - added functionality to calculate time-2-impact of HARMS and adjust behaviour accordingly 2021-08-27 14:56:16 +02:00
Frank
5172619cb1 Merge pull request #1593 from Penecruz/Pene-LHA-and-LHD-edits
Pene-LHA-LHD-additions
2021-08-26 08:22:11 +02:00
Penecruz
3962529698 Update Airboss.lua 2021-08-26 09:33:40 +10:00
Penecruz
6481d5d41e Update Airboss.lua 2021-08-25 17:59:33 +10:00
Applevangelist
6cc3d73c04 Changed priority to show bomb target height in ft if no Player Settings 2021-08-22 12:02:52 +02:00
Applevangelist
e541e39403 Bug fixes. Added support for Ships as load zones 2021-08-22 12:02:26 +02:00
Applevangelist
c7ea45e5fd Clean up UTF-8 mess 2021-08-18 18:01:04 +02:00
Applevangelist
20f28b3d2c Fix for SAM pattern matching not working 2021-08-18 15:01:14 +02:00
Applevangelist
f3f63ab8aa Fix for degree sign extra char 2021-08-18 11:52:13 +02:00
Applevangelist
e91090cfff more corrections 2021-08-18 11:47:25 +02:00
Applevangelist
1a7fb3c13e Fix for degree sign extra char 2021-08-18 11:36:33 +02:00
Applevangelist
59857ed79d CSAR - added changes from Development for AFB landings and safer calculation of distances of lost pilots 2021-08-18 09:29:58 +02:00
Frank
4797665939 Update Range.lua
- Added demo by shagrat
2021-08-02 22:02:21 +02:00
Applevangelist
b89749036d Merge pull request #1584 from FlightControl-Master/Applevangelist-patch-3-1
Update Utils.lua
2021-08-02 19:04:57 +02:00
Applevangelist
c6268488de Update Utils.lua 2021-08-02 19:03:07 +02:00
Applevangelist
de04369703 Various for AI_CARGO, CTLD, CSAR, align with dev 2021-07-29 12:46:16 +02:00
Applevangelist
05b6f19a87 Merge pull request #1583 from FlightControl-Master/Applevangelist-patch-2
Update CSAR.lua
2021-07-28 19:47:48 +02:00
Applevangelist
2753df8216 Update CSAR.lua
Fix for CSAR message to all not working, added option to suppress all messaging, make destroys silent to not affect scoring
2021-07-28 19:45:47 +02:00
Applevangelist
33d761503d Fix for crates dropping is loosing track of troops 2021-07-25 13:26:07 +02:00
Applevangelist
b52272e18e Merge pull request #1581 from FlightControl-Master/Applevangelist-patch-1
Update ATIS.lua
2021-07-24 15:57:15 +02:00
Applevangelist
70fa1cf19f Update ATIS.lua
Correct degree output extra chars
2021-07-24 15:54:54 +02:00
Applevangelist
7ca7caea75 Updates with changes from develop 2021-07-24 15:50:10 +02:00
Applevangelist
277c26821e Updated according to develop version. Added troop extract 2021-07-18 14:54:37 +02:00
Applevangelist
22826b4cd1 Update CTLD.lua
Added correct Mi-8MT unit typename
2021-07-17 16:00:40 +02:00
Applevangelist
8cc1c24b64 Update CSAR.lua
Changes as per development version. Taking care of dead pilots correctly, added FSM event KIA
2021-07-17 15:58:11 +02:00
Applevangelist
0db35a0e9f Updated according to develop version 2021-07-16 14:00:21 +02:00
Applevangelist
b4707bb3eb Update Mantis.lua (#1568)
* Update Mantis.lua

Added state tracker so that Red/Green Events only get triggered when a state actually changes.

* Update Mantis.lua
2021-07-14 15:37:47 +02:00
Applevangelist
bbfc2e9ed7 Added beacon frequency and laser code generator functions 2021-07-14 08:44:55 +02:00
Applevangelist
2fb0ab1aed Reflect DEVELOP changes in MASTER 2021-07-14 08:41:14 +02:00
Applevangelist
6fbf050b72 Slightly updated versions as the new emissions on/off doesn't work really. 2021-07-12 18:16:00 +02:00
Applevangelist
29210f670c Added Fisher-Yeats table shuffle and Helicopter door check 2021-07-11 18:37:46 +02:00
Applevangelist
18b45c9621 Added CTLD and CSAR 2021-07-10 17:11:00 +02:00
Applevangelist
9356d67d74 Added CTLD and CSAR 2021-07-10 17:10:53 +02:00
Applevangelist
5bb2b05f71 Added CTLD and CSAR 2021-07-10 17:10:46 +02:00
Frank
89308f7d06 Update Airbase.lua
- Fixed Andersen
2021-07-06 21:49:51 +02:00
Frank
353d6dfec0 Update Airbase.lua
- Corrected Mariana Islands airbase names
- Fixed bug in Syria airbase name `["Thalah"]="Tha'lah"`
2021-07-02 08:51:51 +02:00
Celso Dantas
3f5e322948 Update Airboss debug msg with BRC/Final heading (#1559)
Including the BRC and Final Heading on the debug message is useful for squadrons relying on that msg to know what is the end heading of the carrier.

The carrier tends to turns a few times before ending up on the final heading as it adjust into its track. Thus, having the BRC/Final Heading on the last message "Starting aircraft recovery Case %d ops." is useful.
2021-06-26 13:33:51 +02:00
Frank
0e8732fd44 ATIS
- ATIS MSRS uses coalition of airbase
- update coalition if base was captured
2021-06-19 22:27:12 +02:00
Applevangelist
77a3c7369d Update Shorad.lua 2021-06-18 12:00:15 +02:00
Frank
8d8070bbd7 Update Globals.lua
Added CR at the end.
2021-06-13 10:59:46 +02:00
Frank
72df687ce7 Merge pull request #1549 from FlightControl-Master/FF/MasterDevel
Sound Update
2021-06-12 22:22:02 +02:00
Frank
246935622c Update ATIS.lua 2021-06-12 21:54:42 +02:00
Frank
078573629d SRS
- added coordinate
- added google
2021-06-11 23:41:37 +02:00
cammel tech
973b8323c9 Ratmanager documentation add :SetTspawn(dt) infite spawns - see discord func.rat discussion (#1548)
Co-authored-by: wob3155@posteo.de <wob3155@posteo.de>
2021-06-11 13:38:25 +02:00
Frank
6d61c5ee94 Update Utils.lua 2021-06-10 23:33:14 +02:00
Frank
3e8db6a1fa ATIS
Improved time TTS format
2021-06-10 23:23:16 +02:00
Applevangelist
832d6b1c08 Update Group.lua (#1546)
Added invisible and immortal commands on GROUP level.
2021-06-10 11:20:01 +02:00
Frank
e7936950f4 ATIS 2021-06-10 00:18:49 +02:00
Frank
831e986ee8 Merge branch 'master' into FF/MasterDevel 2021-06-09 13:18:46 +02:00
Frank
8a44fae3d4 Update SRS.lua
- VBS script
2021-06-09 13:01:48 +02:00
Frank
0c9390914a Update SRS.lua 2021-06-08 23:51:03 +02:00
Frank
4fa525a4ae Update Airbase.lua 2021-06-08 22:06:33 +02:00
Applevangelist
858b00336b Update Spawn.lua 2021-06-07 18:07:14 +02:00
Applevangelist
82d78c98bb Update Spawn.lua (#1544) 2021-06-07 15:16:04 +02:00
Applevangelist
06c3f7998b Added function for message duration (#1542)
... and correct flash status setting
2021-06-07 15:09:30 +02:00
Frank
e03e87f501 Sound update docs 2021-06-05 23:44:25 +02:00
Frank
cf83abfe90 Sound update docs 2021-06-05 23:40:50 +02:00
Frank
5a00f461e9 Sound update 2021-06-04 23:04:49 +02:00
Frank
bb43b08190 Sound update 2021-06-01 23:20:38 +02:00
Frank
05f95796f6 Sound update 2021-05-31 23:47:43 +02:00
Frank
cfcd7d7588 Sound update 2021-05-30 22:36:52 +02:00
Frank
4c3d44a63b Sound update 2021-05-30 01:36:22 +02:00
Frank
fcd75a34eb Merge branch 'master' into FF/MasterDevel 2021-05-28 23:00:50 +02:00
Frank
4827b73bb1 Sound update 2021-05-28 23:00:43 +02:00
Frank
2d7e7d55a9 Merge pull request #1540 from FlightControl-Master/Applevangelist-submarine
Update Controllable.lua
2021-05-28 22:29:59 +02:00
Frank
3129c8c8ea Sound update 4 2021-05-27 23:19:40 +02:00
Applevangelist
6e37300d9b Update Controllable.lua
Added function `IsSubmarine()`removed type from `OptionDisperseOnAttack()`
2021-05-26 08:44:30 +02:00
Frank
6b747e924b Sound update 3 2021-05-25 23:32:54 +02:00
Applevangelist
85fef96d00 ZONE_POLYGON_BASE:Boundary added (#1537)
* ZONE_POLYGON_BASE:Boundary added

ZONE_POLYGON_BASE:Boundary added

* Update Zone.lua

Change Radius default value
2021-05-24 10:00:16 +02:00
Frank
e55bb21401 Sound 2 2021-05-23 23:32:26 +02:00
Frank
7a3fb95851 Sound Update 1
- Moved all related files to new Moose folder "Sound/"
2021-05-22 23:26:18 +02:00
Frank
d39074bf3e Sound 2021-05-22 00:35:00 +02:00
Applevangelist
47d814e409 CONTROLLABLE:PatrolRouteRandom fix for Subs (#1536)
* CONTROLLABLE:PatrolRouteRandom fix for Subs

fixes issue #1535

* Update Controllable.lua

* Update Controllable.lua
2021-05-21 12:35:32 +02:00
Frank
41b01a508d Removed debug coordinate marks for quad zones 2021-05-14 21:57:19 +02:00
Frank
0410ae6877 Mariana Islands 2021-05-13 23:30:34 +02:00
Applevangelist
1ae41319fa Update README.md 2021-05-12 09:18:31 +02:00
Frank
0265152c12 Merge pull request #1534 from FlightControl-Master/FF/MasterDevel
ZONES and COORDINATES: New functions to draw on F10 map
2021-05-11 11:57:17 +02:00
Frank
a893d46cb9 Update Event.lua 2021-05-11 11:54:27 +02:00
Frank
b68f271fb7 Update Event.lua 2021-05-11 11:51:25 +02:00
Frank
057e231a9d Emission 2021-05-11 11:41:26 +02:00
Frank
b6fedbd97d Merge branch 'master' into FF/MasterDevel 2021-05-11 11:18:35 +02:00
Frank
7cd29501a9 Templates update 2021-05-11 00:21:50 +02:00
Frank
59e4f48726 COORDINATES and ZONES
- Quad zones defined in the ME are now registered as ZONE_POLYGON_BASE
- Added draw functions to COORDINATE (line, circle, arrow, rectangle, text)
2021-05-10 17:47:42 +02:00
Frank
20fe2ee505 Update Event.lua 2021-05-10 08:33:15 +02:00
Frank
8b4ba430fb Update Airbase.lua
- Fixed #1524
2021-05-07 21:55:56 +02:00
Frank
3b0949de72 Fixed Ground Speed Issues
COORDINATE
- Fixed issue with ground units not honoring set speed by setting `ETA_locked=false` in `COORDINATE:WaypointGround` function (used by other classes and routines in MOOSE). See #1484
- Also set `ETA_locked=false` for `COORINATE:WaypointAir` and `COORDINATE:WaypointNaval`
2021-05-07 21:46:03 +02:00
Frank
ebb4623bb5 Merge branch 'master' into FF/MasterDevel 2021-05-07 17:42:07 +02:00
Applevangelist
2309487429 Merge pull request #1531 from FlightControl-Master/AA-4
Update Shorad.lua
2021-05-03 17:32:40 +02:00
Applevangelist
a3decc33c9 Update Shorad.lua
Add option to defend a SET_STATIC - e.g. group of bunkers or other static objects.
2021-05-03 17:30:18 +02:00
Applevangelist
b2d87a8780 Update Set.lua
correct typo in function name
2021-05-03 09:27:56 +02:00
Applevangelist
5034005f6b slightly higher landing speed 2021-05-02 15:29:42 +02:00
Applevangelist
c045ba0c6d Update Sead.lua 2021-05-02 11:35:30 +02:00
Applevangelist
d0fb4d44ef Update Sead.lua
test GH Desktop
2021-05-02 11:28:50 +02:00
Frank
d1f3e3f4bb Update Templates.lua 2021-05-02 00:08:17 +02:00
Frank
45f578b5a3 Merge branch 'master' into FF/MasterDevel 2021-05-01 21:37:28 +02:00
Applevangelist
620f4b96b9 Merge pull request #1530 from FlightControl-Master/AA-4
Update Mantis.lua
2021-05-01 17:32:24 +02:00
Applevangelist
39d3f85b71 Update Mantis.lua
Adjustments for 2.7
2021-05-01 17:23:17 +02:00
Applevangelist
9a7df65faa Update Shorad.lua
Adjust to Event changes
2021-05-01 17:18:01 +02:00
Applevangelist
596eb4c7e4 Update Sead.lua
Adjust to Event changes
2021-05-01 17:13:07 +02:00
Frank
2bce305451 Merge branch 'master' into FF/MasterDevel 2021-05-01 17:07:56 +02:00
Applevangelist
2bcc531e73 Update Event.lua (#1526)
Updates for Event 33 and 6 - also makes Scoring work again
2021-05-01 14:26:03 +02:00
Frank
2e66a854b1 Events and Templates 2021-05-01 00:55:43 +02:00
Applevangelist
57f6096aa1 Update Controllable.lua (#1518)
Error line 1466
2021-04-25 20:24:41 +02:00
Frank
dc64b98c59 Update Act_Route.lua
- Fixed bug in ACT_GROUP:GetRouteText
2021-04-23 13:27:31 +02:00
Applevangelist
e6dde7335a Merge pull request #1517 from FlightControl-Master/aa
Update Controllable.lua
2021-04-23 12:02:59 +02:00
Applevangelist
bbb2174957 Update Controllable.lua
added `OptionDisperseOnAttack` for GROUND units
2021-04-23 12:00:49 +02:00
Applevangelist
b37b50a0c6 Merge pull request #1515 from FlightControl-Master/AA-3
Update Controllable.lua
2021-04-23 09:53:58 +02:00
Applevangelist
459ff8038b Update Controllable.lua
Some Docu updates
2021-04-23 09:51:58 +02:00
Applevangelist
f2da862442 Merge pull request #1514 from FlightControl-Master/aa-1
Update .gitignore
2021-04-22 12:47:37 +02:00
Applevangelist
9c6c2ec5ca Update .gitignore 2021-04-22 12:46:37 +02:00
Applevangelist
aa13583aa7 Update README.md
added badge
2021-04-22 12:39:35 +02:00
Applevangelist
002f1e09ef Merge pull request #1513 from FlightControl-Master/AA-2
Update Airboss.lua
2021-04-22 10:39:04 +02:00
Applevangelist
5091ed7d6d Update Airboss.lua 2021-04-22 10:26:02 +02:00
Applevangelist
2475f22937 Create .luacheckrc 2021-04-22 10:08:57 +02:00
Applevangelist
f4f12f786c Update Airboss.lua
Escape error
2021-04-21 18:52:44 +02:00
Applevangelist
eb659446df Merge pull request #1510 from FlightControl-Master/AA
Update DCS.lua
2021-04-21 15:50:26 +02:00
Applevangelist
62c42adebc Update DCS.lua 2021-04-21 15:48:54 +02:00
Applevangelist
0fc91595b8 Merge pull request #1508 from FlightControl-Master/Applevangelist-patch-2
Update Group.lua
2021-04-20 17:31:00 +02:00
Applevangelist
18fca6461b Update Group.lua 2021-04-20 17:27:48 +02:00
Applevangelist
869b819a4f Merge pull request #1506 from IdefixRC/master
Create Airbase.lua
2021-04-20 16:23:19 +02:00
IdefixRC
8ae6192a5f Create Airbase.lua
New Syria Airbases added to list: H4, Gaziantep, Rosh Pina, Sayqal, Shayrat, Tiyas, Tha'lah and Naqoura
2021-04-20 15:43:45 +08:00
Applevangelist
3b2af6ea07 Update appveyor.yml 2021-04-19 20:49:17 +02:00
Applevangelist
82da7a8f24 Update appveyor.yml 2021-04-19 20:47:16 +02:00
Applevangelist
3749686882 Update appveyor.yml 2021-04-19 20:46:30 +02:00
Applevangelist
b2eba4911a Update appveyor.yml 2021-04-19 20:44:50 +02:00
Applevangelist
40700a387c Update appveyor.yml 2021-04-19 20:42:24 +02:00
Applevangelist
0577940d58 Update appveyor.yml 2021-04-19 20:38:50 +02:00
Frank
1d6be07cff Update appveyor.yml 2021-04-18 22:59:20 +02:00
Frank
2d6545429a Update Unit.lua
- Appveyor test
2021-04-18 22:16:46 +02:00
Frank
9f095a1d28 Update appveyor.yml
- Token for my appveyor account.
2021-04-18 22:14:51 +02:00
Frank
91b6da70af Update Event.lua 2021-04-18 14:49:08 +02:00
Frank
235e40cb26 Merge pull request #1505 from Applevangelist/revert-1491-patch-83
Revert "Update Group.lua"
2021-04-18 14:38:07 +02:00
Frank
8ba3a30278 Update appveyor.yml 2021-04-18 14:34:36 +02:00
Applevangelist
3675ddaf04 Revert "Update Group.lua" 2021-04-18 12:09:37 +02:00
Frank
62a59e7b2a Merge pull request #1503 from FlightControl-Master/FF/MasterDevel
Update ATIS.lua
2021-04-17 23:38:25 +02:00
Frank
1f949463aa Update ATIS.lua 2021-04-17 23:36:49 +02:00
Frank
6d53a30b1f Update appveyor.yml 2021-04-17 23:36:14 +02:00
Frank
5aeb480069 Update ATIS.lua 2021-04-17 23:29:53 +02:00
Frank
4b6aa3b56f Update appveyor.yml 2021-04-17 23:29:31 +02:00
Frank
4209569f29 Merge pull request #1502 from FlightControl-Master/FF/MasterDevel
Update Unit.lua
2021-04-17 23:05:42 +02:00
Frank
7106efbc04 Update Unit.lua 2021-04-17 22:56:54 +02:00
Frank
4e878e49a7 Merge pull request #1501 from FlightControl-Master/FF/MasterDevel
Update Unit.lua
2021-04-17 22:20:32 +02:00
Frank
3e470e557d Update Unit.lua 2021-04-17 22:19:27 +02:00
Frank
5f567fcf66 Merge pull request #1500 from FlightControl-Master/FF/MasterDevel
Ff/master devel
2021-04-17 21:46:46 +02:00
Frank
2289e16d91 Update Unit.lua 2021-04-17 21:45:17 +02:00
Frank
40415ef933 Update Unit.lua 2021-04-17 21:31:55 +02:00
Frank
6519c30b3a Update Unit.lua 2021-04-17 21:26:26 +02:00
Frank
42d253a9ca Update Unit.lua 2021-04-17 21:22:09 +02:00
Frank
38b40e2931 Update Unit.lua 2021-04-17 21:16:34 +02:00
Frank
f7a7ea14a3 Merge pull request #1495 from FlightControl-Master/FF/MasterDevel
Update Unit.lua
2021-04-17 19:49:38 +02:00
Frank
41e0f92cc0 Update Unit.lua 2021-04-17 19:49:04 +02:00
Frank
2c5e9789db Merge pull request #1494 from FlightControl-Master/FF/MasterDevel
Update ATIS.lua
2021-04-17 10:29:19 +02:00
Frank
e198aae1dd Update ATIS.lua 2021-04-17 10:28:18 +02:00
Frank
d040cc0d03 DCS 2.7
- Added COORDINATE:LineToAll and COORDINATE:CircleToAll
- Added altitude parameter to CONTROLLABLE:TaskFireAtPoint
- Added emission on/off functions to UNIT
2021-04-16 23:05:02 +02:00
Pikes
f078e94a7a Merge pull request #1493 from Applevangelist/patch-86
Update DCS.lua
2021-04-16 17:57:05 +01:00
Pikes
9973099f57 Merge pull request #1492 from Applevangelist/patch-85
Update Unit.lua
2021-04-16 17:56:50 +01:00
Pikes
9ca7785a64 Merge pull request #1491 from Applevangelist/patch-83
Update Group.lua
2021-04-16 17:56:33 +01:00
Applevangelist
e7d5fd6c1b Update DCS.lua
enableEmissions
2021-04-16 12:46:45 +02:00
Applevangelist
9eed35ace7 Update Unit.lua
Added changes for EnableEmission
2021-04-16 12:40:32 +02:00
Applevangelist
c7ec1b4e5e Update Group.lua
Added code for EnableEmission
2021-04-16 12:37:35 +02:00
Frank
34ffebb46a Merge pull request #1489 from Applevangelist/patch-81
Events - Workaround for 2.7
2021-04-15 14:53:30 +02:00
Applevangelist
e8a2167934 Events - Workaround for 2.7
Same as for Event 31
2021-04-15 13:01:02 +02:00
Frank
fa342a5b6b ATIS v0.9.1
- DCS 2.7 weather presetss
2021-04-14 23:42:36 +02:00
Frank
4ebcaf913c Merge pull request #1488 from Applevangelist/patch-80
Update Shorad.lua
2021-04-07 20:52:29 +02:00
Frank
7a4c9806a4 Merge pull request #1487 from Applevangelist/patch-79
Update Mantis.lua
2021-04-07 20:52:16 +02:00
Applevangelist
bddc1d7fdc Update Shorad.lua
Added option to use AI on/off instead of changing the alert state
2021-04-07 19:32:34 +02:00
Applevangelist
cb813b70e1 Update Mantis.lua
Added option to use AI on/off instead of changing the alert state
2021-04-07 19:31:10 +02:00
Frank
f161f08fc1 Update Event.lua
- Fixed typo
2021-03-29 19:55:11 +02:00
Frank
3e22411328 Events
- Added new DCS events
2021-03-29 18:01:42 +02:00
Frank
b00778bb86 Update AI_A2A_Cap.lua
Fixes #1474
2021-03-29 09:53:30 +02:00
Frank
564f95781e Update Positionable.lua
- Added cargo bay limits for ground (by kappa) and naval units.
2021-03-28 20:29:15 +02:00
Frank
6c2f8aa6ff Merge pull request #1477 from Applevangelist/patch-73
Utils - added Convert knots to alitude corrected KIAS
2021-03-22 12:04:24 +01:00
Applevangelist
2e342e4341 Utils - added Convert knots to alitude corrected KIAS
Added - Convert knots to alitude corrected KIAS, e.g. for tankers.
2021-03-22 11:47:39 +01:00
Frank
3c6089884e Update Airboss.lua
- Added MP wire correction function.
2021-03-02 21:19:30 +01:00
Frank
884ea866e2 AIRBOSS v1.1.6
Adjusted and (hopefully improved) parameters.
- Angled deck 9.0° --> 9.1359° (from DCS config file)
USS Stennis:
- Deck height 19.00 --> 19.06 meters (from DCS config file)
Supercarriers:
- Deck height 20.0 -->20.1394 meters (from DCS config file)
- stern coordinate 1.0 meters more on starboard side (LUR calls).
2021-02-28 23:25:32 +01:00
Frank
1af1ada7dc Merge pull request #1463 from Applevangelist/patch-65
Update Mantis.lua
2021-02-24 13:46:24 +01:00
Applevangelist
c72c3fd091 Update Mantis.lua
Added a smarter switch-on distance for SHORAD, added user function to set AWACS detection range, change default SAM firing range to 95%
2021-02-24 12:55:54 +01:00
Frank
0848718313 Merge pull request #1462 from Applevangelist/patch-64
Error in NewFromLLDD - altitude isn't set if given
2021-02-23 10:37:25 +01:00
Applevangelist
832abd7860 Error in NewFromLLDD - altitude isn't set if given
Thanks to tomekldc
2021-02-23 10:34:55 +01:00
Frank
12e745af54 Merge pull request #1460 from Applevangelist/patch-62
Update AI_Cargo_Helicopter.lua
2021-02-19 14:41:37 +01:00
Applevangelist
f18c818b8e Update AI_Cargo_Helicopter.lua 2021-02-19 10:04:30 +01:00
Applevangelist
4deca4f816 Update AI_Cargo_Helicopter.lua
Helicopters don't drive, they fly... ;)
2021-02-19 09:59:12 +01:00
Frank
b023ecfb99 Merge pull request #1459 from Applevangelist/patch-61
Update AI_Cargo_Helicopter.lua
2021-02-18 17:21:28 +01:00
Applevangelist
cdb491bb5f Update AI_Cargo_Helicopter.lua
Added docu on Pseudo Function, make Home() function clear and Height useful
2021-02-18 17:05:36 +01:00
Frank
3ac47c967e Merge pull request #1458 from Applevangelist/patch-60
Update Shorad.lua
2021-02-17 15:44:47 +01:00
Frank
e577062720 Merge pull request #1457 from Applevangelist/patch-59
Update Mantis.lua
2021-02-17 15:44:34 +01:00
Applevangelist
95dc7ad2b4 Update Shorad.lua
code example added
2021-02-17 14:04:59 +01:00
Applevangelist
fccf58cd6c Update Mantis.lua
added code examples
2021-02-17 14:02:37 +01:00
Frank
6791e4d4ba Update Set.lua
- Improved docs for `:FilterPrefixes()`
2021-02-17 13:11:22 +01:00
Frank
11f8ac4820 Merge pull request #1456 from Applevangelist/patch-58
Update Mantis.lua
2021-02-17 11:00:43 +01:00
Frank
8f2672f928 Merge pull request #1455 from Applevangelist/patch-57
Update Shorad.lua
2021-02-17 11:00:33 +01:00
Frank
fb66d74ab7 ENUMS
- ENUMS: Added Phonetic and ISOLang by A101Wayz

- Fixed CALLSIGN.JTAC.Warrior (was CALLSIGN.JTAC.Warrier)
2021-02-17 10:26:13 +01:00
Applevangelist
37c061bcbd Update Mantis.lua
Nicefy doc output, Shorad link doc
2021-02-17 09:14:56 +01:00
Applevangelist
9491b18ff4 Update Shorad.lua
Own picture and quote, cleanup docs, link missions, less overhead by avoiding checks on friendly fire.
2021-02-17 09:13:35 +01:00
Frank
8cff2eaac0 Merge pull request #1454 from Applevangelist/patch-56
Update Moose.files
2021-02-16 17:56:30 +01:00
Frank
f64550f7c8 Merge pull request #1453 from Applevangelist/patch-55
Include Shorad.lua
2021-02-16 17:56:18 +01:00
Frank
0addafa627 Merge pull request #1452 from Applevangelist/patch-54
Shorad.lua - Initial release
2021-02-16 17:56:06 +01:00
Frank
b643d73f7f Merge pull request #1451 from Applevangelist/patch-53
Update Sead.lua
2021-02-16 17:55:53 +01:00
Applevangelist
798c5b292a Update Moose.files 2021-02-16 16:30:46 +01:00
Applevangelist
688c57d91d Update Moose.files 2021-02-16 16:29:13 +01:00
Applevangelist
d4c8d5d91b Include Shorad.lua 2021-02-16 16:26:25 +01:00
Applevangelist
154cc9fbeb Shorad.lua - Initial release
:)
2021-02-16 16:25:22 +01:00
Frank
327bac3058 Merge pull request #1450 from Applevangelist/patch-52
Integrate option for Shorad in Mantis
2021-02-16 16:23:14 +01:00
Applevangelist
c0e58e9c92 Update Sead.lua
Handle new shortened weapon names in DCS, make handling more graceful
2021-02-16 16:14:53 +01:00
Applevangelist
e8bfab515c Integrate option for Shorad in Mantis
Also, correct some documentation errors
2021-02-16 16:13:10 +01:00
Frank
695f204493 Merge pull request #1442 from Applevangelist/patch-49
Update Task_A2G_Dispatcher.lua
2021-01-28 15:47:22 +01:00
Frank
a7202ccd0d Merge pull request #1443 from Applevangelist/patch-50
Update Designate.lua
2021-01-28 12:24:46 +01:00
Applevangelist
c71c6c5178 Update Designate.lua
Use of added injection of Task Name into DetectedItem; useful when using also Designate with same Detection Object to ensure menu entries are harmonized.
2021-01-28 10:10:39 +01:00
Applevangelist
155b06edc8 Update Task_A2G_Dispatcher.lua
Added injection of Task Name into DetectedItem; useful when using also Designate with same Detection Object to ensure menu entries are harmonized.
2021-01-28 10:04:00 +01:00
Frank
64b8f2fb46 Merge pull request #1441 from Applevangelist/patch-48
Update Mantis.lua
2021-01-26 12:39:14 +01:00
Applevangelist
7a07e15032 Update Mantis.lua 2021-01-26 11:06:08 +01:00
Frank
3d74312d43 Merge pull request #1440 from Applevangelist/patch-47
Update Mantis.lua
2021-01-26 09:27:42 +01:00
Applevangelist
6baf85d429 Update Mantis.lua
Some Doc corrections
2021-01-26 09:26:39 +01:00
Frank
311ab19389 Merge pull request #1438 from Applevangelist/patch-46
Update Set.lua
2021-01-22 23:29:38 +01:00
Applevangelist
e9f92d2250 Update Set.lua
Break loop after x tries
2021-01-22 18:34:05 +01:00
Frank
d469374ed5 Merge pull request #1436 from Applevangelist/patch-44
ATIS.lua update time calculation over midnight (logic error)
2021-01-21 17:11:07 +01:00
Applevangelist
a6ff84c09a ATIS.lua update time calculation over midnight (logic error)
Actually line 1322 + time here, sind it's negative ... :)
2021-01-21 15:34:50 +01:00
Frank
3ed8ca63a8 Merge pull request #1435 from Applevangelist/patch-43
Update ATIS.lua
2021-01-20 11:16:54 +01:00
Applevangelist
89aa08829e Update ATIS.lua
Fix time to be negative if early AM when using time to ZULU or time to GMT difference
2021-01-20 11:05:14 +01:00
Frank
5146106885 Update Controllable.lua 2021-01-19 16:56:26 +01:00
Frank
f05fc956db Merge pull request #1434 from Applevangelist/patch-42
Update AI_A2G_Dispatcher.lua
2021-01-16 15:12:46 +01:00
Applevangelist
d10adb5de8 Update AI_A2G_Dispatcher.lua
Corrected Docu line 434
2021-01-16 14:50:36 +01:00
Frank
45aac21b63 Merge pull request #1430 from Applevangelist/patch-40
Update AI_A2A_Dispatcher.lua
2021-01-13 10:20:22 +01:00
Frank
b52643bb36 Merge pull request #1431 from Applevangelist/patch-41
Update Controllable.lua
2021-01-13 10:20:05 +01:00
Frank
036010c39d Merge pull request #1429 from Applevangelist/patch-39
MANTIS 0.3.6
2021-01-13 10:19:17 +01:00
Applevangelist
9440d2b2c3 Update Controllable.lua
missing # in docu
2021-01-13 08:47:20 +01:00
Applevangelist
29727ec9a6 Update AI_A2A_Dispatcher.lua
Closed gap in Docu
2021-01-13 08:44:10 +01:00
Applevangelist
eb0b43cea8 MANTIS 0.3.6
-- clean up docs
-- added a function to set a new SAM range whilst running
2021-01-13 08:32:45 +01:00
Frank
a6e16fb7ad Update Airbase.lua
Fixed bug in white list
2021-01-12 17:40:21 +01:00
Frank
616b80710a Merge pull request #1428 from FlightControl-Master/FF/MasterDevel
AIRBASE
2021-01-12 13:50:27 +01:00
Frank
94f206af68 AIRBASE
- Added black and white parking spot lists.
2021-01-12 13:49:35 +01:00
Frank
ae6613fae1 CARGO 2021-01-12 00:42:00 +01:00
Frank
b61cfc7390 Update Set.lua 2021-01-09 23:47:45 +01:00
Frank
e2c5688670 Merge pull request #1425 from Applevangelist/patch-37
Update AI_Cargo.lua
2021-01-05 21:18:38 +01:00
Frank
1d016d9fce Merge pull request #1424 from Applevangelist/patch-36
Update AI_Cargo_Helicopter.lua
2021-01-05 21:18:18 +01:00
Applevangelist
cbc5b5bb89 Update AI_Cargo.lua
Fix for another dead end
2021-01-05 11:27:37 +01:00
Applevangelist
218c7736e0 Update AI_Cargo_Helicopter.lua 2021-01-05 11:25:03 +01:00
Applevangelist
8e46e41b34 Update AI_Cargo_Helicopter.lua
Fix for missed FSM call
2021-01-05 09:54:10 +01:00
Frank
bcf639d076 Merge pull request #1423 from Applevangelist/patch-35
Update AI_A2A_Dispatcher.lua
2021-01-03 16:50:07 +01:00
Applevangelist
697d12aefc Update AI_A2A_Dispatcher.lua
#1422
2021-01-03 16:31:07 +01:00
Frank
89afd2b4c0 Merge pull request #1421 from FlightControl-Master/FF/MasterDevel
Events and Database
2021-01-03 00:10:00 +01:00
Frank
3358f98bc4 Some stuff 2021-01-02 23:53:36 +01:00
Frank
417af6a93c Client 2021-01-02 21:45:19 +01:00
Frank
9a2f6c7faa Merge pull request #1420 from Applevangelist/patch-34
Update AI_Cargo_Helicopter.lua
2021-01-02 19:47:06 +01:00
Frank
e52b0edc4e Merge pull request #1419 from Applevangelist/patch-33
Update AI_Cargo.lua
2021-01-02 19:46:10 +01:00
Applevangelist
c51a65f058 Update AI_Cargo_Helicopter.lua
Removed unnecessary FSM transitions, added height to Waypoints to avoid (blue) Helis to creep over the ground
2021-01-02 18:56:27 +01:00
Applevangelist
5b2e67df19 Update AI_Cargo.lua
Some small additions from learnings from AI CARGO HELI
2021-01-02 18:53:58 +01:00
Frank
7b7605e501 Client 2021-01-02 01:21:45 +01:00
Frank
49d7d9ee2e Merge pull request #1417 from Applevangelist/patch-32
Update AI_Cargo_Helicopter.lua
2021-01-01 17:27:28 +01:00
Applevangelist
774c8971c8 Update AI_Cargo_Helicopter.lua
Fixed FSM dead ends
2021-01-01 17:24:00 +01:00
Frank
545c0886fa Merge pull request #1416 from Applevangelist/patch-31
Update AI_Cargo_Helicopter.lua - MI-26 > 5m
2021-01-01 16:39:31 +01:00
Applevangelist
721d027fb3 Update AI_Cargo_Helicopter.lua
MI-26 is slightly over 5m blocking boarding of Cargo
2021-01-01 16:37:55 +01:00
Frank
5185ea35fe Merge pull request #1415 from acrojason/naval-cargo
Multiple ship types, set default cargobay limit, fixed doc bug
2021-01-01 02:21:41 +01:00
acrojason
ec039f2999 Multiple ship types, set default cargobay limit, fixed doc bug 2020-12-31 17:04:21 -08:00
Frank
c0fc649f8b Client 2021-01-01 01:46:14 +01:00
Frank
c49497e68c Merge branch 'master' into FF/MasterDevel 2020-12-31 21:39:37 +01:00
Frank
57bc6de12b Merge pull request #1414 from acrojason/naval-cargo
Naval cargo
2020-12-31 20:38:30 +01:00
acrojason
1b80d68f50 Add support for naval logistics 2020-12-31 10:15:32 -08:00
Frank
787151597c Client 2020-12-31 17:17:15 +01:00
Frank
f725039da5 Client and Event updates 2020-12-31 00:51:54 +01:00
Frank
a4f15a0830 Merge branch 'master' into FF/MasterDevel 2020-12-30 12:43:31 +01:00
Frank
084f00afad Update SpawnStatic.lua
- fixed static spawns
2020-12-29 23:38:27 +01:00
Frank
87f4ebee64 Clients 2020-12-29 01:10:40 +01:00
Frank
a0276821d9 Merge pull request #1412 from FlightControl-Master/FF/MasterDevel
Arty v1.20
2020-12-27 22:39:56 +01:00
Frank
01a5b523da Arty v1.2.0
- fixed cargo issue
2020-12-27 17:59:39 +01:00
Frank
13477e17ec Merge pull request #1411 from Applevangelist/patch-30
Mantis.lua 0.3.5
2020-12-27 17:54:59 +01:00
Applevangelist
c32cec4c5b Mantis.lua 0.3.5
Added option to use Awacs separate from EWR network with own detection range
Extended documentation
Added refresher for SAM table
2020-12-27 16:24:13 +01:00
Frank
e08df3f9ce Update Timer.lua 2020-12-23 16:09:49 +01:00
Frank
f958130b92 Merge pull request #1408 from Applevangelist/patch-28
Mantis 0.3.0 - added advanced mode options
2020-12-23 10:53:27 +01:00
Applevangelist
9a19dd254e Mantis 0.3.0 - added advanced mode options
Mantis 0.3.0 - added advanced mode options
2020-12-22 15:19:29 +01:00
Frank
4869ae4baa cargo 2020-12-21 22:52:51 +01:00
Frank
1fd41eaa55 Merge pull request #1405 from Applevangelist/patch-25
Update Sead.lua to 0.2.2
2020-12-21 15:13:40 +01:00
Frank
25a7260d7f Merge pull request #1404 from Applevangelist/patch-24
Update to 0.2.6
2020-12-21 15:13:13 +01:00
Applevangelist
478934ddd1 Update Sead.lua to 0.2.2
-- corrected error when a single string instead of a table is given to SEAD:New()
-- added a function to extend the watched set of SAMs
2020-12-21 10:31:06 +01:00
Applevangelist
3d7234de19 Update to 0.2.6
Corrected error in dupe function naming, added doc about default values
2020-12-21 10:02:41 +01:00
Frank
fed1fb2839 Update Artillery.lua
- fixed bug in :NewFromCargo
2020-12-20 23:48:13 +01:00
Frank
08ba001b45 Update AI_Air_Engage.lua
Issue #1403
2020-12-19 01:18:01 +01:00
Frank
ce5aaf0b48 Update Airbase.lua
- Changed Persion Gulf Airbase Names
2020-12-18 13:52:08 +01:00
Frank
ee6954a4b9 Update AI_Air_Engage.lua
- Changed :Count to :CountAlive for AttackSetUnit. Issue #1393
2020-12-17 13:54:59 +01:00
Frank
6b6e845a18 Merge pull request #1402 from Applevangelist/patch-23
Update to Mantis 0.2.5
2020-12-17 12:20:07 +01:00
Applevangelist
c01b94518f Update to Mantis 0.2.5
Changelog
-- changed HQ to be a GROUP object instead of creating a CommandCenter. Allows user to use their own CC.
-- added functionality to autorelocate HQ and EWR group(s) at random intervals between 30 and 60 mins
-- added verbosity option for logging
-- tidied up documentation
2020-12-17 11:16:30 +01:00
Frank
9cf5f74e5e Merge pull request #1401 from Applevangelist/patch-22
Add new Routing Function to Controllable.lua
2020-12-16 11:31:14 +01:00
Applevangelist
4fda0414a0 Add new Routing Function to Controllable.lua
--- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving.
-- @param #CONTROLLABLE self
-- @param  #number speed Speed of the controllable, default 20
-- @param  #number radius Radius of the relocation zone, default 500
-- @param  #boolean onroad If true, route on road (less problems with AI way finding), default true
-- @param  #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
2020-12-16 10:10:46 +01:00
Frank
effa4af3a1 Merge pull request #1400 from Applevangelist/patch-21
Update Mantis.lua
2020-12-15 13:00:50 +01:00
Applevangelist
80b7e49eed Update Mantis.lua
Added picture link, cleaned up unnecessary globals
2020-12-15 11:59:10 +01:00
Frank
63748c3841 Merge pull request #1397 from Applevangelist/patch-19
Update Modules.lua
2020-12-14 13:01:38 +01:00
Frank
1c68b66047 Merge pull request #1396 from Applevangelist/patch-18
Create Mantis.lua
2020-12-14 12:51:04 +01:00
Frank
6c9dce70e7 SPAWNSTATIC
- Added new way to spawn FARPs
- Added FARP callsign enum.
2020-12-14 12:36:02 +01:00
Applevangelist
5515aa43d6 Update Modules.lua
added Mantis.lua to Functional
2020-12-14 10:42:11 +01:00
Frank
47c461e504 SPAWNSTATIC
- Attempt to SPAWN FARPS. Does not work yet.
2020-12-14 00:08:30 +01:00
Applevangelist
65174ceaae Create Mantis.lua
New Mantis extended Air-Defense Class
2020-12-13 16:06:57 +01:00
Frank
2500cfb3c7 RANGE v2.3.0
- Performance optimizations
2020-12-03 23:17:40 +01:00
Frank
7e673271ee Merge pull request #1391 from Applevangelist/patch-16
Issue ##1383 never ending flash messages
2020-12-02 21:26:31 +01:00
Applevangelist
e165cb156c Update Task.lua
-- stop message flashing, if any #1383 & #1312
2020-12-02 17:25:57 +01:00
Applevangelist
82bfb0cc55 Issue ##1383 never ending flash messages
Messages appears once and then goes away.
2020-12-01 19:47:29 +01:00
Frank
b0fe0663f1 Merge pull request #1389 from Applevangelist/patch-14
Taskinfo.lua - Issue #1388 - don't just assume this is a string
2020-12-01 18:07:40 +01:00
Applevangelist
1bd5193786 Taskinfo.lua - Issue #1388 - don't just assume this is a string
fixes the catchall at the end in case a task is unassigned - sometimes it's not a string. #1388
2020-12-01 16:10:55 +01:00
Frank
64c3fb985a Merge pull request #1386 from Applevangelist/patch-12
Update Task_A2G_Dispatcher.lua
2020-11-26 17:21:19 +01:00
Frank
1536c94319 Merge pull request #1387 from Applevangelist/patch-13
Update Task_A2A_Dispatcher.lua
2020-11-26 17:20:57 +01:00
Applevangelist
e7efd89d7a Update Task_A2A_Dispatcher.lua
Added user function to change flash message behaviour
2020-11-26 10:30:07 +01:00
Applevangelist
c486167b01 Update Task_A2G_Dispatcher.lua
Option to suppress flashing messages
2020-11-26 10:28:51 +01:00
Frank
e5201f3363 Merge pull request #1384 from Applevangelist/patch-11
Update AI_Air.lua
2020-11-25 22:56:39 +01:00
Applevangelist
ff4927dbb7 Update AI_Air.lua 2020-11-25 12:38:31 +01:00
Applevangelist
5842562ce1 Update AI_Air.lua
Resolves AI reeping low back to base, and possible also issue #1305
2020-11-24 16:23:22 +01:00
Frank
5f545fd3c5 Merge pull request #1382 from Applevangelist/patch-10
Update Sead.lua
2020-11-23 13:37:05 +01:00
Applevangelist
aab2f20280 Update Sead.lua
Feature Request #1355
Added a couple of new missiles, SD-10 and AGM84
Cleaned up the logic
2020-11-23 12:32:00 +01:00
Frank
76ac68ce51 Merge pull request #1381 from Applevangelist/patch-9
Update Controllable.lua
2020-11-22 20:55:59 +01:00
Applevangelist
f034e3680c Update Controllable.lua 2020-11-22 17:39:56 +01:00
Applevangelist
e5c57269bb Update Controllable.lua
Added option OptionAAAttackRange and OptionEngageRange
2020-11-22 17:24:56 +01:00
Frank
ab5167717d Update Airboss.lua
- Added F-14A-135-GR by Heatblur to the list of supported aircraft.
2020-11-20 10:17:45 +01:00
Frank
176abf74a1 Merge pull request #1371 from Applevangelist/patch-2
Update Task_A2A_Dispatcher.lua
2020-11-20 09:49:17 +01:00
Frank
da4f13d0d7 Merge pull request #1374 from Applevangelist/patch-5
Update Task_Capture_Dispatcher.lua
2020-11-20 09:15:30 +01:00
Applevangelist
7ddec7609a Update Task_Capture_Dispatcher.lua
Last annoying flash gordon
2020-11-19 15:59:35 +01:00
Applevangelist
53c3996aef Update Task_A2A_Dispatcher.lua
Add boolean in new to suppress "... has new tasks..." flashes
2020-11-19 15:51:09 +01:00
Frank
bf61f22dd1 Fixed UnHandleEvent for UNIT
#1365
2020-11-15 23:00:03 +01:00
Frank
5e8a36802c Github issues
#1360
#1361
2020-10-31 21:21:25 +01:00
Frank
b34b537a26 Update AI_Formation.lua
- Issue #1358
2020-10-28 09:46:03 +01:00
Frank
812fb99e66 Small Updates
AIRBOSS
- Added USS Harry S. Truman (CVN-75) [Super Carrier Module]
- Fixed little bug in stop time

RADIOQUEUE
- Allowing GROUND units as relay. Only ships cannot be used.
2020-10-26 16:16:04 +01:00
Frank
9cb3111dd5 Github issues addressed
- Fixes #1342
- Fixes #1333
- Fixes #1331
2020-10-26 09:13:49 +01:00
Frank
1312555690 Github Issues Addressed
**GROUP**
#1354
**ARTY v1.1.8**
#1356
#1357
2020-10-20 23:26:32 +02:00
Frank
aa4f8452fa Update Airbase.lua
- Fixed a bug inthe `AIRBASE:GetRunwayData()` function.
2020-10-03 23:11:05 +02:00
Frank
e2c1097ec5 Update Airboss.lua
- Fix for DCS bug that skill is not set in env.mission. Should not happen in current DCS version anyway.
2020-10-03 21:41:35 +02:00
acrojason
4b8b13dd68 Initial changes to support Naval Cargo 2020-09-23 09:40:42 -07:00
Frank
59f795fc17 Merge pull request #1349 from FlightControl-Master/FF/MasterDevel
RANGE, TIMER & Minor
2020-09-02 00:08:36 +02:00
Frank
26a935c29c RANGE etc
RANGE v2.2.3
- Added relay unit option.

TIMER
- Fixed bug.

Minor other stuff
2020-09-01 17:30:26 +02:00
Frank
848336f8ac Merge pull request #1348 from FlightControl-Master/FF/MasterDevel
Updates, fixes and new classes
2020-08-29 22:15:14 +02:00
Frank
8e6e9cbb4d Updated Moose Modules.lua
- Added new class files Profiler, Timer, Marker
2020-08-29 22:10:45 +02:00
Frank
15cb9bec40 Updates, new classes and fixes
**AI_FORMATION**
- Performance improvents. This also affects the **RESCUEHELO** class.

**EVENT**
- Added events when object is of category BASE.

**FSM**
- Removed a few tracing calls.
- Updated docs.

**POINT**
- Added Update Vec functions to COORDINATE.
- Added *overwrite* option to COORDINATE:Translate() function.
- Removed second COORDINATE:Translate() function.
- Optimized COORDINATE:GetClosestAirbase() function.
- Added *Offset* parameter to COORDINATE:IsLOS() function.

**RADIO**
- Updated BEACON type and system enums.

**RADIOQUEUE**
- Use Vec3 instead of COORDINATE. Performance improvement.

**SET**
- Added some functions.

**TIMER**
- Added new class. Little sister of SCHEDULER class.

**DCS**
- Minor changes regarding docs.

**ATIS**
- Added "Miles.ogg", "StatuteMiles.ogg", "Zulu.ogg".

**ENUMS**
- Added ENUMS.Formation.Vehicle
- Added ENUMS.AlarmState

** PROFILER**
- Added new lua profiler.

**UTILS**
- Minor changes and additions.

**AIRBASE**
- Improved Registration.
- Improved Parking spot handling.
- Aded :IsAirdrome(), IsHelipad(), IsShip() functions.
- Improved :GetRunwayData() for Syria airports.

**CONTROLLABLE**
- Fixed bug in :CommandSetFrequency() fuction (Hz vs. MHz).
- Updated/fixed :TaskFAC_AttackGroup() function.

**GROUP**
- Added :GetThreatLevel() function.
- Added :IsInZone() function to check if any unit is in the zone.

**MARKER**
- Added new class handling F10 markers using FSM.
2020-08-29 21:55:59 +02:00
Frank
38f5fd8249 SET_STATIC
- Fixed #1345
2020-08-19 23:10:20 +02:00
Frank
6a04f83280 Syria
ATIS
- Added runway magnetic to true conversion. Channel map -10°, Syria +5°.
- ICAOP phraseology The Channel and Syria true.

UTILS
- Added DCSMAP.Syria="Syria"
- Magnetic declination for Syria set to 5°
- GMT to local time for Syria is GMT+3.

AIRBASE
- Added AIRBASE.Syria enums.
2020-08-19 23:04:38 +02:00
Frank
ec4d12a0d5 Merge pull request #1338 from RISCfuture/improve-atis
Modify ATIS format to more closely match real world
2020-08-19 22:28:31 +02:00
Frank
999aba5d77 Update README.md 2020-07-19 21:15:18 +02:00
Tim Morgan
652f29b782 Use SM for visibility, not NM 2020-07-11 13:15:50 -07:00
Tim Morgan
0c6aae45c1 Do not report visibilities > 10 NM or 9999 m 2020-07-11 13:15:50 -07:00
Tim Morgan
25b8cff442 Don't use ICAO phraseology for US airports 2020-07-11 13:03:41 -07:00
Tim Morgan
9f7471178b Add option to suppress reporting QFE 2020-07-11 13:03:41 -07:00
Tim Morgan
f3f3406d64 Reorder ATIS components to match real-life ATIS, add option to suppress all but Zulu times 2020-07-11 13:03:40 -07:00
Tim Morgan
4eb886d0f3 Typo fix 2020-07-11 13:03:40 -07:00
Frank
5c6e50e7f9 Update SpawnStatic.lua
- Fixed #1335
2020-06-11 23:44:19 +02:00
acrojason
18fd9cdc3d Initial AI_Cargo_Dispatcher_Ship 2020-06-04 21:09:35 -07:00
Frank
fb3115a0f1 The Channel Update
**AIRBASE**
- Added "The Channel" map airbase name enumerators.

**ATIS v0.8.0**
- Added sunset and sunrise times (required new sound files).
- Supports "The Channel" map (needs new sound files).

**UTILS**
- Corrected NTTR local time diff to GMT-8 (was -7) hours.
- Corrected Normany local time diff to GMT+0 (was -1) hours.
- Added "The Channel" map local time diff as GMT+2 (should be GMT).
- Fixed bug in UTILS.GMTToLocalTimeDifference() function.
2020-06-04 23:46:26 +02:00
acrojason
fdcdf52d9a Initial creation of AI_Cargo_Ship 2020-06-04 08:46:30 -07:00
acrojason
5dcefff28d Updated Warehouse to accommodate Naval transport 2020-06-04 08:29:01 -07:00
Frank
833d4f7b65 SPAWNSTATIC class reworked
**SPAWNSTATIC**
- Added option to link statics to units, e.g. carriers.
- Fixed SpawnFromType function.
- Added :InitXYZ functions to set parameters.
- Removed ReSpawn functions. Pointless here. Use Respawn of STATIC class instead.
- Updated docs.

**COORDINATE**
- Added optional parameter to IsDay() and IsNight() functions to check on specific time.

**STATIC**
- Fixed Respawn functions so that statics do not appear on top of each other.

**UTILS**
- Added optional parameter to GetMissionDay and GetMissionDayOfYear functions.

**AIRBOSS**
- Adjusted recovery turn time interval back to 5 min in :SetRecoveryTurnTime() function.
2020-05-27 22:18:18 +02:00
Frank
61bb59d8b3 Astro Update
**COORDINATE**
* Added functions to get sunrise and sunset times.

**UTILS**
* Added functions to calculate sunset and sunrise
* Added function to replace illegal characters.

**AIRBOSS v1.1.5**
* Fixed wrong function name when recovery is resumed.
* Replace illegcal characters in player names when writing trapsheet.
2020-05-24 23:33:21 +02:00
Frank
21652de804 AIRBOSS v1.1.4
Updated Nimitz class parameters.
2020-05-20 23:03:25 +02:00
Frank
e677e2acf4 Merge pull request #1330 from FlightControl-Master/develop
Small fix to A2G dispatcher
2020-05-19 09:24:07 +02:00
Frank
2658230331 Update and fixes
AI_A2G_DISPATCHER
- Fixed report output.

ENUMS
- Added Morse code.
2020-05-17 00:28:12 +02:00
Frank
bb0827bb8c Merge pull request #1327 from FlightControl-Master/develop
Develop
2020-05-16 19:48:58 +02:00
Frank
be1f8cde34 Merge pull request #1326 from FlightControl-Master/FF/Develop
Fixes
2020-05-16 13:14:55 +02:00
Frank
29028bf128 Merge branch 'develop' into FF/Develop 2020-05-16 13:12:49 +02:00
Frank
c30ca2b18a Fixes
RADIOQUEUE
- Fix for issue #1321

USERFLAG
- Added delay parameter

CLEANUP
- Fixes iniunit nil #1324

WAREHOUSE v1.0.2
- Fixes #1322
2020-05-16 13:09:04 +02:00
Frank
d1b40b63c2 Merge pull request #1320 from zlinman/develop
Develop
2020-05-06 09:03:00 +02:00
zlinman
0b7ac754c5 only type error corrected, spaces deleted 2020-05-04 12:53:09 +02:00
Frank
5b514d223b Merge pull request #1319 from FlightControl-Master/FF/Develop
Ff/develop
2020-05-04 00:39:26 +02:00
Frank
7d8e27c83e Update Warehouse.lua 2020-05-04 00:37:28 +02:00
Frank
dcd1060496 Update Warehouse.lua 2020-05-04 00:33:33 +02:00
Frank
533e2abf17 Merge branch 'develop' into FF/Develop 2020-05-04 00:31:54 +02:00
Frank
6b318d0309 Update Enums.lua 2020-05-04 00:31:43 +02:00
Frank
bee44b97fd WAREHOUSE v1.0.1 2020-05-03 23:56:32 +02:00
Frank
77de1dda2b Stuff 2020-05-03 01:14:37 +02:00
Frank
5b03bc5066 Merge branch 'develop' into FF/Develop 2020-05-01 23:05:55 +02:00
Frank
0d6f32581c Merge pull request #1315 from FlightControl-Master/FF/Develop
Fixes
2020-05-01 23:03:54 +02:00
Frank
2620370890 Fixes
UTILS
- Corrected Big Smoke and Fire presets. Issue #1313

CONTROLLABLE
- Fixed callsign number in :CommandSetCallsign function. #1314

ENUMS
- Added formations (old and new)
2020-05-01 23:01:43 +02:00
zlinman
f866af0d4a Update CleanUp.lua 2020-04-30 18:45:06 +02:00
zlinman
b86eac33b6 Revert "Update CleanUp.lua"
This reverts commit c78bd2652a.
2020-04-30 18:38:04 +02:00
zlinman
c78bd2652a Update CleanUp.lua 2020-04-30 18:33:15 +02:00
Frank
392d465b30 Merge pull request #1310 from SteveLockeUK/fix/task-report-order
Added call to self:UpdateTaskInfo( self.DetectedItem ) to RerportOrde…
2020-04-25 21:48:44 +02:00
Stephen Locke
056cc1630d Added call to self:UpdateTaskInfo( self.DetectedItem ) to RerportOrder for both Task_A2A and Task_A2G. This fixes an issue with multi task missions where coordinates are not available when Moose attempts to sort the tasks for the comms menu. 2020-04-25 15:07:04 +01:00
Frank
364ce927e7 Merge pull request #1309 from FlightControl-Master/FF/Develop
Misc Fixes and Improvements
2020-04-25 01:26:53 +02:00
Frank
517d5860fb Update Controllable.lua 2020-04-25 00:57:59 +02:00
Frank
4e49184da2 Update Static.lua 2020-04-24 22:53:42 +02:00
Frank
68ece29ab5 Update Controllable.lua 2020-04-24 22:45:45 +02:00
Frank
c3cc76c15b Update Controllable.lua 2020-04-24 21:57:10 +02:00
Frank
0a570da986 Up 2020-04-24 21:16:42 +02:00
Frank
b25697e261 Merge branch 'develop' into FF/Develop 2020-04-24 16:57:40 +02:00
Frank
863e239204 Update Utils.lua 2020-04-24 16:53:15 +02:00
Frank
457b30ed07 ATIS v0.7.1
Fixed bug that windfrom < 0.
2020-04-24 16:46:46 +02:00
Frank
7360c51a8f Updates Misc 2020-04-24 16:03:28 +02:00
Frank
df512f7a58 Update RadioQueue.lua 2020-04-24 01:19:04 +02:00
Frank
da05c41c27 Update AI_Air_Engage.lua
Should fix #1206
2020-04-24 00:40:54 +02:00
Frank
3c428723f8 Update AI_Air.lua
Should fix issue #1205
2020-04-24 00:16:18 +02:00
Frank
4c3fd1d867 Airboss v1.1.3
- Unicorn only when Tgroove 16-18 sec.
- Recovery time can be given as relative seconds.
- Time before turn can be specified by user.
- Function to get next recovery time.
- Removed some old code.
2020-04-23 16:44:38 +02:00
Frank
909c028e48 Update AI_A2A_Patrol.lua
Fixes issue #1307
2020-04-23 16:04:50 +02:00
Frank
14e12dc36b Merge pull request #1306 from FlightControl-Master/thebgpikester-SPAWN-DOC-UPDATE
Spawn Documentation fixes
2020-04-22 15:37:16 +02:00
Pikes
0502e0a020 Spawn Documentation fixes
no code touched
no code touched
I went through removing where 6-7 year old docs used a non existent method "Schedule" and replaced it with SpawnSchedule() wher ethat worked or Spawn() where I couldnt understand the ancient demo.
Also added warnings on IniLimit and InitCleanUp() the two worst offenders of misconfiguration.
Couple of Belgian typos corrected.
2020-04-22 14:09:31 +01:00
Frank
36aebd3c04 Merge pull request #1299 from FlightControl-Master/thebgpikester-ai_Patrol_Baro
change default to "BARO"
2020-04-20 22:02:43 +02:00
Frank
007831bb9a Merge pull request #1298 from FlightControl-Master/thebgpikester-Dispatcherchanges
Changing default altitude to BARO
2020-04-20 21:57:13 +02:00
Frank
def44bde89 Merge pull request #1304 from FlightControl-Master/FF/Develop
Fixes
2020-04-08 12:04:28 +02:00
Frank
4b369fae95 A2G Dispatcher
Fixes issue #1303
2020-04-08 11:54:09 +02:00
Frank
cb4a44b512 Event 2020-04-08 01:10:43 +02:00
Frank
53fbec9c15 events 2020-04-08 00:09:47 +02:00
Frank
d476922af2 Merge pull request #1301 from FlightControl-Master/FF/Develop
ATIS and WAREHOUSE
2020-04-04 23:39:56 +02:00
Frank
861f2da4d6 ATIS and WAREHOUSE 2020-04-04 23:08:23 +02:00
Pikes
6d75b25f57 change default to "BARO"
addition on AI_A2A_Patrol already done
2020-04-02 13:50:41 +01:00
Pikes
bfb3ae33f4 Changing default altitude to BARO
to avoid DCS AI stopping engaging or even flying int he right direction.
2020-04-02 13:26:03 +01:00
Pikes
a796c7a594 Urgent fix for Ford_AB name change. this breaks Moose completely until pushed.
ATC table was changed to the correct name for the enumerator string of "Ford" to "Ford_AB" The main enumerator table was not also changed so the two mismatched.
One has to agree with the other else Moose stops with an index error.
However, there could be minor risk if someone refers to the old enumerator. Whilst we dont care about the string name, the AB was changed by ED anyway. The actual way to keep is to have the string equal the map name. Either way is damned if you do, damned if you don't but I'd rather keep the convention and blame ED than keep it wrong and inconsistent.
2020-03-28 18:56:14 +00:00
Frank
af63c1a219 Merge pull request #1269 from FlightControl-Master/SPAWNInitArray_#1255
Spawn init array #1255
2020-03-27 23:04:09 +01:00
Frank
72da7a2c04 Merge pull request #1282 from FlightControl-Master/Pikes-ATC---added-the-6-new-airbases
Normandy added 7 new airbases with coords
2020-03-27 22:58:57 +01:00
Frank
f9bdbf4e3f Merge pull request #1293 from FlightControl-Master/FF/Develop
AIRBASE
2020-03-20 16:37:23 +01:00
Frank
850951bcb4 Update Airbase.lua 2020-03-20 16:35:28 +01:00
Frank
3d0f002f73 Merge pull request #1292 from FlightControl-Master/ATC_GROUND_PG_#1291
Applied @Warlords fix on swapped Airbases in ATC_Ground.lua PG map
2020-03-16 18:07:53 +01:00
Thor Vik
ff8c454752 Applied @Warlords fix on swapped Airbases in ATC_Ground.lua PG map
Solves Issue #1291
2020-03-16 17:52:14 +01:00
Frank
6882c4b42f Merge pull request #1290 from FlightControl-Master/FF/Develop
SPAWN
2020-03-14 20:34:39 +01:00
Frank
9b0f474edf Update Spawn.lua 2020-03-14 20:30:58 +01:00
Frank
ceaae5421c SPAWN
corrected spawn name
2020-03-14 20:28:32 +01:00
Frank
36666df76f Temp 2020-03-14 19:19:55 +01:00
Frank
3616e4264a Merge pull request #1288 from FlightControl-Master/Issue_#1286_SpawnNewFromTemplate
Made parameter SpawnAliasPrefix mandatory for SpawnFromTemplate funct…
2020-03-11 22:49:27 +01:00
Thor Vik
a704693abf Made parameter SpawnAliasPrefix mandatory for SpawnFromTemplate function. 2020-03-11 15:28:36 +01:00
Frank
4dc303a166 Merge pull request #1287 from FlightControl-Master/Issue_#1286_SpawnNewFromTemplate
Fix for Issue #1286, partly tested.
2020-03-11 10:50:23 +01:00
Thor Vik
71b5140e69 Fix for Issue #1286, partly tested. 2020-03-11 10:11:59 +01:00
Frank
00e5499f81 Merge pull request #1285 from Loophole62ndFF/InitGroupHeading_implementation
Init group heading implementation
2020-03-05 22:57:29 +01:00
Michael Barnes
7cf73af707 Added some protection against unexpectedly-nil values. 2020-03-05 13:05:25 +10:00
Michael Barnes
4ef827836c Added SPAWN:InitGroupHeading function, to allow orientation of the overall group formation when spawned (handy for static formations like SAM batteries, etc.) 2020-03-05 13:05:11 +10:00
Frank
e3d6337481 Merge pull request #1284 from FlightControl-Master/FF/Develop
MGRS
2020-02-26 20:52:48 +01:00
Frank
f258e9263e MGRS 2020-02-26 20:44:35 +01:00
Frank
48d99a19c5 Merge pull request #1270 from FlightControl-Master/WAREHOUSE-_#1260
Added some line to docs to tell Spawn state might not be as expected.
2020-02-25 21:53:23 +01:00
Frank
f34511bd8c Merge pull request #1283 from FlightControl-Master/FF/Develop
Fixes & Improvements
2020-02-25 21:41:48 +01:00
Frank
63a5d2e3ac Fixes & Improvements 2020-02-25 21:35:00 +01:00
Pikes
3fb52d4fa1 Normandy added 6 airbases with coords
added the coords 
added the list of airbases at the top
2020-02-22 22:17:34 +00:00
Frank
463403cef4 Merge pull request #1280 from FlightControl-Master/Pikes---adding-new-Normandy-airbase-enumerators-to-AIRBASE.Normandy
Update Airbase.lua
2020-02-22 18:48:26 +01:00
Pikes
fedee49eb1 Update Airbase.lua
https://github.com/FlightControl-Master/MOOSE/issues/1279
I dont know if anything else needs to be done apart from this.
2020-02-22 16:35:18 +00:00
132nd-Entropy
042cc7a85f Update ATIS.lua 2020-02-20 15:04:00 +01:00
132nd-Entropy
9583168c4d Update ATIS.lua 2020-02-20 14:38:11 +01:00
Frank
0c32c35c27 Merge pull request #1275 from FlightControl-Master/FF/Develop
Ff/develop
2020-02-17 23:42:47 +01:00
Frank
6ab85072d2 DCS 2.5.6 fixes 2020-02-17 23:19:28 +01:00
Frank
04da941c36 Updates 2020-02-15 23:51:28 +01:00
Wingthor
d6c919a097 Added some line to docs to tell Spawn state might not be as expected.
Added:
Requested assets spawn in various "Engagement Rules" (ROE) and Alerts modes. If your assets will cross into dangerous areas, be sure to change these states. You can do this in @{#WAREHOUSE:OnAfterAssetSpawned}(*From, *Event, *To, *group, *asset, *request)) function.

Initial Spawn states is as follows:
    GROUND: ROE, "Return Fire" Alarm, "Green"
    AIR: 	ROE, "Return Fire" Reaction to Threat, "Passive Defense"
    NAVAL	ROE, "Return Fire" Alarm,"N/A"

Solves issue #1260
2020-02-13 12:16:56 +01:00
Wingthor
1f9ffcd92d The error is to deep to solve on the fly. In database new groups appear, but not in map, when regular spawning (:Spawn()) is done. Spawning scheduled causes groups to appear on map. OnSpawnGroup is not triggered in neither of methods.
#Issue 1255 is for now solved by adding a caution to documentation for function/directive: "CAUTION: this directive will NOT work with OnSpawnGroup function."

Requires work from author or someone else with deep knowledge of the class.
2020-02-13 09:58:59 +01:00
Wingthor
12ef34acaa Merge pull request #1266 from FlightControl-Master/AIRBASE.Nevada.Mellan_Airstrip_does_not_exist_#1263
FIX: removed Mellan_Airstrip from list of Nevada airfields (not a val…

Fixes #1266 which now can be closed.
2020-02-12 18:38:53 +01:00
132nd-Entropy
ce60e7ce74 FIX: removed Mellan_Airstrip from list of Nevada airfields (not a valid airport and has no taxiway designations, resulting in a scripting error if included) 2020-02-12 16:25:45 +01:00
thebgpikester
aa5d36db88 Merge pull request #1265 from FlightControl-Master/ATC_GROUND_PERSIANGULF_Abu_Musa_Island_Airport_double_entry
Issue #1264 Abu_Musa_Island_Airport
2020-02-11 15:11:23 +00:00
Wingthor
a2759b1cc0 Issue #1264 Abu_Musa_Island_Airport 2020-02-11 15:55:10 +01:00
Frank
0da0b025b5 Merge pull request #1262 from FlightControl-Master/#1258-WAREHOUSE-onafterChangeCountry(From,-Event,-To,-Country)
#1258 warehouse onafter change country(from, event, to, country)
2020-02-09 19:41:01 +01:00
Wingthor
f685064c0c fixed the typo on self.airbasse to self.airbase.
Tested with and without this line `SetAirbase(AIRBASE:FindByName(AIRBASE.Caucasus.Kutaisi))` in the test script.

Resolves issue #1258
2020-02-08 12:44:50 +01:00
Wingthor
b68e254bc0 Fixes issue #1258 WAREHOUSE:onafterChangeCountry() 2020-02-06 15:36:54 +01:00
Wingthor
d43f77706e Merge branch '1254-range-setsoundfilespath-path' into 'develop'
Fixed issue 1254

Closes #1254

See merge request Wingthor/MOOSE!1259
2020-02-01 20:28:07 +01:00
Wingthor
5e1fb36d01 Fixed issue 1254 2020-02-01 20:27:00 +01:00
Wingthor
6d3bbe88e6 Merge branch 'wingthor_fixes' into 'develop'
Test Commits and pipe

See merge request Wingthor/MOOSE!1258
2020-02-01 19:53:26 +01:00
Wingthor
e4906eb083 Test Commits and pipe 2020-02-01 19:30:32 +01:00
Frank
19eb81866a Merge pull request #1257 from FlightControl-Master/FF/Develop
Misc/Minor
2020-01-25 23:59:06 +01:00
Frank
cd34755036 Merge pull request #1253 from NachtRaveVL/RouteNameFix
Adding route name fetch to getGroupRoute()
2020-01-21 19:04:23 +01:00
NachtRaveVL
c72625c60f Adding route name fetch to getGroupRoute() 2020-01-20 15:28:03 -08:00
Frank
1e1154d190 controllable 2020-01-19 22:14:45 +01:00
Frank
ff0f62c01b Merge pull request #1252 from FlightControl-Master/FF/Develop
ATIS v0.6.2 and Misc
2020-01-08 19:43:56 +01:00
Frank
e6e5787fc6 Update Point.lua 2020-01-08 19:26:40 +01:00
Frank
431e8a05f9 ZCC
- Fixed unit counting in status report.
2020-01-08 18:45:57 +01:00
Frank
b91a8cf4c8 ATIS v0.6.2
- Added F10 marker option.
2020-01-08 12:04:45 +01:00
Frank
3e8455410e ZCC, ATIS, CONTROLLABLE 2020-01-07 23:34:36 +01:00
Frank
3a9ad1b7cc Merge pull request #1251 from FlightControl-Master/FF/Develop
FOX fixed log spamming
2020-01-06 21:58:25 +01:00
Frank
6000421957 Update Fox.lua 2020-01-06 21:54:59 +01:00
Frank
231ceef41f Merge pull request #1250 from FlightControl-Master/FF/Develop
Misc Improvements
2020-01-06 21:43:31 +01:00
Frank
4661f6981e Misc 2020-01-06 21:09:21 +01:00
Frank
fcfcfeae00 Misc 2020-01-06 00:15:44 +01:00
Frank
09ed562f61 SUPPRESSION & AIRBOSS
- Fixed bug/typo in SUPPRESSION:SetHomeBase() function.
- Added AIRBOSS Marshal event.
2020-01-04 00:15:00 +01:00
Frank
b1f2bab87e Merge pull request #1249 from FlightControl-Master/FF/Develop
Ff/develop
2020-01-02 22:58:51 +01:00
Frank
aa6515e1ca AI_AIR
- Fixed onafterRefuel
2020-01-02 22:50:35 +01:00
Frank
d5b8ed62ae AI_AIR
- Fixed call to .Resume function if AI_A2A_CAP is calling.
2020-01-02 20:15:42 +01:00
Frank
6208c79d1f ARTY v1.1.7 & AIRBASE
- Improved ARTY._PassingWaypoint function.
- Corrected AIRBASE.Ras_Al_Khaimah
2020-01-02 16:17:25 +01:00
Frank
740ee74f61 SUPPRESSION 2019-12-31 00:00:45 +01:00
Frank
0bb0c70667 Merge pull request #1247 from FlightControl-Master/FF/Develop
ZONE_CAPTURE_COALITION and other fixes
2019-12-29 23:40:27 +01:00
Frank
9fb311bf7d Update AI_A2A_Patrol.lua 2019-12-29 23:10:49 +01:00
Frank
8915657e91 ZONE_CAPTURE_COALITION 2019-12-29 23:08:23 +01:00
Frank
8b506a2f20 ZONE_CAPTURE_COALITON & RANGE 2019-12-28 23:59:39 +01:00
Frank
6dd73dba44 AI_AIR
Fixed task in onafterPatrolRoute function
2019-12-28 19:53:02 +01:00
Frank
d3a3d96436 ZONE CAP COAL 2019-12-27 23:03:19 +01:00
Frank
8337058306 CAPTURE ZONE 2019-12-26 23:20:55 +01:00
Frank
389931fbf0 AI_PATROL
- output fix
2019-12-21 01:09:59 +01:00
Frank
b9fd6c0e31 Update RAT.lua 2019-12-19 22:59:37 +01:00
Frank
f48f53fb37 Update RAT.lua
Fixed bug with uncontrolled spawning.
2019-12-19 13:11:58 +01:00
Frank
07edcd7e73 SCHEDULER
Clean up
2019-12-18 23:00:42 +01:00
Frank
a193d342d7 Merge pull request #1244 from FlightControl-Master/FF/Develop
A2A Dispatcher and other fixes/improvements
2019-12-17 00:16:04 +01:00
Frank
5b9978e390 A2A Dispatcher fixes
- Increased engage distance.
2019-12-17 00:09:24 +01:00
Frank
c3c984381e SUPPRESSION 2019-12-16 00:59:23 +01:00
Frank
ca49e8f8ae FOX v0.6.1
- Added Stop event.
2019-12-15 22:30:05 +01:00
Frank
59c46f9b4b Push 2019-12-11 21:38:53 +01:00
Frank
b9384831fb Merge pull request #1242 from FlightControl-Master/FF/Develop
ATIS v0.6.0
2019-12-11 21:12:24 +01:00
Frank
0d57ad5584 ATIS v0.6.0
- Added altimeter QNH calculation
2019-12-11 21:10:23 +01:00
Frank
cf34ec0905 Merge pull request #1241 from FlightControl-Master/FF/Develop
SPAWN AI_Balancer
2019-12-10 20:16:09 +01:00
Frank
1c99e474b2 SPAWN AI_Balancer
SPAWN
- Added :InitAirbase()

AI_BALANCER
- Fixed bug in Return function.
2019-12-10 20:11:49 +01:00
Frank
e45134f086 Merge pull request #1240 from FlightControl-Master/FF/Develop
SCHEDULER fixes
2019-12-08 00:29:32 +01:00
Frank
1b5e9df586 SCHEDULER fixes 2019-12-08 00:27:20 +01:00
Frank
4b6612e114 Cargo fixes 2019-12-04 22:17:51 +01:00
Frank
e4473010eb Docs
HelicopterUsable
2019-12-02 16:33:03 +01:00
Frank
bb259b2572 Merge pull request #1235 from FlightControl-Master/FF/Develop
Ff/develop
2019-11-25 12:17:36 +01:00
Frank
e16e2851c1 ARTY v1.1.6
- Check that arty unit is not dead when addressed via marker
2019-11-25 11:58:29 +01:00
Frank
169c5a674c Fixes
AI_PATROL:
- Target unit is nil. Issue #1234
DETECTION:
- ReportFriendliesNearBy ForEachPlayer added nil check.
CONTROLLABLE:
- Added IsHelicopter()
- Added OptionRestrictBurner()
AI_A2A_Cap
- AtteckUnit not nil check.
AI_A2A_Dispatcher:
- DefenderGroup not nil and alive check #1228
AI_A2A_GCICAP:
- AttackCoordinate nil check
2019-11-25 11:50:07 +01:00
Frank
60042e14dc Update Airboss.lua
- Removed Orion
2019-11-23 21:02:02 +01:00
Frank
14de37f390 AIRBOSS & BASE
BASE:
- Fixed bug that tracing is always ON

AIRBOSS v1.1.0
- Added support for P-3C Orion and C-2A Greyhound (AI) from MAM
2019-11-23 20:36:45 +01:00
Frank
e67b819df9 Merge pull request #1224 from FlightControl-Master/FF/Develop
RANGE v2.2.0
2019-11-09 14:56:55 -05:00
Frank
efccefecad RANGE v2.2.0
- Added option for range instructor and range control radio (needs voice overs).
- Reduced number of smokes for strafe pits.
- Updated docs.
- SetAutoSave will auto load as well.
- other
2019-11-09 20:55:28 +01:00
Frank
faa2debbae DATABASE & RAT
- DATABASE: spawned units (ships) are registered as airbases.
- RAT v2.3.8: script will not crash if (spawned) AIRBASE cannot be found .
2019-11-04 22:17:26 +01:00
Frank
ddf61d1bf3 ARTY v1.1.5
- Fixed bug for tac nukes (critial mass insufficient to trigger nuclear fission).
2019-10-31 18:55:21 +01:00
Frank
c5d514efdf Merge pull request #1220 from FlightControl-Master/FF/Develop
AI_Air_Patrol
2019-10-22 17:28:42 -04:00
Frank
f1c16def9a Update AI_Air_Patrol.lua
- fixed bug that route is not called for racetrack
2019-10-22 23:25:30 +02:00
Frank
626db45a7e Merge pull request #1219 from FlightControl-Master/FF/Develop
AI_Air_Patrol
2019-10-22 16:54:52 -04:00
Frank
21a7594134 Update AI_Air_Patrol.lua
- fixed bug that altitude and speed are not defined for racetrack patterns
2019-10-22 22:53:04 +02:00
Frank
5fd20477d9 Merge pull request #1218 from FlightControl-Master/FF/Develop
AIRBOSS v1.0.9
2019-10-22 08:46:45 -04:00
Frank
01dd3c93d2 AIRBOSS v1.0.9
- Case II/III marshal stacks are not collapsed.
- Fixed little bug that Charlie time cannot be calculated if no future recovery window is defined.
2019-10-22 14:35:10 +02:00
Frank
b733e3b6d8 Merge pull request #1217 from FlightControl-Master/FF/Develop
ATIS v0.5.0
2019-10-21 16:47:44 -04:00
Frank
fe2ef11f8d ATIS v0.5.0 2019-10-21 22:29:45 +02:00
Frank
9b6994749b Merge branch 'develop' into FF/Develop 2019-10-21 00:30:02 +02:00
Frank
9b209d6ce0 ATIS 2019-10-21 00:29:46 +02:00
Frank
ed649747bd Merge pull request #1215 from FlightControl-Master/FF/Develop
ATIS v0.4.1
2019-10-18 15:17:31 -04:00
Frank
f5f1c9043b ATIS v0.4.1
ATIS v0.4.1
- Fixed schedulers being deallocated by lua garbage collector.

RADIOQUEUE
- Added alias optional name.
- Minor.
2019-10-18 21:15:53 +02:00
Frank
c7c371dc34 Merge pull request #1212 from FlightControl-Master/FF/Develop
COORDINATE
2019-10-14 08:36:17 -04:00
Frank
1cea10f03a Update Point.lua
Fixed bug in GetClosestAirbase() function.
2019-10-14 14:34:53 +02:00
Frank
3e9f54aaa5 Merge pull request #1210 from FlightControl-Master/FF/Develop
ATIS v0.4.0
2019-10-10 19:33:48 -04:00
Frank
ab1cd41d79 Update ATIS.lua 2019-10-11 01:23:26 +02:00
Frank
dc9f730d7d ATIS v0.4.0
ATIS
UTILS
AIRBASE
2019-10-09 21:13:08 +02:00
Frank
c0e48288de ATIS v0.3.3
- Improved soud file duration.
- Added Nav point data option.
2019-10-08 00:21:41 +02:00
Frank
a13b79ced1 Merge pull request #1209 from FlightControl-Master/FF/Develop
ATIS v0.3.1
2019-10-03 06:15:02 -04:00
Frank
cd845d385f ATIS v0.3.1
- Fixed bug in NATO alphabet (entry 14 missing)
2019-10-03 12:13:38 +02:00
Frank
dea7a5674c Merge pull request #1208 from FlightControl-Master/FF/Develop
ATIS
2019-10-02 14:55:53 -04:00
Frank
8d1d2179f9 ATIS v0.3.0 2019-10-02 20:43:51 +02:00
Frank
9b96ff5978 AIRBASE
- Added runwaydata and active runway functions
2019-10-02 19:47:33 +02:00
Frank
6cc233cac7 ATIS
- COORDINATE
- RADIO
- RADIOQUEUE
- ATIS
- UTILS
- CONTROLLABLE
2019-10-02 19:45:37 +02:00
FlightControl
ad2ce8df78 Improvements to A2A and A2G Dispatchers 2019-09-30 10:35:29 +02:00
FlightControl
4ea44308bc Fix for older A2A dispatcher CAP api altitude problem during takeoff ... 2019-09-26 19:36:42 +03:00
FlightControl
5cdaf53727 - Added a NoTrace option to the scheduler, so for those schedulers that have a schedule in microseconds, you may wanna use the function NoTrace(), to avoid spamming the dcs.log.
- Started with the implementation of multiple languages of speech. Got now a Russian and English prototype working.
- Moved radio frequency settings to a squadron, so multiple squadrons can communicate in their own radio frequency.
2019-09-25 17:53:11 +03:00
FlightControl
fb875077d7 Speech optimizations for A2A dispatcher 2019-09-22 20:30:59 +02:00
FlightControl
c579aad606 API V2 of the A2A and A2G dispatchers. Now a more fine grained attack is possible. You can now tweak the altitude and speed when units engage the target. Especially useful for ground attacks when SEAD, CAS or BAI.You can model low altitude approaches. Added 2 new test missions under the IAD - A2G API v2. Please report any bugs. 2019-09-22 08:23:28 +02:00
FlightControl
8c54b7298d Tuning A2A AI dispatcher to work with new sound speech system. 2019-09-15 18:36:27 +02:00
FlightControl
b309c25619 Speech Engine Prototype 2019-09-15 18:02:45 +02:00
Frank
04a699f990 Merge pull request #1197 from FlightControl-Master/FF/Develop
Update ScheduleDispatcher.lua
2019-09-13 13:38:52 -04:00
Frank
d51fc5de8a Update ScheduleDispatcher.lua
better fix
2019-09-13 19:37:13 +02:00
Frank
e458c70a7b Merge pull request #1196 from FlightControl-Master/FF/Develop
ScheduleDispatcher Source=nil fix
2019-09-13 12:58:54 -04:00
Frank
2d4a4fb2ca Update ScheduleDispatcher.lua
Fix for Source (=Infor.source) nil problem.
2019-09-13 18:57:27 +02:00
FlightControl
e23ebd622a - Fixing repetition of Patrolling in A2G dispatcher. 2019-09-09 20:24:51 +03:00
FlightControl
98e2f2c09b - Added A2G voice overs to some of the basic events during defender flight. More to come, like multiple languages, and also more voice overs concerning some of the more detailed events, like:
- Damage
  - Firing
  - Enemy location
  - Callsigns
  - Numbers for distance and degrees.
2019-09-09 19:38:22 +03:00
FlightControl
1fd9cbec1f - Added A2G voice overs to some of the basic events during defender flight. More to come, like multiple languages, and also more voice overs concerning some of the more detailed events, like:
- Damage
  - Firing
  - Enemy location
  - Callsigns
  - Numbers for distance and degrees.
2019-09-09 11:17:45 +02:00
FlightControl
1aedcf1ae4 Radio Queue 2019-09-09 08:19:48 +02:00
FlightControl
e0075cc9ac - Added a new UTILS.kpairs iterator, which will base the iteration on a different key, which can be determined before the iteration starts. eg. the key should be a sort in ascending order on the distance of the information in the set, and it should return the distance + item in the set for each element in the set.
- Resolved for TASK_CAPTURE_ZONES the whole logic of determining correctly the player and AI defense zones, as to be displayed in the information panel, this was a very difficult exercise for me.
2019-09-08 08:38:33 +02:00
FlightControl
372dd704d2 - Added a randomization to some of the internal schedulers to ensure that the simulation does not get unnecessary hickups and guarantees more fluent play.
- Added the option SetFlashStatus() to the CommandCenter to ensure flashing of subscribed tasks automatically to inform the player while in-flight. Also extended TASK to ensure the flasing gets initialized once the player joins the task.
2019-09-03 17:25:45 +02:00
FlightControl
14b35cb069 Improvements in trace output:
- Scheduled calls are traced with ---> or +++> prefixes... This is handy to find where scheduled calls were actually called in the code. Line number and function is shown correctly now. ---> is for one schedule, and +++> for repeated schedules.
- Fsm output is now also organized better. All Fsm calls are shown with :::> prefix. The onafter, onbefore, onenter or onleave method names are also shown, and also the state transition including the function call place is shown now.
-- Now for each trace line, the class name has 30 characters space.
2019-08-31 07:24:31 +02:00
FlightControl
c1b240857f - Remove unnecessary trace lines
- Implement Scheduled Trace - Now source and line number are shown for scheduled calls.
- Info lines are now shown where appropriate.
- The width of trace for the class name is now 25 characters instead of 20.
2019-08-26 08:26:14 +02:00
Frank
0f5eb30a0f Merge pull request #1192 from FlightControl-Master/FF/Develop
DATABASE fix
2019-08-25 16:09:05 -04:00
Frank
9e0f2c22b6 DATABASE fix
- Fix bug for RED templates being registered as NEUTRAL
2019-08-25 22:06:51 +02:00
FlightControl
c0771c0096 - Bugfix when enemy plane has crashed, AttackerDetection is not existing anymore.
Implemented an important fix. Refuel airplanes also when they are engaging targets and the fuel treshold has been reached.
- Inherit ZONE_BASE from FSM instead of BASE. Opens a range of possibilities.
- Remove from ZONE_GOAL the Zone field, and make it inherit from ZONE_BASE instead of FSM!
- Rework the new inheritance tree in the code. (Remove .Zone fields).
- Implement the determination of attack and defense zones.
- Reworked the TaskInfo to include Type and ShowKey.
- Flash A2G Tasking Details. Added menu option.
2019-08-25 06:42:03 +02:00
FlightControl
c716ba16b3 - Bugfix when enemy plane has crashed, AttackerDetection is not existing anymore. 2019-08-24 22:28:44 +02:00
FlightControl
96a904c077 Implemented an important fix. Refuel airplanes also when they are engaging targets and the fuel treshold has been reached. 2019-08-24 08:56:50 +02:00
FlightControl
f951aae3ee - Inherit ZONE_BASE from FSM instead of BASE. Opens a range of possibilities.
- Remove from ZONE_GOAL the Zone field, and make it inherit from ZONE_BASE instead of FSM!
- Rework the new inheritance tree in the code. (Remove .Zone fields).
- Implement the determination of attack and defense zones.
- Reworked the TaskInfo to include Type and ShowKey.
- Flash A2G Tasking Details. Added menu option.
2019-08-21 22:04:11 +03:00
Frank
7a859f20bb Merge pull request #1190 from FlightControl-Master/FF/Develop
Ff/develop
2019-08-18 16:26:02 -04:00
Frank
3bfa8830a5 Merge branch 'develop' into FF/Develop 2019-08-18 22:21:54 +02:00
Frank
ec85b42a38 OPS
AIRBOSS v1.0.8
- Fixed bug that status reset after wave off causes a unitcorn grade.
- Optimized radio scheduler calls ==> leads to less mem consumtion according to collectgarbage("count").

RESCUEHELO v1.0.9
- Adjusted default follow time interval to 1.0 sec.
- Added SetFollowTimeInterval() function.
- Respawn after returned delayed by 5 sec.

GROUP
- Added nil check in RespawnAtCurrentAirbase() function.
2019-08-18 22:21:41 +02:00
FlightControl
a216eb4e74 Defense test for TASK_CAPTURE_ZONE 2019-08-18 16:44:20 +02:00
FlightControl
94476e418e Messaging and Tactical Menu 2019-08-17 10:25:26 +02:00
FlightControl
fd1eb5f601 AI_A2A_DISPATCHER and AI_A2G_DISPATCHER ... Fixed in-air refuelling problem. Now refuelling is executed instateneously when patrolling in the air and the fuel treshold has been reached. 2019-08-16 22:15:55 +02:00
Frank
db7ec60c86 Merge pull request #1189 from FlightControl-Master/FF/Develop
Ff/develop
2019-08-14 16:44:20 -04:00
Frank
97e1a65b52 WAREHOUSE v0.9.6
- respawn after destroyed keeps assets
2019-08-14 22:36:39 +02:00
Frank
23497dddd4 ARTY v1.1.3
Unfixed workaround for wrong markpoint coordinates. Was fixed in DCS 2.5.5.34644
2019-08-14 22:32:45 +02:00
Frank
e8a7def26e Update Airboss.lua 2019-08-14 21:51:55 +02:00
Frank
28a23c21f4 Update Airboss.lua 2019-08-14 00:35:54 +02:00
Frank
f32094aa43 up 2019-08-12 22:55:56 +02:00
Frank
c215255ae2 Merge branch 'develop' into FF/Develop 2019-08-11 20:39:31 +02:00
Frank
53b998cfab Airboss v1.0.7
- Corrected Foul Deck waveoff.
- Added LSOGrade event.
2019-08-11 20:25:33 +02:00
FlightControl
0a0a7d0fc3 Added a task message when it is engaging or not. Important for communication to the player. 2019-08-11 12:59:43 +02:00
FlightControl
d84ae236eb Added a task message when it is engaging or not. Important for communication to the player. 2019-08-11 12:59:21 +02:00
FlightControl
6dff84f435 Fixed problem with detections escalating. Too much events fired. 2019-08-11 11:00:53 +02:00
FlightControl
1bfa5b5ee0 Fixed problem with detections escalating. Too much events fired. 2019-08-11 11:00:33 +02:00
FlightControl
c3618bdc26 - Fixed Messaging of A2G defenses to players within the same coalition. Now the callsign will be used to communicate, not the user id. 2019-08-11 10:06:35 +02:00
FlightControl
eebc5d90d1 - Fixed Messaging of A2G defenses to players within the same coalition. Now the callsign will be used to communicate, not the user id. 2019-08-11 10:06:16 +02:00
FlightControl
596b3ddb4a Fixes issue with SetDetectionLimit
Optimized the detection intervals + fixed bugs with player detection.
2019-08-11 09:46:25 +02:00
FlightControl
e770af9fc9 Optimized the detection intervals + fixed bugs with player detection. 2019-08-11 09:45:28 +02:00
FlightControl
d3c76da7a2 Fixes issue with SetDetectionLimit 2019-08-11 08:32:34 +02:00
FlightControl
5f9880f17d - Implemented attack nearest target.
- Implemented attack nearest ground target.
- Implemented attack nearest air target.
- Report all targets.
- Report only ground targets.
- Report only ground targets with radar.
- Report only air targets.
- Sort targest from nearest to furthest.
- Improved menu system.
- Improved detection of ground targets and air targets and reporting.
- Added the task TASK_CAPTURE as part of the reporting framework.
- Improved the detection of targets. Now targets with specific detection methods are correctly detected. f.e. disabling DLINK will now work correctly!

All these fixes are great improvements to the AI_ESCORT framework!
2019-08-10 15:42:00 +02:00
FlightControl
d6b1018700 - Implemented attack nearest target.
- Implemented attack nearest ground target.
- Implemented attack nearest air target.
- Report all targets.
- Report only ground targets.
- Report only ground targets with radar.
- Report only air targets.
- Sort targest from nearest to furthest.
- Improved menu system.
- Improved detection of ground targets and air targets and reporting.
- Added the task TASK_CAPTURE as part of the reporting framework.
- Improved the detection of targets. Now targets with specific detection methods are correctly detected. f.e. disabling DLINK will now work correctly!

All these fixes are great improvements to the AI_ESCORT framework!
2019-08-10 15:41:34 +02:00
Frank
f934c8cb77 Update Warehouse.lua 2019-08-08 23:27:45 +02:00
Frank
db4b5383fa Merge branch 'develop' into FF/Develop 2019-08-07 22:58:07 +02:00
Frank
4982cabcfb AIRBOSS v1.0.7
Corrected foul deck wave off.
2019-08-07 22:17:16 +02:00
FlightControl
5594cd8fdf Removed the escorts to detect targets using DLINK. Now escorts will only report targets that were detected by its own onboard detection methods. 2019-08-07 16:42:08 +02:00
FlightControl
fbb32a9e45 Removed the escorts to detect targets using DLINK. Now escorts will only report targets that were detected by its own onboard detection methods. 2019-08-07 16:41:21 +02:00
FlightControl
1c559110a2 Merged 'develop'. 2019-08-07 16:08:10 +02:00
Frank
6f170db344 Update SWAPR.lua 2019-08-05 21:37:28 +02:00
Frank
27a1401ae1 SWAPR v0.0.2 2019-08-05 18:04:59 +02:00
Frank
debf784d0c SWAPR v0.0.1 2019-08-05 11:15:45 +02:00
FlightControl
5a97f68778 Escort menus are deleted once the escort crashes. 2019-08-02 21:10:56 +03:00
FlightControl
d2ef75b22c Fixed nasty bug where the same plane would be chosen all the time for request spawns. Root cause was the inherit. self variables were also copied during inherit of the chile object, resulting in a bad bad bad behaviour.
Also cleaned up the menus once the player exists the client. When rejoining, it will only see the menu of the current or requested escorts.
I still need to fix a method to renew unused (but alive and intact) escorts for gamey play.
2019-08-01 21:17:46 +03:00
FlightControl
115743263b Fixed nasty bug where the same plane would be chosen all the time for request spawns. Root cause was the inherit. self variables were also copied during inherit of the chile object, resulting in a bad bad bad behaviour. 2019-08-01 21:16:01 +03:00
FlightControl
59447999f0 Updates 2019-08-01 18:29:57 +03:00
Frank
4554ac1f3e Merge pull request #1188 from FlightControl-Master/FF/Develop
AIRBOSS v1.0.6
2019-07-28 22:49:25 +02:00
Frank
d08d8db298 AIRBOSS v1.0.6
- Added Marshal radial to skipper menu.
- Adjusted grading and groove time start for LUL issue.
2019-07-28 22:43:10 +02:00
Frank
099f95f3cc Merge pull request #1187 from FlightControl-Master/FF/Develop
Misc Updates and Fixes
2019-07-22 21:45:06 +02:00
Frank
20cf69c182 Update Airboss.lua 2019-07-22 21:20:05 +02:00
Frank
f931af2ee9 Updates 2019-07-22 21:06:12 +02:00
Frank
96fe9d51d6 Typo bug fix 2019-07-22 16:55:17 +02:00
Frank
d264c6f6a5 Update RecoveryTanker.lua 2019-07-21 23:59:35 +02:00
Frank
e17e635710 Updates 2019-07-21 23:59:05 +02:00
Frank
d4b9fc9e40 Updates 2019-07-21 23:35:09 +02:00
Frank
d66672a29c Merge pull request #1184 from FlightControl-Master/FF/Develop
Updates
2019-07-12 22:27:17 +02:00
Frank
345b0055f3 Update Fox.lua 2019-07-12 21:53:52 +02:00
Frank
7fdc049079 Update Fox.lua 2019-07-12 20:39:07 +02:00
Frank
5caab9c6f3 FOX
- adjustment of destruction distance
- tracking missiles with "unknown" target
2019-07-12 18:38:13 +02:00
Frank
03042c8282 AIRBOSS v1.0.4
One stack per flight group in CASE II/III
2019-07-12 09:41:13 +02:00
Frank
c46028ff2d FOX v0.6.0
FOX v0.6.0
- Missile target constantly updated.
- Increased safety distance to 200 m.
- Added safety distance for BIG missiles > 50 kg TNT as 400 m.
- More output to dcs.log file.

SPAWN
- enabled uncontrolled

UTILS
- LLDMS accurracy fix.

AI_A2A_DISPATCHER
- Squadron visible improvements

ZONE
- Added MarkZone function for F10 zone markings.

AI_PATROL_ZONE
- Some WP code improvements.
2019-07-10 22:29:49 +02:00
Frank
bd83344c21 Merge pull request #1179 from FlightControl-Master/FF/Develop
CAP
2019-07-06 23:43:21 +02:00
Frank
183c05bcf5 CAP
AI_A2A_PATROL
- Added CAP coordinates option.

AI_A2A_DISPATCHER
- Added CAP coordinates option.
2019-07-06 23:39:48 +02:00
Frank
a94591d811 Merge pull request #1178 from FlightControl-Master/FF/Develop
CAP
2019-07-06 20:27:50 +02:00
Frank
6973e8c028 CAP
AI_A2A_PATROL:
- Added optional race track pattern.

AI_A2A_DISPATCHER:
- Added option for CAP patrolling in a race track pattern.
2019-07-06 20:23:07 +02:00
Frank
ab0b3f9b91 Merge pull request #1177 from FlightControl-Master/FF/Develop
Fixes and Docs
2019-07-05 20:47:12 +02:00
Frank
6f140be710 Update
A2A_DISPATCHER
- Docs

CONTROLLABLE
- OptionROEOpenFireWeaponFree()
- Fixed OptionRTBBingoFuel()
2019-07-05 20:01:26 +02:00
Frank
8780ec3687 Docs
Updated docs and function argument description of DETECTION and A2A_DISPATCHER.
2019-07-03 22:13:56 +02:00
FlightControl
1afe1c48b4 Updates - Much improved version! 2019-07-01 17:29:23 +03:00
Frank
14b834e8ad Merge pull request #1175 from FlightControl-Master/FF/Develop
SPOT
2019-06-30 20:07:38 +02:00
Frank
0b53bd8f7e Merge branch 'develop' into FF/Develop 2019-06-30 17:34:22 +02:00
Frank
376a3553fa SPOT
- Fixed bug in LaseOnCoordinate.
- Minor stuff.
2019-06-30 17:34:00 +02:00
FlightControl
35653898cd WIP 2019-06-28 17:15:40 +03:00
Frank
ab1ba9595d Merge pull request #1174 from FlightControl-Master/FF/Develop
MISC Fixes and Improvements
2019-06-26 19:56:15 +02:00
Frank
2a7b9cf898 DETECTION, SET, ARTY, WAREHOUSE, AIRBOSS, A2A_DISPATCHER
DETECTION
- Fixed bug with late activated groups.

ARTY v1.1.2
- Added attack group task for ships.
- Added respawn option.

WAREHOUSE v0.9.5
- Added respawn option.

AIRBOSS v1.0.3
- Recovery time extended if flights are still in the pattern.

SET
- Added CountAlive() function.

A2A_DISPATCHER
- Bug fixes in :ParkDefender() function.
2019-06-26 19:19:17 +02:00
Frank
1224fb2d5a Merge branch 'develop' into FF/Develop 2019-06-24 23:17:32 +02:00
Frank
ac1d07ec66 updates 2019-06-24 23:17:18 +02:00
Frank
1a534108bf Updates
ARTY v1.1.0
- added attack group
- target data struckture

WAREHOUSE v0.9.4
- assignment as descriptor

AIRBOSS v1.0.3
- removed warehouse table
- no stoping of recovery window if pattern is not empty
- changes in patrol route
2019-06-24 14:19:36 +02:00
Frank
c88f7bede5 SPOT
LaseCoordinate
Alarm state ships
zonRadius in task fire at point.
2019-06-24 13:38:01 +02:00
Frank
598ef58849 Merge pull request #1172 from FlightControl-Master/FF/Develop
Rescue Helo v1.0.7
2019-06-21 20:04:00 +02:00
Frank
2a485ef85a Rescue Helo v1.0.7
RESCUEHELO v1.0.7
Compatibility fix for AI_FORMATION changes.
2019-06-21 19:57:55 +02:00
Frank
98ce42e9aa Merge branch 'develop' into FF/Develop 2019-06-21 17:26:10 +02:00
FlightControl
3612d5d171 Fixes 2019-06-20 21:50:52 +03:00
FlightControl
83f8e76584 Fixed error in SPAWN logic. Sorry. fixed now. 2019-06-20 20:42:47 +03:00
FlightControl
a108751e0c Updates 2019-06-19 18:32:39 +03:00
FlightControl
6f4d6c3daf Updates 2019-06-19 18:32:21 +03:00
FlightControl
a907369082 FlightMode 2019-06-19 18:17:12 +03:00
FlightControl
bc9060adfc Initial flight mode is formation. 2019-06-19 18:13:00 +03:00
FlightControl
4296dbade7 Small fixes 2019-06-19 06:57:40 +03:00
FlightControl
b1cee9969f Fixes 2019-06-19 05:17:51 +03:00
FlightControl
9c0c4a11b1 Finish Feature-Escort 2019-06-18 20:22:38 +03:00
FlightControl
b4324cb057 Optimized inclination for formation flying. 2019-06-18 20:18:43 +03:00
FlightControl
1d4dad0180 Updates 2019-06-18 20:03:24 +03:00
FlightControl
1e0e67c13d Updates 2019-06-18 07:06:00 +03:00
FlightControl
9c523cad52 Updates 2019-06-17 20:25:50 +03:00
FlightControl
6a1bf50700 Updated 2019-06-17 10:19:15 +03:00
FlightControl
d34cff1675 Temporary 2019-06-17 09:00:45 +02:00
FlightControl
0a027caa47 Test 2019-06-15 12:47:24 +02:00
Frank
10bfefdd06 pseudoATC
- Fixed bug for ReportBR.
2019-06-08 23:06:45 +02:00
Frank
ead1aed8d5 Merge pull request #1167 from FlightControl-Master/FF/Develop
Airboss v1.0.2
2019-06-04 17:39:33 +02:00
Frank
bc229f1a86 Update Warehouse.lua 2019-06-04 17:36:23 +02:00
Frank
94f9cb4a79 Update Airboss.lua 2019-06-03 23:18:54 +02:00
Frank
acd5b1bdca minor 2019-06-03 22:58:53 +02:00
Frank
9fed91b16f AIRBOSS v1.0.2
- Added skipper menu.
- Added turn help waypoints.
2019-06-02 23:12:08 +02:00
Frank
1627c92460 Airboss v1.0.1 2019-06-01 23:25:44 +02:00
FlightControl
7bae22248a Optimizations ROE and ROT and flight modes. 2019-06-01 08:06:18 +02:00
FlightControl
7b5b57c087 updates 2019-05-31 16:35:35 +02:00
Frank
271217cf04 Update Warehouse.lua 2019-05-28 23:58:14 +02:00
FlightControl
c5236d337e Updates 2019-05-28 20:44:51 +03:00
Frank
747bb949da Merge pull request #1164 from FlightControl-Master/FF/Develop
WH v0.9.1
2019-05-28 19:04:52 +02:00
Frank
a53bb1aac6 WH v0.9.1
- Added request as parameter in AssetSpawned event.
2019-05-28 00:24:47 +02:00
FlightControl
d3ecbac40a Updates for mission mode. 2019-05-27 17:10:19 +02:00
Frank
28fe8c5a0e Merge pull request #1162 from FlightControl-Master/FF/Develop
RANGE 2.1.2
2019-05-27 14:32:57 +02:00
Frank
6d79bbfb45 RANGE 2.1.2
fixes
2019-05-27 14:15:17 +02:00
Frank
944c76c250 Merge pull request #1161 from FlightControl-Master/FF/Develop
FOX Missile Trainer and Misc
2019-05-26 21:30:15 +02:00
Frank
256ef2c7b4 Update Warehouse.lua 2019-05-26 21:25:11 +02:00
thebgpikester
2a0ae95e3e Merge pull request #1160 from FlightControl-Master/wingthor_fixes
Wingthor fixes
2019-05-26 16:06:01 +01:00
Wingthor
604ff07933 fix issue #948 in Scoring.lua
Changed ScoreGoals to PenaltyGoals in function CORING:ReportScoreAllSummary( PlayerGroup )
2019-05-26 16:49:56 +02:00
thebgpikester
be8fc4e2e6 Merge pull request #1151 from TommyC81/patch-1
Corrected AIRBASE name comment.
2019-05-26 15:31:07 +01:00
thebgpikester
e92a625c5c Merge pull request #1142 from FlightControl-Master/wingthor_fixes
Wingthor fixes
2019-05-26 15:29:09 +01:00
Frank
e141823556 FOX Missile Trainer
FOX v0.5.0
- Added new missile trainer.
RANGE v2.1.1
- Fixed bugs.
WAREHOUSE v0.9.0
- Added events AssetSpawned and AssetLowFuel
- Improved Arrived function.
2019-05-26 12:15:21 +02:00
Frank
fbd0e9839d Merge pull request #1159 from FlightControl-Master/FF/Develop
Fixed Database OnEventNewZone function
2019-05-23 21:16:21 +02:00
Frank
889ce8f64b Fixed Database OnEventNewZone function 2019-05-23 21:10:48 +02:00
FlightControl
6be56b1b86 Progress 2019-05-23 16:33:47 +03:00
FlightControl
78a5e89928 Synth 2019-05-17 21:12:02 +02:00
FlightControl
e09a02ec14 Escort menus reworked. It took a while (sorry), was involved in many projects in RL. 2019-05-13 11:22:49 +02:00
Frank
32ff4db36a Merge pull request #1154 from FlightControl-Master/FF/Develop
MISC
2019-05-07 22:06:42 +02:00
Frank
9bb1c83999 MISC
RANGE v2.1
- Added events when players enter/exit range zone.
- misc

RADIO
- Added RADIOQUEUE class.

WAREHOUSE v0.7.0
- Fixed bug in aircraft speed.
- Decreased output if queues are empty.
2019-05-07 22:00:07 +02:00
FlightControl
93951dd4b4 WIP, not for playing. 2019-05-04 07:10:09 +02:00
FlightControl
1440f6c626 WIP, not for play. 2019-05-03 19:11:53 +02:00
Frank
aacf83fd11 Merge pull request #1153 from FlightControl-Master/FF/Develop
RANGE 2.0
2019-05-01 18:00:56 +02:00
Frank
6c35892f05 RANGE 2.0
Range v2.0.0
- Turned into FSM.
- Added radial to bomb impact info.
- Added option to add coordinates as bomb target.
- Added option to save bombing results to file.

AIRBOSS v1.0.0
- Added option to disable welcome message.
- Increased output load/save.
2019-05-01 17:56:25 +02:00
FlightControl
3657a19645 Updates of working version. 2019-04-26 18:42:42 +02:00
FlightControl
ab423b3ba4 First working version of AI_ESCORT_REQUEST. 2019-04-25 18:52:46 +02:00
FlightControl
b4d6f78e42 WIP, need to make a new class next week, AI_ESCORT_REQUEST. Need to solve the issue with the formation flying uof multiple escort classes working together. Planes may not crash into each other. 2019-04-25 08:59:07 +03:00
TommyC81
49217291ad Corrected AIRBASE name comment.
Corrected array name for Bandar-e-Jask Airfield.
2019-04-24 17:22:41 +04:00
FlightControl
315d6bf82a ParkAtAirbase new method. 2019-04-23 20:33:21 +03:00
Frank
486fc83323 Merge pull request #1149 from FlightControl-Master/FF/Develop
Ops and Range
2019-04-23 19:17:49 +02:00
Frank
3dbb013e16 Ops and Range
RANGE v1.3.0
- Generalized weapon handling.
- Decreased time before weapon tracking starts from 1.0 to 0.1 sec.

RESCUE HELO v1.0.6
- Got rid of IsAlive() check in rescue operation.

AIRBOSS v0.9.9.9.9
- Fixed bug in trap sheet output.
2019-04-23 19:13:03 +02:00
FlightControl
b21ff707ba Updates escort before plane trip. 2019-04-23 19:59:11 +03:00
FlightControl
d985d9a41d WIP 2019-04-16 19:49:46 +02:00
Frank
178ee09614 Merge pull request #1145 from FlightControl-Master/FF/Develop
Persian Gulf Airbases Update (Final)
2019-04-13 10:15:31 +02:00
Frank
95b67550eb Persian Gulf Airbases Update (Final)
Thanks to Shadowze.
2019-04-13 10:07:55 +02:00
Frank
48e0f8add6 Merge pull request #1144 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.8
2019-04-12 22:23:07 +02:00
Frank
d4f6ba2bdb Airboss v0.9.9.8 2019-04-12 21:59:53 +02:00
Frank
e26b2f4919 Merge branch 'develop' into FF/Develop 2019-04-12 17:57:15 +02:00
Frank
208761fa46 Static and Delay 2019-04-11 21:46:54 +02:00
FlightControl
03c47cc9f9 documentation 2019-04-10 18:49:05 +02:00
funkyfranky
eb62810911 Range 2019-04-10 16:24:45 +02:00
FlightControl
bf449492b5 Updated approach speed logic to prevent airplanes to fly beyond the hold position. 2019-04-09 17:45:29 +02:00
funkyfranky
4c08002d3e AB 0998 2019-04-09 14:56:20 +02:00
FlightControl
5e67861ea9 Improvements 2019-04-08 17:55:24 +02:00
FlightControl
c657ba9218 Added different formations to the menus. 2019-04-08 06:42:00 +02:00
FlightControl
ddfc22bb50 wip 2019-04-08 06:41:22 +02:00
FlightControl
f9f4b3a9fd wip 2019-04-08 05:39:57 +02:00
FlightControl
0e14624d39 wip 2019-04-07 11:07:20 +02:00
FlightControl
f1d217b6d7 wip 2019-04-07 08:31:22 +02:00
Wingthor
8126b198ba Fixed bug with SPAWN:GetLastAliveGroup() issue 1141
Changed line 2208
2019-04-07 02:56:41 +02:00
Wingthor
c2b46ceea1 Fixes GitHub issue 1140
SPAWN: GetLastAliveGroup() #1140 generating a nil value...

  "attempt to index field 'SpawnTemplatePrefixself' (a nil value)"
2019-04-07 02:04:08 +02:00
FlightControl
fcceb278ec First version 2019-04-06 16:34:00 +02:00
FlightControl
7a89960d21 Updates 2019-04-06 16:33:22 +02:00
FlightControl
4abdad5f35 First version 2019-04-06 13:04:28 +02:00
Frank
6593a262bb Update Airboss.lua 2019-04-03 23:36:22 +02:00
Frank
b2f6b141d4 Merge pull request #1138 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.7
2019-04-02 21:29:36 +02:00
funkyfranky
4bcf43122f AB 2019-04-02 16:27:41 +02:00
Frank
70f30d2c53 AIBOSS v0.9.9.7 2019-04-01 18:28:17 +02:00
Frank
a29a8d7d81 AIBOSS v0.9.9.7 2019-04-01 18:17:52 +02:00
Frank
77f1bb4fdd Merge branch 'develop' into FF/Develop 2019-03-31 21:58:28 +02:00
Frank
53166b555f AIRBOSS v0.9.9.7 2019-03-31 21:55:30 +02:00
funkyfranky
5f58fd3550 AB 0996w 2019-03-28 16:35:38 +01:00
Frank
b7b6034d76 AIRBOSS v0.9.9.6w 2019-03-27 23:29:41 +01:00
FlightControl
59974ea33e New Zone Detection module. 2019-03-27 19:18:12 +01:00
funkyfranky
e4935d8717 Merge branch 'FF/Develop' of https://github.com/FlightControl-Master/MOOSE into FF/Develop 2019-03-27 16:24:17 +01:00
funkyfranky
de0c8e3789 AB 0.996w 2019-03-27 16:23:39 +01:00
FlightControl
bd290ffdab Fixing hit problem in multi player for database when hitting static objects. 2019-03-23 11:24:04 +01:00
FlightControl
bfb11b6f79 Updates on goal logic 2019-03-23 10:45:41 +01:00
Frank
8667c98449 Merge branch 'develop' into FF/Develop 2019-03-22 22:43:57 +01:00
Frank
4c72496599 Merge pull request #1133 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.6
2019-03-22 22:42:40 +01:00
Frank
aa8498ce0b AIRBOSS v0.9.9.6 2019-03-22 22:05:25 +01:00
funkyfranky
ec155ed80c AB v0.9.9.6w 2019-03-22 16:23:22 +01:00
FlightControl
f849732f55 Various improvements 2019-03-21 07:01:24 +01:00
FlightControl
9856a8625d Goal 2019-03-21 06:57:04 +01:00
FlightControl
b1e99dc317 Goal 2019-03-21 06:31:13 +01:00
FlightControl
310ba9bb8a Reverse 2019-03-21 06:18:48 +01:00
FlightControl
565a3062cb Fixing goal state machine rule 2019-03-21 06:11:59 +01:00
FlightControl
e8b74f0fc7 handling static in scan 2019-03-21 06:03:13 +01:00
FlightControl
ca65154ecd Fixes 2019-03-21 05:49:48 +01:00
FlightControl
e996a30333 Updates 2019-03-21 05:21:16 +01:00
Frank
e33a38556e Merge pull request #1132 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.5
2019-03-20 22:17:15 +01:00
Frank
3f2b3e0e1a AIRBOSS v0.9.9.5
- Added Raynor LSO/Marshal voice over parameters.
- Added nil checks for events.
2019-03-20 21:59:32 +01:00
Frank
dab0aef1e3 AIRBOSS v0.9.9.5 2019-03-20 21:57:18 +01:00
FlightControl
d9fb9c4928 Report Mission fix 2019-03-20 18:36:55 +01:00
FlightControl
964a6d0708 Debug 2019-03-20 18:15:37 +01:00
FlightControl
985282ba55 Debug 2019-03-20 18:03:24 +01:00
funkyfranky
8534afe0e3 AB 2019-03-20 16:22:00 +01:00
Frank
a70fa066fe AB 0995 2019-03-20 00:12:04 +01:00
FlightControl
3f8468dcc7 Fixed shortest distance to airbase. 2019-03-19 21:50:34 +01:00
FlightControl
d9d9235fba Great new additions:
- Defense tactics (SetDefenseApproach) now can be:
  AI_A2G_DISPATCHER.DefenseApproach.Random -- random detected item
  AI_A2G_DISPATCHER.DefenseApproach.Distance -- shortest distance between defense point and detected item.
2019-03-19 19:34:28 +01:00
funkyfranky
5e20b244c5 AB 0995w 2019-03-19 16:32:43 +01:00
Frank
40ebc4e990 AB v0.9.9.5 2019-03-19 01:18:34 +01:00
FlightControl
70e7857b62 Implemented the linking of TASK_CAPTURE_DISPATCHER and AI_A2G_DISPATCHER. 2019-03-18 19:53:12 +01:00
FlightControl
02a486e457 In progress. 2019-03-18 18:04:08 +01:00
FlightControl
9e09a5bac9 First version of new Feature-Detection-Zones 2019-03-17 08:26:16 +01:00
FlightControl
ece7fc3ea9 Threat level report for TASK_CAPTURE_ZONE 2019-03-17 08:20:42 +01:00
FlightControl
0ea5c7fa48 Updated bug in Detection and other classes. Now only alive units are considered when broadcasting a message to a SET_GROUP. Made DESIGNATE crash in certain cases. 2019-03-17 07:33:28 +01:00
Frank
3f64a1b33f Merge pull request #1130 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.4
2019-03-16 22:38:23 +01:00
Frank
02ee12402a AIRBOSS v0.9.9.4
**AIRBOSS v0.9.9.4**
- Script now supports human RIOs.
- Added optional prefix for trap sheet files.
- Fixed invalid characters in trap sheet file name cause script to crash (untested). Data will not be saved at all.
- Inverted sign of line up error in trap sheet data to make it more intuetive.
- Player who leave are deleted from self.players table.
2019-03-16 22:34:03 +01:00
FlightControl
837361e899 Updates 2019-03-16 12:57:14 +01:00
FlightControl
cff4f60923 Improvements on reporting. 2019-03-16 12:13:39 +01:00
FlightControl
fb23ac1d55 Updates to make the tasking work correctly. 2019-03-16 09:51:09 +01:00
FlightControl
87a44f7f7f Still need to fix that index issue. 2019-03-15 13:08:48 +01:00
FlightControl
3b520ab0c4 New detection method based on zones and scanning. 2019-03-15 12:39:06 +01:00
FlightControl
044b5cba7c Updates 2019-03-15 11:01:06 +01:00
FlightControl
0f9591464a First version 2019-03-15 09:57:50 +01:00
Frank
6a301060bf Merge pull request #1129 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.3
2019-03-13 20:56:46 +01:00
Frank
1e0b51a0a9 AIRBOSS v0.9.9.3
- Added F-14 support.
2019-03-13 20:52:57 +01:00
FlightControl
e19f0402da Only auto assign tasks to planned; replanned or assigned tasks. 2019-03-10 16:48:38 +01:00
FlightControl
925ce3ad63 Auto assignment of tasks with prioritization of an auto assign method. The default assign method is random, which is set at the command center. Each task type implements a prioritization mechanism which calculates the priotity of the task based on various methods: random, distance or priority. The distance is calculated from the task coordinate from the command center location.
Also fixed in a first try the route bug on Controllables.
2019-03-10 16:41:53 +01:00
FlightControl
afc918c5e5 Updates in defense system. Added SetSquadronEngageProbability method to decide per squadron what is the probability when it will engage. 2019-03-09 17:29:18 +01:00
FlightControl
10b96f6cce Updates to decision engine of the AI_A2G_DISPATCHER 2019-03-09 10:09:03 +01:00
FlightControl
d72e89d52b Fixes in AI_A2G_DISPATCHER 2019-03-09 06:32:11 +01:00
FlightControl
ef1a9330a4 Updates, many fixes. Now also the communication to the players is working. 2019-03-08 10:03:58 +01:00
FlightControl
eee1aca14f Disabled SetSquadronVisible due to a bug fix that is required. 2019-03-07 14:02:49 +01:00
FlightControl
c8c95e063d Now the task routing is also working towards the capture zones. 2019-03-07 09:54:33 +01:00
FlightControl
0af9e57b82 Now the task routing is also working towards the capture zones. 2019-03-07 09:54:15 +01:00
FlightControl
e0899a8a9d Merge remote-tracking branch 'origin/develop' into develop 2019-03-06 22:41:38 +01:00
FlightControl
093dadc0f6 Finish Task_zone_capture_dispatcher 2019-03-06 22:40:59 +01:00
FlightControl
23f92e49dd New TASK CAPTURE DISPATCHER class.
This class allows to send capture tasks to players.
It needs a ZONE_CAPTURE_COALITION class to function.
2019-03-06 22:40:44 +01:00
Frank
d1c909e9ed Merge pull request #1128 from FlightControl-Master/FF/Develop
RAT v2.3.7
2019-03-05 23:24:26 +01:00
Frank
5dbc3b1ba1 Update RAT.lua 2019-03-05 23:09:43 +01:00
Frank
85febea3d9 RAT v2.3.7
Added EPLRS datalink option.
2019-03-05 23:07:35 +01:00
FlightControl
dca772cac6 Automatic assignment of tasks; and automatic accepts of tasks. Documented. Tested. Works. 2019-03-05 09:56:51 +01:00
FlightControl
26347ce779 Automatic assignment of tasks; and automatic accepts of tasks. Documented. Tested. Works. 2019-03-05 09:56:19 +01:00
Frank
b49f3c5b0c Merge pull request #1126 from FlightControl-Master/FF/Develop
Carrier Ops Update
2019-03-04 23:18:09 +01:00
Frank
79be7cbd2c Update Airboss.lua 2019-03-04 23:08:59 +01:00
FlightControl
6fe8667885 Some important fixes that improves the routing of the engagements of aircraft. when ground forces move, the aircraft engaging will approach correctly.
also added the squaddrons with resources in the tactical information panel.
and for patrols, now the amount of resources are correctly calculated.
2019-03-04 20:27:40 +01:00
Frank
b56d62c777 Carrier Ops Update
AIRBOSS v0.9.9.2
RECOVERYTANKER v1.0.7
2019-03-04 17:50:45 +01:00
FlightControl
6f13aa2c5b These changes will blast the hell out of A2G... this is going to be great. 2019-03-03 19:46:51 +01:00
Frank
d4510af249 Merge pull request #1125 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9.1
2019-02-28 23:15:12 +01:00
Frank
fb784a9a96 AIRBOSS v0.9.9.1 2019-02-28 23:06:03 +01:00
Frank
25c7b1ddac AIRBOSS v0.9.9.1 2019-02-28 22:38:17 +01:00
Frank
71c7e15fc3 Update Airboss.lua 2019-02-27 23:32:46 +01:00
funkyfranky
e6e0cf5977 AB 0991 2019-02-27 16:33:24 +01:00
funkyfranky
775757e657 AB Docs 2019-02-26 16:58:57 +01:00
Frank
1a303ec7c2 Merge pull request #1123 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.9
2019-02-25 20:34:16 +01:00
Frank
c16edd65b0 AIRBOSS v0.9.9 2019-02-25 20:23:03 +01:00
FlightControl
1ff41ec7ec Improvements 2019-02-25 19:16:18 +01:00
FlightControl
d874456361 Optimized attack. Prevent airborne defenders to fly over enemy zones to target areas behind enemy lines! This is an important feature. I still need to optimize the attack for SEAD; so when SEAD is behind enemy lines and crossing an enemy area that is in line of the attack; the SEAD won't be engaged. Further optimization is required, but it works already quite well! 2019-02-23 14:57:06 +01:00
Frank
6853eecffa Merge pull request #1122 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.8
2019-02-21 10:20:59 +01:00
Frank
71001adc5e AIRBOSS v0.9.8 2019-02-21 09:55:57 +01:00
Frank
b6a19d34c6 AIRBOSS v0.9.8 2019-02-20 09:41:16 +01:00
FlightControl
1364115b07 Updates in docs 2019-02-19 17:48:57 +01:00
Frank
48c7254c84 Update Airboss.lua 2019-02-19 10:41:58 +01:00
Frank
60b6d2ade0 Update Airboss.lua 2019-02-17 23:16:07 +01:00
funkyfranky
df43ff0841 AB v0.9.8w 2019-02-15 17:00:22 +01:00
Frank
5ee0ae44cd Update Airboss.lua 2019-02-15 01:23:42 +01:00
funkyfranky
9df35840fe AB v0.9.8w 2019-02-14 16:54:59 +01:00
Frank
cff9557217 AB v0.9.8 wip 2019-02-14 00:53:22 +01:00
funkyfranky
b5b6d398ae AB 0.9.8w 2019-02-13 16:46:15 +01:00
Frank
aba0c19215 AB v0.9.8wip 2019-02-13 00:15:31 +01:00
funkyfranky
5050371e0d AB v0.9.8w 2019-02-12 16:26:44 +01:00
Frank
81b36ef37d Merge pull request #1121 from FlightControl-Master/FF/Develop
AIBOSS v0.9.7
2019-02-11 13:57:48 +01:00
Frank
59e219e3d6 WAREHOUSE v0.6.9
Fixed memory issue.
2019-02-11 12:31:28 +01:00
Frank
412d9e7a82 AIRBOSS v0.9.7 2019-02-11 12:24:52 +01:00
funkyfranky
d01b55c790 AB v0.9.7w 2019-02-08 16:41:23 +01:00
Frank
5ed768065c Update Airboss.lua 2019-02-08 01:04:07 +01:00
funkyfranky
e64703de03 AB v0.96w 2019-02-07 16:16:17 +01:00
Frank
71126eb34d AIRBOSS v0.96 2019-02-06 23:50:41 +01:00
funkyfranky
6992e47a66 AB 0.95wip 2019-02-06 16:16:29 +01:00
Frank
70d1f96681 AB v0.95wip 2019-02-06 00:12:24 +01:00
funkyfranky
83ed456c29 AB 0.9.5wip 2019-02-05 16:41:20 +01:00
Frank
26607776a2 Merge pull request #1120 from FlightControl-Master/FF/Develop
AIRBOSS v0.95
2019-02-04 23:21:24 +01:00
Frank
7a075e73ca AIRBOSS v0.95
- Fixed bug in counting units
- Carrier will only turn into the wind if wind > 0.1 m/s
2019-02-04 23:15:12 +01:00
Frank
6c15091df2 Merge pull request #1119 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.4
2019-02-04 20:37:32 +01:00
Frank
e063379cd8 AIRBOSS v0.9.4
- Added AV-8B and USS Tarawa.
- Corrected A-4E AoA threshold.
- Other minor adjustments.

RADIO: Fixed frequency check.
2019-02-04 20:34:45 +01:00
Frank
3a49f2258b Merge pull request #1118 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.3
2019-01-31 21:55:42 +01:00
Frank
dddf056b20 AIRBOSS v0.9.3
- Added tower frequency.
- Added AI exclude set.
2019-01-31 21:54:44 +01:00
Frank
bdbfed13ff Merge pull request #1114 from 131stGutts/Functional-PseudoATC_Allow_Human_Group
Fix bug when 2 humans are in the same group of planes
2019-01-31 19:15:45 +01:00
Frank
9eb0800b66 Merge pull request #1113 from 131stGutts/Functional-ATC_Ground_AlertFreqCustom_PersianGulf
Functional atc ground alert freq custom persian gulf
2019-01-31 18:26:21 +01:00
Frank
713ec61031 Merge branch 'develop' into Functional-ATC_Ground_AlertFreqCustom_PersianGulf 2019-01-31 18:25:25 +01:00
Frank
264e84649e Merge pull request #1112 from FlightControl-Master/FF/Develop
Carrier Ops
2019-01-31 14:21:49 +01:00
Frank
c4016cfa4d RESCUEHELO v1.0.5
- Added Returned event.
- Fixed automatic RTB on low fuel bug.
2019-01-31 13:43:17 +01:00
Frank
1cb248692b CONTROLLABLE
Setting output of SetTask from self:I() to self:T(). AI_FORMATION class (used by RESCUEHELO) calls the function twice per second and is spamming the DCS log file with messages and causing large file I/O.
2019-01-30 20:18:47 +01:00
Frank
331315a83e Merge branch 'develop' into FF/Develop 2019-01-30 20:11:11 +01:00
Frank
e325b2192b AIRBOSS, RECOVERYTANKER, RESCUEHELO, RANGE
AIRBOSS v0.9.3
RECOVERYTANKER v1.0.6
RESCUEHELO v1.0.4
RANGE v1.2.4
SPAWN added modex
GROUP added modex respawn
2019-01-30 20:10:41 +01:00
FlightControl
86094bc6d2 Improvements in the attack methods! This is going to be great!
# Conflicts:
#	Moose Development/Moose/AI/AI_A2G_Dispatcher.lua
2019-01-29 20:51:03 +01:00
FlightControl
f2b2b1974d Improvements in the attack methods! This is going to be great! 2019-01-29 19:58:06 +01:00
Frank
080c496cc3 Merge pull request #1111 from FlightControl-Master/FF/Develop
RECOVERYTANKER v1.0.5
2019-01-29 17:11:58 +01:00
Frank
bcfa0f9ac5 RECOVERYTANKER v1.0.5
Recoverytanker:
- Added AWACS roll
- Added function to set callsign

UTILS
- Added callsigns enumerator

CONTROLLABLE
- Added command SetCallsign
2019-01-29 17:07:48 +01:00
Frank
746c0d25f7 Merge pull request #1110 from FlightControl-Master/FF/Develop
RECOVERYTANKER v1.0.4
2019-01-28 22:07:30 +01:00
Frank
9e450ce76b RECOVERYTANKER v1.0.4
- Fixed PG airbase enumerator names
- Rescuehelo v1.0.3
- minor stuff
2019-01-28 22:03:21 +01:00
FlightControl
77bee89ea5 Updates 2019-01-27 17:19:41 +01:00
Frank
dbc80183e6 Merge pull request #1109 from FlightControl-Master/FF/Develop
CONTROLLABLE
2019-01-27 13:09:33 +01:00
Frank
ff0371faa5 CONTROLLABLE
Fixed task bombing runway.
2019-01-27 13:07:48 +01:00
Frank
ebaddb3fe4 Merge pull request #1107 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.2
2019-01-26 23:30:29 +01:00
Frank
8f3aeeb182 AIRBOSS v0.92 2019-01-26 23:12:24 +01:00
Frank
3157e4f986 Merge branch 'develop' into FF/Develop 2019-01-23 11:24:31 +01:00
Frank
0fe18ee5b4 Merge pull request #1104 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.1
2019-01-23 11:09:11 +01:00
Frank
265d6da331 AIRBOSS v0.9.1 2019-01-23 11:00:36 +01:00
FlightControl
1c063ca308 Optimizations 2019-01-22 08:51:17 +01:00
FlightControl
5a5340431e Improvements ...
-- Implementation of queueing of aircraft launches.
-- Fixed the RTB bug resulting in "lost control".
-- Communication of AI to players (first version).
-- Aircraft engage distance calculation for each wave.
2019-01-20 17:03:18 +01:00
Frank
869a8a57e1 Merge pull request #1103 from FlightControl-Master/FF/Develop
AIRBOSS v0.9.0 and other fixes/improvements
2019-01-20 02:33:05 +01:00
Frank
dd15da28b2 AIBOSS v0.9.0
SPAWN: Added delay for respawn on landing.
RAT v2.3.5: Fixed bug for Vmax cruise.
RECOVERYTANKER v1.0.3
RESCUEHELO v1.0.2
POSITIONABLE: Added GetOffsetCoordinate function
UNIT: Added Explode function.
2019-01-20 02:02:22 +01:00
131stGutts
47778c6fe7 Set the time frequency of alert with PersianGulf
Add new functions "Start" for
  ATC_GROUND_CAUCASUS,
  ATC_GROUND_NEVADA,
  ATC_GROUND_NORMANDY,
  ATC_GROUND_PERSIANGULF

 which accept none or one parameter for setting the SCHEDULER frequency.

If none defined, value set before (0.05) is used.
The SCHEDULER is not anymore in "New" functions of ATC_GROUND_XXX but in "Start"

Usages:
atcGroundCaucasus=ATC_GROUND_CAUCASUS:New()
atcGroundCausasus:Start()

or

atcGroundCaucasus=ATC_GROUND_CAUCASUS:New()
atcGroundCausasus:Start(0.5)

Relative to PullRequest #1094, #1096, #1098
Include grammar issues fix from ticket #753
2019-01-13 16:27:58 +01:00
131stGutts
0d90eed270 Implements PersianGulf ATC_Ground supervisor
All Persian Gulf airbases and runways added so ATC_Ground supervisor
Minor correction to comment for Normandy (saying Caucasus)
2019-01-13 12:45:21 +01:00
Frank
d8147348c6 Merge pull request #1097 from FlightControl-Master/FF/Develop
AIRBOSS v0.8.1
2019-01-13 00:28:42 +01:00
Frank
6b85141a39 AIRBOSS v0.8.1
* Persistence
* Fixed menu mark zones bug
* minor things
2019-01-13 00:23:27 +01:00
131stGutts
0fcc379ede Fix bug when 2 humans are in the same group of planes
If more than one human is in a plane's group, the menu bug and a lot of PSEUDOATC menu are shown.
The new menu structure is:
- F10
-  PSEUDOATC
-   <PlayerName>
-    <PSEUDOATC Functions>

For achieving that goal, the self structure has changed from self.player[GID] to self.group[GID].player[UID]. Doing that way, if the last player pit leave the group this functionality do not generate a nil error anymore.
Some text have been changed to indicate CallSign or PlayerName.
2019-01-12 21:35:05 +01:00
Frank
f22f62339f Merge pull request #1092 from FlightControl-Master/FF/Develop
AIRBOSS v0.8.0
2019-01-10 17:50:19 +01:00
Frank
934b9d9cf1 Merge branch 'develop' into FF/Develop 2019-01-10 17:26:57 +01:00
Frank
47dd068655 AIRBOSS v0.8.0 2019-01-10 17:26:37 +01:00
FlightControl
382dfd797c Optimizations A2G 2019-01-09 21:31:22 +01:00
FlightControl
8e66031afe Optimizations A2G 2019-01-09 21:30:21 +01:00
FlightControl
eea1d56468 Optimizations A2G 2019-01-09 21:29:45 +01:00
FlightControl
6edcc58b9a updates 2019-01-09 07:36:36 +01:00
Frank
7a8197208c Merge pull request #1087 from FlightControl-Master/FF/Develop
AIRBOSS v0.7.2
2019-01-05 10:33:23 +01:00
Frank
acd6e0f423 AIRBOSS v0.7.2 2019-01-05 09:51:19 +01:00
Frank
e741e0d1d6 Merge pull request #1085 from FlightControl-Master/FF/Develop
AIRBOSS v0.7.1
2019-01-04 00:25:39 +01:00
Frank
09f28a555f AIRBOSS v0.7.1
* Fixed radio transmission subtitle bug.
* Added on-the-fly LSO grading to attitude monitor.
* Added doc menu images.
2019-01-04 00:22:42 +01:00
Frank
a9590561d2 Merge pull request #1084 from FlightControl-Master/FF/Develop
Controllable SetTask debug output
2019-01-03 15:32:10 +01:00
Frank
9700776468 Controllable log output
Controllable, changed self:E to self:F/T2 since it was spamming the DCS log.
Airboss docs.
Spawn scheduler(self,...) --> scheduler(nil,...)
2019-01-03 14:09:19 +01:00
Frank
cec6940aeb Merge pull request #1083 from FlightControl-Master/FF/Develop
AIRBOSS v0.7.0
2019-01-03 01:05:24 +01:00
Frank
e082ee17e4 Merge branch 'develop' into FF/Develop 2019-01-03 00:30:27 +01:00
Frank
2327fa7e4b AIRBOSS v0.7.0
RECOVERYTANKER v1.0.1
RESCUEHELO v1.0.1
2019-01-03 00:30:03 +01:00
FlightControl
bd25a8ccb4 Fixing too small static Moose.lua 2018-12-29 23:49:52 +01:00
FlightControl
fff0c8ccd3 New dynamic loader and adapted Moose.lua creation from Modules.lua in the MOOSE root directory. 2018-12-28 09:30:49 +01:00
FlightControl
df6a10862e New dynamic loader and adapted Moose.lua creation from Modules.lua in the MOOSE root directory. 2018-12-28 09:29:33 +01:00
FlightControl
a33b7b2769 Merged 'develop'. 2018-12-27 07:34:29 +01:00
FlightControl
5e01db8809 New stuff 2018-12-27 07:33:51 +01:00
FlightControl
32c064a1be Remove Moose.lua 2018-12-26 09:10:03 +01:00
FlightControl
610938b6e8 Changes 2018-12-26 09:06:16 +01:00
FlightControl
ea93e3863b Loader 2018-12-26 07:42:47 +01:00
Frank
ab38efd69e Merge pull request #1077 from FlightControl-Master/FF/Develop
New classes, fixes and improvements.
2018-12-26 04:50:56 +01:00
Frank
8dc5642599 AIBOSS v0.6.3
Recovery Tanker v1.0.0
Rescue Helo v1.0.0
Fixed spawn after engine shutdown bug.
Added new PG airbases.
2018-12-25 18:55:35 +01:00
Frank
d8c5ab7eae AIRBOSS v0.6.2 2018-12-23 01:30:33 +01:00
Frank
49e7a24e28 AIRBOSS v0.6.1
Lineup, glide slope fixes.
2018-12-22 15:55:43 +01:00
Frank
6f2de65b64 AIRBOSS v0.6.0 2018-12-22 09:27:51 +01:00
funkyfranky
34603d69ab AIRBOSS v0.5.9w 2018-12-21 16:17:42 +01:00
Frank
afb79391dd AIRBOSS v0.5.9 2018-12-21 00:34:25 +01:00
funkyfranky
7f1dcf93d9 AIRBOSS v0.58w 2018-12-20 16:16:29 +01:00
Frank
04722a7d83 AIRBOSS v0.5.8 2018-12-20 00:49:41 +01:00
funkyfranky
fd6a319928 AIRBOSS v0.5.7w 2018-12-19 17:02:58 +01:00
FlightControl
e91a744bd9 Fixed for A2G. Default parameters and proper amount of spawnings. Now also altitude is added for engage. And RTB has min and max speed set. Defaults of speed between 50% and 75% of maximum speed of group. And altitude defaults to 1000 and 1500 meters. 2018-12-19 07:24:01 +01:00
Frank
876b369c0d AIRBOSS v0.5.7 2018-12-19 00:40:42 +01:00
Frank
75ac76a8e5 AIBOSS v0.5.6
RESCUEHELO v0.9.9
2018-12-18 14:04:31 +01:00
FlightControl
6c4bde4ceb Fixes 2018-12-17 20:59:58 +01:00
FlightControl
cfc9c655c5 Fixes for patrol. 2018-12-17 19:39:23 +01:00
Frank
d576573cf8 RECOVERYTANKER v0.9.9
RESCUEHELO v0.9.8
2018-12-16 16:59:53 +01:00
Frank
1559f14f11 RESCUEHELO v0.9.7
- Fixed spawn bug with multiple helos.
- Adjusted default parameters.
- Changed RTB to respawn (not perfect).
- Improved documentation.
2018-12-15 23:06:36 +01:00
Frank
242151eedf AIBOSS v0.5.5 2018-12-15 08:47:01 +01:00
funkyfranky
7cf10e90f8 AIRBOSS 0.5.4w 2018-12-14 16:44:00 +01:00
Frank
5201c73d35 AIRBOSS v0.5.4 2018-12-14 00:17:11 +01:00
funkyfranky
07f313a4a6 AIRBOSS v0.5.3w 2018-12-13 16:08:59 +01:00
Frank
fa08434690 AIRBOSS v0.5.3 2018-12-12 23:38:48 +01:00
funkyfranky
8969c58ca5 AIRBOS v0.5.2w 2018-12-12 16:03:37 +01:00
FlightControl
810cebfe0a Optimizations 2018-12-12 07:31:08 +01:00
Frank
d607b96021 AIRBOSS v0.5.2 2018-12-12 00:50:42 +01:00
FlightControl
d098930351 -- New SEAD, BAI and CAS files.
-- Correct handling of task assignments.
-- Added Weapon stuff in DCS.lua.
-- Dispatcher optimizations.
2018-12-11 19:20:11 +01:00
funkyfranky
143e729a5e AIRBOSS v0.5.1w 2018-12-11 16:02:45 +01:00
Frank
5ef4b593a2 AIRBOSS v0.5.1 2018-12-11 00:19:14 +01:00
funkyfranky
7eb98c05eb AIRBOSS v0.5.0w 2018-12-10 16:59:00 +01:00
Frank
43ec58fe85 RT & RH
Recovery Tanker
Rescue Helo
2018-12-09 23:11:44 +01:00
Frank
0a5f042555 Merge branch 'FF/Development' into FF/Develop 2018-12-09 21:25:47 +01:00
Frank
44f8c2a933 WAREHOUSE v0.6.7
SPAWNSTATIC
UNIT
2018-12-09 21:16:47 +01:00
Frank
ecbdbedd96 Warehouse v0.6.6
Again
2018-12-09 19:32:39 +01:00
Frank
c6a4e4c533 Warehouse v0.6.6
Again
2018-12-09 19:32:07 +01:00
Frank
afb0a8af33 Manual diff merge from FF/Development
Hopefully cleans up the mess now.
2018-12-09 19:11:46 +01:00
Frank
8d222fd0c3 Merge branch 'FF/Development' into FF/Develop 2018-12-09 12:12:32 +01:00
Frank
9795d5655f Improvements and Fixes from FF/Develop 2018-12-09 12:01:15 +01:00
Frank
db6c7f1a2c AIROSS v0.5.0 2018-12-09 01:06:01 +01:00
funkyfranky
cfdce853a3 AIRBOSS w 2018-12-07 15:36:41 +01:00
Frank
01b2f238c5 AIRBOSS v0.4.9
RECOVERYTANKER v0.9.7
2018-12-06 23:36:37 +01:00
funkyfranky
5a327b1d6b AIRBOSS v0.4.8w 2018-12-06 16:10:17 +01:00
Frank
96b60d8ac0 AIRBOSS v0.4.8 2018-12-06 00:05:41 +01:00
Frank
e7f1d98b64 AIRBOSS v0.4.7 2018-12-05 23:58:37 +01:00
Frank
c2ddf17aa2 AIRBOS v0.4.6 2018-12-05 23:35:03 +01:00
funkyfranky
302d9dfad4 AIRBOSS v0.4.5w 2018-12-05 15:57:26 +01:00
Frank
a83008aad3 AIRBOSS v0.4.5 2018-12-04 23:37:56 +01:00
funkyfranky
047df5917a AIRBOSS v0.4.4w 2018-12-04 16:05:15 +01:00
funkyfranky
6b53e9f9a8 Merge branch 'FF/Develop' of https://github.com/FlightControl-Master/MOOSE into FF/Develop 2018-12-04 11:37:14 +01:00
funkyfranky
d66028a1b9 AIRBOSS v0.4.3w 2018-12-04 11:37:07 +01:00
Frank
51de6756a7 AIRBOSS update 2018-12-03 23:07:08 +01:00
Frank
74d97cc220 AIRBOSS v0.4.3 2018-12-02 23:48:39 +01:00
Frank
cf2ad6f277 AIRBOSS v0.4.2 2018-12-02 00:30:22 +01:00
Frank
b05d3fbde2 AIRBOSS v0.4.1 2018-11-30 23:48:59 +01:00
funkyfranky
7b0db24e00 AIRBOSS v0.4.0w 2018-11-30 16:02:53 +01:00
Frank
21f0094d7c AIRBOSS v0.4.0 2018-11-29 23:41:57 +01:00
funkyfranky
99c2e76e53 AIRBOSS v0.3.9w 2018-11-29 16:19:19 +01:00
Frank
50c40cb4e9 AIRBOSS v0.3.9 2018-11-28 23:55:14 +01:00
funkyfranky
5e8b461478 AIRBOSS v0.3.6w 2018-11-28 17:17:05 +01:00
funkyfranky
73db4b365b Merge branch 'FF/Develop' of https://github.com/FlightControl-Master/MOOSE into FF/Develop 2018-11-28 11:52:06 +01:00
funkyfranky
0cf5bee09a Pics 2018-11-28 11:52:02 +01:00
Frank
3d54d78be8 AIRBOSS v0.3.7
Zones.
2018-11-27 23:37:48 +01:00
Frank
bd537ece00 Revert "Merge branch 'develop' into FF/Develop"
This reverts commit 1f282693b3, reversing
changes made to e16f306e1d.
2018-11-27 18:38:21 +01:00
Frank
1f282693b3 Merge branch 'develop' into FF/Develop 2018-11-27 18:13:58 +01:00
Frank
e16f306e1d AIRBOSS v0.3.6 2018-11-27 00:03:45 +01:00
funkyfranky
6b24673b6f AIRBOSS v0.3.5w 2018-11-26 16:35:35 +01:00
Frank
58886dc42d AIRBOSS v0.3.5 2018-11-26 00:43:38 +01:00
Frank
8cea501748 RESCUEHELO v0.9.4 2018-11-25 11:10:11 +01:00
Frank
c5171e8722 RECOVERYTANKER v0.9.4 2018-11-25 00:05:31 +01:00
Frank
5af235a345 AIRBOSS v0.3.4 2018-11-23 23:57:54 +01:00
funkyfranky
8133ef2036 AIRBOSS v0.3.3w 2018-11-23 16:30:32 +01:00
Frank
9269e1e943 AIBOSS v0.3.3
little bug fixes
2018-11-22 23:27:43 +01:00
funkyfranky
b4c82d0aac docs 2018-11-22 16:12:00 +01:00
Frank
39ffc28cb4 AIRBOSS v0.3.2 2018-11-21 23:45:24 +01:00
funkyfranky
e23488a723 AIRBOSS v0.3.1w 2018-11-21 16:01:10 +01:00
Frank
ba1cb61f4d AIRBOSS v0.3.1
Tanker v0.9.3
* added TACAN option

Helo v0.9.3
* added respawn and uncontrolled
2018-11-21 00:55:00 +01:00
FlightControl
921ec8732c A lot of optimizations ...
- Depart from runway now works again.
 - Depart from hotspot works again.
 - Depart from ramp works again.
 - Implemented the limit, it works, but it is not waterproof (yet).
2018-11-20 20:00:38 +01:00
Frank
51a1f56011 AIRBOSS v0.3.0
RESCUEHELO v0.9.2
RECOVERYTANKER v0.9.2
2018-11-20 00:15:39 +01:00
Frank
2feb293c12 AIRBOSS v0.2.9
- Added holding check (WIP)
- Many improvents and fixes.
2018-11-18 23:57:41 +01:00
Frank
9d9f233c15 AIRBOSS v0.2.8
good commit, improved and fixed a lot of stuff.
2018-11-18 00:45:18 +01:00
funkyfranky
9ad948632c AIRBOSS v0.2.7w 2018-11-16 16:24:01 +01:00
Frank
9b40ebe00c AIRBOSS v0.2.7 2018-11-15 23:16:58 +01:00
funkyfranky
dab3ed5fbe AIRBOSS v0.2.6w 2018-11-15 16:11:14 +01:00
Frank
34d7b18c26 AIRBOSS v0.2.6 2018-11-14 23:23:46 +01:00
FlightControl
dc4115d4e3 Finish 2.4.14 2018-11-14 20:09:35 +01:00
FlightControl
5cfd0aac1c Finish 2.4.14 2018-11-14 20:09:35 +01:00
FlightControl
9481cbf7e8 Fixing issue #1054 related to ZONE_CAPTURE_COALITION going into Attacked state when the zone is attacked, meaning, when a unit is hit that is in the zone. 2018-11-14 20:01:29 +01:00
FlightControl
748f9b52aa Finish 2.4.13 2018-11-14 19:22:23 +01:00
FlightControl
c5ce3342e2 Finish 2.4.13 2018-11-14 19:22:22 +01:00
FlightControl
6c4ca046a9 Fixed problem with DESIGNATE:SetFlashStatusMenu() documentation. 2018-11-14 19:22:10 +01:00
FlightControl
256697c39e Fixing problem in SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ). 2018-11-14 18:58:44 +01:00
FlightControl
4017876fe2 Fixing problem in SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ). 2018-11-14 18:58:43 +01:00
FlightControl
9651614894 Fixing problem in SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ). 2018-11-14 18:58:08 +01:00
funkyfranky
3bc2baaf9d AIRBOSS v0.2.5w 2018-11-14 16:11:54 +01:00
Frank
fa05352882 AIRBOSS v0.2.5 2018-11-13 23:39:29 +01:00
FlightControl
849ca24ac8 Finish Feature-AI-A2G-Dispatcher 2018-11-13 20:21:00 +01:00
FlightControl
84e9d225e9 Fixing problem with squadron not found for CAS tasking. 2018-11-13 20:20:42 +01:00
funkyfranky
7b53a43c5c AB v0.2.4 2018-11-13 16:24:08 +01:00
Frank
e3121781d0 AIRBOSS v0.2.4 2018-11-13 00:00:20 +01:00
FlightControl
2c788b1b38 Finish 2.4.11 2018-11-12 19:38:49 +01:00
FlightControl
369606f0f1 Finish 2.4.11 2018-11-12 19:38:48 +01:00
FlightControl
8d4ce5e486 Fixing autolase problem... now the duration is fixed.
I think I also fixed the lase duration problem ...
2018-11-12 19:38:22 +01:00
FlightControl
1b691e5887 Documentation 2018-11-12 19:35:32 +01:00
Frank
2d7d19880f AIRBOSS v0.2.3
group fixed init heading
2018-11-11 23:55:19 +01:00
FlightControl
8b06af01b0 More documentation for A2G dispatcher. 2018-11-11 21:51:11 +01:00
FlightControl
ea3899f469 More documentation for A2G dispatcher. 2018-11-11 21:50:41 +01:00
FlightControl
adb8b6cbca Fixed overhead (again), and optimized defense radius logic. 2018-11-11 08:43:39 +01:00
FlightControl
252950af73 Fixed overhead (again), and optimized defense radius logic. 2018-11-11 08:43:20 +01:00
FlightControl
14189e1bca Images 2018-11-11 07:31:13 +01:00
FlightControl
ed60c5dca4 Images 2018-11-11 07:25:47 +01:00
FlightControl
c4984a7576 Doc update 2018-11-11 07:22:22 +01:00
FlightControl
6a1052d95f Introduction documentation of A2G dispatching. 2018-11-10 16:30:31 +01:00
FlightControl
caa3bcb02b Introduction documentation of A2G dispatching. 2018-11-10 16:30:00 +01:00
FlightControl
74123d8ff3 Fixed overhead bug in dispatchiing from ground. 2018-11-10 13:21:46 +01:00
FlightControl
70b858d200 - Optimized the overhead problem in case of engage from patrol.
- Change in defense reactivity and distance calculations.
- Select correct template to engage is working now also.
2018-11-10 12:44:13 +01:00
FlightControl
3ca712a223 Optimized the overhead problem in case of engage from patrol. 2018-11-10 12:42:16 +01:00
Frank
cf4be99093 AB v0.2.2 2018-11-10 00:57:14 +01:00
FlightControl
900a55614b Change in defense reactivity and distance calculations. 2018-11-09 17:00:40 +01:00
FlightControl
f76b0dc7d3 Select correct template to engage is working now also. 2018-11-09 16:47:06 +01:00
funkyfranky
4542c96eae AB v0.2.1w 2018-11-09 16:07:30 +01:00
FlightControl
dd242bc1fe Finish 2.4.10 2018-11-09 07:44:37 +01:00
FlightControl
456a38022f Finish 2.4.10 2018-11-09 07:44:37 +01:00
FlightControl
34d73af8bf Changed the default lase duration for designations to 120 seconds. A new method has been added to change the lase duration yourself. Use SetLaseDuratioin API! 2018-11-09 07:44:11 +01:00
FlightControl
238b1cb925 Finish 2.4.9 2018-11-09 06:46:24 +01:00
FlightControl
604255bfba Finish 2.4.9 2018-11-09 06:46:23 +01:00
FlightControl
cfc0367c55 Fixing problems with A2A_GCI. 2018-11-09 06:45:34 +01:00
FlightControl
488804308b Undo my last stupid mistakes. 2018-11-09 06:27:16 +01:00
Frank
6fac8fe940 CT v0.2.1 2018-11-09 00:17:57 +01:00
funkyfranky
b53b1edd29 CT v0.2.0w 2018-11-08 16:04:51 +01:00
FlightControl
5a2bac9979 Got engage working from the air. Now I need to fix:
- Overhead
- Correct Defense Type assignment to engage.

# Conflicts:
#	Moose Development/Moose/AI/AI_A2G_Dispatcher.lua
#	Moose Setup/Moose.files
2018-11-08 07:44:50 +01:00
FlightControl
e7181c255b Got engage working from the air. Now I need to fix:
- Overhead
- Correct Defense Type assignment to engage.
2018-11-08 07:38:04 +01:00
Frank
ac074ca23d CT v0.2.0 2018-11-07 23:26:51 +01:00
funkyfranky
b21bb3d433 CT v0.1.9w 2018-11-07 16:08:13 +01:00
FlightControl
0e656c8520 Progress, patrolling works. Engagement not yet. 2018-11-07 07:24:45 +01:00
Frank
708ff794b0 CT v0.1.9 2018-11-07 00:37:49 +01:00
FlightControl
6e34f12ac8 Finish 2.4.8 2018-11-06 19:11:26 +01:00
FlightControl
f4a89d62e3 Finish 2.4.8 2018-11-06 19:11:25 +01:00
FlightControl
a36e39205b Further fixes and optimizations for the A2A dispatcher plane optimization. 2018-11-06 18:50:42 +01:00
funkyfranky
6d45c09ea2 CTv0.18w 2018-11-06 16:04:19 +01:00
Frank
9fac65f31f CT v0.1..8 2018-11-06 00:04:52 +01:00
FlightControl
3789674666 First version of AI_A2G_DISPATCHER ...
- Created a new AI_AIR class. AI_A2A and AI_A2G inherit from AI_AIR.
- Created a new AI_A2G_DISPATCHER class.
- Created a new AI_A2G_ENGAGE class, that implements the SEAD, CAS and BAI actions.
- The AI_A2G_DISPATCHER implements now:
  * SEAD, CAS and BAI attacks from airbases.
  * AddDefenseCoordinate() to set a defense coordinate.
  * RemoveDefenseCoordinate() to remove a defense coordinate.
  * SetDefenseReactivenessHigh() for high reactiveness.
  * SetDefenseReactivenessMedium() for medium reactiveness.
  * SetDefenseReactivenessLow() for low reactiveness.
  * Overhead for ground attacks.
  * Grouping for ground attacks.
  * Evaluates task type based on enemy group composition.

- Tested AI_A2A_DISPATCHER for backward compatibility.
- Implemented spawning of airplanes in AI_A2A_DISPATCHER:
  * SetSquadronVisible() method to make the squadron visible at the airport.
2018-11-05 21:52:31 +01:00
FlightControl
a22774b278 Fixing takeoff bug in air for dispatchers. 2018-11-05 21:44:47 +01:00
FlightControl
81bb8dd65f Reworking a confusion with the takeoff in air spawning. 2018-11-05 21:41:52 +01:00
FlightControl
9d3796b605 Changes 2018-11-05 21:26:47 +01:00
funkyfranky
ba4caf179a CTv0.1.7w 2018-11-05 16:18:15 +01:00
Frank
1febe765bf CT v0.1.7 2018-11-05 00:35:48 +01:00
Frank
087ac992a2 CT v0.1.6 2018-11-04 01:14:47 +01:00
FlightControl
3e3dfc83aa First Version of AI_A2G_DISPATCHER. There are lots of challenges here to overcome. One of the first one is the defense points. 2018-11-02 16:43:06 +01:00
funkyfranky
27bf6069d7 CT v0.1.5w 2018-11-02 16:07:40 +01:00
Frank
3153d196a2 CT v0.1.5 2018-11-02 00:29:50 +01:00
funkyfranky
8c320f729a CT v0.1.4w 2018-11-01 16:20:49 +01:00
Frank
c7fdc77ec8 CT v0.1.4
Added aircraft check.
Fixed problems with new player.
2018-11-01 00:09:58 +01:00
Frank
dfd20ced59 CT v0.1.3 2018-10-30 00:02:45 +01:00
funkyfranky
5f40feb1af CTv0.1.2w 2018-10-29 16:25:09 +01:00
Frank
ae754fe334 CT v0.1.2 2018-10-29 00:01:53 +01:00
Frank
e062db9411 Merge pull request #1050 from FlightControl-Master/FF/Develop
Several improvements and fixes.
2018-10-28 14:16:07 +01:00
Frank
023eae825d RCT 2018-10-28 13:16:32 +01:00
Frank
1a4baeafb6 Warehose v0.6.6
WAREHOUSE:
- Improved parking spot check for airbase ship.
AI_FORMATION:
- Added Stop option
CT:
- fixes
COORDINATE:
- Improved landing waypoint.
2018-10-28 13:15:38 +01:00
Frank
b754972490 Merge branch 'FF/Develop' of https://github.com/FlightControl-Master/MOOSE into FF/Develop 2018-10-26 18:55:41 +02:00
Frank
0c36e4e40d CT 2018-10-26 18:55:36 +02:00
funkyfranky
1f97495fdd CT,RAT,WH work 2018-10-26 16:04:02 +02:00
FlightControl
c164c0f9e3 Merge branch 'hotfix/2.4.8' into feature/Feature-AI-A2A-Dispatcher 2018-10-26 14:09:25 +02:00
FlightControl
f0b5ec1025 Fixed the out of fuel bug. 2018-10-26 12:08:48 +02:00
Frank
b72ea2d02c minor 2018-10-26 00:12:21 +02:00
Frank
238fcf1176 WAREHOUSE v0.6.5
* fixed bug for assignments
2018-10-24 23:13:56 +02:00
Frank
c354fecc6d Merge branch 'develop' into FF/Develop 2018-10-24 21:47:52 +02:00
Frank
4434d1da21 Fixes
AI_FORMATION:
* added stop possibility ad interval input
WAREHOUSE
* added autodefence assignment
UNIT:
* improved  InAir() check function
2018-10-24 21:32:07 +02:00
FlightControl
17ffc7cef9 Updates for visibility of squadrons before start. 2018-10-24 20:55:04 +02:00
Frank
006fd25e56 Merge pull request #1044 from cjbehm/bugfix/CONTROLLABLE-honor-GotPath
Honor the GotPath return value from COORDINATE
2018-10-24 15:20:18 +02:00
Chris
f2eafe0302 Honor the GotPath return value from COORDINATE
PathOnRoad is always defined, at least as an empty table.
Use the 3rd return value instead, which indicates whether
a road route was found
2018-10-24 00:17:26 -04:00
FlightControl
52069cc1d0 Respawning of existing visible resources at the airbases is working. 2018-10-24 00:18:02 +02:00
Frank
d9374f0389 Controllable
Added more general orbit task.
2018-10-23 23:35:25 +02:00
Frank
19520f9688 CT v0.1.0 2018-10-22 23:41:48 +02:00
FlightControl
1eda5af7df Visible before start, uncontrolled planes. 2018-10-22 07:06:24 +02:00
Frank
2ea8487e04 ARTY v1.0.7
Fixed bug when targets get removed because ARTY group is immobile and not declared as cargo.
CT fixes
2018-10-22 00:08:38 +02:00
Frank
645ca570a8 CT 0.0.9
many fixes
2018-10-20 12:20:35 +02:00
funkyfranky
07e690caf0 CT008 2018-10-19 15:09:26 +02:00
Frank
223de9d1aa Range 2018-10-18 22:51:51 +02:00
funkyfranky
7d7c521bce CT007 2018-10-18 16:50:08 +02:00
Frank
baaee9f92e Merge branch 'develop' into FF/Develop 2018-10-17 22:10:04 +02:00
Frank
3a9be7a890 CTA 0.0.6 2018-10-17 22:07:11 +02:00
Sven Van de Velde
74d2ee9430 Improvements 2018-03-25 20:49:49 +02:00
Sven Van de Velde
856bd56cde Improved dynamic loading 2018-03-25 20:33:43 +02:00
Sven Van de Velde
4c91880a85 Improved dynamic loader process 2018-03-25 20:27:18 +02:00
141 changed files with 93809 additions and 22304 deletions

View File

@@ -26,24 +26,25 @@ init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
install:
- cmd:
# Outcomment if lua environment invalidates and needs to be reinstalled, otherwise all will run from the cache.
# - call choco install 7zip.commandline
# - call choco install lua51
# - call choco install luarocks
# - call refreshenv
# - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
# - cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
# - cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
# - cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
# - call luarocks install luasrcdiet
# - call luarocks install checks
# - call luarocks install luadocumentor
# - call luarocks install luacheck
call choco install 7zip.commandline
call choco install lua51
call choco install luarocks
call refreshenv
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
call luarocks install luasrcdiet
call luarocks install checks
call luarocks install luadocumentor
call luarocks install luacheck
#cache:
# - C:\ProgramData\chocolatey\lib
# - C:\ProgramData\chocolatey\bin
cache:
C:\ProgramData\chocolatey\lib
C:\ProgramData\chocolatey\bin
@@ -51,8 +52,9 @@ build_script:
- ps: |
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
{
echo "Hello World!"
$apiUrl = 'https://ci.appveyor.com/api'
$token = 'qts80b5kpq0ooj4x6vvw'
$token = 'v2.6hcv3ige78kg3yvg4ge8'
$headers = @{
"Authorization" = "Bearer $token"
"Content-type" = "application/json"
@@ -65,7 +67,7 @@ build_script:
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
{
$apiUrl = 'https://ci.appveyor.com/api'
$token = 'qts80b5kpq0ooj4x6vvw'
$token = 'v2.6hcv3ige78kg3yvg4ge8'
$headers = @{
"Authorization" = "Bearer $token"
"Content-type" = "application/json"

7
.gitignore vendored
View File

@@ -18,6 +18,8 @@ local.properties
# External tool builders
.externalToolBuilders/
# AppVeyor
.appveyor/
# CDT-specific
.cproject
@@ -221,3 +223,8 @@ _gsdata_/
.gitattributes
.gitignore
Moose Test Missions/MOOSE_Test_Template.miz
Moose Development/Moose/.vscode/launch.json
MooseCodeWS.code-workspace
.gitignore
.gitignore
/.gitignore

54
.luacheckrc Normal file
View File

@@ -0,0 +1,54 @@
ignore = {
"011", -- A syntax error.
"021", -- An invalid inline option.
"022", -- An unpaired inline push directive.
"023", -- An unpaired inline pop directive.
"111", -- Setting an undefined global variable.
"112", -- Mutating an undefined global variable.
"113", -- Accessing an undefined global variable.
"121", -- Setting a read-only global variable.
"122", -- Setting a read-only field of a global variable.
"131", -- Unused implicitly defined global variable.
"142", -- Setting an undefined field of a global variable.
"143", -- Accessing an undefined field of a global variable.
"211", -- Unused local variable.
"212", -- Unused argument.
"213", -- Unused loop variable.
"221", -- Local variable is accessed but never set.
"231", -- Local variable is set but never accessed.
"232", -- An argument is set but never accessed.
"233", -- Loop variable is set but never accessed.
"241", -- Local variable is mutated but never accessed.
"311", -- Value assigned to a local variable is unused.
"312", -- Value of an argument is unused.
"313", -- Value of a loop variable is unused.
"314", -- Value of a field in a table literal is unused.
"321", -- Accessing uninitialized local variable.
"331", -- Value assigned to a local variable is mutated but never accessed.
"341", -- Mutating uninitialized local variable.
"411", -- Redefining a local variable.
"412", -- Redefining an argument.
"413", -- Redefining a loop variable.
"421", -- Shadowing a local variable.
"422", -- Shadowing an argument.
"423", -- Shadowing a loop variable.
"431", -- Shadowing an upvalue.
"432", -- Shadowing an upvalue argument.
"433", -- Shadowing an upvalue loop variable.
"511", -- Unreachable code.
"512", -- Loop can be executed at most once.
"521", -- Unused label.
"531", -- Left-hand side of an assignment is too short.
"532", -- Left-hand side of an assignment is too long.
"541", -- An empty do end block.
"542", -- An empty if branch.
"551", -- An empty statement.
"561", -- Cyclomatic complexity of a function is too high.
"571", -- A numeric for loop goes from #(expr) down to 1 or less without negative step.
"611", -- A line consists of nothing but whitespace.
"612", -- A line contains trailing whitespace.
"613", -- Trailing whitespace in a string.
"614", -- Trailing whitespace in a comment.
"621", -- Inconsistent indentation (SPACE followed by TAB).
"631", -- Line is too long.
}

0
.scannerwork/.sonar_lock Normal file
View File

View File

@@ -0,0 +1,6 @@
projectKey=Test
serverUrl=http://localhost:9000
serverVersion=8.1.0.31237
dashboardUrl=http://localhost:9000/dashboard?id=Test
ceTaskId=AXAlUJO97YLjwz1VUDXR
ceTaskUrl=http://localhost:9000/api/ce/task?id=AXAlUJO97YLjwz1VUDXR

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

@@ -1,66 +1,67 @@
--- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes.
--
-- ===
--
--
-- ### Author: **FlightControl**
--
-- ===
--
-- ===
--
-- @module AI.AI_A2A_Cap
-- @image AI_Combat_Air_Patrol.JPG
--- @type AI_A2A_CAP
-- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL
-- @extends AI.AI_Air_Patrol#AI_AIR_PATROL
-- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
--
-- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event.
--
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
--
-- The AI will fly towards the random 3D point within the patrol zone, 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.
--
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
--
-- This cycle will continue.
--
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
--
-- ![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.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
--
-- ## 1. AI_A2A_CAP constructor
--
--
-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object.
--
--
-- ## 2. AI_A2A_CAP is a FSM
--
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
--
-- ### 2.1 AI_A2A_CAP States
--
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
--
-- ### 2.2 AI_A2A_CAP Events
--
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys.
@@ -70,33 +71,64 @@
-- * **@{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.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
--
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
-- An optional range can be set in meters,
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI.AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
--
-- An optional @{Zone} can be set,
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone.
--
--
-- ===
--
--
-- @field #AI_A2A_CAP
AI_A2A_CAP = {
ClassName = "AI_A2A_CAP",
}
--- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_A2A_CAP
function AI_A2A_CAP:New2( AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
-- Multiple inheritance ... :-)
local AI_Air = AI_AIR:New( AICap )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage ) --#AI_A2A_CAP
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
return self
end
--- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap
@@ -111,174 +143,8 @@ AI_A2A_CAP = {
-- @return #AI_A2A_CAP
function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP
return self:New2( AICap, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
self.Accomplished = false
self.Engaging = false
self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed
self:AddTransition( { "Patrolling", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_CAP] OnBeforeEngage
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_CAP] OnAfterEngage
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_CAP] Engage
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_CAP] __Engage
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_CAP] OnLeaveEngaging
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_CAP] OnEnterEngaging
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_CAP] OnBeforeFired
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_CAP] OnAfterFired
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_CAP] Fired
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_CAP] __Fired
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_CAP] OnBeforeDestroy
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_CAP] OnAfterDestroy
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_CAP] Destroy
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_CAP] __Destroy
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_CAP] OnBeforeAbort
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_CAP] OnAfterAbort
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_CAP] Abort
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_CAP] __Abort
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] OnBeforeAccomplish
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] OnAfterAccomplish
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] Accomplish
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] __Accomplish
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
return self
end
--- onafter State Transition for Event Patrol.
@@ -289,199 +155,58 @@ end
-- @param #string To The To State string.
function AI_A2A_CAP:onafterStart( AICap, From, Event, To )
self:GetParent( self ).onafterStart( self, AICap, From, Event, To )
self:GetParent( self, AI_A2A_CAP ).onafterStart( self, AICap, From, Event, To )
AICap:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- Set the Engage Zone which defines where the AI will engage bogies.
--- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_A2A_CAP self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Set the Engage Range when the AI will engage with airborne enemies.
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_A2A_CAP self
-- @param #number EngageRange The Engage Range.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- onafter State Transition for Event Patrol.
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterPatrol( AICap, From, Event, To )
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
-- Call the parent Start event handler
self:GetParent(self).onafterPatrol( self, AICap, From, Event, To )
self:HandleEvent( EVENTS.Dead )
local AttackUnitTasks = {}
end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AICap
function AI_A2A_CAP.AttackRoute( AICap, Fsm )
AICap:F( { "AI_A2A_CAP.AttackRoute:", AICap:GetName() } )
if AICap:IsAlive() then
Fsm:__Engage( 0.5 )
end
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onbeforeEngage( AICap, From, Event, To )
if self.Accomplished == true then
return false
end
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterAbort( AICap, From, Event, To )
AICap:ClearTasks()
self:__Route( 0.5 )
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The AICap Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterEngage( AICap, From, Event, To, AttackSetUnit )
self:F( { AICap, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
if AICap:IsAlive() then
local EngageRoute = {}
--- Calculate the target route point.
local CurrentCoord = AICap:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AICap:TaskAttackUnit( AttackUnit )
end
end
if #AttackTasks == 0 then
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
else
AICap:OptionROEOpenFire()
AICap:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = AICap:TaskFunction( "AI_A2A_CAP.AttackRoute", self )
EngageRoute[#EngageRoute].task = AICap:TaskCombo( AttackTasks )
end
AICap:Route( EngageRoute, 0.5 )
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
if AttackUnit and AttackUnit:IsAlive() and AttackUnit:IsAir() then
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
-- Maybe the detected set also contains
self:T( { "Attacking Task:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
else
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
end
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterAccomplish( AICap, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_CAP:onafterDestroy( AICap, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_A2A_CAP self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_CAP:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end
--- @param Wrapper.Group#GROUP AICap
function AI_A2A_CAP.Resume( AICap, Fsm )
AICap:I( { "AI_A2A_CAP.Resume:", AICap:GetName() } )
if AICap:IsAlive() then
Fsm:__Reset( 1 )
Fsm:__Route( 5 )
end
return AttackUnitTasks
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
--- **AI** -- (R2.2) - Models the process of Ground Controlled Interception (GCI) for airplanes.
--
-- This is a class used in the @{AI_A2A_Dispatcher}.
--
--
-- ===
--
--
-- ### Author: **FlightControl**
--
-- ===
--
-- ===
--
-- @module AI.AI_A2A_GCI
-- @image AI_Ground_Control_Intercept.JPG
@@ -18,52 +18,52 @@
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
--
-- ![Process](..\Presentations\AI_GCI\Dia3.JPG)
--
--
-- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event.
--
--
-- ![Process](..\Presentations\AI_GCI\Dia4.JPG)
--
--
-- The AI will fly towards the random 3D point within the patrol zone, 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.
--
--
-- ![Process](..\Presentations\AI_GCI\Dia5.JPG)
--
--
-- This cycle will continue.
--
--
-- ![Process](..\Presentations\AI_GCI\Dia6.JPG)
--
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia9.JPG)
--
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
--
-- ![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.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
--
-- ## 1. AI_A2A_GCI constructor
--
--
-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object.
--
--
-- ## 2. AI_A2A_GCI is a FSM
--
--
-- ![Process](..\Presentations\AI_GCI\Dia2.JPG)
--
--
-- ### 2.1 AI_A2A_GCI States
--
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
--
-- ### 2.2 AI_A2A_GCI Events
--
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys.
@@ -73,28 +73,28 @@
-- * **@{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.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
--
--
-- ![Range](..\Presentations\AI_GCI\Dia11.JPG)
--
-- An optional range can be set in meters,
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI.AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
--
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
--
-- An optional @{Zone} can be set,
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone.
--
--
-- ===
--
--
-- @field #AI_A2A_GCI
AI_A2A_GCI = {
ClassName = "AI_A2A_GCI",
@@ -105,183 +105,39 @@ AI_A2A_GCI = {
--- Creates a new AI_A2A_GCI object
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_A2A_GCI
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed )
function AI_A2A_GCI:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A:New( AIIntercept ) ) -- #AI_A2A_GCI
local AI_Air = AI_AIR:New( AIIntercept )
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air, AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage ) -- #AI_A2A_GCI
self.Accomplished = false
self.Engaging = false
self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed
self.PatrolMinSpeed = EngageMinSpeed
self.PatrolMaxSpeed = EngageMaxSpeed
self.PatrolAltType = "RADIO"
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnBeforeEngage
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnAfterEngage
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_GCI] Engage
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_GCI] __Engage
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_GCI] OnLeaveEngaging
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_GCI] OnEnterEngaging
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_GCI] OnBeforeFired
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_GCI] OnAfterFired
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_GCI] Fired
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_GCI] __Fired
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_GCI] OnBeforeDestroy
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_GCI] OnAfterDestroy
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_GCI] Destroy
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_GCI] __Destroy
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_GCI] OnBeforeAbort
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_GCI] OnAfterAbort
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_GCI] Abort
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_GCI] __Abort
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] OnBeforeAccomplish
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] OnAfterAccomplish
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] Accomplish
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] __Accomplish
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
return self
end
--- Creates a new AI_A2A_GCI object
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_A2A_GCI
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
return self:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
@@ -290,173 +146,29 @@ end
-- @param #string To The To State string.
function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
self:GetParent( self ).onafterStart( self, AIIntercept, From, Event, To )
AIIntercept:HandleEvent( EVENTS.Takeoff, nil, self )
self:GetParent( self, AI_A2A_GCI ).onafterStart( self, AIIntercept, From, Event, To )
end
--- onafter State Transition for Event Patrol.
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterEngage( AIIntercept, From, Event, To )
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2A_GCI self
function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
self:HandleEvent( EVENTS.Dead )
local AttackUnitTasks = {}
end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AIControllable
function AI_A2A_GCI.InterceptRoute( AIIntercept, Fsm )
AIIntercept:F( { "AI_A2A_GCI.InterceptRoute:", AIIntercept:GetName() } )
if AIIntercept:IsAlive() then
Fsm:__Engage( 0.5 )
--local Task = AIIntercept:TaskOrbitCircle( 4000, 400 )
--AIIntercept:SetTask( Task )
end
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onbeforeEngage( AIIntercept, From, Event, To )
if self.Accomplished == true then
return false
end
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterAbort( AIIntercept, From, Event, To )
AIIntercept:ClearTasks()
self:Return()
self:__RTB( 0.5 )
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterEngage( AIIntercept, From, Event, To, AttackSetUnit )
self:F( { AIIntercept, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
if FirstAttackUnit and FirstAttackUnit:IsAlive() then
if AIIntercept:IsAlive() then
local EngageRoute = {}
local CurrentCoord = AIIntercept:GetCoordinate()
--- Calculate the target route point.
local CurrentCoord = AIIntercept:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 15000, ToInterceptAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
AttackTasks[#AttackTasks+1] = AIIntercept:TaskAttackUnit( AttackUnit )
end
end
if #AttackTasks == 0 then
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
AIIntercept:OptionROEOpenFire()
AIIntercept:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = AIIntercept:TaskFunction( "AI_A2A_GCI.InterceptRoute", self )
EngageRoute[#EngageRoute].task = AIIntercept:TaskCombo( AttackTasks )
end
AIIntercept:Route( EngageRoute, 0.5 )
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
-- Maybe the detected set also contains
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
else
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
end
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterAccomplish( AIIntercept, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_GCI:onafterDestroy( AIIntercept, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_A2A_GCI self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_GCI:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
return AttackUnitTasks
end

View File

@@ -39,8 +39,8 @@
--
-- ![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.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
@@ -68,7 +68,7 @@
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting 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
--
@@ -100,8 +100,8 @@
-- ## 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.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_A2A_PATROL.
-- 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.
@@ -109,7 +109,7 @@
-- ## 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.
-- 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.
--
-- ===
@@ -121,13 +121,13 @@ AI_A2A_PATROL = {
--- Creates a new AI_A2A_PATROL object
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol
-- @param Wrapper.Group#GROUP AIPatrol The patrol group object.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to BARO
-- @return #AI_A2A_PATROL self
-- @usage
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
@@ -136,8 +136,14 @@ AI_A2A_PATROL = {
-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A:New( AIPatrol ) ) -- #AI_A2A_PATROL
local AI_Air = AI_AIR:New( AIPatrol )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local self = BASE:Inherit( self, AI_Air_Patrol ) -- #AI_A2A_PATROL
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
@@ -145,8 +151,8 @@ function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCei
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
-- defafult PatrolAltType to "BARO" if not specified
self.PatrolAltType = PatrolAltType or "BARO"
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
@@ -281,15 +287,15 @@ function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
end
--- @param Wrapper.Group#GROUP AIPatrol
-- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
-- @param #AI_A2A_PATROL Fsm The FSM.
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then
if AIPatrol and AIPatrol:IsAlive() then
Fsm:Route()
end
@@ -303,7 +309,6 @@ end
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
self:F2()
-- When RTB, don't allow anymore the routing.
@@ -312,7 +317,7 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
end
if AIPatrol:IsAlive() then
if AIPatrol and AIPatrol:IsAlive() then
local PatrolRoute = {}
@@ -320,43 +325,80 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
local CurrentCoord = AIPatrol:GetCoordinate()
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) )
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
-- Random altitude.
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
-- Random speed in km/h.
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
-- First waypoint is current position.
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
--- Create a route point of type air.
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
if self.racetrack then
-- Random heading.
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
-- Random leg length.
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
-- Random duration if any.
local duration = self.racetrackdurationmin
if self.racetrackdurationmax then
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
end
-- CAP coordinate.
local c0=self.PatrolZone:GetRandomCoordinate()
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
end
-- Race track points.
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
self:SetTargetDistance(c0) -- For RTB status check
-- Debug:
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
--c1:MarkToAll("Race track c1")
--c2:MarkToAll("Race track c2")
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
local Tasks = {}
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
-- Task to orbit.
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
-- Task function to redo the patrol at other random position.
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
-- Controlled task with task condition.
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
-- Second waypoint
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
else
-- Target coordinate.
local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() --Core.Point#COORDINATE
ToTargetCoord:SetAltitude(altitude)
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local taskReRoute=AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskReRoute}, "Patrol Point")
end
-- ROE
AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire()
AIPatrol:Route( PatrolRoute, 0.5 )
end
end
--- @param Wrapper.Group#GROUP AIPatrol
function AI_A2A_PATROL.Resume( AIPatrol, Fsm )
AIPatrol:I( { "AI_A2A_PATROL.Resume:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then
Fsm:__Reset( 1 )
Fsm:__Route( 5 )
end
-- Patrol.
AIPatrol:Route( PatrolRoute, 0.5)
end
end

View File

@@ -0,0 +1,99 @@
--- **AI** -- Models the process of air to ground BAI engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_BAI
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_BAI
-- @extends AI.AI_A2A_Engage#AI_A2A_Engage
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- ===
--
-- @field #AI_A2G_BAI
AI_A2G_BAI = {
ClassName = "AI_A2G_BAI",
}
--- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_BAI
function AI_A2G_BAI:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIGroup )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage )
return self
end
--- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_BAI
function AI_A2G_BAI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_BAI self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_BAI self
function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "BAI Unit:", AttackUnit:GetName() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
end
end
end
return AttackUnitTasks
end

View File

@@ -0,0 +1,100 @@
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_CAS
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_CAS
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- ===
--
-- @field #AI_A2G_CAS
AI_A2G_CAS = {
ClassName = "AI_A2G_CAS",
}
--- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_CAS
function AI_A2G_CAS:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIGroup )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage )
return self
end
--- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_CAS
function AI_A2G_CAS:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_CAS self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_CAS self
function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "CAS Unit:", AttackUnit:GetName() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
end
end
end
return AttackUnitTasks
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
--- **AI** -- Models the process of air to ground SEAD engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_SEAD
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_SEAD
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL
--- Implements the core functions to SEAD intruders. Use the Engage trigger to intercept intruders.
--
-- ![Process](..\Presentations\AI_GCI\Dia3.JPG)
--
-- The AI_A2G_SEAD is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_SEAD process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, 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.
--
-- ![Process](..\Presentations\AI_GCI\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_GCI\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
-- ## 1. AI_A2G_SEAD constructor
--
-- * @{#AI_A2G_SEAD.New}(): Creates a new AI_A2G_SEAD object.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_GCI\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI.AI_GCI#AI_A2G_SEAD.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2G_SEAD.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_A2G_SEAD
AI_A2G_SEAD = {
ClassName = "AI_A2G_SEAD",
}
--- Creates a new AI_A2G_SEAD object
-- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_SEAD
function AI_A2G_SEAD:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
local AI_Air = AI_AIR:New( AIGroup )
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
local self = BASE:Inherit( self, AI_Air_Engage )
return self
end
--- Creates a new AI_A2G_SEAD object
-- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP AIGroup
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2G_SEAD
function AI_A2G_SEAD:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
end
--- Evaluate the attack and create an AttackUnitTask list.
-- @param #AI_A2G_SEAD self
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
-- @param #number EngageAltitude The altitude to engage the targets.
-- @return #AI_A2G_SEAD self
function AI_A2G_SEAD:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
local AttackUnitTasks = {}
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
for AttackUnitID, AttackUnit in ipairs( AttackSetUnitPerThreatLevel ) do
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:F( { "SEAD Unit:", AttackUnit:GetName() } )
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
end
end
end
end
return AttackUnitTasks
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,603 @@
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air_Engage
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_AIR_ENGAGE
-- @extends AI.AI_AIR#AI_AIR
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- ![Process](..\Presentations\AI_GCI\Dia3.JPG)
--
-- The AI_AIR_ENGAGE is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_ENGAGE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, 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.
--
-- ![Process](..\Presentations\AI_GCI\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_GCI\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
-- ## 1. AI_AIR_ENGAGE constructor
--
-- * @{#AI_AIR_ENGAGE.New}(): Creates a new AI_AIR_ENGAGE object.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_GCI\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI.AI_GCI#AI_AIR_ENGAGE.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_AIR_ENGAGE.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_AIR_ENGAGE
AI_AIR_ENGAGE = {
ClassName = "AI_AIR_ENGAGE",
}
--- Creates a new AI_AIR_ENGAGE object
-- @param #AI_AIR_ENGAGE self
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
-- @param Wrapper.Group#GROUP AIGroup The AI group.
-- @param DCS#Speed EngageMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
-- @return #AI_AIR_ENGAGE
function AI_AIR_ENGAGE:New( AI_Air, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_ENGAGE
self.Accomplished = false
self.Engaging = false
local SpeedMax = AIGroup:GetSpeedMax()
self.EngageMinSpeed = EngageMinSpeed or SpeedMax * 0.5
self.EngageMaxSpeed = EngageMaxSpeed or SpeedMax * 0.75
self.EngageFloorAltitude = EngageFloorAltitude or 1000
self.EngageCeilingAltitude = EngageCeilingAltitude or 1500
self.EngageAltType = EngageAltType or "RADIO"
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "EngageRoute", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngageRoute
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngageRoute
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] EngageRoute
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event EngageRoute.
-- @function [parent=#AI_AIR_ENGAGE] __EngageRoute
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnEnterEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngage
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngage
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] Engage
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_AIR_ENGAGE] __Engage
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_AIR_ENGAGE] OnEnterEngaging
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeFired
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterFired
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] Fired
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_AIR_ENGAGE] __Fired
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeDestroy
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterDestroy
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] Destroy
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_AIR_ENGAGE] __Destroy
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAbort
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterAbort
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] Abort
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_AIR_ENGAGE] __Abort
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAccomplish
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] OnAfterAccomplish
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] Accomplish
-- @param #AI_AIR_ENGAGE self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_AIR_ENGAGE] __Accomplish
-- @param #AI_AIR_ENGAGE self
-- @param #number Delay The delay in seconds.
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
return self
end
--- onafter event handler for Start event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterStart( AIGroup, From, Event, To )
self:GetParent( self, AI_AIR_ENGAGE ).onafterStart( self, AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- onafter event handler for Engage event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterEngage( AIGroup, From, Event, To )
-- TODO: This function is overwritten below!
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- onbefore event handler for Engage event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onbeforeEngage( AIGroup, From, Event, To )
if self.Accomplished == true then
return false
end
return true
end
--- onafter event handler for Abort event.
-- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks()
self:Return()
end
--- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_ENGAGE:onafterAccomplish( AIGroup, From, Event, To )
self.Accomplished = true
--self:SetDetectionOff()
end
--- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_AIR_ENGAGE:onafterDestroy( AIGroup, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_AIR_ENGAGE self
-- @param Core.Event#EVENTDATA EventData
function AI_AIR_ENGAGE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( self.TaskDelay, EventData )
end
end
end
--- @param Wrapper.Group#GROUP AIControllable
function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit )
Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName())))
if AIGroup and AIGroup:IsAlive() then
Fsm:__EngageRoute( Fsm.TaskDelay or 0.1, AttackSetUnit )
end
end
--- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked.
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
self:I( { DefenderGroup, From, Event, To, AttackSetUnit } )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
local AttackCount = AttackSetUnit:CountAlive()
if AttackCount > 0 then
if DefenderGroup:IsAlive() then
local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
-- Determine the distance to the target.
-- If it is less than 10km, then attack without a route.
-- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
-- TODO: A factor of * 3 is way too close. This causes the AI not to engange until merged sometimes!
if TargetDistance <= EngageDistance * 9 then
self:I(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000))
self:__Engage( 0.1, AttackSetUnit )
else
self:I(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000))
local EngageRoute = {}
local AttackTasks = {}
--- Calculate the target route point.
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = FromWP
self:SetTargetDistance( TargetCoord ) -- For RTB status check
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = ToWP
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___EngageRoute", self, AttackSetUnit )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
DefenderGroup:OptionROEReturnFire()
DefenderGroup:OptionROTEvadeFire()
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
end
end
else
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
end
end
--- @param Wrapper.Group#GROUP AIControllable
function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit )
Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName())))
if AIGroup and AIGroup:IsAlive() then
local delay=Fsm.TaskDelay or 0.1
Fsm:__Engage(delay, AttackSetUnit)
end
end
--- @param #AI_AIR_ENGAGE self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Set#SET_UNIT AttackSetUnit Set of units to be attacked.
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
local AttackCount = AttackSetUnit:CountAlive()
self:T({AttackCount = AttackCount})
if AttackCount > 0 then
if DefenderGroup and DefenderGroup:IsAlive() then
local EngageAltitude = math.random( self.EngageFloorAltitude or 500, self.EngageCeilingAltitude or 1000 )
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
if not TargetCoord then
self:Return()
return
end
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
local EngageRoute = {}
local AttackTasks = {}
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = FromWP
self:SetTargetDistance( TargetCoord ) -- For RTB status check
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
EngageRoute[#EngageRoute+1] = ToWP
-- TODO: A factor of * 3 this way too low. This causes the AI NOT to engage until very close or even merged sometimes. Some A2A missiles have a much longer range! Needs more frequent updates of the task!
if TargetDistance <= EngageDistance * 9 then
local AttackUnitTasks = self:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) -- Polymorphic
if #AttackUnitTasks == 0 then
self:I( DefenderGroupName .. ": No valid targets found -> Going RTB")
self:Return()
return
else
local text=string.format("%s: Engaging targets at distance %.2f NM", DefenderGroupName, UTILS.MetersToNM(TargetDistance))
self:I(text)
DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTEvadeFire()
DefenderGroup:OptionKeepWeaponsOnThreat()
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskCombo( AttackUnitTasks )
end
end
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___Engage", self, AttackSetUnit )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
end
else
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
self:I( DefenderGroupName .. ": No targets found -> returning.")
self:Return()
return
end
end
--- @param Wrapper.Group#GROUP AIEngage
function AI_AIR_ENGAGE.Resume( AIEngage, Fsm )
AIEngage:F( { "Resume:", AIEngage:GetName() } )
if AIEngage and AIEngage:IsAlive() then
Fsm:__Reset( Fsm.TaskDelay or 0.1 )
Fsm:__EngageRoute( Fsm.TaskDelay or 0.2, Fsm.AttackSetUnit )
end
end

View File

@@ -0,0 +1,398 @@
--- **AI** -- Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air_Patrol
-- @image AI_Air_To_Ground_Patrol.JPG
--- @type AI_AIR_PATROL
-- @extends AI.AI_Air#AI_AIR
--- The AI_AIR_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
-- The AI_AIR_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_PATROL process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, 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.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
-- ## 1. AI_AIR_PATROL constructor
--
-- * @{#AI_AIR_PATROL.New}(): Creates a new AI_AIR_PATROL object.
--
-- ## 2. AI_AIR_PATROL is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_AIR_PATROL States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_AIR_PATROL Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.PatrolRoute}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_AIR_PATROL.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_AIR_PATROL.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting 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.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 thresholds have been reached, the AI will RTB.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI.AI_CAP#AI_AIR_PATROL.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_AIR_PATROL.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_AIR_PATROL
AI_AIR_PATROL = {
ClassName = "AI_AIR_PATROL",
}
--- Creates a new AI_AIR_PATROL object
-- @param #AI_AIR_PATROL self
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
-- @param Wrapper.Group#GROUP AIGroup The AI group.
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO.
-- @return #AI_AIR_PATROL
function AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_PATROL
local SpeedMax = AIGroup:GetSpeedMax()
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude or 1000
self.PatrolCeilingAltitude = PatrolCeilingAltitude or 1500
self.PatrolMinSpeed = PatrolMinSpeed or SpeedMax * 0.5
self.PatrolMaxSpeed = PatrolMaxSpeed or SpeedMax * 0.75
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
--- OnBefore Transition Handler for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrol
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrol
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] Patrol
-- @param #AI_AIR_PATROL self
--- Asynchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_AIR_PATROL] __Patrol
-- @param #AI_AIR_PATROL self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Patrolling.
-- @function [parent=#AI_AIR_PATROL] OnLeavePatrolling
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Patrolling.
-- @function [parent=#AI_AIR_PATROL] OnEnterPatrolling
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Patrolling", "PatrolRoute", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
--- OnBefore Transition Handler for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrolRoute
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrolRoute
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] PatrolRoute
-- @param #AI_AIR_PATROL self
--- Asynchronous Event Trigger for Event PatrolRoute.
-- @function [parent=#AI_AIR_PATROL] __PatrolRoute
-- @param #AI_AIR_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
return self
end
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_AIR_PATROL self
-- @param #number EngageRange The Engage Range.
-- @return #AI_AIR_PATROL self
function AI_AIR_PATROL:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- Set race track parameters. CAP flights will perform race track patterns rather than randomly patrolling the zone.
-- @param #AI_AIR_PATROL self
-- @param #number LegMin Min Length of the race track leg in meters. Default 10,000 m.
-- @param #number LegMax Max length of the race track leg in meters. Default 15,000 m.
-- @param #number HeadingMin Min heading of the race track in degrees. Default 0 deg, i.e. from South to North.
-- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. from South to North.
-- @param #number DurationMin (Optional) Min duration before switching the orbit position. Default is keep same orbit until RTB or engage.
-- @param #number DurationMax (Optional) Max duration before switching the orbit position. Default is keep same orbit until RTB or engage.
-- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading.
-- @return #AI_AIR_PATROL self
function AI_AIR_PATROL:SetRaceTrackPattern(LegMin, LegMax, HeadingMin, HeadingMax, DurationMin, DurationMax, CapCoordinates)
self.racetrack=true
self.racetracklegmin=LegMin or 10000
self.racetracklegmax=LegMax or 15000
self.racetrackheadingmin=HeadingMin or 0
self.racetrackheadingmax=HeadingMax or 180
self.racetrackdurationmin=DurationMin
self.racetrackdurationmax=DurationMax
if self.racetrackdurationmax and not self.racetrackdurationmin then
self.racetrackdurationmin=self.racetrackdurationmax
end
self.racetrackcapcoordinates=CapCoordinates
end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_AIR_PATROL self
-- @return #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_PATROL:onafterPatrol( AIPatrol, From, Event, To )
self:F2()
self:ClearTargetDistance()
self:__PatrolRoute( self.TaskDelay )
AIPatrol:OnReSpawn(
function( PatrolGroup )
self:__Reset( self.TaskDelay )
self:__PatrolRoute( self.TaskDelay )
end
)
end
--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
-- @param #AI_AIR_PATROL Fsm The FSM.
function AI_AIR_PATROL.___PatrolRoute( AIPatrol, Fsm )
AIPatrol:F( { "AI_AIR_PATROL.___PatrolRoute:", AIPatrol:GetName() } )
if AIPatrol and AIPatrol:IsAlive() then
Fsm:PatrolRoute()
end
end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_AIR_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
self:F2()
-- When RTB, don't allow anymore the routing.
if From == "RTB" then
return
end
if AIPatrol and AIPatrol:IsAlive() then
local PatrolRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIPatrol:GetCoordinate()
local altitude= math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
ToTargetCoord:SetAlt( altitude )
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
local speedkmh=ToTargetSpeed
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
PatrolRoute[#PatrolRoute+1] = FromWP
if self.racetrack then
-- Random heading.
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
-- Random leg length.
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
-- Random duration if any.
local duration = self.racetrackdurationmin
if self.racetrackdurationmax then
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
end
-- CAP coordinate.
local c0=self.PatrolZone:GetRandomCoordinate()
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
end
-- Race track points.
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
self:SetTargetDistance(c0) -- For RTB status check
-- Debug:
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
--c1:MarkToAll("Race track c1")
--c2:MarkToAll("Race track c2")
-- Task to orbit.
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
-- Task function to redo the patrol at other random position.
local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
-- Controlled task with task condition.
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
-- Second waypoint
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
else
--- Create a route point of type air.
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
PatrolRoute[#PatrolRoute+1] = ToWP
local Tasks = {}
Tasks[#Tasks+1] = AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
end
AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire()
AIPatrol:Route( PatrolRoute, self.TaskDelay )
end
end
--- @param Wrapper.Group#GROUP AIPatrol
function AI_AIR_PATROL.Resume( AIPatrol, Fsm )
AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } )
if AIPatrol and AIPatrol:IsAlive() then
Fsm:__Reset( Fsm.TaskDelay )
Fsm:__PatrolRoute( Fsm.TaskDelay )
end
end

View File

@@ -0,0 +1,289 @@
--- **AI** - Models squadrons for airplanes and helicopters.
--
-- This is a class used in the @{AI_Air_Dispatcher} and derived dispatcher classes.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Air_Squadron
-- @image MOOSE.JPG
--- @type AI_AIR_SQUADRON
-- @extends Core.Base#BASE
--- Implements the core functions modeling squadrons for airplanes and helicopters.
--
-- ===
--
-- @field #AI_AIR_SQUADRON
AI_AIR_SQUADRON = {
ClassName = "AI_AIR_SQUADRON",
}
--- Creates a new AI_AIR_SQUADRON object
-- @param #AI_AIR_SQUADRON self
-- @return #AI_AIR_SQUADRON
function AI_AIR_SQUADRON:New( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
self:I( { Air_Squadron = { SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } )
local AI_Air_Squadron = BASE:New() -- #AI_AIR_SQUADRON
AI_Air_Squadron.Name = SquadronName
AI_Air_Squadron.Airbase = AIRBASE:FindByName( AirbaseName )
AI_Air_Squadron.AirbaseName = AI_Air_Squadron.Airbase:GetName()
if not AI_Air_Squadron.Airbase then
error( "Cannot find airbase with name:" .. AirbaseName )
end
AI_Air_Squadron.Spawn = {}
if type( TemplatePrefixes ) == "string" then
local SpawnTemplate = TemplatePrefixes
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
AI_Air_Squadron.Spawn[1] = self.DefenderSpawns[SpawnTemplate]
else
for TemplateID, SpawnTemplate in pairs( TemplatePrefixes ) do
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
AI_Air_Squadron.Spawn[#AI_Air_Squadron.Spawn+1] = self.DefenderSpawns[SpawnTemplate]
end
end
AI_Air_Squadron.ResourceCount = ResourceCount
AI_Air_Squadron.TemplatePrefixes = TemplatePrefixes
AI_Air_Squadron.Captured = false -- Not captured. This flag will be set to true, when the airbase where the squadron is located, is captured.
self:SetSquadronLanguage( SquadronName, "EN" ) -- Squadrons speak English by default.
return AI_Air_Squadron
end
--- Set the Name of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #string Name The Squadron Name.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetName( Name )
self.Name = Name
return self
end
--- Get the Name of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #string The Squadron Name.
function AI_AIR_SQUADRON:GetName()
return self.Name
end
--- Set the ResourceCount of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number ResourceCount The Squadron ResourceCount.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetResourceCount( ResourceCount )
self.ResourceCount = ResourceCount
return self
end
--- Get the ResourceCount of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron ResourceCount.
function AI_AIR_SQUADRON:GetResourceCount()
return self.ResourceCount
end
--- Add Resources to the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Resources The Resources to be added.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:AddResources( Resources )
self.ResourceCount = self.ResourceCount + Resources
return self
end
--- Remove Resources to the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Resources The Resources to be removed.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:RemoveResources( Resources )
self.ResourceCount = self.ResourceCount - Resources
return self
end
--- Set the Overhead of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Overhead The Squadron Overhead.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetOverhead( Overhead )
self.Overhead = Overhead
return self
end
--- Get the Overhead of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Overhead.
function AI_AIR_SQUADRON:GetOverhead()
return self.Overhead
end
--- Set the Grouping of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Grouping The Squadron Grouping.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetGrouping( Grouping )
self.Grouping = Grouping
return self
end
--- Get the Grouping of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Grouping.
function AI_AIR_SQUADRON:GetGrouping()
return self.Grouping
end
--- Set the FuelThreshold of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number FuelThreshold The Squadron FuelThreshold.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetFuelThreshold( FuelThreshold )
self.FuelThreshold = FuelThreshold
return self
end
--- Get the FuelThreshold of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron FuelThreshold.
function AI_AIR_SQUADRON:GetFuelThreshold()
return self.FuelThreshold
end
--- Set the EngageProbability of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number EngageProbability The Squadron EngageProbability.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetEngageProbability( EngageProbability )
self.EngageProbability = EngageProbability
return self
end
--- Get the EngageProbability of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron EngageProbability.
function AI_AIR_SQUADRON:GetEngageProbability()
return self.EngageProbability
end
--- Set the Takeoff of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Takeoff The Squadron Takeoff.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetTakeoff( Takeoff )
self.Takeoff = Takeoff
return self
end
--- Get the Takeoff of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Takeoff.
function AI_AIR_SQUADRON:GetTakeoff()
return self.Takeoff
end
--- Set the Landing of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number Landing The Squadron Landing.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetLanding( Landing )
self.Landing = Landing
return self
end
--- Get the Landing of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #number The Squadron Landing.
function AI_AIR_SQUADRON:GetLanding()
return self.Landing
end
--- Set the TankerName of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #string TankerName The Squadron Tanker Name.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetTankerName( TankerName )
self.TankerName = TankerName
return self
end
--- Get the Tanker Name of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @return #string The Squadron Tanker Name.
function AI_AIR_SQUADRON:GetTankerName()
return self.TankerName
end
--- Set the Radio of the Squadron.
-- @param #AI_AIR_SQUADRON self
-- @param #number RadioFrequency The frequency of communication.
-- @param #number RadioModulation The modulation of communication.
-- @param #number RadioPower The power in Watts of communication.
-- @param #string Language The language of the radio speech.
-- @return #AI_AIR_SQUADRON The Squadron.
function AI_AIR_SQUADRON:SetRadio( RadioFrequency, RadioModulation, RadioPower, Language )
self.RadioFrequency = RadioFrequency
self.RadioModulation = RadioModulation or radio.modulation.AM
self.RadioPower = RadioPower or 100
if self.RadioSpeech then
self.RadioSpeech:Stop()
end
self.RadioSpeech = nil
self.RadioSpeech = RADIOSPEECH:New( RadioFrequency, RadioModulation )
self.RadioSpeech.power = RadioPower
self.RadioSpeech:Start( 0.5 )
self.RadioSpeech:SetLanguage( Language )
return self
end

View File

@@ -49,7 +49,7 @@
-- 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.
-- 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)
--
@@ -87,7 +87,7 @@
-- 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.
--
-- 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)
--
@@ -117,7 +117,7 @@
-- * **@{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.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**
--
@@ -515,8 +515,8 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
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 ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -602,7 +602,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting
self:__Target( -2 ) -- Start targeting
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)
--
-- ===
--
@@ -196,8 +196,12 @@ function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, A
SetGroup:Flush( self )
end
--- @param #AI_BALANCER self
--- RTB
-- @param #AI_BALANCER self
-- @param Core.Set#SET_GROUP SetGroup
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Group#GROUP AIGroup
function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
@@ -213,10 +217,13 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
self:T( ClosestAirbase.AirbaseName )
--[[
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
AIGroupTemplate.route = RTBRoute
AIGroup:Respawn( AIGroupTemplate )
]]
AIGroup:RouteRTB(ClosestAirbase)
end
end

View File

@@ -4,13 +4,13 @@
--
-- * Patrol AI airplanes within a given zone.
-- * 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.
--
--
-- ===
--
-- ### [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)
--
-- Until a fuel or damage treshold 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.
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
@@ -96,7 +96,7 @@
-- * **@{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.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
--
@@ -421,15 +421,19 @@ end
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
if Controllable:IsAlive() then
if Controllable and Controllable:IsAlive() then
local EngageRoute = {}
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
if not CurrentVec2 then -- flight dead at this point
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 ToEngageZoneSpeed = self.PatrolMaxSpeed
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,
-- 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.
-- 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)
--
@@ -87,7 +87,7 @@
-- 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.
--
-- 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)
--
@@ -117,7 +117,7 @@
-- * **@{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.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()
--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 ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -520,7 +520,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting
self:__Target( -2 ) -- Start targeting
end
end

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
--- **AI** - Models the intelligent transportation of infantry and other cargo.
--
-- ===
--
@@ -35,10 +35,9 @@ AI_CARGO = {
--- Creates a new AI_CARGO object.
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param Core.Set#SET_CARGO CargoSet
-- @param #number CombatRadius
-- @return #AI_CARGO
-- @param Wrapper.Group#GROUP Carrier Cargo carrier group.
-- @param Core.Set#SET_CARGO CargoSet Set of cargo(s) to transport.
-- @return #AI_CARGO self
function AI_CARGO:New( Carrier, CargoSet )
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
@@ -48,18 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
self:SetStartState( "Unloaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
-- Board
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Reload", "*" )
self:AddTransition( "*", "Board", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
self:AddTransition( "*", "Load", "Boarding" )
self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" )
self:AddTransition( "Boarding", "Loaded", "Boarding" )
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
-- Unload
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "Unboard", "*" )
self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
--- Pickup Handler OnBefore for AI_CARGO
-- @function [parent=#AI_CARGO] OnBeforePickup
@@ -141,6 +143,17 @@ function AI_CARGO:New( Carrier, CargoSet )
-- @param #string From
-- @param #string Event
-- @param #string To
--- On after Deployed event.
-- @function [parent=#AI_CARGO] OnAfterDeployed
-- @param #AI_CARGO self
-- @param Wrapper.Group#GROUP Carrier
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
-- @param #boolean Defend Defend for APCs.
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
@@ -256,9 +269,10 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
self:F( { "In radius", CarrierUnit:GetName() } )
local CargoWeight = Cargo:GetWeight()
local CarrierSpace=Carrier_Weight[CarrierUnit]
-- Only when there is space within the bay to load the next cargo item!
if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then
if CarrierSpace > CargoWeight then
Carrier:RouteStop()
--Cargo:Ungroup()
Cargo:__Board( -LoadDelay, CarrierUnit )
@@ -275,6 +289,8 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
-- Ok, we loaded a cargo, now we can stop the loop.
break
else
self:T(string.format("WARNING: Cargo too heavy for carrier %s. Cargo=%.1f > %.1f free space", tostring(CarrierUnit:GetName()), CargoWeight, CarrierSpace))
end
end
end
@@ -554,6 +570,7 @@ end
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
-- @param #boolean Defend Defend for APCs.
function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend )
self:F( { Carrier, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
@@ -565,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
end
end

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
--- **AI** - Models the intelligent transportation of cargo using ground vehicles.
--
-- ===
--
@@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
self:AddTransition( "*", "Guard", "Unloaded" )
self:AddTransition( "*", "Home", "*" )
self:AddTransition( "*", "Reload", "Boarding" )
self:AddTransition( "*", "Deployed", "*" )
self:AddTransition( "*", "PickedUp", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
self:SetCombatRadius( CombatRadius )
@@ -157,6 +158,45 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier )
return self
end
--- Set whether or not the carrier will use roads to *pickup* and *deploy* the cargo.
-- @param #AI_CARGO_APC self
-- @param #boolean Offroad If true, carrier will not use roads. If `nil` or `false` the carrier will use roads when available.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_APC self
function AI_CARGO_APC:SetOffRoad(Offroad, Formation)
self:SetPickupOffRoad(Offroad, Formation)
self:SetDeployOffRoad(Offroad, Formation)
return self
end
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
-- @param #AI_CARGO_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_APC self
function AI_CARGO_APC:SetPickupOffRoad(Offroad, Formation)
self.pickupOffroad=Offroad
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
-- @param #AI_CARGO_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_APC self
function AI_CARGO_APC:SetDeployOffRoad(Offroad, Formation)
self.deployOffroad=Offroad
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end
--- Find a free Carrier within a radius.
-- @param #AI_CARGO_APC self
@@ -350,10 +390,13 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To )
end
--- @param #AI_CARGO_APC
-- @param Wrapper.Group#GROUP APC
function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
--- Pickup task function. Triggers Load event.
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
-- @param #AI_CARGO_APC sel `AI_CARGO_APC` class.
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
-- @param #number Speed Speed (not used).
-- @param Core.Zone#ZONE PickupZone Pickup zone.
function AI_CARGO_APC._Pickup(APC, self, Coordinate, Speed, PickupZone)
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
@@ -362,8 +405,12 @@ function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
end
end
function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone )
--- Deploy task function. Triggers Unload event.
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
-- @param #AI_CARGO_APC self `AI_CARGO_APC` class.
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
-- @param Core.Zone#ZONE DeployZone Deploy zone.
function AI_CARGO_APC._Deploy(APC, self, Coordinate, DeployZone)
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
@@ -392,12 +439,20 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, He
self.RoutePickup = true
local _speed=Speed or APC:GetSpeedMax()*0.5
-- Route on road.
local Waypoints = {}
if self.pickupOffroad then
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.pickupFormation)
Waypoints[2]=Coordinate:WaypointGround(_speed, self.pickupFormation, DCSTasks)
else
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
end
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone )
self:F({Waypoints = Waypoints})
local Waypoint = Waypoints[#Waypoints]
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
@@ -428,18 +483,34 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, He
self.RouteDeploy = true
local _speed=Speed or APC:GetSpeedMax()*0.5
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
-- Set speed in km/h.
local speedmax=APC:GetSpeedMax()
local _speed=Speed or speedmax*0.5
_speed=math.min(_speed, speedmax)
-- Route on road.
local Waypoints = {}
if self.deployOffroad then
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.deployFormation)
Waypoints[2]=Coordinate:WaypointGround(_speed, self.deployFormation, DCSTasks)
else
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
end
-- Task function
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone )
self:F({Waypoints = Waypoints})
-- Last waypoint
local Waypoint = Waypoints[#Waypoints]
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
-- Set task function
APC:SetTaskWaypoint(Waypoint, TaskFunction) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
-- Route group
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
-- Call parent function.
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
end

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo).
--- **AI** - Models the intelligent transportation of cargo using airplanes.
--
-- ===
--
@@ -76,25 +76,31 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
-- @param #number Speed in km/h for travelling to pickup base.
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
--- Pickup Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
-- @param #number Speed in km/h for travelling to pickup base.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
-- @param #AI_CARGO_AIRPLANE self
-- @param #number Delay Delay in seconds.
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
-- @param #number Speed in km/h for travelling to pickup base.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
@@ -111,24 +117,30 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
-- @param #number Speed Speed in km/h for travelling to deploy base.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
--- Deploy Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
-- @param #number Speed Speed in km/h for travelling to deploy base.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
-- @param #AI_CARGO_AIRPLANE self
-- @param #number Delay Delay in seconds.
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
-- @param #number Speed Speed in km/h for travelling to deploy base.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
--- On after Loaded event, i.e. triggered when the cargo is inside the carrier.
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterLoaded
@@ -137,6 +149,16 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
-- @param From
-- @param Event
-- @param To
--- On after Deployed event.
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeployed
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed.
-- Set carrier.
self:SetCarrier( Airplane )
@@ -259,15 +281,17 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed in km/h for travelling to pickup base.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
-- @param #number Speed Speed in km/h for travelling to pickup base.
-- @param #number Height Height in meters to move to the pickup coordinate.
-- @param Core.Zone#ZONE_AIRBASE (optional) PickupZone The zone where the cargo will be picked up.
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
if Airplane and Airplane:IsAlive() then
self.PickupZone = PickupZone
local airbasepickup=Coordinate:GetClosestAirbase()
self.PickupZone = PickupZone or ZONE_AIRBASE:New(airbasepickup:GetName())
-- Get closest airbase of current position.
local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase()
@@ -280,11 +304,10 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate,
end
-- Set pickup airbase.
local Airbase = PickupZone:GetAirbase()
local Airbase = self.PickupZone:GetAirbase()
-- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase.
local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate())
--env.info("Distance closest to pickup airbase = "..Dist)
if Airplane:InAir() or Dist>500 then
@@ -305,7 +328,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate,
end
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, self.PickupZone )
end
@@ -318,15 +341,19 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed in km/h for travelling to pickup base.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
-- @param #number Speed Speed in km/h for travelling to the deploy base.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The zone where the cargo will be deployed.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone )
if Airplane and Airplane:IsAlive()~=nil then
local Airbase = DeployZone:GetAirbase()
local Airbase = Coordinate:GetClosestAirbase()
if DeployZone then
Airbase=DeployZone:GetAirbase()
end
-- Activate uncontrolled airplane.
if Airplane:IsAlive()==false then
@@ -354,6 +381,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone )
local UnboardInterval = 10
@@ -380,9 +408,6 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone
end
--- Route the airplane from one airport or it's current position to another airbase.
-- @param #AI_CARGO_AIRPLANE self
-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed.
@@ -410,14 +435,10 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled
-- To point.
local AirbasePointVec2 = Airbase:GetPointVec2()
local ToWaypoint = AirbasePointVec2:WaypointAir(
POINT_VEC3.RoutePointAltType.BARO,
"Land",
"Landing",
Speed or Airplane:GetSpeedMax()*0.8
)
ToWaypoint["airdromeId"] = Airbase:GetID()
ToWaypoint["speed_locked"] = true
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
--ToWaypoint["airdromeId"] = Airbase:GetID()
--ToWaypoint["speed_locked"] = true
-- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned.

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
--- **AI** - Models the intelligent transportation of infantry and other cargo.
--
-- ## Features:
--
@@ -1104,7 +1104,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor()
-- The Pickup sequence ...
-- Check if this Carrier need to go and Pickup something...
-- So, if the cargo bay is not full yet with cargo to be loaded ...
self:I( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
self:T( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
-- ok, so there is a free Carrier
-- now find the first cargo that is Unloaded
@@ -1151,6 +1151,10 @@ function AI_CARGO_DISPATCHER:onafterMonitor()
self.PickupCargo[Carrier] = CargoCoordinate
PickupCargo = Cargo
break
else
local text=string.format("WARNING: Cargo %s is too heavy to be loaded into transport. Cargo weight %.1f > %.1f load capacity of carrier %s.",
tostring(Cargo:GetName()), Cargo:GetWeight(), LargestLoadCapacity, tostring(Carrier:GetName()))
self:I(text)
end
end
end

View File

@@ -1,4 +1,4 @@
--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using APCs.
--- **AI** - Models the intelligent transportation of infantry and other cargo using APCs.
--
-- ## Features:
--
@@ -181,25 +181,36 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon
return self
end
--- AI cargo
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param Wrapper.Group#GROUP APC The APC carrier.
-- @param Core.Set#SET_CARGO CargoSet Cargo set.
-- @return AI.AI_Cargo_APC#AI_CARGO_DISPATCHER_APC AI cargo APC object.
function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet )
return AI_CARGO_APC:New( APC, CargoSet, self.CombatRadius )
local aicargoapc=AI_CARGO_APC:New(APC, CargoSet, self.CombatRadius)
aicargoapc:SetDeployOffRoad(self.deployOffroad, self.deployFormation)
aicargoapc:SetPickupOffRoad(self.pickupOffroad, self.pickupFormation)
return aicargoapc
end
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
-- This is only valid for APCs and trucks etc, thus ground vehicles.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
-- When the combat radius is 0, no defense will happen of the carrier.
-- When the combat radius is 0 (default), no defense will happen of the carrier.
-- When the combat radius is not provided, no defense will happen!
-- @return #AI_CARGO_DISPATCHER_APC
-- @usage
--
-- -- Disembark the infantry when the carrier is under attack.
-- AICargoDispatcher:SetCombatRadius( true )
-- AICargoDispatcher:SetCombatRadius( 500 )
--
-- -- Keep the cargo in the carrier when the carrier is under attack.
-- AICargoDispatcher:SetCombatRadius( false )
-- AICargoDispatcher:SetCombatRadius( 0 )
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
self.CombatRadius = CombatRadius or 0
@@ -207,3 +218,41 @@ function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
return self
end
--- Set whether the carrier will *not* use roads to *pickup* and *deploy* the cargo.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_DISPATCHER_APC self
function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad, Formation)
self:SetPickupOffRoad(Offroad, Formation)
self:SetDeployOffRoad(Offroad, Formation)
return self
end
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_DISPATCHER_APC self
function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad, Formation)
self.pickupOffroad=Offroad
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
-- @param #AI_CARGO_DISPATCHER_APC self
-- @param #boolean Offroad If true, carrier will not use roads.
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
-- @return #AI_CARGO_DISPATCHER_APC self
function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad, Formation)
self.deployOffroad=Offroad
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
return self
end

View File

@@ -0,0 +1,193 @@
--- **AI** -- (2.5.1) - Models the intelligent transportation of infantry and other cargo using Ships
--
-- ## Features:
--
-- * Transport cargo to various deploy zones using naval vehicles.
-- * Various @{Cargo.Cargo#CARGO} types can be transported, including infantry, vehicles, and crates.
-- * Define a deploy zone of various types to determine the destination of the cargo.
-- * Ships will follow shipping lanes as defined in the Mission Editor.
-- * Multiple ships can transport multiple cargo as a single group.
--
-- ===
--
-- ## Test Missions:
--
-- NEED TO DO
--
-- ===
--
-- ### Author: **acrojason** (derived from AI_Cargo_Dispatcher_APC by FlightControl)
--
-- ===
--
-- @module AI.AI_Cargo_Dispatcher_Ship
-- @image AI_Cargo_Dispatching_For_Ship.JPG
--- @type AI_CARGO_DISPATCHER_SHIP
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
--- A dynamic cargo transportation capability for AI groups.
--
-- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation.
--
-- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module.
--
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_SHIP class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
--
-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**.
--
-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framwork.
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo.
--
--
-- # 1) AI_CARGO_DISPATCHER_SHIP constructor.
--
-- * @{AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object.
--
-- ---
--
-- # 2) AI_CARGO_DISPATCHER_SHIP is a Finite State Machine.
--
-- This section must be read as follows... Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
--
-- So, each of the rows have the following structure.
--
-- * **From** => **Event** => **To**
--
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
-- and the resulting state will be the **To** state.
--
-- These are the different possible state transitions of this state machine implementation:
--
-- * Idle => Start => Monitoring
-- * Monitoring => Monitor => Monitoring
-- * Monitoring => Stop => Idle
--
-- * Monitoring => Pickup => Monitoring
-- * Monitoring => Load => Monitoring
-- * Monitoring => Loading => Monitoring
-- * Monitoring => Loaded => Monitoring
-- * Monitoring => PickedUp => Monitoring
-- * Monitoring => Deploy => Monitoring
-- * Monitoring => Unload => Monitoring
-- * Monitoring => Unloaded => Monitoring
-- * Monitoring => Deployed => Monitoring
-- * Monitoring => Home => Monitoring
--
--
-- ## 2.1) AI_CARGO_DISPATCHER States.
--
-- * **Monitoring**: The process is dispatching.
-- * **Idle**: The process is idle.
--
-- ## 2.2) AI_CARGO_DISPATCHER Events.
--
-- * **Start**: Start the transport process.
-- * **Stop**: Stop the transport process.
-- * **Monitor**: Monitor and take action.
--
-- * **Pickup**: Pickup cargo.
-- * **Load**: Load the cargo.
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
-- * **Loaded**: Flag that the cargo is loaded.
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
-- * **Deploy**: Deploy cargo to a location.
-- * **Unload**: Unload the cargo.
-- * **Unloaded**: Flag that the cargo is unloaded.
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
-- * **Home**: A Carrier is going home.
--
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
--
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
--
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
--
-- ---
--
-- # 3) Set the pickup parameters.
--
-- Several parameters can be set to pickup cargo:
--
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupRadius}(): Sets or randomizes the pickup location for the Ship around the cargo coordinate in a radius defined an outer and optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
--
-- # 4) Set the deploy parameters.
--
-- Several parameters can be set to deploy cargo:
--
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeployRadius}(): Sets or randomizes the deploy location for the Ship around the cargo coordinate in a radius defined an outer and an optional inner radius.
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
--
-- # 5) Set the home zone when there isn't any more cargo to pickup.
--
-- A home zone can be specified to where the Ship will move when there isn't any cargo left for pickup.
-- Use @{#AI_CARGO_DISPATCHER_SHIP.SetHomeZone}() to specify the home zone.
--
-- If no home zone is specified, the Ship will wait near the deploy zone for a new pickup command.
--
-- ===
--
-- @field #AI_CARGO_DISPATCHER_SHIP
AI_CARGO_DISPATCHER_SHIP = {
ClassName = "AI_CARGO_DISPATCHER_SHIP"
}
--- Creates a new AI_CARGO_DISPATCHER_SHIP object.
-- @param #AI_CARGO_DISPATCHER_SHIP self
-- @param Core.Set#SET_GROUP ShipSet The set of @{Wrapper.Group#GROUP} objects of Ships that will transport the cargo
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, or CARGO_SLINGLOAD objects.
-- @param Core.Set#SET_ZONE PickupZoneSet The set of pickup zones which are used to determine from where the cargo can be picked up by the Ship.
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones which determine where the cargo will be deployed by the Ship.
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
-- @return #AI_CARGO_DISPATCHER_SHIP
-- @usage
--
-- -- An AI dispatcher object for a naval group, moving cargo from pickup zones to deploy zones via a predetermined Shipping Lane
--
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
-- local SetShip = SET_GROUP:New():FilterPrefixes( "Ship" ):FilterStart()
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
-- NEED MORE THOUGHT - ShippingLane is part of Warehouse.......
-- local ShippingLane = GROUP:New():FilterPrefixes( "ShippingLane" ):FilterStart()
--
-- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane )
-- AICargoDispatcherShip:Start()
--
function AI_CARGO_DISPATCHER_SHIP:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet, ShippingLane )
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet ) )
self:SetPickupSpeed( 60, 10 )
self:SetDeploySpeed( 60, 10 )
self:SetPickupRadius( 500, 6000 )
self:SetDeployRadius( 500, 6000 )
self:SetPickupHeight( 0, 0 )
self:SetDeployHeight( 0, 0 )
self:SetShippingLane( ShippingLane )
self:SetMonitorTimeInterval( 600 )
return self
end
function AI_CARGO_DISPATCHER_SHIP:SetShippingLane( ShippingLane )
self.ShippingLane = ShippingLane
return self
end
function AI_CARGO_DISPATCHER_SHIP:AICargo( Ship, CargoSet )
return AI_CARGO_SHIP:New( Ship, CargoSet, 0, self.ShippingLane )
end

View File

@@ -1,4 +1,4 @@
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo).
--- **AI** - Models the intelligent transportation of cargo using helicopters.
--
-- ===
--
@@ -33,7 +33,7 @@
--
-- ## Infantry health.
--
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
-- When infantry is unboarded from the helicopters, the infantry is actually respawned into the battlefield.
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
@@ -64,25 +64,24 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
self:SetStartState( "Unloaded" )
-- Boarding
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Load", "*" )
self:AddTransition( "*", "Loaded", "Loaded" )
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
self:AddTransition( "Unloaded", "Pickup", "*" )
self:AddTransition( "Loaded", "Deploy", "*" )
-- Unboarding
self:AddTransition( "Loaded", "Deploy", "*" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" )
self:AddTransition( "*", "Destroyed", "*" )
self:AddTransition( "*", "Unload", "*" )
self:AddTransition( "*", "Unloaded", "Unloaded" )
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" )
self:AddTransition( "Boarding", "Board", "Boarding" )
self:AddTransition( "Boarding", "Loaded", "Boarding" )
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
self:AddTransition( "Loaded", "Unload", "Unboarding" )
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
self:AddTransition( "*", "Landed", "*" )
self:AddTransition( "*", "Queue", "*" )
self:AddTransition( "*", "Orbit" , "*" )
-- RTB
self:AddTransition( "*", "Home" , "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
@@ -100,13 +99,31 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- PickedUp Handler OnAfter for AI_CARGO_HELICOPTER - Cargo set has been picked up, ready to deploy
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickedUp
-- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter The helicopter #GROUP object
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
--- Unloaded Handler OnAfter for AI_CARGO_HELICOPTER - Cargo unloaded, carrier is empty
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterUnloaded
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo The #CARGO_GROUP object.
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
--- Pickup Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
@@ -122,7 +139,7 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate Place at which cargo is deployed.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @return #boolean
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
@@ -132,21 +149,42 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Deployed Handler OnAfter for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeployed
-- @param #AI_CARGO_HELICOPTER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Deploy Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
-- @param #number Delay Delay in seconds.
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
--- Home Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] Home
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height (optional) Height the Helicopter should be flying at.
--- Home Asynchronous Trigger for AI_CARGO_HELICOPTER
-- @function [parent=#AI_CARGO_HELICOPTER] __Home
-- @param #number Delay Delay in seconds.
-- @param #AI_CARGO_HELICOPTER self
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height (optional) Height the Helicopter should be flying at.
-- We need to capture the Crash events for the helicopters.
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
@@ -173,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
self:SetCarrier( Helicopter )
self.landingspeed = 15 -- kph
self.landingheight = 5.5 -- meter
return self
end
@@ -221,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
return self
end
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
-- @param #AI_CARGO_HELICOPTER self
-- @param #number speed Landing speed in kph(!), e.g. 15
-- @param #number height Landing height in meters(!), e.g. 5.5
-- @return #AI_CARGO_HELICOPTER self
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
--
-- BASE:TraceOn()
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
--
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
-- Adjust if necessary.
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
local _speed = speed or 15
local _height = height or 5.5
self.landingheight = _height
self.landingspeed = _speed
return self
end
--- @param #AI_CARGO_HELICOPTER self
-- @param Wrapper.Group#GROUP Helicopter
@@ -228,7 +288,7 @@ end
-- @param Event
-- @param To
function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
self:F({From, Event, To})
Helicopter:F( { Name = Helicopter:GetName() } )
if Helicopter and Helicopter:IsAlive() then
@@ -237,13 +297,13 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
-- 1 - When the helo lands normally on the ground.
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
-- For point 2, this is an issue, the infantry may not unload in this case!
-- So we check if the helo is on the ground, and velocity< 5.
-- So we check if the helo is on the ground, and velocity< 15.
-- Only then the infantry can unload (and load too, for consistency)!
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
if self.RoutePickup == true then
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
--self:Load( Helicopter:GetPointVec2() )
self:Load( self.PickupZone )
self.RoutePickup = false
@@ -251,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
end
if self.RouteDeploy == true then
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
self:Unload( self.DeployZone )
self.RouteDeploy = false
end
@@ -269,7 +329,7 @@ end
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone )
self:F({From, Event, To, Coordinate, Speed, DeployZone})
local HelicopterInZone = false
if Helicopter and Helicopter:IsAlive() == true then
@@ -308,7 +368,11 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
-- true
-- )
-- Route[#Route+1] = WaypointFrom
local CoordinateTo = Coordinate
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
@@ -348,28 +412,17 @@ end
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Speed
function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate )
self:F({From, Event, To, Coordinate})
if Helicopter and Helicopter:IsAlive() then
local Route = {}
-- local CoordinateFrom = Helicopter:GetCoordinate()
-- local WaypointFrom = CoordinateFrom:WaypointAir(
-- "RADIO",
-- POINT_VEC3.RoutePointType.TurningPoint,
-- POINT_VEC3.RoutePointAction.TurningPoint,
-- Speed,
-- true
-- )
-- Route[#Route+1] = WaypointFrom
local CoordinateTo = Coordinate
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
50,
true
)
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
Route[#Route+1] = WaypointTo
local Tasks = {}
@@ -379,7 +432,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route( Route, 0 )
Helicopter:Route(Route, 0)
end
end
@@ -395,7 +448,7 @@ end
-- @param #boolean Deployed Cargo is deployed.
-- @return #boolean True if all cargo has been unloaded.
function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone )
self:F( { Helicopter, From, Event, To, DeployZone = DeployZone } )
self:F( { From, Event, To, DeployZone = DeployZone } )
self:Orbit( Helicopter:GetCoordinate(), 50 )
@@ -408,7 +461,6 @@ function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, Deplo
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone )
end
--- On after Pickup event.
@@ -418,11 +470,12 @@ end
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Pickup place.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone )
self:F({Coordinate, Speed, Height, PickupZone })
if Helicopter and Helicopter:IsAlive() ~= nil then
Helicopter:Activate()
@@ -436,25 +489,16 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin
--- Calculate the target route point.
local CoordinateFrom = Helicopter:GetCoordinate()
local CoordinateTo = Coordinate
--- Create a route point of type air.
local WaypointFrom = CoordinateFrom:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
_speed,
true
)
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
--- Create a route point of type air.
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
_speed,
true
)
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
Route[#Route+1] = WaypointFrom
Route[#Route+1] = WaypointTo
@@ -495,10 +539,10 @@ end
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the deploy coordinate.
function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone )
self:F({From, Event, To, Coordinate, Speed, Height, DeployZone})
if Helicopter and Helicopter:IsAlive() ~= nil then
self.RouteDeploy = true
@@ -514,25 +558,17 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
--- Create a route point of type air.
local CoordinateFrom = Helicopter:GetCoordinate()
local WaypointFrom = CoordinateFrom:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
_speed,
true
)
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
Route[#Route+1] = WaypointFrom
Route[#Route+1] = WaypointFrom
--- Create a route point of type air.
local CoordinateTo = Coordinate
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
_speed,
true
)
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
Route[#Route+1] = WaypointTo
Route[#Route+1] = WaypointTo
@@ -542,7 +578,9 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
local Tasks = {}
-- The _Deploy function does not exist.
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone )
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
@@ -566,11 +604,12 @@ end
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Home place.
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
-- @param #number Height Height in meters to move to the home coordinate.
-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, Height, HomeZone )
self:F({From, Event, To, Coordinate, Speed, Height})
if Helicopter and Helicopter:IsAlive() ~= nil then
self.RouteHome = true
@@ -579,30 +618,23 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
--- Calculate the target route point.
Coordinate.y = Height
--Coordinate.y = Height
Height = Height or 50
Speed = Speed or Helicopter:GetSpeedMax()*0.5
--- Create a route point of type air.
local CoordinateFrom = Helicopter:GetCoordinate()
local WaypointFrom = CoordinateFrom:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
Speed ,
true
)
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
Route[#Route+1] = WaypointFrom
--- Create a route point of type air.
local CoordinateTo = Coordinate
local WaypointTo = CoordinateTo:WaypointAir(
"RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
Speed ,
true
)
local CoordinateTo = Coordinate
local landheight = CoordinateTo:GetLandHeight() -- get target height
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
Route[#Route+1] = WaypointTo
@@ -613,12 +645,11 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
Route[#Route].task = Helicopter:TaskCombo( Tasks )
Route[#Route+1] = WaypointTo
-- Now route the helicopter
Helicopter:Route( Route, 0 )
Helicopter:Route(Route, 0)
end
end

View File

@@ -0,0 +1,397 @@
--- **AI** -- (R2.5.1) - Models the intelligent transportation of infantry and other cargo.
--
-- ===
--
-- ### Author: **acrojason** (derived from AI_Cargo_APC by FlightControl)
--
-- ===
--
-- @module AI.AI_Cargo_Ship
-- @image AI_Cargo_Dispatching_For_Ship.JPG
--- @type AI_CARGO_SHIP
-- @extends AI.AI_Cargo#AI_CARGO
--- Brings a dynamic cargo handling capability for an AI naval group.
--
-- Naval ships can be utilized to transport cargo around the map following naval shipping lanes.
-- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
-- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo.
-- Please consult the @{Cargo.Cargo} module for more information.
--
-- ## Cargo loading.
--
-- The module will automatically load cargo when the Ship is within boarding or loading radius.
-- The boarding or loading radius is specified when the cargo is created in the simulation and depends on the type of
-- cargo and the specified boarding radius.
--
-- ## Defending the Ship when enemies are nearby
-- This is not supported for naval cargo because most tanks don't float. Protect your transports...
--
-- ## Infantry or cargo **health**.
-- When cargo is unboarded from the Ship, the cargo is actually respawned into the battlefield.
-- As a result, the unboarding cargo is very _healthy_ every time it unboards.
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of newly spawned units as a parameter.
-- However, cargo that was destroyed when unboarded and following the Ship won't be respawned again (this is likely not a thing for
-- naval cargo due to the lack of support for defending the Ship mentioned above). Destroyed is destroyed.
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance
-- this has marginal impact on the overall battlefield simulation. Given the relatively short duration of DCS missions and the somewhat
-- lengthy naval transport times, most units entering the Ship as cargo will be freshly en route to an amphibious landing or transporting
-- between warehouses.
--
-- ## Control the Ships on the map.
--
-- Currently, naval transports can only be controlled via scripts due to their reliance upon predefined Shipping Lanes created in the Mission
-- Editor. An interesting future enhancement could leverage new pathfinding functionality for ships in the Ops module.
--
-- ## Cargo deployment.
--
-- Using the @{AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the
-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
--
-- ## Cargo pickup.
--
-- Using the @{AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified
-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
--
--
-- @field #AI_CARGO_SHIP
AI_CARGO_SHIP = {
ClassName = "AI_CARGO_SHIP",
Coordinate = nil -- Core.Point#COORDINATE
}
--- Creates a new AI_CARGO_SHIP object.
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP Ship The carrier Ship group
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When CombatRadius is 0, no defense will occur.
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
-- @return #AI_CARGO_SHIP
function AI_CARGO_SHIP:New( Ship, CargoSet, CombatRadius, ShippingLane )
local self = BASE:Inherit( self, AI_CARGO:New( Ship, CargoSet ) ) -- #AI_CARGO_SHIP
self:AddTransition( "*", "Monitor", "*" )
self:AddTransition( "*", "Destroyed", "Destroyed" )
self:AddTransition( "*", "Home", "*" )
self:SetCombatRadius( 0 ) -- Don't want to deploy cargo in middle of water to defend Ship, so set CombatRadius to 0
self:SetShippingLane ( ShippingLane )
self:SetCarrier( Ship )
return self
end
--- Set the Carrier
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP CargoCarrier
-- @return #AI_CARGO_SHIP
function AI_CARGO_SHIP:SetCarrier( CargoCarrier )
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUIP
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_SHIP", self )
CargoCarrier:HandleEvent( EVENTS.Dead )
function CargoCarrier:OnEventDead( EventData )
self:F({"dead"})
local AICargoTroops = self:GetState( self, "AI_CARGO_SHIP" )
self:F({AICargoTroops=AICargoTroops})
if AICargoTroops then
self:F({})
if not AICargoTroops:Is( "Loaded" ) then
-- Better hope they can swim!
AICargoTroops:Destroyed()
end
end
end
self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius )
self.Coalition = self.CargoCarrier:GetCoalition()
self:SetControllable( CargoCarrier )
return self
end
--- FInd a free Carrier within a radius
-- @param #AI_CARGO_SHIP self
-- @param Core.Point#COORDINATE Coordinate
-- @param #number Radius
-- @return Wrapper.Group#GROUP NewCarrier
function AI_CARGO_SHIP:FindCarrier( Coordinate, Radius )
local CoordinateZone = ZONE_RADIUS:New( "Zone", Coordinate:GetVec2(), Radius )
CoordinateZone:Scan( { Object.Category.UNIT } )
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
local NearUnit = UNIT:Find( DCSUnit )
self:F({NearUnit=NearUnit})
if not NearUnit:GetState( NearUnit, "AI_CARGO_SHIP" ) then
local Attributes = NearUnit:GetDesc()
self:F({Desc=Attributes})
if NearUnit:HasAttributes( "Trucks" ) then
return NearUnit:GetGroup()
end
end
end
return nil
end
function AI_CARGO_SHIP:SetShippingLane( ShippingLane )
self.ShippingLane = ShippingLane
return self
end
function AI_CARGO_SHIP:SetCombatRadius( CombatRadius )
self.CombatRadius = CombatRadius or 0
return self
end
--- Follow Infantry to the Carrier
-- @param #AI_CARGO_SHIP self
-- @param #AI_CARGO_SHIP Me
-- @param Wrapper.Unit#UNIT ShipUnit
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
-- @return #AI_CARGO_SHIP
function AI_CARGO_SHIP:FollowToCarrier( Me, ShipUnit, CargoGroup )
local InfantryGroup = CargoGroup:GetGroup()
self:F( { self=self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
if ShipUnit:IsAlive() then
-- Check if the Cargo is near the CargoCarrier
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", ShipUnit, 1000 ) ) then
-- Cargo does not need to navigate to Carrier
Me:Guard()
else
self:F( { InfantryGroup = InfantryGroup:GetName() } )
if InfantryGroup:IsAlive() then
self:F( { InfantryGroup = InfantryGroup:GetName() } )
local Waypoints = {}
-- Calculate new route
local FromCoord = InfantryGroup:GetCoordinate()
local FromGround = FromCoord:WaypointGround( 10, "Diamond" )
self:F({FromGround=FromGround})
table.insert( Waypoints, FromGround )
local ToCoord = ShipUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 )
local ToGround = ToCoord:WaypointGround( 10, "Diamond" )
self:F({ToGround=ToGround})
table.insert( Waypoints, ToGround )
local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_SHIP.FollowToCarrier", Me, ShipUnit, CargoGroup )
self:F({Waypoints=Waypoints})
local Waypoint = Waypoints[#Waypoints]
InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random number of seconds to the Route. See Route method for details
end
end
end
end
function AI_CARGO_SHIP:onafterMonitor( Ship, From, Event, To )
self:F( { Ship, From, Event, To, IsTransporting = self:IsTransporting() } )
if self.CombatRadius > 0 then
-- We really shouldn't find ourselves in here for Ships since the CombatRadius should always be 0.
-- This is to avoid Unloading the Ship in the middle of the sea.
if Ship and Ship:IsAlive() then
if self.CarrierCoordinate then
if self:IsTransporting() == true then
local Coordinate = Ship:GetCoordinate()
if self:Is( "Unloaded" ) or self:Is( "Loaded" ) then
self.Zone:Scan( { Object.Category.UNIT } )
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
if self:Is( "Unloaded" ) then
-- There are no enemies within combat radius. Reload the CargoCarrier.
self:Reload()
end
else
if self:Is( "Loaded" ) then
-- There are enemies within combat radius. Unload the CargoCarrier.
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
else
if self:Is( "Unloaded" ) then
--self:Follow()
end
self:F( "I am here" .. self:GetCurrentState() )
if self:Is( "Following" ) then
for Cargo, ShipUnit in pairs( self.Carrier_Cargo ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
if Cargo:IsAlive() then
if not Cargo:IsNear( ShipUnit, 40 ) then
ShipUnit:RouteStop()
self.CarrierStopped = true
else
if self.CarrierStopped then
if Cargo:IsNear( ShipUnit, 25 ) then
ShipUnit:RouteResume()
self.CarrierStopped = nil
end
end
end
end
end
end
end
end
end
end
end
self.CarrierCoordinate = Ship:GetCoordinate()
end
self:__Monitor( -5 )
end
end
--- Check if cargo ship is alive and trigger Load event
-- @param Wrapper.Group#Group Ship
-- @param #AI_CARGO_SHIP self
function AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
Ship:F( { "AI_CARGO_Ship._Pickup:", Ship:GetName() } )
if Ship:IsAlive() then
self:Load( PickupZone )
end
end
--- Check if cargo ship is alive and trigger Unload event. Good time to remind people that Lua is case sensitive and Unload != UnLoad
-- @param Wrapper.Group#GROUP Ship
-- @param #AI_CARGO_SHIP self
function AI_CARGO_SHIP._Deploy( Ship, self, Coordinate, DeployZone )
Ship:F( { "AI_CARGO_Ship._Deploy:", Ship } )
if Ship:IsAlive() then
self:Unload( DeployZone )
end
end
--- on after Pickup event.
-- @param AI_CARGO_SHIP Ship
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate of the pickup point
-- @param #number Speed Speed in km/h to sail to the pickup coordinate. Default is 50% of max speed for the unit
-- @param #number Height Altitude in meters to move to the pickup coordinate. This parameter is ignored for Ships
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil if there was no PickupZoneSet provided
function AI_CARGO_SHIP:onafterPickup( Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
if Ship and Ship:IsAlive() then
AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
self:GetParent( self, AI_CARGO_SHIP ).onafterPickup( self, Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
end
end
--- On after Deploy event.
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP SHIP
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate Coordinate of the deploy point
-- @param #number Speed Speed in km/h to sail to the deploy coordinate. Default is 50% of max speed for the unit
-- @param #number Height Altitude in meters to move to the deploy coordinate. This parameter is ignored for Ships
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
function AI_CARGO_SHIP:onafterDeploy( Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
if Ship and Ship:IsAlive() then
Speed = Speed or Ship:GetSpeedMax()*0.8
local lane = self.ShippingLane
if lane then
local Waypoints = {}
for i=1, #lane do
local coord = lane[i]
local Waypoint = coord:WaypointGround(_speed)
table.insert(Waypoints, Waypoint)
end
local TaskFunction = Ship:TaskFunction( "AI_CARGO_SHIP._Deploy", self, Coordinate, DeployZone )
local Waypoint = Waypoints[#Waypoints]
Ship:SetTaskWaypoint( Waypoint, TaskFunction )
Ship:Route(Waypoints, 1)
self:GetParent( self, AI_CARGO_SHIP ).onafterDeploy( self, Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
else
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
end
end
end
--- On after Unload event.
-- @param #AI_CARGO_SHIP self
-- @param Wrapper.Group#GROUP Ship
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
function AI_CARGO_SHIP:onafterUnload( Ship, From, Event, To, DeployZone, Defend )
self:F( { Ship, From, Event, To, DeployZone, Defend = Defend } )
local UnboardInterval = 5
local UnboardDelay = 5
if Ship and Ship:IsAlive() then
for _, ShipUnit in pairs( Ship:GetUnits() ) do
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
Ship:RouteStop()
for _, Cargo in pairs( ShipUnit:GetCargo() ) do
self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } )
if Cargo:IsLoaded() then
local unboardCoord = DeployZone:GetRandomPointVec2()
Cargo:__UnBoard( UnboardDelay, unboardCoord, 1000)
UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval
self:__Unboard( UnboardDelay, Cargo, ShipUnit, DeployZone, Defend )
if not Defend == true then
Cargo:SetDeployed( true )
end
end
end
end
end
end
function AI_CARGO_SHIP:onafterHome( Ship, From, Event, To, Coordinate, Speed, Height, HomeZone )
if Ship and Ship:IsAlive() then
self.RouteHome = true
Speed = Speed or Ship:GetSpeedMax()*0.8
local lane = self.ShippingLane
if lane then
local Waypoints = {}
-- Need to find a more generalized way to do this instead of reversing the shipping lane.
-- This only works if the Source/Dest route waypoints are numbered 1..n and not n..1
for i=#lane, 1, -1 do
local coord = lane[i]
local Waypoint = coord:WaypointGround(_speed)
table.insert(Waypoints, Waypoint)
end
local Waypoint = Waypoints[#Waypoints]
Ship:Route(Waypoints, 1)
else
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
--- **AI** - Models the automatic assignment of AI escorts to player flights.
--
-- ## Features:
-- --
-- * Provides the facilities to trigger escorts when players join flight slots.
-- *
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_Escort_Dispatcher
-- @image MOOSE.JPG
--- @type AI_ESCORT_DISPATCHER
-- @extends Core.Fsm#FSM
--- Models the automatic assignment of AI escorts to player flights.
--
-- ===
--
-- @field #AI_ESCORT_DISPATCHER
AI_ESCORT_DISPATCHER = {
ClassName = "AI_ESCORT_DISPATCHER",
}
--- @field #list
AI_ESCORT_DISPATCHER.AI_Escorts = {}
--- Creates a new AI_ESCORT_DISPATCHER object.
-- @param #AI_ESCORT_DISPATCHER self
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are spawned in.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
-- @return #AI_ESCORT_DISPATCHER
-- @usage
--
-- -- Create a new escort when a player joins an SU-25T plane.
-- Create a carrier set, which contains the player slots that can be joined by the players, for which escorts will be defined.
-- local Red_SU25T_CarrierSet = SET_GROUP:New():FilterPrefixes( "Red A2G Player Su-25T" ):FilterStart()
--
-- -- Create a spawn object that will spawn in the escorts, once the player has joined the player slot.
-- local Red_SU25T_EscortSpawn = SPAWN:NewWithAlias( "Red A2G Su-25 Escort", "Red AI A2G SU-25 Escort" ):InitLimit( 10, 10 )
--
-- -- Create an airbase object, where the escorts will be spawned.
-- local Red_SU25T_Airbase = AIRBASE:FindByName( AIRBASE.Caucasus.Maykop_Khanskaya )
--
-- -- Park the airplanes at the airbase, visible before start.
-- Red_SU25T_EscortSpawn:ParkAtAirbase( Red_SU25T_Airbase, AIRBASE.TerminalType.OpenMedOrBig )
--
-- -- New create the escort dispatcher, using the carrier set, the escort spawn object at the escort airbase.
-- -- Provide a name of the escort, which will be also the name appearing on the radio menu for the group.
-- -- And a briefing to appear when the player joins the player slot.
-- Red_SU25T_EscortDispatcher = AI_ESCORT_DISPATCHER:New( Red_SU25T_CarrierSet, Red_SU25T_EscortSpawn, Red_SU25T_Airbase, "Escort Su-25", "You Su-25T is escorted by one Su-25. Use the radio menu to control the escorts." )
--
-- -- The dispatcher needs to be started using the :Start() method.
-- Red_SU25T_EscortDispatcher:Start()
function AI_ESCORT_DISPATCHER:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER
self.CarrierSet = CarrierSet
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
self.EscortName = EscortName
self.EscortBriefing = EscortBriefing
self:SetStartState( "Idle" )
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
self:AddTransition( "Idle", "Start", "Monitoring" )
self:AddTransition( "Monitoring", "Stop", "Idle" )
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
self:F( { Carrier = Carrier:GetName() } )
end
return self
end
function AI_ESCORT_DISPATCHER:onafterStart( From, Event, To )
self:HandleEvent( EVENTS.Birth )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventExit )
self:HandleEvent( EVENTS.Crash, self.OnEventExit )
self:HandleEvent( EVENTS.Dead, self.OnEventExit )
end
--- @param #AI_ESCORT_DISPATCHER self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER:OnEventExit( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
self:I({EscortAirbase= self.EscortAirbase } )
self:I({PlayerGroupName = PlayerGroupName } )
self:I({PlayerGroup = PlayerGroup})
self:I({FirstGroup = self.CarrierSet:GetFirst()})
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if self.AI_Escorts[PlayerGroupName] then
self.AI_Escorts[PlayerGroupName]:Stop()
self.AI_Escorts[PlayerGroupName] = nil
end
end
end
--- @param #AI_ESCORT_DISPATCHER self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER:OnEventBirth( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
self:I({EscortAirbase= self.EscortAirbase } )
self:I({PlayerGroupName = PlayerGroupName } )
self:I({PlayerGroup = PlayerGroup})
self:I({FirstGroup = self.CarrierSet:GetFirst()})
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if not self.AI_Escorts[PlayerGroupName] then
local LeaderUnit = PlayerUnit
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
self:I({EscortGroup = EscortGroup})
self:ScheduleOnce( 1,
function( EscortGroup )
local EscortSet = SET_GROUP:New()
EscortSet:AddGroup( EscortGroup )
self.AI_Escorts[PlayerGroupName] = AI_ESCORT:New( LeaderUnit, EscortSet, self.EscortName, self.EscortBriefing )
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
if EscortGroup:IsHelicopter() then
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
else
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
end
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
end, EscortGroup
)
end
end
end
--- Start Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] Start
-- @param #AI_ESCORT_DISPATCHER self
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] __Start
-- @param #AI_ESCORT_DISPATCHER self
-- @param #number Delay
--- Stop Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] Stop
-- @param #AI_ESCORT_DISPATCHER self
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER
-- @function [parent=#AI_ESCORT_DISPATCHER] __Stop
-- @param #AI_ESCORT_DISPATCHER self
-- @param #number Delay

View File

@@ -0,0 +1,146 @@
--- **AI** - Models the assignment of AI escorts to player flights upon request using the radio menu.
--
-- ## Features:
--
-- * Provides the facilities to trigger escorts when players join flight units.
-- * Provide a menu for which escorts can be requested.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_ESCORT_DISPATCHER_REQUEST
-- @image MOOSE.JPG
--- @type AI_ESCORT_DISPATCHER_REQUEST
-- @extends Core.Fsm#FSM
--- Models the assignment of AI escorts to player flights upon request using the radio menu.
--
-- ===
--
-- @field #AI_ESCORT_DISPATCHER_REQUEST
AI_ESCORT_DISPATCHER_REQUEST = {
ClassName = "AI_ESCORT_DISPATCHER_REQUEST",
}
--- @field #list
AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts = {}
--- Creates a new AI_ESCORT_DISPATCHER_REQUEST object.
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are requested.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
-- @return #AI_ESCORT_DISPATCHER_REQUEST
function AI_ESCORT_DISPATCHER_REQUEST:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER_REQUEST
self.CarrierSet = CarrierSet
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
self.EscortName = EscortName
self.EscortBriefing = EscortBriefing
self:SetStartState( "Idle" )
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
self:AddTransition( "Idle", "Start", "Monitoring" )
self:AddTransition( "Monitoring", "Stop", "Idle" )
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
self:F( { Carrier = Carrier:GetName() } )
end
return self
end
function AI_ESCORT_DISPATCHER_REQUEST:onafterStart( From, Event, To )
self:HandleEvent( EVENTS.Birth )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventExit )
self:HandleEvent( EVENTS.Crash, self.OnEventExit )
self:HandleEvent( EVENTS.Dead, self.OnEventExit )
end
--- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if self.AI_Escorts[PlayerGroupName] then
self.AI_Escorts[PlayerGroupName]:Stop()
self.AI_Escorts[PlayerGroupName] = nil
end
end
end
--- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param Core.Event#EVENTDATA EventData
function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth( EventData )
local PlayerGroupName = EventData.IniGroupName
local PlayerGroup = EventData.IniGroup
local PlayerUnit = EventData.IniUnit
if self.CarrierSet:FindGroup( PlayerGroupName ) then
if not self.AI_Escorts[PlayerGroupName] then
local LeaderUnit = PlayerUnit
self:ScheduleOnce( 0.1,
function()
self.AI_Escorts[PlayerGroupName] = AI_ESCORT_REQUEST:New( LeaderUnit, self.EscortSpawn, self.EscortAirbase, self.EscortName, self.EscortBriefing )
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
if PlayerGroup:IsHelicopter() then
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
else
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
end
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
end
)
end
end
end
--- Start Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Start
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Start
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param #number Delay
--- Stop Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Stop
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Stop
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
-- @param #number Delay

View File

@@ -0,0 +1,316 @@
--- **Functional** -- Taking the lead of AI escorting your flight or of other AI, upon request using the menu.
--
-- ===
--
-- ## Features:
--
-- * Escort navigation commands.
-- * Escort hold at position commands.
-- * Escorts reporting detected targets.
-- * Escorts scanning targets in advance.
-- * Escorts attacking specific targets.
-- * Request assistance from other groups for attack.
-- * Manage rule of engagement of escorts.
-- * Manage the allowed evasion techniques of escorts.
-- * Make escort to execute a defined mission or path.
-- * Escort tactical situation reporting.
--
-- ===
--
-- ## Missions:
--
-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ESC%20-%20Escorting)
--
-- ===
--
-- Allows you to interact with escorting AI on your flight and take the lead.
--
-- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10).
--
-- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes.
-- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts.
--
-- Escorts detect targets using a built-in detection mechanism. The detected targets are reported at a specified time interval.
-- Once targets are reported, each escort has these targets as menu options to command the attack of these targets.
-- Targets are by default grouped per area of 5000 meters, but the kind of detection and the grouping range can be altered.
--
-- Different formations can be selected in the Flight menu: Trail, Stack, Left Line, Right Line, Left Wing, Right Wing, Central Wing and Boxed formations are available.
-- The Flight menu also allows for a mass attack, where all of the escorts are commanded to attack a target.
--
-- Escorts can emit flares to reports their location. They can be commanded to hold at a location, which can be their current or the leader location.
-- In this way, you can spread out the escorts over the battle field before a coordinated attack.
--
-- But basically, the escort class provides 4 modes of operation, and depending on the mode, you are either leading the flight, or following the flight.
--
-- ## Leading the flight
--
-- When leading the flight, you are expected to guide the escorts towards the target areas,
-- and carefully coordinate the attack based on the threat levels reported, and the available weapons
-- carried by the escorts. Ground ships or ground troops can execute A-assisted attacks, when they have long-range ground precision weapons for attack.
--
-- ## Following the flight
--
-- Escorts can be commanded to execute a specific mission path. In this mode, the escorts are in the lead.
-- You as a player, are following the escorts, and are commanding them to progress the mission while
-- ensuring that the escorts survive. You are joining the escorts in the battlefield. They will detect and report targets
-- and you will ensure that the attacks are well coordinated, assigning the correct escort type for the detected target
-- type. Once the attack is finished, the escort will resume the mission it was assigned.
-- In other words, you can use the escorts for reconnaissance, and for guiding the attack.
-- Imagine you as a mi-8 pilot, assigned to pickup cargo. Two ka-50s are guiding the way, and you are
-- following. You are in control. The ka-50s detect targets, report them, and you command how the attack
-- will commence and from where. You can control where the escorts are holding position and which targets
-- are attacked first. You are in control how the ka-50s will follow their mission path.
--
-- Escorts can act as part of a AI A2G dispatcher offensive. In this way, You was a player are in control.
-- The mission is defined by the A2G dispatcher, and you are responsible to join the flight and ensure that the
-- attack is well coordinated.
--
-- It is with great proud that I present you this class, and I hope you will enjoy the functionality and the dynamism
-- it brings in your DCS world simulations.
--
-- # RADIO MENUs that can be created:
--
-- Find a summary below of the current available commands:
--
-- ## Navigation ...:
--
-- Escort group navigation functions:
--
-- * **"Join-Up":** The escort group fill follow you in the assigned formation.
-- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color.
-- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops.
--
-- ## Hold position ...:
--
-- Escort group navigation functions:
--
-- * **"At current location":** The escort group will hover above the ground at the position they were. The altitude can be specified as a parameter.
-- * **"At my location":** The escort group will hover or orbit at the position where you are. The escort will fly to your location and hold position. The altitude can be specified as a parameter.
--
-- ## Report targets ...:
--
-- Report targets will make the escort group to report any target that it identifies within detection range. Any detected target can be attacked using the "Attack Targets" menu function. (see below).
--
-- * **"Report now":** Will report the current detected targets.
-- * **"Report targets on":** Will make the escorts to report the detected targets and will fill the "Attack Targets" menu list.
-- * **"Report targets off":** Will stop detecting targets.
--
-- ## Attack targets ...:
--
-- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed.
-- This menu will be available in Flight menu or in each Escort menu.
--
-- ## Scan targets ...:
--
-- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or rejoin formation.
--
-- * **"Scan targets 30 seconds":** Scan 30 seconds for targets.
-- * **"Scan targets 60 seconds":** Scan 60 seconds for targets.
--
-- ## Request assistance from ...:
--
-- This menu item will list all detected targets within a 15km range, similar as with the menu item **Attack Targets**.
-- This menu item allows to request attack support from other ground based escorts supporting the current escort.
-- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles.
-- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area.
--
-- ## ROE ...:
--
-- Sets the Rules of Engagement (ROE) of the escort group when in flight.
--
-- * **"Hold Fire":** The escort group will hold fire.
-- * **"Return Fire":** The escort group will return fire.
-- * **"Open Fire":** The escort group will open fire on designated targets.
-- * **"Weapon Free":** The escort group will engage with any target.
--
-- ## Evasion ...:
--
-- Will define the evasion techniques that the escort group will perform during flight or combat.
--
-- * **"Fight until death":** The escort group will have no reaction to threats.
-- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed.
-- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing.
-- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres.
--
-- ## Resume Mission ...:
--
-- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint.
-- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission.
--
-- ===
--
-- ### Authors: **FlightControl**
--
-- ===
--
-- @module AI.AI_Escort
-- @image Escorting.JPG
--- @type AI_ESCORT_REQUEST
-- @extends AI.AI_Escort#AI_ESCORT
--- AI_ESCORT_REQUEST class
--
-- # AI_ESCORT_REQUEST construction methods.
--
-- Create a new AI_ESCORT_REQUEST object with the @{#AI_ESCORT_REQUEST.New} method:
--
-- * @{#AI_ESCORT_REQUEST.New}: Creates a new AI_ESCORT_REQUEST object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
--
-- @usage
-- -- Declare a new EscortPlanes object as follows:
--
-- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
--
-- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT_REQUEST:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
--
-- @field #AI_ESCORT_REQUEST
AI_ESCORT_REQUEST = {
ClassName = "AI_ESCORT_REQUEST",
}
--- AI_ESCORT_REQUEST.Mode class
-- @type AI_ESCORT_REQUEST.MODE
-- @field #number FOLLOW
-- @field #number MISSION
--- MENUPARAM type
-- @type MENUPARAM
-- @field #AI_ESCORT_REQUEST ParamSelf
-- @field #Distance ParamDistance
-- @field #function ParamFunction
-- @field #string ParamMessage
--- AI_ESCORT_REQUEST class constructor for an AI group
-- @param #AI_ESCORT_REQUEST self
-- @param Wrapper.Client#CLIENT EscortUnit The client escorted by the EscortGroup.
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object of AI, escorting the EscortUnit.
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where escorts will be spawned once requested.
-- @param #string EscortName Name of the escort.
-- @param #string EscortBriefing A text showing the AI_ESCORT_REQUEST briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
-- @return #AI_ESCORT_REQUEST
-- @usage
-- EscortSpawn = SPAWN:NewWithAlias( "Red A2G Escort Template", "Red A2G Escort AI" ):InitLimit( 10, 10 )
-- EscortSpawn:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), AIRBASE.TerminalType.OpenBig )
--
-- local EscortUnit = UNIT:FindByName( "Red A2G Pilot" )
--
-- Escort = AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, AIRBASE:FindByName(AIRBASE.Caucasus.Sochi_Adler), "A2G", "Briefing" )
-- Escort:FormationTrail( 50, 100, 100 )
-- Escort:Menus()
-- Escort:__Start( 5 )
function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
local EscortGroupSet = SET_GROUP:New():FilterDeads():FilterCrashes()
local self = BASE:Inherit( self, AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT_REQUEST
self.EscortGroupSet = EscortGroupSet
self.EscortSpawn = EscortSpawn
self.EscortAirbase = EscortAirbase
self.LeaderGroup = self.PlayerUnit:GetGroup()
self.Detection = DETECTION_AREAS:New( self.EscortGroupSet, 5000 )
self.Detection:__Start( 30 )
self.SpawnMode = self.__Enum.Mode.Mission
return self
end
--- @param #AI_ESCORT_REQUEST self
function AI_ESCORT_REQUEST:SpawnEscort()
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
self:ScheduleOnce( 0.1,
function( EscortGroup )
EscortGroup:OptionROTVertical()
EscortGroup:OptionROEHoldFire()
self.EscortGroupSet:AddGroup( EscortGroup )
local LeaderEscort = self.EscortGroupSet:GetFirst() -- Wrapper.Group#GROUP
local Report = REPORT:New()
Report:Add( "Joining Up " .. self.EscortGroupSet:GetUnitTypeNames():Text( ", " ) .. " from " .. LeaderEscort:GetCoordinate():ToString( self.EscortUnit ) )
LeaderEscort:MessageTypeToGroup( Report:Text(), MESSAGE.Type.Information, self.PlayerUnit )
self:SetFlightModeFormation( EscortGroup )
self:FormationTrail()
self:_InitFlightMenus()
self:_InitEscortMenus( EscortGroup )
self:_InitEscortRoute( EscortGroup )
--- @param #AI_ESCORT self
-- @param Core.Event#EVENTDATA EventData
function EscortGroup:OnEventDeadOrCrash( EventData )
self:F( { "EventDead", EventData } )
self.EscortMenu:Remove()
end
EscortGroup:HandleEvent( EVENTS.Dead, EscortGroup.OnEventDeadOrCrash )
EscortGroup:HandleEvent( EVENTS.Crash, EscortGroup.OnEventDeadOrCrash )
end, EscortGroup
)
end
--- @param #AI_ESCORT_REQUEST self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet )
self:F()
if not self.MenuRequestEscort then
self.MainMenu = MENU_GROUP:New( self.PlayerGroup, self.EscortName )
self.MenuRequestEscort = MENU_GROUP_COMMAND:New( self.LeaderGroup, "Request new escort ", self.MainMenu,
function()
self:SpawnEscort()
end
)
end
self:GetParent( self ).onafterStart( self, EscortGroupSet )
self:HandleEvent( EVENTS.Dead, self.OnEventDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self.OnEventDeadOrCrash )
end
--- @param #AI_ESCORT_REQUEST self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT_REQUEST:onafterStop( EscortGroupSet )
self:F()
EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
EscortGroup:WayPointInitialize()
EscortGroup:OptionROTVertical()
EscortGroup:OptionROEOpenFire()
end
)
self.Detection:Stop()
self.MainMenu:Remove()
end
--- Set the spawn mode to be mission execution.
-- @param #AI_ESCORT_REQUEST self
function AI_ESCORT_REQUEST:SetEscortSpawnMission()
self.SpawnMode = self.__Enum.Mode.Mission
end

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
--
-- * Patrol AI airplanes within a given zone.
-- * 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)
--
-- Until a fuel or damage treshold 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.
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- 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)
--
@@ -101,7 +101,7 @@
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting 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
--
@@ -133,8 +133,8 @@
-- ## 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.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_PATROL_ZONE.
-- 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.
@@ -142,7 +142,7 @@
-- ## 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.
-- 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.
--
-- ===
@@ -178,8 +178,8 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
-- defafult PatrolAltType to "BARO" if not specified
self.PatrolAltType = PatrolAltType or "BARO"
self:SetRefreshTimeInterval( 30 )
@@ -581,11 +581,11 @@ function AI_PATROL_ZONE:ClearDetectedUnits()
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.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold 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.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base.
-- @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.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
@@ -596,14 +596,14 @@ function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFu
return self
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.
-- Therefore, when the damage treshold is reached,
-- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB).
-- 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 #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
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
@@ -636,7 +636,7 @@ function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To )
self.Controllable:OnReSpawn(
function( PatrolGroup )
self:E( "ReSpawn" )
self:T( "ReSpawn" )
self:__Reset( 1 )
self:__Route( 5 )
end
@@ -667,21 +667,27 @@ function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To )
if TargetObject and TargetObject:isExist() and TargetObject.id_ < 50000000 then
local TargetUnit = UNIT:Find( TargetObject )
local TargetUnitName = TargetUnit:GetName()
if self.DetectionZone then
if TargetUnit:IsInZone( self.DetectionZone ) then
self:T( {"Detected ", TargetUnit } )
-- Check that target is alive due to issue https://github.com/FlightControl-Master/MOOSE/issues/1234
if TargetUnit and TargetUnit:IsAlive() then
local TargetUnitName = TargetUnit:GetName()
if self.DetectionZone then
if TargetUnit:IsInZone( self.DetectionZone ) then
self:T( {"Detected ", TargetUnit } )
if self.DetectedUnits[TargetUnit] == nil then
self.DetectedUnits[TargetUnit] = true
end
Detected = true
end
else
if self.DetectedUnits[TargetUnit] == nil then
self.DetectedUnits[TargetUnit] = true
end
Detected = true
Detected = true
end
else
if self.DetectedUnits[TargetUnit] == nil then
self.DetectedUnits[TargetUnit] = true
end
Detected = true
end
end
end
@@ -720,7 +726,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
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.
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
@@ -735,10 +742,11 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
-- This will make the plane fly immediately to the patrol zone.
if self.Controllable:InAir() == false then
self:E( "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()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
if not CurrentVec2 then return 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 ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -750,10 +758,11 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
)
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
else
self:E( "In the air, finding route path within PatrolZone" )
self:T( "In the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
if not CurrentVec2 then return 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 ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -825,7 +834,7 @@ function AI_PATROL_ZONE:onafterStatus()
local Fuel = self.Controllable:GetFuelMin()
if Fuel < self.PatrolFuelThresholdPercentage then
self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
self:I( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
local OldAIControllable = self.Controllable
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
@@ -839,7 +848,7 @@ function AI_PATROL_ZONE:onafterStatus()
-- TODO: Check GROUP damage function.
local Damage = self.Controllable:GetLife()
if Damage <= self.PatrolDamageThreshold then
self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
self:I( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
RTB = true
end
@@ -864,9 +873,10 @@ function AI_PATROL_ZONE:onafterRTB()
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
if not CurrentVec2 then return end
--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 ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -900,7 +910,6 @@ end
function AI_PATROL_ZONE:OnCrash( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:E( self.Controllable:GetUnits() )
if #self.Controllable:GetUnits() == 1 then
self:__Crash( 1, EventData )
end

View File

@@ -1,34 +1,34 @@
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Wrapper.Unit}s.
--
--
-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG)
--
-- ===
--
--
-- ===
--
-- @module Actions.Account
-- @image MOOSE.JPG
do -- ACT_ACCOUNT
--- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS}
--
-- ## ACT_ACCOUNT state machine:
--
--
-- ## ACT_ACCOUNT state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ACCOUNT States
--
--
-- ### ACT_ACCOUNT States
--
-- * **Asigned**: The player is assigned.
-- * **Waiting**: Waiting for an event.
-- * **Report**: Reporting.
-- * **Account**: Account for an event.
-- * **Accounted**: All events have been accounted for, end of the process.
-- * **Failed**: Failed the process.
--
-- ### ACT_ACCOUNT Events
--
--
-- ### ACT_ACCOUNT Events
--
-- * **Start**: Start the process.
-- * **Wait**: Wait for an event.
-- * **Report**: Report the status of the accounting.
@@ -36,32 +36,32 @@ do -- ACT_ACCOUNT
-- * **More**: More targets.
-- * **NoMore (*)**: No more targets.
-- * **Fail (*)**: The action process has failed.
--
--
-- (*) End states of the process.
--
--
-- ### ACT_ACCOUNT state transition methods:
--
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
--
-- @type ACT_ACCOUNT
-- @field Core.Set#SET_UNIT TargetSetUnit
-- @extends Core.Fsm#FSM_PROCESS
ACT_ACCOUNT = {
ACT_ACCOUNT = {
ClassName = "ACT_ACCOUNT",
TargetSetUnit = nil,
}
--- Creates a new DESTROY process.
-- @param #ACT_ACCOUNT self
-- @return #ACT_ACCOUNT
@@ -69,7 +69,7 @@ do -- ACT_ACCOUNT
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "Assigned", "Start", "Waiting" )
self:AddTransition( "*", "Wait", "Waiting" )
self:AddTransition( "*", "Report", "Report" )
@@ -79,16 +79,16 @@ do -- ACT_ACCOUNT
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" )
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Failed" )
self:SetStartState( "Assigned" )
self:SetStartState( "Assigned" )
return self
end
--- Process Events
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -104,7 +104,7 @@ do -- ACT_ACCOUNT
self:__Wait( 1 )
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -112,17 +112,17 @@ do -- ACT_ACCOUNT
-- @param #string From
-- @param #string To
function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To )
if self.DisplayCount >= self.DisplayInterval then
self:Report()
self.DisplayCount = 1
else
self.DisplayCount = self.DisplayCount + 1
end
return true -- Process always the event.
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -130,30 +130,30 @@ do -- ACT_ACCOUNT
-- @param #string From
-- @param #string To
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event )
self:__NoMore( 1 )
end
end -- ACT_ACCOUNT
do -- ACT_ACCOUNT_DEADS
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT}
--
--
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
-- The process is given a @{Set} of units that will be tracked upon successful destruction.
-- The process will end after each target has been successfully destroyed.
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
--
--
--
--
-- ## ACT_ACCOUNT_DEADS constructor:
--
--
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
--
--
-- @type ACT_ACCOUNT_DEADS
-- @field Core.Set#SET_UNIT TargetSetUnit
-- @extends #ACT_ACCOUNT
ACT_ACCOUNT_DEADS = {
ACT_ACCOUNT_DEADS = {
ClassName = "ACT_ACCOUNT_DEADS",
}
@@ -165,24 +165,24 @@ do -- ACT_ACCOUNT_DEADS
function ACT_ACCOUNT_DEADS:New()
-- Inherits from BASE
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
self.DisplayCategory = "HQ" -- Targets is the default display category
return self
end
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
self.Task = self:GetTask()
self.Task = self:GetTask()
self.TaskName = self.Task:GetName()
end
--- Process Events
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -190,12 +190,12 @@ do -- ACT_ACCOUNT_DEADS
-- @param #string From
-- @param #string To
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -206,7 +206,7 @@ do -- ACT_ACCOUNT_DEADS
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
local PlayerName = ProcessUnit:GetPlayerName()
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
@@ -228,14 +228,14 @@ do -- ACT_ACCOUNT_DEADS
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessUnit:GetGroup()
Task.TargetSetUnit:Remove( EventData.IniUnitName )
local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
local PlayerName = ProcessUnit:GetPlayerName()
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
@@ -256,10 +256,10 @@ do -- ACT_ACCOUNT_DEADS
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessUnit:GetGroup()
Task.TargetSetUnit:Remove( EventData.IniUnitName )
local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
@@ -270,9 +270,9 @@ do -- ACT_ACCOUNT_DEADS
end
end
--- DCS Events
--- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:OnEventHit( EventData )
@@ -282,8 +282,8 @@ do -- ACT_ACCOUNT_DEADS
self.PlayerHits = self.PlayerHits or {}
self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName
end
end
end
--- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData )
@@ -295,7 +295,7 @@ do -- ACT_ACCOUNT_DEADS
end
--- DCS Events
--- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData )

View File

@@ -1,82 +1,82 @@
--- (SP) (MP) (FSM) Accept or reject process for player (task) assignments.
--
--
-- ===
--
--
-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS}
--
--
-- ## ACT_ASSIGN state machine:
--
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
--
-- ### ACT_ASSIGN **Events**:
--
--
-- These are the events defined in this class:
--
--
-- * **Start**: Start the tasking acceptance process.
-- * **Assign**: Assign the task.
-- * **Reject**: Reject the task..
--
--
-- ### ACT_ASSIGN **Event methods**:
--
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ASSIGN **States**:
--
--
-- * **UnAssigned**: The player has not accepted the task.
-- * **Assigned (*)**: The player has accepted the task.
-- * **Rejected (*)**: The player has not accepted the task.
-- * **Waiting**: The process is awaiting player feedback.
-- * **Failed (*)**: The process has failed.
--
--
-- (*) End states of the process.
--
--
-- ### ACT_ASSIGN state transition methods:
--
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
--
-- ===
--
--
-- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN}
--
--
-- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task.
--
--
-- ## 1.1) ACT_ASSIGN_ACCEPT constructor:
--
--
-- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object.
--
--
-- ===
--
--
-- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN}
--
--
-- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option.
-- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task.
-- The assignment type also allows to reject the task.
--
--
-- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor:
-- -----------------------------------------
--
--
-- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object.
--
--
-- ===
--
--
-- @module Actions.Assign
-- @image MOOSE.JPG
@@ -89,11 +89,11 @@ do -- ACT_ASSIGN
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends Core.Fsm#FSM_PROCESS
ACT_ASSIGN = {
ACT_ASSIGN = {
ClassName = "ACT_ASSIGN",
}
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
-- @param #ACT_ASSIGN self
-- @return #ACT_ASSIGN The task acceptance process.
@@ -106,16 +106,16 @@ do -- ACT_ASSIGN
self:AddTransition( "Waiting", "Assign", "Assigned" )
self:AddTransition( "Waiting", "Reject", "Rejected" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Assigned" )
self:AddEndState( "Rejected" )
self:AddEndState( "Failed" )
self:SetStartState( "UnAssigned" )
self:SetStartState( "UnAssigned" )
return self
end
end -- ACT_ASSIGN
@@ -128,26 +128,26 @@ do -- ACT_ASSIGN_ACCEPT
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends #ACT_ASSIGN
ACT_ASSIGN_ACCEPT = {
ACT_ASSIGN_ACCEPT = {
ClassName = "ACT_ASSIGN_ACCEPT",
}
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
-- @param #ACT_ASSIGN_ACCEPT self
-- @param #string TaskBriefing
function ACT_ASSIGN_ACCEPT:New( TaskBriefing )
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT
self.TaskBriefing = TaskBriefing
return self
end
function ACT_ASSIGN_ACCEPT:Init( FsmAssign )
self.TaskBriefing = FsmAssign.TaskBriefing
self.TaskBriefing = FsmAssign.TaskBriefing
end
--- StateMachine callback function
@@ -157,8 +157,8 @@ do -- ACT_ASSIGN_ACCEPT
-- @param #string From
-- @param #string To
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
self:__Assign( 1 )
self:__Assign( 1 )
end
--- StateMachine callback function
@@ -167,11 +167,11 @@ do -- ACT_ASSIGN_ACCEPT
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To )
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
end
end -- ACT_ASSIGN_ACCEPT
@@ -183,7 +183,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends #ACT_ASSIGN
ACT_ASSIGN_MENU_ACCEPT = {
ACT_ASSIGN_MENU_ACCEPT = {
ClassName = "ACT_ASSIGN_MENU_ACCEPT",
}
@@ -197,7 +197,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
self.TaskBriefing = TaskBriefing
return self
end
@@ -207,12 +207,12 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string TaskBriefing
-- @return #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
self.TaskBriefing = TaskBriefing
return self
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -222,30 +222,30 @@ do -- ACT_ASSIGN_MENU_ACCEPT
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 )
local TaskGroup = ProcessUnit:GetGroup()
local TaskGroup = ProcessUnit:GetGroup()
self.Menu = MENU_GROUP:New( TaskGroup, "Task " .. self.Task:GetName() .. " CONFIRMATION" )
self.MenuAcceptTask = MENU_GROUP_COMMAND:New( TaskGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self, TaskGroup )
self.MenuRejectTask = MENU_GROUP_COMMAND:New( TaskGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self, TaskGroup )
self:__Reject( 120, TaskGroup )
end
--- Menu function.
-- @param #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
self:__Assign( -1, TaskGroup )
end
--- Menu function.
-- @param #ACT_ASSIGN_MENU_ACCEPT self
function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
self:__Reject( -1, TaskGroup )
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -253,10 +253,10 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup )
self.Menu:Remove()
end
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -265,7 +265,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup )
self:F( { TaskGroup = TaskGroup } )
self.Menu:Remove()
--TODO: need to resolve this problem ... it has to do with the events ...
--self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event
@@ -279,7 +279,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
--self.Task:AssignToGroup( TaskGroup )
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
end

View File

@@ -1,65 +1,65 @@
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
--
--
-- ## ACT_ASSIST state machine:
--
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
--
-- ### ACT_ASSIST **Events**:
--
--
-- These are the events defined in this class:
--
--
-- * **Start**: The process is started.
-- * **Next**: The process is smoking the targets in the given zone.
--
--
-- ### ACT_ASSIST **Event methods**:
--
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ASSIST **States**:
--
--
-- * **None**: The controllable did not receive route commands.
-- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone.
-- * **Smoking (*)**: The process is smoking the targets in the zone.
-- * **Failed (*)**: The process has failed.
--
--
-- (*) End states of the process.
--
--
-- ### ACT_ASSIST state transition methods:
--
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
--
-- ===
--
--
-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST}
--
--
-- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}.
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
-- At random intervals, a new target is smoked.
--
--
-- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor:
--
--
-- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object.
--
--
-- ===
--
--
-- @module Actions.Assist
-- @image MOOSE.JPG
@@ -69,7 +69,7 @@ do -- ACT_ASSIST
--- ACT_ASSIST class
-- @type ACT_ASSIST
-- @extends Core.Fsm#FSM_PROCESS
ACT_ASSIST = {
ACT_ASSIST = {
ClassName = "ACT_ASSIST",
}
@@ -86,15 +86,15 @@ do -- ACT_ASSIST
self:AddTransition( "Smoking", "Next", "AwaitSmoke" )
self:AddTransition( "*", "Stop", "Success" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Failed" )
self:AddEndState( "Success" )
self:SetStartState( "None" )
self:SetStartState( "None" )
return self
end
--- Task Events
--- StateMachine callback function
@@ -104,17 +104,17 @@ do -- ACT_ASSIST
-- @param #string From
-- @param #string To
function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To )
local ProcessGroup = ProcessUnit:GetGroup()
local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
local function MenuSmoke( MenuParam )
local self = MenuParam.self
local SmokeColor = MenuParam.SmokeColor
self.SmokeColor = SmokeColor
self:__Next( 1 )
end
self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu )
self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } )
self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } )
@@ -130,10 +130,10 @@ do -- ACT_ASSIST
-- @param #string From
-- @param #string To
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
self.Menu:Remove() -- When stopped, remove the menus
end
end
do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
@@ -143,17 +143,17 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
-- @field Core.Set#SET_UNIT TargetSetUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @extends #ACT_ASSIST
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE",
}
-- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor()
-- self:E("_Destructor")
--
--
-- self.Menu:Remove()
-- self:EventRemoveAll()
-- end
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
-- @param Core.Set#SET_UNIT TargetSetUnit
@@ -163,29 +163,29 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
return self
end
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke )
self.TargetSetUnit = FsmSmoke.TargetSetUnit
self.TargetZone = FsmSmoke.TargetZone
end
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param Core.Zone#ZONE_BASE TargetZone
-- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone )
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
return self
end
--- StateMachine callback function
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
@@ -193,7 +193,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
-- @param #string From
-- @param #string To
function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To )
self.TargetSetUnit:ForEachUnit(
--- @param Wrapper.Unit#UNIT SmokeUnit
function( SmokeUnit )
@@ -203,12 +203,12 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
if SmokeUnit:IsAlive() then
SmokeUnit:Smoke( self.SmokeColor, 150 )
end
end, {}, math.random( 10, 60 )
end, {}, math.random( 10, 60 )
)
end
end
)
end
end
end

View File

@@ -1,20 +1,20 @@
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
--
--
-- ===
--
--
-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS}
--
--
-- ## ACT_ROUTE state machine:
--
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
--
-- ### ACT_ROUTE **Events**:
--
--
-- These are the events defined in this class:
--
--
-- * **Start**: The process is started. The process will go into the Report state.
-- * **Report**: The process is reporting to the player the route to be followed.
-- * **Route**: The process is routing the controllable.
@@ -22,56 +22,56 @@
-- * **Arrive**: The controllable has arrived at a route point.
-- * **More**: There are more route points that need to be followed. The process will go back into the Report state.
-- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state.
--
--
-- ### ACT_ROUTE **Event methods**:
--
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ROUTE **States**:
--
--
-- * **None**: The controllable did not receive route commands.
-- * **Arrived (*)**: The controllable has arrived at a route point.
-- * **Aborted (*)**: The controllable has aborted the route path.
-- * **Routing**: The controllable is understay to the route point.
-- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around.
-- * **Success (*)**: All route points were reached.
-- * **Success (*)**: All route points were reached.
-- * **Failed (*)**: The process has failed.
--
--
-- (*) End states of the process.
--
--
-- ### ACT_ROUTE state transition methods:
--
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
--
-- ===
--
--
-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE}
--
--
-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}.
-- The player receives on perioding times messages with the coordinates of the route to follow.
-- The player receives on perioding times messages with the coordinates of the route to follow.
-- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended.
--
--
-- # 1.1) ACT_ROUTE_ZONE constructor:
--
--
-- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object.
--
--
-- ===
--
--
-- @module Actions.Route
-- @image MOOSE.JPG
@@ -85,11 +85,11 @@ do -- ACT_ROUTE
-- @field Core.Zone#ZONE_BASE Zone
-- @field Core.Point#COORDINATE Coordinate
-- @extends Core.Fsm#FSM_PROCESS
ACT_ROUTE = {
ACT_ROUTE = {
ClassName = "ACT_ROUTE",
}
--- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE.
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE self
@@ -97,7 +97,7 @@ do -- ACT_ROUTE
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "*", "Reset", "None" )
self:AddTransition( "None", "Start", "Routing" )
self:AddTransition( "*", "Report", "*" )
@@ -109,23 +109,23 @@ do -- ACT_ROUTE
self:AddTransition( "*", "Fail", "Failed" )
self:AddTransition( "", "", "" )
self:AddTransition( "", "", "" )
self:AddEndState( "Arrived" )
self:AddEndState( "Failed" )
self:AddEndState( "Cancelled" )
self:SetStartState( "None" )
self:SetRouteMode( "C" )
self:SetRouteMode( "C" )
return self
end
--- Set a Cancel Menu item.
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag )
self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New(
MenuGroup,
MenuText,
@@ -135,47 +135,47 @@ do -- ACT_ROUTE
):SetTime( MenuTime ):SetTag( MenuTag )
ParentMenu:SetTime( MenuTime )
ParentMenu:Remove( MenuTime, MenuTag )
return self
end
--- Set the route mode.
-- There are 2 route modes supported:
--
--
-- * SetRouteMode( "B" ): Route mode is Bearing and Range.
-- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup.
--
--
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE
function ACT_ROUTE:SetRouteMode( RouteMode )
self.RouteMode = RouteMode
return self
return self
end
--- Get the routing text to be displayed.
-- The route mode determines the text displayed.
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT Controllable
-- @return #string
function ACT_ROUTE:GetRouteText( Controllable )
local RouteText = ""
local Coordinate = nil -- Core.Point#COORDINATE
if self.Coordinate then
Coordinate = self.Coordinate
end
if self.Zone then
Coordinate = self.Zone:GetPointVec3( self.Altitude )
Coordinate:SetHeading( self.Heading )
end
local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
local CC = self:GetTask():GetMission():GetCommandCenter()
@@ -190,7 +190,7 @@ do -- ACT_ROUTE
self:F( { ZoneName = ZoneName } )
local Zone = Zone -- Core.Zone#ZONE
local ZoneCoord = Zone:GetCoordinate()
local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate )
local ZoneDistance = ZoneCoord:Get2DDistance( Coordinate )
self:F( { ShortestDistance, ShortestReferenceName } )
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
ShortestDistance = ZoneDistance
@@ -209,7 +209,7 @@ do -- ACT_ROUTE
return RouteText
end
function ACT_ROUTE:MenuCancel()
self:F("Cancelled")
self.CancelMenuGroupCommand:Remove()
@@ -225,11 +225,11 @@ do -- ACT_ROUTE
-- @param #string From
-- @param #string To
function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To )
self:__Route( 1 )
end
--- Check if the controllable has arrived.
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -237,7 +237,7 @@ do -- ACT_ROUTE
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
return false
end
--- StateMachine callback function
-- @param #ACT_ROUTE self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -245,7 +245,7 @@ do -- ACT_ROUTE
-- @param #string From
-- @param #string To
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
if ProcessUnit:IsAlive() then
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
if self.DisplayCount >= self.DisplayInterval then
@@ -257,18 +257,18 @@ do -- ACT_ROUTE
else
self.DisplayCount = self.DisplayCount + 1
end
if HasArrived then
self:__Arrive( 1 )
else
self:__Route( 1 )
end
return HasArrived -- if false, then the event will not be executed...
end
return false
end
end -- ACT_ROUTE
@@ -280,12 +280,12 @@ do -- ACT_ROUTE_POINT
-- @type ACT_ROUTE_POINT
-- @field Tasking.Task#TASK TASK
-- @extends #ACT_ROUTE
ACT_ROUTE_POINT = {
ACT_ROUTE_POINT = {
ClassName = "ACT_ROUTE_POINT",
}
--- Creates a new routing state machine.
--- Creates a new routing state machine.
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#COORDINATE The Coordinate to Target.
@@ -296,29 +296,29 @@ do -- ACT_ROUTE_POINT
self.Coordinate = Coordinate
self.Range = Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
return self
end
--- Creates a new routing state machine.
--- Creates a new routing state machine.
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
function ACT_ROUTE_POINT:Init( FsmRoute )
self.Coordinate = FsmRoute.Coordinate
self.Range = FsmRoute.Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
self:SetStartState("None")
end
end
--- Set Coordinate
-- @param #ACT_ROUTE_POINT self
@@ -326,7 +326,7 @@ do -- ACT_ROUTE_POINT
function ACT_ROUTE_POINT:SetCoordinate( Coordinate )
self:F2( { Coordinate } )
self.Coordinate = Coordinate
end
end
--- Get Coordinate
-- @param #ACT_ROUTE_POINT self
@@ -334,7 +334,7 @@ do -- ACT_ROUTE_POINT
function ACT_ROUTE_POINT:GetCoordinate()
self:F2( { self.Coordinate } )
return self.Coordinate
end
end
--- Set Range around Coordinate
-- @param #ACT_ROUTE_POINT self
@@ -342,16 +342,16 @@ do -- ACT_ROUTE_POINT
function ACT_ROUTE_POINT:SetRange( Range )
self:F2( { Range } )
self.Range = Range or 10000
end
end
--- Get Range around Coordinate
-- @param #ACT_ROUTE_POINT self
-- @return #number The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:GetRange()
self:F2( { self.Range } )
return self.Range
end
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -360,7 +360,7 @@ do -- ACT_ROUTE_POINT
if ProcessUnit:IsAlive() then
local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() )
if Distance <= self.Range then
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived."
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
@@ -370,9 +370,9 @@ do -- ACT_ROUTE_POINT
return false
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -380,9 +380,9 @@ do -- ACT_ROUTE_POINT
-- @param #string From
-- @param #string To
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
end
@@ -397,7 +397,7 @@ do -- ACT_ROUTE_ZONE
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE Zone
-- @extends #ACT_ROUTE
ACT_ROUTE_ZONE = {
ACT_ROUTE_ZONE = {
ClassName = "ACT_ROUTE_ZONE",
}
@@ -409,25 +409,25 @@ do -- ACT_ROUTE_ZONE
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
self.Zone = Zone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
return self
end
function ACT_ROUTE_ZONE:Init( FsmRoute )
self.Zone = FsmRoute.Zone
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
end
end
--- Set Zone
-- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
@@ -437,14 +437,14 @@ do -- ACT_ROUTE_ZONE
self.Zone = Zone
self.Altitude = Altitude
self.Heading = Heading
end
end
--- Get Zone
-- @param #ACT_ROUTE_ZONE self
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
function ACT_ROUTE_ZONE:GetZone()
return self.Zone
end
return self.Zone
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE self
@@ -459,9 +459,9 @@ do -- ACT_ROUTE_ZONE
return ProcessUnit:IsInZone( self.Zone )
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE_ZONE self
-- @param Wrapper.Unit#UNIT ProcessUnit
@@ -470,7 +470,7 @@ do -- ACT_ROUTE_ZONE
-- @param #string To
function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To )
self:F( { ProcessUnit = ProcessUnit } )
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
end

View File

@@ -1,4 +1,4 @@
--- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers.
--- **Cargo** - Management of CARGO logistics, that can be transported from and to transportation carriers.
--
-- ===
--
@@ -194,7 +194,7 @@
-- * is of type `Workmaterials`
-- * 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 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:
--
@@ -220,7 +220,7 @@
-- * is of type `Workmaterials`
-- * 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 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:
--
@@ -1066,18 +1066,26 @@ do -- CARGO_REPRESENTABLE
--- CARGO_REPRESENTABLE Constructor.
-- @param #CARGO_REPRESENTABLE self
-- @param #string Type
-- @param #string Name
-- @param #number LoadRadius (optional)
-- @param #number NearRadius (optional)
-- @param Wrapper.Positionable#POSITIONABLE CargoObject The cargo object.
-- @param #string Type Type name
-- @param #string Name Name.
-- @param #number LoadRadius (optional) Radius in meters.
-- @param #number NearRadius (optional) Radius in meters when the cargo is loaded into the carrier.
-- @return #CARGO_REPRESENTABLE
function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, LoadRadius, NearRadius )
-- Inherit CARGO.
local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE
self:F( { Type, Name, LoadRadius, NearRadius } )
local Desc = CargoObject:GetDesc()
self:I( { Desc = Desc } )
-- Descriptors.
local Desc=CargoObject:GetDesc()
self:T({Desc=Desc})
-- Weight.
local Weight = math.random( 80, 120 )
-- Adjust weight..
if Desc then
if Desc.typeName == "2B11 mortar" then
Weight = 210
@@ -1086,13 +1094,8 @@ do -- CARGO_REPRESENTABLE
end
end
-- Set weight.
self:SetWeight( Weight )
-- local Box = CargoUnit:GetBoundingBox()
-- local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z )
-- self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } )
--self:SetVolume( VolumeUnit )
return self
end

View File

@@ -1,4 +1,4 @@
--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object.
--- **Cargo** - Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object.
--
-- ===
--
@@ -56,6 +56,8 @@ do -- CARGO_GROUP
-- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene.
-- @return #CARGO_GROUP Cargo group object.
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
-- Inherit CAROG_REPORTABLE
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
self:F( { Type, Name, LoadRadius } )
@@ -76,6 +78,9 @@ do -- CARGO_GROUP
local GroupName = CargoGroup:GetName()
self.CargoName = Name
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
-- Deactivate late activation.
self.CargoTemplate.lateActivation=false
self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
self.GroupTemplate.name = self.CargoName .. "#CARGO"
@@ -481,7 +486,7 @@ do -- CARGO_GROUP
-- @param #string Event
-- @param #string From
-- @param #string To
-- @param Core.Point#POINT_VEC2
-- @param Core.Point#POINT_VEC2 ToPointVec2
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
--self:F( { From, Event, To, ToPointVec2 } )
@@ -491,7 +496,10 @@ do -- CARGO_GROUP
self.CargoSet:ForEach(
function( Cargo )
--Cargo:UnLoad( ToPointVec2 )
local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
local RandomVec2=nil
if ToPointVec2 then
RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
end
Cargo:UnBoard( RandomVec2 )
end
)

View File

@@ -1,4 +1,4 @@
--- **Cargo** -- Management of single cargo logistics, which are based on a @{Wrapper.Unit} object.
--- **Cargo** - Management of single cargo logistics, which are based on a @{Wrapper.Unit} object.
--
-- ===
--
@@ -46,14 +46,17 @@ do -- CARGO_UNIT
-- @param #number NearRadius (optional)
-- @return #CARGO_UNIT
function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
self:I( { Type, Name, LoadRadius, NearRadius } )
self:T( CargoUnit )
-- Inherit CARGO_REPRESENTABLE.
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
-- Debug info.
self:T({Type=Type, Name=Name, LoadRadius=LoadRadius, NearRadius=NearRadius})
-- Set cargo object.
self.CargoObject = CargoUnit
self:T( self.ClassName )
-- Set event prio.
self:SetEventPriority( 5 )
return self
@@ -100,7 +103,12 @@ do -- CARGO_UNIT
-- Respawn the group...
if self.CargoObject then
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
if CargoCarrier:IsShip() then
-- If CargoCarrier is a ship, we don't want to spawn the units in the water next to the boat. Use destination coord instead.
self.CargoObject:ReSpawnAt( ToPointVec2, CargoDeployHeading )
else
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
end
self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
self.CargoCarrier = nil

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,479 @@
--- **Core** - TACAN and other beacons.
--
-- ===
--
-- ## Features:
--
-- * Provide beacon functionality to assist pilots.
--
-- ===
--
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
--
-- @module Core.Beacon
-- @image Core_Radio.JPG
--- *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.
-- 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 useful to simulate the battery time if your BEACON is
-- attach to a cargo crate, for exemple.
--
-- ## Aircraft TACAN Beacon usage
--
-- 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.StopRadioBeacon}() to stop it.
--
-- ## 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
-- @{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.
--
-- @type BEACON
-- @field #string ClassName Name of the class "BEACON".
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
-- @extends Core.Base#BASE
BEACON = {
ClassName = "BEACON",
Positionable = nil,
name = nil,
}
--- Beacon types supported by DCS.
-- @type BEACON.Type
-- @field #number NULL
-- @field #number VOR
-- @field #number DME
-- @field #number VOR_DME
-- @field #number TACAN TACtical Air Navigation system.
-- @field #number VORTAC
-- @field #number RSBN
-- @field #number BROADCAST_STATION
-- @field #number HOMER
-- @field #number AIRPORT_HOMER
-- @field #number AIRPORT_HOMER_WITH_MARKER
-- @field #number ILS_FAR_HOMER
-- @field #number ILS_NEAR_HOMER
-- @field #number ILS_LOCALIZER
-- @field #number ILS_GLIDESLOPE
-- @field #number PRMG_LOCALIZER
-- @field #number PRMG_GLIDESLOPE
-- @field #number ICLS Same as ICLS glideslope.
-- @field #number ICLS_LOCALIZER
-- @field #number ICLS_GLIDESLOPE
-- @field #number NAUTICAL_HOMER
BEACON.Type={
NULL = 0,
VOR = 1,
DME = 2,
VOR_DME = 3,
TACAN = 4,
VORTAC = 5,
RSBN = 128,
BROADCAST_STATION = 1024,
HOMER = 8,
AIRPORT_HOMER = 4104,
AIRPORT_HOMER_WITH_MARKER = 4136,
ILS_FAR_HOMER = 16408,
ILS_NEAR_HOMER = 16424,
ILS_LOCALIZER = 16640,
ILS_GLIDESLOPE = 16896,
PRMG_LOCALIZER = 33024,
PRMG_GLIDESLOPE = 33280,
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
ICLS_LOCALIZER = 131328,
ICLS_GLIDESLOPE = 131584,
NAUTICAL_HOMER = 65536,
}
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
-- @type BEACON.System
-- @field #number PAR_10 ?
-- @field #number RSBN_5 Russian VOR/DME system.
-- @field #number TACAN TACtical Air Navigation system on ground.
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
-- @field #number VOR Very High Frequency Omni-Directional Range
-- @field #number ILS_LOCALIZER ILS localizer
-- @field #number ILS_GLIDESLOPE ILS glide slope.
-- @field #number PRGM_LOCALIZER PRGM localizer.
-- @field #number PRGM_GLIDESLOPE PRGM glide slope.
-- @field #number BROADCAST_STATION Broadcast station.
-- @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_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 ICLS_LOCALIZER Carrier landing system.
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
BEACON.System={
PAR_10 = 1,
RSBN_5 = 2,
TACAN = 3,
TACAN_TANKER_X = 4,
TACAN_TANKER_Y = 5,
VOR = 6,
ILS_LOCALIZER = 7,
ILS_GLIDESLOPE = 8,
PRMG_LOCALIZER = 9,
PRMG_GLIDESLOPE = 10,
BROADCAST_STATION = 11,
VORTAC = 12,
TACAN_AA_MODE_X = 13,
TACAN_AA_MODE_Y = 14,
VORDME = 15,
ICLS_LOCALIZER = 16,
ICLS_GLIDESLOPE = 17,
}
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
-- @param #BEACON self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
function BEACON:New(Positionable)
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) --#BEACON
-- Debug.
self:F(Positionable)
-- Set positionable.
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable
self.name=Positionable:GetName()
self:I(string.format("New BEACON %s", tostring(self.name)))
return self
end
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
return nil
end
--- Activates a TACAN BEACON.
-- @param #BEACON self
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
-- @param #string Mode TACAN mode, i.e. the "Y" part in "10Y".
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon.
-- @param #boolean Bearing If true, beacon provides bearing information. If false (or nil), only distance information is available.
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
-- @usage
-- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
--
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
Mode=Mode or "Y"
-- Get frequency.
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
-- Check.
if not Frequency then
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
return self
end
-- Beacon type.
local Type=BEACON.Type.TACAN
-- Beacon system.
local System=BEACON.System.TACAN
-- Check if unit is an aircraft and set system accordingly.
local AA=self.Positionable:IsAir()
if AA then
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
-- Check if "Y" mode is selected for aircraft.
if Mode=="X" then
--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
-- Attached unit.
local UnitID=self.Positionable:GetID()
-- Debug.
self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
-- Start beacon.
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
-- Stop scheduler.
if Duration then
self.Positionable:DeactivateBeacon(Duration)
end
return self
end
--- Activates an ICLS BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
-- @param #BEACON self
-- @param #number Channel ICLS channel.
-- @param #string Callsign The Message 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:ActivateICLS(Channel, Callsign, Duration)
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
-- Attached unit.
local UnitID=self.Positionable:GetID()
-- Debug
self:T2({"ICLS BEACON started!"})
-- Start beacon.
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
-- Stop scheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:DeactivateBeacon(Duration)
end
return self
end
--- 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 #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 #boolean Bearing Can the BEACON be homed on ?
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
-- @usage
-- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
--
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
self:F({TACANChannel, Message, Bearing, BeaconDuration})
local IsValid = true
if not self.Positionable:IsAir() then
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
IsValid = false
end
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
if not Frequency then
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
IsValid = false
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 or 14 (TACAN_AA_MODE_Y) if it does not
local System
if Bearing then
System = BEACON.System.TACAN_TANKER_Y
else
System = BEACON.System.TACAN_AA_MODE_Y
end
if IsValid then -- Starts the BEACON
self:T2({"AA TACAN BEACON started !"})
self.Positionable:SetCommand({
id = "ActivateBeacon",
params = {
type = BEACON.Type.TACAN,
system = System,
callsign = Message,
AA = true,
frequency = Frequency,
bearing = Bearing,
modeChannel = "Y",
}
})
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New(nil,
function()
self:StopAATACAN()
end, {}, BeaconDuration)
end
end
return self
end
--- Stops the AA TACAN BEACON
-- @param #BEACON self
-- @return #BEACON self
function BEACON:StopAATACAN()
self:F()
if not self.Positionable then
self:E({"Start the beacon first before stoping it !"})
else
self.Positionable:SetCommand({
id = 'DeactivateBeacon',
params = {
}
})
end
end
--- 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.
-- 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.
-- The following e.g. can home in on these specific frequencies :
-- * **Mi8**
-- * 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
-- * ARK9 -> 150-1300KHz
-- * **Huey**
-- * AN/ARC-131 -> 30-76 Mhz FM
-- @param #BEACON self
-- @param #string FileName The name of the audio file
-- @param #number Frequency in MHz
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @param #number Power in W
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
-- @usage
-- -- Let's create a beacon for a unit in distress.
-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131)
-- -- The beacon they use is battery-powered, and only lasts for 5 min
-- local UnitInDistress = UNIT:FindByName("Unit1")
-- local UnitBeacon = UnitInDistress:GetBeacon()
--
-- -- Set the beacon and start it
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
local IsValid = false
-- Check the filename
if type(FileName) == "string" then
if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName
end
IsValid = true
end
end
if not IsValid then
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
end
-- Check the Frequency
if type(Frequency) ~= "number" and IsValid then
self:E({"Frequency invalid. ", Frequency})
IsValid = false
end
Frequency = Frequency * 1000000 -- Conversion to Hz
-- Check the modulation
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
IsValid = false
end
-- Check the Power
if type(Power) ~= "number" and IsValid then
self:E({"Power is invalid. ", Power})
IsValid = false
end
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
if IsValid then
self:T2({"Activating Beacon on ", Frequency, Modulation})
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil,
function()
self:StopRadioBeacon()
end, {}, BeaconDuration)
end
end
end
--- Stop the Radio Beacon
-- @param #BEACON self
-- @return #BEACON self
function BEACON:StopRadioBeacon()
self:F()
-- The unique name of the transmission is the class ID
trigger.action.stopRadioTransmission(tostring(self.ID))
return self
end
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
-- @param #BEACON self
-- @param #number TACANChannel
-- @param #string TACANMode
-- @return #number Frequecy
-- @return #nil if parameters are invalid
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
self:F3({TACANChannel, TACANMode})
if type(TACANChannel) ~= "number" then
if TACANMode ~= "X" and TACANMode ~= "Y" then
return nil -- error in arguments
end
end
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
-- I have no idea what it does but it seems to work
local A = 1151 -- 'X', channel >= 64
local B = 64 -- channel >= 64
if TACANChannel < 64 then
B = 1
end
if TACANMode == 'Y' then
A = 1025
if TACANChannel < 64 then
A = 1088
end
else -- 'X'
if TACANChannel < 64 then
A = 962
end
end
return (A + TACANChannel - B) * 1000000
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +1,87 @@
--- **Core** - Models the process to achieve goal(s).
--
-- ===
--
--
-- ## Features:
--
--
-- * Define the goal.
-- * Monitor the goal achievement.
-- * Manage goal contribution by players.
--
--
-- ===
--
--
-- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized.
--
--
-- ===
--
--
-- ### Author: **FlightControl**
--
-- ### Contributions: **funkyfranky**
--
-- ===
--
--
-- @module Core.Goal
-- @image Core_Goal.JPG
do -- Goal
--- @type GOAL
-- @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.
--
--
-- # 1. GOAL constructor
--
--
-- * @{#GOAL.New}(): Creates a new GOAL object.
--
--
-- # 2. GOAL is a finite state machine (FSM).
--
--
-- ## 2.1. GOAL States
--
--
-- * **Pending**: The goal object is in progress.
-- * **Achieved**: The goal objective is Achieved.
--
--
-- ## 2.2. GOAL Events
--
--
-- * **Achieved**: Set the goal objective to Achieved.
--
--
-- # 3. Player contributions.
--
--
-- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement.
-- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal.
-- The player contributions are based on a points system, an internal counter per player.
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
-- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}().
-- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}().
--
--
-- # 4. Goal achievement.
--
--
-- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event.
-- The underlying 2 examples will achieve the goals for the `Goal` object:
--
--
-- Goal:Achieved() -- Achieve the goal immediately.
-- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds.
--
--
-- # 5. Check goal achievement.
--
--
-- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed).
-- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler.
--
--
-- @field #GOAL
GOAL = {
ClassName = "GOAL",
}
--- @field #table GOAL.Players
GOAL.Players = {}
--- @field #number GOAL.TotalContributions
GOAL.TotalContributions = 0
--- GOAL Constructor.
-- @param #GOAL self
-- @return #GOAL
function GOAL:New()
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
self:F( {} )
@@ -103,11 +102,10 @@ do -- Goal
-- @param #string From
-- @param #string Event
-- @param #string To
self:SetStartState( "Pending" )
self:AddTransition( "*", "Achieved", "Achieved" )
self:AddTransition( "*", "Achieved", "Achieved" )
--- Achieved Handler OnBefore for GOAL
-- @function [parent=#GOAL] OnBeforeAchieved
-- @param #GOAL self
@@ -115,46 +113,44 @@ do -- Goal
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Achieved Handler OnAfter for GOAL
-- @function [parent=#GOAL] OnAfterAchieved
-- @param #GOAL self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Achieved Trigger for GOAL
-- @function [parent=#GOAL] Achieved
-- @param #GOAL self
--- Achieved Asynchronous Trigger for GOAL
-- @function [parent=#GOAL] __Achieved
-- @param #GOAL self
-- @param #number Delay
self:SetEventPriority( 5 )
return self
end
--- Add a new contribution by a player.
-- @param #GOAL self
-- @param #string PlayerName The name of the player.
function GOAL:AddPlayerContribution( PlayerName )
self:F( { PlayerName } )
self.Players[PlayerName] = self.Players[PlayerName] or 0
self.Players[PlayerName] = self.Players[PlayerName] + 1
self.TotalContributions = self.TotalContributions + 1
end
--- @param #GOAL self
-- @param #number Player contribution.
function GOAL:GetPlayerContribution( PlayerName )
return self.Players[PlayerName] or 0
return self.Players[PlayerName] or 0
end
--- Get the players who contributed to achieve the goal.
-- The result is a list of players, sorted by the name of the players.
-- @param #GOAL self
@@ -163,7 +159,6 @@ do -- Goal
return self.Players or {}
end
--- Gets the total contributions that happened to achieve the goal.
-- The result is a number.
-- @param #GOAL self
@@ -171,9 +166,7 @@ do -- Goal
function GOAL:GetTotalContributions()
return self.TotalContributions or 0
end
--- Validates if the goal is achieved.
-- @param #GOAL self
-- @return #boolean true if the goal is achieved.
@@ -181,4 +174,4 @@ do -- Goal
return self:Is( "Achieved" )
end
end
end

View File

@@ -14,7 +14,7 @@
-- * Only create or delete menus when required, and keep existing menus persistent.
-- * Update menu structures.
-- * Refresh menu structures intelligently, based on a time stamp of updates.
-- - Delete obscolete menus.
-- - Delete obsolete menus.
-- - Create new one where required.
-- - Don't touch the existing ones.
-- * Provide a variable amount of parameters to menus.
@@ -23,7 +23,7 @@
-- * Provide a great tool to manage menus in your code.
--
-- 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
-- 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.
@@ -53,7 +53,6 @@
-- @module Core.Menu
-- @image Core_Menu.JPG
MENU_INDEX = {}
MENU_INDEX.MenuMission = {}
MENU_INDEX.MenuMission.Menus = {}
@@ -64,10 +63,7 @@ MENU_INDEX.Coalition[coalition.side.RED] = {}
MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
MENU_INDEX.Group = {}
function MENU_INDEX:ParentPath( ParentMenu, MenuText )
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
if ParentMenu 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
return Path
end
function MENU_INDEX:PrepareMission()
self.MenuMission.Menus = self.MenuMission.Menus or {}
end
function MENU_INDEX:PrepareCoalition( CoalitionSide )
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
end
---
-- @param Wrapper.Group#GROUP Group
function MENU_INDEX:PrepareGroup( Group )
@@ -119,42 +111,26 @@ function MENU_INDEX:PrepareGroup( Group )
end
end
function MENU_INDEX:HasMissionMenu( Path )
return self.MenuMission.Menus[Path]
end
function MENU_INDEX:SetMissionMenu( Path, Menu )
self.MenuMission.Menus[Path] = Menu
end
function MENU_INDEX:ClearMissionMenu( Path )
self.MenuMission.Menus[Path] = nil
end
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
return self.Coalition[Coalition].Menus[Path]
end
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
self.Coalition[Coalition].Menus[Path] = Menu
end
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
self.Coalition[Coalition].Menus[Path] = nil
end
function MENU_INDEX:HasGroupMenu( Group, Path )
if Group and Group:IsAlive() then
local MenuGroupName = Group:GetName()
@@ -162,53 +138,36 @@ function MENU_INDEX:HasGroupMenu( Group, Path )
end
return nil
end
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
local MenuGroupName = Group:GetName()
Group:F({MenuGroupName=MenuGroupName,Path=Path})
self.Group[MenuGroupName].Menus[Path] = Menu
end
function MENU_INDEX:ClearGroupMenu( Group, Path )
local MenuGroupName = Group:GetName()
self.Group[MenuGroupName].Menus[Path] = nil
end
function MENU_INDEX:Refresh( Group )
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
Menu:Refresh()
end
for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do
Menu:Refresh()
end
for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do
Menu:Refresh()
end
local GroupName = Group:GetName()
for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do
Menu:Refresh()
end
return self
end
do -- MENU_BASE
--- @type MENU_BASE
-- @extends Base#BASE
-- @extends Core.Base#BASE
--- Defines the main MENU class where other MENU classes are derived from.
-- This is an abstract class, so don't use it.
-- @field #MENU_BASE
@@ -216,10 +175,10 @@ do -- MENU_BASE
ClassName = "MENU_BASE",
MenuPath = nil,
MenuText = "",
MenuParentPath = nil
MenuParentPath = nil,
}
--- Consructor
--- Constructor
-- @param #MENU_BASE
-- @return #MENU_BASE
function MENU_BASE:New( MenuText, ParentMenu )
@@ -228,27 +187,25 @@ do -- MENU_BASE
if ParentMenu ~= nil then
MenuParentPath = ParentMenu.MenuPath
end
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() )
self.MenuPath = nil
self.MenuText = MenuText
self.ParentMenu = ParentMenu
self.MenuParentPath = MenuParentPath
self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
self.MenuPath = nil
self.MenuText = MenuText
self.ParentMenu = ParentMenu
self.MenuParentPath = MenuParentPath
self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
self.Menus = {}
self.MenuCount = 0
self.MenuTime = timer.getTime()
self.MenuStamp = timer.getTime()
self.MenuRemoveParent = false
if self.ParentMenu then
self.ParentMenu.Menus = self.ParentMenu.Menus or {}
self.ParentMenu.Menus[MenuText] = self
end
return self
return self
end
function MENU_BASE:SetParentMenu( MenuText, Menu )
if self.ParentMenu then
self.ParentMenu.Menus = self.ParentMenu.Menus or {}
@@ -256,7 +213,6 @@ do -- MENU_BASE
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
end
end
function MENU_BASE:ClearParentMenu( MenuText )
if self.ParentMenu and self.ParentMenu.Menus[MenuText] then
self.ParentMenu.Menus[MenuText] = nil
@@ -266,7 +222,6 @@ do -- MENU_BASE
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}.
-- @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}.
@@ -276,7 +231,6 @@ do -- MENU_BASE
self.MenuRemoveParent = RemoveParent
return self
end
--- Gets a @{Menu} from a parent @{Menu}
-- @param #MENU_BASE self
@@ -285,13 +239,30 @@ do -- MENU_BASE
function MENU_BASE:GetMenu( MenuText )
return self.Menus[MenuText]
end
--- Sets a menu stamp for later prevention of menu removal.
-- @param #MENU_BASE self
-- @param MenuStamp
-- @return #MENU_BASE
function MENU_BASE:SetStamp( MenuStamp )
self.MenuStamp = MenuStamp
return self
end
--- Gets a menu stamp for later prevention of menu removal.
-- @param #MENU_BASE self
-- @return MenuStamp
function MENU_BASE:GetStamp()
return timer.getTime()
end
--- Sets a time stamp for later prevention of menu removal.
-- @param #MENU_BASE self
-- @param MenuTime
-- @param MenuStamp
-- @return #MENU_BASE
function MENU_BASE:SetTime( MenuTime )
self.MenuTime = MenuTime
function MENU_BASE:SetTime( MenuStamp )
self.MenuStamp = MenuStamp
return self
end
@@ -305,9 +276,7 @@ do -- MENU_BASE
end
end
do -- MENU_COMMAND_BASE
--- @type MENU_COMMAND_BASE
-- @field #function MenuCallHandler
-- @extends Core.Menu#MENU_BASE
@@ -328,8 +297,7 @@ do -- MENU_COMMAND_BASE
-- @return #MENU_COMMAND_BASE
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.
-- This error handler catches the menu error and displays the full call stack.
local ErrorHandler = function( errmsg )
@@ -349,7 +317,7 @@ do -- MENU_COMMAND_BASE
local Status, Result = xpcall( MenuFunction, ErrorHandler )
end
return self
return self
end
--- This sets the new command function of a menu,
@@ -362,7 +330,6 @@ do -- MENU_COMMAND_BASE
self.CommandMenuFunction = CommandMenuFunction
return self
end
--- This sets the new command arguments of a menu,
-- 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!!!
@@ -373,35 +340,30 @@ do -- MENU_COMMAND_BASE
self.CommandMenuArguments = CommandMenuArguments
return self
end
end
do -- MENU_MISSION
--- @type MENU_MISSION
-- @extends Core.Menu#MENU_BASE
--- 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.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
-- @field #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.
-- @param #MENU_MISSION self
-- @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
function MENU_MISSION:New( MenuText, ParentMenu )
MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu then
return MissionMenu
else
@@ -414,17 +376,15 @@ do -- MENU_MISSION
end
end
--- Refreshes a radio item for a mission
-- @param #MENU_MISSION self
-- @return #MENU_MISSION
function MENU_MISSION:Refresh()
do
missionCommands.removeItem( self.MenuPath )
self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath )
end
return self
end
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
@@ -443,15 +403,14 @@ do -- MENU_MISSION
--- Removes the main menu and the sub menus recursively of this MENU_MISSION.
-- @param #MENU_MISSION self
-- @return #nil
function MENU_MISSION:Remove( MenuTime, MenuTag )
function MENU_MISSION:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu == self then
self:RemoveSubMenus()
if not MenuTime or self.MenuTime ~= MenuTime then
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then
@@ -469,10 +428,7 @@ do -- MENU_MISSION
return self
end
end
do -- MENU_MISSION_COMMAND
--- @type MENU_MISSION_COMMAND
@@ -485,7 +441,7 @@ do -- MENU_MISSION_COMMAND
--
-- @field #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.
@@ -500,7 +456,6 @@ do -- MENU_MISSION_COMMAND
MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu then
MissionMenu:SetCommandMenuFunction( CommandMenuFunction )
MissionMenu:SetCommandMenuArguments( arg )
@@ -514,17 +469,15 @@ do -- MENU_MISSION_COMMAND
return self
end
end
--- Refreshes a radio item for a mission
-- @param #MENU_MISSION_COMMAND self
-- @return #MENU_MISSION_COMMAND
function MENU_MISSION_COMMAND:Refresh()
do
missionCommands.removeItem( self.MenuPath )
missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end
return self
end
--- Removes a radio command item for a coalition
@@ -535,9 +488,8 @@ do -- MENU_MISSION_COMMAND
MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu == self then
if not MenuTime or self.MenuTime ~= MenuTime then
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then
@@ -554,13 +506,8 @@ do -- MENU_MISSION_COMMAND
return self
end
end
do -- MENU_COALITION
--- @type MENU_COALITION
-- @extends Core.Menu#MENU_BASE
@@ -616,18 +563,15 @@ do -- MENU_COALITION
-- @param #MENU_COALITION self
-- @param DCS#coalition.side Coalition The coalition owning 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
function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
MENU_INDEX:PrepareCoalition( Coalition )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
if CoalitionMenu then
return CoalitionMenu
else
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetCoalitionMenu( Coalition, Path, self )
@@ -638,17 +582,15 @@ do -- MENU_COALITION
return self
end
end
--- Refreshes a radio item for a coalition
-- @param #MENU_COALITION self
-- @return #MENU_COALITION
function MENU_COALITION:Refresh()
do
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath )
end
return self
end
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
@@ -666,15 +608,14 @@ do -- MENU_COALITION
--- Removes the main menu and the sub menus recursively of this MENU_COALITION.
-- @param #MENU_COALITION self
-- @return #nil
function MENU_COALITION:Remove( MenuTime, MenuTag )
function MENU_COALITION:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareCoalition( self.Coalition )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
if CoalitionMenu == self then
self:RemoveSubMenus()
if not MenuTime or self.MenuTime ~= MenuTime then
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then
@@ -691,11 +632,7 @@ do -- MENU_COALITION
return self
end
end
do -- MENU_COALITION_COMMAND
--- @type MENU_COALITION_COMMAND
@@ -724,7 +661,6 @@ do -- MENU_COALITION_COMMAND
MENU_INDEX:PrepareCoalition( Coalition )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
if CoalitionMenu then
CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction )
CoalitionMenu:SetCommandMenuArguments( arg )
@@ -739,33 +675,29 @@ do -- MENU_COALITION_COMMAND
self:SetParentMenu( self.MenuText, self )
return self
end
end
--- Refreshes a radio item for a coalition
-- @param #MENU_COALITION_COMMAND self
-- @return #MENU_COALITION_COMMAND
function MENU_COALITION_COMMAND:Refresh()
do
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end
return self
end
--- Removes a radio command item for a coalition
-- @param #MENU_COALITION_COMMAND self
-- @return #nil
function MENU_COALITION_COMMAND:Remove( MenuTime, MenuTag )
function MENU_COALITION_COMMAND:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareCoalition( self.Coalition )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
if CoalitionMenu == self then
if not MenuTime or self.MenuTime ~= MenuTime then
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then
@@ -782,20 +714,16 @@ do -- MENU_COALITION_COMMAND
return self
end
end
--- MENU_GROUP
do
-- 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.
-- the same menus twice during initialization logic.
-- These menu classes are handling this logic with this variable.
local _MENUGROUPS = {}
--- @type MENU_GROUP
-- @extends Core.Menu#MENU_BASE
@@ -871,16 +799,13 @@ do
MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then
return GroupMenu
else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group
self.GroupID = Group:GetID()
self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath )
self:SetParentMenu( self.MenuText, self )
@@ -888,12 +813,10 @@ do
end
end
--- Refreshes a new radio item for a group and submenus
-- @param #MENU_GROUP self
-- @return #MENU_GROUP
function MENU_GROUP:Refresh()
do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -902,39 +825,36 @@ do
Menu:Refresh()
end
end
return self
end
--- Removes the sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self
-- @param MenuTime
-- @param MenuStamp
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP self
function MENU_GROUP:RemoveSubMenus( MenuTime, MenuTag )
function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag )
for MenuText, Menu in pairs( self.Menus or {} ) do
Menu:Remove( MenuTime, MenuTag )
Menu:Remove( MenuStamp, MenuTag )
end
self.Menus = nil
end
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self
-- @param MenuTime
-- @param MenuStamp
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil
function MENU_GROUP:Remove( MenuTime, MenuTag )
function MENU_GROUP:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then
self:RemoveSubMenus( MenuTime, MenuTag )
if not MenuTime or self.MenuTime ~= MenuTime then
self:RemoveSubMenus( MenuStamp, MenuTag )
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
@@ -975,18 +895,15 @@ do
-- @param CommandMenuArgument An argument for the function.
-- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then
GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
GroupMenu:SetCommandMenuArguments( arg )
return GroupMenu
else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group
@@ -997,34 +914,30 @@ do
self:SetParentMenu( self.MenuText, self )
return self
end
end
--- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND self
-- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:Refresh()
do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end
return self
end
--- Removes a menu structure for a group.
-- @param #MENU_GROUP_COMMAND self
-- @param MenuTime
-- @param MenuStamp
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil
function MENU_GROUP_COMMAND:Remove( MenuTime, MenuTag )
function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then
if not MenuTime or self.MenuTime ~= MenuTime then
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
@@ -1041,13 +954,9 @@ do
return self
end
end
--- MENU_GROUP_DELAYED
do
--- @type MENU_GROUP_DELAYED
-- @extends Core.Menu#MENU_BASE
@@ -1075,16 +984,13 @@ do
MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then
return GroupMenu
else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group
self.GroupID = Group:GetID()
if self.MenuParentPath then
self.MenuPath = UTILS.DeepCopy( self.MenuParentPath )
else
@@ -1098,12 +1004,10 @@ do
end
--- Refreshes a new radio item for a group and submenus
-- @param #MENU_GROUP_DELAYED self
-- @return #MENU_GROUP_DELAYED
function MENU_GROUP_DELAYED:Set()
do
if not self.MenuSet then
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -1114,15 +1018,12 @@ do
Menu:Set()
end
end
end
--- Refreshes a new radio item for a group and submenus
-- @param #MENU_GROUP_DELAYED self
-- @return #MENU_GROUP_DELAYED
function MENU_GROUP_DELAYED:Refresh()
do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -1131,39 +1032,36 @@ do
Menu:Refresh()
end
end
return self
end
--- Removes the sub menus recursively of this MENU_GROUP_DELAYED.
-- @param #MENU_GROUP_DELAYED self
-- @param MenuTime
-- @param MenuStamp
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP_DELAYED self
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuTime, MenuTag )
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag )
for MenuText, Menu in pairs( self.Menus or {} ) do
Menu:Remove( MenuTime, MenuTag )
Menu:Remove( MenuStamp, MenuTag )
end
self.Menus = nil
end
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP_DELAYED self
-- @param MenuTime
-- @param MenuStamp
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil
function MENU_GROUP_DELAYED:Remove( MenuTime, MenuTag )
function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then
self:RemoveSubMenus( MenuTime, MenuTag )
if not MenuTime or self.MenuTime ~= MenuTime then
self:RemoveSubMenus( MenuStamp, MenuTag )
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
@@ -1205,18 +1103,15 @@ do
-- @param CommandMenuArgument An argument for the function.
-- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then
GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
GroupMenu:SetCommandMenuArguments( arg )
return GroupMenu
else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group
@@ -1232,48 +1127,42 @@ do
self:SetParentMenu( self.MenuText, self )
return self
end
end
--- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND_DELAYED self
-- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:Set()
do
if not self.MenuSet then
self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
self.MenuSet = true
end
end
end
--- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND_DELAYED self
-- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:Refresh()
do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end
return self
end
--- Removes a menu structure for a group.
-- @param #MENU_GROUP_COMMAND_DELAYED self
-- @param MenuTime
-- @param MenuStamp
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuTime, MenuTag )
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then
if not MenuTime or self.MenuTime ~= MenuTime then
if not MenuStamp or self.MenuStamp ~= MenuStamp then
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
@@ -1290,6 +1179,5 @@ do
return self
end
end

View File

@@ -1,18 +1,20 @@
--- **Core** - Informs the players using messages during a simulation.
--
--
-- ===
--
--
-- ## Features:
--
--
-- * A more advanced messaging system using the DCS message system.
-- * Time messages.
-- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS.
-- * Send message to all players.
-- * Send messages to a coalition.
-- * Send messages to a specific group.
--
-- * Send messages to a specific unit or client.
--
--
-- ===
--
--
-- @module Core.Message
-- @image Core_Message.JPG
@@ -23,42 +25,43 @@
--- Message System to display Messages to Clients, Coalitions or All.
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
-- Messages can contain a category which is indicating the category of the message.
--
--
-- ## MESSAGE construction
--
--
-- Messages are created with @{#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
-- To send messages, you need to use the To functions.
--
--
-- ## Send messages to an audience
--
--
-- Messages are sent:
--
-- * To a @{Client} using @{#MESSAGE.ToClient}().
-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
-- * To a @{Wrapper.Unit} using @{#MESSAGE.ToUnit}()
-- * To a coalition using @{#MESSAGE.ToCoalition}().
-- * To the red coalition using @{#MESSAGE.ToRed}().
-- * To the blue coalition using @{#MESSAGE.ToBlue}().
-- * To all Players using @{#MESSAGE.ToAll}().
--
--
-- ## Send conditionally to an audience
--
--
-- Messages can be sent conditionally to an audience (when a condition is true):
--
--
-- * To all players using @{#MESSAGE.ToAllIf}().
-- * To a coalition using @{#MESSAGE.ToCoalitionIf}().
--
--
-- ===
--
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ### Contributions:
--
-- ===
--
--
-- @field #MESSAGE
MESSAGE = {
ClassName = "MESSAGE",
MessageCategory = 0,
MessageID = 0,
ClassName = "MESSAGE",
MessageCategory = 0,
MessageID = 0,
}
--- Message Types
@@ -68,10 +71,9 @@ MESSAGE.Type = {
Information = "Information",
Briefing = "Briefing 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.
-- @param self
-- @param #string MessageText is the text of the Message.
@@ -80,52 +82,52 @@ MESSAGE.Type = {
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
-- @return #MESSAGE
-- @usage
-- -- Create a series of new Messages.
-- -- 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".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
-- 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" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
--
-- -- Create a series of new Messages.
-- -- 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".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
-- 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" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
--
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText, MessageDuration, MessageCategory } )
local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText, MessageDuration, MessageCategory } )
self.MessageType = nil
-- When no MessageCategory is given, we don't show it as a title...
if MessageCategory and MessageCategory ~= "" then
if MessageCategory:sub(-1) ~= "\n" then
if MessageCategory and MessageCategory ~= "" then
if MessageCategory:sub( -1 ) ~= "\n" then
self.MessageCategory = MessageCategory .. ": "
else
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
end
else
self.MessageCategory = ""
end
self.ClearScreen=false
if ClearScreen~=nil then
self.ClearScreen=ClearScreen
self.ClearScreen = false
if ClearScreen ~= nil then
self.ClearScreen = ClearScreen
end
self.MessageDuration = MessageDuration or 5
self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
self.MessageSent = false
self.MessageGroup = false
self.MessageCoalition = false
self.MessageDuration = MessageDuration or 5
self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
return self
self.MessageSent = false
self.MessageGroup = false
self.MessageCoalition = false
return self
end
--- Creates a new MESSAGE object of a certain type.
-- Note that these MESSAGE objects are not yet displayed on the display panel.
--- Creates a new MESSAGE object of a certain type.
-- 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.
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
-- @param self
@@ -134,83 +136,86 @@ end
-- @param #boolean ClearScreen (optional) Clear all previous messages.
-- @return #MESSAGE
-- @usage
--
-- 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 )
-- 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 )
--
function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText } )
self.MessageType = MessageType
self.ClearScreen=false
if ClearScreen~=nil then
self.ClearScreen=ClearScreen
self.ClearScreen = false
if ClearScreen ~= nil then
self.ClearScreen = ClearScreen
end
self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
return self
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
-- @return #MESSAGE
function MESSAGE:Clear()
self:F()
self.ClearScreen=true
self.ClearScreen = true
return self
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".
-- @param #MESSAGE self
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
-- @param Core.Settings#SETTINGS Settings Settings used to display the message.
-- @return #MESSAGE
-- @usage
-- -- 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.
-- ClientGroup = Group.getByName( "ClientGroup" )
--
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- or
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- or
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
-- MessageClient1:ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup )
-- -- 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.
-- ClientGroup = Group.getByName( "ClientGroup" )
--
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- or
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- or
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
-- MessageClient1:ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup )
--
function MESSAGE:ToClient( Client, Settings )
self:F( Client )
self:F( Client )
if Client and Client:GetClientGroupID() then
if Client and Client:GetClientGroupID() then
if self.MessageType then
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
local Unit = Client:GetClient()
if self.MessageDuration ~= 0 then
local ClientGroupID = Client:GetClientGroupID()
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)
end
end
return self
local ClientGroupID = Client:GetClientGroupID()
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.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
end
end
return self
end
--- Sends a MESSAGE to a Group.
--- Sends a MESSAGE to a Group.
-- @param #MESSAGE self
-- @param Wrapper.Group#GROUP Group to which the message is displayed.
-- @return #MESSAGE Message object.
@@ -218,74 +223,105 @@ function MESSAGE:ToGroup( Group, Settings )
self:F( Group.GroupName )
if Group then
if self.MessageType then
local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group: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.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end
end
return self
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 ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
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.outTextForGroup( Group:GetID(), 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
return self
end
--- Sends a MESSAGE to the Blue coalition.
-- @param #MESSAGE self
-- @return #MESSAGE
-- @usage
-- -- 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()
-- or
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
-- 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:ToBlue()
function MESSAGE:ToBlue()
self:F()
self:ToCoalition( coalition.side.BLUE )
return self
end
--- Sends a MESSAGE to the Red Coalition.
-- @param #MESSAGE self
-- @return #MESSAGE
-- @usage
-- -- 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()
-- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
-- 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:ToRed()
function MESSAGE:ToRed( )
self:F()
--
-- -- 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()
-- or
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
-- 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:ToBlue()
--
function MESSAGE:ToBlue()
self:F()
self:ToCoalition( coalition.side.RED )
return self
self:ToCoalition( coalition.side.BLUE )
return self
end
--- Sends a MESSAGE to a Coalition.
--- Sends a MESSAGE to the Red Coalition.
-- @param #MESSAGE self
-- @return #MESSAGE
-- @usage
--
-- -- 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()
-- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
-- 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:ToRed()
--
function MESSAGE:ToRed()
self:F()
self:ToCoalition( coalition.side.RED )
return self
end
--- Sends a MESSAGE to a Coalition.
-- @param #MESSAGE self
-- @param #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed.
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object.
-- @usage
-- -- 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 )
-- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
-- 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:ToCoalition( coalition.side.RED )
--
-- -- 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 )
-- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
-- 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:ToCoalition( coalition.side.RED )
--
function MESSAGE:ToCoalition( CoalitionSide, Settings )
self:F( CoalitionSide )
self:F( CoalitionSide )
if self.MessageType then
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
@@ -293,20 +329,20 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if CoalitionSide then
if CoalitionSide then
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end
end
return self
end
return self
end
--- Sends a MESSAGE to a Coalition if the given Condition is true.
--- Sends a MESSAGE to a Coalition if the given Condition is true.
-- @param #MESSAGE self
-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}.
-- @param #boolean Condition Sends the message only if the condition is true.
-- @param #boolean Condition Sends the message only if the condition is true.
-- @return #MESSAGE self
function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
self:F( CoalitionSide )
@@ -314,7 +350,7 @@ function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
if Condition and Condition == true then
self:ToCoalition( CoalitionSide )
end
return self
end
@@ -323,14 +359,16 @@ end
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE
-- @usage
-- -- 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()
-- or
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
-- or
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
-- MessageAll:ToAll()
function MESSAGE:ToAll(Settings)
--
-- -- 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()
-- or
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
-- or
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
-- MessageAll:ToAll()
--
function MESSAGE:ToAll( Settings )
self:F()
if self.MessageType then
@@ -340,14 +378,13 @@ function MESSAGE:ToAll(Settings)
end
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end
return self
end
--- Sends a MESSAGE to all players if the given Condition is true.
-- @param #MESSAGE self
-- @return #MESSAGE
@@ -357,5 +394,5 @@ function MESSAGE:ToAllIf( Condition )
self:ToAll()
end
return self
return self
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,594 +0,0 @@
--- **Core** - Is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions.
--
-- ===
--
-- ## Features:
--
-- * Provide radio functionality to broadcast radio transmissions.
-- * Provide beacon functionality to assist pilots.
--
-- The Radio contains 2 classes : RADIO and BEACON
--
-- What are radio communications in DCS ?
--
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
--
-- How to supply DCS my own Sound Files ?
--
-- * Your sound files need to be encoded in **.ogg** or .wav,
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
-- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
--
-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE}
--
-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically,
-- * If the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped.
--
-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft,
-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below).
-- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircaft isn't compatible,
-- you won't hear/be able to use the TACAN beacon informations.
--
-- ===
--
-- ### Author: Hugues "Grey_Echo" Bousquet
--
-- @module Core.Radio
-- @image Core_Radio.JPG
--- Models the radio capabilty.
--
-- ## RADIO usage
--
-- There are 3 steps to a successful radio transmission.
--
-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function,
-- * Then, you will **set the relevant parameters** to the transmission (see below),
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
--
-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE}
--
-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"),
-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission.
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
--
-- Additional Methods to set relevant parameters if the transmiter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}
--
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
--
-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE}
--
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
--
-- What is this power thing ?
--
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
-- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
-- * This an automated DCS calculation you have no say on,
-- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W,
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
--
-- @type RADIO
-- @field Positionable#POSITIONABLE Positionable The transmiter
-- @field #string FileName Name of the sound file
-- @field #number Frequency Frequency of the transmission in Hz
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)
-- @field #string Subtitle Subtitle of the transmission
-- @field #number SubtitleDuration Duration of the Subtitle in seconds
-- @field #number Power Power of the antenna is Watts
-- @field #boolean Loop (default true)
-- @extends Core.Base#BASE
RADIO = {
ClassName = "RADIO",
FileName = "",
Frequency = 0,
Modulation = radio.modulation.AM,
Subtitle = "",
SubtitleDuration = 0,
Power = 100,
Loop = true,
}
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead
-- @param #RADIO self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #RADIO Radio
-- @return #nil If Positionable is invalid
function RADIO:New(Positionable)
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
self.Loop = true -- default Loop to true (not sure the above RADIO definition actually is working)
self:F(Positionable)
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable
return self
end
self:E({"The passed positionable is invalid, no RADIO created", Positionable})
return nil
end
--- Check validity of the filename passed and sets RADIO.FileName
-- @param #RADIO self
-- @param #string FileName File name of the sound file (i.e. "Noise.ogg")
-- @return #RADIO self
function RADIO:SetFileName(FileName)
self:F2(FileName)
if type(FileName) == "string" then
if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName
end
self.FileName = FileName
return self
end
end
self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName})
return self
end
--- Check validity of the frequency passed and sets RADIO.Frequency
-- @param #RADIO self
-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz)
-- @return #RADIO self
function RADIO:SetFrequency(Frequency)
self:F2(Frequency)
if type(Frequency) == "number" then
-- If frequency is in range
if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then
self.Frequency = Frequency * 1000000 -- Conversion in Hz
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
self.Positionable:SetCommand({
id = "SetFrequency",
params = {
frequency = self.Frequency,
modulation = self.Modulation,
}
})
end
return self
end
end
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency})
return self
end
--- Check validity of the frequency passed and sets RADIO.Modulation
-- @param #RADIO self
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @return #RADIO self
function RADIO:SetModulation(Modulation)
self:F2(Modulation)
if type(Modulation) == "number" then
if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self.Modulation = Modulation
return self
end
end
self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation})
return self
end
--- Check validity of the power passed and sets RADIO.Power
-- @param #RADIO self
-- @param #number Power in W
-- @return #RADIO self
function RADIO:SetPower(Power)
self:F2(Power)
if type(Power) == "number" then
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
return self
end
self:E({"Power is invalid. Power unchanged.", self.Power})
return self
end
--- Check validity of the loop passed and sets RADIO.Loop
-- @param #RADIO self
-- @param #boolean Loop
-- @return #RADIO self
-- @usage
function RADIO:SetLoop(Loop)
self:F2(Loop)
if type(Loop) == "boolean" then
self.Loop = Loop
return self
end
self:E({"Loop is invalid. Loop unchanged.", self.Loop})
return self
end
--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration
-- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration
-- @param #RADIO self
-- @param #string Subtitle
-- @param #number SubtitleDuration in s
-- @return #RADIO self
-- @usage
-- -- create the broadcaster and attaches it a RADIO
-- local MyUnit = UNIT:FindByName("MyUnit")
-- local MyUnitRadio = MyUnit:GetRadio()
--
-- -- add a subtitle for the next transmission, which will be up for 10s
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
self:F2({Subtitle, SubtitleDuration})
if type(Subtitle) == "string" then
self.Subtitle = Subtitle
else
self.Subtitle = ""
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
end
if type(SubtitleDuration) == "number" then
if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then
self.SubtitleDuration = SubtitleDuration
return self
end
end
self.SubtitleDuration = 0
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
end
--- Create a new transmission, that is to say, populate the RADIO with relevant data
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
-- but it will work with a UNIT or a GROUP anyway.
-- Only the #RADIO and the Filename are mandatory
-- @param #RADIO self
-- @param #string FileName
-- @param #number Frequency in MHz
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @param #number Power in W
-- @return #RADIO self
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
self:F({FileName, Frequency, Modulation, Power})
self:SetFileName(FileName)
if Frequency then self:SetFrequency(Frequency) end
if Modulation then self:SetModulation(Modulation) end
if Power then self:SetPower(Power) end
if Loop then self:SetLoop(Loop) end
return self
end
--- Create a new transmission, that is to say, populate the RADIO with relevant data
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
-- Only the RADIO and the Filename are mandatory.
-- @param #RADIO self
-- @param #string FileName
-- @param #string Subtitle
-- @param #number SubtitleDuration in s
-- @param #number Frequency in MHz
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @param #boolean Loop
-- @return #RADIO self
function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop)
self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop})
self:SetFileName(FileName)
local Duration = 5
if SubtitleDuration then Duration = SubtitleDuration end
-- SubtitleDuration argument was missing, adding it
if Subtitle then self:SetSubtitle(Subtitle, Duration) end
-- self:SetSubtitleDuration is non existent, removing faulty line
-- if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end
if Frequency then self:SetFrequency(Frequency) end
if Modulation then self:SetModulation(Modulation) end
if Loop then self:SetLoop(Loop) end
return self
end
--- Actually Broadcast the transmission
-- * The Radio has to be populated with the new transmission before broadcasting.
-- * Please use RADIO setters or either @{#RADIO.NewGenericTransmission} or @{#RADIO.NewUnitTransmission}
-- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE
-- * If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission()
-- * If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
-- @param #RADIO self
-- @return #RADIO self
function RADIO:Broadcast()
self:F()
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
self:T2("Broadcasting from a UNIT or a GROUP")
self.Positionable:SetCommand({
id = "TransmitMessage",
params = {
file = self.FileName,
duration = self.SubtitleDuration,
subtitle = self.Subtitle,
loop = self.Loop,
}
})
else
-- If the POSITIONABLE is anything else, we revert to the general singleton function
-- I need to give it a unique name, so that the transmission can be stopped later. I use the class ID
self:T2("Broadcasting from a POSITIONABLE")
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
end
return self
end
--- Stops a transmission
-- This function is especially usefull to stop the broadcast of looped transmissions
-- @param #RADIO self
-- @return #RADIO self
function RADIO:StopBroadcast()
self:F()
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
self.Positionable:SetCommand({
id = "StopTransmission",
params = {}
})
else
-- Else, we use the appropriate singleton funciton
trigger.action.stopRadioTransmission(tostring(self.ID))
end
return self
end
--- 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.
-- 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
-- attach to a cargo crate, for exemple.
--
-- ## AA 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.
-- Use @#BEACON:StopAATACAN}() to stop it.
--
-- ## 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
-- @{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.
--
-- @type BEACON
-- @extends Core.Base#BASE
BEACON = {
ClassName = "BEACON",
}
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.AATACAN} or @{#BEACON.Generic}
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
-- @param #BEACON self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #BEACON Beacon
-- @return #nil If Positionable is invalid
function BEACON:New(Positionable)
local self = BASE:Inherit(self, BASE:New())
self:F(Positionable)
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable
return self
end
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
return nil
end
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
-- @param #BEACON self
-- @param #number TACANChannel
-- @param #string TACANMode
-- @return #number Frequecy
-- @return #nil if parameters are invalid
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
self:F3({TACANChannel, TACANMode})
if type(TACANChannel) ~= "number" then
if TACANMode ~= "X" and TACANMode ~= "Y" then
return nil -- error in arguments
end
end
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
-- I have no idea what it does but it seems to work
local A = 1151 -- 'X', channel >= 64
local B = 64 -- channel >= 64
if TACANChannel < 64 then
B = 1
end
if TACANMode == 'Y' then
A = 1025
if TACANChannel < 64 then
A = 1088
end
else -- 'X'
if TACANChannel < 64 then
A = 962
end
end
return (A + TACANChannel - B) * 1000000
end
--- Activates a TACAN BEACON on an Aircraft.
-- @param #BEACON self
-- @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 #boolean Bearing Can the BEACON be homed on ?
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
-- @usage
-- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
--
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
self:F({TACANChannel, Message, Bearing, BeaconDuration})
local IsValid = true
if not self.Positionable:IsAir() then
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
IsValid = false
end
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
if not Frequency then
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
IsValid = false
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
-- or 14 (TACAN_AA_MODE_Y) if it does not
local System
if Bearing then
System = 5
else
System = 14
end
if IsValid then -- Starts the BEACON
self:T2({"AA TACAN BEACON started !"})
self.Positionable:SetCommand({
id = "ActivateBeacon",
params = {
type = 4,
system = System,
callsign = Message,
frequency = Frequency,
}
})
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil,
function()
self:StopAATACAN()
end, {}, BeaconDuration)
end
end
return self
end
--- Stops the AA TACAN BEACON
-- @param #BEACON self
-- @return #BEACON self
function BEACON:StopAATACAN()
self:F()
if not self.Positionable then
self:E({"Start the beacon first before stoping it !"})
else
self.Positionable:SetCommand({
id = 'DeactivateBeacon',
params = {
}
})
end
end
--- Activates a general pupose Radio Beacon
-- 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.
-- They can home in on these specific frequencies :
-- * **Mi8**
-- * 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
-- * ARK9 -> 150-1300KHz
-- * **Huey**
-- * AN/ARC-131 -> 30-76 Mhz FM
-- @param #BEACON self
-- @param #string FileName The name of the audio file
-- @param #number Frequency in MHz
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @param #number Power in W
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
-- @usage
-- -- Let's create a beacon for a unit in distress.
-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131)
-- -- The beacon they use is battery-powered, and only lasts for 5 min
-- local UnitInDistress = UNIT:FindByName("Unit1")
-- local UnitBeacon = UnitInDistress:GetBeacon()
--
-- -- Set the beacon and start it
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
local IsValid = false
-- Check the filename
if type(FileName) == "string" then
if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName
end
IsValid = true
end
end
if not IsValid then
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
end
-- Check the Frequency
if type(Frequency) ~= "number" and IsValid then
self:E({"Frequency invalid. ", Frequency})
IsValid = false
end
Frequency = Frequency * 1000000 -- Conversion to Hz
-- Check the modulation
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
IsValid = false
end
-- Check the Power
if type(Power) ~= "number" and IsValid then
self:E({"Power is invalid. ", Power})
IsValid = false
end
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
if IsValid then
self:T2({"Activating Beacon on ", Frequency, Modulation})
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil,
function()
self:StopRadioBeacon()
end, {}, BeaconDuration)
end
end
end
--- Stops the AA TACAN BEACON
-- @param #BEACON self
-- @return #BEACON self
function BEACON:StopRadioBeacon()
self:F()
-- The unique name of the transmission is the class ID
trigger.action.stopRadioTransmission(tostring(self.ID))
end

View File

@@ -1,13 +1,13 @@
--- **Core** - Provides a handy means to create messages and reports.
--
-- ===
--
--
-- ## Features:
--
--
-- * Create text blocks that are formatted.
-- * Create automatic indents.
-- * Variate the delimiters between reporting lines.
--
--
-- ===
--
-- ### Authors: FlightControl : Design & Programming
@@ -15,7 +15,6 @@
-- @module Core.Report
-- @image Core_Report.JPG
--- @type REPORT
-- @extends Core.Base#BASE
@@ -36,7 +35,7 @@ function REPORT:New( Title )
self.Report = {}
self:SetTitle( Title or "" )
self:SetTitle( Title or "" )
self:SetIndent( 3 )
return self
@@ -45,47 +44,46 @@ end
--- Has the REPORT Text?
-- @param #REPORT self
-- @return #boolean
function REPORT:HasText() --R2.1
function REPORT:HasText() -- R2.1
return #self.Report > 0
end
--- Set indent of a REPORT.
-- @param #REPORT self
-- @param #number Indent
-- @return #REPORT
function REPORT:SetIndent( Indent ) --R2.1
function REPORT:SetIndent( Indent ) -- R2.1
self.Indent = Indent
return self
end
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:Add( Text )
self.Report[#self.Report+1] = Text
self.Report[#self.Report + 1] = Text
return self
end
--- Add a new line to a REPORT.
--- Add a new line to a REPORT, but indented. A separator character can be specified to separate the reported lines visually.
-- @param #REPORT self
-- @param #string Text
-- @param #string Text The report text.
-- @param #string Separator (optional) The start of each report line can begin with an optional separator character. This can be a "-", or "#", or "*". You're free to choose what you find the best.
-- @return #REPORT
function REPORT:AddIndent( Text, Separator ) --R2.1
self.Report[#self.Report+1] = ( ( Separator and Separator .. string.rep( " ", self.Indent - 1 ) ) or string.rep(" ", self.Indent ) ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
function REPORT:AddIndent( Text, Separator )
self.Report[#self.Report + 1] = ((Separator and Separator .. string.rep( " ", self.Indent - 1 )) or string.rep( " ", self.Indent )) .. Text:gsub( "\n", "\n" .. string.rep( " ", self.Indent ) )
return self
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 #string Delimiter (optional) A delimiter text.
-- @return #string The report text.
function REPORT:Text( Delimiter )
Delimiter = Delimiter or "\n"
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
local ReportText = (self.Title ~= "" and self.Title .. Delimiter or self.Title) .. table.concat( self.Report, Delimiter ) or ""
return ReportText
end
@@ -94,7 +92,7 @@ end
-- @param #string Title The title of the report.
-- @return #REPORT
function REPORT:SetTitle( Title )
self.Title = Title
self.Title = Title
return self
end

View File

@@ -1,45 +1,74 @@
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules.
--
--
-- ===
--
--
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
--
-- This class is tricky and needs some thorought explanation.
--
-- This class is tricky and needs some thorough explanation.
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
-- The SCHEDULEDISPATCHER class ensures that:
--
--
-- - Scheduled functions are planned according the SCHEDULER object parameters.
-- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters.
-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters.
--
--
-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection:
-- - 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!
-- 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.
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
--
-- 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,
-- these will not be executed anymore when the SCHEDULER object has been destroyed.
--
--
-- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object.
-- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER.
-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method.
-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method.
-- The Schedule() method returns the CallID that is the reference ID for each planned schedule.
--
--
-- ===
--
--
-- ### Contributions: -
-- ### Authors: FlightControl : Design & Programming
--
--
-- @module Core.ScheduleDispatcher
-- @image Core_Schedule_Dispatcher.JPG
--- SCHEDULEDISPATCHER class.
-- @type SCHEDULEDISPATCHER
-- @field #string ClassName Name of the class.
-- @field #number CallID Call ID counter.
-- @field #table PersistentSchedulers Persistent schedulers.
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
-- @extends Core.Base#BASE
--- The SCHEDULEDISPATCHER structure
-- @type SCHEDULEDISPATCHER
SCHEDULEDISPATCHER = {
ClassName = "SCHEDULEDISPATCHER",
CallID = 0,
PersistentSchedulers = {},
ObjectSchedulers = {},
Schedule = nil,
}
--- Player data table holding all important parameters of each player.
-- @type SCHEDULEDISPATCHER.ScheduleData
-- @field #function Function The schedule function to be called.
-- @field #table Arguments Schedule function arguments.
-- @field #number Start Start time in seconds.
-- @field #number Repeat Repeat time interval in seconds.
-- @field #number Randomize Randomization factor [0,1].
-- @field #number Stop Stop time in seconds.
-- @field #number StartTime Time in seconds when the scheduler is created.
-- @field #number ScheduleID Schedule ID.
-- @field #function CallHandler Function to be passed to the DCS timer.scheduleFunction().
-- @field #boolean ShowTrace If true, show tracing info.
--- Create a new schedule dispatcher object.
-- @param #SCHEDULEDISPATCHER self
-- @return #SCHEDULEDISPATCHER self
function SCHEDULEDISPATCHER:New()
local self = BASE:Inherit( self, BASE:New() )
self:F3()
@@ -48,47 +77,100 @@ end
--- Add a Schedule to the ScheduleDispatcher.
-- 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.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop )
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
-- @param #function ScheduleFunction Scheduler function.
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
-- @param #number Start Start time in seconds.
-- @param #number Repeat Repeat interval in seconds.
-- @param #number Randomize Randomization factor [0,1].
-- @param #number Stop Stop time in seconds.
-- @param #number TraceLevel Trace level [0,3].
-- @param Core.Fsm#FSM Fsm Finite state model.
-- @return #string Call ID or nil.
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm )
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm } )
-- Increase counter.
self.CallID = self.CallID + 1
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
-- Create ID.
local CallID = self.CallID .. "#" .. (Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "") or ""
self:T2( string.format( "Adding schedule #%d CallID=%s", self.CallID, CallID ) )
-- Initialize PersistentSchedulers
self.PersistentSchedulers = self.PersistentSchedulers or {}
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
if Scheduler.MasterObject then
self.ObjectSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
self:F3( { CallID = CallID, ObjectScheduler = tostring( self.ObjectSchedulers[CallID] ), MasterObject = tostring( Scheduler.MasterObject ) } )
else
self.PersistentSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
end
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
self.Schedule[Scheduler][CallID] = {}
self.Schedule[Scheduler][CallID] = {} -- #SCHEDULEDISPATCHER.ScheduleData
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
self.Schedule[Scheduler][CallID].Start = Start + .1
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + (Start or 0)
self.Schedule[Scheduler][CallID].Start = Start + 0.1
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
self.Schedule[Scheduler][CallID].Stop = Stop
-- 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.
-- 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 call stack has many levels, and the correct semantical function call depends on where in the code AddSchedule was "used".
-- - Using SCHEDULER:New()
-- - Using Schedule:AddSchedule()
-- - Using Fsm:__Func()
-- - Using Class:ScheduleOnce()
-- - Using Class:ScheduleRepeat()
-- - ...
-- So for each of these scheduled call variations, AddSchedule is the workhorse which will schedule the call.
-- But the correct level with the correct semantical function location will differ depending on the above scheduled call invocation forms.
-- That's where the field TraceLevel contains optionally the level in the call stack where the call information is obtained.
-- The TraceLevel field indicates the correct level where the semantical scheduled call was invoked within the source, ensuring that function name, line number and source name are correct.
-- There is one quick ...
-- The FSM class models scheduled calls using the __Func syntax. However, these functions are "tailed".
-- There aren't defined anywhere within the source code, but rather implemented as triggers within the FSM logic,
-- and using the onbefore, onafter, onenter, onleave prefixes. (See the FSM for details).
-- 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.
-- 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, performance overhead is negligible, as scheduled calls don't happen that often.
local Info = {}
if debug then
TraceLevel = TraceLevel or 2
Info = debug.getinfo( TraceLevel, "nlS" )
local name_fsm = debug.getinfo( TraceLevel - 1, "n" ).name -- #string
if name_fsm then
Info.name = name_fsm
end
end
self:T3( self.Schedule[Scheduler][CallID] )
self.Schedule[Scheduler][CallID].CallHandler = function( CallID )
--self:E( CallID )
--- Function passed to the DCS timer.scheduleFunction()
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
local CallID = Params.CallID
local Info = Params.Info or {}
local Source = Info.source or "?"
local Line = Info.currentline or "?"
local Name = Info.name or "?"
local ErrorHandler = function( errmsg )
env.info( "Error in timer function: " .. errmsg )
@@ -97,81 +179,92 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
end
return errmsg
end
local Scheduler = self.ObjectSchedulers[CallID]
-- Get object or persistent scheduler object.
local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER
if not Scheduler then
Scheduler = self.PersistentSchedulers[CallID]
end
--self:T3( { Scheduler = Scheduler } )
-- self:T3( { Scheduler = Scheduler } )
if Scheduler then
local MasterObject = tostring(Scheduler.MasterObject)
local Schedule = self.Schedule[Scheduler][CallID]
--self:T3( { Schedule = Schedule } )
local MasterObject = tostring( Scheduler.MasterObject )
-- Schedule object.
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- self:T3( { Schedule = Schedule } )
local SchedulerObject = Scheduler.MasterObject -- Scheduler.SchedulerObject Now is this the Master or Scheduler object?
local ShowTrace = Scheduler.ShowTrace
local ScheduleFunction = Schedule.Function
local ScheduleArguments = Schedule.Arguments or {}
local Start = Schedule.Start
local Repeat = Schedule.Repeat or 0
local Randomize = Schedule.Randomize or 0
local Stop = Schedule.Stop or 0
local ScheduleID = Schedule.ScheduleID
local Prefix = (Repeat == 0) and "--->" or "+++>"
local SchedulerObject = Scheduler.SchedulerObject
--local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID()
local ScheduleFunction = Schedule.Function
local ScheduleArguments = Schedule.Arguments
local Start = Schedule.Start
local Repeat = Schedule.Repeat or 0
local Randomize = Schedule.Randomize or 0
local Stop = Schedule.Stop or 0
local ScheduleID = Schedule.ScheduleID
local Status, Result
--self:E( { SchedulerObject = SchedulerObject } )
-- self:E( { SchedulerObject = SchedulerObject } )
if SchedulerObject then
local function Timer()
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
if ShowTrace then
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
end
Status, Result = xpcall( Timer, ErrorHandler )
else
local function Timer()
return ScheduleFunction( unpack( ScheduleArguments ) )
if ShowTrace then
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end
return ScheduleFunction( unpack( ScheduleArguments ) )
end
Status, Result = xpcall( Timer, ErrorHandler )
end
local CurrentTime = timer.getTime()
local StartTime = Schedule.StartTime
self:F3( { 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 Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
local ScheduleTime =
CurrentTime +
Repeat +
math.random(
- ( Randomize * Repeat / 2 ),
( Randomize * Repeat / 2 )
) +
0.01
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
-- Debug info.
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 Repeat ~= 0 and ((Stop == 0) or (Stop ~= 0 and CurrentTime <= StartTime + Stop)) then
local ScheduleTime = CurrentTime + Repeat + math.random( -(Randomize * Repeat / 2), (Randomize * Repeat / 2) ) + 0.0001 -- Accuracy
-- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
return ScheduleTime -- returns the next time the function needs to be called.
else
self:Stop( Scheduler, CallID )
end
else
self:Stop( Scheduler, CallID )
end
else
self:E( "Scheduled obsolete call for CallID: " .. CallID )
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end
return nil
end
self:Start( Scheduler, CallID )
self:Start( Scheduler, CallID, Info )
return CallID
end
--- Remove schedule.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
-- @param #table CallID Call ID.
function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
self:F2( { Remove = CallID, Scheduler = Scheduler } )
@@ -181,46 +274,80 @@ function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
end
end
function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
--- Start dispatcher.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
-- @param #table CallID (Optional) Call ID.
-- @param #string Info (Optional) Debug info.
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
self:F2( { Start = CallID, Scheduler = Scheduler } )
if CallID then
local Schedule = self.Schedule[Scheduler]
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- Only start when there is no ScheduleID defined!
-- This prevents to "Start" the scheduler twice with the same CallID...
if not Schedule[CallID].ScheduleID then
Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started.
Schedule[CallID].ScheduleID = timer.scheduleFunction(
Schedule[CallID].CallHandler,
CallID,
timer.getTime() + Schedule[CallID].Start
)
if not Schedule.ScheduleID then
-- Current time in seconds.
local Tnow = timer.getTime()
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
-- 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 )
self:T( string.format( "Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
end
else
-- Recursive.
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Start( Scheduler, CallID ) -- Recursive
self:Start( Scheduler, CallID, Info ) -- Recursive
end
end
end
--- Stop dispatcher.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
-- @param #table CallID Call ID.
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
self:F2( { Stop = CallID, Scheduler = Scheduler } )
if CallID then
local Schedule = self.Schedule[Scheduler]
-- Only stop when there is a ScheduleID defined for the CallID.
-- So, when the scheduler was stopped before, do nothing.
if Schedule[CallID].ScheduleID then
timer.removeFunction( Schedule[CallID].ScheduleID )
Schedule[CallID].ScheduleID = nil
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
if Schedule.ScheduleID then
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
timer.removeFunction( Schedule.ScheduleID )
Schedule.ScheduleID = nil
else
self:T( string.format( "Error no ScheduleID for CallID=%s", tostring( CallID ) ) )
end
else
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Stop( Scheduler, CallID ) -- Recursive
end
end
end
--- Clear all schedules by stopping all dispatchers.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
function SCHEDULEDISPATCHER:Clear( Scheduler )
self:F2( { Scheduler = Scheduler } )
@@ -229,5 +356,19 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
end
end
--- Show tracing info.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
self:F2( { Scheduler = Scheduler } )
Scheduler.ShowTrace = true
end
--- No tracing info.
-- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
function SCHEDULEDISPATCHER:NoTrace( Scheduler )
self:F2( { Scheduler = Scheduler } )
Scheduler.ShowTrace = false
end

View File

@@ -1,41 +1,41 @@
--- **Core** - Prepares and handles the execution of functions over scheduled time (intervals).
--
-- ===
--
--
-- ## Features:
--
--
-- * Schedule functions over time,
-- * optionally in an optional specified time interval,
-- * optionally **repeating** with a specified time repeat interval,
-- * optionally **randomizing** with a specified time interval randomization factor,
-- * optionally **stop** the repeating after a specified time interval.
-- * optionally in an optional specified time interval,
-- * optionally **repeating** with a specified time repeat interval,
-- * optionally **randomizing** with a specified time interval randomization factor,
-- * optionally **stop** the repeating after a specified time interval.
--
-- ===
--
--
-- # 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)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ===
--
-- # YouTube Channel
--
-- ### [SCHEDULER YouTube Channel (none)]()
--
--
-- ===
--
-- ### Contributions:
--
-- # YouTube Channel
--
-- ### [SCHEDULER YouTube Channel (none)]()
--
-- ===
--
-- ### Contributions:
--
-- * FlightControl : Concept & Testing
--
-- ### Authors:
--
--
-- ### Authors:
--
-- * FlightControl : Design & Programming
--
--
-- ===
--
-- @module Core.Scheduler
@@ -43,80 +43,81 @@
--- The SCHEDULER class
-- @type SCHEDULER
-- @field #number ScheduleID the ID of the scheduler.
-- @field #table Schedules Table of schedules.
-- @field #table MasterObject Master object.
-- @field #boolean ShowTrace Trace info if true.
-- @extends Core.Base#BASE
--- 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**.
-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called.
-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(),
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
--
-- ## SCHEDULER constructor
--
--
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
--
--
-- The @{#SCHEDULER.New}() method returns 2 variables:
--
--
-- 1. The SCHEDULER object reference.
-- 2. The first schedule planned in the SCHEDULER object.
--
--
-- To clarify the different appliances, lets have a look at the following examples:
--
--
-- ### Construct a SCHEDULER object without a persistent schedule.
--
--
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
--
-- SchedulerObject = SCHEDULER:New()
-- SchedulerID = SchedulerObject:Schedule( nil, ScheduleFunction, {} )
--
-- The above example creates a new SchedulerObject, but does not schedule anything.
-- A separate schedule is created by using the SchedulerObject using the method :Schedule..., which returns a ScheduleID
--
--
-- MasterObject = SCHEDULER:New()
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
--
-- The above example creates a new MasterObject, but does not schedule anything.
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
--
-- ### 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" )
-- SchedulerObject = SCHEDULER:New( ZoneObject )
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- MasterObject = SCHEDULER:New( ZoneObject )
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- ...
-- ZoneObject = nil
-- garbagecollect()
--
-- The above example creates a new SchedulerObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
-- A separate schedule is created by using the SchedulerObject using the method :Schedule()..., which returns a ScheduleID
--
-- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
-- As a result, the ScheduleObject will cancel any planned schedule.
--
-- As a result, the MasterObject will cancel any planned schedule.
--
-- ### Construct a SCHEDULER object with a persistent schedule.
--
--
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
--
-- SchedulerObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
--
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
--
--
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
--
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
--
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
--
--
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- MasterObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- ...
-- ZoneObject = nil
-- garbagecollect()
--
-- The above example creates a new SchedulerObject, and schedules a method call (ScheduleFunction),
--
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
-- Both a ScheduleObject and a SchedulerID variable are returned.
-- Both a MasterObject and a SchedulerID variable are returned.
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
-- As a result, the ScheduleObject will cancel the planned schedule.
--
-- As a result, the MasterObject will cancel the planned schedule.
--
-- ## SCHEDULER timer stopping and (re-)starting.
--
-- The SCHEDULER can be stopped and restarted with the following methods:
@@ -125,79 +126,81 @@
-- * @{#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
-- MasterObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
-- ...
-- SchedulerObject:Stop( SchedulerID )
-- MasterObject:Stop( SchedulerID )
-- ...
-- SchedulerObject:Start( SchedulerID )
--
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
--
-- MasterObject:Start( SchedulerID )
--
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
--
-- ## Create a new schedule
--
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
--
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
-- This method is used by the :New() constructor when a new schedule is planned.
--
--
-- Consider the following code fragment of the SCHEDULER object creation.
--
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject = SCHEDULER:New( ZoneObject )
--
-- Several parameters can be specified that influence the behaviour of a Schedule.
--
-- MasterObject = SCHEDULER:New( ZoneObject )
--
-- Several parameters can be specified that influence the behavior of a Schedule.
--
-- ### A single schedule, immediately executed
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
--
--
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milliseconds ...
--
-- ### A single schedule, planned over time
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
--
--
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
--
--
-- ### A schedule with a repeating time interval, planned over time
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
--
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- and repeating 60 every seconds ...
--
--
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
--
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- and repeating 60 seconds, with a 50% time interval randomization ...
-- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- which is in this example between **30** and **90** seconds.
--
--
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
--
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- The schedule will repeat every 60 seconds.
-- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- which is in this example between **30** and **90** seconds.
-- The schedule will stop after **300** seconds.
--
--
-- @field #SCHEDULER
SCHEDULER = {
ClassName = "SCHEDULER",
Schedules = {},
MasterObject = nil,
ShowTrace = nil,
}
--- SCHEDULER constructor.
-- @param #SCHEDULER self
-- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
-- @param #table MasterObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
@@ -205,110 +208,110 @@ SCHEDULER = {
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
-- @return #SCHEDULER self.
-- @return #number The ScheduleID of the planned schedule.
function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
-- @return #table The ScheduleID of the planned schedule.
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
local ScheduleID = nil
self.MasterObject = SchedulerObject
self.MasterObject = MasterObject
self.ShowTrace = false
if SchedulerFunction then
ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
end
return self, ScheduleID
end
--function SCHEDULER:_Destructor()
-- --self:E("_Destructor")
--
-- _SCHEDULEDISPATCHER:RemoveSchedule( self.CallID )
--end
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
-- @param #SCHEDULER self
-- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
-- @param #table MasterObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @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 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 Stop Specifies the amount of seconds when the scheduler will be stopped.
-- @return #number The ScheduleID of the planned schedule.
function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
-- @param #number Stop Time interval in seconds after which the scheduler will be stopped.
-- @param #number TraceLevel Trace level [0,3]. Default 3.
-- @param Core.Fsm#FSM Fsm Finite state model.
-- @return #table The ScheduleID of the planned schedule.
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
self:T3( { SchedulerArguments } )
-- Debug info.
local ObjectName = "-"
if SchedulerObject and SchedulerObject.ClassName and SchedulerObject.ClassID then
ObjectName = SchedulerObject.ClassName .. SchedulerObject.ClassID
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
end
self:F3( { "Schedule :", ObjectName, tostring( SchedulerObject ), Start, Repeat, RandomizeFactor, Stop } )
self.SchedulerObject = SchedulerObject
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
self,
SchedulerFunction,
SchedulerArguments,
Start,
Repeat,
RandomizeFactor,
Stop
)
self.Schedules[#self.Schedules+1] = ScheduleID
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
-- Set master object.
self.MasterObject = MasterObject
-- Add schedule.
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self,
SchedulerFunction,
SchedulerArguments,
Start,
Repeat,
RandomizeFactor,
Stop,
TraceLevel or 3,
Fsm
)
self.Schedules[#self.Schedules + 1] = ScheduleID
return ScheduleID
end
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
-- @param #SCHEDULER self
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
function SCHEDULER:Start( ScheduleID )
self:F3( { ScheduleID } )
self:T( string.format( "Starting scheduler ID=%s", tostring( ScheduleID ) ) )
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
end
--- Stops the schedules or a specific schedule if a valid ScheduleID is provided.
-- @param #SCHEDULER self
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
function SCHEDULER:Stop( ScheduleID )
self:F3( { ScheduleID } )
self:T( string.format( "Stopping scheduler ID=%s", tostring( ScheduleID ) ) )
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
end
--- Removes a specific schedule if a valid ScheduleID is provided.
-- @param #SCHEDULER self
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
function SCHEDULER:Remove( ScheduleID )
self:F3( { ScheduleID } )
_SCHEDULEDISPATCHER:Remove( self, ScheduleID )
self:T( string.format( "Removing scheduler ID=%s", tostring( ScheduleID ) ) )
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
end
--- Clears all pending schedules.
-- @param #SCHEDULER self
function SCHEDULER:Clear()
self:F3( )
self:F3()
self:T( string.format( "Clearing scheduler" ) )
_SCHEDULEDISPATCHER:Clear( self )
end
--- Show tracing for this scheduler.
-- @param #SCHEDULER self
function SCHEDULER:ShowTrace()
_SCHEDULEDISPATCHER:ShowTrace( self )
end
--- No tracing for this scheduler.
-- @param #SCHEDULER self
function SCHEDULER:NoTrace()
_SCHEDULEDISPATCHER:NoTrace( self )
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,260 +1,497 @@
--- **Core** - Spawn new statics in your running missions.
--- **Core** - Spawn statics.
--
-- ===
--
-- ## Features:
--
-- * Spawn new statics from a static already defined using the mission editor.
-- * Spawn new statics from a static already defined in the mission editor.
-- * Spawn new statics from a given template.
-- * Spawn new statics from a given type.
-- * Spawn with a custom heading and location.
-- * Spawn within a zone.
-- * Spawn statics linked to units, .e.g on aircraft carriers.
--
-- ===
--
-- # Demo Missions
--
-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics)
--
-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
-- ## [SPAWNSTATIC Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ===
--
-- # YouTube Channel
--
-- ### [SPAWNSTATIC YouTube Channel]()
-- ## [SPAWNSTATIC YouTube Channel]() [No videos yet!]
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions:
-- ### Contributions: **funkyfranky**
--
-- ===
--
-- @module Core.SpawnStatic
-- @image Core_Spawnstatic.JPG
--- @type SPAWNSTATIC
-- @field #string SpawnTemplatePrefix Name of the template group.
-- @field #number CountryID Country ID.
-- @field #number CoalitionID Coalition ID.
-- @field #number CategoryID Category ID.
-- @field #number SpawnIndex Running number increased with each new Spawn.
-- @field Wrapper.Unit#UNIT InitLinkUnit The unit the static is linked to.
-- @field #number InitOffsetX Link offset X coordinate.
-- @field #number InitOffsetY Link offset Y coordinate.
-- @field #number InitOffsetAngle Link offset angle in degrees.
-- @field #number InitStaticHeading Heading of the static.
-- @field #string InitStaticLivery Livery for aircraft.
-- @field #string InitStaticShape Shape of the static.
-- @field #string InitStaticType Type of the static.
-- @field #string InitStaticCategory Categrory of the static.
-- @field #string InitStaticName Name of the static.
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
-- @field #boolean InitStaticDead Set static to be dead if true.
-- @field #boolean InitStaticCargo If true, static can act as cargo.
-- @field #number InitStaticCargoMass Mass of cargo in kg.
-- @extends Core.Base#BASE
--- Allows to spawn dynamically new @{Static}s.
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME),
-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy"
-- these properties to create a new static object and place it at the desired coordinate.
--- Allows to spawn dynamically new @{Static}s into your mission.
--
-- New spawned @{Static}s get **the same name** as the name of the template Static,
-- or gets the given name when a new name is provided at the Spawn method.
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc),
-- and "copy" these properties to create a new static object and place it at the desired coordinate.
--
-- New spawned @{Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
-- By default, spawned @{Static}s will follow a naming convention at run-time:
--
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**,
-- and _nnn_ is a **counter from 0 to 99999**.
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, and _nnn_ is a **counter from 0 to 99999**.
--
--
-- ## SPAWNSTATIC construction methods
-- # SPAWNSTATIC Constructors
--
-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}():
-- Firstly, we need to create a SPAWNSTATIC object that will be used to spawn new statics into the mission. There are three ways to do this.
--
-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
-- ## Use another Static
--
-- A new SPAWNSTATIC object can be created using another static by the @{#SPAWNSTATIC.NewFromStatic}() function. All parameters such as position, heading, country will be initialized
-- from the static.
--
-- ## From a Template
--
-- A SPAWNSTATIC object can also be created from a template table using the @{#SPAWNSTATIC.NewFromTemplate}(SpawnTemplate, CountryID) function. All parameters are taken from the template.
--
-- ## From a Type
--
-- A very basic method is to create a SPAWNSTATIC object by just giving the type of the static. All parameters must be initialized from the InitXYZ functions described below. Otherwise default values
-- are used. For example, if no spawn coordinate is given, the static will be created at the origin of the map.
--
-- # Setting Parameters
--
-- Parameters such as the spawn position, heading, country etc. can be set via :Init*XYZ* functions. Note that these functions must be given before the actual spawn command!
--
-- * @{#SPAWNSTATIC.InitCoordinate}(Coordinate) Sets the coordinate where the static is spawned. Statics are always spawnd on the ground.
-- * @{#SPAWNSTATIC.InitHeading}(Heading) sets the orientation of the static.
-- * @{#SPAWNSTATIC.InitLivery}(LiveryName) sets the livery of the static. Not all statics support this.
-- * @{#SPAWNSTATIC.InitType}(StaticType) sets the type of the static.
-- * @{#SPAWNSTATIC.InitShape}(StaticType) sets the shape of the static. Not all statics have this parameter.
-- * @{#SPAWNSTATIC.InitNamePrefix}(NamePrefix) sets the name prefix of the spawned statics.
-- * @{#SPAWNSTATIC.InitCountry}(CountryID) sets the country and therefore the coalition of the spawned statics.
-- * @{#SPAWNSTATIC.InitLinkToUnit}(Unit, OffsetX, OffsetY, OffsetAngle) links the static to a unit, e.g. to an aircraft carrier.
--
-- ## **Spawn** methods
-- # Spawning the Statics
--
-- Groups can be spawned at different times and methods:
-- Once the SPAWNSTATIC object is created and parameters are initialized, the spawn command can be given. There are different methods where some can be used to directly set parameters
-- such as position and heading.
--
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate.
-- (The group will be spawned at land height ).
-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}.
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Zone}. Optionally, heading and name can be given. The name **must be unique**!
--
-- @field #SPAWNSTATIC SPAWNSTATIC
--
SPAWNSTATIC = {
ClassName = "SPAWNSTATIC",
ClassName = "SPAWNSTATIC",
SpawnIndex = 0,
}
--- Static template table data.
-- @type SPAWNSTATIC.TemplateData
-- @field #string name Name of the static.
-- @field #string type Type of the static.
-- @field #string category Category of the static.
-- @field #number x X-coordinate of the static.
-- @field #number y Y-coordinate of teh static.
-- @field #number heading Heading in rad.
-- @field #boolean dead Static is dead if true.
-- @field #string livery_id Livery name.
-- @field #number unitId Unit ID.
-- @field #number groupId Group ID.
-- @field #table offsets Offset parameters when linked to a unit.
-- @field #number mass Cargo mass in kg.
-- @field #boolean canCargo Static can be a cargo.
--- @type SPAWNSTATIC.SpawnZoneTable
-- @list <Core.Zone#ZONE_BASE> SpawnZone
--- Creates the main object to spawn a @{Static} defined in the ME.
--- Creates the main object to spawn a @{Static} defined in the mission editor (ME).
-- @param #SPAWNSTATIC 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 DCS#country.id SpawnCountryID The ID of the country.
-- @param DCS#coalition.side SpawnCoalitionID The ID of the coalition.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID, SpawnCoalitionID )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTemplatePrefix } )
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( SpawnTemplatePrefix )
if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.CountryID = SpawnCountryID or CountryID
self.CategoryID = CategoryID
self.CoalitionID = SpawnCoalitionID or CoalitionID
self.SpawnIndex = 0
else
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
end
-- @param #string SpawnTemplateName Name of the static object in the ME. Each new static will have the name starting with this prefix.
-- @param DCS#country.id SpawnCountryID (Optional) The ID of the country.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
self:SetEventPriority( 5 )
return self
end
--- Creates the main object to spawn a @{Static} based on a type name.
-- @param #SPAWNSTATIC self
-- @param #string SpawnTypeName is the name of the type.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, SpawnCountryID, SpawnCoalitionID )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTypeName } )
self.SpawnTypeName = SpawnTypeName
self.CountryID = SpawnCountryID
self.CoalitionID = SpawnCoalitionID
self.SpawnIndex = 0
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplateName
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
self.CountryID = SpawnCountryID or CountryID
self.CategoryID = CategoryID
self.CoalitionID = CoalitionID
self.SpawnIndex = 0
else
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
end
self:SetEventPriority( 5 )
return self
end
--- Creates a new @{Static} at the original position.
--- Creates the main object to spawn a @{Static} given a template table.
-- @param #SPAWNSTATIC self
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3
self:F( { Heading, NewName } )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
-- @param #table SpawnTemplate Template used for spawning.
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
self.SpawnIndex = self.SpawnIndex + 1
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
return _DATABASE:FindStatic(Static:getName())
end
self.TemplateStaticUnit = UTILS.DeepCopy(SpawnTemplate)
self.SpawnTemplatePrefix = SpawnTemplate.name
self.CountryID = CountryID or country.id.USA
return nil
return self
end
--- Creates the main object to spawn a @{Static} from a given type.
-- NOTE that you have to init many other parameters as spawn coordinate etc.
-- @param #SPAWNSTATIC self
-- @param #string StaticType Type of the static.
-- @param #string StaticCategory Category of the static, e.g. "Planes".
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:NewFromType(StaticType, StaticCategory, CountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self.InitStaticType=StaticType
self.InitStaticCategory=StaticCategory
self.CountryID=CountryID or country.id.USA
self.SpawnTemplatePrefix=self.InitStaticType
self.InitStaticCoordinate=COORDINATE:New(0, 0, 0)
self.InitStaticHeading=0
return self
end
--- Initialize heading of the spawned static.
-- @param #SPAWNSTATIC self
-- @param Core.Point#COORDINATE Coordinate Position where the static is spawned.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCoordinate(Coordinate)
self.InitStaticCoordinate=Coordinate
return self
end
--- Initialize heading of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #number Heading The heading in degrees.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitHeading(Heading)
self.InitStaticHeading=Heading
return self
end
--- Initialize livery of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #string LiveryName Name of the livery to use.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitLivery(LiveryName)
self.InitStaticLivery=LiveryName
return self
end
--- Initialize type of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #string StaticType Type of the static, e.g. "FA-18C_hornet".
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitType(StaticType)
self.InitStaticType=StaticType
return self
end
--- Initialize shape of the spawned static. Required by some but not all statics.
-- @param #SPAWNSTATIC self
-- @param #string StaticShape Shape of the static, e.g. "carrier_tech_USA".
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitShape(StaticShape)
self.InitStaticShape=StaticShape
return self
end
--- Initialize parameters for spawning FARPs.
-- @param #SPAWNSTATIC self
-- @param #number CallsignID Callsign ID. Default 1 (="London").
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
-- @param #number Modulation Modulation 0=AM, 1=FM.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation)
self.InitFarp=true
self.InitFarpCallsignID=CallsignID or 1
self.InitFarpFreq=Frequency or 127.5
self.InitFarpModu=Modulation or 0
return self
end
--- Initialize cargo mass.
-- @param #SPAWNSTATIC self
-- @param #number Mass Mass of the cargo in kg.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargoMass(Mass)
self.InitStaticCargoMass=Mass
return self
end
--- Initialize as cargo.
-- @param #SPAWNSTATIC self
-- @param #boolean IsCargo If true, this static can act as cargo.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC: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
end
--- Initialize country of the spawned static. This determines the category.
-- @param #SPAWNSTATIC self
-- @param #string CountryID The country ID, e.g. country.id.USA.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCountry(CountryID)
self.CountryID=CountryID
return self
end
--- Initialize name prefix statics get. This will be appended by "#0001", "#0002" etc.
-- @param #SPAWNSTATIC self
-- @param #string NamePrefix Name prefix of statics spawned. Will append #0001, etc to the name.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitNamePrefix(NamePrefix)
self.SpawnTemplatePrefix=NamePrefix
return self
end
--- Init link to a unit.
-- @param #SPAWNSTATIC self
-- @param Wrapper.Unit#UNIT Unit The unit to which the static is linked.
-- @param #number OffsetX Offset in X.
-- @param #number OffsetY Offset in Y.
-- @param #number OffsetAngle Offset angle in degrees.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
self.InitLinkUnit=Unit
self.InitOffsetX=OffsetX or 0
self.InitOffsetY=OffsetY or 0
self.InitOffsetAngle=OffsetAngle or 0
return self
end
--- Spawn a new STATIC object.
-- @param #SPAWNSTATIC self
-- @param #number Heading (Optional) The heading of the static, which is a number in degrees from 0 to 360. Default is the heading of the template.
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:Spawn(Heading, NewName)
if Heading then
self.InitStaticHeading=Heading
end
if NewName then
self.InitStaticName=NewName
end
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
end
--- Creates a new @{Static} from a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
self:F( { PointVec2, Heading, NewName } )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticUnitTemplate.x = PointVec2.x
StaticUnitTemplate.y = PointVec2.z
StaticTemplate.route = nil
StaticTemplate.groupId = nil
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticUnitTemplate.name = StaticTemplate.name
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
self:F({StaticTemplate = StaticTemplate})
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, NewName)
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
self.SpawnIndex = self.SpawnIndex + 1
return _DATABASE:FindStatic(Static:getName())
end
local vec2={x=PointVec2:GetX(), y=PointVec2:GetY()}
local Coordinate=COORDINATE:NewFromVec2(vec2)
return nil
return self:SpawnFromCoordinate(Coordinate, Heading, NewName)
end
--- Respawns the original @{Static}.
--- Creates a new @{Static} from a COORDINATE.
-- @param #SPAWNSTATIC self
-- @return #SPAWNSTATIC
function SPAWNSTATIC:ReSpawn()
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
-- @param #number Heading (Optional) Heading The heading of the static in degrees. Default is 0 degrees.
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The spawned STATIC object.
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName)
local StaticUnitTemplate = StaticTemplate.units[1]
StaticTemplate.route = nil
StaticTemplate.groupId = nil
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
return _DATABASE:FindStatic(Static:getName())
-- Set up coordinate.
self.InitStaticCoordinate=Coordinate
if Heading then
self.InitStaticHeading=Heading
end
return nil
end
--- Creates the original @{Static} at a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#COORDINATE Coordinate The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading )
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
if StaticTemplate then
local StaticUnitTemplate = StaticTemplate.units[1]
StaticUnitTemplate.x = Coordinate.x
StaticUnitTemplate.y = Coordinate.z
StaticUnitTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
return _DATABASE:FindStatic(Static:getName())
if NewName then
self.InitStaticName=NewName
end
return nil
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
end
--- Creates a new @{Static} from a @{Zone}.
-- @param #SPAWNSTATIC self
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
self:F( { Zone, Heading, NewName } )
-- @param #number Heading (Optional)The heading of the static in degrees. Default is the heading of the template.
-- @param #string NewName (Optional) The name of the new static.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
-- Spawn the new static at the center of the zone.
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
return Static
end
--- Spawns a new static using a given template. Additionally, the country ID needs to be specified, which also determines the coalition of the spawned static.
-- @param #SPAWNSTATIC self
-- @param #SPAWNSTATIC.TemplateData Template Spawn unit template.
-- @param #number CountryID The country ID.
-- @return Wrapper.Static#STATIC The static spawned.
function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template=Template or {}
local CountryID=CountryID or self.CountryID
if self.InitStaticType then
Template.type=self.InitStaticType
end
if self.InitStaticCategory then
Template.category=self.InitStaticCategory
end
if self.InitStaticCoordinate then
Template.x = self.InitStaticCoordinate.x
Template.y = self.InitStaticCoordinate.z
Template.alt = self.InitStaticCoordinate.y
end
if self.InitStaticHeading then
Template.heading = math.rad(self.InitStaticHeading)
end
if self.InitStaticShape then
Template.shape_name=self.InitStaticShape
end
if self.InitStaticLivery then
Template.livery_id=self.InitStaticLivery
end
if self.InitStaticDead~=nil then
Template.dead=self.InitStaticDead
end
if self.InitStaticCargo~=nil then
Template.canCargo=self.InitStaticCargo
end
if self.InitStaticCargoMass~=nil then
Template.mass=self.InitStaticCargoMass
end
if self.InitLinkUnit then
Template.linkUnit=self.InitLinkUnit:GetID()
Template.linkOffset=true
Template.offsets={}
Template.offsets.y=self.InitOffsetY
Template.offsets.x=self.InitOffsetX
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
end
if self.InitFarp then
Template.heliport_callsign_id = self.InitFarpCallsignID
Template.heliport_frequency = self.InitFarpFreq
Template.heliport_modulation = self.InitFarpModu
Template.unitId=nil
end
-- Increase spawn index counter.
self.SpawnIndex = self.SpawnIndex + 1
-- Name of the spawned static.
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
-- Add and register the new static.
local mystatic=_DATABASE:AddStatic(Template.name)
-- Debug output.
self:T(Template)
-- Add static to the game.
local Static=nil
if self.InitFarp then
local TemplateGroup={}
TemplateGroup.units={}
TemplateGroup.units[1]=Template
TemplateGroup.visible=true
TemplateGroup.hidden=false
TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
self:T("Spawning FARP")
self:T({Template=Template})
self:T({TemplateGroup=TemplateGroup})
-- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
else
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template)
end
return mystatic
end

View File

@@ -126,6 +126,39 @@ do
-- @param Wrapper.Positionable#POSITIONABLE Target
-- @param #number LaserCode Laser code.
-- @param #number Duration Duration of lasing in seconds.
self:AddTransition( "Off", "LaseOnCoordinate", "On" )
--- LaseOnCoordinate Handler OnBefore for SPOT.
-- @function [parent=#SPOT] OnBeforeLaseOnCoordinate
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- LaseOnCoordinate Handler OnAfter for SPOT.
-- @function [parent=#SPOT] OnAfterLaseOnCoordinate
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
--- LaseOnCoordinate Trigger for SPOT.
-- @function [parent=#SPOT] LaseOnCoordinate
-- @param #SPOT self
-- @param Core.Point#COORDINATE Coordinate The coordinate to lase.
-- @param #number LaserCode Laser code.
-- @param #number Duration Duration of lasing in seconds.
--- LaseOn Asynchronous Trigger for SPOT
-- @function [parent=#SPOT] __LaseOn
-- @param #SPOT self
-- @param #number Delay
-- @param Wrapper.Positionable#POSITIONABLE Target
-- @param #number LaserCode Laser code.
-- @param #number Duration Duration of lasing in seconds.
self:AddTransition( "On", "Lasing", "On" )
@@ -194,7 +227,8 @@ do
return self
end
--- @param #SPOT self
--- On after LaseOn event. Activates the laser spot.
-- @param #SPOT self
-- @param From
-- @param Event
-- @param To
@@ -226,6 +260,40 @@ do
self:__Lasing( -1 )
end
--- On after LaseOnCoordinate event. Activates the laser spot.
-- @param #SPOT self
-- @param From
-- @param Event
-- @param To
-- @param Core.Point#COORDINATE Coordinate The coordinate at which the laser is pointing.
-- @param #number LaserCode Laser code.
-- @param #number Duration Duration of lasing in seconds.
function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration)
self:F( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } )
local function StopLase( self )
self:LaseOff()
end
self.Target = nil
self.TargetCoord=Coordinate
self.LaserCode = LaserCode
self.Lasing = true
local RecceDcsUnit = self.Recce:GetDCSObject()
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 1, z = 0 }, Coordinate:GetVec3() )
self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 1, z = 0 }, Coordinate:GetVec3(), LaserCode )
if Duration then
self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration )
end
self:__Lasing(-1)
end
--- @param #SPOT self
-- @param Core.Event#EVENTDATA EventData
@@ -246,10 +314,20 @@ do
-- @param To
function SPOT:onafterLasing( From, Event, To )
if self.Target:IsAlive() then
if self.Target and self.Target:IsAlive() then
self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() )
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
self:__Lasing( -0.2 )
elseif self.TargetCoord then
-- Wiggle the IR spot a bit.
local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100, y=self.TargetCoord.y+math.random(-100,100)/100, z=self.TargetCoord.z} --#DCS.Vec3
local lsvec3={x=self.TargetCoord.x, y=self.TargetCoord.y, z=self.TargetCoord.z} --#DCS.Vec3
self.SpotIR:setPoint(irvec3)
self.SpotLaser:setPoint(lsvec3)
self:__Lasing(-0.25)
else
self:F( { "Target is not alive", self.Target:IsAlive() } )
end
@@ -263,7 +341,7 @@ do
-- @return #SPOT
function SPOT:onafterLaseOff( From, Event, To )
self:F( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
self.Lasing = false

View File

@@ -0,0 +1,292 @@
--- **Core** - Timer scheduler.
--
-- **Main Features:**
--
-- * Delay function calls
-- * Easy set up and little overhead
-- * Set start, stop and time interval
-- * Define max function calls
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Core.Timer
-- @image Core_Scheduler.JPG
--- TIMER class.
-- @type TIMER
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #number tid Timer ID returned by the DCS API function.
-- @field #number uid Unique ID of the timer.
-- @field #function func Timer function.
-- @field #table para Parameters passed to the timer function.
-- @field #number Tstart Relative start time in seconds.
-- @field #number Tstop Relative stop time in seconds.
-- @field #number dT Time interval between function calls in seconds.
-- @field #number ncalls Counter of function calls.
-- @field #number ncallsMax Max number of function calls. If reached, timer is stopped.
-- @field #boolean isrunning If `true`, timer is running. Else it was not started yet or was stopped.
-- @extends Core.Base#BASE
--- *Better three hours too soon than a minute too late.* - William Shakespeare
--
-- ===
--
-- ![Banner Image](..\Presentations\Timer\TIMER_Main.jpg)
--
-- # The TIMER Concept
--
-- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
--
-- It provides an easy interface to the DCS [timer.scheduleFunction](https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction).
--
-- # Construction
--
-- A new TIMER is created by the @{#TIMER.New}(*func*, *...*) function
--
-- local mytimer=TIMER:New(myfunction, a, b)
--
-- The first parameter *func* is the function that is called followed by the necessary comma separeted parameters that are passed to that function.
--
-- ## Starting the Timer
--
-- The timer is started by the @{#TIMER.Start}(*Tstart*, *dT*, *Duration*) function
--
-- mytimer:Start(5, 1, 20)
--
-- where
--
-- * *Tstart* is the relative start time in seconds. In the example, the first function call happens after 5 sec.
-- * *dT* is the time interval between function calls in seconds. Above, the function is called once per second.
-- * *Duration* is the duration in seconds after which the timer is stopped. This is relative to the start time. Here, the timer will run for 20 seconds.
--
-- Note that
--
-- * if *Tstart* is not specified (*nil*), the first function call happens immediately, i.e. after one millisecond.
-- * if *dT* is not specified (*nil*), the function is called only once.
-- * if *Duration* is not specified (*nil*), the timer runs forever or until stopped manually or until the max function calls are reached (see below).
--
-- For example,
--
-- mytimer:Start(3) -- Will call the function once after 3 seconds.
-- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternity.
-- mytimer:Start(nil, 2.0, 20) -- Will call right now and then every 2.0 sec for 20 sec.
-- mytimer:Start(1.0, nil, 10) -- Does not make sense as the function is only called once anyway.
--
-- ## Stopping the Timer
--
-- The timer can be stopped manually by the @{#TIMER.Stop}(*Delay*) function
--
-- mytimer:Stop()
--
-- If the optional paramter *Delay* is specified, the timer is stopped after *delay* seconds.
--
-- ## Limit Function Calls
--
-- The timer can be stopped after a certain amount of function calles with the @{#TIMER.SetMaxFunctionCalls}(*Nmax*) function
--
-- mytimer:SetMaxFunctionCalls(20)
--
-- where *Nmax* is the number of calls after which the timer is stopped, here 20.
--
-- For example,
--
-- mytimer:SetMaxFunctionCalls(66):Start(1.0, 0.1)
--
-- will start the timer after one second and call the function every 0.1 seconds. Once the function has been called 66 times, the timer is stopped.
--
--
-- @field #TIMER
TIMER = {
ClassName = "TIMER",
lid = nil,
}
--- Timer ID.
_TIMERID=0
--- Timer data base.
--_TIMERDB={}
--- TIMER class version.
-- @field #string version
TIMER.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot.
-- TODO: Write docs.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new TIMER object.
-- @param #TIMER self
-- @param #function Function The function to call.
-- @param ... Parameters passed to the function if any.
-- @return #TIMER self
function TIMER:New(Function, ...)
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) --#TIMER
-- Function to call.
self.func=Function
-- Function arguments.
self.para=arg or {}
-- Number of function calls.
self.ncalls=0
-- Not running yet.
self.isrunning=false
-- Increase counter
_TIMERID=_TIMERID+1
-- Set UID.
self.uid=_TIMERID
-- Log id.
self.lid=string.format("TIMER UID=%d | ", self.uid)
-- Add to DB.
--_TIMERDB[self.uid]=self
return self
end
--- Create a new TIMER object.
-- @param #TIMER self
-- @param #number Tstart Relative start time in seconds.
-- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once.
-- @param #number Duration Time in seconds for how long the timer is running. If not specified `nil`, the timer runs forever or until stopped manually by the `TIMER:Stop()` function.
-- @return #TIMER self
function TIMER:Start(Tstart, dT, Duration)
-- Current time.
local Tnow=timer.getTime()
-- Start time in sec.
self.Tstart=Tstart and Tnow+Tstart or Tnow+0.001 -- one millisecond delay if Tstart=nil
-- Set time interval.
self.dT=dT
-- Stop time.
if Duration then
self.Tstop=self.Tstart+Duration
end
-- Call DCS timer function.
self.tid=timer.scheduleFunction(self._Function, self, self.Tstart)
-- Set log id.
self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid)
-- Is now running.
self.isrunning=true
-- Debug info.
self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop)))
return self
end
--- Stop the timer by removing the timer function.
-- @param #TIMER self
-- @param #number Delay (Optional) Delay in seconds, before the timer is stopped.
-- @return #TIMER self
function TIMER:Stop(Delay)
if Delay and Delay>0 then
self.Tstop=timer.getTime()+Delay
else
if self.tid then
-- Remove timer function.
self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls))
timer.removeFunction(self.tid)
-- Not running any more.
self.isrunning=false
-- Remove DB entry.
--_TIMERDB[self.uid]=nil
end
end
return self
end
--- Set max number of function calls. When the function has been called this many times, the TIMER is stopped.
-- @param #TIMER self
-- @param #number Nmax Set number of max function calls.
-- @return #TIMER self
function TIMER:SetMaxFunctionCalls(Nmax)
self.ncallsMax=Nmax
return self
end
--- Check if the timer has been started and was not stopped.
-- @param #TIMER self
-- @return #boolean If `true`, the timer is running.
function TIMER:IsRunning()
return self.isrunning
end
--- Call timer function.
-- @param #TIMER self
-- @param #number time DCS model time in seconds.
-- @return #number Time when the function is called again or `nil` if the timer is stopped.
function TIMER:_Function(time)
-- Call function.
self.func(unpack(self.para))
-- Increase number of calls.
self.ncalls=self.ncalls+1
-- Next time.
local Tnext=self.dT and time+self.dT or nil
-- Check if we stop the timer.
local stopme=false
if Tnext==nil then
-- No next time.
self:T(self.lid..string.format("No next time as dT=nil ==> Stopping timer after %d function calls", self.ncalls))
stopme=true
elseif self.Tstop and Tnext>self.Tstop then
-- Stop time passed.
self:T(self.lid..string.format("Stop time passed %.3f > %.3f ==> Stopping timer after %d function calls", Tnext, self.Tstop, self.ncalls))
stopme=true
elseif self.ncallsMax and self.ncalls>=self.ncallsMax then
-- Number of max function calls reached.
self:T(self.lid..string.format("Max function calls Nmax=%d reached ==> Stopping timer after %d function calls", self.ncallsMax, self.ncalls))
stopme=true
end
if stopme then
-- Remove timer function.
self:Stop()
return nil
else
-- Call again in Tnext seconds.
return Tnext
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -19,6 +19,8 @@
do -- UserFlag
--- @type USERFLAG
-- @field #string ClassName Name of the class
-- @field #string UserFlagName Name of the flag.
-- @extends Core.Base#BASE
@@ -30,7 +32,8 @@ do -- UserFlag
--
-- @field #USERFLAG
USERFLAG = {
ClassName = "USERFLAG",
ClassName = "USERFLAG",
UserFlagName = nil,
}
--- USERFLAG Constructor.
@@ -46,18 +49,30 @@ do -- UserFlag
return self
end
--- Get the userflag name.
-- @param #USERFLAG self
-- @return #string Name of the user flag.
function USERFLAG:GetName()
return self.UserFlagName
end
--- Set the userflag to a given Number.
-- @param #USERFLAG self
-- @param #number Number The number value to be checked if it is the same as the userflag.
-- @param #number Delay Delay in seconds, before the flag is set.
-- @return #USERFLAG The userflag instance.
-- @usage
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
-- BlueVictory:Set( 100 ) -- Set the UserFlag VictoryBlue to 100.
--
function USERFLAG:Set( Number ) --R2.3
function USERFLAG:Set( Number, Delay ) --R2.3
trigger.action.setUserFlag( self.UserFlagName, Number )
if Delay and Delay>0 then
self:ScheduleOnce(Delay, USERFLAG.Set, self, Number)
else
--env.info(string.format("Setting flag \"%s\" to %d at T=%.1f", self.UserFlagName, Number, timer.getTime()))
trigger.action.setUserFlag( self.UserFlagName, Number )
end
return self
end
@@ -70,7 +85,7 @@ do -- UserFlag
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
--
function USERFLAG:Get( Number ) --R2.3
function USERFLAG:Get() --R2.3
return trigger.misc.getUserFlag( self.UserFlagName )
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
--- The ZONE_DETECTION class, defined by a zone name, a detection object and a radius.
-- @type ZONE_DETECTION
-- @field DCS#Vec2 Vec2 The current location of the zone.
-- @field DCS#Distance Radius The radius of the zone.
-- @extends #ZONE_BASE
--- The ZONE_DETECTION class defined by a zone name, a location and a radius.
-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.
--
-- ## ZONE_DETECTION constructor
--
-- * @{#ZONE_DETECTION.New}(): Constructor.
--
-- @field #ZONE_DETECTION
ZONE_DETECTION = {
ClassName="ZONE_DETECTION",
}
--- Constructor of @{#ZONE_DETECTION}, taking the zone name, the zone location and a radius.
-- @param #ZONE_DETECTION self
-- @param #string ZoneName Name of the zone.
-- @param Functional.Detection#DETECTION_BASE Detection The detection object defining the locations of the central detections.
-- @param DCS#Distance Radius The radius around the detections defining the combined zone.
-- @return #ZONE_DETECTION self
function ZONE_DETECTION:New( ZoneName, Detection, Radius )
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_DETECTION
self:F( { ZoneName, Detection, Radius } )
self.Detection = Detection
self.Radius = Radius
return self
end
--- Bounds the zone with tires.
-- @param #ZONE_DETECTION self
-- @param #number Points (optional) The amount of points in the circle. Default 360.
-- @param DCS#country.id CountryID The country id of the tire objects, e.g. country.id.USA for blue or country.id.RUSSIA for red.
-- @param #boolean UnBound (Optional) If true the tyres will be destroyed.
-- @return #ZONE_DETECTION self
function ZONE_DETECTION:BoundZone( Points, CountryID, UnBound )
local Point = {}
local Vec2 = self:GetVec2()
Points = Points and Points or 360
local Angle
local RadialBase = math.pi*2
--
for Angle = 0, 360, (360 / Points ) do
local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
local Tire = {
["country"] = CountryName,
["category"] = "Fortifications",
["canCargo"] = false,
["shape_name"] = "H-tyre_B_WF",
["type"] = "Black_Tyre_WF",
--["unitId"] = Angle + 10000,
["y"] = Point.y,
["x"] = Point.x,
["name"] = string.format( "%s-Tire #%0d", self:GetName(), Angle ),
["heading"] = 0,
} -- end of ["group"]
local Group = coalition.addStaticObject( CountryID, Tire )
if UnBound and UnBound == true then
Group:destroy()
end
end
return self
end
--- Smokes the zone boundaries in a color.
-- @param #ZONE_DETECTION self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
-- @param #number Points (optional) The amount of points in the circle.
-- @param #number AddHeight (optional) The height to be added for the smoke.
-- @param #number AddOffSet (optional) The angle to be added for the smoking start position.
-- @return #ZONE_DETECTION self
function ZONE_DETECTION:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
self:F2( SmokeColor )
local Point = {}
local Vec2 = self:GetVec2()
AddHeight = AddHeight or 0
AngleOffset = AngleOffset or 0
Points = Points and Points or 360
local Angle
local RadialBase = math.pi*2
for Angle = 0, 360, 360 / Points do
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
end
return self
end
--- Flares the zone boundaries in a color.
-- @param #ZONE_DETECTION self
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
-- @param #number Points (optional) The amount of points in the circle.
-- @param DCS#Azimuth Azimuth (optional) Azimuth The azimuth of the flare.
-- @param #number AddHeight (optional) The height to be added for the smoke.
-- @return #ZONE_DETECTION self
function ZONE_DETECTION:FlareZone( FlareColor, Points, Azimuth, AddHeight )
self:F2( { FlareColor, Azimuth } )
local Point = {}
local Vec2 = self:GetVec2()
AddHeight = AddHeight or 0
Points = Points and Points or 360
local Angle
local RadialBase = math.pi*2
for Angle = 0, 360, 360 / Points do
local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
end
return self
end
--- Returns the radius around the detected locations defining the combine zone.
-- @param #ZONE_DETECTION self
-- @return DCS#Distance The radius.
function ZONE_DETECTION:GetRadius()
self:F2( self.ZoneName )
self:T2( { self.Radius } )
return self.Radius
end
--- Sets the radius around the detected locations defining the combine zone.
-- @param #ZONE_DETECTION self
-- @param DCS#Distance Radius The radius.
-- @return #ZONE_DETECTION self
function ZONE_DETECTION:SetRadius( Radius )
self:F2( self.ZoneName )
self.Radius = Radius
self:T2( { self.Radius } )
return self.Radius
end
--- Returns if a location is within the zone.
-- @param #ZONE_DETECTION self
-- @param DCS#Vec2 Vec2 The location to test.
-- @return #boolean true if the location is within the zone.
function ZONE_DETECTION:IsVec2InZone( Vec2 )
self:F2( Vec2 )
local Coordinates = self.Detection:GetDetectedItemCoordinates() -- This returns a list of coordinates that define the (central) locations of the detections.
for CoordinateID, Coordinate in pairs( Coordinates ) do
local ZoneVec2 = Coordinate:GetVec2()
if ZoneVec2 then
if (( Vec2.x - ZoneVec2.x )^2 + ( Vec2.y - ZoneVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then
return true
end
end
end
return false
end
--- Returns if a point is within the zone.
-- @param #ZONE_DETECTION self
-- @param DCS#Vec3 Vec3 The point to test.
-- @return #boolean true if the point is within the zone.
function ZONE_DETECTION:IsVec3InZone( Vec3 )
self:F2( Vec3 )
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone
end

View File

@@ -1,6 +1,7 @@
--- DCS API prototypes
-- See [https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation)
-- for further explanation and examples.
--- **DCS API** Prototypes
--
-- See the [Simulator Scripting Engine Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation) on Hoggit for further explanation and examples.
--
-- @module DCS
-- @image MOOSE.JPG
@@ -46,9 +47,13 @@ do -- world
-- @field S_EVENT_PLAYER_COMMENT
-- @field S_EVENT_SHOOTING_START [https://wiki.hoggitworld.com/view/DCS_event_shooting_start](https://wiki.hoggitworld.com/view/DCS_event_shooting_start)
-- @field S_EVENT_SHOOTING_END [https://wiki.hoggitworld.com/view/DCS_event_shooting_end](https://wiki.hoggitworld.com/view/DCS_event_shooting_end)
-- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added)
-- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change)
-- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove)
-- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added) DCS>=2.5.1
-- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change) DCS>=2.5.1
-- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove) DCS>=2.5.1
-- @field S_EVENT_KILL [https://wiki.hoggitworld.com/view/DCS_event_kill](https://wiki.hoggitworld.com/view/DCS_event_kill) DCS>=2.5.6
-- @field S_EVENT_SCORE [https://wiki.hoggitworld.com/view/DCS_event_score](https://wiki.hoggitworld.com/view/DCS_event_score) DCS>=2.5.6
-- @field S_EVENT_UNIT_LOST [https://wiki.hoggitworld.com/view/DCS_event_unit_lost](https://wiki.hoggitworld.com/view/DCS_event_unit_lost) DCS>=2.5.6
-- @field S_EVENT_LANDING_AFTER_EJECTION [https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection](https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection) DCS>=2.5.6
-- @field S_EVENT_MAX
--- The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events.
@@ -90,6 +95,11 @@ do -- world
--- Returns a table of mark panels indexed numerically that are present within the mission. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_getMarkPanels)
-- @function [parent=#world] getMarkPanels
-- @return #table Table of marks.
--- Returns a table of DCS airbase objects.
-- @function [parent=#world] getAirbases
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
-- @return #table Table of DCS airbase objects.
end -- world
@@ -285,6 +295,17 @@ do -- country
-- @field QATAR
-- @field OMAN
-- @field UNITED_ARAB_EMIRATES
-- @field SOUTH_AFRICA
-- @field CUBA
-- @field PORTUGAL
-- @field GDR
-- @field LEBANON
-- @field CJTF_BLUE
-- @field CJTF_RED
-- @field UN_PEACEKEEPERS
-- @field Argentinia
-- @field Cyprus
-- @field Slovenia
country = {} --#country
@@ -325,9 +346,27 @@ end -- coalition
do -- Types
--- @type Desc
-- @field #TypeName typeName type name
-- @field #string displayName localized display name
-- @field #table attributes object type attributes
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
-- @field #number massEmpty Empty mass in kg.
-- @field #number tankerType Type of refueling system: 0=boom, 1=probe.
-- @field #number range Range in km(?).
-- @field #table box Bounding box.
-- @field #number Hmax Max height in meters.
-- @field #number Kmax ?
-- @field #number speedMax10K Max speed in meters/second at 10k altitude.
-- @field #number NyMin ?
-- @field #number NyMax ?
-- @field #number fuelMassMax Max fuel mass in kg.
-- @field #number speedMax10K Max speed in meters/second.
-- @field #number massMax Max mass of unit.
-- @field #number RCS ?
-- @field #number life Life points.
-- @field #number VyMax Max vertical velocity in m/s.
-- @field #number Kab ?
-- @field #table attributes Table of attributes.
-- @field #TypeName typeName Type Name.
-- @field #string displayName Localized display name.
-- @field #number category Unit category.
--- A distance type
-- @type Distance
@@ -337,7 +376,7 @@ do -- Types
--- Time is given in seconds.
-- @type Time
-- @extends #number
-- @extends #number Time in seconds.
--- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time.
-- @type ModelTime
@@ -345,20 +384,20 @@ do -- Types
--- Mission time is a model time plus time of the mission start.
-- @type MissionTime
-- @extends #number
-- @extends #number Time in seconds.
--- Distance is given in meters.
-- @type Distance
-- @extends #number
-- @extends #number Distance in meters.
--- Angle is given in radians.
-- @type Angle
-- @extends #number
-- @extends #number Angle in radians.
--- Azimuth is an angle of rotation around world axis y counter-clockwise.
-- @type Azimuth
-- @extends #number
-- @extends #number Angle in radians.
--- Mass is given in kilograms.
-- @type Mass
@@ -378,15 +417,15 @@ do -- Types
--- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format:
-- @type Position3
-- @field #Vec3 p
-- @field #Vec3 x
-- @field #Vec3 y
-- @field #Vec3 z
-- @field #Vec3 p 3D position vector.
-- @field #Vec3 x Orientation component of vector pointing East.
-- @field #Vec3 y Orientation component of vector pointing up.
-- @field #Vec3 z Orientation component of vector pointing North.
--- 3-dimensional box.
-- @type Box3
-- @field #Vec3 min
-- @field #Vec3 max
-- @field #Vec3 min Min.
-- @field #Vec3 max Max
--- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley".
-- @type TypeName
@@ -427,9 +466,15 @@ do -- Types
-- @type TaskArray
-- @list <#Task>
---
--@type WaypointAir
--@field #boolean lateActivated
--@field #boolean uncontrolled
end --
do -- Object
--- [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object)
@@ -442,8 +487,10 @@ do -- Object
-- @field UNIT
-- @field WEAPON
-- @field STATIC
-- @field SCENERY
-- @field BASE
-- @field SCENERY
-- @field CARGO
--- @type Object.Desc
-- @extends #Desc
@@ -485,7 +532,7 @@ do -- Object
--- Returns object coordinates for current time.
-- @function [parent=#Object] getPoint
-- @param #Object self
-- @return #Vec3
-- @return #Vec3 3D position vector with x,y,z components.
--- Returns object position for current time.
-- @function [parent=#Object] getPosition
@@ -495,7 +542,7 @@ do -- Object
--- Returns the unit's velocity vector.
-- @function [parent=#Object] getVelocity
-- @param #Object self
-- @return #Vec3
-- @return #Vec3 3D velocity vector.
--- Returns true if the unit is in air.
-- @function [parent=#Object] inAir
@@ -527,6 +574,126 @@ do -- CoalitionObject
end -- CoalitionObject
do -- Weapon
--- [DCS Class Weapon](https://wiki.hoggitworld.com/view/DCS_Class_Weapon)
-- @type Weapon
-- @extends #CoalitionObject
-- @field #Weapon.flag flag enum stores weapon flags. Some of them are combination of another flags.
-- @field #Weapon.Category Category enum that stores weapon categories.
-- @field #Weapon.GuidanceType GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
-- @field #Weapon.MissileCategory MissileCategory enum that stores missile category. Available only for missiles (Weapon.Category.MISSILE).
-- @field #Weapon.WarheadType WarheadType enum that stores warhead types.
-- @field #Weapon.Desc Desc The descriptor of a weapon.
--- enum stores weapon flags. Some of them are combination of another flags.
-- @type Weapon.flag
-- @field LGB
-- @field TvGB
-- @field SNSGB
-- @field HEBomb
-- @field Penetrator
-- @field NapalmBomb
-- @field FAEBomb
-- @field ClusterBomb
-- @field Dispencer
-- @field CandleBomb
-- @field ParachuteBomb
-- @field GuidedBomb = LGB + TvGB + SNSGB
-- @field AnyUnguidedBomb = HEBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb
-- @field AnyBomb = GuidedBomb + AnyUnguidedBomb
-- @field LightRocket
-- @field MarkerRocket
-- @field CandleRocket
-- @field HeavyRocket
-- @field AnyRocket = LightRocket + HeavyRocket + MarkerRocket + CandleRocket
-- @field AntiRadarMissile
-- @field AntiShipMissile
-- @field AntiTankMissile
-- @field FireAndForgetASM
-- @field LaserASM
-- @field TeleASM
-- @field CruiseMissile
-- @field GuidedASM = LaserASM + TeleASM
-- @field TacticASM = GuidedASM + FireAndForgetASM
-- @field AnyASM = AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile
-- @field SRAAM
-- @field MRAAM
-- @field LRAAM
-- @field IR_AAM
-- @field SAR_AAM
-- @field AR_AAM
-- @field AnyAAM = IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
-- @field AnyMissile = AnyASM + AnyAAM
-- @field AnyAutonomousMissile = IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
-- @field GUN_POD
-- @field BuiltInCannon
-- @field Cannons = GUN_POD + BuiltInCannon
-- @field AnyAGWeapon = BuiltInCannon + GUN_POD + AnyBomb + AnyRocket + AnyASM
-- @field AnyAAWeapon = BuiltInCannon + GUN_POD + AnyAAM
-- @field UnguidedWeapon = Cannons + BuiltInCannon + GUN_POD + AnyUnguidedBomb + AnyRocket
-- @field GuidedWeapon = GuidedBomb + AnyASM + AnyAAM
-- @field AnyWeapon = AnyBomb + AnyRocket + AnyMissile + Cannons
-- @field MarkerWeapon = MarkerRocket + CandleRocket + CandleBomb
-- @field ArmWeapon = AnyWeapon - MarkerWeapon
--- Weapon.Category enum that stores weapon categories.
-- @type Weapon.Category
-- @field SHELL
-- @field MISSILE
-- @field ROCKET
-- @field BOMB
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
-- @type Weapon.GuidanceType
-- @field INS
-- @field IR
-- @field RADAR_ACTIVE
-- @field RADAR_SEMI_ACTIVE
-- @field RADAR_PASSIVE
-- @field TV
-- @field LASER
-- @field TELE
--- Weapon.MissileCategory enum that stores missile category. Available only for missiles (Weapon.Category.MISSILE).
-- @type Weapon.MissileCategory
-- @field AAM
-- @field SAM
-- @field BM
-- @field ANTI_SHIP
-- @field CRUISE
-- @field OTHER
--- Weapon.WarheadType enum that stores warhead types.
-- @type Weapon.WarheadType
-- @field AP
-- @field HE
-- @field SHAPED_EXPLOSIVE
--- Returns the unit that launched the weapon.
-- @function [parent=#Weapon] getLauncher
-- @param #Weapon self
-- @return #Unit
--- returns target of the guided weapon. Unguided weapons and guided weapon that is targeted at the point on the ground will return nil.
-- @function [parent=#Weapon] getTarget
-- @param #Weapon self
-- @return #Object
--- returns weapon descriptor. Descriptor type depends on weapon category.
-- @function [parent=#Weapon] getDesc
-- @param #Weapon self
-- @return #Weapon.Desc
Weapon = {} --#Weapon
end -- Weapon
do -- Airbase
--- [DCS Class Airbase](https://wiki.hoggitworld.com/view/DCS_Class_Airbase)
@@ -582,7 +749,66 @@ do -- Airbase
end -- Airbase
do -- Spot
--- [DCS Class Spot](https://wiki.hoggitworld.com/view/DCS_Class_Spot)
-- Represents a spot from laser or IR-pointer.
-- @type Spot
-- @field #Spot.Category Category enum that stores spot categories.
--- Enum that stores spot categories.
-- @type Spot.Category
-- @field #string INFRA_RED
-- @field #string LASER
--- Creates a laser ray emanating from the given object to a point in 3d space.
-- @function [parent=#Spot] createLaser
-- @param DCS#Object Source The source object of the laser.
-- @param DCS#Vec3 LocalRef An optional 3D offset for the source.
-- @param DCS#Vec3 Vec3 Target coordinate where the ray is pointing at.
-- @param #number LaserCode Any 4 digit number between 1111 and 1788.
-- @return #Spot
--- Creates an infrared ray emanating from the given object to a point in 3d space. Can be seen with night vision goggles.
-- @function [parent=#Spot] createInfraRed
-- @param DCS#Object Source Source position of the IR ray.
-- @param DCS#Vec3 LocalRef An optional 3D offset for the source.
-- @param DCS#Vec3 Vec3 Target coordinate where the ray is pointing at.
-- @return #Spot
--- Returns a vec3 table of the x, y, and z coordinates for the position of the given object in 3D space. Coordinates are dependent on the position of the maps origin.
-- @function [parent=#Spot] getPoint
-- @param #Spot self
-- @return DCS#Vec3 Point in 3D, where the beam is pointing at.
--- Sets the destination point from which the source of the spot is drawn toward.
-- @function [parent=#Spot] setPoint
-- @param #Spot self
-- @param DCS#Vec3 Vec3 Point in 3D, where the beam is pointing at.
--- Returns the number that is used to define the laser code for which laser designation can track.
-- @function [parent=#Spot] getCode
-- @param #Spot self
-- @return #number Code The laser code used.
--- Sets the number that is used to define the laser code for which laser designation can track.
-- @function [parent=#Spot] setCode
-- @param #Spot self
-- @param #number Code The laser code. Default value is 1688.
--- Destroys the spot.
-- @function [parent=#Spot] destroy
-- @param #Spot self
--- Gets the category of the spot (laser or IR).
-- @function [parent=#Spot] getCategory
-- @param #Spot self
-- @return #string Category.
Spot = {} --#Spot
end -- Spot
do -- Controller
--- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
@@ -939,6 +1165,10 @@ do -- Unit
-- @param #Unit self
-- @return #Unit.Desc
--- GROUND - Switch on/off radar emissions
-- @function [parent=#Unit] enableEmission
-- @param #Unit self
-- @param #boolean switch
Unit = {} --#Unit
@@ -1009,7 +1239,7 @@ do -- Group
-- @param #Group self
-- @return #number
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function.
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed; Initial size limits the unitNumber parameter for Group.getUnit() function.
-- @function [parent=#Group] getInitialSize
-- @param #Group self
-- @return #number
@@ -1024,6 +1254,11 @@ do -- Group
-- @param #Group self
-- @return #Controller
--- GROUND - Switch on/off radar emissions
-- @function [parent=#Group] enableEmission
-- @param #Group self
-- @param #boolean switch
Group = {} --#Group
end -- Group
@@ -1082,6 +1317,7 @@ do -- AI
-- @field TAKEOFF
-- @field TAKEOFF_PARKING
-- @field TURNING_POINT
-- @field TAKEOFF_PARKING_HOT
-- @field LAND
--- @type AI.Task.TurnMethod
@@ -1118,8 +1354,8 @@ do -- AI
--- @type AI.Option.Naval
-- @field #AI.Option.Naval.id id
-- @field #AI.Option.Naval.val val
--TODO: work on formation
--- @type AI.Option.Air.id
-- @field NO_OPTION
-- @field ROE
@@ -1128,7 +1364,34 @@ do -- AI
-- @field FLARE_USING
-- @field FORMATION
-- @field RTB_ON_BINGO
-- @field SILENCE
-- @field SILENCE
-- @field RTB_ON_OUT_OF_AMMO
-- @field ECM_USING
-- @field PROHIBIT_AA
-- @field PROHIBIT_JETT
-- @field PROHIBIT_AB
-- @field PROHIBIT_AG
-- @field MISSILE_ATTACK
-- @field PROHIBIT_WP_PASS_REPORT
--- @type AI.Option.Air.id.FORMATION
-- @field LINE_ABREAST
-- @field TRAIL
-- @field WEDGE
-- @field ECHELON_RIGHT
-- @field ECHELON_LEFT
-- @field FINGER_FOUR
-- @field SPREAD_FOUR
-- @field WW2_BOMBER_ELEMENT
-- @field WW2_BOMBER_ELEMENT_HEIGHT
-- @field WW2_FIGHTER_VIC
-- @field HEL_WEDGE
-- @field HEL_ECHELON
-- @field HEL_FRONT
-- @field HEL_COLUMN
-- @field COMBAT_BOX
-- @field JAVELIN_DOWN
--- @type AI.Option.Air.val
-- @field #AI.Option.Air.val.ROE ROE
@@ -1161,12 +1424,27 @@ do -- AI
-- @field AGAINST_FIRED_MISSILE
-- @field WHEN_FLYING_IN_SAM_WEZ
-- @field WHEN_FLYING_NEAR_ENEMIES
--- @type AI.Option.Air.val.ECM_USING
-- @field NEVER_USE
-- @field USE_IF_ONLY_LOCK_BY_RADAR
-- @field USE_IF_DETECTED_LOCK_BY_RADAR
-- @field ALWAYS_USE
--- @type AI.Option.Air.val.MISSILE_ATTACK
-- @field MAX_RANGE
-- @field NEZ_RANGE
-- @field HALF_WAY_RMAX_NEZ
-- @field TARGET_THREAT_EST
-- @field RANDOM_RANGE
--- @type AI.Option.Ground.id
-- @field NO_OPTION
-- @field ROE @{#AI.Option.Ground.val.ROE}
-- @field DISPERSE_ON_ATTACK true or false
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
-- @field ENGAGE_AIR_WEAPONS
--- @type AI.Option.Ground.val
-- @field #AI.Option.Ground.val.ROE ROE
@@ -1197,6 +1475,3 @@ do -- AI
AI = {} --#AI
end -- AI

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,54 +1,54 @@
--- **Functional** -- Keep airbases clean of crashing or colliding airplanes, and kill missiles when being fired at airbases.
--
--
-- ===
--
--
-- ## Features:
--
--
--
--
-- * Try to keep the airbase clean and operational.
-- * Prevent airplanes from crashing.
-- * Clean up obstructing airplanes from the runway that are standing still for a period of time.
-- * Prevent airplanes firing missiles within the airbase zone.
--
--
-- ===
--
--
-- ## Missions:
--
--
-- [CLA - CleanUp Airbase](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CLA%20-%20CleanUp%20Airbase)
--
--
-- ===
--
--
-- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase.
-- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE.
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
-- Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged.
--
--
-- This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean.
-- The following situations may happen that will still stop the runway of an airbase:
--
--
-- * A damaged unit is not removed on time when above the runway, and crashes on the runway.
-- * A bomb or missile is still able to dropped on the runway.
-- * Units collide on the airbase, and could not be removed on time.
--
--
-- When a unit is within the airbase zone and needs to be monitored,
-- its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean.
-- But as a result, there is more CPU overload.
--
--
-- So as an advise, I suggest you use the CLEANUP_AIRBASE class with care:
--
--
-- * Only monitor airbases that really need to be monitored!
-- * Try not to monitor airbases that are likely to be invaded by enemy troops.
-- For these airbases, there is little use to keep them clean, as they will be invaded anyway...
--
--
-- By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead.
--
--
-- ===
--
--
-- ### Author: **FlightControl**
-- ### Contributions:
--
-- ### Contributions:
--
-- ===
--
--
-- @module Functional.CleanUp
-- @image CleanUp_Airbases.JPG
@@ -60,29 +60,29 @@
-- @extends #CLEANUP_AIRBASE.__
--- Keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.
--
--
-- # 1. CLEANUP_AIRBASE Constructor
--
--
-- Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase.
--
--
-- -- Clean these Zones.
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
--
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi } )
--
-- -- or
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
--
--
-- # 2. Add or Remove airbases
--
--
-- The method @{#CLEANUP_AIRBASE.AddAirbase}() to add an airbase to the cleanup validation process.
-- The method @{#CLEANUP_AIRBASE.RemoveAirbase}() removes an airbase from the cleanup validation process.
--
--
-- # 3. Clean missiles and bombs within the airbase zone.
--
--
-- When missiles or bombs hit the runway, the airbase operations stop.
-- Use the method @{#CLEANUP_AIRBASE.SetCleanMissiles}() to control the cleaning of missiles, which will prevent airbases to stop.
-- Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do.
--
--
-- @field #CLEANUP_AIRBASE
CLEANUP_AIRBASE = {
ClassName = "CLEANUP_AIRBASE",
@@ -106,11 +106,11 @@ CLEANUP_AIRBASE.__.Airbases = {}
-- or
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
function CLEANUP_AIRBASE:New( AirbaseNames )
function CLEANUP_AIRBASE:New( AirbaseNames )
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE
self:F( { AirbaseNames } )
if type( AirbaseNames ) == 'table' then
for AirbaseID, AirbaseName in pairs( AirbaseNames ) do
self:AddAirbase( AirbaseName )
@@ -119,9 +119,9 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
local AirbaseName = AirbaseNames
self:AddAirbase( AirbaseName )
end
self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth )
self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval )
self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp )
@@ -130,7 +130,7 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash )
self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash )
self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash )
for UnitName, Unit in pairs( _DATABASE.UNITS ) do
local Unit = Unit -- Wrapper.Unit#UNIT
if Unit:IsAlive() ~= nil then
@@ -144,7 +144,7 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
end
end
end
return self
end
@@ -155,7 +155,7 @@ end
function CLEANUP_AIRBASE:AddAirbase( AirbaseName )
self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName )
self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()})
return self
end
@@ -197,7 +197,7 @@ function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 )
break;
end
end
return InAirbase
end
@@ -233,7 +233,7 @@ end
-- @param DCS#Weapon MissileObject
function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject )
self:F( { MissileObject } )
if MissileObject and MissileObject:isExist() then
MissileObject:destroy()
self:T( "MissileObject Destroyed")
@@ -245,7 +245,7 @@ end
function CLEANUP_AIRBASE.__:OnEventBirth( EventData )
self:F( { EventData } )
if EventData.IniUnit:IsAlive() ~= nil then
if EventData and EventData.IniUnit and EventData.IniUnit:IsAlive() ~= nil then
if self:IsInAirbase( EventData.IniUnit:GetVec2() ) then
self.CleanUpList[EventData.IniDCSUnitName] = {}
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit
@@ -267,7 +267,7 @@ function CLEANUP_AIRBASE.__:OnEventCrash( Event )
--TODO: DCS BUG - This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
-- self:T("before getGroup")
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
-- self:T("after getGroup")
-- _grp:destroy()
-- self:T("after deactivateGroup")
@@ -280,7 +280,7 @@ function CLEANUP_AIRBASE.__:OnEventCrash( Event )
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
end
end
--- Detects if a unit shoots a missile.
@@ -334,16 +334,16 @@ function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName )
self.CleanUpList[CleanUpUnitName] = {}
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
local CleanUpGroup = CleanUpUnit:GetGroup()
self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroup:GetName()
self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime()
self.CleanUpList[CleanUpUnitName].CleanUpMoved = false
self:T( { "CleanUp: Add to CleanUpList: ", CleanUpGroup:GetName(), CleanUpUnitName } )
end
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given AirbaseNames. If this is the case, add the Group to the CLEANUP_AIRBASE List.
@@ -369,7 +369,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
end
end
end
end
@@ -380,26 +380,26 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
local CleanUpCount = 0
for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do
CleanUpCount = CleanUpCount + 1
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
if CleanUpUnit:IsAlive() ~= nil then
if self:IsInAirbase( CleanUpUnit:GetVec2() ) then
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
self:T( { "CleanUp Scheduler", CleanUpUnitName } )
if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then
if CleanUpUnit:IsAboveRunway() then
if CleanUpUnit:InAir() then
local CleanUpLandHeight = CleanUpCoordinate:GetLandHeight()
local CleanUpUnitHeight = CleanUpCoordinate.y - CleanUpLandHeight
if CleanUpUnitHeight < 100 then
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } )
self:DestroyUnit( CleanUpUnit )
@@ -439,7 +439,6 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
end
end
self:T(CleanUpCount)
return true
end

View File

@@ -286,9 +286,9 @@ do -- DESIGNATE
-- * The status report can be automatically flashed by selecting "Status" -> "Flash Status On".
-- * The automatic flashing of the status report can be deactivated by selecting "Status" -> "Flash Status Off".
-- * The flashing of the status menu is disabled by default.
-- * The method @{#DESIGNATE.FlashStatusMenu}() can be used to enable or disable to flashing of the status menu.
-- * The method @{#DESIGNATE.SetFlashStatusMenu}() can be used to enable or disable to flashing of the status menu.
--
-- Designate:FlashStatusMenu( true )
-- Designate:SetFlashStatusMenu( true )
--
-- The example will activate the flashing of the status menu for this Designate object.
--
@@ -469,12 +469,12 @@ do -- DESIGNATE
self.CC = CC
self.Detection = Detection
self.AttackSet = AttackSet
self.RecceSet = Detection:GetDetectionSetGroup()
self.RecceSet = Detection:GetDetectionSet()
self.Recces = {}
self.Designating = {}
self:SetDesignateName()
self.LaseDuration = 60
self:SetLaseDuration() -- Default is 120 seconds.
self:SetFlashStatusMenu( false )
self:SetFlashDetectionMessages( true )
@@ -677,6 +677,14 @@ do -- DESIGNATE
return self
end
--- Set the lase duration for designations.
-- @param #DESIGNATE self
-- @param #number LaseDuration The time in seconds a lase will continue to hold on target. The default is 120 seconds.
-- @return #DESIGNATE
function DESIGNATE:SetLaseDuration( LaseDuration )
self.LaseDuration = LaseDuration or 120
return self
end
--- Generate an array of possible laser codes.
-- Each new lase will select a code from this table.
@@ -993,16 +1001,22 @@ do -- DESIGNATE
local ID = self.Detection:GetDetectedItemID( DetectedItem )
local MenuText = ID --.. ", " .. Coord:ToStringA2G( AttackGroup )
MenuText = string.format( "(%3s) %s", Designating, MenuText )
-- Use injected MenuName from TaskA2GDispatcher if using same Detection Object
if DetectedItem.DesignateMenuName then
MenuText = string.format( "(%3s) %s", Designating, DetectedItem.DesignateMenuName )
else
MenuText = string.format( "(%3s) %s", Designating, MenuText )
end
local DetectedMenu = MENU_GROUP_DELAYED:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
-- Build the Lasing menu.
if string.find( Designating, "L", 1, true ) == nil then
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, self.LaseDuration, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName )
end
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, self.LaseDuration ):SetTime( MenuTime ):SetTag( self.DesignateName )
else
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
end
@@ -1160,10 +1174,10 @@ do -- DESIGNATE
if string.find( self.Designating[Index], "L", 1, true ) == nil then
self.Designating[Index] = self.Designating[Index] .. "L"
self.LaseStart = timer.getTime()
self.LaseDuration = Duration
self:Lasing( Index, Duration, LaserCode )
end
self.LaseStart = timer.getTime()
self.LaseDuration = Duration
self:Lasing( Index, Duration, LaserCode )
end
@@ -1174,7 +1188,7 @@ do -- DESIGNATE
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
local MarkingCount = 0
local MarkedTypes = {}
@@ -1322,7 +1336,7 @@ do -- DESIGNATE
local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
self:__Lasing( -30, Index, Duration, LaserCodeRequested )
self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested )
self:SetDesignateMenu()
@@ -1344,7 +1358,7 @@ do -- DESIGNATE
end
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
local Recces = self.Recces
@@ -1369,7 +1383,7 @@ do -- DESIGNATE
function DESIGNATE:onafterSmoke( From, Event, To, Index, Color )
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
local TargetSetUnitCount = TargetSetUnit:Count()
local MarkedCount = 0
@@ -1385,7 +1399,7 @@ do -- DESIGNATE
self:F( "Smoking ..." )
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(SmokeUnit:GetPointVec2())
local RecceUnit = RecceGroup:GetUnit( 1 )
local RecceUnit = RecceGroup:GetUnit( 1 ) -- Wrapper.Unit#UNIT
if RecceUnit then
@@ -1414,7 +1428,7 @@ do -- DESIGNATE
function DESIGNATE:onafterIlluminate( From, Event, To, Index )
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
local TargetUnit = TargetSetUnit:GetFirst()
if TargetUnit then

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,416 @@
do -- DETECTION_ZONES
--- @type DETECTION_ZONES
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
-- @extends Functional.Detection#DETECTION_BASE
--- (old, to be revised ) Detect units within the battle zone for a list of @{Core.Zone}s detecting targets following (a) detection method(s),
-- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Wrapper.Unit#UNIT}s detected.
-- The class is group the detected units within zones given a DetectedZoneRange parameter.
-- A set with multiple detected zones will be created as there are groups of units detected.
--
-- ## 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[].Zone(s) is implemented in @{Functional.Detection#DETECTION_ZONES}.
--
-- 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}().
-- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZoneCount}().
-- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZone}() with a given index.
--
-- ## 4.4) Flare or Smoke detected units
--
-- Use the methods @{Functional.Detection#DETECTION_ZONES.FlareDetectedUnits}() or @{Functional.Detection#DETECTION_ZONES.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place.
--
-- ## 4.5) Flare or Smoke or Bound detected zones
--
-- Use the methods:
--
-- * @{Functional.Detection#DETECTION_ZONES.FlareDetectedZones}() to flare in a color
-- * @{Functional.Detection#DETECTION_ZONES.SmokeDetectedZones}() to smoke in a color
-- * @{Functional.Detection#DETECTION_ZONES.SmokeDetectedZones}() to bound with a tire with a white flag
--
-- the detected zones when a new detection has taken place.
--
-- @field #DETECTION_ZONES
DETECTION_ZONES = {
ClassName = "DETECTION_ZONES",
DetectionZoneRange = nil,
}
--- DETECTION_ZONES constructor.
-- @param #DETECTION_ZONES self
-- @param Core.Set#SET_ZONE DetectionSetZone The @{Set} of ZONE_RADIUS.
-- @param DCS#Coalition.side DetectionCoalition The coalition of the detection.
-- @return #DETECTION_ZONES
function DETECTION_ZONES:New( DetectionSetZone, DetectionCoalition )
-- Inherits from DETECTION_BASE
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetZone ) ) -- #DETECTION_ZONES
self.DetectionSetZone = DetectionSetZone -- Core.Set#SET_ZONE
self.DetectionCoalition = DetectionCoalition
self._SmokeDetectedUnits = false
self._FlareDetectedUnits = false
self._SmokeDetectedZones = false
self._FlareDetectedZones = false
self._BoundDetectedZones = false
return self
end
--- @param #DETECTION_ZONES self
-- @param #number The amount of alive recce.
function DETECTION_ZONES:CountAliveRecce()
return self.DetectionSetZone:Count()
end
--- @param #DETECTION_ZONES self
function DETECTION_ZONES:ForEachAliveRecce( IteratorFunction, ... )
self:F2( arg )
self.DetectionSetZone:ForEachZone( IteratorFunction, arg )
return self
end
--- Report summary of a detected item using a given numeric index.
-- @param #DETECTION_ZONES self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
-- @param Wrapper.Group#GROUP AttackGroup The group to get the settings for.
-- @param Core.Settings#SETTINGS Settings (Optional) Message formatting settings to use.
-- @return Core.Report#REPORT The report of the detection items.
function DETECTION_ZONES:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings )
self:F( { DetectedItem = DetectedItem } )
local DetectedItemID = self:GetDetectedItemID( DetectedItem )
if DetectedItem then
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
local ReportSummaryItem
local DetectedZone = self:GetDetectedItemZone( DetectedItem )
local DetectedItemCoordinate = DetectedZone:GetCoordinate()
local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings )
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
local DetectedItemsCount = DetectedSet:Count()
local DetectedItemsTypes = DetectedSet:GetTypeNames()
local Report = REPORT:New()
Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText)
Report:Add( string.format( "Threat: [%s]", string.rep( "", ThreatLevelA2G ), string.rep( "", 10-ThreatLevelA2G ) ) )
Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemsTypes ) )
Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) )
return Report
end
return nil
end
--- Report detailed of a detection result.
-- @param #DETECTION_ZONES self
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
-- @return #string
function DETECTION_ZONES:DetectedReportDetailed( AttackGroup ) --R2.1 Fixed missing report
self:F()
local Report = REPORT:New()
for DetectedItemIndex, DetectedItem in pairs( self.DetectedItems ) do
local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem
local ReportSummary = self:DetectedItemReportSummary( DetectedItem, AttackGroup )
Report:SetTitle( "Detected areas:" )
Report:Add( ReportSummary:Text() )
end
local ReportText = Report:Text()
return ReportText
end
--- Calculate the optimal intercept point of the DetectedItem.
-- @param #DETECTION_ZONES self
-- @param #DETECTION_BASE.DetectedItem DetectedItem
function DETECTION_ZONES:CalculateIntercept( DetectedItem )
local DetectedCoord = DetectedItem.Coordinate
-- local DetectedSpeed = DetectedCoord:GetVelocity()
-- local DetectedHeading = DetectedCoord:GetHeading()
--
-- if self.Intercept then
-- local DetectedSet = DetectedItem.Set
-- -- todo: speed
--
-- local TranslateDistance = DetectedSpeed * self.InterceptDelay
--
-- local InterceptCoord = DetectedCoord:Translate( TranslateDistance, DetectedHeading )
--
-- DetectedItem.InterceptCoord = InterceptCoord
-- else
-- DetectedItem.InterceptCoord = DetectedCoord
-- end
DetectedItem.InterceptCoord = DetectedCoord
end
--- Smoke the detected units
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES self
function DETECTION_ZONES:SmokeDetectedUnits()
self:F2()
self._SmokeDetectedUnits = true
return self
end
--- Flare the detected units
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES self
function DETECTION_ZONES:FlareDetectedUnits()
self:F2()
self._FlareDetectedUnits = true
return self
end
--- Smoke the detected zones
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES self
function DETECTION_ZONES:SmokeDetectedZones()
self:F2()
self._SmokeDetectedZones = true
return self
end
--- Flare the detected zones
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES self
function DETECTION_ZONES:FlareDetectedZones()
self:F2()
self._FlareDetectedZones = true
return self
end
--- Bound the detected zones
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES self
function DETECTION_ZONES:BoundDetectedZones()
self:F2()
self._BoundDetectedZones = true
return self
end
--- Make text documenting the changes of the detected zone.
-- @param #DETECTION_ZONES self
-- @param #DETECTION_BASE.DetectedItem DetectedItem
-- @return #string The Changes text
function DETECTION_ZONES:GetChangeText( DetectedItem )
self:F( DetectedItem )
local MT = {}
for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do
if ChangeCode == "AA" then
MT[#MT+1] = "Detected new area " .. ChangeData.ID .. ". The center target is a " .. ChangeData.ItemUnitType .. "."
end
if ChangeCode == "RAU" then
MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". Removed the center target."
end
if ChangeCode == "AAU" then
MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "."
end
if ChangeCode == "RA" then
MT[#MT+1] = "Removed old area " .. ChangeData.ID .. ". No more targets in this area."
end
if ChangeCode == "AU" then
local MTUT = {}
for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do
if ChangeUnitType ~= "ID" then
MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType
end
end
MT[#MT+1] = "Detected for area " .. ChangeData.ID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "."
end
if ChangeCode == "RU" then
local MTUT = {}
for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do
if ChangeUnitType ~= "ID" then
MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType
end
end
MT[#MT+1] = "Removed for area " .. ChangeData.ID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "."
end
end
return table.concat( MT, "\n" )
end
--- Make a DetectionSet table. This function will be overridden in the derived clsses.
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES self
function DETECTION_ZONES:CreateDetectionItems()
self:F( "Checking Detected Items for new Detected Units ..." )
local DetectedUnits = SET_UNIT:New()
-- First go through all zones, and check if there are new Zones.
-- New Zones become a new DetectedItem.
for ZoneName, DetectionZone in pairs( self.DetectionSetZone:GetSet() ) do
local DetectedItem = self:GetDetectedItemByKey( ZoneName )
if DetectedItem == nil then
DetectedItem = self:AddDetectedItemZone( "ZONE", ZoneName, nil, DetectionZone )
end
local DetectedItemSetUnit = self:GetDetectedItemSet( DetectedItem )
-- Scan the zone
DetectionZone:Scan( { Object.Category.UNIT }, { Unit.Category.GROUND_UNIT } )
-- For all the units in the zone,
-- check if they are of the same coalition to be included.
local ZoneUnits = DetectionZone:GetScannedUnits()
for DCSUnitID, DCSUnit in pairs( ZoneUnits ) do
local UnitName = DCSUnit:getName()
local ZoneUnit = UNIT:FindByName( UnitName )
local ZoneUnitCoalition = ZoneUnit:GetCoalition()
if ZoneUnitCoalition == self.DetectionCoalition then
if DetectedItemSetUnit:FindUnit( UnitName ) == nil and DetectedUnits:FindUnit( UnitName ) == nil then
self:F( "Adding " .. UnitName )
DetectedItemSetUnit:AddUnit( ZoneUnit )
DetectedUnits:AddUnit( ZoneUnit )
end
end
end
end
-- Now all the tests should have been build, now make some smoke and flares...
-- We also report here the friendlies within the detected areas.
for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do
local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
local DetectedFirstUnit = DetectedSet:GetFirst()
local DetectedZone = self:GetDetectedItemZone( DetectedItem )
-- Set the last known coordinate to the detection item.
local DetectedZoneCoord = DetectedZone:GetCoordinate()
self:SetDetectedItemCoordinate( DetectedItem, DetectedZoneCoord, DetectedFirstUnit )
self:CalculateIntercept( DetectedItem )
-- We search for friendlies nearby.
-- If there weren't any friendlies nearby, and now there are friendlies nearby, we flag the area as "changed".
-- If there were friendlies nearby, and now there aren't any friendlies nearby, we flag the area as "changed".
-- This is for the A2G dispatcher to detect if there is a change in the tactical situation.
local OldFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table
local NewFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
if OldFriendliesNearbyGround ~= NewFriendliesNearbyGround then
DetectedItem.Changed = true
end
self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level
--self:NearestRecce( DetectedItem )
if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then
DetectedZone:SmokeZone( SMOKECOLOR.Red, 30 )
end
--DetectedSet:Flush( self )
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit )
if DetectedUnit:IsAlive() then
--self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )
if DETECTION_ZONES._FlareDetectedUnits or self._FlareDetectedUnits then
DetectedUnit:FlareGreen()
end
if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then
DetectedUnit:SmokeGreen()
end
end
end
)
if DETECTION_ZONES._FlareDetectedZones or self._FlareDetectedZones then
DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) )
end
if DETECTION_ZONES._SmokeDetectedZones or self._SmokeDetectedZones then
DetectedZone:SmokeZone( SMOKECOLOR.White, 30 )
end
if DETECTION_ZONES._BoundDetectedZones or self._BoundDetectedZones then
self.CountryID = DetectedSet:GetFirst():GetCountry()
DetectedZone:BoundZone( 12, self.CountryID )
end
end
end
--- @param #DETECTION_ZONES self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Detection The element on which the detection is based.
-- @param #number DetectionTimeStamp Time stamp of detection event.
function DETECTION_ZONES:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
self.DetectionRun = self.DetectionRun + 1
if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then
self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method.
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
self:UpdateDetectedItemDetection( DetectedItem )
self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list.
if DetectedItem then
self:__DetectedItem( 0.1, DetectedItem )
end
end
self:__Detect( -self.RefreshTimeInterval )
end
end
--- Set IsDetected flag for the DetectedItem, which can have more units.
-- @param #DETECTION_ZONES self
-- @return #DETECTION_ZONES.DetectedItem DetectedItem
-- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet.
function DETECTION_ZONES:UpdateDetectedItemDetection( DetectedItem )
local IsDetected = true
DetectedItem.IsDetected = true
return IsDetected
end
end

View File

@@ -872,7 +872,7 @@ function ESCORT:_AttackTarget( DetectedItem )
EscortGroup:OptionROTPassiveDefense()
EscortGroup:SetState( EscortGroup, "Escort", self )
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
local Tasks = {}
@@ -895,7 +895,7 @@ function ESCORT:_AttackTarget( DetectedItem )
else
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
local Tasks = {}
@@ -934,7 +934,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem )
EscortGroupAttack:OptionROEOpenFire()
EscortGroupAttack:OptionROTVertical()
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
local Tasks = {}
@@ -956,7 +956,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem )
)
else
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
local Tasks = {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
-- ## Features:
--
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <EFBFBD>
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range °
-- * Provide alerts when a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.

View File

@@ -81,7 +81,7 @@
-- @field #PSEUDOATC
PSEUDOATC={
ClassName = "PSEUDOATC",
player={},
group={},
Debug=false,
mdur=30,
mrefresh=120,
@@ -98,7 +98,7 @@ PSEUDOATC.id="PseudoATC | "
--- PSEUDOATC version.
-- @field #number version
PSEUDOATC.version="0.9.1"
PSEUDOATC.version="0.9.2"
-----------------------------------------------------------------------------------------------------------------------------------------
@@ -383,16 +383,23 @@ function PSEUDOATC:PlayerEntered(unit)
local PlayerName=unit:GetPlayerName()
local UnitName=unit:GetName()
local CallSign=unit:GetCallsign()
local UID=unit:GetDCSObject():getID()
if not self.group[GID] then
self.group[GID]={}
self.group[GID].player={}
end
-- Init player table.
self.player[GID]={}
self.player[GID].group=group
self.player[GID].unit=unit
self.player[GID].groupname=GroupName
self.player[GID].unitname=UnitName
self.player[GID].playername=PlayerName
self.player[GID].callsign=CallSign
self.player[GID].waypoints=group:GetTaskRoute()
self.group[GID].player[UID]={}
self.group[GID].player[UID].group=group
self.group[GID].player[UID].unit=unit
self.group[GID].player[UID].groupname=GroupName
self.group[GID].player[UID].unitname=UnitName
self.group[GID].player[UID].playername=PlayerName
self.group[GID].player[UID].callsign=CallSign
self.group[GID].player[UID].waypoints=group:GetTaskRoute()
-- Info message.
local text=string.format("Player %s entered unit %s of group %s (id=%d).", PlayerName, UnitName, GroupName, GID)
@@ -400,19 +407,26 @@ function PSEUDOATC:PlayerEntered(unit)
MESSAGE:New(text, 30):ToAllIf(self.Debug)
-- Create main F10 menu, i.e. "F10/Pseudo ATC"
self.player[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC")
local countPlayerInGroup = 0
for _ in pairs(self.group[GID].player) do countPlayerInGroup = countPlayerInGroup + 1 end
if countPlayerInGroup <= 1 then
self.group[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC")
end
-- Create/update custom menu for player
self:MenuCreatePlayer(GID,UID)
-- Create/update list of nearby airports.
self:LocalAirports(GID)
self:LocalAirports(GID,UID)
-- Create submenu of local airports.
self:MenuAirports(GID)
self:MenuAirports(GID,UID)
-- Create submenu Waypoints.
self:MenuWaypoints(GID)
self:MenuWaypoints(GID,UID)
-- Start scheduler to refresh the F10 menues.
self.player[GID].scheduler, self.player[GID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID}, self.mrefresh, self.mrefresh)
self.group[GID].player[UID].scheduler, self.group[GID].player[UID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID, UID}, self.mrefresh, self.mrefresh)
end
@@ -425,24 +439,23 @@ function PSEUDOATC:PlayerLanded(unit, place)
-- Gather some information.
local group=unit:GetGroup()
local id=group:GetID()
local PlayerName=self.player[id].playername
local Callsign=self.player[id].callsign
local UnitName=self.player[id].unitname
local GroupName=self.player[id].groupname
local CallSign=self.player[id].callsign
local GID=group:GetID()
local UID=unit:GetDCSObject():getID()
local PlayerName=self.group[GID].player[UID].playername
local UnitName=self.group[GID].player[UID].unitname
local GroupName=self.group[GID].player[UID].groupname
-- Debug message.
local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, id, place)
local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, GID, place)
self:T(PSEUDOATC.id..text)
MESSAGE:New(text, 30):ToAllIf(self.Debug)
-- Stop altitude reporting timer if its activated.
self:AltitudeTimerStop(id)
self:AltitudeTimerStop(GID,UID)
-- Welcome message.
if place and self.chatty then
local text=string.format("Touchdown! Welcome to %s. Have a nice day!", place)
local text=string.format("Touchdown! Welcome to %s pilot %s. Have a nice day!", place,PlayerName)
MESSAGE:New(text, self.mdur):ToGroup(group)
end
@@ -457,15 +470,15 @@ function PSEUDOATC:PlayerTakeOff(unit, place)
-- Gather some information.
local group=unit:GetGroup()
local id=group:GetID()
local PlayerName=self.player[id].playername
local Callsign=self.player[id].callsign
local UnitName=self.player[id].unitname
local GroupName=self.player[id].groupname
local CallSign=self.player[id].callsign
local GID=group:GetID()
local UID=unit:GetDCSObject():getID()
local PlayerName=self.group[GID].player[UID].playername
local CallSign=self.group[GID].player[UID].callsign
local UnitName=self.group[GID].player[UID].unitname
local GroupName=self.group[GID].player[UID].groupname
-- Debug message.
local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, id, place)
local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, GID, place)
self:T(PSEUDOATC.id..text)
MESSAGE:New(text, 30):ToAllIf(self.Debug)
@@ -485,30 +498,44 @@ function PSEUDOATC:PlayerLeft(unit)
-- Get id.
local group=unit:GetGroup()
local id=group:GetID()
local GID=group:GetID()
local UID=unit:GetDCSObject():getID()
if self.player[id] then
if self.group[GID].player[UID] then
local PlayerName=self.group[GID].player[UID].playername
local CallSign=self.group[GID].player[UID].callsign
local UnitName=self.group[GID].player[UID].unitname
local GroupName=self.group[GID].player[UID].groupname
-- Debug message.
local text=string.format("Player %s (callsign %s) of group %s just left unit %s.", self.player[id].playername, self.player[id].callsign, self.player[id].groupname, self.player[id].unitname)
local text=string.format("Player %s (callsign %s) of group %s just left unit %s.", PlayerName, CallSign, GroupName, UnitName)
self:T(PSEUDOATC.id..text)
MESSAGE:New(text, 30):ToAllIf(self.Debug)
-- Stop scheduler for menu updates
if self.player[id].schedulerid then
self.player[id].scheduler:Stop(self.player[id].schedulerid)
if self.group[GID].player[UID].schedulerid then
self.group[GID].player[UID].scheduler:Stop(self.group[GID].player[UID].schedulerid)
end
-- Stop scheduler for reporting alt if it runs.
self:AltitudeTimerStop(id)
self:AltitudeTimerStop(GID,UID)
-- Remove own menu.
if self.group[GID].player[UID].menu_own then
missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_own)
end
-- Remove main menu.
if self.player[id].menu_main then
missionCommands.removeItem(self.player[id].menu_main)
-- WARNING: Remove only if last human element of group
local countPlayerInGroup = 0
for _ in pairs(self.group[GID].player) do countPlayerInGroup = countPlayerInGroup + 1 end
if self.group[GID].menu_main and countPlayerInGroup==1 then
missionCommands.removeItemForGroup(GID,self.group[GID].menu_main)
end
-- Remove player array.
self.player[id]=nil
self.group[GID].player[UID]=nil
end
end
@@ -518,80 +545,94 @@ end
--- Refreshes all player menues.
-- @param #PSEUDOATC self.
-- @param #number id Group id of player unit.
function PSEUDOATC:MenuRefresh(id)
self:F({id=id})
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:MenuRefresh(GID,UID)
self:F({GID=GID,UID=UID})
-- Debug message.
local text=string.format("Refreshing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname)
local text=string.format("Refreshing menues for player %s in group %s.", self.group[GID].player[UID].playername, self.group[GID].player[UID].groupname)
self:T(PSEUDOATC.id..text)
MESSAGE:New(text,30):ToAllIf(self.Debug)
-- Clear menu.
self:MenuClear(id)
self:MenuClear(GID,UID)
-- Create list of nearby airports.
self:LocalAirports(id)
self:LocalAirports(GID,UID)
-- Create submenu Local Airports.
self:MenuAirports(id)
self:MenuAirports(GID,UID)
-- Create submenu Waypoints etc.
self:MenuWaypoints(id)
self:MenuWaypoints(GID,UID)
end
--- Create player menus.
-- @param #PSEUDOATC self.
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:MenuCreatePlayer(GID,UID)
self:F({GID=GID,UID=UID})
-- Table for menu entries.
local PlayerName=self.group[GID].player[UID].playername
self.group[GID].player[UID].menu_own=missionCommands.addSubMenuForGroup(GID, PlayerName, self.group[GID].menu_main)
end
--- Clear player menus.
-- @param #PSEUDOATC self.
-- @param #number id Group id of player unit.
function PSEUDOATC:MenuClear(id)
self:F(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:MenuClear(GID,UID)
self:F({GID=GID,UID=UID})
-- Debug message.
local text=string.format("Clearing menus for player %s in group %s.", self.player[id].playername, self.player[id].groupname)
local text=string.format("Clearing menus for player %s in group %s.", self.group[GID].player[UID].playername, self.group[GID].player[UID].groupname)
self:T(PSEUDOATC.id..text)
MESSAGE:New(text,30):ToAllIf(self.Debug)
-- Delete Airports menu.
if self.player[id].menu_airports then
missionCommands.removeItemForGroup(id, self.player[id].menu_airports)
self.player[id].menu_airports=nil
if self.group[GID].player[UID].menu_airports then
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_airports)
self.group[GID].player[UID].menu_airports=nil
else
self:T2(PSEUDOATC.id.."No airports to clear menus.")
end
-- Delete waypoints menu.
if self.player[id].menu_waypoints then
missionCommands.removeItemForGroup(id, self.player[id].menu_waypoints)
self.player[id].menu_waypoints=nil
if self.group[GID].player[UID].menu_waypoints then
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_waypoints)
self.group[GID].player[UID].menu_waypoints=nil
end
-- Delete report alt until touchdown menu command.
if self.player[id].menu_reportalt then
missionCommands.removeItemForGroup(id, self.player[id].menu_reportalt)
self.player[id].menu_reportalt=nil
if self.group[GID].player[UID].menu_reportalt then
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_reportalt)
self.group[GID].player[UID].menu_reportalt=nil
end
-- Delete request current alt menu command.
if self.player[id].menu_requestalt then
missionCommands.removeItemForGroup(id, self.player[id].menu_requestalt)
self.player[id].menu_requestalt=nil
if self.group[GID].player[UID].menu_requestalt then
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_requestalt)
self.group[GID].player[UID].menu_requestalt=nil
end
end
--- Create "F10/Pseudo ATC/Local Airports/Airport Name/" menu items each containing weather report and BR request.
-- @param #PSEUDOATC self
-- @param #number id Group id of player unit for which menues are created.
function PSEUDOATC:MenuAirports(id)
self:F(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:MenuAirports(GID,UID)
self:F({GID=GID,UID=UID})
-- Table for menu entries.
self.player[id].menu_airports=missionCommands.addSubMenuForGroup(id, "Local Airports", self.player[id].menu_main)
self.group[GID].player[UID].menu_airports=missionCommands.addSubMenuForGroup(GID, "Local Airports", self.group[GID].player[UID].menu_own)
local i=0
for _,airport in pairs(self.player[id].airports) do
for _,airport in pairs(self.group[GID].player[UID].airports) do
i=i+1
if i > 10 then
@@ -603,37 +644,38 @@ function PSEUDOATC:MenuAirports(id)
local pos=AIRBASE:FindByName(name):GetCoordinate()
--F10menu_ATC_airports[ID][name] = missionCommands.addSubMenuForGroup(ID, name, F10menu_ATC)
local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_airports)
local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_airports)
-- Create menu reporting commands
missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name)
missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name)
missionCommands.addCommandForGroup(GID, "Weather Report", submenu, self.ReportWeather, self, GID, UID, pos, name)
missionCommands.addCommandForGroup(GID, "Request BR", submenu, self.ReportBR, self, GID, UID, pos, name)
-- Debug message.
self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, id))
self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, GID))
end
end
--- Create "F10/Pseudo ATC/Waypoints/<Waypoint i> menu items.
-- @param #PSEUDOATC self
-- @param #number id Group id of player unit for which menues are created.
function PSEUDOATC:MenuWaypoints(id)
self:F(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:MenuWaypoints(GID, UID)
self:F({GID=GID, UID=UID})
-- Player unit and callsign.
local unit=self.player[id].unit --Wrapper.Unit#UNIT
local callsign=self.player[id].callsign
-- local unit=self.group[GID].player[UID].unit --Wrapper.Unit#UNIT
local callsign=self.group[GID].player[UID].callsign
-- Debug info.
self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", callsign, id))
self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", callsign, GID))
if #self.player[id].waypoints>0 then
if #self.group[GID].player[UID].waypoints>0 then
-- F10/PseudoATC/Waypoints
self.player[id].menu_waypoints=missionCommands.addSubMenuForGroup(id, "Waypoints", self.player[id].menu_main)
self.group[GID].player[UID].menu_waypoints=missionCommands.addSubMenuForGroup(GID, "Waypoints", self.group[GID].player[UID].menu_own)
local j=0
for i, wp in pairs(self.player[id].waypoints) do
for i, wp in pairs(self.group[GID].player[UID].waypoints) do
-- Increase counter
j=j+1
@@ -647,16 +689,16 @@ function PSEUDOATC:MenuWaypoints(id)
local name=string.format("Waypoint %d", i-1)
-- "F10/PseudoATC/Waypoints/Waypoint X"
local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_waypoints)
local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_waypoints)
-- Menu commands for each waypoint "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X/<Commands>"
missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name)
missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name)
missionCommands.addCommandForGroup(GID, "Weather Report", submenu, self.ReportWeather, self, GID, UID, pos, name)
missionCommands.addCommandForGroup(GID, "Request BR", submenu, self.ReportBR, self, GID, UID, pos, name)
end
end
self.player[id].menu_reportalt = missionCommands.addCommandForGroup(id, "Talk me down", self.player[id].menu_main, self.AltidudeTimerToggle, self, id)
self.player[id].menu_requestalt = missionCommands.addCommandForGroup(id, "Request altitude", self.player[id].menu_main, self.ReportHeight, self, id)
self.group[GID].player[UID].menu_reportalt = missionCommands.addCommandForGroup(GID, "Talk me down", self.group[GID].player[UID].menu_own, self.AltidudeTimerToggle, self, GID, UID)
self.group[GID].player[UID].menu_requestalt = missionCommands.addCommandForGroup(GID, "Request altitude", self.group[GID].player[UID].menu_own, self.ReportHeight, self, GID, UID)
end
-----------------------------------------------------------------------------------------------------------------------------------------
@@ -664,14 +706,15 @@ end
--- Weather Report. Report pressure QFE/QNH, temperature, wind at certain location.
-- @param #PSEUDOATC self
-- @param #number id Group id to which the report is delivered.
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured.
-- @param #string location Name of the location at which the pressure is measured.
function PSEUDOATC:ReportWeather(id, position, location)
self:F({id=id, position=position, location=location})
function PSEUDOATC:ReportWeather(GID, UID, position, location)
self:F({GID=GID, UID=UID, position=position, location=location})
-- Player unit system settings.
local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
local text=string.format("Local weather at %s:\n", location)
@@ -699,7 +742,7 @@ function PSEUDOATC:ReportWeather(id, position, location)
local T=position:GetTemperature()
-- 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
_T=string.format('%d°C', T)
end
@@ -723,23 +766,24 @@ function PSEUDOATC:ReportWeather(id, position, location)
end
-- Message text.
local text=text..string.format("Wind from %s at %s (%s).", Ds, Vs, Bd)
local text=text..string.format("%s, Wind from %s at %s (%s).", self.group[GID].player[UID].playername, Ds, Vs, Bd)
-- Send message
self:_DisplayMessageToGroup(self.player[id].unit, text, self.mdur, true)
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit, text, self.mdur, true)
end
--- Report absolute bearing and range form player unit to airport.
-- @param #PSEUDOATC self
-- @param #number id Group id to the report is delivered.
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured.
-- @param #string location Name of the location at which the pressure is measured.
function PSEUDOATC:ReportBR(id, position, location)
self:F({id=id, position=position, location=location})
function PSEUDOATC:ReportBR(GID, UID, position, location)
self:F({GID=GID, UID=UID, position=position, location=location})
-- Current coordinates.
local unit=self.player[id].unit --Wrapper.Unit#UNIT
local unit=self.group[GID].player[UID].unit --Wrapper.Unit#UNIT
local coord=unit:GetCoordinate()
-- Direction vector from current position (coord) to target (position).
@@ -752,7 +796,7 @@ function PSEUDOATC:ReportBR(id, position, location)
local Bs=string.format('%03d°', angle)
-- Settings.
local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
local Rs=string.format("%.1f NM", UTILS.MetersToNM(range))
@@ -763,18 +807,20 @@ function PSEUDOATC:ReportBR(id, position, location)
-- Message text.
local text=string.format("%s: Bearing %s, Range %s.", location, Bs, Rs)
-- Send message to player group.
MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group)
-- Send message
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit, text, self.mdur, true)
end
--- Report altitude above ground level of player unit.
-- @param #PSEUDOATC self
-- @param #number id Group id to the report is delivered.
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
-- @param #number dt (Optional) Duration the message is displayed.
-- @param #boolean _clear (Optional) Clear previouse messages.
-- @return #number Altitude above ground.
function PSEUDOATC:ReportHeight(id, dt, _clear)
self:F({id=id, dt=dt})
function PSEUDOATC:ReportHeight(GID, UID, dt, _clear)
self:F({GID=GID, UID=UID, dt=dt})
local dt = dt or self.mdur
if _clear==nil then
@@ -791,7 +837,7 @@ function PSEUDOATC:ReportHeight(id, dt, _clear)
end
-- Get height AGL.
local unit=self.player[id].unit --Wrapper.Unit#UNIT
local unit=self.group[GID].player[UID].unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
@@ -800,7 +846,7 @@ function PSEUDOATC:ReportHeight(id, dt, _clear)
local callsign=unit:GetCallsign()
-- Settings.
local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
-- Height string.
local Hs=string.format("%d ft", UTILS.MetersToFeet(height))
@@ -817,7 +863,7 @@ function PSEUDOATC:ReportHeight(id, dt, _clear)
end
-- Send message to player group.
self:_DisplayMessageToGroup(self.player[id].unit,_text, dt,_clear)
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,_text, dt,_clear)
-- Return height
return height
@@ -830,47 +876,50 @@ end
--- Toggle report altitude reporting on/off.
-- @param #PSEUDOATC self.
-- @param #number id Group id of player unit.
function PSEUDOATC:AltidudeTimerToggle(id)
self:F(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:AltidudeTimerToggle(GID,UID)
self:F({GID=GID, UID=UID})
if self.player[id].altimerid then
if self.group[GID].player[UID].altimerid then
-- If the timer is on, we turn it off.
self:AltitudeTimerStop(id)
self:AltitudeTimerStop(GID, UID)
else
-- If the timer is off, we turn it on.
self:AltitudeTimeStart(id)
self:AltitudeTimeStart(GID, UID)
end
end
--- Start altitude reporting scheduler.
-- @param #PSEUDOATC self.
-- @param #number id Group id of player unit.
function PSEUDOATC:AltitudeTimeStart(id)
self:F(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:AltitudeTimeStart(GID, UID)
self:F({GID=GID, UID=UID})
-- Debug info.
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", id))
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID))
-- Start timer. Altitude is reported every ~3 seconds.
self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1, true}, 1, 3)
self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 0.1, true}, 1, 3)
end
--- Stop/destroy DCS scheduler function for reporting altitude.
-- @param #PSEUDOATC self.
-- @param #number id Group id of player unit.
function PSEUDOATC:AltitudeTimerStop(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:AltitudeTimerStop(GID, UID)
self:F({GID=GID,UID=UID})
-- Debug info.
self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id))
self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", UID))
-- Stop timer.
if self.player[id].altimerid then
self.player[id].altimer:Stop(self.player[id].altimerid)
if self.group[GID].player[UID].altimerid then
self.group[GID].player[UID].altimer:Stop(self.group[GID].player[UID].altimerid)
end
self.player[id].altimer=nil
self.player[id].altimerid=nil
self.group[GID].player[UID].altimer=nil
self.group[GID].player[UID].altimerid=nil
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -878,16 +927,17 @@ end
--- Create list of nearby airports sorted by distance to player unit.
-- @param #PSEUDOATC self
-- @param #number id Group id of player unit.
function PSEUDOATC:LocalAirports(id)
self:F(id)
-- @param #number GID Group id of player unit.
-- @param #number UID Unit id of player.
function PSEUDOATC:LocalAirports(GID, UID)
self:F({GID=GID, UID=UID})
-- Airports table.
self.player[id].airports=nil
self.player[id].airports={}
self.group[GID].player[UID].airports=nil
self.group[GID].player[UID].airports={}
-- Current player position.
local pos=self.player[id].unit:GetCoordinate()
local pos=self.group[GID].player[UID].unit:GetCoordinate()
-- Loop over coalitions.
for i=0,2 do
@@ -899,11 +949,14 @@ function PSEUDOATC:LocalAirports(id)
for _,airbase in pairs(airports) do
local name=airbase:getName()
local q=AIRBASE:FindByName(name):GetCoordinate()
local d=q:Get2DDistance(pos)
local a=AIRBASE:FindByName(name)
if a then
local q=a:GetCoordinate()
local d=q:Get2DDistance(pos)
-- Add to table.
table.insert(self.player[id].airports, {distance=d, name=name})
-- Add to table.
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
end
end
end
@@ -914,7 +967,7 @@ function PSEUDOATC:LocalAirports(id)
end
-- Sort airports table w.r.t. distance to player.
table.sort(self.player[id].airports, compare)
table.sort(self.group[GID].player[UID].airports, compare)
end
@@ -992,3 +1045,5 @@ function PSEUDOATC:_myname(unitname)
return string.format("%s (%s)", csign, pname)
end

View File

@@ -69,7 +69,7 @@
-- @field #string category Category of aircarft: "plane" or "heli".
-- @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 #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 #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.
@@ -150,6 +150,8 @@
-- @field #number parkingscanradius Radius in meters until which parking spots are scanned for obstacles like other units, statics or scenery.
-- @field #boolean parkingscanscenery If true, area around parking spots is scanned for scenery objects. Default is false.
-- @field #boolean parkingverysafe If true, parking spots are considered as non-free until a possible aircraft has left and taken off. Default false.
-- @field #boolean despawnair If true, aircraft are despawned when they reach their destination zone. Default.
-- @field #boolean eplrs If true, turn on EPLSR datalink for the RAT group.
-- @extends Core.Spawn#SPAWN
--- Implements an easy to use way to randomly fill your map with AI aircraft.
@@ -240,7 +242,7 @@
-- * AIRBASE.TerminalType.OpenMed: Open/Shelter air airplane only.
-- * AIRBASE.TerminalType.OpenBig: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
-- * AIRBASE.TerminalType.OpenMedOrBig: Combines OpenMed and OpenBig spots.
-- * AIRBASE.TerminalType.HelicopterUnsable: Combines HelicopterOnly, OpenMed and OpenBig.
-- * AIRBASE.TerminalType.HelicopterUsable: Combines HelicopterOnly, OpenMed and OpenBig.
-- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter, OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
--
-- So for example
@@ -346,7 +348,7 @@ RAT={
category = nil, -- Category of aircarft: "plane" or "heli".
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.
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, ...).
Vcruisemax=nil, -- Max cruise speed in set by user.
Vclimb=1500, -- Default climb rate in ft/min.
@@ -428,6 +430,8 @@ RAT={
parkingscanradius=40, -- Scan radius.
parkingscanscenery=false, -- Scan parking spots for scenery obstacles.
parkingverysafe=false, -- Very safe option.
despawnair=true,
eplrs=false,
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -546,7 +550,7 @@ RAT.id="RAT | "
--- RAT version.
-- @list version
RAT.version={
version = "2.3.4",
version = "2.3.9",
print = true,
}
@@ -653,7 +657,7 @@ end
-- @param #RAT self
-- @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.
-- @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)
-- Make sure that this function is only been called once per RAT object.
@@ -717,6 +721,11 @@ function RAT:Spawn(naircraft)
self.FLcruise=005*RAT.unit.FL2m
end
end
-- Enable helos to go to destinations 100 meters away.
if self.category==RAT.cat.heli then
self.mindist=50
end
-- Run consistency checks.
self:_CheckConsistency()
@@ -1093,6 +1102,14 @@ function RAT:SetParkingSpotSafeOFF()
return self
end
--- Aircraft that reach their destination zone are not despawned. They will probably go the the nearest airbase and try to land.
-- @param #RAT self
-- @return #RAT RAT self object.
function RAT:SetDespawnAirOFF()
self.despawnair=false
return self
end
--- Set takeoff type. Starting cold at airport, starting hot at airport, starting at runway, starting in the air.
-- Default is "takeoff-coldorhot". So there is a 50% chance that the aircraft starts with cold engines and 50% that it starts with hot engines.
-- @param #RAT self
@@ -1272,7 +1289,7 @@ end
--- Include all airports which lie in a zone as possible destinations.
-- @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.
function RAT:SetDestinationsFromZone(zone)
self:F2(zone)
@@ -1288,7 +1305,7 @@ end
--- Include all airports which lie in a zone as possible destinations.
-- @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.
function RAT:SetDeparturesFromZone(zone)
self:F2(zone)
@@ -1627,6 +1644,19 @@ function RAT:Invisible()
return self
end
--- Turn EPLRS datalink on/off.
-- @param #RAT self
-- @param #boolean switch If true (or nil), turn EPLRS on.
-- @return #RAT RAT self object.
function RAT:SetEPLRS(switch)
if switch==nil or switch==true then
self.eplrs=true
else
self.eplrs=false
end
return self
end
--- Aircraft are immortal.
-- @param #RAT self
-- @return #RAT RAT self object.
@@ -1812,14 +1842,14 @@ function RAT:ATC_Delay(time)
end
--- Set minimum distance between departure and destination. Default is 5 km.
-- Minimum distance should not be smaller than maybe ~500 meters to ensure that departure and destination are different.
-- Minimum distance should not be smaller than maybe ~100 meters to ensure that departure and destination are different.
-- @param #RAT self
-- @param #number dist Distance in km.
-- @return #RAT RAT self object.
function RAT:SetMinDistance(dist)
self:F2(dist)
-- Distance in meters. Absolute minimum is 500 m.
self.mindist=math.max(500, dist*1000)
self.mindist=math.max(100, dist*1000)
return self
end
@@ -2149,6 +2179,11 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live
self:_CommandImmortal(group, true)
end
-- Set group to be immortal.
if self.eplrs then
group:CommandEPLRS(true, 1)
end
-- Set ROE, default is "weapon hold".
self:_SetROE(group, self.roe)
@@ -2446,7 +2481,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
local VxCruiseMax
if self.Vcruisemax then
-- User input.
VxCruiseMax = min(self.Vcruisemax, self.aircraft.Vmax)
VxCruiseMax = math.min(self.Vcruisemax, self.aircraft.Vmax)
else
-- Max cruise speed 90% of Vmax or 900 km/h whichever is lower.
VxCruiseMax = math.min(self.aircraft.Vmax*0.90, 250)
@@ -3357,11 +3392,19 @@ function RAT:_GetAirportsOfMap()
local _name=airbase:getName()
local _myab=AIRBASE:FindByName(_name)
-- Add airport to table.
table.insert(self.airports_map, _myab)
if _myab then
local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName()
self:T(RAT.id..text)
-- Add airport to table.
table.insert(self.airports_map, _myab)
local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName()
self:T(RAT.id..text)
else
self:E(RAT.id..string.format("WARNING: Airbase %s does not exsist as MOOSE object!", tostring(_name)))
end
end
end
@@ -3373,7 +3416,7 @@ function RAT:_GetAirportsOfCoalition()
for _,coalition in pairs(self.ctable) do
for _,_airport in pairs(self.airports_map) do
local airport=_airport --Wrapper.Airbase#AIRBASE
local category=airport:GetDesc().category
local category=airport:GetAirbaseCategory()
if airport:GetCoalition()==coalition then
-- Planes cannot land on FARPs.
--local condition1=self.category==RAT.cat.plane and airport:GetTypeName()=="FARP"
@@ -3599,13 +3642,18 @@ function RAT:Status(message, forID)
local text=string.format("Flight %s will be despawned NOW!", self.alias)
self:T(RAT.id..text)
-- Despawn old group.
-- Respawn group
if (not self.norespawn) and (not self.respawn_after_takeoff) then
local idx=self:GetSpawnIndexFromGroup(group)
local coord=group:GetCoordinate()
self:_Respawn(idx, coord, 0)
end
self:_Despawn(group, 0)
-- Despawn old group.
if self.despawnair then
self:_Despawn(group, 0)
end
end
@@ -3799,7 +3847,7 @@ function RAT:_OnBirth(EventData)
-- Check if any unit of the group was spawned on top of another unit in the MOOSE data base.
local ontop=false
if self.checkontop and (_airbase and _airbase:GetDesc().category==Airbase.Category.AIRDROME) then
if self.checkontop and (_airbase and _airbase:GetAirbaseCategory()==Airbase.Category.AIRDROME) then
ontop=self:_CheckOnTop(SpawnGroup, self.ontopradius)
end
@@ -4409,7 +4457,7 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport
if (Airport~=nil) and (Type~=RAT.wp.air) then
local AirbaseID = Airport:GetID()
local AirbaseCategory = Airport:GetDesc().category
local AirbaseCategory = Airport:GetAirbaseCategory()
if AirbaseCategory == Airbase.Category.SHIP then
RoutePoint.linkUnit = AirbaseID
RoutePoint.helipadId = AirbaseID
@@ -5093,7 +5141,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
local spawnonrunway=false
local spawnonairport=false
if spawnonground then
local AirbaseCategory = departure:GetDesc().category
local AirbaseCategory = departure:GetAirbaseCategory()
if AirbaseCategory == Airbase.Category.SHIP then
spawnonship=true
elseif AirbaseCategory == Airbase.Category.HELIPAD then
@@ -5125,6 +5173,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
if self.uncontrolled then
-- This is used in the SPAWN:SpawnWithIndex() function. Some values are overwritten there!
self.SpawnUnControlled=true
SpawnTemplate.uncontrolled=true
end
-- Number of units in the group. With grouping this can actually differ from the template group size!
@@ -5322,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
if spawnonground then
-- Sh<EFBFBD>ps and FARPS seem to have a build in queue.
-- Sh°ps and FARPS seem to have a build in queue.
if spawnonship or spawnonfarp or spawnonrunway or automatic then
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
@@ -5435,7 +5484,7 @@ function RAT:_ATCInit(airports_map)
if not RAT.ATC.init then
local text
text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
self:T(RAT.id..text)
BASE:T(RAT.id..text)
RAT.ATC.init=true
for _,ap in pairs(airports_map) do
local name=ap:GetName()
@@ -5458,7 +5507,7 @@ end
-- @param #string name Group name of the flight.
-- @param #string dest Name of the destination airport.
function RAT:_ATCAddFlight(name, dest)
self:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
RAT.ATC.flight[name]={}
RAT.ATC.flight[name].destination=dest
RAT.ATC.flight[name].Tarrive=-1
@@ -5483,7 +5532,7 @@ end
-- @param #string name Group name of the flight.
-- @param #number time Time the fight first registered.
function RAT:_ATCRegisterFlight(name, time)
self:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
BASE:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
RAT.ATC.flight[name].Tarrive=time
RAT.ATC.flight[name].holding=0
end
@@ -5514,7 +5563,7 @@ function RAT:_ATCStatus()
-- Aircraft is holding.
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
self:T(RAT.id..text)
BASE:T(RAT.id..text)
elseif hold==RAT.ATC.onfinal then
@@ -5522,7 +5571,7 @@ function RAT:_ATCStatus()
local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60)
self:T(RAT.id..text)
BASE:T(RAT.id..text)
elseif hold==RAT.ATC.unregistered then
@@ -5530,7 +5579,7 @@ function RAT:_ATCStatus()
--self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold))
else
self:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
end
end
@@ -5572,12 +5621,12 @@ function RAT:_ATCCheck()
-- Debug message.
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
self:T(RAT.id..text)
BASE:T(RAT.id..text)
else
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
self:T(RAT.id..text)
BASE:T(RAT.id..text)
-- Clear flight for landing.
RAT:_ATCClearForLanding(name, flight)
@@ -5705,12 +5754,7 @@ function RAT:_ATCQueue()
for k,v in ipairs(_queue) do
table.insert(RAT.ATC.airport[airport].queue, v[1])
end
--fvh
--for k,v in ipairs(RAT.ATC.airport[airport].queue) do
--print(string.format("queue #%02i flight \"%s\" holding %d seconds",k, v, RAT.ATC.flight[v].holding))
--end
end
end
@@ -5742,6 +5786,8 @@ end
-- If desired, the @{#RATMANAGER} can be stopped by the @{#RATMANAGER.Stop}(stoptime) function. The parameter "stoptime" specifies the time delay in seconds after which the manager stops.
-- When this happens, no new aircraft will be spawned and the population will eventually decrease to zero.
--
-- When you are using a time intervall like @{#RATMANAGER.dTspawn}(delay), @{#RATMANAGER} will ignore the amount set with @{#RATMANAGER.New}(). @{#RATMANAGER.dTspawn}(delay) will spawn infinite groups.
--
-- ## Example
-- In this example, three different @{#RAT} objects are created (but not spawned manually). The @{#RATMANAGER} takes care that at least five aircraft of each type are alive and that the total number of aircraft
-- spawned is 25. The @{#RATMANAGER} is started after 30 seconds and stopped after two hours.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,162 +1,518 @@
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
--
--
-- ===
--
--
-- ## Features:
--
--
-- * 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.
--
-- * 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.
--
-- ===
--
--
-- ## Missions:
--
--
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
--
--
-- ===
--
-- ### Authors: **FlightControl**
--
--
-- ### Authors: **FlightControl**, **applevangelist**
--
-- Last Update: Feb 2022
--
-- ===
--
--
-- @module Functional.Sead
-- @image SEAD.JPG
--- @type SEAD
---
-- @type SEAD
-- @extends Core.Base#BASE
--- 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.
-- 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:
--
--
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
--
--
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
--
--
-- @field #SEAD
SEAD = {
ClassName = "SEAD",
TargetSkill = {
Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } ,
Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } ,
High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } ,
Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } }
},
SEADGroupPrefixes = {}
ClassName = "SEAD",
TargetSkill = {
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
High = { Evade = 15, DelayOn = { 20, 40 } } ,
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
},
SEADGroupPrefixes = {},
SuppressedGroups = {},
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
Padding = 10,
CallBack = nil,
UseCallBack = false,
debug = false,
}
--- Missile enumerators
-- @field Harms
SEAD.Harms = {
["AGM_88"] = "AGM_88",
["AGM_45"] = "AGM_45",
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["ALARM"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
["X_28"] = "X_28",
["X_25"] = "X_25",
["X_31"] = "X_31",
["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154",
}
--- Missile enumerators - from DCS ME and Wikipedia
-- @field HarmData
SEAD.HarmData = {
-- km and mach
["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.8},
["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4},
["X_58"] = { 70, 4},
["X_28"] = { 80, 2.5},
["X_25"] = { 25, 0.76},
["X_31"] = {150, 3},
["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.
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
-- Chances are big that the missile will miss.
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken.
-- @return SEAD
-- @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 #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
-- @return #SEAD self
-- @usage
-- -- CCCP SEAD Defenses
-- -- 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' } )
function SEAD:New( SEADGroupPrefixes )
local self = BASE:Inherit( self, BASE:New() )
self:F( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
end
else
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
end
self:HandleEvent( EVENTS.Shot )
return self
function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, FSM:New() )
self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
end
else
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
end
local padding = Padding or 10
if padding < 10 then padding = 10 end
self.Padding = padding
self.UseEmissionsOnOff = true
self.debug = false
self.CallBack = nil
self.UseCallBack = false
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
-- Start State.
self:SetStartState("Running")
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.3")
return self
end
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @see SEAD
-- @param #SEAD
--- Update the active SEAD Set (while running)
-- @param #SEAD self
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
-- @return #SEAD self
function SEAD:UpdateSet( SEADGroupPrefixes )
self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
end
else
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
end
return self
end
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
-- @param #SEAD self
-- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
-- @return #SEAD self
function SEAD:SetEngagementRange(range)
self:T( { range } )
range = range or 75
if range < 0 or range > 100 then
range = 75
end
self.EngagementRange = range
self:T(string.format("*** SEAD - Engagement range set to %s",range))
return self
end
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
-- @param #SEAD self
-- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
-- @return #SEAD self
function SEAD:SetPadding(Padding)
self:T( { Padding } )
local padding = Padding or 10
if padding < 10 then padding = 10 end
self.Padding = padding
return self
end
--- 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 #string WeaponName
-- @return #boolean Returns true for a match
-- @return #string name Name of hit in table
function SEAD:_CheckHarms(WeaponName)
self:T( { WeaponName } )
local hit = false
local name = ""
for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1,true) then
hit = true
name = _name
break
end
end
return hit, name
end
--- (Internal) Return distance in meters between two coordinates or -1 on error.
-- @param #SEAD self
-- @param Core.Point#COORDINATE _point1 Coordinate one
-- @param Core.Point#COORDINATE _point2 Coordinate two
-- @return #number Distance in meters
function SEAD:_GetDistance(_point1, _point2)
self:T("_GetDistance")
if _point1 and _point2 then
local distance1 = _point1:Get2DDistance(_point2)
local distance2 = _point1:DistanceFromPointVec2(_point2)
--self:T({dist1=distance1, dist2=distance2})
if distance1 and type(distance1) == "number" then
return distance1
elseif distance2 and type(distance2) == "number" then
return distance2
else
self:E("*****Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
end
--- (Internal) Calculate hit zone of an AGM-88
-- @param #SEAD self
-- @param #table SEADWeapon DCS.Weapon object
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @param #string SEADWeaponName Weapon Name
-- @return #SEAD self
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
if SEADWeapon and SEADWeapon:isExist() then
--local pos = SEADWeapon:getPoint()
-- postion and height
local position = SEADWeapon:getPosition()
local mheight = height
-- heading
local wph = math.atan2(position.x.z, position.x.x)
if wph < 0 then
wph=wph+2*math.pi
end
wph=math.deg(wph)
-- velocity
local wpndata = SEAD.HarmData["AGM_88"]
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 _targetskill = "Random"
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
end
--end
end
end
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
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
--self:T( _targetskill )
if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T("*** SEAD - Evading")
-- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
-- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
local wpnspeed = 666 -- ;)
local reach = 10
if hit then
local wpndata = SEAD.HarmData[data]
reach = wpndata[1] * 1,1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
end
-- time to impact
local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km
else
_distance = 0
end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
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")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name,attacker)
end
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
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:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end
-- randomize switch-on time
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 _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
local _targetgroupname = _targetgroup:GetName()
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname, SEADGroup},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime, SEADGroup)
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
function SEAD:OnEventShot( EventData )
self:F( { 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
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
-- Start of the 2nd loop
self:T( "Missile Launched = " .. SEADWeaponName )
if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
local _evade = math.random (1,100) -- random number for chance of evading action
local _targetMim = EventData.Weapon:getTarget() -- Identify target
local _targetMimname = Unit.getName(_targetMim)
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
local _targetMimgroupName = _targetMimgroup:getName()
local _targetMimcont= _targetMimgroup:getController()
local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill
self:T( self.SEADGroupPrefixes )
self:T( _targetMimgroupName )
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
SEADGroupFound = true
self:T( 'Group Found' )
break
end
end
if SEADGroupFound == true then
if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
self:T( _targetskill )
if self.TargetSkill[_targetskill] then
if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T( string.format("Evading, target skill " ..string.format(_targetskill)) )
local _targetMim = Weapon.getTarget(SEADWeapon)
local _targetMimname = Unit.getName(_targetMim)
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
local _targetMimcont= _targetMimgroup:getController()
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
local function SuppressionEnd1(id)
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
SuppressedGroups1[id.groupName] = nil
end
local id = {
groupName = _targetMimgroup,
ctrl = _targetMimcont
}
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
if SuppressedGroups1[id.groupName] == nil then
SuppressedGroups1[id.groupName] = {
SuppressionEndTime1 = timer.getTime() + delay1,
SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function
}
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function
--trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20)
end
local SuppressedGroups = {}
local function SuppressionEnd(id)
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
SuppressedGroups[id.groupName] = nil
end
local id = {
groupName = _targetMimgroup,
ctrl = _targetMimcont
}
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if SuppressedGroups[id.groupName] == nil then
SuppressedGroups[id.groupName] = {
SuppressionEndTime = timer.getTime() + delay,
SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function
}
timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function
--trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20)
end
end
end
end
end
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
-- 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

View File

@@ -0,0 +1,548 @@
--- **Functional** -- Short Range Air Defense System
--
-- ===
--
-- **SHORAD** - Short Range Air Defense System
-- Controls a network of short range air/missile defense groups.
--
-- ===
--
-- ## Missions:
--
-- ### [SHORAD - Short Range Air Defense](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SRD%20-%20SHORAD%20Defense)
--
-- ===
--
-- ### Author : **applevangelist **
--
-- @module Functional.Shorad
-- @image Functional.Shorad.jpg
--
-- Date: July 2021
-------------------------------------------------------------------------
--- **SHORAD** class, extends Core.Base#BASE
-- @type SHORAD
-- @field #string ClassName
-- @field #string name Name of this Shorad
-- @field #boolean debug Set the debug state
-- @field #string Prefixes String to be used to build the @{#Core.Set#SET_GROUP}
-- @field #number Radius Shorad defense radius in meters
-- @field Core.Set#SET_GROUP Groupset The set of Shorad groups
-- @field Core.Set#SET_GROUP Samset The set of SAM groups to defend
-- @field #string Coalition The coalition of this Shorad
-- @field #number ActiveTimer How long a Shorad stays active after wake-up in seconds
-- @field #table ActiveGroups Table for the timer function
-- @field #string lid The log ID for the dcs.log
-- @field #boolean DefendHarms Default true, intercept incoming HARMS
-- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles
-- @field #number DefenseLowProb Default 70, minimum detection limit
-- @field #number DefenseHighProb Default 90, maximim detection limit
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
-- @extends Core.Base#BASE
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
--
-- Simple Class for a more intelligent Short Range Air Defense System
--
-- #SHORAD
-- Moose derived missile intercepting short range defense system.
-- Protects a network of SAM sites. Uses events to switch on the defense groups closest to the enemy.
-- Easily integrated with @{Functional.Mantis#MANTIS} to complete the defensive system setup.
--
-- ## Usage
--
-- Set up a #SET_GROUP for the SAM sites to be protected:
--
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Red SAM"):FilterCoalitions("red"):FilterStart()`
--
-- By default, SHORAD will defense against both HARMs and AG-Missiles with short to medium range. The default defense probability is 70-90%.
-- When a missile is detected, SHORAD will activate defense groups in the given radius around the target for 10 minutes. It will *not* react to friendly fire.
--
-- ### Start a new SHORAD system, parameters are:
--
-- * Name: Name of this SHORAD.
-- * ShoradPrefix: Filter for the Shorad #SET_GROUP.
-- * Samset: The #SET_GROUP of SAM sites to defend.
-- * Radius: Defense radius in meters.
-- * ActiveTimer: Determines how many seconds the systems stay on red alert after wake-up call.
-- * Coalition: Coalition, i.e. "blue", "red", or "neutral".*
--
-- `myshorad = SHORAD:New("RedShorad", "Red SHORAD", SamSet, 25000, 600, "red")`
--
-- ## Customize options
--
-- * SHORAD:SwitchDebug(debug)
-- * SHORAD:SwitchHARMDefense(onoff)
-- * SHORAD:SwitchAGMDefense(onoff)
-- * SHORAD:SetDefenseLimits(low,high)
-- * SHORAD:SetActiveTimer(seconds)
-- * SHORAD:SetDefenseRadius(meters)
--
-- @field #SHORAD
SHORAD = {
ClassName = "SHORAD",
name = "MyShorad",
debug = false,
Prefixes = "",
Radius = 20000,
Groupset = nil,
Samset = nil,
Coalition = nil,
ActiveTimer = 600, --stay on 10 mins
ActiveGroups = {},
lid = "",
DefendHarms = true,
DefendMavs = true,
DefenseLowProb = 70,
DefenseHighProb = 90,
UseEmOnOff = false,
}
-----------------------------------------------------------------------
-- SHORAD System
-----------------------------------------------------------------------
do
-- TODO Complete list?
--- Missile enumerators
-- @field Harms
SHORAD.Harms = {
["AGM_88"] = "AGM_88",
["AGM_45"] = "AGM_45",
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["ALARM"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
["X_28"] = "X_28",
["X_25"] = "X_25",
["X_31"] = "X_31",
["Kh25"] = "Kh25",
}
--- TODO complete list?
-- @field Mavs
SHORAD.Mavs = {
["AGM"] = "AGM",
["C-701"] = "C-701",
["Kh25"] = "Kh25",
["Kh29"] = "Kh29",
["Kh31"] = "Kh31",
["Kh66"] = "Kh66",
}
--- Instantiates a new SHORAD object
-- @param #SHORAD self
-- @param #string Name Name of this SHORAD
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
-- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
-- @param #number Radius Defense radius in meters, used to switch on groups
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
-- @retunr #SHORAD self
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
local self = BASE:Inherit( self, BASE:New() )
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
self.name = Name or "MyShorad"
self.Prefixes = ShoradPrefix or "SAM SHORAD"
self.Radius = Radius or 20000
self.Coalition = Coalition or "blue"
self.Samset = Samset or GroupSet
self.ActiveTimer = ActiveTimer or 600
self.ActiveGroups = {}
self.Groupset = GroupSet
self.DefendHarms = true
self.DefendMavs = true
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
self:I("*** SHORAD - Started Version 0.2.8")
-- Set the string id for output to DCS.log file.
self.lid=string.format("SHORAD %s | ", self.name)
self:_InitState()
self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
return self
end
--- Initially set all groups to alarm state GREEN
-- @param #SHORAD self
function SHORAD:_InitState()
self:T(self.lid .. " _InitState")
local table = {}
local set = self.Groupset
self:T({set = set})
local aliveset = set:GetAliveSet() --#table
for _,_group in pairs (aliveset) do
if self.UseEmOnOff then
--_group:SetAIOff()
_group:EnableEmission(false)
_group:OptionAlarmStateRed() --Wrapper.Group#GROUP
else
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
end
_group:OptionDisperseOnAttack(30)
end
-- gather entropy
for i=1,100 do
math.random()
end
return self
end
--- Switch debug state on
-- @param #SHORAD self
-- @param #boolean debug Switch debug on (true) or off (false)
function SHORAD:SwitchDebug(onoff)
self:T( { onoff } )
if onoff then
self:SwitchDebugOn()
else
self.SwitchDebugOff()
end
return self
end
--- Switch debug state on
-- @param #SHORAD self
function SHORAD:SwitchDebugOn()
self.debug = true
--tracing
BASE:TraceOn()
BASE:TraceClass("SHORAD")
return self
end
--- Switch debug state off
-- @param #SHORAD self
function SHORAD:SwitchDebugOff()
self.debug = false
BASE:TraceOff()
return self
end
--- Switch defense for HARMs
-- @param #SHORAD self
-- @param #boolean onoff
function SHORAD:SwitchHARMDefense(onoff)
self:T( { onoff } )
local onoff = onoff or true
self.DefendHarms = onoff
return self
end
--- Switch defense for AGMs
-- @param #SHORAD self
-- @param #boolean onoff
function SHORAD:SwitchAGMDefense(onoff)
self:T( { onoff } )
local onoff = onoff or true
self.DefendMavs = onoff
return self
end
--- Set defense probability limits
-- @param #SHORAD self
-- @param #number low Minimum detection limit, integer 1-100
-- @param #number high Maximum detection limit integer 1-100
function SHORAD:SetDefenseLimits(low,high)
self:T( { low, high } )
local low = low or 70
local high = high or 90
if (low < 0) or (low > 100) or (low > high) then
low = 70
end
if (high < 0) or (high > 100) or (high < low ) then
high = 90
end
self.DefenseLowProb = low
self.DefenseHighProb = high
return self
end
--- Set the number of seconds a SHORAD site will stay active
-- @param #SHORAD self
-- @param #number seconds Number of seconds systems stay active
function SHORAD:SetActiveTimer(seconds)
self:T(self.lid .. " SetActiveTimer")
local timer = seconds or 600
if timer < 0 then
timer = 600
end
self.ActiveTimer = timer
return self
end
--- Set the number of meters for the SHORAD defense zone
-- @param #SHORAD self
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
function SHORAD:SetDefenseRadius(meters)
self:T(self.lid .. " SetDefenseRadius")
local radius = meters or 20000
if radius < 0 then
radius = 20000
end
self.Radius = radius
return self
end
--- Set using Emission on/off instead of changing alarm state
-- @param #SHORAD self
-- @param #boolean switch Decide if we are changing alarm state or AI state
function SHORAD:SetUsingEmOnOff(switch)
self:T(self.lid .. " SetUsingEmOnOff")
self.UseEmOnOff = switch or false
return self
end
--- Check if a HARM was fired
-- @param #SHORAD self
-- @param #string WeaponName
-- @return #boolean Returns true for a match
function SHORAD:_CheckHarms(WeaponName)
self:T(self.lid .. " _CheckHarms")
self:T( { WeaponName } )
local hit = false
if self.DefendHarms then
for _,_name in pairs (SHORAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end
end
end
return hit
end
--- Check if an AGM was fired
-- @param #SHORAD self
-- @param #string WeaponName
-- @return #boolean Returns true for a match
function SHORAD:_CheckMavs(WeaponName)
self:T(self.lid .. " _CheckMavs")
self:T( { WeaponName } )
local hit = false
if self.DefendMavs then
for _,_name in pairs (SHORAD.Mavs) do
if string.find(WeaponName,_name,1) then hit = true end
end
end
return hit
end
--- Check the coalition of the attacker
-- @param #SHORAD self
-- @param #string Coalition name
-- @return #boolean Returns false for a match
function SHORAD:_CheckCoalition(Coalition)
self:T(self.lid .. " _CheckCoalition")
local owncoalition = self.Coalition
local othercoalition = ""
if Coalition == 0 then
othercoalition = "neutral"
elseif Coalition == 1 then
othercoalition = "red"
else
othercoalition = "blue"
end
self:T({owncoalition = owncoalition, othercoalition = othercoalition})
if owncoalition ~= othercoalition then
return true
else
return false
end
end
--- Check if the missile is aimed at a SHORAD
-- @param #SHORAD self
-- @param #string TargetGroupName Name of the target group
-- @return #boolean Returns true for a match, else false
function SHORAD:_CheckShotAtShorad(TargetGroupName)
self:T(self.lid .. " _CheckShotAtShorad")
local tgtgrp = TargetGroupName
local shorad = self.Groupset
local shoradset = shorad:GetAliveSet() --#table
local returnname = false
for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1) then
returnname = true
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
end
end
return returnname
end
--- Check if the missile is aimed at a SAM site
-- @param #SHORAD self
-- @param #string TargetGroupName Name of the target group
-- @return #boolean Returns true for a match, else false
function SHORAD:_CheckShotAtSams(TargetGroupName)
self:T(self.lid .. " _CheckShotAtSams")
local tgtgrp = TargetGroupName
local shorad = self.Samset
--local shoradset = shorad:GetAliveSet() --#table
local shoradset = shorad:GetSet() --#table
local returnname = false
for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1) then
returnname = true
end
end
return returnname
end
--- Calculate if the missile shot is detected
-- @param #SHORAD self
-- @return #boolean Returns true for a detection, else false
function SHORAD:_ShotIsDetected()
self:T(self.lid .. " _ShotIsDetected")
local IsDetected = false
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
local ActualDetection = math.random(1,100) -- value for this shot
if ActualDetection <= DetectionProb then
IsDetected = true
end
return IsDetected
end
--- Wake up #SHORADs in a zone with diameter Radius for ActiveTimer seconds
-- @param #SHORAD self
-- @param #string TargetGroup Name of the target group used to build the #ZONE
-- @param #number Radius Radius of the #ZONE
-- @param #number ActiveTimer Number of seconds to stay active
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
-- @usage Use this function to integrate with other systems, example
--
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
-- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
-- myshorad:SwitchDebug(true)
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
-- mymantis:AddShorad(myshorad,720)
-- mymantis:Start()
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
self:T(self.lid .. " WakeUpShorad")
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
local targetcat = TargetCat or Object.Category.UNIT
local targetgroup = TargetGroup
local targetvec2 = nil
if targetcat == Object.Category.UNIT then
targetvec2 = GROUP:FindByName(targetgroup):GetVec2()
elseif targetcat == Object.Category.STATIC then
targetvec2 = STATIC:FindByName(targetgroup,false):GetVec2()
else
local samset = self.Samset
local sam = samset:GetRandom()
targetvec2 = sam:GetVec2()
end
local targetzone = ZONE_RADIUS:New("Shorad",targetvec2,Radius) -- create a defense zone to check
local groupset = self.Groupset --Core.Set#SET_GROUP
local shoradset = groupset:GetAliveSet() --#table
-- local function to switch off shorad again
local function SleepShorad(group)
local groupname = group:GetName()
self.ActiveGroups[groupname] = nil
if self.UseEmOnOff then
group:EnableEmission(false)
--group:SetAIOff()
else
group:OptionAlarmStateGreen()
end
local text = string.format("Sleeping SHORAD %s", group:GetName())
self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
end
-- go through set and find the one(s) to activate
for _,_group in pairs (shoradset) do
if _group:IsAnyInZone(targetzone) then
local text = string.format("Waking up SHORAD %s", _group:GetName())
self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
if self.UseEmOnOff then
--_group:SetAIOn()
_group:EnableEmission(true)
end
_group:OptionAlarmStateRed()
local groupname = _group:GetName()
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
timer.scheduleFunction(SleepShorad, _group, endtime)
end
end
end
return self
end
--- Main function - work on the EventData
-- @param #SHORAD self
-- @param Core.Event#EVENTDATA EventData The event details table data set
function SHORAD:HandleEventShot( EventData )
self:T( { EventData } )
self:T(self.lid .. " HandleEventShot")
--local ShootingUnit = EventData.IniDCSUnit
--local ShootingUnitName = EventData.IniDCSUnitName
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
local ShootingWeaponName = EventData.WeaponName -- return weapon type
-- get firing coalition
local weaponcoalition = EventData.IniGroup:GetCoalition()
-- get detection probability
if self:_CheckCoalition(weaponcoalition) then --avoid overhead on friendly fire
local IsDetected = self:_ShotIsDetected()
-- convert to text
local DetectedText = "false"
if IsDetected then
DetectedText = "true"
end
local text = string.format("%s Missile Launched = %s | Detected probability state is %s", self.lid, ShootingWeaponName, DetectedText)
self:T( text )
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
--
if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
-- get target data
local targetdata = EventData.Weapon:getTarget() -- Identify target
local targetcat = targetdata:getCategory() -- Identify category
self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
local targetunit = nil
if targetcat == Object.Category.UNIT then -- UNIT
targetunit = UNIT:Find(targetdata)
elseif targetcat == Object.Category.STATIC then -- STATIC
targetunit = STATIC:Find(targetdata)
end
--local targetunitname = Unit.getName(targetdata) -- Unit name
if targetunit and targetunit:IsAlive() then
local targetunitname = targetunit:GetName()
--local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group
local targetgroup = nil
local targetgroupname = "none"
if targetcat == Object.Category.UNIT then
targetgroup = targetunit:GetGroup()
targetgroupname = targetgroup:GetName() -- group name
elseif targetcat == Object.Category.STATIC then
targetgroup = targetunit
targetgroupname = targetunitname
end
local text = string.format("%s Missile Target = %s", self.lid, tostring(targetgroupname))
self:T( text )
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
-- check if we or a SAM site are the target
--local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
-- if being shot at, find closest SHORADs to activate
if shotatsams or shotatus then
self:T({shotatsams=shotatsams,shotatus=shotatus})
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
end
end
end
end
end
--
end
-----------------------------------------------------------------------
-- SHORAD end
-----------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions: **Millertime** - Concept
-- ### Contributions: **Millertime** - Concept, **funkyfranky**
--
-- ===
--
@@ -49,6 +49,15 @@
do -- ZONE_CAPTURE_COALITION
--- @type ZONE_CAPTURE_COALITION
-- @field #string ClassName Name of the class.
-- @field #number MarkBlue ID of blue F10 mark.
-- @field #number MarkRed ID of red F10 mark.
-- @field #number StartInterval Time in seconds after the status monitor is started.
-- @field #number RepeatInterval Time in seconds after which the zone status is updated.
-- @field #boolean HitsOn If true, hit events are monitored and trigger the "Attack" event when a defending unit is hit.
-- @field #number HitTimeLast Time stamp in seconds when the last unit inside the zone was hit.
-- @field #number HitTimeAttackOver Time interval in seconds before the zone goes from "Attacked" to "Guarded" state after the last hit.
-- @field #boolean MarkOn If true, create marks of zone status on F10 map.
-- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION
@@ -197,8 +206,7 @@ do -- ZONE_CAPTURE_COALITION
--
-- ### IMPORTANT
--
-- **Each capture zone object must have the monitoring process started specifically.
-- The monitoring process is NOT started by default!!!**
-- **Each capture zone object must have the monitoring process started specifically. The monitoring process is NOT started by default!**
--
--
-- # Full Example
@@ -338,29 +346,48 @@ do -- ZONE_CAPTURE_COALITION
--
-- @field #ZONE_CAPTURE_COALITION
ZONE_CAPTURE_COALITION = {
ClassName = "ZONE_CAPTURE_COALITION",
ClassName = "ZONE_CAPTURE_COALITION",
MarkBlue = nil,
MarkRed = nil,
StartInterval = nil,
RepeatInterval = nil,
HitsOn = nil,
HitTimeLast = nil,
HitTimeAttackOver = nil,
MarkOn = nil,
}
--- @field #table ZONE_CAPTURE_COALITION.States
ZONE_CAPTURE_COALITION.States = {}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor and Start/Stop Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- ZONE_CAPTURE_COALITION Constructor.
-- @param #ZONE_CAPTURE_COALITION self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
-- @return #ZONE_CAPTURE_COALITION
-- @usage
--
-- AttackZone = ZONE:New( "AttackZone" )
--
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED, {UNITS ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
-- ZoneCaptureCoalition:__Guard( 1 ) -- Start the Guarding of the AttackZone.
--
function ZONE_CAPTURE_COALITION:New( Zone, Coalition )
function ZONE_CAPTURE_COALITION:New( Zone, Coalition, UnitCategories, ObjectCategories )
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition ) ) -- #ZONE_CAPTURE_COALITION
self:F( { Zone = Zone, Coalition = Coalition } )
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) ) -- #ZONE_CAPTURE_COALITION
self:F( { Zone = Zone, Coalition = Coalition, UnitCategories = UnitCategories, ObjectCategories = ObjectCategories } )
self:SetObjectCategories(ObjectCategories)
-- Default is no smoke.
self:SetSmokeZone(false)
-- Default is F10 marks ON.
self:SetMarkZone(true)
-- Start in state "Empty".
self:SetStartState("Empty")
do
@@ -545,174 +572,12 @@ do -- ZONE_CAPTURE_COALITION
-- @param #ZONE_CAPTURE_COALITION self
-- @param #number Delay
-- ZoneGoal objects are added to the _DATABASE.ZONES_GOAL and SET_ZONE_GOAL sets.
_EVENTDISPATCHER:CreateEventNewZoneGoal(self)
return self
end
--- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterCaptured()
self:GetParent( self, ZONE_CAPTURE_COALITION ).onenterCaptured( self )
self.Goal:Achieved()
end
function ZONE_CAPTURE_COALITION:IsGuarded()
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
self:F( { IsGuarded = IsGuarded } )
return IsGuarded
end
function ZONE_CAPTURE_COALITION:IsEmpty()
local IsEmpty = self.Zone:IsNoneInZone()
self:F( { IsEmpty = IsEmpty } )
return IsEmpty
end
function ZONE_CAPTURE_COALITION:IsCaptured()
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:F( { IsCaptured = IsCaptured } )
return IsCaptured
end
function ZONE_CAPTURE_COALITION:IsAttacked()
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
self:F( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Mark.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:Mark()
local Coord = self.Zone:GetCoordinate()
local ZoneName = self:GetZoneName()
local State = self:GetState()
if self.MarkRed and self.MarkBlue then
self:F( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
Coord:RemoveMark( self.MarkRed )
Coord:RemoveMark( self.MarkBlue )
end
if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
else
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
end
end
--- Bound.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterGuarded()
--self:GetParent( self ):onenterGuarded()
if self.Coalition == coalition.side.BLUE then
--elf.ProtectZone:BoundZone( 12, country.id.USA )
else
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
end
self:Mark()
end
function ZONE_CAPTURE_COALITION:onenterCaptured()
--self:GetParent( self ):onenterCaptured()
local NewCoalition = self.Zone:GetScannedCoalition()
self:F( { NewCoalition = NewCoalition } )
self:SetCoalition( NewCoalition )
self:Mark()
end
function ZONE_CAPTURE_COALITION:onenterEmpty()
--self:GetParent( self ):onenterEmpty()
self:Mark()
end
function ZONE_CAPTURE_COALITION:onenterAttacked()
--self:GetParent( self ):onenterAttacked()
self:Mark()
end
--- When started, check the Coalition status.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onafterGuard()
--self:F({BASE:GetParent( self )})
--BASE:GetParent( self ).onafterGuard( self )
if not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
end
end
function ZONE_CAPTURE_COALITION:IsCaptured()
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:F( { IsCaptured = IsCaptured } )
return IsCaptured
end
function ZONE_CAPTURE_COALITION:IsAttacked()
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
self:F( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Check status Coalition ownership.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:StatusZone()
local State = self:GetState()
self:F( { State = self:GetState() } )
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
if State ~= "Guarded" and self:IsGuarded() then
self:Guard()
end
if State ~= "Empty" and self:IsEmpty() then
self:Empty()
end
if State ~= "Attacked" and self:IsAttacked() then
self:Attack()
end
if State ~= "Captured" and self:IsCaptured() then
self:Capture()
end
end
--- Starts the zone capturing monitoring process.
-- This process can be CPU intensive, ensure that you specify reasonable time intervals for the monitoring process.
@@ -723,6 +588,7 @@ do -- ZONE_CAPTURE_COALITION
-- @param #ZONE_CAPTURE_COALITION self
-- @param #number StartInterval (optional) Specifies the start time interval in seconds when the zone state will be checked for the first time.
-- @param #number RepeatInterval (optional) Specifies the repeat time interval in seconds when the zone state will be checked repeatedly.
-- @return #ZONE_CAPTURE_COALITION self
-- @usage
--
-- -- Setup the zone.
@@ -737,13 +603,23 @@ do -- ZONE_CAPTURE_COALITION
--
function ZONE_CAPTURE_COALITION:Start( StartInterval, RepeatInterval )
StartInterval = StartInterval or 15
RepeatInterval = RepeatInterval or 15
self.StartInterval = StartInterval or 1
self.RepeatInterval = RepeatInterval or 15
if self.ScheduleStatusZone then
self:ScheduleStop( self.ScheduleStatusZone )
end
self.ScheduleStatusZone = self:ScheduleRepeat( StartInterval, RepeatInterval, 0.1, nil, self.StatusZone, self )
-- Start Status scheduler.
self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusZone, self )
-- We check if a unit within the zone is hit. If it is, then we must move the zone to attack state.
self:HandleEvent(EVENTS.Hit, self.OnEventHit)
-- Create mark on F10 map.
self:Mark()
return self
end
@@ -785,9 +661,283 @@ do -- ZONE_CAPTURE_COALITION
function ZONE_CAPTURE_COALITION:Stop()
if self.ScheduleStatusZone then
self:ScheduleStop( self.ScheduleStatusZone )
self:ScheduleStop(self.ScheduleStatusZone)
end
if self.SmokeScheduler then
self:ScheduleStop(self.SmokeScheduler)
end
self:UnHandleEvent(EVENTS.Hit)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User API Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set whether hit events of defending units are monitored and trigger "Attack" events.
-- @param #ZONE_CAPTURE_COALITION self
-- @param #boolean Switch If *true*, hit events are monitored. If *false* or *nil*, hit events are not monitored.
-- @param #number TimeAttackOver (Optional) Time in seconds after an attack is over after the last hit and the zone state goes to "Guarded". Default is 300 sec = 5 min.
-- @return #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:SetMonitorHits(Switch, TimeAttackOver)
self.HitsOn=Switch
self.HitTimeAttackOver=TimeAttackOver or 5*60
return self
end
--- Set whether marks on the F10 map are shown, which display the current zone status.
-- @param #ZONE_CAPTURE_COALITION self
-- @param #boolean Switch If *true* or *nil*, marks are shown. If *false*, marks are not displayed.
-- @return #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:SetMarkZone(Switch)
if Switch==nil or Switch==true then
self.MarkOn=true
else
self.MarkOn=false
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DCS Event Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Monitor hit events.
-- @param #ZONE_CAPTURE_COALITION self
-- @param Core.Event#EVENTDATA EventData The event data.
function ZONE_CAPTURE_COALITION:OnEventHit( EventData )
if self.HitsOn then
local UnitHit = EventData.TgtUnit
if UnitHit.ClassName ~= "SCENERY" then
-- Check if unit is inside the capture zone and that it is of the defending coalition.
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
-- Update last hit time.
self.HitTimeLast=timer.getTime()
-- Only trigger attacked event if not already in state "Attacked".
if self:GetState()~="Attacked" then
self:F2("Hit ==> Attack")
self:Attack()
end
end
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Event Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "Guard" event.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onafterGuard()
self:F2("After Guard")
if self.SmokeZone and not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self )
end
end
end
--- On enter "Guarded" state.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterGuarded()
self:F2("Enter Guarded")
self:Mark()
end
--- On enter "Captured" state.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterCaptured()
self:F2("Enter Captured")
-- Get new coalition.
local NewCoalition = self:GetScannedCoalition()
self:F( { NewCoalition = NewCoalition } )
-- Set new owner of zone.
self:SetCoalition(NewCoalition)
-- Update mark.
self:Mark()
-- Goal achieved.
self.Goal:Achieved()
end
--- On enter "Empty" state.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterEmpty()
self:F2("Enter Empty")
self:Mark()
end
--- On enter "Attacked" state.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterAttacked()
self:F2("Enter Attacked")
self:Mark()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Status Check Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Check if zone is "Empty".
-- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsNoneInZone()
function ZONE_CAPTURE_COALITION:IsEmpty()
local IsEmpty = self:IsNoneInZone()
self:F( { IsEmpty = IsEmpty } )
return IsEmpty
end
--- Check if zone is "Guarded", i.e. only one (the defending) coalition is present inside the zone.
-- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsGuarded()
local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition )
self:F( { IsGuarded = IsGuarded } )
return IsGuarded
end
--- Check if zone is "Captured", i.e. another coalition took control over the zone and is the only one present.
-- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsAllInZoneOfOtherCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsCaptured()
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
self:F( { IsCaptured = IsCaptured } )
return IsCaptured
end
--- Check if zone is "Attacked", i.e. another coalition entered the zone.
-- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsAttacked()
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
self:F( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Check status Coalition ownership.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:StatusZone()
-- Get FSM state.
local State = self:GetState()
-- Scan zone in parent class ZONE_GOAL_COALITION
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
local Tnow=timer.getTime()
-- Check if zone is guarded.
if State ~= "Guarded" and self:IsGuarded() then
-- Check that there was a sufficient amount of time after the last hit before going back to "Guarded".
if self.HitTimeLast==nil or Tnow>=self.HitTimeLast+self.HitTimeAttackOver then
self:Guard()
self.HitTimeLast=nil
end
end
-- Check if zone is empty.
if State ~= "Empty" and self:IsEmpty() then
self:Empty()
end
-- Check if zone is attacked.
if State ~= "Attacked" and self:IsAttacked() then
self:Attack()
end
-- Check if zone is captured.
if State ~= "Captured" and self:IsCaptured() then
self:Capture()
end
-- Count stuff in zone.
local unitset=self:GetScannedSetUnit()
local nRed=0
local nBlue=0
for _,object in pairs(unitset:GetSet()) do
local coal=object:GetCoalition()
if object:IsAlive() then
if coal==coalition.side.RED then
nRed=nRed+1
elseif coal==coalition.side.BLUE then
nBlue=nBlue+1
end
end
end
-- Status text.
if false then
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
local NewState = self:GetState()
if NewState~=State then
text=text..string.format(" --> %s", NewState)
end
self:I(text)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Update Mark on F10 map.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:Mark()
if self.MarkOn then
local Coord = self:GetCoordinate()
local ZoneName = self:GetZoneName()
local State = self:GetState()
-- Remove marks.
if self.MarkRed then
Coord:RemoveMark(self.MarkRed)
end
if self.MarkBlue then
Coord:RemoveMark(self.MarkBlue)
end
-- Create new marks for each coalition.
if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
elseif self.Coalition == coalition.side.RED then
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
else
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
end
end
end
end

View File

@@ -8,6 +8,7 @@
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions: **funkyfranky**
--
-- ===
--
@@ -17,10 +18,16 @@
do -- Zone
--- @type ZONE_GOAL
-- @extends Core.Fsm#FSM
-- @field #string ClassName Name of the class.
-- @field Core.Goal#GOAL Goal The goal object.
-- @field #number SmokeTime Time stamp in seconds when the last smoke of the zone was triggered.
-- @field Core.Scheduler#SCHEDULER SmokeScheduler Scheduler responsible for smoking the zone.
-- @field #number SmokeColor Color of the smoke.
-- @field #boolean SmokeZone If true, smoke zone.
-- @extends Core.Zone#ZONE_RADIUS
-- Models processes that have a Goal with a defined achievement involving a Zone.
--- Models processes that have a Goal with a defined achievement involving a Zone.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ## 1. ZONE_GOAL constructor
@@ -39,24 +46,41 @@ do -- Zone
--
-- @field #ZONE_GOAL
ZONE_GOAL = {
ClassName = "ZONE_GOAL",
ClassName = "ZONE_GOAL",
Goal = nil,
SmokeTime = nil,
SmokeScheduler = nil,
SmokeColor = nil,
SmokeZone = nil,
}
--- ZONE_GOAL Constructor.
-- @param #ZONE_GOAL self
-- @param Core.Zone#ZONE_BASE Zone A @{Zone} object with the goal to be achieved.
-- @param Core.Zone#ZONE_RADIUS Zone A @{Zone} object with the goal to be achieved.
-- @return #ZONE_GOAL
function ZONE_GOAL:New( Zone )
local self = BASE:Inherit( self, FSM:New() ) -- #ZONE_GOAL
local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL
self:F( { Zone = Zone } )
self.Zone = Zone -- Core.Zone#ZONE_BASE
-- Goal object.
self.Goal = GOAL:New()
self.SmokeTime = nil
-- Set smoke ON.
self:SetSmokeZone(true)
self:AddTransition( "*", "DestroyedUnit", "*" )
--- DestroyedUnit event.
-- @function [parent=#ZONE_GOAL] DestroyedUnit
-- @param #ZONE_GOAL self
--- DestroyedUnit delayed event
-- @function [parent=#ZONE_GOAL] __DestroyedUnit
-- @param #ZONE_GOAL self
-- @param #number delay Delay in seconds.
--- DestroyedUnit Handler OnAfter for ZONE_GOAL
-- @function [parent=#ZONE_GOAL] OnAfterDestroyedUnit
@@ -70,52 +94,64 @@ do -- Zone
return self
end
--- Get the Zone
--- Get the Zone.
-- @param #ZONE_GOAL self
-- @return Core.Zone#ZONE_BASE
-- @return #ZONE_GOAL
function ZONE_GOAL:GetZone()
return self.Zone
return self
end
--- Get the name of the ProtectZone
--- Get the name of the Zone.
-- @param #ZONE_GOAL self
-- @return #string
function ZONE_GOAL:GetZoneName()
return self.Zone:GetName()
return self:GetName()
end
--- Smoke the center of theh zone.
--- Activate smoking of zone with the color or the current owner.
-- @param #ZONE_GOAL self
-- @param #SMOKECOLOR.Color SmokeColor
function ZONE_GOAL:Smoke( SmokeColor )
-- @param #boolean switch If *true* or *nil* activate smoke. If *false* or *nil*, no smoke.
-- @return #ZONE_GOAL
function ZONE_GOAL:SetSmokeZone(switch)
self.SmokeZone=switch
--[[
if switch==nil or switch==true then
self.SmokeZone=true
else
self.SmokeZone=false
end
]]
return self
end
--- Set the smoke color.
-- @param #ZONE_GOAL self
-- @param DCS#SMOKECOLOR.Color SmokeColor
function ZONE_GOAL:Smoke( SmokeColor )
self:F( { SmokeColor = SmokeColor} )
self.SmokeColor = SmokeColor
end
--- Flare the center of the zone.
--- Flare the zone boundary.
-- @param #ZONE_GOAL self
-- @param #SMOKECOLOR.Color FlareColor
-- @param DCS#SMOKECOLOR.Color FlareColor
function ZONE_GOAL:Flare( FlareColor )
self.Zone:FlareZone( FlareColor, math.random( 1, 360 ) )
self:FlareZone( FlareColor, 30)
end
--- When started, check the Smoke and the Zone status.
-- @param #ZONE_GOAL self
function ZONE_GOAL:onafterGuard()
--self:GetParent( self ):onafterStart()
self:F("Guard")
--self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
if not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
-- Start smoke
if self.SmokeZone and not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat(1, 1, 0.1, nil, self.StatusSmoke, self)
end
end
@@ -123,44 +159,54 @@ do -- Zone
--- Check status Smoke.
-- @param #ZONE_GOAL self
function ZONE_GOAL:StatusSmoke()
self:F({self.SmokeTime, self.SmokeColor})
local CurrentTime = timer.getTime()
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
if self.SmokeColor then
self.Zone:GetCoordinate():Smoke( self.SmokeColor )
--self.SmokeColor = nil
self.SmokeTime = CurrentTime
if self.SmokeZone then
-- Current time.
local CurrentTime = timer.getTime()
-- Restart smoke every 5 min.
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
if self.SmokeColor then
self:GetCoordinate():Smoke( self.SmokeColor )
self.SmokeTime = CurrentTime
end
end
end
end
--- @param #ZONE_GOAL self
-- @param Core.Event#EVENTDATA EventData
-- @param Core.Event#EVENTDATA EventData Event data table.
function ZONE_GOAL:__Destroyed( EventData )
self:F( { "EventDead", EventData } )
self:F( { EventData.IniUnit } )
local Vec3 = EventData.IniDCSUnit:getPosition().p
self:F( { Vec3 = Vec3 } )
local ZoneGoal = self:GetZone()
self:F({ZoneGoal})
if EventData.IniDCSUnit then
if ZoneGoal:IsVec3InZone(Vec3) then
local Vec3 = EventData.IniDCSUnit:getPosition().p
self:F( { Vec3 = Vec3 } )
if Vec3 and self:IsVec3InZone(Vec3) then
local PlayerHits = _DATABASE.HITS[EventData.IniUnitName]
if PlayerHits then
for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do
self.Goal:AddPlayerContribution( PlayerName )
self:DestroyedUnit( EventData.IniUnitName, PlayerName )
end
end
end
end
end

View File

@@ -1,73 +1,123 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals 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.
--
-- 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.
--
--
-- ===
--
--
-- ### Author: **FlightControl**
--
--
-- ===
--
--
-- @module Functional.ZoneGoalCoalition
-- @image MOOSE.JPG
do -- ZoneGoal
--- @type ZONE_GOAL_COALITION
-- @field #string ClassName Name of the Class.
-- @field #number Coalition The current coalition ID of the zone owner.
-- @field #number PreviousCoalition The previous owner of the zone.
-- @field #table UnitCategories Table of unit categories that are able to capture and hold the zone. Default is only GROUND units.
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
-- @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.
--
--
-- ## 1. ZONE_GOAL_COALITION constructor
--
--
-- * @{#ZONE_GOAL_COALITION.New}(): Creates a new ZONE_GOAL_COALITION object.
--
--
-- ## 2. ZONE_GOAL_COALITION is a finite state machine (FSM).
--
--
-- ### 2.1 ZONE_GOAL_COALITION States
--
--
-- ### 2.2 ZONE_GOAL_COALITION Events
--
--
-- ### 2.3 ZONE_GOAL_COALITION State Machine
--
--
-- @field #ZONE_GOAL_COALITION
ZONE_GOAL_COALITION = {
ClassName = "ZONE_GOAL_COALITION",
Coalition = nil,
PreviousCoalition = nil,
UnitCategories = nil,
ObjectCategories = nil,
}
--- @field #table ZONE_GOAL_COALITION.States
ZONE_GOAL_COALITION.States = {}
--- ZONE_GOAL_COALITION Constructor.
-- @param #ZONE_GOAL_COALITION self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL.
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
-- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:New( Zone, Coalition )
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
if not Zone then
BASE:E( "ERROR: No Zone specified in ZONE_GOAL_COALITION!" )
return nil
end
-- Inherit ZONE_GOAL.
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
self:F( { Zone = Zone, Coalition = Coalition } )
self:F( { Zone = Zone, Coalition = Coalition } )
self:SetCoalition( Coalition )
-- Set initial owner.
self:SetCoalition( Coalition or coalition.side.NEUTRAL )
-- Set default unit and object categories for the zone scan.
self:SetUnitCategories( UnitCategories )
self:SetObjectCategories()
return self
end
--- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @param DCSCoalition.DCSCoalition#coalition Coalition
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
-- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
self.PreviousCoalition = self.Coalition or Coalition
self.Coalition = Coalition
return self
end
--- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
-- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
if UnitCategories and type( UnitCategories ) ~= "table" then
UnitCategories = { UnitCategories }
end
self.UnitCategories = UnitCategories or { Unit.Category.GROUND_UNIT }
return self
end
--- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
-- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
if ObjectCategories and type( ObjectCategories ) ~= "table" then
ObjectCategories = { ObjectCategories }
end
self.ObjectCategories = ObjectCategories or { Object.Category.UNIT, Object.Category.STATIC }
return self
end
--- Get the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
@@ -75,38 +125,37 @@ do -- ZoneGoal
return self.Coalition
end
--- Get the previous coalition, i.e. the one owning the zone before the current one.
-- @param #ZONE_GOAL_COALITION self
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
function ZONE_GOAL_COALITION:GetPreviousCoalition()
return self.PreviousCoalition
end
--- Get the owning coalition name of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @return #string Coalition name.
function ZONE_GOAL_COALITION:GetCoalitionName()
if self.Coalition == coalition.side.BLUE then
return "Blue"
end
if self.Coalition == coalition.side.RED then
return "Red"
end
if self.Coalition == coalition.side.NEUTRAL then
return "Neutral"
end
return ""
return UTILS.GetCoalitionName( self.Coalition )
end
--- Check status Coalition ownership.
-- @param #ZONE_GOAL_COALITION self
-- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:StatusZone()
-- Get current state.
local State = self:GetState()
self:F( { State = self:GetState() } )
self.Zone:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
-- Debug text.
local text = string.format( "Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName() )
self:F( text )
-- Scan zone.
self:Scan( self.ObjectCategories, self.UnitCategories )
return self
end
end

View File

@@ -0,0 +1,46 @@
--- GLOBALS: The order of the declarations is important here. Don't touch it.
--- Declare the event dispatcher based on the EVENT class
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
--- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDULEDISPATCHER
--- Declare the main database object, which is used internally by the MOOSE classes.
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
--- Settings
_SETTINGS = SETTINGS:Set()
_SETTINGS:SetPlayerMenuOn()
--- Register cargos.
_DATABASE:_RegisterCargos()
--- Register zones.
_DATABASE:_RegisterZones()
_DATABASE:_RegisterAirbases()
--- Check if os etc is available.
BASE:I("Checking de-sanitization of os, io and lfs:")
local __na=false
if os then
BASE:I("- os available")
else
BASE:I("- os NOT available! Some functions may not work.")
__na=true
end
if io then
BASE:I("- io available")
else
BASE:I("- io NOT available! Some functions may not work.")
__na=true
end
if lfs then
BASE:I("- lfs available")
else
BASE:I("- lfs NOT available! Some functions may not work.")
__na=true
end
if __na then
BASE:I("Check <DCS install folder>/Scripts/MissionScripting.lua and comment out the lines with sanitizeModule(''). Use at your own risk!)")
end

View File

@@ -0,0 +1,141 @@
__Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Templates.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/Beacon.lua' )
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
__Moose.Include( 'Scripts/Moose/Core/Report.lua' )
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
__Moose.Include( 'Scripts/Moose/Core/ScheduleDispatcher.lua' )
__Moose.Include( 'Scripts/Moose/Core/Event.lua' )
__Moose.Include( 'Scripts/Moose/Core/Settings.lua' )
__Moose.Include( 'Scripts/Moose/Core/Menu.lua' )
__Moose.Include( 'Scripts/Moose/Core/Zone.lua' )
__Moose.Include( 'Scripts/Moose/Core/Zone_Detection.lua' )
__Moose.Include( 'Scripts/Moose/Core/Database.lua' )
__Moose.Include( 'Scripts/Moose/Core/Set.lua' )
__Moose.Include( 'Scripts/Moose/Core/Point.lua' )
__Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Positionable.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Controllable.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Group.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Unit.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoSlingload.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoCrate.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoGroup.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Scoring.lua' )
__Moose.Include( 'Scripts/Moose/Functional/CleanUp.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Movement.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Sead.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Escort.lua' )
__Moose.Include( 'Scripts/Moose/Functional/MissileTrainer.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ATC_Ground.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Detection.lua' )
__Moose.Include( 'Scripts/Moose/Functional/DetectionZones.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Designate.lua' )
__Moose.Include( 'Scripts/Moose/Functional/RAT.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Range.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ZoneCaptureCoalition.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Artillery.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Suppression.lua' )
__Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
__Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' )
__Moose.Include( 'Scripts/Moose/Ops/ATIS.lua' )
__Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' )
__Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Air_Patrol.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Air_Engage.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Patrol.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Cap.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Gci.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_BAI.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_CAS.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_SEAD.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Patrol.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cap.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cas.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Request.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher_Request.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' )
__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' )
__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' )
__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' )
__Moose.Include( 'Scripts/Moose/Sound/UserSound.lua' )
__Moose.Include( 'Scripts/Moose/Sound/SoundOutput.lua' )
__Moose.Include( 'Scripts/Moose/Sound/Radio.lua' )
__Moose.Include( 'Scripts/Moose/Sound/RadioQueue.lua' )
__Moose.Include( 'Scripts/Moose/Sound/RadioSpeech.lua' )
__Moose.Include( 'Scripts/Moose/Sound/SRS.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/TaskInfo.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Manager.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/DetectionManager.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Transport.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_CSAR.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Zone.lua' )
__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Dispatcher.lua' )
__Moose.Include( 'Scripts/Moose/Globals.lua' )

View File

@@ -1,18 +0,0 @@
-- The order of the declarations is important here. Don't touch it.
--- Declare the event dispatcher based on the EVENT class
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
--- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER
--- Declare the main database object, which is used internally by the MOOSE classes.
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
_SETTINGS = SETTINGS:Set()
_SETTINGS:SetPlayerMenuOn()
_DATABASE:_RegisterCargos()
_DATABASE:_RegisterZones()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,394 @@
--- **Sound** - Radio transmissions.
--
-- ===
--
-- ## Features:
--
-- * Provide radio functionality to broadcast radio transmissions.
--
-- What are radio communications in DCS?
--
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
--
-- How to supply DCS my own Sound Files?
--
-- * Your sound files need to be encoded in **.ogg** or .wav,
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
-- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
--
-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE}
--
-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically,
-- * If the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped.
--
-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft,
-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below).
-- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible,
-- you won't hear/be able to use the TACAN beacon informations.
--
-- ===
--
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
--
-- @module Sound.Radio
-- @image Core_Radio.JPG
--- *It's not true I had nothing on, I had the radio on.* -- Marilyn Monroe
--
-- # RADIO usage
--
-- There are 3 steps to a successful radio transmission.
--
-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function,
-- * Then, you will **set the relevant parameters** to the transmission (see below),
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
--
-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE}
--
-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"),
-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission.
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
--
-- Additional Methods to set relevant parameters if the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}
--
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
--
-- Additional Methods to set relevant parameters if the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}
--
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
--
-- What is this power thing?
--
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
-- * If the player gets **too far** from the transmitter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
-- * This an automated DCS calculation you have no say on,
-- * For reference, a standard VOR station has a 100 W antenna, a standard AA TACAN has a 120 W antenna, and civilian ATC's antenna usually range between 300 and 500 W,
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
--
-- @type RADIO
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will transmit the radio calls.
-- @field #string FileName Name of the sound file played.
-- @field #number Frequency Frequency of the transmission in Hz.
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM).
-- @field #string Subtitle Subtitle of the transmission.
-- @field #number SubtitleDuration Duration of the Subtitle in seconds.
-- @field #number Power Power of the antenna is Watts.
-- @field #boolean Loop Transmission is repeated (default true).
-- @field #string alias Name of the radio transmitter.
-- @extends Core.Base#BASE
RADIO = {
ClassName = "RADIO",
FileName = "",
Frequency = 0,
Modulation = radio.modulation.AM,
Subtitle = "",
SubtitleDuration = 0,
Power = 100,
Loop = false,
alias = nil,
}
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast.
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead.
-- @param #RADIO self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #RADIO The RADIO object or #nil if Positionable is invalid.
function RADIO:New(Positionable)
-- Inherit base
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
self:F(Positionable)
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable
return self
end
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
return nil
end
--- Set alias of the transmitter.
-- @param #RADIO self
-- @param #string alias Name of the radio transmitter.
-- @return #RADIO self
function RADIO:SetAlias(alias)
self.alias=tostring(alias)
return self
end
--- Get alias of the transmitter.
-- @param #RADIO self
-- @return #string Name of the transmitter.
function RADIO:GetAlias()
return tostring(self.alias)
end
--- Set the file name for the radio transmission.
-- @param #RADIO self
-- @param #string FileName File name of the sound file (i.e. "Noise.ogg")
-- @return #RADIO self
function RADIO:SetFileName(FileName)
self:F2(FileName)
if type(FileName) == "string" then
if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName
end
self.FileName = FileName
return self
end
end
self:E({"File name invalid. Maybe something wrong with the extension?", FileName})
return self
end
--- Set the frequency for the radio transmission.
-- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation.
-- @param #RADIO self
-- @param #number Frequency Frequency in MHz.
-- @return #RADIO self
function RADIO:SetFrequency(Frequency)
self:F2(Frequency)
if type(Frequency) == "number" then
-- If frequency is in range
-- if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
-- Convert frequency from MHz to Hz
self.Frequency = Frequency * 1000000
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
local commandSetFrequency={
id = "SetFrequency",
params = {
frequency = self.Frequency,
modulation = self.Modulation,
}
}
self:T2(commandSetFrequency)
self.Positionable:SetCommand(commandSetFrequency)
end
return self
-- end
end
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
return self
end
--- Set AM or FM modulation of the radio transmitter.
-- @param #RADIO self
-- @param #number Modulation Modulation is either radio.modulation.AM or radio.modulation.FM.
-- @return #RADIO self
function RADIO:SetModulation(Modulation)
self:F2(Modulation)
if type(Modulation) == "number" then
if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self.Modulation = Modulation
return self
end
end
self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation})
return self
end
--- Check validity of the power passed and sets RADIO.Power
-- @param #RADIO self
-- @param #number Power Power in W.
-- @return #RADIO self
function RADIO:SetPower(Power)
self:F2(Power)
if type(Power) == "number" then
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
else
self:E({"Power is invalid. Power unchanged.", self.Power})
end
return self
end
--- Set message looping on or off.
-- @param #RADIO self
-- @param #boolean Loop If true, message is repeated indefinitely.
-- @return #RADIO self
function RADIO:SetLoop(Loop)
self:F2(Loop)
if type(Loop) == "boolean" then
self.Loop = Loop
return self
end
self:E({"Loop is invalid. Loop unchanged.", self.Loop})
return self
end
--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration
-- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration
-- @param #RADIO self
-- @param #string Subtitle
-- @param #number SubtitleDuration in s
-- @return #RADIO self
-- @usage
-- -- create the broadcaster and attaches it a RADIO
-- local MyUnit = UNIT:FindByName("MyUnit")
-- local MyUnitRadio = MyUnit:GetRadio()
--
-- -- add a subtitle for the next transmission, which will be up for 10s
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
self:F2({Subtitle, SubtitleDuration})
if type(Subtitle) == "string" then
self.Subtitle = Subtitle
else
self.Subtitle = ""
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
end
if type(SubtitleDuration) == "number" then
self.SubtitleDuration = SubtitleDuration
else
self.SubtitleDuration = 0
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
end
return self
end
--- Create a new transmission, that is to say, populate the RADIO with relevant data
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
-- but it will work with a UNIT or a GROUP anyway.
-- Only the #RADIO and the Filename are mandatory
-- @param #RADIO self
-- @param #string FileName Name of the sound file that will be transmitted.
-- @param #number Frequency Frequency in MHz.
-- @param #number Modulation Modulation of frequency, which is either radio.modulation.AM or radio.modulation.FM.
-- @param #number Power Power in W.
-- @return #RADIO self
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
self:F({FileName, Frequency, Modulation, Power})
self:SetFileName(FileName)
if Frequency then self:SetFrequency(Frequency) end
if Modulation then self:SetModulation(Modulation) end
if Power then self:SetPower(Power) end
if Loop then self:SetLoop(Loop) end
return self
end
--- Create a new transmission, that is to say, populate the RADIO with relevant data
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
-- Only the RADIO and the Filename are mandatory.
-- @param #RADIO self
-- @param #string FileName Name of sound file.
-- @param #string Subtitle Subtitle to be displayed with sound file.
-- @param #number SubtitleDuration Duration of subtitle display in seconds.
-- @param #number Frequency Frequency in MHz.
-- @param #number Modulation Modulation which can be either radio.modulation.AM or radio.modulation.FM
-- @param #boolean Loop If true, loop message.
-- @return #RADIO self
function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop)
self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop})
-- Set file name.
self:SetFileName(FileName)
-- Set modulation AM/FM.
if Modulation then
self:SetModulation(Modulation)
end
-- Set frequency.
if Frequency then
self:SetFrequency(Frequency)
end
-- Set subtitle.
if Subtitle then
self:SetSubtitle(Subtitle, SubtitleDuration or 0)
end
-- Set Looping.
if Loop then
self:SetLoop(Loop)
end
return self
end
--- Broadcast the transmission.
-- * The Radio has to be populated with the new transmission before broadcasting.
-- * Please use RADIO setters or either @{#RADIO.NewGenericTransmission} or @{#RADIO.NewUnitTransmission}
-- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE
-- * If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission()
-- * If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
-- @param #RADIO self
-- @param #boolean viatrigger Use trigger.action.radioTransmission() in any case, i.e. also for UNITS and GROUPS.
-- @return #RADIO self
function RADIO:Broadcast(viatrigger)
self:F({viatrigger=viatrigger})
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system.
if (self.Positionable.ClassName=="UNIT" or self.Positionable.ClassName=="GROUP") and (not viatrigger) then
self:T("Broadcasting from a UNIT or a GROUP")
local commandTransmitMessage={
id = "TransmitMessage",
params = {
file = self.FileName,
duration = self.SubtitleDuration,
subtitle = self.Subtitle,
loop = self.Loop,
}}
self:T3(commandTransmitMessage)
self.Positionable:SetCommand(commandTransmitMessage)
else
-- If the POSITIONABLE is anything else, we revert to the general singleton function
-- I need to give it a unique name, so that the transmission can be stopped later. I use the class ID
self:T("Broadcasting from a POSITIONABLE")
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
end
return self
end
--- Stops a transmission
-- This function is especially usefull to stop the broadcast of looped transmissions
-- @param #RADIO self
-- @return #RADIO self
function RADIO:StopBroadcast()
self:F()
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
local commandStopTransmission={id="StopTransmission", params={}}
self.Positionable:SetCommand(commandStopTransmission)
else
-- Else, we use the appropriate singleton funciton
trigger.action.stopRadioTransmission(tostring(self.ID))
end
return self
end

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